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() }