From e060a3e79dbc47d600aa509e6eb15c6d4ef3e02a Mon Sep 17 00:00:00 2001 From: nareix Date: Fri, 18 Mar 2016 19:57:00 +0800 Subject: [PATCH] add demuxer.go --- demuxer.go | 212 +++++++++++++++++++++++++++++++++++++++++++++ example/example.go | 26 ++++++ example/read.go | 30 ------- test.go | 200 ------------------------------------------ 4 files changed, 238 insertions(+), 230 deletions(-) create mode 100644 demuxer.go create mode 100644 example/example.go delete mode 100644 example/read.go delete mode 100644 test.go diff --git a/demuxer.go b/demuxer.go new file mode 100644 index 0000000..c1d0984 --- /dev/null +++ b/demuxer.go @@ -0,0 +1,212 @@ + +package mp4 + +import ( + "github.com/nareix/mp4/atom" + _ "os" + "fmt" + "io" + _ "log" +) + +type Demuxer struct { + R io.ReadSeeker + Tracks []*Track + TrackH264 *Track + TrackAAC *Track + MovieAtom *atom.Movie +} + +func (self *Demuxer) ReadHeader() (err error) { + var N int64 + var moov *atom.Movie + + if N, err = self.R.Seek(0, 2); err != nil { + return + } + if _, err = self.R.Seek(0, 0); err != nil { + return + } + + lr := &io.LimitedReader{R: self.R, N: N} + for lr.N > 0 { + var ar *io.LimitedReader + + var cc4 string + if ar, cc4, err = atom.ReadAtomHeader(lr, ""); err != nil { + return + } + + if cc4 == "moov" { + if moov, err = atom.ReadMovie(ar); err != nil { + return + } + } + + if _, err = atom.ReadDummy(lr, int(ar.N)); err != nil { + return + } + } + + if moov == nil { + err = fmt.Errorf("'moov' atom not found") + return + } + self.MovieAtom = moov + + self.Tracks = []*Track{} + for _, atrack := range(moov.Tracks) { + track := &Track{ + TrackAtom: atrack, + r: self.R, + } + if atrack.Media != nil && atrack.Media.Info != nil && atrack.Media.Info.Sample != nil { + track.sample = atrack.Media.Info.Sample + } else { + err = fmt.Errorf("sample table not found") + return + } + if record := atom.GetAVCDecoderConfRecordByTrack(atrack); record != nil { + track.Type = H264 + self.TrackH264 = track + if len(record.PPS) > 0 { + track.PPS = record.PPS[0] + } + if len(record.SPS) > 0 { + track.SPS = record.SPS[0] + } + self.Tracks = append(self.Tracks, track) + } else if mp4a := atom.GetMp4aDescByTrack(atrack); mp4a != nil { + self.TrackAAC = track + track.Type = AAC + self.Tracks = append(self.Tracks, track) + } + } + + return +} + +func (self *Track) setSampleIndex(index int) (err error) { + found := false + start := 0 + self.chunkGroupIndex = 0 + + for self.chunkIndex = range(self.sample.ChunkOffset.Entries) { + n := self.sample.SampleToChunk.Entries[self.chunkGroupIndex].SamplesPerChunk + if index >= start && index < start+n { + found = true + self.sampleIndexInChunk = index-start + break + } + start += n + if self.chunkGroupIndex+1 < len(self.sample.SampleToChunk.Entries) && + self.chunkIndex+1 == self.sample.SampleToChunk.Entries[self.chunkGroupIndex+1].FirstChunk { + self.chunkGroupIndex++ + } + } + if !found { + err = io.EOF + return + } + + self.sampleIndex = index + return +} + +func (self *Track) incSampleIndex() { + self.sampleIndexInChunk++ + if self.sampleIndexInChunk == self.sample.SampleToChunk.Entries[self.chunkGroupIndex].SamplesPerChunk { + self.chunkIndex++ + self.sampleIndexInChunk = 0 + } + if self.chunkGroupIndex+1 < len(self.sample.SampleToChunk.Entries) && + self.chunkIndex+1 == self.sample.SampleToChunk.Entries[self.chunkGroupIndex+1].FirstChunk { + self.chunkGroupIndex++ + } + self.sampleIndex++ +} + +func (self *Track) SampleCount() int { + if self.sample.SampleSize.SampleSize == 0 { + chunkGroupIndex := 0 + count := 0 + for chunkIndex := range(self.sample.ChunkOffset.Entries) { + n := self.sample.SampleToChunk.Entries[chunkGroupIndex].SamplesPerChunk + count += n + if chunkGroupIndex+1 < len(self.sample.SampleToChunk.Entries) && + chunkIndex+1 == self.sample.SampleToChunk.Entries[chunkGroupIndex+1].FirstChunk { + chunkGroupIndex++ + } + } + return count + } else { + return len(self.sample.SampleSize.Entries) + } +} + +func (self *Track) ReadSample() (pts int64, dts int64, isKeyFrame bool, data []byte, err error) { + return +} + +func (self *Track) ReadSampleAtIndex(index int) (pts int64, dts int64, isKeyFrame bool, data []byte, err error) { + if self.sampleIndex+1 == index { + self.incSampleIndex() + } else if self.sampleIndex != index { + if err = self.setSampleIndex(index); err != nil { + return + } + } + + if self.chunkIndex > len(self.sample.ChunkOffset.Entries) { + err = io.EOF + return + } + if self.chunkGroupIndex >= len(self.sample.SampleToChunk.Entries) { + err = io.EOF + return + } + + chunkOffset := self.sample.ChunkOffset.Entries[self.chunkIndex] + sampleOffset := 0 + sampleSize := 0 + + if self.sample.SampleSize.SampleSize != 0 { + sampleOffset = chunkOffset + self.sampleIndexInChunk*self.sample.SampleSize.SampleSize + } else { + sampleOffset = chunkOffset + for i := self.sampleIndex-self.sampleIndexInChunk; i < self.sampleIndex; i++ { + sampleOffset += self.sample.SampleSize.Entries[i] + } + } + + if _, err = self.r.Seek(int64(sampleOffset), 0); err != nil { + return + } + data = make([]byte, sampleSize) + if _, err = self.r.Read(data); err != nil { + return + } + + return +} + +func (self *Track) Duration() float32 { + total := int64(0) + for _, entry := range(self.sample.TimeToSample.Entries) { + total += int64(entry.Duration*entry.Count) + } + return float32(total)/float32(self.TrackAtom.Media.Header.TimeScale) +} + +func (self *Track) TimeToSampleIndex(second float32) int { + return 0 +} + +func (self *Track) TimeStampToTime(ts int64) float32 { + return 0.0 +} + +func (self *Track) WriteSample(pts int64, dts int64, data []byte) (err error) { + return +} + diff --git a/example/example.go b/example/example.go new file mode 100644 index 0000000..c061d01 --- /dev/null +++ b/example/example.go @@ -0,0 +1,26 @@ + +package main + +import ( + "github.com/nareix/mp4" + "os" + "fmt" +) + +func DemuxExample() { + file, _ := os.Open("test.mp4") + + demuxer := &mp4.Demuxer{ + R: file, + } + demuxer.ReadHeader() + + fmt.Println("Duration: ", demuxer.TrackH264.Duration()) + count := demuxer.TrackH264.SampleCount() + fmt.Println("SampleCount: ", count) +} + +func main() { + DemuxExample() +} + diff --git a/example/read.go b/example/read.go deleted file mode 100644 index 422d439..0000000 --- a/example/read.go +++ /dev/null @@ -1,30 +0,0 @@ - -package main - -import ( - mp4 "./.." - "flag" - "log" -) - -func main() { - testconv := flag.Bool("testconv", false, "") - probe := flag.Bool("probe", false, "") - flag.Parse() - - if *testconv { - if err := mp4.TestConvert(flag.Arg(0)); err != nil { - log.Println(err) - return - } - } - - if *probe { - if err := mp4.ProbeFile(flag.Arg(0)); err != nil { - log.Println(err) - return - } - } - -} - diff --git a/test.go b/test.go deleted file mode 100644 index c89f626..0000000 --- a/test.go +++ /dev/null @@ -1,200 +0,0 @@ - -package mp4 - -import ( - "github.com/nareix/mp4/atom" - "os" - "io" - "fmt" - "log" - "encoding/hex" -) - -/* -func TestRewrite(filename string) (err error) { - var infile *os.File - if infile, err = os.Open(filename); err != nil { - return - } - - var outfile *os.File - if outfile, err = os.Open(filename+".out.mp4"); err != nil { - return - } - - return -} -*/ - -func getAVCDecoderConfRecordByTrack(track *atom.Track) (record *atom.AVCDecoderConfRecord) { - if media := track.Media; media != nil { - if info := media.Info; info != nil { - if sample := info.Sample; sample != nil { - if desc := sample.SampleDesc; desc != nil { - if avc1 := desc.Avc1Desc; avc1 != nil { - if conf := avc1.Conf; conf != nil { - return &conf.Record - } - } - } - } - } - } - return -} - -func ProbeFile(filename string) (err error) { - var osfile *os.File - if osfile, err = os.Open(filename); err != nil { - return - } - - var finfo os.FileInfo - if finfo, err = osfile.Stat(); err != nil { - return - } - - dumper := &atom.Dumper{} - var moov *atom.Movie - - lr := &io.LimitedReader{R: osfile, N: finfo.Size()} - for lr.N > 0 { - var ar *io.LimitedReader - - var cc4 string - if ar, cc4, err = atom.ReadAtomHeader(lr, ""); err != nil { - log.Println("read atom failed") - return - } - - if cc4 == "moov" { - if moov, err = atom.ReadMovie(ar); err != nil { - log.Println("read '%s' atom failed", cc4) - return - } - if false { - atom.WalkMovie(dumper, moov) - } - } else { - fmt.Println("atom:", cc4) - } - - if _, err = atom.ReadDummy(lr, int(ar.N)); err != nil { - return - } - } - - if moov == nil { - err = fmt.Errorf("'moov' atom not found") - log.Println("'moov' atom not found") - return - } - - if len(moov.Tracks) > 0 { - track := moov.Tracks[0] - record := getAVCDecoderConfRecordByTrack(track) - - if record != nil && len(record.SPS) > 0 { - sps := record.SPS[0] - if len(sps) > 1 { - sps = sps[1:] - log.Println(hex.Dump(sps)) - var info *atom.H264SPSInfo - if info, err = atom.ParseH264SPS(sps); err != nil { - return - } - log.Println(info) - } - } - } - - return -} - -func TestConvert(filename string) (err error) { - var osfile *os.File - if osfile, err = os.Open(filename); err != nil { - return - } - - var finfo os.FileInfo - if finfo, err = osfile.Stat(); err != nil { - return - } - log.Println("filesize", finfo.Size()) - - lr := &io.LimitedReader{R: osfile, N: finfo.Size()} - - var outfile *os.File - if outfile, err = os.Create(filename+".out.mp4"); err != nil { - return - } - - for lr.N > 0 { - var ar *io.LimitedReader - - var cc4 string - if ar, cc4, err = atom.ReadAtomHeader(lr, ""); err != nil { - return - } - - if cc4 == "moov" { - - curPos, _ := outfile.Seek(0, 1) - origSize := ar.N+8 - var moov *atom.Movie - if moov, err = atom.ReadMovie(ar); err != nil { - return - } - - //log.Println(moov.Tracks[0].Media.Info.Data.Refer) - - if err = atom.WriteMovie(outfile, moov); err != nil { - return - } - curPosAfterRead, _ := outfile.Seek(0, 1) - bytesWritten := curPosAfterRead - curPos - - log.Println("regen moov", "tracks nr", len(moov.Tracks), - "origSize", origSize, "bytesWritten", bytesWritten, - ) - - padSize := origSize - bytesWritten - 8 - aw, _ := atom.WriteAtomHeader(outfile, "free") - atom.WriteDummy(outfile, int(padSize)) - aw.Close() - - } else { - - outcc4 := cc4 - if outcc4 != "mdat" { - log.Println("omit", cc4) - outcc4 = "free" - } else { - log.Println("copy", cc4) - } - - var aw *atom.Writer - if aw, err = atom.WriteAtomHeader(outfile, outcc4); err != nil { - return - } - - if _, err = io.CopyN(aw, ar, ar.N); err != nil { - return - } - if err = aw.Close(); err != nil { - return - } - } - - //log.Println("atom", cc4, "left", lr.N) - //atom.ReadDummy(ar, int(ar.N)) - } - - if err = outfile.Close(); err != nil { - return - } - - return -} -