diff --git a/demuxer.go b/demuxer.go index 5f62125..edbdfb5 100644 --- a/demuxer.go +++ b/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 } - diff --git a/muxer.go b/muxer.go index 6c5ad64..f60436a 100644 --- a/muxer.go +++ b/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 diff --git a/stream.go b/stream.go index b1126b2..5252423 100644 --- a/stream.go +++ b/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) +}