replace Track to Stream

This commit is contained in:
nareix 2016-04-19 08:56:08 +08:00
parent 371d0f13e3
commit 07ed4e9098
14 changed files with 225 additions and 561 deletions

View File

@ -1,3 +1 @@
package atom package atom

View File

@ -1,16 +1,15 @@
package atom package atom
import ( import (
"io"
"fmt"
"strings"
"encoding/hex" "encoding/hex"
"fmt"
"io"
"strings"
) )
type Walker interface { type Walker interface {
FilterArrayItem(string,string,int,int) bool FilterArrayItem(string, string, int, int) bool
ArrayLeft(int,int) ArrayLeft(int, int)
StartStruct(string) StartStruct(string)
EndStruct() EndStruct()
Name(string) Name(string)
@ -25,9 +24,9 @@ type Walker interface {
} }
type Dumper struct { type Dumper struct {
W io.Writer W io.Writer
depth int depth int
name string name string
arrlen int arrlen int
arridx int arridx int
} }
@ -94,4 +93,3 @@ func (self Dumper) Bytes(val []byte) {
func (self Dumper) TimeStamp(val TimeStamp) { func (self Dumper) TimeStamp(val TimeStamp) {
self.Println(fmt.Sprintf("%s: %d", self.name, int(val))) self.Println(fmt.Sprintf("%s: %d", self.name, int(val)))
} }

View File

@ -3,8 +3,8 @@ package atom
import ( import (
"bytes" "bytes"
"fmt" "fmt"
"io"
"github.com/nareix/bits" "github.com/nareix/bits"
"io"
) )
type H264SPSInfo struct { type H264SPSInfo struct {
@ -363,14 +363,14 @@ const (
) )
type TrackFragHeader struct { type TrackFragHeader struct {
Version int Version int
Flags int Flags int
Id int Id int
DefaultSize int DefaultSize int
DefaultDuration int DefaultDuration int
DefaultFlags int DefaultFlags int
BaseDataOffset int64 BaseDataOffset int64
StsdId int StsdId int
} }
func WalkTrackFragHeader(w Walker, self *TrackFragHeader) { func WalkTrackFragHeader(w Walker, self *TrackFragHeader) {
@ -431,7 +431,7 @@ func ReadTrackFragHeader(r *io.LimitedReader) (res *TrackFragHeader, err error)
} }
if self.Flags&TFHD_DEFAULT_FLAGS != 0 { if self.Flags&TFHD_DEFAULT_FLAGS != 0 {
if self.DefaultFlags,err = ReadInt(r, 4); err != nil { if self.DefaultFlags, err = ReadInt(r, 4); err != nil {
return return
} }
} }
@ -617,4 +617,3 @@ func WalkTrackFragDecodeTime(w Walker, self *TrackFragDecodeTime) {
w.EndStruct() w.EndStruct()
return return
} }

View File

@ -1,4 +1,3 @@
package atom package atom
import ( import (
@ -33,7 +32,7 @@ func ReadInt(r io.Reader, n int) (res int, err error) {
return return
} }
if uval&(1<<uint(n*8-1)) != 0 { if uval&(1<<uint(n*8-1)) != 0 {
res = -int((1<<uint(n*8))-uval) res = -int((1 << uint(n*8)) - uval)
} else { } else {
res = int(uval) res = int(uval)
} }
@ -47,7 +46,7 @@ func ReadFixed(r io.Reader, n int) (res Fixed, err error) {
} }
if n == 2 { if n == 2 {
res = Fixed(ui<<8) res = Fixed(ui << 8)
} else if n == 4 { } else if n == 4 {
res = Fixed(ui) res = Fixed(ui)
} else { } else {
@ -114,4 +113,3 @@ func ReadAtomHeader(r io.Reader, targetCC4 string) (res *io.LimitedReader, cc4 s
return return
} }
} }

View File

@ -565,11 +565,11 @@ func WalkTrackHeader(w Walker, self *TrackHeader) {
} }
type HandlerRefer struct { type HandlerRefer struct {
Version int Version int
Flags int Flags int
Type string CodecType string
SubType string SubType string
Name string Name string
} }
func ReadHandlerRefer(r *io.LimitedReader) (res *HandlerRefer, err error) { func ReadHandlerRefer(r *io.LimitedReader) (res *HandlerRefer, err error) {
@ -581,7 +581,7 @@ func ReadHandlerRefer(r *io.LimitedReader) (res *HandlerRefer, err error) {
if self.Flags, err = ReadInt(r, 3); err != nil { if self.Flags, err = ReadInt(r, 3); err != nil {
return return
} }
if self.Type, err = ReadString(r, 4); err != nil { if self.CodecType, err = ReadString(r, 4); err != nil {
return return
} }
if self.SubType, err = ReadString(r, 4); err != nil { if self.SubType, err = ReadString(r, 4); err != nil {
@ -606,7 +606,7 @@ func WriteHandlerRefer(w io.WriteSeeker, self *HandlerRefer) (err error) {
if err = WriteInt(w, self.Flags, 3); err != nil { if err = WriteInt(w, self.Flags, 3); err != nil {
return return
} }
if err = WriteString(w, self.Type, 4); err != nil { if err = WriteString(w, self.CodecType, 4); err != nil {
return return
} }
if err = WriteString(w, self.SubType, 4); err != nil { if err = WriteString(w, self.SubType, 4); err != nil {
@ -628,7 +628,7 @@ func WalkHandlerRefer(w Walker, self *HandlerRefer) {
w.Name("Flags") w.Name("Flags")
w.Int(self.Flags) w.Int(self.Flags)
w.Name("Type") w.Name("Type")
w.String(self.Type) w.String(self.CodecType)
w.Name("SubType") w.Name("SubType")
w.String(self.SubType) w.String(self.SubType)
w.Name("Name") w.Name("Name")

View File

@ -1,14 +1,12 @@
package atom package atom
type Fixed uint32 type Fixed uint32
type TimeStamp uint32 type TimeStamp uint32
func IntToFixed(val int) Fixed { func IntToFixed(val int) Fixed {
return Fixed(val<<16) return Fixed(val << 16)
} }
func FixedToInt(val Fixed) int { func FixedToInt(val Fixed) int {
return int(val>>16) return int(val >> 16)
} }

View File

@ -1,8 +1,7 @@
package atom package atom
func GetAVCDecoderConfRecordByTrack(track *Track) (record *AVCDecoderConfRecord) { func GetAVCDecoderConfRecordByTrack(stream *Track) (record *AVCDecoderConfRecord) {
if media := track.Media; media != nil { if media := stream.Media; media != nil {
if info := media.Info; info != nil { if info := media.Info; info != nil {
if sample := info.Sample; sample != nil { if sample := info.Sample; sample != nil {
if desc := sample.SampleDesc; desc != nil { if desc := sample.SampleDesc; desc != nil {
@ -18,8 +17,8 @@ func GetAVCDecoderConfRecordByTrack(track *Track) (record *AVCDecoderConfRecord)
return return
} }
func GetMp4aDescByTrack(track *Track) (mp4a *Mp4aDesc) { func GetMp4aDescByTrack(stream *Track) (mp4a *Mp4aDesc) {
if media := track.Media; media != nil { if media := stream.Media; media != nil {
if info := media.Info; info != nil { if info := media.Info; info != nil {
if sample := info.Sample; sample != nil { if sample := info.Sample; sample != nil {
if desc := sample.SampleDesc; desc != nil { if desc := sample.SampleDesc; desc != nil {
@ -32,4 +31,3 @@ func GetMp4aDescByTrack(track *Track) (mp4a *Mp4aDesc) {
} }
return return
} }

View File

@ -1,4 +1,3 @@
package atom package atom
import ( import (
@ -16,7 +15,7 @@ func WriteBytes(w io.Writer, b []byte, n int) (err error) {
func WriteUInt(w io.Writer, val uint, n int) (err error) { func WriteUInt(w io.Writer, val uint, n int) (err error) {
var b [8]byte var b [8]byte
for i := n-1; i >= 0; i-- { for i := n - 1; i >= 0; i-- {
b[i] = byte(val) b[i] = byte(val)
val >>= 8 val >>= 8
} }
@ -26,7 +25,7 @@ func WriteUInt(w io.Writer, val uint, n int) (err error) {
func WriteInt(w io.Writer, val int, n int) (err error) { func WriteInt(w io.Writer, val int, n int) (err error) {
var uval uint var uval uint
if val < 0 { if val < 0 {
uval = uint((1<<uint(n*8))+val) uval = uint((1 << uint(n*8)) + val)
} else { } else {
uval = uint(val) uval = uint(val)
} }
@ -37,7 +36,7 @@ func WriteFixed(w io.Writer, val Fixed, n int) (err error) {
var uval uint var uval uint
if n == 2 { if n == 2 {
uval = uint(val)>>8 uval = uint(val) >> 8
} else if n == 4 { } else if n == 4 {
uval = uint(val) uval = uint(val)
} else { } else {
@ -96,7 +95,7 @@ func (self *Writer) Close() (err error) {
if curPos, err = self.Seek(0, 1); err != nil { if curPos, err = self.Seek(0, 1); err != nil {
return return
} }
if err = RefillInt(self, self.sizePos, int(curPos - self.sizePos), 4); err != nil { if err = RefillInt(self, self.sizePos, int(curPos-self.sizePos), 4); err != nil {
return return
} }
if false { if false {
@ -118,4 +117,3 @@ func WriteAtomHeader(w io.WriteSeeker, cc4 string) (res *Writer, err error) {
res = self res = self
return return
} }

View File

@ -1,20 +1,19 @@
package mp4 package mp4
import ( import (
"github.com/nareix/mp4/atom"
"github.com/nareix/mp4/isom"
"bytes" "bytes"
"fmt" "fmt"
"github.com/nareix/av"
"github.com/nareix/mp4/atom"
"github.com/nareix/mp4/isom"
"io" "io"
) )
type Demuxer struct { type Demuxer struct {
R io.ReadSeeker R io.ReadSeeker
Tracks []*Track
TrackH264 *Track streams []*Stream
TrackAAC *Track movieAtom *atom.Movie
MovieAtom *atom.Movie
} }
func (self *Demuxer) ReadHeader() (err error) { func (self *Demuxer) ReadHeader() (err error) {
@ -52,36 +51,35 @@ func (self *Demuxer) ReadHeader() (err error) {
err = fmt.Errorf("'moov' atom not found") err = fmt.Errorf("'moov' atom not found")
return return
} }
self.MovieAtom = moov self.movieAtom = moov
self.Tracks = []*Track{} self.streams = []*Stream{}
for _, atrack := range(moov.Tracks) { for _, atrack := range moov.Tracks {
track := &Track{ stream := &Stream{
TrackAtom: atrack, trackAtom: atrack,
r: self.R, r: self.R,
} }
if atrack.Media != nil && atrack.Media.Info != nil && atrack.Media.Info.Sample != nil { if atrack.Media != nil && atrack.Media.Info != nil && atrack.Media.Info.Sample != nil {
track.sample = atrack.Media.Info.Sample stream.sample = atrack.Media.Info.Sample
stream.SetTimeScale(int64(atrack.Media.Header.TimeScale))
} else { } else {
err = fmt.Errorf("sample table not found") err = fmt.Errorf("sample table not found")
return return
} }
if record := atom.GetAVCDecoderConfRecordByTrack(atrack); record != nil { if record := atom.GetAVCDecoderConfRecordByTrack(atrack); record != nil {
if len(record.PPS) > 0 { if len(record.PPS) > 0 {
track.pps = record.PPS[0] stream.pps = record.PPS[0]
} }
if len(record.SPS) > 0 { if len(record.SPS) > 0 {
track.sps = record.SPS[0] stream.sps = record.SPS[0]
} }
track.Type = H264 stream.SetType(av.H264)
self.TrackH264 = track self.streams = append(self.streams, stream)
self.Tracks = append(self.Tracks, track)
} else if mp4a := atom.GetMp4aDescByTrack(atrack); mp4a != nil && mp4a.Conf != nil { } else if mp4a := atom.GetMp4aDescByTrack(atrack); mp4a != nil && mp4a.Conf != nil {
if config, err := isom.ReadElemStreamDescAAC(bytes.NewReader(mp4a.Conf.Data)); err == nil { if config, err := isom.ReadElemStreamDescAAC(bytes.NewReader(mp4a.Conf.Data)); err == nil {
track.mpeg4AudioConfig = config.Complete() stream.mpeg4AudioConfig = config.Complete()
track.Type = AAC stream.SetType(av.AAC)
self.TrackAAC = track self.streams = append(self.streams, stream)
self.Tracks = append(self.Tracks, track)
} }
} }
} }
@ -89,16 +87,16 @@ func (self *Demuxer) ReadHeader() (err error) {
return return
} }
func (self *Track) setSampleIndex(index int) (err error) { func (self *Stream) setSampleIndex(index int) (err error) {
found := false found := false
start := 0 start := 0
self.chunkGroupIndex = 0 self.chunkGroupIndex = 0
for self.chunkIndex = range(self.sample.ChunkOffset.Entries) { for self.chunkIndex = range self.sample.ChunkOffset.Entries {
n := self.sample.SampleToChunk.Entries[self.chunkGroupIndex].SamplesPerChunk n := self.sample.SampleToChunk.Entries[self.chunkGroupIndex].SamplesPerChunk
if index >= start && index < start+n { if index >= start && index < start+n {
found = true found = true
self.sampleIndexInChunk = index-start self.sampleIndexInChunk = index - start
break break
} }
start += n start += n
@ -113,14 +111,14 @@ func (self *Track) setSampleIndex(index int) (err error) {
} }
if self.sample.SampleSize.SampleSize != 0 { if self.sample.SampleSize.SampleSize != 0 {
self.sampleOffsetInChunk = int64(self.sampleIndexInChunk*self.sample.SampleSize.SampleSize) self.sampleOffsetInChunk = int64(self.sampleIndexInChunk * self.sample.SampleSize.SampleSize)
} else { } else {
if index >= len(self.sample.SampleSize.Entries) { if index >= len(self.sample.SampleSize.Entries) {
err = io.EOF err = io.EOF
return return
} }
self.sampleOffsetInChunk = int64(0) self.sampleOffsetInChunk = int64(0)
for i := index-self.sampleIndexInChunk; i < index; i++ { for i := index - self.sampleIndexInChunk; i < index; i++ {
self.sampleOffsetInChunk += int64(self.sample.SampleSize.Entries[i]) self.sampleOffsetInChunk += int64(self.sample.SampleSize.Entries[i])
} }
} }
@ -133,12 +131,12 @@ func (self *Track) setSampleIndex(index int) (err error) {
entry := self.sample.TimeToSample.Entries[self.sttsEntryIndex] entry := self.sample.TimeToSample.Entries[self.sttsEntryIndex]
n := entry.Count n := entry.Count
if index >= start && index < start+n { if index >= start && index < start+n {
self.sampleIndexInSttsEntry = index-start self.sampleIndexInSttsEntry = index - start
self.dts += int64((index-start)*entry.Duration) self.dts += int64((index - start) * entry.Duration)
break break
} }
start += n start += n
self.dts += int64(n*entry.Duration) self.dts += int64(n * entry.Duration)
self.sttsEntryIndex++ self.sttsEntryIndex++
} }
if !found { if !found {
@ -153,7 +151,7 @@ func (self *Track) setSampleIndex(index int) (err error) {
for self.cttsEntryIndex < len(self.sample.CompositionOffset.Entries) { for self.cttsEntryIndex < len(self.sample.CompositionOffset.Entries) {
n := self.sample.CompositionOffset.Entries[self.cttsEntryIndex].Count n := self.sample.CompositionOffset.Entries[self.cttsEntryIndex].Count
if index >= start && index < start+n { if index >= start && index < start+n {
self.sampleIndexInCttsEntry = index-start self.sampleIndexInCttsEntry = index - start
break break
} }
start += n start += n
@ -179,7 +177,7 @@ func (self *Track) setSampleIndex(index int) (err error) {
return return
} }
func (self *Track) isSampleValid() bool { func (self *Stream) isSampleValid() bool {
if self.chunkIndex >= len(self.sample.ChunkOffset.Entries) { if self.chunkIndex >= len(self.sample.ChunkOffset.Entries) {
return false return false
} }
@ -207,7 +205,7 @@ func (self *Track) isSampleValid() bool {
return true return true
} }
func (self *Track) incSampleIndex() { func (self *Stream) 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++
@ -252,11 +250,11 @@ func (self *Track) incSampleIndex() {
self.sampleIndex++ self.sampleIndex++
} }
func (self *Track) SampleCount() int { func (self *Stream) SampleCount() int {
if self.sample.SampleSize.SampleSize == 0 { if self.sample.SampleSize.SampleSize == 0 {
chunkGroupIndex := 0 chunkGroupIndex := 0
count := 0 count := 0
for chunkIndex := range(self.sample.ChunkOffset.Entries) { for chunkIndex := range self.sample.ChunkOffset.Entries {
n := self.sample.SampleToChunk.Entries[chunkGroupIndex].SamplesPerChunk n := self.sample.SampleToChunk.Entries[chunkGroupIndex].SamplesPerChunk
count += n count += n
if chunkGroupIndex+1 < len(self.sample.SampleToChunk.Entries) && if chunkGroupIndex+1 < len(self.sample.SampleToChunk.Entries) &&
@ -270,7 +268,11 @@ func (self *Track) SampleCount() int {
} }
} }
func (self *Track) ReadSample() (pts int64, dts int64, isKeyFrame bool, data []byte, err error) { func (self *Demuxer) ReadPacket() (pkt av.Packet, err error) {
return
}
func (self *Stream) readSample() (pts int64, dts int64, isKeyFrame bool, data []byte, err error) {
if !self.isSampleValid() { if !self.isSampleValid() {
err = io.EOF err = io.EOF
return return
@ -284,7 +286,7 @@ func (self *Track) ReadSample() (pts int64, dts int64, isKeyFrame bool, data []b
sampleSize = self.sample.SampleSize.Entries[self.sampleIndex] sampleSize = self.sample.SampleSize.Entries[self.sampleIndex]
} }
sampleOffset := int64(chunkOffset)+self.sampleOffsetInChunk 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
} }
@ -303,7 +305,7 @@ func (self *Track) ReadSample() (pts int64, dts int64, isKeyFrame bool, data []b
//println("pts/dts", self.ptsEntryIndex, self.dtsEntryIndex) //println("pts/dts", self.ptsEntryIndex, self.dtsEntryIndex)
dts = self.dts dts = self.dts
if self.sample.CompositionOffset != nil && len(self.sample.CompositionOffset.Entries) > 0 { if self.sample.CompositionOffset != nil && len(self.sample.CompositionOffset.Entries) > 0 {
pts = self.dts+int64(self.sample.CompositionOffset.Entries[self.cttsEntryIndex].Offset) pts = self.dts + int64(self.sample.CompositionOffset.Entries[self.cttsEntryIndex].Offset)
} else { } else {
pts = dts pts = dts
} }
@ -312,36 +314,28 @@ func (self *Track) ReadSample() (pts int64, dts int64, isKeyFrame bool, data []b
return return
} }
func (self *Track) Duration() float64 { func (self *Stream) 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 float64(total)/float64(self.TrackAtom.Media.Header.TimeScale) return float64(total) / float64(self.trackAtom.Media.Header.TimeScale)
} }
func (self *Track) CurTime() float64 { func (self *Stream) CurSampleIndex() int {
return self.TsToTime(self.dts)
}
func (self *Track) CurTs() int64 {
return self.dts
}
func (self *Track) CurSampleIndex() int {
return self.sampleIndex return self.sampleIndex
} }
func (self *Track) SeekToTime(time float64) error { func (self *Stream) SeekToTime(time float64) error {
index := self.TimeToSampleIndex(time) index := self.TimeToSampleIndex(time)
return self.setSampleIndex(index) return self.setSampleIndex(index)
} }
func (self *Track) SeekToSampleIndex(index int) error { func (self *Stream) SeekToSampleIndex(index int) error {
return self.setSampleIndex(index) return self.setSampleIndex(index)
} }
func (self *Track) TimeToSampleIndex(time float64) int { func (self *Stream) TimeToSampleIndex(time float64) int {
targetTs := self.TimeToTs(time) targetTs := self.TimeToTs(time)
targetIndex := 0 targetIndex := 0
@ -350,11 +344,11 @@ func (self *Track) TimeToSampleIndex(time float64) int {
startIndex := 0 startIndex := 0
endIndex := 0 endIndex := 0
found := false found := false
for _, entry := range(self.sample.TimeToSample.Entries) { for _, entry := range self.sample.TimeToSample.Entries {
endTs = startTs+int64(entry.Count*entry.Duration) endTs = startTs + int64(entry.Count*entry.Duration)
endIndex = startIndex+entry.Count endIndex = startIndex + entry.Count
if targetTs >= startTs && targetTs < endTs { if targetTs >= startTs && targetTs < endTs {
targetIndex = startIndex+int((targetTs-startTs)/int64(entry.Duration)) targetIndex = startIndex + int((targetTs-startTs)/int64(entry.Duration))
found = true found = true
} }
startTs = endTs startTs = endTs
@ -364,15 +358,15 @@ func (self *Track) TimeToSampleIndex(time float64) int {
if targetTs < 0 { if targetTs < 0 {
targetIndex = 0 targetIndex = 0
} else { } else {
targetIndex = endIndex-1 targetIndex = endIndex - 1
} }
} }
if self.sample.SyncSample != nil { if self.sample.SyncSample != nil {
entries := self.sample.SyncSample.Entries entries := self.sample.SyncSample.Entries
for i := len(entries)-1; i >= 0; i-- { for i := len(entries) - 1; i >= 0; i-- {
if entries[i]-1 < targetIndex { if entries[i]-1 < targetIndex {
targetIndex = entries[i]-1 targetIndex = entries[i] - 1
break break
} }
} }
@ -380,24 +374,3 @@ func (self *Track) TimeToSampleIndex(time float64) int {
return targetIndex return targetIndex
} }
func (self *Track) TimeToTs(time float64) int64 {
return int64(time*float64(self.TrackAtom.Media.Header.TimeScale))
}
func (self *Track) TsToTime(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
}

View File

@ -1,11 +1,10 @@
package main package main
import ( import (
"encoding/hex"
"fmt"
"github.com/nareix/mp4" "github.com/nareix/mp4"
"os" "os"
"fmt"
"encoding/hex"
) )
func DemuxExample() { func DemuxExample() {
@ -86,4 +85,3 @@ func main() {
//DemuxExample() //DemuxExample()
MuxExample() MuxExample()
} }

View File

@ -82,7 +82,7 @@ var chanConfigTable = []int{
} }
func IsADTSFrame(frames []byte) bool { func IsADTSFrame(frames []byte) bool {
return len(frames) > 7 && frames[0]==0xff&&frames[1]&0xf0==0xf0 return len(frames) > 7 && frames[0] == 0xff && frames[1]&0xf0 == 0xf0
} }
func ReadADTSFrame(frame []byte) (config MPEG4AudioConfig, payload []byte, samples int, framelen int, err error) { func ReadADTSFrame(frame []byte) (config MPEG4AudioConfig, payload []byte, samples int, framelen int, err error) {
@ -90,11 +90,11 @@ func ReadADTSFrame(frame []byte) (config MPEG4AudioConfig, payload []byte, sampl
err = fmt.Errorf("not adts frame") err = fmt.Errorf("not adts frame")
return return
} }
config.ObjectType = uint(frame[2]>>6)+1 config.ObjectType = uint(frame[2]>>6) + 1
config.SampleRateIndex = uint(frame[2]>>2&0xf) config.SampleRateIndex = uint(frame[2] >> 2 & 0xf)
config.ChannelConfig = uint(frame[2]<<2&0x4|frame[3]>>6&0x3) config.ChannelConfig = uint(frame[2]<<2&0x4 | frame[3]>>6&0x3)
framelen = int(frame[3]&0x3)<<11|int(frame[4])<<3|int(frame[5]>>5) framelen = int(frame[3]&0x3)<<11 | int(frame[4])<<3 | int(frame[5]>>5)
samples = (int(frame[6]&0x3)+1)*1024 samples = (int(frame[6]&0x3) + 1) * 1024
hdrlen := 7 hdrlen := 7
if frame[1]&0x1 == 0 { if frame[1]&0x1 == 0 {
hdrlen = 9 hdrlen = 9
@ -110,16 +110,16 @@ func ReadADTSFrame(frame []byte) (config MPEG4AudioConfig, payload []byte, sampl
func MakeADTSHeader(config MPEG4AudioConfig, samples int, payloadLength int) (header []byte) { func MakeADTSHeader(config MPEG4AudioConfig, samples int, payloadLength int) (header []byte) {
payloadLength += 7 payloadLength += 7
//AAAAAAAA AAAABCCD EEFFFFGH HHIJKLMM MMMMMMMM MMMOOOOO OOOOOOPP (QQQQQQQQ QQQQQQQQ) //AAAAAAAA AAAABCCD EEFFFFGH HHIJKLMM MMMMMMMM MMMOOOOO OOOOOOPP (QQQQQQQQ QQQQQQQQ)
header = []byte{0xff,0xf1,0x50,0x80,0x043,0xff,0xcd} header = []byte{0xff, 0xf1, 0x50, 0x80, 0x043, 0xff, 0xcd}
//config.ObjectType = uint(frames[2]>>6)+1 //config.ObjectType = uint(frames[2]>>6)+1
//config.SampleRateIndex = uint(frames[2]>>2&0xf) //config.SampleRateIndex = uint(frames[2]>>2&0xf)
//config.ChannelConfig = uint(frames[2]<<2&0x4|frames[3]>>6&0x3) //config.ChannelConfig = uint(frames[2]<<2&0x4|frames[3]>>6&0x3)
header[2] = (byte(config.ObjectType-1)&0x3)<<6|(byte(config.SampleRateIndex)&0xf)<<2|byte(config.ChannelConfig>>2)&0x1 header[2] = (byte(config.ObjectType-1)&0x3)<<6 | (byte(config.SampleRateIndex)&0xf)<<2 | byte(config.ChannelConfig>>2)&0x1
header[3] = header[3]&0x3f|byte(config.ChannelConfig&0x3)<<6 header[3] = header[3]&0x3f | byte(config.ChannelConfig&0x3)<<6
header[3] = header[3]&0xfc|byte(payloadLength>>11)&0x3 header[3] = header[3]&0xfc | byte(payloadLength>>11)&0x3
header[4] = byte(payloadLength>>3) header[4] = byte(payloadLength >> 3)
header[5] = header[5]&0x1f|(byte(payloadLength)&0x7)<<5 header[5] = header[5]&0x1f | (byte(payloadLength)&0x7)<<5
header[6] = header[6]&0xfc|byte(samples/1024-1) header[6] = header[6]&0xfc | byte(samples/1024-1)
return return
} }

View File

@ -46,7 +46,7 @@ func TestReadElemStreamDesc(t *testing.T) {
t.Logf("%v n=%d", aconfig.Complete(), n) t.Logf("%v n=%d", aconfig.Complete(), n)
data = MakeADTSHeader(aconfig, 1024*3, 33) data = MakeADTSHeader(aconfig, 1024*3, 33)
data = append(data, []byte{1,2,3,4,5}...) data = append(data, []byte{1, 2, 3, 4, 5}...)
t.Logf("%x", data) t.Logf("%x", data)
aconfig, _, n, framelen, err = ReadADTSFrame(data) aconfig, _, n, framelen, err = ReadADTSFrame(data)
t.Logf("%v n=%d framelen=%d err=%v", aconfig.Complete(), n, framelen, err) t.Logf("%v n=%d framelen=%d err=%v", aconfig.Complete(), n, framelen, err)

477
muxer.go
View File

@ -1,57 +1,54 @@
package mp4 package mp4
import ( import (
"bytes"
"fmt"
"github.com/nareix/av"
"github.com/nareix/mp4/atom" "github.com/nareix/mp4/atom"
"github.com/nareix/mp4/isom" "github.com/nareix/mp4/isom"
"io" "io"
"bytes"
"fmt"
) )
type Muxer struct { type Muxer struct {
W io.WriteSeeker W io.WriteSeeker
Tracks []*Track streams []*Stream
TrackH264 *Track
TrackAAC *Track
mdatWriter *atom.Writer mdatWriter *atom.Writer
} }
func (self *Muxer) newTrack() *Track { func (self *Muxer) NewStream() av.Stream {
track := &Track{} stream := &Stream{}
track.sample = &atom.SampleTable{ stream.sample = &atom.SampleTable{
SampleDesc: &atom.SampleDesc{}, SampleDesc: &atom.SampleDesc{},
TimeToSample: &atom.TimeToSample{}, TimeToSample: &atom.TimeToSample{},
SampleToChunk: &atom.SampleToChunk{ SampleToChunk: &atom.SampleToChunk{
Entries: []atom.SampleToChunkEntry{ Entries: []atom.SampleToChunkEntry{
{ {
FirstChunk: 1, FirstChunk: 1,
SampleDescId: 1, SampleDescId: 1,
SamplesPerChunk: 1, SamplesPerChunk: 1,
}, },
}, },
}, },
SampleSize: &atom.SampleSize{}, SampleSize: &atom.SampleSize{},
ChunkOffset: &atom.ChunkOffset{}, ChunkOffset: &atom.ChunkOffset{},
} }
track.TrackAtom = &atom.Track{ stream.trackAtom = &atom.Track{
Header: &atom.TrackHeader{ Header: &atom.TrackHeader{
TrackId: len(self.Tracks)+1, TrackId: len(self.streams) + 1,
Flags: 0x0003, // Track enabled | Track in movie Flags: 0x0003, // Track enabled | Track in movie
Duration: 0, // fill later Duration: 0, // fill later
Matrix: [9]int{0x10000, 0, 0, 0, 0x10000, 0, 0, 0, 0x40000000}, Matrix: [9]int{0x10000, 0, 0, 0, 0x10000, 0, 0, 0, 0x40000000},
}, },
Media: &atom.Media{ Media: &atom.Media{
Header: &atom.MediaHeader{ Header: &atom.MediaHeader{
TimeScale: 0, // fill later TimeScale: 0, // fill later
Duration: 0, // fill later Duration: 0, // fill later
Language: 21956, Language: 21956,
}, },
Info: &atom.MediaInfo{ Info: &atom.MediaInfo{
Sample: track.sample, Sample: stream.sample,
Data: &atom.DataInfo{ Data: &atom.DataInfo{
Refer: &atom.DataRefer{ Refer: &atom.DataRefer{
Url: &atom.DataReferUrl{ Url: &atom.DataReferUrl{
@ -63,57 +60,86 @@ func (self *Muxer) newTrack() *Track {
}, },
} }
track.writeMdat = self.writeMdat stream.writeMdat = self.writeMdat
self.Tracks = append(self.Tracks, track) self.streams = append(self.streams, stream)
return track return stream
} }
func (self *Muxer) AddAACTrack() (track *Track) { func (self *Stream) fillTrackAtom() (err error) {
track = self.newTrack() if self.sampleIndex > 0 {
self.TrackAAC = track self.sttsEntry.Count++
track.Type = AAC
track.sample.SampleDesc.Mp4aDesc = &atom.Mp4aDesc{
DataRefIdx: 1,
NumberOfChannels: 0, // fill later
SampleSize: 0, // fill later
SampleRate: 0, // fill later
Conf: &atom.ElemStreamDesc{},
} }
track.TrackAtom.Header.Volume = atom.IntToFixed(1)
track.TrackAtom.Header.AlternateGroup = 1
track.TrackAtom.Media.Handler = &atom.HandlerRefer{
SubType: "soun",
Name: "Sound Handler",
}
track.TrackAtom.Media.Info.Sound = &atom.SoundMediaInfo{}
return
}
func (self *Muxer) AddH264Track() (track *Track) { self.trackAtom.Media.Header.TimeScale = int(self.TimeScale())
track = self.newTrack() self.trackAtom.Media.Header.Duration = int(self.lastDts)
self.TrackH264 = track
track.Type = H264 if self.Type() == av.H264 {
track.sample.SampleDesc.Avc1Desc = &atom.Avc1Desc{ self.sample.SampleDesc.Avc1Desc.Conf.Record, err = atom.CreateAVCDecoderConfRecord(
DataRefIdx: 1, self.sps,
HorizontalResolution: 72, self.pps,
VorizontalResolution: 72, )
Width: 0, // fill later if err != nil {
Height: 0, // fill later return
FrameCount: 1, }
Depth: 24, var info *atom.H264SPSInfo
ColorTableId: -1, if info, err = atom.ParseH264SPS(self.sps[1:]); err != nil {
Conf: &atom.Avc1Conf{}, return
} }
track.sample.SyncSample = &atom.SyncSample{} self.sample.SampleDesc.Avc1Desc = &atom.Avc1Desc{
track.TrackAtom.Media.Handler = &atom.HandlerRefer{ DataRefIdx: 1,
SubType: "vide", HorizontalResolution: 72,
Name: "Video Media Handler", VorizontalResolution: 72,
} Width: int(info.Width),
track.sample.CompositionOffset = &atom.CompositionOffset{} Height: int(info.Height),
track.TrackAtom.Media.Info.Video = &atom.VideoMediaInfo{ FrameCount: 1,
Flags: 0x000001, Depth: 24,
ColorTableId: -1,
Conf: &atom.Avc1Conf{},
}
self.sample.SyncSample = &atom.SyncSample{}
self.trackAtom.Media.Handler = &atom.HandlerRefer{
SubType: "vide",
Name: "Video Media Handler",
}
self.sample.CompositionOffset = &atom.CompositionOffset{}
self.trackAtom.Media.Info.Video = &atom.VideoMediaInfo{
Flags: 0x000001,
}
self.trackAtom.Header.TrackWidth = atom.IntToFixed(int(info.Width))
self.trackAtom.Header.TrackHeight = atom.IntToFixed(int(info.Height))
} else if self.Type() == av.AAC {
if !self.mpeg4AudioConfig.IsValid() {
err = fmt.Errorf("invalie MPEG4AudioConfig")
return
}
buf := &bytes.Buffer{}
config := self.mpeg4AudioConfig.Complete()
if err = isom.WriteElemStreamDescAAC(buf, config, uint(self.trackAtom.Header.TrackId)); err != nil {
return
}
self.sample.SampleDesc.Mp4aDesc = &atom.Mp4aDesc{
DataRefIdx: 1,
NumberOfChannels: config.ChannelCount,
SampleSize: config.ChannelCount * 8,
SampleRate: atom.IntToFixed(config.SampleRate),
Conf: &atom.ElemStreamDesc{
Data: buf.Bytes(),
},
}
self.trackAtom.Header.Volume = atom.IntToFixed(1)
self.trackAtom.Header.AlternateGroup = 1
self.trackAtom.Media.Handler = &atom.HandlerRefer{
SubType: "soun",
Name: "Sound Handler",
}
self.trackAtom.Media.Info.Sound = &atom.SoundMediaInfo{}
} else {
err = fmt.Errorf("please specify stream type")
} }
return return
} }
@ -132,22 +158,12 @@ func (self *Muxer) WriteHeader() (err error) {
return return
} }
func (self *Track) SetH264PPSAndSPS(pps, sps []byte) { func (self *Muxer) WriteSample(pkt av.Packet) (err error) {
self.pps, self.sps = pps, sps pts, dts, isKeyFrame, frame := pkt.Pts, pkt.Dts, pkt.IsKeyFrame, pkt.Data
} stream := self.streams[pkt.StreamIdx]
func (self *Track) SetMPEG4AudioConfig(config isom.MPEG4AudioConfig) { if stream.Type() == av.AAC && isom.IsADTSFrame(frame) {
self.mpeg4AudioConfig = config config := stream.mpeg4AudioConfig.Complete()
}
func (self *Track) SetTimeScale(timeScale int64) {
self.TrackAtom.Media.Header.TimeScale = int(timeScale)
return
}
func (self *Track) WriteSample(pts int64, dts int64, isKeyFrame bool, frame []byte) (err error) {
if self.Type == AAC && isom.IsADTSFrame(frame) {
config := self.mpeg4AudioConfig.Complete()
if config.SampleRate == 0 { if config.SampleRate == 0 {
err = fmt.Errorf("invalid sample rate") err = fmt.Errorf("invalid sample rate")
return return
@ -159,19 +175,20 @@ func (self *Track) WriteSample(pts int64, dts int64, isKeyFrame bool, frame []by
if _, payload, samples, framelen, err = isom.ReadADTSFrame(frame); err != nil { if _, payload, samples, framelen, err = isom.ReadADTSFrame(frame); err != nil {
return return
} }
delta := int64(samples)*self.TimeScale()/int64(config.SampleRate) delta := int64(samples) * stream.TimeScale() / int64(config.SampleRate)
pts += delta pts += delta
dts += delta dts += delta
frame = frame[framelen:] frame = frame[framelen:]
if self.writeSample(pts, dts, isKeyFrame, payload); err != nil { if stream.writeSample(pts, dts, isKeyFrame, payload); err != nil {
return return
} }
} }
} }
return self.writeSample(pts, dts, isKeyFrame, frame)
return stream.writeSample(pts, dts, isKeyFrame, frame)
} }
func (self *Track) writeSample(pts int64, dts int64, isKeyFrame bool, data []byte) (err error) { func (self *Stream) writeSample(pts int64, dts int64, isKeyFrame bool, data []byte) (err error) {
var filePos int64 var filePos int64
sampleSize := len(data) sampleSize := len(data)
if filePos, err = self.writeMdat(data); err != nil { if filePos, err = self.writeMdat(data); err != nil {
@ -187,7 +204,7 @@ func (self *Track) writeSample(pts int64, dts int64, isKeyFrame bool, data []byt
err = fmt.Errorf("dts must be incremental") err = fmt.Errorf("dts must be incremental")
return return
} }
duration := int(dts-self.lastDts) duration := int(dts - self.lastDts)
if self.sttsEntry == nil || duration != self.sttsEntry.Duration { if self.sttsEntry == nil || duration != self.sttsEntry.Duration {
self.sample.TimeToSample.Entries = append(self.sample.TimeToSample.Entries, atom.TimeToSampleEntry{Duration: duration}) self.sample.TimeToSample.Entries = append(self.sample.TimeToSample.Entries, atom.TimeToSampleEntry{Duration: duration})
self.sttsEntry = &self.sample.TimeToSample.Entries[len(self.sample.TimeToSample.Entries)-1] self.sttsEntry = &self.sample.TimeToSample.Entries[len(self.sample.TimeToSample.Entries)-1]
@ -200,7 +217,7 @@ func (self *Track) writeSample(pts int64, dts int64, isKeyFrame bool, data []byt
err = fmt.Errorf("pts must greater than dts") err = fmt.Errorf("pts must greater than dts")
return return
} }
offset := int(pts-dts) offset := int(pts - dts)
if self.cttsEntry == nil || offset != self.cttsEntry.Offset { if self.cttsEntry == nil || offset != self.cttsEntry.Offset {
table := self.sample.CompositionOffset table := self.sample.CompositionOffset
table.Entries = append(table.Entries, atom.CompositionOffsetEntry{Offset: offset}) table.Entries = append(table.Entries, atom.CompositionOffsetEntry{Offset: offset})
@ -217,71 +234,30 @@ func (self *Track) writeSample(pts int64, dts int64, isKeyFrame bool, data []byt
return return
} }
func (self *Track) fillTrackAtom() (err error) {
if self.sampleIndex > 0 {
self.sttsEntry.Count++
}
if self.Type == H264 {
self.sample.SampleDesc.Avc1Desc.Conf.Record, err = atom.CreateAVCDecoderConfRecord(
self.sps,
self.pps,
)
if err != nil {
return
}
var info *atom.H264SPSInfo
if info, err = atom.ParseH264SPS(self.sps[1:]); err != nil {
return
}
self.sample.SampleDesc.Avc1Desc.Width = int(info.Width)
self.sample.SampleDesc.Avc1Desc.Height = int(info.Height)
self.TrackAtom.Header.TrackWidth = atom.IntToFixed(int(info.Width))
self.TrackAtom.Header.TrackHeight = atom.IntToFixed(int(info.Height))
self.TrackAtom.Media.Header.Duration = int(self.lastDts)
} else if self.Type == AAC {
if !self.mpeg4AudioConfig.IsValid() {
err = fmt.Errorf("invalie MPEG4AudioConfig")
return
}
buf := &bytes.Buffer{}
config := self.mpeg4AudioConfig.Complete()
if err = isom.WriteElemStreamDescAAC(buf, config, uint(self.TrackAtom.Header.TrackId)); err != nil {
return
}
self.sample.SampleDesc.Mp4aDesc.Conf.Data = buf.Bytes()
self.sample.SampleDesc.Mp4aDesc.NumberOfChannels = config.ChannelCount
self.sample.SampleDesc.Mp4aDesc.SampleSize = config.ChannelCount*8
self.sample.SampleDesc.Mp4aDesc.SampleRate = atom.IntToFixed(config.SampleRate)
self.TrackAtom.Media.Header.Duration = int(self.lastDts)
}
return
}
func (self *Muxer) WriteTrailer() (err error) { func (self *Muxer) WriteTrailer() (err error) {
moov := &atom.Movie{} moov := &atom.Movie{}
moov.Header = &atom.MovieHeader{ moov.Header = &atom.MovieHeader{
PreferredRate: atom.IntToFixed(1), PreferredRate: atom.IntToFixed(1),
PreferredVolume: atom.IntToFixed(1), PreferredVolume: atom.IntToFixed(1),
Matrix: [9]int{0x10000, 0, 0, 0, 0x10000, 0, 0, 0, 0x40000000}, Matrix: [9]int{0x10000, 0, 0, 0, 0x10000, 0, 0, 0, 0x40000000},
NextTrackId: 2, NextTrackId: 2,
} }
maxDur := float64(0) maxDur := float64(0)
timeScale := 10000 timeScale := 10000
for _, track := range(self.Tracks) { for _, stream := range self.streams {
if err = track.fillTrackAtom(); err != nil { if err = stream.fillTrackAtom(); err != nil {
return return
} }
dur := track.Duration() dur := stream.Duration()
track.TrackAtom.Header.Duration = int(float64(timeScale)*dur) stream.trackAtom.Header.Duration = int(float64(timeScale) * dur)
if dur > maxDur { if dur > maxDur {
maxDur = dur maxDur = dur
} }
moov.Tracks = append(moov.Tracks, track.TrackAtom) moov.Tracks = append(moov.Tracks, stream.trackAtom)
} }
moov.Header.TimeScale = timeScale moov.Header.TimeScale = timeScale
moov.Header.Duration = int(float64(timeScale)*maxDur) moov.Header.Duration = int(float64(timeScale) * maxDur)
if err = self.mdatWriter.Close(); err != nil { if err = self.mdatWriter.Close(); err != nil {
return return
@ -292,226 +268,3 @@ func (self *Muxer) WriteTrailer() (err error) {
return return
} }
/*
type SimpleH264Writer struct {
W io.WriteSeeker
TimeScale int
sps []byte
pps []byte
Width int
Height int
duration int
sample *atom.SampleTable
sampleToChunk *atom.SampleToChunkEntry
sampleIdx int
timeToSample *atom.TimeToSampleEntry
mdatWriter *atom.Writer
}
func (self *SimpleH264Writer) prepare() (err error) {
if self.mdatWriter, err = atom.WriteAtomHeader(self.W, "mdat"); err != nil {
return
}
if len(self.sps) == 0 {
err = fmt.Errorf("invalid sps")
return
}
if len(self.pps) == 0 {
err = fmt.Errorf("invalid pps")
return
}
if self.Width == 0 || self.Height == 0 {
var info *atom.H264spsInfo
if info, err = atom.ParseH264sps(self.sps[1:]); err != nil {
return
}
self.Width = int(info.Width)
self.Height = int(info.Height)
}
self.sampleIdx = 1
self.sample = &atom.SampleTable{
SampleDesc: &atom.SampleDesc{
Avc1Desc: &atom.Avc1Desc{
DataRefIdx: 1,
HorizontalResolution: 72,
VorizontalResolution: 72,
Width: self.Width,
Height: self.Height,
FrameCount: 1,
Depth: 24,
ColorTableId: -1,
Conf: &atom.Avc1Conf{},
},
},
TimeToSample: &atom.TimeToSample{},
SampleToChunk: &atom.SampleToChunk{
Entries: []atom.SampleToChunkEntry{
{
FirstChunk: 1,
SampleDescId: 1,
},
},
},
SampleSize: &atom.SampleSize{},
ChunkOffset: &atom.ChunkOffset{
Entries: []int{8},
},
SyncSample: &atom.SyncSample{},
}
self.sampleToChunk = &self.sample.SampleToChunk.Entries[0]
return
}
func (self *SimpleH264Writer) WriteSample(sync bool, duration int, data []byte) (err error) {
return self.writeSample(false, sync, duration, data)
}
func (self *SimpleH264Writer) WriteNALU(sync bool, duration int, data []byte) (err error) {
return self.writeSample(true, sync, duration, data)
}
func splitNALUByStartCode(data []byte) (out [][]byte) {
last := 0
for i := 0; i < len(data)-3; {
if data[i] == 0 && data[i+1] == 0 && data[i+2] == 1 {
out = append(out, data[last:i])
i += 3
last = i
} else {
i++
}
}
out = append(out, data[last:])
return
}
func (self *SimpleH264Writer) writeSample(isNALU, sync bool, duration int, data []byte) (err error) {
if self.mdatWriter == nil {
if err = self.prepare(); err != nil {
return
}
}
var sampleSize int
if isNALU {
if sampleSize, err = atom.WriteSampleByNALU(self.mdatWriter, data); err != nil {
return
}
} else {
sampleSize = len(data)
if _, err = self.mdatWriter.Write(data); err != nil {
return
}
}
if sync {
self.sample.SyncSample.Entries = append(self.sample.SyncSample.Entries, self.sampleIdx)
}
if self.timeToSample != nil && duration != self.timeToSample.Duration {
self.sample.TimeToSample.Entries = append(self.sample.TimeToSample.Entries, *self.timeToSample)
self.timeToSample = nil
}
if self.timeToSample == nil {
self.timeToSample = &atom.TimeToSampleEntry{
Duration: duration,
}
}
self.duration += duration
self.sampleIdx++
self.timeToSample.Count++
self.sampleToChunk.SamplesPerChunk++
self.sample.SampleSize.Entries = append(self.sample.SampleSize.Entries, sampleSize)
return
}
func (self *SimpleH264Writer) Finish() (err error) {
self.sample.SampleDesc.Avc1Desc.Conf.Record, err = atom.CreateAVCDecoderConfRecord(
self.sps,
self.pps,
)
if err != nil {
return
}
if self.timeToSample != nil {
self.sample.TimeToSample.Entries = append(
self.sample.TimeToSample.Entries,
*self.timeToSample,
)
}
if err = self.mdatWriter.Close(); err != nil {
return
}
moov := &atom.Movie{}
moov.Header = &atom.MovieHeader{
TimeScale: self.TimeScale,
Duration: self.duration,
PreferredRate: atom.IntToFixed(1),
PreferredVolume: atom.IntToFixed(1),
Matrix: [9]int{0x10000, 0, 0, 0, 0x10000, 0, 0, 0, 0x40000000},
NextTrackId: 2,
}
track := &atom.Track{
Header: &atom.TrackHeader{
TrackId: 1,
Flags: 0x0003, // Track enabled | Track in movie
Duration: self.duration,
Volume: atom.IntToFixed(1),
Matrix: [9]int{0x10000, 0, 0, 0, 0x10000, 0, 0, 0, 0x40000000},
TrackWidth: atom.IntToFixed(self.Width),
TrackHeight: atom.IntToFixed(self.Height),
},
Media: &atom.Media{
Header: &atom.MediaHeader{
TimeScale: self.TimeScale,
Duration: self.duration,
},
Info: &atom.MediaInfo{
Video: &atom.VideoMediaInfo{
Flags: 0x000001,
},
Sample: self.sample,
Data: &atom.DataInfo{
Refer: &atom.DataRefer{
Url: &atom.DataReferUrl{
Flags: 0x000001, // Self reference
},
},
},
},
Handler: &atom.HandlerRefer{
SubType: "vide",
Name: "Video Media Handler",
},
},
}
moov.Tracks = append(moov.Tracks, track)
if err = atom.WriteMovie(self.W, moov); err != nil {
return
}
return
}
*/

View File

@ -1,47 +0,0 @@
package mp4
import (
"github.com/nareix/mp4/atom"
"github.com/nareix/mp4/isom"
"io"
)
const (
H264 = 1
AAC = 2
)
type Track struct {
Type int
TrackAtom *atom.Track
r io.ReadSeeker
sps []byte
pps []byte
mpeg4AudioConfig isom.MPEG4AudioConfig
sample *atom.SampleTable
sampleIndex int
sampleOffsetInChunk int64
syncSampleIndex int
dts int64
sttsEntryIndex int
sampleIndexInSttsEntry int
cttsEntryIndex int
sampleIndexInCttsEntry int
chunkGroupIndex int
chunkIndex int
sampleIndexInChunk int
sttsEntry *atom.TimeToSampleEntry
cttsEntry *atom.CompositionOffsetEntry
writeMdat func ([]byte) (int64,error)
lastDts int64
}