replace Track to Stream
This commit is contained in:
parent
371d0f13e3
commit
07ed4e9098
@ -1,3 +1 @@
|
|||||||
|
|
||||||
package atom
|
package atom
|
||||||
|
|
||||||
|
@ -1,11 +1,10 @@
|
|||||||
|
|
||||||
package atom
|
package atom
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"io"
|
|
||||||
"fmt"
|
|
||||||
"strings"
|
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Walker interface {
|
type Walker interface {
|
||||||
@ -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)))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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 {
|
||||||
@ -617,4 +617,3 @@ func WalkTrackFragDecodeTime(w Walker, self *TrackFragDecodeTime) {
|
|||||||
w.EndStruct()
|
w.EndStruct()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
|
|
||||||
package atom
|
package atom
|
||||||
|
|
||||||
import (
|
import (
|
||||||
@ -114,4 +113,3 @@ func ReadAtomHeader(r io.Reader, targetCC4 string) (res *io.LimitedReader, cc4 s
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -567,7 +567,7 @@ 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
|
||||||
}
|
}
|
||||||
@ -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")
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
|
|
||||||
package atom
|
package atom
|
||||||
|
|
||||||
type Fixed uint32
|
type Fixed uint32
|
||||||
@ -11,4 +10,3 @@ func IntToFixed(val int) Fixed {
|
|||||||
func FixedToInt(val Fixed) int {
|
func FixedToInt(val Fixed) int {
|
||||||
return int(val >> 16)
|
return int(val >> 16)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
|
|
||||||
package atom
|
package atom
|
||||||
|
|
||||||
import (
|
import (
|
||||||
@ -118,4 +117,3 @@ func WriteAtomHeader(w io.WriteSeeker, cc4 string) (res *Writer, err error) {
|
|||||||
res = self
|
res = self
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
105
demuxer.go
105
demuxer.go
@ -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,12 +87,12 @@ 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
|
||||||
@ -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
|
||||||
@ -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,7 +344,7 @@ 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 {
|
||||||
@ -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
|
|
||||||
}
|
|
||||||
|
|
||||||
|
@ -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()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
419
muxer.go
419
muxer.go
@ -1,27 +1,24 @@
|
|||||||
|
|
||||||
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{
|
||||||
@ -37,9 +34,9 @@ func (self *Muxer) newTrack() *Track {
|
|||||||
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},
|
||||||
@ -51,7 +48,7 @@ func (self *Muxer) newTrack() *Track {
|
|||||||
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
|
self.trackAtom.Media.Header.TimeScale = int(self.TimeScale())
|
||||||
track.TrackAtom.Media.Handler = &atom.HandlerRefer{
|
self.trackAtom.Media.Header.Duration = int(self.lastDts)
|
||||||
SubType: "soun",
|
|
||||||
Name: "Sound Handler",
|
if self.Type() == av.H264 {
|
||||||
}
|
self.sample.SampleDesc.Avc1Desc.Conf.Record, err = atom.CreateAVCDecoderConfRecord(
|
||||||
track.TrackAtom.Media.Info.Sound = &atom.SoundMediaInfo{}
|
self.sps,
|
||||||
|
self.pps,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
var info *atom.H264SPSInfo
|
||||||
func (self *Muxer) AddH264Track() (track *Track) {
|
if info, err = atom.ParseH264SPS(self.sps[1:]); err != nil {
|
||||||
track = self.newTrack()
|
return
|
||||||
self.TrackH264 = track
|
}
|
||||||
track.Type = H264
|
self.sample.SampleDesc.Avc1Desc = &atom.Avc1Desc{
|
||||||
track.sample.SampleDesc.Avc1Desc = &atom.Avc1Desc{
|
|
||||||
DataRefIdx: 1,
|
DataRefIdx: 1,
|
||||||
HorizontalResolution: 72,
|
HorizontalResolution: 72,
|
||||||
VorizontalResolution: 72,
|
VorizontalResolution: 72,
|
||||||
Width: 0, // fill later
|
Width: int(info.Width),
|
||||||
Height: 0, // fill later
|
Height: int(info.Height),
|
||||||
FrameCount: 1,
|
FrameCount: 1,
|
||||||
Depth: 24,
|
Depth: 24,
|
||||||
ColorTableId: -1,
|
ColorTableId: -1,
|
||||||
Conf: &atom.Avc1Conf{},
|
Conf: &atom.Avc1Conf{},
|
||||||
}
|
}
|
||||||
track.sample.SyncSample = &atom.SyncSample{}
|
self.sample.SyncSample = &atom.SyncSample{}
|
||||||
track.TrackAtom.Media.Handler = &atom.HandlerRefer{
|
self.trackAtom.Media.Handler = &atom.HandlerRefer{
|
||||||
SubType: "vide",
|
SubType: "vide",
|
||||||
Name: "Video Media Handler",
|
Name: "Video Media Handler",
|
||||||
}
|
}
|
||||||
track.sample.CompositionOffset = &atom.CompositionOffset{}
|
self.sample.CompositionOffset = &atom.CompositionOffset{}
|
||||||
track.TrackAtom.Media.Info.Video = &atom.VideoMediaInfo{
|
self.trackAtom.Media.Info.Video = &atom.VideoMediaInfo{
|
||||||
Flags: 0x000001,
|
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 {
|
||||||
@ -217,47 +234,6 @@ 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{
|
||||||
@ -269,16 +245,16 @@ func (self *Muxer) WriteTrailer() (err error) {
|
|||||||
|
|
||||||
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)
|
||||||
@ -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
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
47
track.go
47
track.go
@ -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
|
|
||||||
}
|
|
||||||
|
|
Loading…
x
Reference in New Issue
Block a user