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 (
"github.com/nareix/mp4/atom"
_ "os"
"fmt"
"io"
_ "log"
)
type Demuxer struct {
@ -109,54 +107,143 @@ func (self *Track) setSampleIndex(index int) (err error) {
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.ptsEntryIndex = 0
for self.ptsEntryIndex < len(self.sample.TimeToSample.Entries) {
n := self.sample.TimeToSample.Entries[self.ptsEntryIndex].Count
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.sampleIndexInPtsEntry = index-start
self.sampleIndexInSttsEntry = index-start
self.dts += int64((index-start)*entry.Duration)
break
}
start += n
self.ptsEntryIndex++
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.dtsEntryIndex = 0
for self.dtsEntryIndex < len(self.sample.CompositionOffset.Entries) {
n := self.sample.CompositionOffset.Entries[self.dtsEntryIndex].Count
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.sampleIndexInDtsEntry = index-start
self.sampleIndexInCttsEntry = index-start
break
}
start += n
self.dtsEntryIndex++
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++
}
@ -179,65 +266,122 @@ func (self *Track) SampleCount() int {
}
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) {
if !self.isSampleValid() {
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
sampleSize = self.sample.SampleSize.SampleSize
} else {
sampleOffset = chunkOffset
for i := self.sampleIndex-self.sampleIndexInChunk; i < self.sampleIndex; i++ {
sampleOffset += self.sample.SampleSize.Entries[i]
}
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() float32 {
func (self *Track) Duration() float64 {
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)
return float64(total)/float64(self.TrackAtom.Media.Header.TimeScale)
}
func (self *Track) TimeToSampleIndex(second float32) int {
return 0
func (self *Track) CurTime() float64 {
return self.TimeStampToTime(self.dts)
}
func (self *Track) TimeStampToTime(ts int64) float32 {
return 0.0
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) WriteSample(pts int64, dts int64, data []byte) (err error) {

View File

@ -5,19 +5,56 @@ import (
"github.com/nareix/mp4"
"os"
"fmt"
"encoding/hex"
)
func DemuxExample() {
file, _ := os.Open("test.mp4")
demuxer := &mp4.Demuxer{
R: file,
}
demuxer.ReadHeader()
fmt.Println("Total tracks: ", len(demuxer.Tracks))
fmt.Println("Duration: ", demuxer.TrackH264.Duration())
count := demuxer.TrackH264.SampleCount()
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() {

View File

View File

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