first commit

This commit is contained in:
Andrey Semochkin
2019-11-30 21:53:21 +01:00
commit 087a2b4c2d
60 changed files with 19091 additions and 0 deletions

30
format/mp4f/fd.go Normal file
View File

@@ -0,0 +1,30 @@
package mp4f
import "github.com/deepch/vdk/format/mp4/mp4io"
type FDummy struct {
Data []byte
Tag_ mp4io.Tag
mp4io.AtomPos
}
func (self FDummy) Children() []mp4io.Atom {
return nil
}
func (self FDummy) Tag() mp4io.Tag {
return self.Tag_
}
func (self FDummy) Len() int {
return len(self.Data)
}
func (self FDummy) Marshal(b []byte) int {
copy(b, self.Data)
return len(self.Data)
}
func (self FDummy) Unmarshal(b []byte, offset int) (n int, err error) {
return
}

356
format/mp4f/mp4fio/atoms.go Normal file
View File

@@ -0,0 +1,356 @@
package mp4fio
import (
"github.com/deepch/vdk/format/mp4/mp4io"
"github.com/deepch/vdk/utils/bits/pio"
)
func (self MovieFrag) Tag() mp4io.Tag {
return mp4io.MOOF
}
type MovieFrag struct {
Header *MovieFragHeader
Tracks []*TrackFrag
Unknowns []mp4io.Atom
mp4io.AtomPos
}
func (self MovieFrag) Marshal(b []byte) (n int) {
pio.PutU32BE(b[4:], uint32(mp4io.MOOF))
n += self.marshal(b[8:]) + 8
pio.PutU32BE(b[0:], uint32(n))
return
}
func (self MovieFrag) marshal(b []byte) (n int) {
if self.Header != nil {
n += self.Header.Marshal(b[n:])
}
for _, atom := range self.Tracks {
n += atom.Marshal(b[n:])
}
for _, atom := range self.Unknowns {
n += atom.Marshal(b[n:])
}
return
}
func (self MovieFrag) Len() (n int) {
n += 8
if self.Header != nil {
n += self.Header.Len()
}
for _, atom := range self.Tracks {
n += atom.Len()
}
for _, atom := range self.Unknowns {
n += atom.Len()
}
return
}
func (self *MovieFrag) Unmarshal(b []byte, offset int) (n int, err error) {
return
}
func (self MovieFrag) Children() (r []mp4io.Atom) {
if self.Header != nil {
r = append(r, self.Header)
}
for _, atom := range self.Tracks {
r = append(r, atom)
}
r = append(r, self.Unknowns...)
return
}
func (self MovieFragHeader) Tag() mp4io.Tag {
return mp4io.MFHD
}
type MovieFragHeader struct {
Version uint8
Flags uint32
Seqnum uint32
mp4io.AtomPos
}
func (self MovieFragHeader) Marshal(b []byte) (n int) {
pio.PutU32BE(b[4:], uint32(mp4io.MFHD))
n += self.marshal(b[8:]) + 8
pio.PutU32BE(b[0:], uint32(n))
return
}
func (self MovieFragHeader) marshal(b []byte) (n int) {
pio.PutU8(b[n:], self.Version)
n += 1
pio.PutU24BE(b[n:], self.Flags)
n += 3
pio.PutU32BE(b[n:], self.Seqnum)
n += 4
return
}
func (self MovieFragHeader) Len() (n int) {
n += 8
n += 1
n += 3
n += 4
return
}
func (self *MovieFragHeader) Unmarshal(b []byte, offset int) (n int, err error) {
return
}
func (self MovieFragHeader) Children() (r []mp4io.Atom) {
return
}
func (self TrackFragRun) Tag() mp4io.Tag {
return mp4io.TRUN
}
type TrackFragRun struct {
Version uint8
Flags uint32
DataOffset uint32
FirstSampleFlags uint32
Entries []mp4io.TrackFragRunEntry
mp4io.AtomPos
}
func (self TrackFragRun) Marshal(b []byte) (n int) {
pio.PutU32BE(b[4:], uint32(mp4io.TRUN))
n += self.marshal(b[8:]) + 8
pio.PutU32BE(b[0:], uint32(n))
return
}
func (self TrackFragRun) marshal(b []byte) (n int) {
pio.PutU8(b[n:], self.Version)
n += 1
pio.PutU24BE(b[n:], self.Flags)
n += 3
pio.PutU32BE(b[n:], uint32(len(self.Entries)))
n += 4
if self.Flags&mp4io.TRUN_DATA_OFFSET != 0 {
{
pio.PutU32BE(b[n:], self.DataOffset)
n += 4
}
}
if self.Flags&mp4io.TRUN_FIRST_SAMPLE_FLAGS != 0 {
{
pio.PutU32BE(b[n:], self.FirstSampleFlags)
n += 4
}
}
for i, entry := range self.Entries {
var flags uint32
if i > 0 {
flags = self.Flags
} else {
flags = self.FirstSampleFlags
}
//if flags&TRUN_SAMPLE_DURATION != 0 {
pio.PutU32BE(b[n:], entry.Duration)
n += 4
//}
//if flags&TRUN_SAMPLE_SIZE != 0 {
pio.PutU32BE(b[n:], entry.Size)
n += 4
//}
if flags&mp4io.TRUN_SAMPLE_FLAGS != 0 {
pio.PutU32BE(b[n:], entry.Flags)
n += 4
}
//if flags&TRUN_SAMPLE_CTS != 0 {
pio.PutU32BE(b[n:], entry.Cts)
n += 4
//}
}
return
}
func (self TrackFragRun) Len() (n int) {
n += 8
n += 1
n += 3
n += 4
if self.Flags&mp4io.TRUN_DATA_OFFSET != 0 {
{
n += 4
}
}
if self.Flags&mp4io.TRUN_FIRST_SAMPLE_FLAGS != 0 {
{
n += 4
}
}
for i := range self.Entries {
var flags uint32
if i > 0 {
flags = self.Flags
} else {
flags = self.FirstSampleFlags
}
//if flags&TRUN_SAMPLE_DURATION != 0 {
n += 4
//}
//if flags&TRUN_SAMPLE_SIZE != 0 {
n += 4
//}
if flags&mp4io.TRUN_SAMPLE_FLAGS != 0 {
n += 4
}
//if flags&TRUN_SAMPLE_CTS != 0 {
n += 4
//}
}
return
}
func (self *TrackFragRun) Unmarshal(b []byte, offset int) (n int, err error) {
return
}
func (self TrackFragRun) Children() (r []mp4io.Atom) {
return
}
func (self TrackFrag) Tag() mp4io.Tag {
return mp4io.TRAF
}
type TrackFrag struct {
Header *TrackFragHeader
DecodeTime *TrackFragDecodeTime
Run *TrackFragRun
Unknowns []mp4io.Atom
mp4io.AtomPos
}
func (self TrackFrag) Marshal(b []byte) (n int) {
pio.PutU32BE(b[4:], uint32(mp4io.TRAF))
n += self.marshal(b[8:]) + 8
pio.PutU32BE(b[0:], uint32(n))
return
}
func (self TrackFrag) marshal(b []byte) (n int) {
if self.Header != nil {
n += self.Header.Marshal(b[n:])
}
if self.DecodeTime != nil {
n += self.DecodeTime.Marshal(b[n:])
}
if self.Run != nil {
n += self.Run.Marshal(b[n:])
}
for _, atom := range self.Unknowns {
n += atom.Marshal(b[n:])
}
return
}
func (self TrackFrag) Len() (n int) {
n += 8
if self.Header != nil {
n += self.Header.Len()
}
if self.DecodeTime != nil {
n += self.DecodeTime.Len()
}
if self.Run != nil {
n += self.Run.Len()
}
for _, atom := range self.Unknowns {
n += atom.Len()
}
return
}
func (self *TrackFrag) Unmarshal(b []byte, offset int) (n int, err error) {
return
}
func (self TrackFrag) Children() (r []mp4io.Atom) {
if self.Header != nil {
r = append(r, self.Header)
}
if self.DecodeTime != nil {
r = append(r, self.DecodeTime)
}
if self.Run != nil {
r = append(r, self.Run)
}
r = append(r, self.Unknowns...)
return
}
const LenTrackFragRunEntry = 16
func (self TrackFragHeader) Tag() mp4io.Tag {
return mp4io.TFHD
}
type TrackFragHeader struct {
Data []byte
mp4io.AtomPos
}
func (self TrackFragHeader) Marshal(b []byte) (n int) {
pio.PutU32BE(b[4:], uint32(mp4io.TFHD))
n += self.marshal(b[8:]) + 8
pio.PutU32BE(b[0:], uint32(n))
return
}
func (self TrackFragHeader) marshal(b []byte) (n int) {
copy(b, self.Data)
n += len(self.Data)
return
}
func (self TrackFragHeader) Len() (n int) {
return len(self.Data) + 8
}
func (self *TrackFragHeader) Unmarshal(b []byte, offset int) (n int, err error) {
return
}
func (self TrackFragHeader) Children() (r []mp4io.Atom) {
return
}
func (self TrackFragDecodeTime) Tag() mp4io.Tag {
return mp4io.TFDT
}
type TrackFragDecodeTime struct {
Version uint8
Flags uint32
Time uint64
mp4io.AtomPos
}
func (self TrackFragDecodeTime) Marshal(b []byte) (n int) {
pio.PutU32BE(b[4:], uint32(mp4io.TFDT))
n += self.marshal(b[8:]) + 8
pio.PutU32BE(b[0:], uint32(n))
return
}
func (self TrackFragDecodeTime) marshal(b []byte) (n int) {
pio.PutU8(b[n:], self.Version)
n += 1
pio.PutU24BE(b[n:], self.Flags)
n += 3
pio.PutU64BE(b[n:], self.Time)
n += 8
return
}
func (self TrackFragDecodeTime) Len() (n int) {
n += 8
n += 1
n += 3
n += 8
return
}
func (self *TrackFragDecodeTime) Unmarshal(b []byte, offset int) (n int, err error) {
return
}
func (self TrackFragDecodeTime) Children() (r []mp4io.Atom) {
return
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,437 @@
package main
func moov_Movie() {
atom(Header, MovieHeader)
atom(MovieExtend, MovieExtend)
atoms(Tracks, Track)
_unknowns()
}
func mvhd_MovieHeader() {
uint8(Version)
uint24(Flags)
time32(CreateTime)
time32(ModifyTime)
int32(TimeScale)
int32(Duration)
fixed32(PreferredRate)
fixed16(PreferredVolume)
_skip(10)
array(Matrix, int32, 9)
time32(PreviewTime)
time32(PreviewDuration)
time32(PosterTime)
time32(SelectionTime)
time32(SelectionDuration)
time32(CurrentTime)
int32(NextTrackId)
}
func trak_Track() {
atom(Header, TrackHeader)
atom(Media, Media)
_unknowns()
}
func tkhd_TrackHeader() {
uint8(Version)
uint24(Flags)
time32(CreateTime)
time32(ModifyTime)
int32(TrackId)
_skip(4)
int32(Duration)
_skip(8)
int16(Layer)
int16(AlternateGroup)
fixed16(Volume)
_skip(2)
array(Matrix, int32, 9)
fixed32(TrackWidth)
fixed32(TrackHeight)
}
func hdlr_HandlerRefer() {
uint8(Version)
uint24(Flags)
bytes(Type, 4)
bytes(SubType, 4)
bytesleft(Name)
}
func mdia_Media() {
atom(Header, MediaHeader)
atom(Handler, HandlerRefer)
atom(Info, MediaInfo)
_unknowns()
}
func mdhd_MediaHeader() {
uint8(Version)
uint24(Flags)
time32(CreateTime)
time32(ModifyTime)
int32(TimeScale)
int32(Duration)
int16(Language)
int16(Quality)
}
func minf_MediaInfo() {
atom(Sound, SoundMediaInfo)
atom(Video, VideoMediaInfo)
atom(Data, DataInfo)
atom(Sample, SampleTable)
_unknowns()
}
func dinf_DataInfo() {
atom(Refer, DataRefer)
_unknowns()
}
func dref_DataRefer() {
uint8(Version)
uint24(Flags)
int32(_childrenNR)
atom(Url, DataReferUrl)
}
func url__DataReferUrl() {
uint8(Version)
uint24(Flags)
}
func smhd_SoundMediaInfo() {
uint8(Version)
uint24(Flags)
int16(Balance)
_skip(2)
}
func vmhd_VideoMediaInfo() {
uint8(Version)
uint24(Flags)
int16(GraphicsMode)
array(Opcolor, int16, 3)
}
func stbl_SampleTable() {
atom(SampleDesc, SampleDesc)
atom(TimeToSample, TimeToSample)
atom(CompositionOffset, CompositionOffset)
atom(SampleToChunk, SampleToChunk)
atom(SyncSample, SyncSample)
atom(ChunkOffset, ChunkOffset)
atom(SampleSize, SampleSize)
}
func stsd_SampleDesc() {
uint8(Version)
_skip(3)
int32(_childrenNR)
atom(AVC1Desc, AVC1Desc)
atom(MP4ADesc, MP4ADesc)
_unknowns()
}
func mp4a_MP4ADesc() {
_skip(6)
int16(DataRefIdx)
int16(Version)
int16(RevisionLevel)
int32(Vendor)
int16(NumberOfChannels)
int16(SampleSize)
int16(CompressionId)
_skip(2)
fixed32(SampleRate)
atom(Conf, ElemStreamDesc)
_unknowns()
}
func avc1_AVC1Desc() {
_skip(6)
int16(DataRefIdx)
int16(Version)
int16(Revision)
int32(Vendor)
int32(TemporalQuality)
int32(SpatialQuality)
int16(Width)
int16(Height)
fixed32(HorizontalResolution)
fixed32(VorizontalResolution)
_skip(4)
int16(FrameCount)
bytes(CompressorName, 32)
int16(Depth)
int16(ColorTableId)
atom(Conf, AVC1Conf)
_unknowns()
}
func avcC_AVC1Conf() {
bytesleft(Data)
}
func stts_TimeToSample() {
uint8(Version)
uint24(Flags)
uint32(_len_Entries)
slice(Entries, TimeToSampleEntry)
}
func TimeToSampleEntry() {
uint32(Count)
uint32(Duration)
}
func stsc_SampleToChunk() {
uint8(Version)
uint24(Flags)
uint32(_len_Entries)
slice(Entries, SampleToChunkEntry)
}
func SampleToChunkEntry() {
uint32(FirstChunk)
uint32(SamplesPerChunk)
uint32(SampleDescId)
}
func ctts_CompositionOffset() {
uint8(Version)
uint24(Flags)
uint32(_len_Entries)
slice(Entries, CompositionOffsetEntry)
}
func CompositionOffsetEntry() {
uint32(Count)
uint32(Offset)
}
func stss_SyncSample() {
uint8(Version)
uint24(Flags)
uint32(_len_Entries)
slice(Entries, uint32)
}
func stco_ChunkOffset() {
uint8(Version)
uint24(Flags)
uint32(_len_Entries)
slice(Entries, uint32)
}
func moof_MovieFrag() {
atom(Header, MovieFragHeader)
atoms(Tracks, TrackFrag)
_unknowns()
}
func mfhd_MovieFragHeader() {
uint8(Version)
uint24(Flags)
uint32(Seqnum)
}
func traf_TrackFrag() {
atom(Header, TrackFragHeader)
atom(DecodeTime, TrackFragDecodeTime)
atom(Run, TrackFragRun)
_unknowns()
}
func mvex_MovieExtend() {
atoms(Tracks, TrackExtend)
_unknowns()
}
func trex_TrackExtend() {
uint8(Version)
uint24(Flags)
uint32(TrackId)
uint32(DefaultSampleDescIdx)
uint32(DefaultSampleDuration)
uint32(DefaultSampleSize)
uint32(DefaultSampleFlags)
}
func stsz_SampleSize() {
uint8(Version)
uint24(Flags)
uint32(SampleSize)
_code(func() {
if self.SampleSize != 0 {
return
}
})
uint32(_len_Entries)
slice(Entries, uint32)
}
func trun_TrackFragRun() {
uint8(Version)
uint24(Flags)
uint32(_len_Entries)
uint32(DataOffset, _code(func() {
if self.Flags&TRUN_DATA_OFFSET != 0 {
doit()
}
}))
uint32(FirstSampleFlags, _code(func() {
if self.Flags&TRUN_FIRST_SAMPLE_FLAGS != 0 {
doit()
}
}))
slice(Entries, TrackFragRunEntry, _code(func() {
for i, entry := range self.Entries {
var flags uint32
if i > 0 {
flags = self.Flags
} else {
flags = self.FirstSampleFlags
}
if flags&TRUN_SAMPLE_DURATION != 0 {
pio.PutU32BE(b[n:], entry.Duration)
n += 4
}
if flags&TRUN_SAMPLE_SIZE != 0 {
pio.PutU32BE(b[n:], entry.Size)
n += 4
}
if flags&TRUN_SAMPLE_FLAGS != 0 {
pio.PutU32BE(b[n:], entry.Flags)
n += 4
}
if flags&TRUN_SAMPLE_CTS != 0 {
pio.PutU32BE(b[n:], entry.Cts)
n += 4
}
}
}, func() {
for i := range self.Entries {
var flags uint32
if i > 0 {
flags = self.Flags
} else {
flags = self.FirstSampleFlags
}
if flags&TRUN_SAMPLE_DURATION != 0 {
n += 4
}
if flags&TRUN_SAMPLE_SIZE != 0 {
n += 4
}
if flags&TRUN_SAMPLE_FLAGS != 0 {
n += 4
}
if flags&TRUN_SAMPLE_CTS != 0 {
n += 4
}
}
}, func() {
for i := 0; i < int(_len_Entries); i++ {
var flags uint32
if i > 0 {
flags = self.Flags
} else {
flags = self.FirstSampleFlags
}
entry := &self.Entries[i]
if flags&TRUN_SAMPLE_DURATION != 0 {
entry.Duration = pio.U32BE(b[n:])
n += 4
}
if flags&TRUN_SAMPLE_SIZE != 0 {
entry.Size = pio.U32BE(b[n:])
n += 4
}
if flags&TRUN_SAMPLE_FLAGS != 0 {
entry.Flags = pio.U32BE(b[n:])
n += 4
}
if flags&TRUN_SAMPLE_CTS != 0 {
entry.Cts = pio.U32BE(b[n:])
n += 4
}
}
}))
}
func TrackFragRunEntry() {
uint32(Duration)
uint32(Size)
uint32(Flags)
uint32(Cts)
}
func tfhd_TrackFragHeader() {
uint8(Version)
uint24(Flags)
uint64(BaseDataOffset, _code(func() {
if self.Flags&TFHD_BASE_DATA_OFFSET != 0 {
doit()
}
}))
uint32(StsdId, _code(func() {
if self.Flags&TFHD_STSD_ID != 0 {
doit()
}
}))
uint32(DefaultDuration, _code(func() {
if self.Flags&TFHD_DEFAULT_DURATION != 0 {
doit()
}
}))
uint32(DefaultSize, _code(func() {
if self.Flags&TFHD_DEFAULT_SIZE != 0 {
doit()
}
}))
uint32(DefaultFlags, _code(func() {
if self.Flags&TFHD_DEFAULT_FLAGS != 0 {
doit()
}
}))
}
func tfdt_TrackFragDecodeTime() {
uint8(Version)
uint24(Flags)
time64(Time, _code(func() {
if self.Version != 0 {
PutTime64(b[n:], self.Time)
n += 8
} else {
PutTime32(b[n:], self.Time)
n += 4
}
}, func() {
if self.Version != 0 {
n += 8
} else {
n += 4
}
}, func() {
if self.Version != 0 {
self.Time = GetTime64(b[n:])
n += 8
} else {
self.Time = GetTime32(b[n:])
n += 4
}
}))
}

113
format/mp4f/mp4fio/mp4io.go Normal file
View File

@@ -0,0 +1,113 @@
package mp4fio
import (
"github.com/deepch/vdk/format/mp4/mp4io"
"github.com/deepch/vdk/utils/bits/pio"
)
type ElemStreamDesc struct {
DecConfig []byte
TrackId uint16
mp4io.AtomPos
}
func (self ElemStreamDesc) Children() []mp4io.Atom {
return nil
}
func (self ElemStreamDesc) fillLength(b []byte, length int) (n int) {
b[n] = uint8(length & 0x7f)
n++
return
}
func (self ElemStreamDesc) lenDescHdr() (n int) {
return 2
}
func (self ElemStreamDesc) fillDescHdr(b []byte, tag uint8, datalen int) (n int) {
b[n] = tag
n++
n += self.fillLength(b[n:], datalen)
return
}
func (self ElemStreamDesc) lenESDescHdr() (n int) {
return self.lenDescHdr() + 3
}
func (self ElemStreamDesc) fillESDescHdr(b []byte, datalen int) (n int) {
n += self.fillDescHdr(b[n:], mp4io.MP4ESDescrTag, datalen)
pio.PutU16BE(b[n:], self.TrackId)
n += 2
b[n] = 0 // flags
n++
return
}
func (self ElemStreamDesc) lenDecConfigDescHdr() (n int) {
return self.lenDescHdr() + 2 + 3 + 4 + 4 + self.lenDescHdr()
}
func (self ElemStreamDesc) fillDecConfigDescHdr(b []byte, datalen int) (n int) {
n += self.fillDescHdr(b[n:], mp4io.MP4DecConfigDescrTag, datalen)
b[n] = 0x40 // objectid
n++
b[n] = 0x15 // streamtype
n++
// buffer size db
pio.PutU24BE(b[n:], 0)
n += 3
// max bitrage
pio.PutU32BE(b[n:], uint32(200000))
n += 4
// avg bitrage
pio.PutU32BE(b[n:], uint32(0))
n += 4
n += self.fillDescHdr(b[n:], mp4io.MP4DecSpecificDescrTag, datalen-n)
return
}
func (self ElemStreamDesc) Len() (n int) {
// len + tag
return 8 +
// ver + flags
4 +
self.lenESDescHdr() +
self.lenDecConfigDescHdr() +
len(self.DecConfig) +
self.lenDescHdr() + 1
}
// Version(4)
// ESDesc(
// MP4ESDescrTag
// ESID(2)
// ESFlags(1)
// DecConfigDesc(
// MP4DecConfigDescrTag
// objectId streamType bufSize avgBitrate
// DecSpecificDesc(
// MP4DecSpecificDescrTag
// decConfig
// )
// )
// ?Desc(lenDescHdr+1)
// )
func (self ElemStreamDesc) Marshal(b []byte) (n int) {
pio.PutU32BE(b[4:], uint32(mp4io.ESDS))
n += 8
pio.PutU32BE(b[n:], 0) // Version
n += 4
datalen := self.Len()
n += self.fillESDescHdr(b[n:], datalen-n-self.lenESDescHdr()+3)
n += self.fillDecConfigDescHdr(b[n:], datalen-n-self.lenDescHdr()-3)
copy(b[n:], self.DecConfig)
n += len(self.DecConfig)
n += self.fillDescHdr(b[n:], 0x06, datalen-n-self.lenDescHdr())
b[n] = 0x02
n++
pio.PutU32BE(b[0:], uint32(n))
return
}

402
format/mp4f/muxer.go Normal file
View File

@@ -0,0 +1,402 @@
package mp4f
import (
"bufio"
"fmt"
"os"
"time"
"github.com/deepch/vdk/av"
"github.com/deepch/vdk/codec/aacparser"
"github.com/deepch/vdk/codec/h264parser"
"github.com/deepch/vdk/format/mp4/mp4io"
"github.com/deepch/vdk/format/mp4f/mp4fio"
"github.com/deepch/vdk/utils/bits/pio"
)
type Muxer struct {
maxFrames int
bufw *bufio.Writer
wpos int64
fragmentIndex int
streams []*Stream
path string
}
func NewMuxer(w *os.File) *Muxer {
return &Muxer{}
}
func (self *Muxer) SetPath(path string) {
self.path = path
}
func (self *Muxer) SetMaxFrames(count int) {
self.maxFrames = count
}
func (self *Muxer) newStream(codec av.CodecData) (err error) {
switch codec.Type() {
case av.H264, av.AAC:
default:
err = fmt.Errorf("fmp4: codec type=%v is not supported", codec.Type())
return
}
stream := &Stream{CodecData: codec}
stream.sample = &mp4io.SampleTable{
SampleDesc: &mp4io.SampleDesc{},
TimeToSample: &mp4io.TimeToSample{},
SampleToChunk: &mp4io.SampleToChunk{},
SampleSize: &mp4io.SampleSize{},
ChunkOffset: &mp4io.ChunkOffset{},
}
stream.trackAtom = &mp4io.Track{
Header: &mp4io.TrackHeader{
TrackId: int32(len(self.streams) + 1),
Flags: 0x0007,
Duration: 0,
Matrix: [9]int32{0x10000, 0, 0, 0, 0x10000, 0, 0, 0, 0x40000000},
},
Media: &mp4io.Media{
Header: &mp4io.MediaHeader{
TimeScale: 1000,
Duration: 0,
Language: 21956,
},
Info: &mp4io.MediaInfo{
Sample: stream.sample,
Data: &mp4io.DataInfo{
Refer: &mp4io.DataRefer{
Url: &mp4io.DataReferUrl{
Flags: 0x000001,
},
},
},
},
},
}
switch codec.Type() {
case av.H264:
stream.sample.SyncSample = &mp4io.SyncSample{}
stream.timeScale = 90000
case av.AAC:
stream.timeScale = 8000
}
stream.muxer = self
self.streams = append(self.streams, stream)
return
}
func (self *Stream) buildEsds(conf []byte) *FDummy {
esds := &mp4fio.ElemStreamDesc{DecConfig: conf}
b := make([]byte, esds.Len())
esds.Marshal(b)
esdsDummy := FDummy{
Data: b,
Tag_: mp4io.Tag(uint32(mp4io.ESDS)),
}
return &esdsDummy
}
func (self *Stream) buildHdlr() *FDummy {
hdlr := FDummy{
Data: []byte{
0x00, 0x00, 0x00, 0x35, 0x68, 0x64, 0x6C, 0x72,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x76, 0x69,
0x64, 0x65, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x47, 0x6F, 0x6F, 0x64, 0x67, 0x61,
0x6D, 0x65, 0x20, 0x47, 0x4F, 0x20, 0x53, 0x65, 0x72, 0x76,
0x65, 0x72, 0x00, 0x00, 0x00},
Tag_: mp4io.Tag(uint32(mp4io.HDLR)),
}
return &hdlr
}
func (self *Stream) buildAudioHdlr() *FDummy {
hdlr := FDummy{
Data: []byte{
0x00, 0x00, 0x00, 0x35, 0x68, 0x64, 0x6C, 0x72,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x73, 0x6F,
0x75, 0x6E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x42, 0x65, 0x6E, 0x74, 0x6F, 0x34,
0x20, 0x53, 0x6F, 0x75, 0x6E, 0x64, 0x20, 0x48, 0x61, 0x6E,
0x64, 0x6C, 0x65, 0x72, 0x00},
Tag_: mp4io.Tag(uint32(mp4io.HDLR)),
}
return &hdlr
}
func (self *Stream) buildEdts() *FDummy {
edts := FDummy{
Data: []byte{
0x00, 0x00, 0x00, 0x30, 0x65, 0x64, 0x74, 0x73,
0x00, 0x00, 0x00, 0x28, 0x65, 0x6C, 0x73, 0x74, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x21,
0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
0x9B, 0x24, 0x00, 0x00, 0x02, 0x10, 0x00, 0x01, 0x00, 0x00,
},
Tag_: mp4io.Tag(0x65647473),
}
return &edts
}
func (self *Stream) fillTrackAtom() (err error) {
self.trackAtom.Media.Header.TimeScale = int32(self.timeScale)
self.trackAtom.Media.Header.Duration = int32(self.duration)
if self.Type() == av.H264 {
codec := self.CodecData.(h264parser.CodecData)
width, height := codec.Width(), codec.Height()
self.sample.SampleDesc.AVC1Desc = &mp4io.AVC1Desc{
DataRefIdx: 1,
HorizontalResolution: 72,
VorizontalResolution: 72,
Width: int16(width),
Height: int16(height),
FrameCount: 1,
Depth: 24,
ColorTableId: -1,
Conf: &mp4io.AVC1Conf{Data: codec.AVCDecoderConfRecordBytes()},
}
self.trackAtom.Header.TrackWidth = float64(width)
self.trackAtom.Header.TrackHeight = float64(height)
self.trackAtom.Media.Handler = &mp4io.HandlerRefer{
SubType: [4]byte{'v', 'i', 'd', 'e'},
Name: []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 'G', 'G', 0, 0, 0},
}
self.trackAtom.Media.Info.Video = &mp4io.VideoMediaInfo{
Flags: 0x000001,
}
self.codecString = fmt.Sprintf("avc1.%02X%02X%02X", codec.RecordInfo.AVCProfileIndication, codec.RecordInfo.ProfileCompatibility, codec.RecordInfo.AVCLevelIndication)
} else if self.Type() == av.AAC {
codec := self.CodecData.(aacparser.CodecData)
self.sample.SampleDesc.MP4ADesc = &mp4io.MP4ADesc{
DataRefIdx: 1,
NumberOfChannels: int16(codec.ChannelLayout().Count()),
SampleSize: 16,
SampleRate: float64(codec.SampleRate()),
Unknowns: []mp4io.Atom{self.buildEsds(codec.MPEG4AudioConfigBytes())},
}
self.trackAtom.Header.Volume = 1
self.trackAtom.Header.AlternateGroup = 1
self.trackAtom.Header.Duration = 0
self.trackAtom.Media.Handler = &mp4io.HandlerRefer{
SubType: [4]byte{'s', 'o', 'u', 'n'},
Name: []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 'G', 'G', 0, 0, 0},
}
self.trackAtom.Media.Info.Sound = &mp4io.SoundMediaInfo{}
self.codecString = "mp4a.40.2"
} else {
err = fmt.Errorf("fmp4: codec type=%d invalid", self.Type())
}
return
}
func (self *Muxer) WriteTrailer() (err error) {
return
}
func (element *Muxer) WriteHeader(streams []av.CodecData) (err error) {
element.streams = []*Stream{}
for _, stream := range streams {
if err = element.newStream(stream); err != nil {
return
}
}
return
}
func (element *Muxer) GetInit(streams []av.CodecData) (string, []byte) {
moov := &mp4io.Movie{
Header: &mp4io.MovieHeader{
PreferredRate: 1,
PreferredVolume: 1,
Matrix: [9]int32{0x10000, 0, 0, 0, 0x10000, 0, 0, 0, 0x40000000},
NextTrackId: 3,
Duration: 0,
TimeScale: 1000,
CreateTime: time0(),
ModifyTime: time0(),
PreviewTime: time0(),
PreviewDuration: time0(),
PosterTime: time0(),
SelectionTime: time0(),
SelectionDuration: time0(),
CurrentTime: time0(),
},
Unknowns: []mp4io.Atom{element.buildMvex()},
}
var meta string
for _, stream := range element.streams {
if err := stream.fillTrackAtom(); err != nil {
return meta, []byte{}
}
moov.Tracks = append(moov.Tracks, stream.trackAtom)
meta += stream.codecString + ","
}
meta = meta[:len(meta)-1]
ftypeData := []byte{0x00, 0x00, 0x00, 0x18, 0x66, 0x74, 0x79, 0x70, 0x69, 0x73, 0x6f, 0x36, 0x00, 0x00, 0x00, 0x01, 0x69, 0x73, 0x6f, 0x36, 0x64, 0x61, 0x73, 0x68}
file := make([]byte, moov.Len()+len(ftypeData))
copy(file, ftypeData)
moov.Marshal(file[len(ftypeData):])
return meta, file
}
func (element *Muxer) WritePacket(pkt av.Packet, GOP bool) (bool, []byte, error) {
stream := element.streams[pkt.Idx]
if GOP {
ts := time.Duration(0)
if stream.lastpkt != nil {
ts = pkt.Time - stream.lastpkt.Time
}
got, buf, err := stream.writePacketV3(pkt, ts, 5)
stream.lastpkt = &pkt
if err != nil {
return false, []byte{}, err
}
return got, buf, err
}
ts := time.Duration(0)
if stream.lastpkt != nil {
ts = pkt.Time - stream.lastpkt.Time
}
got, buf, err := stream.writePacketV2(pkt, ts, 5)
stream.lastpkt = &pkt
if err != nil {
return false, []byte{}, err
}
return got, buf, err
}
func (element *Stream) writePacketV3(pkt av.Packet, rawdur time.Duration, maxFrames int) (bool, []byte, error) {
trackID := pkt.Idx + 1
var out []byte
var got bool
if element.sampleIndex > maxFrames && pkt.IsKeyFrame {
element.moof.Tracks[0].Run.DataOffset = uint32(element.moof.Len() + 8)
out = make([]byte, element.moof.Len()+len(element.buffer))
element.moof.Marshal(out)
pio.PutU32BE(element.buffer, uint32(len(element.buffer)))
copy(out[element.moof.Len():], element.buffer)
element.sampleIndex = 0
element.muxer.fragmentIndex++
got = true
}
if element.sampleIndex == 0 {
element.moof.Header = &mp4fio.MovieFragHeader{Seqnum: uint32(element.muxer.fragmentIndex + 1)}
element.moof.Tracks = []*mp4fio.TrackFrag{
&mp4fio.TrackFrag{
Header: &mp4fio.TrackFragHeader{
Data: []byte{0x00, 0x02, 0x00, 0x20, 0x00, 0x00, 0x00, uint8(trackID), 0x01, 0x01, 0x00, 0x00},
},
DecodeTime: &mp4fio.TrackFragDecodeTime{
Version: 1,
Flags: 0,
Time: uint64(element.dts),
},
Run: &mp4fio.TrackFragRun{
Flags: 0x000b05,
FirstSampleFlags: 0x02000000,
DataOffset: 0,
Entries: []mp4io.TrackFragRunEntry{},
},
},
}
element.buffer = []byte{0x00, 0x00, 0x00, 0x00, 0x6d, 0x64, 0x61, 0x74}
}
runEnrty := mp4io.TrackFragRunEntry{
Duration: uint32(element.timeToTs(rawdur)),
Size: uint32(len(pkt.Data)),
Cts: uint32(element.timeToTs(pkt.CompositionTime)),
}
element.moof.Tracks[0].Run.Entries = append(element.moof.Tracks[0].Run.Entries, runEnrty)
element.buffer = append(element.buffer, pkt.Data...)
element.sampleIndex++
element.dts += element.timeToTs(rawdur)
return got, out, nil
}
func (element *Stream) writePacketV2(pkt av.Packet, rawdur time.Duration, maxFrames int) (bool, []byte, error) {
trackID := pkt.Idx + 1
if element.sampleIndex == 0 {
element.moof.Header = &mp4fio.MovieFragHeader{Seqnum: uint32(element.muxer.fragmentIndex + 1)}
element.moof.Tracks = []*mp4fio.TrackFrag{
&mp4fio.TrackFrag{
Header: &mp4fio.TrackFragHeader{
Data: []byte{0x00, 0x02, 0x00, 0x20, 0x00, 0x00, 0x00, uint8(trackID), 0x01, 0x01, 0x00, 0x00},
},
DecodeTime: &mp4fio.TrackFragDecodeTime{
Version: 1,
Flags: 0,
Time: uint64(element.dts),
},
Run: &mp4fio.TrackFragRun{
Flags: 0x000b05,
FirstSampleFlags: 0x02000000,
DataOffset: 0,
Entries: []mp4io.TrackFragRunEntry{},
},
},
}
element.buffer = []byte{0x00, 0x00, 0x00, 0x00, 0x6d, 0x64, 0x61, 0x74}
}
runEnrty := mp4io.TrackFragRunEntry{
Duration: uint32(element.timeToTs(rawdur)),
Size: uint32(len(pkt.Data)),
Cts: uint32(element.timeToTs(pkt.CompositionTime)),
}
element.moof.Tracks[0].Run.Entries = append(element.moof.Tracks[0].Run.Entries, runEnrty)
element.buffer = append(element.buffer, pkt.Data...)
element.sampleIndex++
element.dts += element.timeToTs(rawdur)
if element.sampleIndex > maxFrames { // Количество фреймов в пакете
element.moof.Tracks[0].Run.DataOffset = uint32(element.moof.Len() + 8)
file := make([]byte, element.moof.Len()+len(element.buffer))
element.moof.Marshal(file)
pio.PutU32BE(element.buffer, uint32(len(element.buffer)))
copy(file[element.moof.Len():], element.buffer)
element.sampleIndex = 0
element.muxer.fragmentIndex++
return true, file, nil
}
return false, []byte{}, nil
}
func (self *Muxer) buildMvex() *FDummy {
mvex := &FDummy{
Data: []byte{
0x00, 0x00, 0x00, 0x38, 0x6D, 0x76, 0x65, 0x78,
0x00, 0x00, 0x00, 0x10, 0x6D, 0x65, 0x68, 0x64, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
Tag_: mp4io.Tag(0x6D766578),
}
for i := 1; i <= len(self.streams); i++ {
trex := self.buildTrex(i)
mvex.Data = append(mvex.Data, trex...)
}
pio.PutU32BE(mvex.Data, uint32(len(mvex.Data)))
return mvex
}
func (self *Muxer) buildTrex(trackId int) []byte {
return []byte{
0x00, 0x00, 0x00, 0x20, 0x74, 0x72, 0x65, 0x78,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, uint8(trackId), 0x00, 0x00,
0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00}
}
func time0() time.Time {
return time.Date(1904, time.January, 1, 0, 0, 0, 0, time.UTC)
}

54
format/mp4f/stream.go Normal file
View File

@@ -0,0 +1,54 @@
package mp4f
import (
"time"
"github.com/deepch/vdk/av"
"github.com/deepch/vdk/format/mp4"
"github.com/deepch/vdk/format/mp4/mp4io"
"github.com/deepch/vdk/format/mp4f/mp4fio"
)
type Stream struct {
av.CodecData
codecString string
trackAtom *mp4io.Track
idx int
lastpkt *av.Packet
timeScale int64
duration int64
muxer *Muxer
demuxer *mp4.Demuxer
sample *mp4io.SampleTable
sampleIndex int
sampleOffsetInChunk int64
syncSampleIndex int
dts int64
sttsEntryIndex int
sampleIndexInSttsEntry int
cttsEntryIndex int
sampleIndexInCttsEntry int
chunkGroupIndex int
chunkIndex int
sampleIndexInChunk int
sttsEntry *mp4io.TimeToSampleEntry
cttsEntry *mp4io.CompositionOffsetEntry
moof mp4fio.MovieFrag
buffer []byte
}
func timeToTs(tm time.Duration, timeScale int64) int64 {
return int64(tm * time.Duration(timeScale) / time.Second)
}
func tsToTime(ts int64, timeScale int64) time.Duration {
return time.Duration(ts) * time.Second / time.Duration(timeScale)
}
func (obj *Stream) timeToTs(tm time.Duration) int64 {
return int64(tm * time.Duration(obj.timeScale) / time.Second)
}
func (obj *Stream) tsToTime(ts int64) time.Duration {
return time.Duration(ts) * time.Second / time.Duration(obj.timeScale)
}