mirror of
https://github.com/AlexxIT/go2rtc.git
synced 2025-09-26 20:31:11 +08:00
340 lines
9.0 KiB
Go
340 lines
9.0 KiB
Go
package iso
|
|
|
|
const (
|
|
Ftyp = "ftyp"
|
|
Moov = "moov"
|
|
MoovMvhd = "mvhd"
|
|
MoovTrak = "trak"
|
|
MoovTrakTkhd = "tkhd"
|
|
MoovTrakMdia = "mdia"
|
|
MoovTrakMdiaMdhd = "mdhd"
|
|
MoovTrakMdiaHdlr = "hdlr"
|
|
MoovTrakMdiaMinf = "minf"
|
|
MoovTrakMdiaMinfVmhd = "vmhd"
|
|
MoovTrakMdiaMinfSmhd = "smhd"
|
|
MoovTrakMdiaMinfDinf = "dinf"
|
|
MoovTrakMdiaMinfDinfDref = "dref"
|
|
MoovTrakMdiaMinfDinfDrefUrl = "url "
|
|
MoovTrakMdiaMinfStbl = "stbl"
|
|
MoovTrakMdiaMinfStblStsd = "stsd"
|
|
MoovTrakMdiaMinfStblStts = "stts"
|
|
MoovTrakMdiaMinfStblStsc = "stsc"
|
|
MoovTrakMdiaMinfStblStsz = "stsz"
|
|
MoovTrakMdiaMinfStblStco = "stco"
|
|
MoovMvex = "mvex"
|
|
MoovMvexTrex = "trex"
|
|
Moof = "moof"
|
|
MoofMfhd = "mfhd"
|
|
MoofTraf = "traf"
|
|
MoofTrafTfhd = "tfhd"
|
|
MoofTrafTfdt = "tfdt"
|
|
MoofTrafTrun = "trun"
|
|
Mdat = "mdat"
|
|
)
|
|
|
|
const (
|
|
sampleIsNonSync = 0x10000
|
|
sampleDependsOn1 = 0x1000000
|
|
sampleDependsOn2 = 0x2000000
|
|
|
|
SampleVideoIFrame = sampleDependsOn2
|
|
SampleVideoNonIFrame = sampleDependsOn1 | sampleIsNonSync
|
|
SampleAudio = sampleDependsOn2 //sampleIsNonSync
|
|
)
|
|
|
|
func (m *Movie) WriteFileType() {
|
|
m.StartAtom(Ftyp)
|
|
m.WriteString("iso5")
|
|
m.WriteUint32(512)
|
|
m.WriteString("iso5")
|
|
m.WriteString("iso6")
|
|
m.WriteString("mp41")
|
|
m.EndAtom()
|
|
}
|
|
|
|
func (m *Movie) WriteMovieHeader() {
|
|
m.StartAtom(MoovMvhd)
|
|
m.Skip(1) // version
|
|
m.Skip(3) // flags
|
|
m.Skip(4) // create time
|
|
m.Skip(4) // modify time
|
|
m.WriteUint32(1000) // time scale
|
|
m.Skip(4) // duration
|
|
m.WriteFloat32(1) // preferred rate
|
|
m.WriteFloat16(1) // preferred volume
|
|
m.Skip(10) // reserved
|
|
m.WriteMatrix()
|
|
m.Skip(6 * 4) // predefined?
|
|
m.WriteUint32(0xFFFFFFFF) // next track ID
|
|
m.EndAtom()
|
|
}
|
|
|
|
func (m *Movie) WriteTrackHeader(id uint32, width, height uint16) {
|
|
const (
|
|
TkhdTrackEnabled = 0x0001
|
|
TkhdTrackInMovie = 0x0002
|
|
TkhdTrackInPreview = 0x0004
|
|
TkhdTrackInPoster = 0x0008
|
|
)
|
|
|
|
// https://developer.apple.com/library/archive/documentation/QuickTime/QTFF/QTFFChap2/qtff2.html#//apple_ref/doc/uid/TP40000939-CH204-32963
|
|
m.StartAtom(MoovTrakTkhd)
|
|
m.Skip(1) // version
|
|
m.WriteUint24(TkhdTrackEnabled | TkhdTrackInMovie)
|
|
m.Skip(4) // create time
|
|
m.Skip(4) // modify time
|
|
m.WriteUint32(id) // trackID
|
|
m.Skip(4) // reserved
|
|
m.Skip(4) // duration
|
|
m.Skip(8) // reserved
|
|
m.Skip(2) // layer
|
|
if width > 0 {
|
|
m.Skip(2)
|
|
m.Skip(2)
|
|
} else {
|
|
m.WriteUint16(1) // alternate group
|
|
m.WriteFloat16(1) // volume
|
|
}
|
|
m.Skip(2) // reserved
|
|
m.WriteMatrix()
|
|
if width > 0 {
|
|
m.WriteFloat32(float64(width))
|
|
m.WriteFloat32(float64(height))
|
|
} else {
|
|
m.Skip(4)
|
|
m.Skip(4)
|
|
}
|
|
m.EndAtom()
|
|
}
|
|
|
|
func (m *Movie) WriteMediaHeader(timescale uint32) {
|
|
// https://developer.apple.com/library/archive/documentation/QuickTime/QTFF/QTFFChap2/qtff2.html#//apple_ref/doc/uid/TP40000939-CH204-32999
|
|
m.StartAtom(MoovTrakMdiaMdhd)
|
|
m.Skip(1) // version
|
|
m.Skip(3) // flags
|
|
m.Skip(4) // creation time
|
|
m.Skip(4) // modification time
|
|
m.WriteUint32(timescale) // timescale
|
|
m.Skip(4) // duration
|
|
m.WriteUint16(0x55C4) // language (Unspecified)
|
|
m.Skip(2) // quality
|
|
m.EndAtom()
|
|
}
|
|
|
|
func (m *Movie) WriteMediaHandler(s, name string) {
|
|
// https://developer.apple.com/library/archive/documentation/QuickTime/QTFF/QTFFChap2/qtff2.html#//apple_ref/doc/uid/TP40000939-CH204-33004
|
|
m.StartAtom(MoovTrakMdiaHdlr)
|
|
m.Skip(1) // version
|
|
m.Skip(3) // flags
|
|
m.Skip(4)
|
|
m.WriteString(s) // handler type (4 byte!)
|
|
m.Skip(3 * 4) // reserved
|
|
m.WriteString(name) // handler name (any len)
|
|
m.Skip(1) // end string
|
|
m.EndAtom()
|
|
}
|
|
|
|
func (m *Movie) WriteVideoMediaInfo() {
|
|
// https://developer.apple.com/library/archive/documentation/QuickTime/QTFF/QTFFChap2/qtff2.html#//apple_ref/doc/uid/TP40000939-CH204-33012
|
|
m.StartAtom(MoovTrakMdiaMinfVmhd)
|
|
m.Skip(1) // version
|
|
m.WriteUint24(1) // flags (You should always set this flag to 1)
|
|
m.Skip(2) // graphics mode
|
|
m.Skip(3 * 2) // op color
|
|
m.EndAtom()
|
|
}
|
|
|
|
func (m *Movie) WriteAudioMediaInfo() {
|
|
m.StartAtom(MoovTrakMdiaMinfSmhd)
|
|
m.Skip(1) // version
|
|
m.Skip(3) // flags
|
|
m.Skip(4) // balance
|
|
m.EndAtom()
|
|
}
|
|
|
|
func (m *Movie) WriteDataInfo() {
|
|
// https://developer.apple.com/library/archive/documentation/QuickTime/QTFF/QTFFChap2/qtff2.html#//apple_ref/doc/uid/TP40000939-CH204-25680
|
|
m.StartAtom(MoovTrakMdiaMinfDinf)
|
|
m.StartAtom(MoovTrakMdiaMinfDinfDref)
|
|
m.Skip(1) // version
|
|
m.Skip(3) // flags
|
|
m.WriteUint32(1) // childrens
|
|
|
|
m.StartAtom(MoovTrakMdiaMinfDinfDrefUrl)
|
|
m.Skip(1) // version
|
|
m.WriteUint24(1) // flags (self reference)
|
|
m.EndAtom()
|
|
|
|
m.EndAtom() // DREF
|
|
m.EndAtom() // DINF
|
|
}
|
|
|
|
func (m *Movie) WriteSampleTable(writeSampleDesc func()) {
|
|
// https://developer.apple.com/library/archive/documentation/QuickTime/QTFF/QTFFChap2/qtff2.html#//apple_ref/doc/uid/TP40000939-CH204-33040
|
|
m.StartAtom(MoovTrakMdiaMinfStbl)
|
|
|
|
m.StartAtom(MoovTrakMdiaMinfStblStsd)
|
|
m.Skip(1) // version
|
|
m.Skip(3) // flags
|
|
m.WriteUint32(1) // entry count
|
|
writeSampleDesc()
|
|
m.EndAtom()
|
|
|
|
m.StartAtom(MoovTrakMdiaMinfStblStts)
|
|
m.Skip(1) // version
|
|
m.Skip(3) // flags
|
|
m.Skip(4) // entry count
|
|
m.EndAtom()
|
|
|
|
m.StartAtom(MoovTrakMdiaMinfStblStsc)
|
|
m.Skip(1) // version
|
|
m.Skip(3) // flags
|
|
m.Skip(4) // entry count
|
|
m.EndAtom()
|
|
|
|
m.StartAtom(MoovTrakMdiaMinfStblStsz)
|
|
m.Skip(1) // version
|
|
m.Skip(3) // flags
|
|
m.Skip(4) // sample size
|
|
m.Skip(4) // entry count
|
|
m.EndAtom()
|
|
|
|
m.StartAtom(MoovTrakMdiaMinfStblStco)
|
|
m.Skip(1) // version
|
|
m.Skip(3) // flags
|
|
m.Skip(4) // entry count
|
|
m.EndAtom()
|
|
|
|
m.EndAtom()
|
|
}
|
|
|
|
func (m *Movie) WriteTrackExtend(id uint32) {
|
|
m.StartAtom(MoovMvexTrex)
|
|
m.Skip(1) // version
|
|
m.Skip(3) // flags
|
|
m.WriteUint32(id) // trackID
|
|
m.WriteUint32(1) // default sample description index
|
|
m.Skip(4) // default sample duration
|
|
m.Skip(4) // default sample size
|
|
m.Skip(4) // default sample flags
|
|
m.EndAtom()
|
|
}
|
|
|
|
func (m *Movie) WriteVideoTrack(id uint32, codec string, timescale uint32, width, height uint16, conf []byte) {
|
|
m.StartAtom(MoovTrak)
|
|
m.WriteTrackHeader(id, width, height)
|
|
|
|
m.StartAtom(MoovTrakMdia)
|
|
m.WriteMediaHeader(timescale)
|
|
m.WriteMediaHandler("vide", "VideoHandler")
|
|
|
|
m.StartAtom(MoovTrakMdiaMinf)
|
|
m.WriteVideoMediaInfo()
|
|
m.WriteDataInfo()
|
|
m.WriteSampleTable(func() {
|
|
m.WriteVideo(codec, width, height, conf)
|
|
})
|
|
m.EndAtom() // MINF
|
|
|
|
m.EndAtom() // MDIA
|
|
m.EndAtom() // TRAK
|
|
}
|
|
|
|
func (m *Movie) WriteAudioTrack(id uint32, codec string, timescale uint32, channels uint16, conf []byte) {
|
|
m.StartAtom(MoovTrak)
|
|
m.WriteTrackHeader(id, 0, 0)
|
|
|
|
m.StartAtom(MoovTrakMdia)
|
|
m.WriteMediaHeader(timescale)
|
|
m.WriteMediaHandler("soun", "SoundHandler")
|
|
|
|
m.StartAtom(MoovTrakMdiaMinf)
|
|
m.WriteAudioMediaInfo()
|
|
m.WriteDataInfo()
|
|
m.WriteSampleTable(func() {
|
|
m.WriteAudio(codec, channels, timescale, conf)
|
|
})
|
|
m.EndAtom() // MINF
|
|
|
|
m.EndAtom() // MDIA
|
|
m.EndAtom() // TRAK
|
|
}
|
|
|
|
const (
|
|
TfhdDefaultSampleDuration = 0x000008
|
|
TfhdDefaultSampleSize = 0x000010
|
|
TfhdDefaultSampleFlags = 0x000020
|
|
TfhdDefaultBaseIsMoof = 0x020000
|
|
)
|
|
|
|
const (
|
|
TrunDataOffset = 0x000001
|
|
TrunFirstSampleFlags = 0x000004
|
|
TrunSampleDuration = 0x0000100
|
|
TrunSampleSize = 0x0000200
|
|
TrunSampleFlags = 0x0000400
|
|
TrunSampleCTS = 0x0000800
|
|
)
|
|
|
|
func (m *Movie) WriteMovieFragment(seq, tid, duration, size, flags uint32, dts uint64, cts uint32) {
|
|
m.StartAtom(Moof)
|
|
|
|
m.StartAtom(MoofMfhd)
|
|
m.Skip(1) // version
|
|
m.Skip(3) // flags
|
|
m.WriteUint32(seq) // sequence number
|
|
m.EndAtom()
|
|
|
|
m.StartAtom(MoofTraf)
|
|
|
|
m.StartAtom(MoofTrafTfhd)
|
|
m.Skip(1) // version
|
|
m.WriteUint24(
|
|
TfhdDefaultSampleDuration |
|
|
TfhdDefaultSampleSize |
|
|
TfhdDefaultSampleFlags |
|
|
TfhdDefaultBaseIsMoof,
|
|
)
|
|
m.WriteUint32(tid) // track id
|
|
m.WriteUint32(duration) // default sample duration
|
|
m.WriteUint32(size) // default sample size
|
|
m.WriteUint32(flags) // default sample flags
|
|
m.EndAtom()
|
|
|
|
m.StartAtom(MoofTrafTfdt)
|
|
m.WriteBytes(1) // version
|
|
m.Skip(3) // flags
|
|
m.WriteUint64(dts) // base media decode time
|
|
m.EndAtom()
|
|
|
|
m.StartAtom(MoofTrafTrun)
|
|
m.Skip(1) // version
|
|
|
|
if cts == 0 {
|
|
m.WriteUint24(TrunDataOffset) // flags
|
|
m.WriteUint32(1) // sample count
|
|
|
|
// data offset: current pos + uint32 len + MDAT header len
|
|
m.WriteUint32(uint32(len(m.b)) + 4 + 8)
|
|
} else {
|
|
m.WriteUint24(TrunDataOffset | TrunSampleCTS)
|
|
m.WriteUint32(1)
|
|
|
|
// data offset: current pos + uint32 len + CTS + MDAT header len
|
|
m.WriteUint32(uint32(len(m.b)) + 4 + 4 + 8)
|
|
m.WriteUint32(cts)
|
|
}
|
|
|
|
m.EndAtom() // TRUN
|
|
|
|
m.EndAtom() // TRAF
|
|
|
|
m.EndAtom() // MOOF
|
|
}
|
|
|
|
func (m *Movie) WriteData(b []byte) {
|
|
m.StartAtom(Mdat)
|
|
m.Write(b)
|
|
m.EndAtom()
|
|
}
|