demuxer complete

This commit is contained in:
nareix 2016-03-19 15:07:55 +08:00
parent 21888f54d7
commit ae08044e8d
5 changed files with 275 additions and 55 deletions

35
atom/utils.go Normal file
View File

@ -0,0 +1,35 @@
package atom
func GetAVCDecoderConfRecordByTrack(track *Track) (record *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 GetMp4aDescByTrack(track *Track) (mp4a *Mp4aDesc) {
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 mp4a = desc.Mp4aDesc; mp4a != nil {
return
}
}
}
}
}
return
}

View File

@ -3,10 +3,8 @@ package mp4
import ( import (
"github.com/nareix/mp4/atom" "github.com/nareix/mp4/atom"
_ "os"
"fmt" "fmt"
"io" "io"
_ "log"
) )
type Demuxer struct { type Demuxer struct {
@ -109,54 +107,143 @@ func (self *Track) setSampleIndex(index int) (err error) {
return 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 start = 0
found = false found = false
self.ptsEntryIndex = 0 self.sttsEntryIndex = 0
for self.ptsEntryIndex < len(self.sample.TimeToSample.Entries) { for self.sttsEntryIndex < len(self.sample.TimeToSample.Entries) {
n := self.sample.TimeToSample.Entries[self.ptsEntryIndex].Count entry := self.sample.TimeToSample.Entries[self.sttsEntryIndex]
n := entry.Count
if index >= start && index < start+n { if index >= start && index < start+n {
self.sampleIndexInPtsEntry = index-start self.sampleIndexInSttsEntry = index-start
self.dts += int64((index-start)*entry.Duration)
break break
} }
start += n start += n
self.ptsEntryIndex++ self.dts += int64(n*entry.Duration)
self.sttsEntryIndex++
} }
if !found { if !found {
err = io.EOF err = io.EOF
return return
} }
start = 0 if self.sample.CompositionOffset != nil && len(self.sample.CompositionOffset.Entries) > 0 {
found = false start = 0
self.dtsEntryIndex = 0 found = false
for self.dtsEntryIndex < len(self.sample.CompositionOffset.Entries) { self.cttsEntryIndex = 0
n := self.sample.CompositionOffset.Entries[self.dtsEntryIndex].Count for self.cttsEntryIndex < len(self.sample.CompositionOffset.Entries) {
if index >= start && index < start+n { n := self.sample.CompositionOffset.Entries[self.cttsEntryIndex].Count
self.sampleIndexInDtsEntry = index-start if index >= start && index < start+n {
break self.sampleIndexInCttsEntry = index-start
break
}
start += n
self.cttsEntryIndex++
}
if !found {
err = io.EOF
return
} }
start += n
self.dtsEntryIndex++
} }
if !found {
err = io.EOF if self.sample.SyncSample != nil {
return 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 self.sampleIndex = index
return 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() { func (self *Track) incSampleIndex() {
self.sampleIndexInChunk++ self.sampleIndexInChunk++
if self.sampleIndexInChunk == self.sample.SampleToChunk.Entries[self.chunkGroupIndex].SamplesPerChunk { if self.sampleIndexInChunk == self.sample.SampleToChunk.Entries[self.chunkGroupIndex].SamplesPerChunk {
self.chunkIndex++ self.chunkIndex++
self.sampleIndexInChunk = 0 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) && if self.chunkGroupIndex+1 < len(self.sample.SampleToChunk.Entries) &&
self.chunkIndex+1 == self.sample.SampleToChunk.Entries[self.chunkGroupIndex+1].FirstChunk { self.chunkIndex+1 == self.sample.SampleToChunk.Entries[self.chunkGroupIndex+1].FirstChunk {
self.chunkGroupIndex++ 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++ self.sampleIndex++
} }
@ -179,65 +266,122 @@ func (self *Track) SampleCount() int {
} }
func (self *Track) ReadSample() (pts int64, dts int64, isKeyFrame bool, data []byte, err error) { func (self *Track) ReadSample() (pts int64, dts int64, isKeyFrame bool, data []byte, err error) {
return if !self.isSampleValid() {
}
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 err = io.EOF
return return
} }
chunkOffset := self.sample.ChunkOffset.Entries[self.chunkIndex] chunkOffset := self.sample.ChunkOffset.Entries[self.chunkIndex]
sampleOffset := 0
sampleSize := 0 sampleSize := 0
if self.sample.SampleSize.SampleSize != 0 { if self.sample.SampleSize.SampleSize != 0 {
sampleOffset = chunkOffset + self.sampleIndexInChunk*self.sample.SampleSize.SampleSize sampleSize = self.sample.SampleSize.SampleSize
} else { } else {
sampleOffset = chunkOffset sampleSize = self.sample.SampleSize.Entries[self.sampleIndex]
for i := self.sampleIndex-self.sampleIndexInChunk; i < self.sampleIndex; i++ {
sampleOffset += self.sample.SampleSize.Entries[i]
}
} }
sampleOffset := int64(chunkOffset)+self.sampleOffsetInChunk
if _, err = self.r.Seek(int64(sampleOffset), 0); err != nil { if _, err = self.r.Seek(int64(sampleOffset), 0); err != nil {
return return
} }
data = make([]byte, sampleSize) data = make([]byte, sampleSize)
if _, err = self.r.Read(data); err != nil { if _, err = self.r.Read(data); err != nil {
return 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 return
} }
func (self *Track) Duration() float32 { func (self *Track) Duration() float64 {
total := int64(0) total := int64(0)
for _, entry := range(self.sample.TimeToSample.Entries) { for _, entry := range(self.sample.TimeToSample.Entries) {
total += int64(entry.Duration*entry.Count) total += int64(entry.Duration*entry.Count)
} }
return float32(total)/float32(self.TrackAtom.Media.Header.TimeScale) return float64(total)/float64(self.TrackAtom.Media.Header.TimeScale)
} }
func (self *Track) TimeToSampleIndex(second float32) int { func (self *Track) CurTime() float64 {
return 0 return self.TimeStampToTime(self.dts)
} }
func (self *Track) TimeStampToTime(ts int64) float32 { func (self *Track) CurTimeStamp() int64 {
return 0.0 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) WriteSample(pts int64, dts int64, data []byte) (err error) { func (self *Track) WriteSample(pts int64, dts int64, data []byte) (err error) {

View File

@ -5,19 +5,56 @@ import (
"github.com/nareix/mp4" "github.com/nareix/mp4"
"os" "os"
"fmt" "fmt"
"encoding/hex"
) )
func DemuxExample() { func DemuxExample() {
file, _ := os.Open("test.mp4") file, _ := os.Open("test.mp4")
demuxer := &mp4.Demuxer{ demuxer := &mp4.Demuxer{
R: file, R: file,
} }
demuxer.ReadHeader() demuxer.ReadHeader()
fmt.Println("Total tracks: ", len(demuxer.Tracks))
fmt.Println("Duration: ", demuxer.TrackH264.Duration()) fmt.Println("Duration: ", demuxer.TrackH264.Duration())
count := demuxer.TrackH264.SampleCount() count := demuxer.TrackH264.SampleCount()
fmt.Println("SampleCount: ", count) fmt.Println("SampleCount: ", count)
demuxer.TrackH264.SeekToTime(2.3)
var sample []byte
for i := 0; i < 5; i++ {
pts, dts, isKeyFrame, data, err := demuxer.TrackH264.ReadSample()
fmt.Println("sample #",
i, pts, dts, isKeyFrame, len(data),
demuxer.TrackH264.CurTime(),
err,
)
if i == 3 {
sample = data
}
}
fmt.Println("Sample H264 frame:")
fmt.Print(hex.Dump(sample))
fmt.Println("Duration(AAC): ", demuxer.TrackAAC.Duration())
fmt.Println("SampleCount(AAC): ", demuxer.TrackAAC.SampleCount())
demuxer.TrackAAC.SeekToTime(1.3)
for i := 0; i < 5; i++ {
pts, dts, isKeyFrame, data, err := demuxer.TrackAAC.ReadSample()
fmt.Println("sample(AAC) #",
i, pts, dts, isKeyFrame, len(data),
demuxer.TrackAAC.CurTime(),
err,
)
if i == 1 {
sample = data
}
}
fmt.Println("Sample AAC frame:")
fmt.Print(hex.Dump(sample))
} }
func main() { func main() {

View File

View File

@ -21,11 +21,15 @@ type Track struct {
sample *atom.SampleTable sample *atom.SampleTable
sampleIndex int sampleIndex int
ptsEntryIndex int sampleOffsetInChunk int64
sampleIndexInPtsEntry int syncSampleIndex int
dtsEntryIndex int dts int64
sampleIndexInDtsEntry int sttsEntryIndex int
sampleIndexInSttsEntry int
cttsEntryIndex int
sampleIndexInCttsEntry int
chunkGroupIndex int chunkGroupIndex int
chunkIndex int chunkIndex int