joy4/demuxer.go
2016-03-31 00:34:58 +08:00

404 lines
9.8 KiB
Go

package mp4
import (
"github.com/nareix/mp4/atom"
"github.com/nareix/mp4/isom"
"bytes"
"fmt"
"io"
)
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 {
if len(record.PPS) > 0 {
track.pps = record.PPS[0]
}
if len(record.SPS) > 0 {
track.sps = record.SPS[0]
}
track.Type = H264
self.TrackH264 = track
self.Tracks = append(self.Tracks, track)
} else if mp4a := atom.GetMp4aDescByTrack(atrack); mp4a != nil && mp4a.Conf != nil {
if config, err := isom.ReadElemStreamDescAAC(bytes.NewReader(mp4a.Conf.Data)); err == nil {
track.mpeg4AudioConfig = config.Complete()
track.Type = AAC
self.TrackAAC = track
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
}
if self.sample.SampleSize.SampleSize != 0 {
self.sampleOffsetInChunk = int64(self.sampleIndexInChunk*self.sample.SampleSize.SampleSize)
} else {
if index >= len(self.sample.SampleSize.Entries) {
err = io.EOF
return
}
self.sampleOffsetInChunk = int64(0)
for i := index-self.sampleIndexInChunk; i < index; i++ {
self.sampleOffsetInChunk += int64(self.sample.SampleSize.Entries[i])
}
}
self.dts = int64(0)
start = 0
found = false
self.sttsEntryIndex = 0
for self.sttsEntryIndex < len(self.sample.TimeToSample.Entries) {
entry := self.sample.TimeToSample.Entries[self.sttsEntryIndex]
n := entry.Count
if index >= start && index < start+n {
self.sampleIndexInSttsEntry = index-start
self.dts += int64((index-start)*entry.Duration)
break
}
start += n
self.dts += int64(n*entry.Duration)
self.sttsEntryIndex++
}
if !found {
err = io.EOF
return
}
if self.sample.CompositionOffset != nil && len(self.sample.CompositionOffset.Entries) > 0 {
start = 0
found = false
self.cttsEntryIndex = 0
for self.cttsEntryIndex < len(self.sample.CompositionOffset.Entries) {
n := self.sample.CompositionOffset.Entries[self.cttsEntryIndex].Count
if index >= start && index < start+n {
self.sampleIndexInCttsEntry = index-start
break
}
start += n
self.cttsEntryIndex++
}
if !found {
err = io.EOF
return
}
}
if self.sample.SyncSample != nil {
self.syncSampleIndex = 0
for self.syncSampleIndex < len(self.sample.SyncSample.Entries)-1 {
if self.sample.SyncSample.Entries[self.syncSampleIndex+1]-1 > index {
break
}
self.syncSampleIndex++
}
}
self.sampleIndex = index
return
}
func (self *Track) isSampleValid() bool {
if self.chunkIndex >= len(self.sample.ChunkOffset.Entries) {
return false
}
if self.chunkGroupIndex >= len(self.sample.SampleToChunk.Entries) {
return false
}
if self.sttsEntryIndex >= len(self.sample.TimeToSample.Entries) {
return false
}
if self.sample.CompositionOffset != nil && len(self.sample.CompositionOffset.Entries) > 0 {
if self.cttsEntryIndex >= len(self.sample.CompositionOffset.Entries) {
return false
}
}
if self.sample.SyncSample != nil {
if self.syncSampleIndex >= len(self.sample.SyncSample.Entries) {
return false
}
}
if self.sample.SampleSize.SampleSize != 0 {
if self.sampleIndex >= len(self.sample.SampleSize.Entries) {
return false
}
}
return true
}
func (self *Track) incSampleIndex() {
self.sampleIndexInChunk++
if self.sampleIndexInChunk == self.sample.SampleToChunk.Entries[self.chunkGroupIndex].SamplesPerChunk {
self.chunkIndex++
self.sampleIndexInChunk = 0
self.sampleOffsetInChunk = int64(0)
} else {
if self.sample.SampleSize.SampleSize != 0 {
self.sampleOffsetInChunk += int64(self.sample.SampleSize.SampleSize)
} else {
self.sampleOffsetInChunk += int64(self.sample.SampleSize.Entries[self.sampleIndex])
}
}
if self.chunkGroupIndex+1 < len(self.sample.SampleToChunk.Entries) &&
self.chunkIndex+1 == self.sample.SampleToChunk.Entries[self.chunkGroupIndex+1].FirstChunk {
self.chunkGroupIndex++
}
sttsEntry := self.sample.TimeToSample.Entries[self.sttsEntryIndex]
self.sampleIndexInSttsEntry++
self.dts += int64(sttsEntry.Duration)
if self.sampleIndexInSttsEntry == sttsEntry.Count {
self.sampleIndexInSttsEntry = 0
self.sttsEntryIndex++
}
if self.sample.CompositionOffset != nil && len(self.sample.CompositionOffset.Entries) > 0 {
self.sampleIndexInCttsEntry++
if self.sampleIndexInCttsEntry == self.sample.CompositionOffset.Entries[self.cttsEntryIndex].Count {
self.sampleIndexInCttsEntry = 0
self.cttsEntryIndex++
}
}
if self.sample.SyncSample != nil {
entries := self.sample.SyncSample.Entries
if self.syncSampleIndex+1 < len(entries) && entries[self.syncSampleIndex+1]-1 == self.sampleIndex+1 {
self.syncSampleIndex++
}
}
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) {
if !self.isSampleValid() {
err = io.EOF
return
}
chunkOffset := self.sample.ChunkOffset.Entries[self.chunkIndex]
sampleSize := 0
if self.sample.SampleSize.SampleSize != 0 {
sampleSize = self.sample.SampleSize.SampleSize
} else {
sampleSize = self.sample.SampleSize.Entries[self.sampleIndex]
}
sampleOffset := int64(chunkOffset)+self.sampleOffsetInChunk
if _, err = self.r.Seek(int64(sampleOffset), 0); err != nil {
return
}
data = make([]byte, sampleSize)
if _, err = self.r.Read(data); err != nil {
return
}
if self.sample.SyncSample != nil {
if self.sample.SyncSample.Entries[self.syncSampleIndex]-1 == self.sampleIndex {
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
}
self.incSampleIndex()
return
}
func (self *Track) Duration() float64 {
total := int64(0)
for _, entry := range(self.sample.TimeToSample.Entries) {
total += int64(entry.Duration*entry.Count)
}
return float64(total)/float64(self.TrackAtom.Media.Header.TimeScale)
}
func (self *Track) CurTime() float64 {
return self.TimeStampToTime(self.dts)
}
func (self *Track) CurTimeStamp() int64 {
return self.dts
}
func (self *Track) CurSampleIndex() int {
return self.sampleIndex
}
func (self *Track) SeekToTime(time float64) error {
index := self.TimeToSampleIndex(time)
return self.setSampleIndex(index)
}
func (self *Track) SeekToSampleIndex(index int) error {
return self.setSampleIndex(index)
}
func (self *Track) TimeToSampleIndex(time float64) int {
targetTs := self.TimeToTimeStamp(time)
targetIndex := 0
startTs := int64(0)
endTs := int64(0)
startIndex := 0
endIndex := 0
found := false
for _, entry := range(self.sample.TimeToSample.Entries) {
endTs = startTs+int64(entry.Count*entry.Duration)
endIndex = startIndex+entry.Count
if targetTs >= startTs && targetTs < endTs {
targetIndex = startIndex+int((targetTs-startTs)/int64(entry.Duration))
found = true
}
startTs = endTs
startIndex = endIndex
}
if !found {
if targetTs < 0 {
targetIndex = 0
} else {
targetIndex = endIndex-1
}
}
if self.sample.SyncSample != nil {
entries := self.sample.SyncSample.Entries
for i := len(entries)-1; i >= 0; i-- {
if entries[i]-1 < targetIndex {
targetIndex = entries[i]-1
break
}
}
}
return targetIndex
}
func (self *Track) TimeToTimeStamp(time float64) int64 {
return int64(time*float64(self.TrackAtom.Media.Header.TimeScale))
}
func (self *Track) TimeStampToTime(ts int64) float64 {
return float64(ts)/float64(self.TrackAtom.Media.Header.TimeScale)
}
func (self *Track) TimeScale() int64 {
return int64(self.TrackAtom.Media.Header.TimeScale)
}
func (self *Track) GetH264PPSAndSPS() (pps, sps []byte) {
return self.pps, self.sps
}
func (self *Track) GetMPEG4AudioConfig() isom.MPEG4AudioConfig {
return self.mpeg4AudioConfig
}