package box import ( "bytes" "encoding/binary" "io" ) // aligned(8) abstract class SampleEntry (unsigned int(32) format) extends Box(format){ // const unsigned int(8)[6] reserved = 0; // unsigned int(16) data_reference_index; // } type SampleEntry struct { BaseBox DataReferenceIndex uint16 } // class HintSampleEntry() extends SampleEntry (protocol) { // unsigned int(8) data []; // } type HintSampleEntry struct { SampleEntry Data []byte } // class AudioSampleEntry(codingname) extends SampleEntry (codingname){ // const unsigned int(32)[2] reserved = 0; // template unsigned int(16) channelcount = 2; // template unsigned int(16) samplesize = 16; // unsigned int(16) pre_defined = 0; // const unsigned int(16) reserved = 0 ; // template unsigned int(32) samplerate = { default samplerate of media}<<16; // } type AudioSampleEntry struct { SampleEntry Version uint16 // ffmpeg mov.c mov_parse_stsd_audio ChannelCount uint16 SampleSize uint16 Samplerate uint32 ExtraData IBox } func (s *SampleEntry) WriteTo(w io.Writer) (n int64, err error) { var tmp [8]byte binary.BigEndian.PutUint16(tmp[6:], s.DataReferenceIndex) _, err = w.Write(tmp[:]) return 8, err } func (s *SampleEntry) Unmarshal(buf []byte) { s.DataReferenceIndex = binary.BigEndian.Uint16(buf[6:]) } func CreateAudioSampleEntry(codecName BoxType, channelCount uint16, sampleSize uint16, samplerate uint32, extraData IBox) *AudioSampleEntry { size := 28 + BasicBoxLen if extraData != nil { size += int(extraData.Size()) } return &AudioSampleEntry{ SampleEntry: SampleEntry{ BaseBox: BaseBox{ typ: codecName, size: uint32(size), }, DataReferenceIndex: 1, }, Version: 0, ChannelCount: channelCount, SampleSize: sampleSize, Samplerate: samplerate, ExtraData: extraData, } } func (audio *AudioSampleEntry) WriteTo(w io.Writer) (n int64, err error) { n, err = audio.SampleEntry.WriteTo(w) if err != nil { return } var buf [20]byte binary.BigEndian.PutUint16(buf[:], audio.Version) binary.BigEndian.PutUint16(buf[8:], audio.ChannelCount) binary.BigEndian.PutUint16(buf[10:], audio.SampleSize) binary.BigEndian.PutUint32(buf[16:], audio.Samplerate<<16) _, err = w.Write(buf[:]) n += 20 var nn int64 if audio.ExtraData != nil { nn, err = WriteTo(w, audio.ExtraData) if err != nil { return } n += nn } return } func (audio *AudioSampleEntry) Unmarshal(buf []byte) (IBox, error) { audio.SampleEntry.Unmarshal(buf) buf = buf[8:] audio.Version = binary.BigEndian.Uint16(buf) audio.ChannelCount = binary.BigEndian.Uint16(buf[8:]) audio.SampleSize = binary.BigEndian.Uint16(buf[10:]) audio.Samplerate = binary.BigEndian.Uint32(buf[16:]) >> 16 if len(buf) > 20 { box, err := ReadFrom(bytes.NewReader(buf[20:])) if err != nil { return nil, err } audio.ExtraData = box } return audio, nil } // class VisualSampleEntry(codingname) extends SampleEntry (codingname){ // unsigned int(16) pre_defined = 0; // const unsigned int(16) reserved = 0; // unsigned int(32)[3] pre_defined = 0; // unsigned int(16) width; // unsigned int(16) height; // template unsigned int(32) horizresolution = 0x00480000; // 72 dpi // template unsigned int(32) vertresolution = 0x00480000; // 72 dpi // const unsigned int(32) reserved = 0; // template unsigned int(16) frame_count = 1; // string[32] compressorname; // template unsigned int(16) depth = 0x0018; // int(16) pre_defined = -1; // // other boxes from derived specifications // CleanApertureBox clap; // optional // PixelAspectRatioBox pasp; // optional // } type VisualSampleEntry struct { SampleEntry Width, Height uint16 Horizresolution, Vertresolution uint32 FrameCount uint16 Compressorname [32]byte Depth uint16 ExtraData IBox } func CreateVisualSampleEntry(codecName BoxType, width, height uint16, extraData IBox) *VisualSampleEntry { size := 78 + BasicBoxLen if extraData != nil { size += int(extraData.Size()) } return &VisualSampleEntry{ SampleEntry: SampleEntry{ BaseBox: BaseBox{ typ: codecName, size: uint32(size), }, DataReferenceIndex: 1, }, Width: width, Height: height, Horizresolution: 0x00480000, Vertresolution: 0x00480000, FrameCount: 1, Depth: 0x0018, ExtraData: extraData, } } func (visual *VisualSampleEntry) WriteTo(w io.Writer) (n int64, err error) { n, err = visual.SampleEntry.WriteTo(w) if err != nil { return } var buf [70]byte // 16(pre_defined) + 2(width) + 2(height) + 4(horiz) + 4(vert) + 4(reserved) + 2(frame) + 32(compressor) + 2(depth) + 2(pre_defined) binary.BigEndian.PutUint16(buf[16:], visual.Width) binary.BigEndian.PutUint16(buf[18:], visual.Height) binary.BigEndian.PutUint32(buf[20:], visual.Horizresolution) binary.BigEndian.PutUint32(buf[24:], visual.Vertresolution) binary.BigEndian.PutUint16(buf[32:], visual.FrameCount) copy(buf[34:66], visual.Compressorname[:]) binary.BigEndian.PutUint16(buf[66:], visual.Depth) binary.BigEndian.PutUint16(buf[68:], 0xFFFF) // pre_defined = -1 _, err = w.Write(buf[:]) n += 70 var nn int64 if visual.ExtraData != nil { nn, err = WriteTo(w, visual.ExtraData) if err != nil { return } n += nn } return } func (visual *VisualSampleEntry) Unmarshal(buf []byte) (IBox, error) { visual.SampleEntry.Unmarshal(buf) buf = buf[24:] // Skip 8 bytes from SampleEntry + 16 bytes pre_defined visual.Width = binary.BigEndian.Uint16(buf[0:]) visual.Height = binary.BigEndian.Uint16(buf[2:]) visual.Horizresolution = binary.BigEndian.Uint32(buf[4:]) visual.Vertresolution = binary.BigEndian.Uint32(buf[8:]) visual.FrameCount = binary.BigEndian.Uint16(buf[16:]) copy(visual.Compressorname[:], buf[18:50]) visual.Depth = binary.BigEndian.Uint16(buf[50:]) // 52 pre-defined if len(buf) > 54 { box, err := ReadFrom(bytes.NewReader(buf[54:])) if err != nil { return nil, err } visual.ExtraData = box } return visual, nil } // aligned(8) class SampleDescriptionBox (unsigned int(32) handler_type) extends FullBox('stsd', 0, 0){ // int i ; // unsigned int(32) entry_count; // for (i = 1 ; i <= entry_count ; i++){ // switch (handler_type){ // case 'soun': // for audio tracks // AudioSampleEntry(); // break; // case 'vide': // for video tracks // VisualSampleEntry(); // break; // case 'hint': // Hint track // HintSampleEntry(); // break; // case 'meta': // Metadata track // MetadataSampleEntry(); // break; // } // } // } type SampleEntryType uint8 const ( SAMPLE_AUDIO SampleEntryType = iota SAMPLE_VIDEO ) type STSDBox struct { FullBox Entries []IBox } func CreateSTSDBox(entries ...IBox) *STSDBox { childSize := 0 for _, entry := range entries { childSize += int(entry.Size()) } return &STSDBox{ FullBox: FullBox{ BaseBox: BaseBox{ typ: TypeSTSD, size: uint32(FullBoxLen + 4 + childSize), }, }, Entries: entries, } } func (stsd *STSDBox) Unmarshal(buf []byte) (IBox, error) { stsd.Entries = make([]IBox, 0, binary.BigEndian.Uint32(buf)) r := bytes.NewReader(buf[4:]) for { box, err := ReadFrom(r) if err != nil { break } stsd.Entries = append(stsd.Entries, box) } return stsd, nil } func (stsd *STSDBox) WriteTo(w io.Writer) (n int64, err error) { var tmp [4]byte var nn int64 binary.BigEndian.PutUint32(tmp[:], uint32(len(stsd.Entries))) _, err = w.Write(tmp[:]) if err != nil { return } n += 4 for _, entry := range stsd.Entries { nn, err = WriteTo(w, entry) if err != nil { return } n += nn } return int64(n), nil } func (h *HintSampleEntry) Unmarshal(buf []byte) (IBox, error) { h.SampleEntry.Unmarshal(buf) h.Data = buf[8:] return h, nil } func (h *HintSampleEntry) WriteTo(w io.Writer) (n int64, err error) { var offset int64 offset, err = h.SampleEntry.WriteTo(w) if err != nil { return } _, err = w.Write(h.Data) return offset + int64(len(h.Data)), err } func init() { RegisterBox[*STSDBox](TypeSTSD) RegisterBox[*AudioSampleEntry](TypeMP4A, TypeULAW, TypeALAW, TypeOPUS, TypeENCA) RegisterBox[*VisualSampleEntry](TypeAVC1, TypeHVC1, TypeHEV1, TypeENCV) RegisterBox[*HintSampleEntry](TypeHINT) RegisterBox[*DataBox](TypeAVCC, TypeHVCC) // RegisterBox[*MetadataSampleEntry](TypeMETA) } //ffmpeg mov_write_wave_tag // avio_wb32(pb, 12); /* size */ // ffio_wfourcc(pb, "frma"); // avio_wl32(pb, track->tag); // avio_wb32(pb, 12); /* size */ // ffio_wfourcc(pb, "mp4a"); // avio_wb32(pb, 0);