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)
@ -25,9 +24,9 @@ type Walker interface {
}
type Dumper struct {
W io.Writer
depth int
name string
W io.Writer
depth int
name string
arrlen int
arridx int
}
@ -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 {
@ -363,14 +363,14 @@ const (
)
type TrackFragHeader struct {
Version int
Flags int
Id int
DefaultSize int
DefaultDuration int
DefaultFlags int
BaseDataOffset int64
StsdId int
Version int
Flags int
Id int
DefaultSize int
DefaultDuration int
DefaultFlags int
BaseDataOffset int64
StsdId int
}
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.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

@ -565,11 +565,11 @@ func WalkTrackHeader(w Walker, self *TrackHeader) {
}
type HandlerRefer struct {
Version int
Flags int
Type string
SubType string
Name string
Version int
Flags int
CodecType string
SubType string
Name string
}
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 {
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,
r: self.R,
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)

477
muxer.go
View File

@ -1,57 +1,54 @@
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
W io.WriteSeeker
streams []*Stream
mdatWriter *atom.Writer
}
func (self *Muxer) newTrack() *Track {
track := &Track{}
func (self *Muxer) NewStream() av.Stream {
stream := &Stream{}
track.sample = &atom.SampleTable{
SampleDesc: &atom.SampleDesc{},
stream.sample = &atom.SampleTable{
SampleDesc: &atom.SampleDesc{},
TimeToSample: &atom.TimeToSample{},
SampleToChunk: &atom.SampleToChunk{
Entries: []atom.SampleToChunkEntry{
{
FirstChunk: 1,
SampleDescId: 1,
FirstChunk: 1,
SampleDescId: 1,
SamplesPerChunk: 1,
},
},
},
SampleSize: &atom.SampleSize{},
SampleSize: &atom.SampleSize{},
ChunkOffset: &atom.ChunkOffset{},
}
track.TrackAtom = &atom.Track{
stream.trackAtom = &atom.Track{
Header: &atom.TrackHeader{
TrackId: len(self.Tracks)+1,
Flags: 0x0003, // Track enabled | Track in movie
Duration: 0, // fill later
Matrix: [9]int{0x10000, 0, 0, 0, 0x10000, 0, 0, 0, 0x40000000},
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},
},
Media: &atom.Media{
Header: &atom.MediaHeader{
TimeScale: 0, // fill later
Duration: 0, // fill later
Language: 21956,
Duration: 0, // fill later
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{}
return
}
func (self *Muxer) AddH264Track() (track *Track) {
track = self.newTrack()
self.TrackH264 = track
track.Type = H264
track.sample.SampleDesc.Avc1Desc = &atom.Avc1Desc{
DataRefIdx: 1,
HorizontalResolution: 72,
VorizontalResolution: 72,
Width: 0, // fill later
Height: 0, // fill later
FrameCount: 1,
Depth: 24,
ColorTableId: -1,
Conf: &atom.Avc1Conf{},
}
track.sample.SyncSample = &atom.SyncSample{}
track.TrackAtom.Media.Handler = &atom.HandlerRefer{
SubType: "vide",
Name: "Video Media Handler",
}
track.sample.CompositionOffset = &atom.CompositionOffset{}
track.TrackAtom.Media.Info.Video = &atom.VideoMediaInfo{
Flags: 0x000001,
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
}
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: int(info.Width),
Height: int(info.Height),
FrameCount: 1,
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
}
@ -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,71 +234,30 @@ 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{
PreferredRate: atom.IntToFixed(1),
PreferredRate: atom.IntToFixed(1),
PreferredVolume: atom.IntToFixed(1),
Matrix: [9]int{0x10000, 0, 0, 0, 0x10000, 0, 0, 0, 0x40000000},
NextTrackId: 2,
Matrix: [9]int{0x10000, 0, 0, 0, 0x10000, 0, 0, 0, 0x40000000},
NextTrackId: 2,
}
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
}