213 lines
4.8 KiB
Go
213 lines
4.8 KiB
Go
|
|
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
|
|
}
|
|
|