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

View File

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

View File

@ -3,8 +3,8 @@ package atom
import (
"bytes"
"fmt"
"io"
"github.com/nareix/bits"
"io"
)
type H264SPSInfo struct {
@ -431,7 +431,7 @@ func ReadTrackFragHeader(r *io.LimitedReader) (res *TrackFragHeader, err error)
}
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
}
}
@ -617,4 +617,3 @@ func WalkTrackFragDecodeTime(w Walker, self *TrackFragDecodeTime) {
w.EndStruct()
return
}

View File

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

View File

@ -567,7 +567,7 @@ func WalkTrackHeader(w Walker, self *TrackHeader) {
type HandlerRefer struct {
Version int
Flags int
Type string
CodecType string
SubType 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 {
return
}
if self.Type, err = ReadString(r, 4); err != nil {
if self.CodecType, err = ReadString(r, 4); err != nil {
return
}
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 {
return
}
if err = WriteString(w, self.Type, 4); err != nil {
if err = WriteString(w, self.CodecType, 4); err != nil {
return
}
if err = WriteString(w, self.SubType, 4); err != nil {
@ -628,7 +628,7 @@ func WalkHandlerRefer(w Walker, self *HandlerRefer) {
w.Name("Flags")
w.Int(self.Flags)
w.Name("Type")
w.String(self.Type)
w.String(self.CodecType)
w.Name("SubType")
w.String(self.SubType)
w.Name("Name")

View File

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

View File

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

View File

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

View File

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

View File

@ -82,7 +82,7 @@ var chanConfigTable = []int{
}
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) {
@ -90,11 +90,11 @@ func ReadADTSFrame(frame []byte) (config MPEG4AudioConfig, payload []byte, sampl
err = fmt.Errorf("not adts frame")
return
}
config.ObjectType = uint(frame[2]>>6)+1
config.SampleRateIndex = uint(frame[2]>>2&0xf)
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)
samples = (int(frame[6]&0x3)+1)*1024
config.ObjectType = uint(frame[2]>>6) + 1
config.SampleRateIndex = uint(frame[2] >> 2 & 0xf)
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)
samples = (int(frame[6]&0x3) + 1) * 1024
hdrlen := 7
if frame[1]&0x1 == 0 {
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) {
payloadLength += 7
//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.SampleRateIndex = uint(frames[2]>>2&0xf)
//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[3] = header[3]&0x3f|byte(config.ChannelConfig&0x3)<<6
header[3] = header[3]&0xfc|byte(payloadLength>>11)&0x3
header[4] = byte(payloadLength>>3)
header[5] = header[5]&0x1f|(byte(payloadLength)&0x7)<<5
header[6] = header[6]&0xfc|byte(samples/1024-1)
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]&0xfc | byte(payloadLength>>11)&0x3
header[4] = byte(payloadLength >> 3)
header[5] = header[5]&0x1f | (byte(payloadLength)&0x7)<<5
header[6] = header[6]&0xfc | byte(samples/1024-1)
return
}

View File

@ -46,7 +46,7 @@ func TestReadElemStreamDesc(t *testing.T) {
t.Logf("%v n=%d", aconfig.Complete(), n)
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)
aconfig, _, n, framelen, err = ReadADTSFrame(data)
t.Logf("%v n=%d framelen=%d err=%v", aconfig.Complete(), n, framelen, err)

427
muxer.go
View File

@ -1,27 +1,24 @@
package mp4
import (
"bytes"
"fmt"
"github.com/nareix/av"
"github.com/nareix/mp4/atom"
"github.com/nareix/mp4/isom"
"io"
"bytes"
"fmt"
)
type Muxer struct {
W io.WriteSeeker
Tracks []*Track
TrackH264 *Track
TrackAAC *Track
streams []*Stream
mdatWriter *atom.Writer
}
func (self *Muxer) newTrack() *Track {
track := &Track{}
func (self *Muxer) NewStream() av.Stream {
stream := &Stream{}
track.sample = &atom.SampleTable{
stream.sample = &atom.SampleTable{
SampleDesc: &atom.SampleDesc{},
TimeToSample: &atom.TimeToSample{},
SampleToChunk: &atom.SampleToChunk{
@ -37,9 +34,9 @@ func (self *Muxer) newTrack() *Track {
ChunkOffset: &atom.ChunkOffset{},
}
track.TrackAtom = &atom.Track{
stream.trackAtom = &atom.Track{
Header: &atom.TrackHeader{
TrackId: len(self.Tracks)+1,
TrackId: len(self.streams) + 1,
Flags: 0x0003, // Track enabled | Track in movie
Duration: 0, // fill later
Matrix: [9]int{0x10000, 0, 0, 0, 0x10000, 0, 0, 0, 0x40000000},
@ -51,7 +48,7 @@ func (self *Muxer) newTrack() *Track {
Language: 21956,
},
Info: &atom.MediaInfo{
Sample: track.sample,
Sample: stream.sample,
Data: &atom.DataInfo{
Refer: &atom.DataRefer{
Url: &atom.DataReferUrl{
@ -63,57 +60,86 @@ func (self *Muxer) newTrack() *Track {
},
}
track.writeMdat = self.writeMdat
self.Tracks = append(self.Tracks, track)
stream.writeMdat = self.writeMdat
self.streams = append(self.streams, stream)
return track
return stream
}
func (self *Muxer) AddAACTrack() (track *Track) {
track = self.newTrack()
self.TrackAAC = track
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{},
func (self *Stream) fillTrackAtom() (err error) {
if self.sampleIndex > 0 {
self.sttsEntry.Count++
}
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{}
self.trackAtom.Media.Header.TimeScale = int(self.TimeScale())
self.trackAtom.Media.Header.Duration = int(self.lastDts)
if self.Type() == av.H264 {
self.sample.SampleDesc.Avc1Desc.Conf.Record, err = atom.CreateAVCDecoderConfRecord(
self.sps,
self.pps,
)
if err != nil {
return
}
func (self *Muxer) AddH264Track() (track *Track) {
track = self.newTrack()
self.TrackH264 = track
track.Type = H264
track.sample.SampleDesc.Avc1Desc = &atom.Avc1Desc{
}
var info *atom.H264SPSInfo
if info, err = atom.ParseH264SPS(self.sps[1:]); err != nil {
return
}
self.sample.SampleDesc.Avc1Desc = &atom.Avc1Desc{
DataRefIdx: 1,
HorizontalResolution: 72,
VorizontalResolution: 72,
Width: 0, // fill later
Height: 0, // fill later
Width: int(info.Width),
Height: int(info.Height),
FrameCount: 1,
Depth: 24,
ColorTableId: -1,
Conf: &atom.Avc1Conf{},
}
track.sample.SyncSample = &atom.SyncSample{}
track.TrackAtom.Media.Handler = &atom.HandlerRefer{
self.sample.SyncSample = &atom.SyncSample{}
self.trackAtom.Media.Handler = &atom.HandlerRefer{
SubType: "vide",
Name: "Video Media Handler",
}
track.sample.CompositionOffset = &atom.CompositionOffset{}
track.TrackAtom.Media.Info.Video = &atom.VideoMediaInfo{
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
}
@ -132,22 +158,12 @@ func (self *Muxer) WriteHeader() (err error) {
return
}
func (self *Track) SetH264PPSAndSPS(pps, sps []byte) {
self.pps, self.sps = pps, sps
}
func (self *Muxer) WriteSample(pkt av.Packet) (err error) {
pts, dts, isKeyFrame, frame := pkt.Pts, pkt.Dts, pkt.IsKeyFrame, pkt.Data
stream := self.streams[pkt.StreamIdx]
func (self *Track) SetMPEG4AudioConfig(config isom.MPEG4AudioConfig) {
self.mpeg4AudioConfig = config
}
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 stream.Type() == av.AAC && isom.IsADTSFrame(frame) {
config := stream.mpeg4AudioConfig.Complete()
if config.SampleRate == 0 {
err = fmt.Errorf("invalid sample rate")
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 {
return
}
delta := int64(samples)*self.TimeScale()/int64(config.SampleRate)
delta := int64(samples) * stream.TimeScale() / int64(config.SampleRate)
pts += delta
dts += delta
frame = frame[framelen:]
if self.writeSample(pts, dts, isKeyFrame, payload); err != nil {
if stream.writeSample(pts, dts, isKeyFrame, payload); err != nil {
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
sampleSize := len(data)
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")
return
}
duration := int(dts-self.lastDts)
duration := int(dts - self.lastDts)
if self.sttsEntry == nil || duration != self.sttsEntry.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]
@ -200,7 +217,7 @@ func (self *Track) writeSample(pts int64, dts int64, isKeyFrame bool, data []byt
err = fmt.Errorf("pts must greater than dts")
return
}
offset := int(pts-dts)
offset := int(pts - dts)
if self.cttsEntry == nil || offset != self.cttsEntry.Offset {
table := self.sample.CompositionOffset
table.Entries = append(table.Entries, atom.CompositionOffsetEntry{Offset: offset})
@ -217,47 +234,6 @@ func (self *Track) writeSample(pts int64, dts int64, isKeyFrame bool, data []byt
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) {
moov := &atom.Movie{}
moov.Header = &atom.MovieHeader{
@ -269,19 +245,19 @@ func (self *Muxer) WriteTrailer() (err error) {
maxDur := float64(0)
timeScale := 10000
for _, track := range(self.Tracks) {
if err = track.fillTrackAtom(); err != nil {
for _, stream := range self.streams {
if err = stream.fillTrackAtom(); err != nil {
return
}
dur := track.Duration()
track.TrackAtom.Header.Duration = int(float64(timeScale)*dur)
dur := stream.Duration()
stream.trackAtom.Header.Duration = int(float64(timeScale) * dur)
if dur > maxDur {
maxDur = dur
}
moov.Tracks = append(moov.Tracks, track.TrackAtom)
moov.Tracks = append(moov.Tracks, stream.trackAtom)
}
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 {
return
@ -292,226 +268,3 @@ func (self *Muxer) WriteTrailer() (err error) {
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
}