api change
This commit is contained in:
parent
3a136cd8f5
commit
7d1af58451
70
demuxer.go
70
demuxer.go
@ -17,7 +17,7 @@ type Demuxer struct {
|
||||
}
|
||||
|
||||
func (self *Demuxer) Streams() (streams []av.Stream) {
|
||||
for _, stream := range(self.streams) {
|
||||
for _, stream := range self.streams {
|
||||
streams = append(streams, stream)
|
||||
}
|
||||
return
|
||||
@ -69,7 +69,7 @@ func (self *Demuxer) ReadHeader() (err error) {
|
||||
}
|
||||
if atrack.Media != nil && atrack.Media.Info != nil && atrack.Media.Info.Sample != nil {
|
||||
stream.sample = atrack.Media.Info.Sample
|
||||
stream.SetTimeScale(int64(atrack.Media.Header.TimeScale))
|
||||
stream.timeScale = int64(atrack.Media.Header.TimeScale)
|
||||
} else {
|
||||
err = fmt.Errorf("sample table not found")
|
||||
return
|
||||
@ -219,7 +219,7 @@ func (self *Stream) isSampleValid() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (self *Stream) incSampleIndex() {
|
||||
func (self *Stream) incSampleIndex() (duration int64) {
|
||||
self.sampleIndexInChunk++
|
||||
if self.sampleIndexInChunk == self.sample.SampleToChunk.Entries[self.chunkGroupIndex].SamplesPerChunk {
|
||||
self.chunkIndex++
|
||||
@ -239,8 +239,9 @@ func (self *Stream) incSampleIndex() {
|
||||
}
|
||||
|
||||
sttsEntry := self.sample.TimeToSample.Entries[self.sttsEntryIndex]
|
||||
duration = int64(sttsEntry.Duration)
|
||||
self.sampleIndexInSttsEntry++
|
||||
self.dts += int64(sttsEntry.Duration)
|
||||
self.dts += duration
|
||||
if self.sampleIndexInSttsEntry == sttsEntry.Count {
|
||||
self.sampleIndexInSttsEntry = 0
|
||||
self.sttsEntryIndex++
|
||||
@ -262,6 +263,7 @@ func (self *Stream) incSampleIndex() {
|
||||
}
|
||||
|
||||
self.sampleIndex++
|
||||
return
|
||||
}
|
||||
|
||||
func (self *Stream) sampleCount() int {
|
||||
@ -282,33 +284,41 @@ func (self *Stream) sampleCount() int {
|
||||
}
|
||||
}
|
||||
|
||||
func (self *Demuxer) ReadPacket() (pkt av.Packet, err error) {
|
||||
func (self *Demuxer) ReadPacket() (streamIndex int, pkt av.Packet, err error) {
|
||||
var choose *Stream
|
||||
for _, stream := range(self.streams) {
|
||||
if choose == nil || stream.TsToTime(stream.dts) < choose.TsToTime(choose.dts) {
|
||||
for i, stream := range self.streams {
|
||||
if choose == nil || stream.tsToTime(stream.dts) < choose.tsToTime(choose.dts) {
|
||||
choose = stream
|
||||
streamIndex = i
|
||||
}
|
||||
}
|
||||
if false {
|
||||
fmt.Printf("ReadPacket: choose index=%v time=%v\n", choose.idx, choose.TsToTime(choose.dts))
|
||||
fmt.Printf("ReadPacket: choose index=%v time=%v\n", choose.idx, choose.tsToTime(choose.dts))
|
||||
}
|
||||
pkt, err = choose.readPacket()
|
||||
return
|
||||
}
|
||||
|
||||
func (self *Demuxer) Time() (time float64) {
|
||||
if len(self.streams) > 0 {
|
||||
stream := self.streams[0]
|
||||
time = stream.tsToTime(stream.dts)
|
||||
}
|
||||
pkt.StreamIdx = choose.idx
|
||||
pkt.Pts, pkt.Dts, pkt.IsKeyFrame, pkt.Data, err = choose.readSample()
|
||||
return
|
||||
}
|
||||
|
||||
func (self *Demuxer) SeekToTime(time float64) (err error) {
|
||||
for _, stream := range(self.streams) {
|
||||
for _, stream := range self.streams {
|
||||
if stream.IsVideo() {
|
||||
if err = stream.seekToTime(time); err != nil {
|
||||
return
|
||||
}
|
||||
time = stream.TsToTime(stream.dts)
|
||||
time = stream.tsToTime(stream.dts)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
for _, stream := range(self.streams) {
|
||||
for _, stream := range self.streams {
|
||||
if !stream.IsVideo() {
|
||||
if err = stream.seekToTime(time); err != nil {
|
||||
return
|
||||
@ -319,11 +329,12 @@ func (self *Demuxer) SeekToTime(time float64) (err error) {
|
||||
return
|
||||
}
|
||||
|
||||
func (self *Stream) readSample() (pts int64, dts int64, isKeyFrame bool, data []byte, err error) {
|
||||
func (self *Stream) readPacket() (pkt av.Packet, err error) {
|
||||
if !self.isSampleValid() {
|
||||
err = io.EOF
|
||||
return
|
||||
}
|
||||
//fmt.Println("readPacket", self.sampleIndex)
|
||||
|
||||
chunkOffset := self.sample.ChunkOffset.Entries[self.chunkIndex]
|
||||
sampleSize := 0
|
||||
@ -334,54 +345,46 @@ func (self *Stream) readSample() (pts int64, dts int64, isKeyFrame bool, data []
|
||||
}
|
||||
|
||||
sampleOffset := int64(chunkOffset) + self.sampleOffsetInChunk
|
||||
if _, err = self.r.Seek(int64(sampleOffset), 0); err != nil {
|
||||
if _, err = self.r.Seek(sampleOffset, 0); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
data = make([]byte, sampleSize)
|
||||
if _, err = self.r.Read(data); err != nil {
|
||||
pkt.Data = make([]byte, sampleSize)
|
||||
if _, err = self.r.Read(pkt.Data); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if self.sample.SyncSample != nil {
|
||||
if self.sample.SyncSample.Entries[self.syncSampleIndex]-1 == self.sampleIndex {
|
||||
isKeyFrame = true
|
||||
pkt.IsKeyFrame = true
|
||||
}
|
||||
}
|
||||
|
||||
//println("pts/dts", self.ptsEntryIndex, self.dtsEntryIndex)
|
||||
dts = self.dts
|
||||
if self.sample.CompositionOffset != nil && len(self.sample.CompositionOffset.Entries) > 0 {
|
||||
pts = self.dts + int64(self.sample.CompositionOffset.Entries[self.cttsEntryIndex].Offset)
|
||||
} else {
|
||||
pts = dts
|
||||
cts := int64(self.sample.CompositionOffset.Entries[self.cttsEntryIndex].Offset)
|
||||
pkt.CompositionTime = self.tsToTime(cts)
|
||||
}
|
||||
|
||||
self.incSampleIndex()
|
||||
duration := self.incSampleIndex()
|
||||
pkt.Duration = self.tsToTime(duration)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (self *Stream) duration() float64 {
|
||||
total := int64(0)
|
||||
for _, entry := range self.sample.TimeToSample.Entries {
|
||||
total += int64(entry.Duration * entry.Count)
|
||||
}
|
||||
return float64(total) / float64(self.TimeScale())
|
||||
}
|
||||
|
||||
func (self *Stream) seekToTime(time float64) (err error) {
|
||||
index := self.timeToSampleIndex(time)
|
||||
if err = self.setSampleIndex(index); err != nil {
|
||||
return
|
||||
}
|
||||
if false {
|
||||
fmt.Printf("stream[%d]: seekToTime index=%v time=%v cur=%v\n", self.idx, index, time, self.TsToTime(self.dts))
|
||||
fmt.Printf("stream[%d]: seekToTime index=%v time=%v cur=%v\n", self.idx, index, time, self.tsToTime(self.dts))
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (self *Stream) timeToSampleIndex(time float64) int {
|
||||
targetTs := self.TimeToTs(time)
|
||||
targetTs := self.timeToTs(time)
|
||||
targetIndex := 0
|
||||
|
||||
startTs := int64(0)
|
||||
@ -419,4 +422,3 @@ func (self *Stream) timeToSampleIndex(time float64) int {
|
||||
|
||||
return targetIndex
|
||||
}
|
||||
|
||||
|
77
muxer.go
77
muxer.go
@ -4,10 +4,10 @@ import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"github.com/nareix/av"
|
||||
"github.com/nareix/codec/aacparser"
|
||||
"github.com/nareix/codec/h264parser"
|
||||
"github.com/nareix/mp4/atom"
|
||||
"github.com/nareix/mp4/isom"
|
||||
"github.com/nareix/codec/h264parser"
|
||||
"github.com/nareix/codec/aacparser"
|
||||
"io"
|
||||
)
|
||||
|
||||
@ -62,6 +62,7 @@ func (self *Muxer) NewStream() av.Stream {
|
||||
},
|
||||
}
|
||||
|
||||
stream.timeScale = 90000
|
||||
stream.muxer = self
|
||||
self.streams = append(self.streams, stream)
|
||||
|
||||
@ -69,12 +70,8 @@ func (self *Muxer) NewStream() av.Stream {
|
||||
}
|
||||
|
||||
func (self *Stream) fillTrackAtom() (err error) {
|
||||
if self.sampleIndex > 0 {
|
||||
self.sttsEntry.Count++
|
||||
}
|
||||
|
||||
self.trackAtom.Media.Header.TimeScale = int(self.TimeScale())
|
||||
self.trackAtom.Media.Header.Duration = int(self.lastDts)
|
||||
self.trackAtom.Media.Header.TimeScale = int(self.timeScale)
|
||||
self.trackAtom.Media.Header.Duration = int(self.duration)
|
||||
|
||||
if self.Type() == av.H264 {
|
||||
width, height := self.Width(), self.Height()
|
||||
@ -130,14 +127,6 @@ func (self *Stream) fillTrackAtom() (err error) {
|
||||
return
|
||||
}
|
||||
|
||||
func (self *Muxer) writeMdat(data []byte) (pos int64, err error) {
|
||||
if pos, err = self.mdatWriter.Seek(0, 1); err != nil {
|
||||
return
|
||||
}
|
||||
_, err = self.mdatWriter.Write(data)
|
||||
return
|
||||
}
|
||||
|
||||
func (self *Muxer) WriteHeader() (err error) {
|
||||
if self.mdatWriter, err = atom.WriteAtomHeader(self.W, "mdat"); err != nil {
|
||||
return
|
||||
@ -145,11 +134,12 @@ func (self *Muxer) WriteHeader() (err error) {
|
||||
return
|
||||
}
|
||||
|
||||
func (self *Muxer) WriteSample(pkt av.Packet) (err error) {
|
||||
pts, dts, isKeyFrame, frame := pkt.Pts, pkt.Dts, pkt.IsKeyFrame, pkt.Data
|
||||
stream := self.streams[pkt.StreamIdx]
|
||||
func (self *Muxer) WritePacket(streamIndex int, pkt av.Packet) (err error) {
|
||||
stream := self.streams[streamIndex]
|
||||
frame := pkt.Data
|
||||
|
||||
if stream.Type() == av.AAC && aacparser.IsADTSFrame(frame) {
|
||||
sampleRate := stream.SampleRate()
|
||||
for len(frame) > 0 {
|
||||
var payload []byte
|
||||
var samples int
|
||||
@ -157,20 +147,21 @@ func (self *Muxer) WriteSample(pkt av.Packet) (err error) {
|
||||
if _, payload, samples, framelen, err = aacparser.ReadADTSFrame(frame); err != nil {
|
||||
return
|
||||
}
|
||||
delta := int64(samples) * stream.TimeScale() / int64(stream.SampleRate())
|
||||
pts += delta
|
||||
dts += delta
|
||||
frame = frame[framelen:]
|
||||
if stream.writeSample(pts, dts, isKeyFrame, payload); err != nil {
|
||||
newpkt := pkt
|
||||
newpkt.Data = payload
|
||||
newpkt.Duration = float64(samples)/float64(sampleRate)
|
||||
if err = stream.writePacket(newpkt); err != nil {
|
||||
return
|
||||
}
|
||||
frame = frame[framelen:]
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
return stream.writeSample(pts, dts, isKeyFrame, frame)
|
||||
return stream.writePacket(pkt)
|
||||
}
|
||||
|
||||
func (self *Stream) writeSample(pts int64, dts int64, isKeyFrame bool, data []byte) (err error) {
|
||||
func (self *Stream) writePacket(pkt av.Packet) (err error) {
|
||||
var filePos int64
|
||||
var sampleSize int
|
||||
|
||||
@ -179,7 +170,7 @@ func (self *Stream) writeSample(pts int64, dts int64, isKeyFrame bool, data []by
|
||||
}
|
||||
|
||||
if self.Type() == av.H264 {
|
||||
nalus, _ := h264parser.SplitNALUs(data)
|
||||
nalus, _ := h264parser.SplitNALUs(pkt.Data)
|
||||
h264parser.WalkNALUsAVCC(nalus, func(b []byte) {
|
||||
sampleSize += len(b)
|
||||
_, err = self.muxer.mdatWriter.Write(b)
|
||||
@ -188,35 +179,25 @@ func (self *Stream) writeSample(pts int64, dts int64, isKeyFrame bool, data []by
|
||||
return
|
||||
}
|
||||
} else {
|
||||
sampleSize = len(data)
|
||||
if _, err = self.muxer.mdatWriter.Write(data); err != nil {
|
||||
sampleSize = len(pkt.Data)
|
||||
if _, err = self.muxer.mdatWriter.Write(pkt.Data); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if isKeyFrame && self.sample.SyncSample != nil {
|
||||
if pkt.IsKeyFrame && self.sample.SyncSample != nil {
|
||||
self.sample.SyncSample.Entries = append(self.sample.SyncSample.Entries, self.sampleIndex+1)
|
||||
}
|
||||
|
||||
if self.sampleIndex > 0 {
|
||||
if dts <= self.lastDts {
|
||||
err = fmt.Errorf("dts must be incremental")
|
||||
return
|
||||
}
|
||||
duration := int(dts - self.lastDts)
|
||||
if self.sttsEntry == nil || duration != self.sttsEntry.Duration {
|
||||
self.sample.TimeToSample.Entries = append(self.sample.TimeToSample.Entries, atom.TimeToSampleEntry{Duration: duration})
|
||||
self.sttsEntry = &self.sample.TimeToSample.Entries[len(self.sample.TimeToSample.Entries)-1]
|
||||
}
|
||||
self.sttsEntry.Count++
|
||||
duration := int(self.timeToTs(pkt.Duration))
|
||||
if self.sttsEntry == nil || duration != self.sttsEntry.Duration {
|
||||
self.sample.TimeToSample.Entries = append(self.sample.TimeToSample.Entries, atom.TimeToSampleEntry{Duration: duration})
|
||||
self.sttsEntry = &self.sample.TimeToSample.Entries[len(self.sample.TimeToSample.Entries)-1]
|
||||
}
|
||||
self.sttsEntry.Count++
|
||||
|
||||
if self.sample.CompositionOffset != nil {
|
||||
if pts < dts {
|
||||
err = fmt.Errorf("pts must greater than dts")
|
||||
return
|
||||
}
|
||||
offset := int(pts - dts)
|
||||
offset := int(self.timeToTs(pkt.CompositionTime))
|
||||
if self.cttsEntry == nil || offset != self.cttsEntry.Offset {
|
||||
table := self.sample.CompositionOffset
|
||||
table.Entries = append(table.Entries, atom.CompositionOffsetEntry{Offset: offset})
|
||||
@ -225,7 +206,7 @@ func (self *Stream) writeSample(pts int64, dts int64, isKeyFrame bool, data []by
|
||||
self.cttsEntry.Count++
|
||||
}
|
||||
|
||||
self.lastDts = dts
|
||||
self.duration += int64(duration)
|
||||
self.sampleIndex++
|
||||
self.sample.ChunkOffset.Entries = append(self.sample.ChunkOffset.Entries, int(filePos))
|
||||
self.sample.SampleSize.Entries = append(self.sample.SampleSize.Entries, sampleSize)
|
||||
@ -248,7 +229,7 @@ func (self *Muxer) WriteTrailer() (err error) {
|
||||
if err = stream.fillTrackAtom(); err != nil {
|
||||
return
|
||||
}
|
||||
dur := stream.duration()
|
||||
dur := stream.tsToTime(stream.duration)
|
||||
stream.trackAtom.Header.Duration = int(float64(timeScale) * dur)
|
||||
if dur > maxDur {
|
||||
maxDur = dur
|
||||
|
12
stream.go
12
stream.go
@ -11,7 +11,10 @@ type Stream struct {
|
||||
|
||||
trackAtom *atom.Track
|
||||
r io.ReadSeeker
|
||||
idx int
|
||||
idx int
|
||||
|
||||
timeScale int64
|
||||
duration int64
|
||||
|
||||
muxer *Muxer
|
||||
|
||||
@ -35,7 +38,12 @@ type Stream struct {
|
||||
sttsEntry *atom.TimeToSampleEntry
|
||||
cttsEntry *atom.CompositionOffsetEntry
|
||||
writeMdat func([]byte) (int64, error)
|
||||
lastDts int64
|
||||
}
|
||||
|
||||
func (self *Stream) timeToTs(time float64) int64 {
|
||||
return int64(time * float64(self.timeScale))
|
||||
}
|
||||
|
||||
func (self *Stream) tsToTime(ts int64) float64 {
|
||||
return float64(ts) / float64(self.timeScale)
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user