package mp4 import ( "bytes" "errors" "fmt" "io" "github.com/abema/go-mp4/bitio" "github.com/abema/go-mp4/util" "github.com/google/uuid" ) /*************************** btrt ****************************/ func BoxTypeBtrt() BoxType { return StrToBoxType("btrt") } func init() { AddBoxDef(&Btrt{}, 0) } type Btrt struct { Box BufferSizeDB uint32 `mp4:"0,size=32"` MaxBitrate uint32 `mp4:"1,size=32"` AvgBitrate uint32 `mp4:"2,size=32"` } // GetType returns the BoxType func (*Btrt) GetType() BoxType { return BoxTypeBtrt() } /*************************** co64 ****************************/ func BoxTypeCo64() BoxType { return StrToBoxType("co64") } func init() { AddBoxDef(&Co64{}, 0) } type Co64 struct { FullBox `mp4:"0,extend"` EntryCount uint32 `mp4:"1,size=32"` ChunkOffset []uint64 `mp4:"2,size=64,len=dynamic"` } // GetType returns the BoxType func (*Co64) GetType() BoxType { return BoxTypeCo64() } // GetFieldLength returns length of dynamic field func (co64 *Co64) GetFieldLength(name string, ctx Context) uint { switch name { case "ChunkOffset": return uint(co64.EntryCount) } panic(fmt.Errorf("invalid name of dynamic-length field: boxType=co64 fieldName=%s", name)) } /*************************** colr ****************************/ func BoxTypeColr() BoxType { return StrToBoxType("colr") } func init() { AddBoxDef(&Colr{}) } type Colr struct { Box ColourType [4]byte `mp4:"0,size=8,string"` ColourPrimaries uint16 `mp4:"1,size=16,opt=dynamic"` TransferCharacteristics uint16 `mp4:"2,size=16,opt=dynamic"` MatrixCoefficients uint16 `mp4:"3,size=16,opt=dynamic"` FullRangeFlag bool `mp4:"4,size=1,opt=dynamic"` Reserved uint8 `mp4:"5,size=7,opt=dynamic"` Profile []byte `mp4:"6,size=8,opt=dynamic"` Unknown []byte `mp4:"7,size=8,opt=dynamic"` } func (colr *Colr) IsOptFieldEnabled(name string, ctx Context) bool { switch colr.ColourType { case [4]byte{'n', 'c', 'l', 'x'}: switch name { case "ColourType", "ColourPrimaries", "TransferCharacteristics", "MatrixCoefficients", "FullRangeFlag", "Reserved": return true default: return false } case [4]byte{'r', 'I', 'C', 'C'}, [4]byte{'p', 'r', 'o', 'f'}: return name == "Profile" default: return name == "Unknown" } } // GetType returns the BoxType func (*Colr) GetType() BoxType { return BoxTypeColr() } /*************************** cslg ****************************/ func BoxTypeCslg() BoxType { return StrToBoxType("cslg") } func init() { AddBoxDef(&Cslg{}, 0, 1) } type Cslg struct { FullBox `mp4:"0,extend"` CompositionToDTSShiftV0 int32 `mp4:"1,size=32,ver=0"` LeastDecodeToDisplayDeltaV0 int32 `mp4:"2,size=32,ver=0"` GreatestDecodeToDisplayDeltaV0 int32 `mp4:"3,size=32,ver=0"` CompositionStartTimeV0 int32 `mp4:"4,size=32,ver=0"` CompositionEndTimeV0 int32 `mp4:"5,size=32,ver=0"` CompositionToDTSShiftV1 int64 `mp4:"6,size=64,nver=0"` LeastDecodeToDisplayDeltaV1 int64 `mp4:"7,size=64,nver=0"` GreatestDecodeToDisplayDeltaV1 int64 `mp4:"8,size=64,nver=0"` CompositionStartTimeV1 int64 `mp4:"9,size=64,nver=0"` CompositionEndTimeV1 int64 `mp4:"10,size=64,nver=0"` } // GetType returns the BoxType func (*Cslg) GetType() BoxType { return BoxTypeCslg() } func (cslg *Cslg) GetCompositionToDTSShift() int64 { switch cslg.GetVersion() { case 0: return int64(cslg.CompositionToDTSShiftV0) case 1: return cslg.CompositionToDTSShiftV1 default: return 0 } } func (cslg *Cslg) GetLeastDecodeToDisplayDelta() int64 { switch cslg.GetVersion() { case 0: return int64(cslg.LeastDecodeToDisplayDeltaV0) case 1: return cslg.LeastDecodeToDisplayDeltaV1 default: return 0 } } func (cslg *Cslg) GetGreatestDecodeToDisplayDelta() int64 { switch cslg.GetVersion() { case 0: return int64(cslg.GreatestDecodeToDisplayDeltaV0) case 1: return cslg.GreatestDecodeToDisplayDeltaV1 default: return 0 } } func (cslg *Cslg) GetCompositionStartTime() int64 { switch cslg.GetVersion() { case 0: return int64(cslg.CompositionStartTimeV0) case 1: return cslg.CompositionStartTimeV1 default: return 0 } } func (cslg *Cslg) GetCompositionEndTime() int64 { switch cslg.GetVersion() { case 0: return int64(cslg.CompositionEndTimeV0) case 1: return cslg.CompositionEndTimeV1 default: return 0 } } /*************************** ctts ****************************/ func BoxTypeCtts() BoxType { return StrToBoxType("ctts") } func init() { AddBoxDef(&Ctts{}, 0, 1) } type Ctts struct { FullBox `mp4:"0,extend"` EntryCount uint32 `mp4:"1,size=32"` Entries []CttsEntry `mp4:"2,len=dynamic,size=64"` } type CttsEntry struct { SampleCount uint32 `mp4:"0,size=32"` SampleOffsetV0 uint32 `mp4:"1,size=32,ver=0"` SampleOffsetV1 int32 `mp4:"2,size=32,ver=1"` } // GetType returns the BoxType func (*Ctts) GetType() BoxType { return BoxTypeCtts() } // GetFieldLength returns length of dynamic field func (ctts *Ctts) GetFieldLength(name string, ctx Context) uint { switch name { case "Entries": return uint(ctts.EntryCount) } panic(fmt.Errorf("invalid name of dynamic-length field: boxType=ctts fieldName=%s", name)) } func (ctts *Ctts) GetSampleOffset(index int) int64 { switch ctts.GetVersion() { case 0: return int64(ctts.Entries[index].SampleOffsetV0) case 1: return int64(ctts.Entries[index].SampleOffsetV1) default: return 0 } } /*************************** dinf ****************************/ func BoxTypeDinf() BoxType { return StrToBoxType("dinf") } func init() { AddBoxDef(&Dinf{}) } // Dinf is ISOBMFF dinf box type type Dinf struct { Box } // GetType returns the BoxType func (*Dinf) GetType() BoxType { return BoxTypeDinf() } /*************************** dref ****************************/ func BoxTypeDref() BoxType { return StrToBoxType("dref") } func BoxTypeUrl() BoxType { return StrToBoxType("url ") } func BoxTypeUrn() BoxType { return StrToBoxType("urn ") } func init() { AddBoxDef(&Dref{}, 0) AddBoxDef(&Url{}, 0) AddBoxDef(&Urn{}, 0) } // Dref is ISOBMFF dref box type type Dref struct { FullBox `mp4:"0,extend"` EntryCount uint32 `mp4:"1,size=32"` } // GetType returns the BoxType func (*Dref) GetType() BoxType { return BoxTypeDref() } type Url struct { FullBox `mp4:"0,extend"` Location string `mp4:"1,string,nopt=0x000001"` } func (*Url) GetType() BoxType { return BoxTypeUrl() } const UrlSelfContained = 0x000001 type Urn struct { FullBox `mp4:"0,extend"` Name string `mp4:"1,string,nopt=0x000001"` Location string `mp4:"2,string,nopt=0x000001"` } func (*Urn) GetType() BoxType { return BoxTypeUrn() } const UrnSelfContained = 0x000001 /*************************** edts ****************************/ func BoxTypeEdts() BoxType { return StrToBoxType("edts") } func init() { AddBoxDef(&Edts{}) } // Edts is ISOBMFF edts box type type Edts struct { Box } // GetType returns the BoxType func (*Edts) GetType() BoxType { return BoxTypeEdts() } /*************************** elst ****************************/ func BoxTypeElst() BoxType { return StrToBoxType("elst") } func init() { AddBoxDef(&Elst{}, 0, 1) } // Elst is ISOBMFF elst box type type Elst struct { FullBox `mp4:"0,extend"` EntryCount uint32 `mp4:"1,size=32"` Entries []ElstEntry `mp4:"2,len=dynamic,size=dynamic"` } type ElstEntry struct { SegmentDurationV0 uint32 `mp4:"0,size=32,ver=0"` MediaTimeV0 int32 `mp4:"1,size=32,ver=0"` SegmentDurationV1 uint64 `mp4:"2,size=64,ver=1"` MediaTimeV1 int64 `mp4:"3,size=64,ver=1"` MediaRateInteger int16 `mp4:"4,size=16"` MediaRateFraction int16 `mp4:"5,size=16,const=0"` } // GetType returns the BoxType func (*Elst) GetType() BoxType { return BoxTypeElst() } // GetFieldSize returns size of dynamic field func (elst *Elst) GetFieldSize(name string, ctx Context) uint { switch name { case "Entries": switch elst.GetVersion() { case 0: return 0 + /* segmentDurationV0 */ 32 + /* mediaTimeV0 */ 32 + /* mediaRateInteger */ 16 + /* mediaRateFraction */ 16 case 1: return 0 + /* segmentDurationV1 */ 64 + /* mediaTimeV1 */ 64 + /* mediaRateInteger */ 16 + /* mediaRateFraction */ 16 } } panic(fmt.Errorf("invalid name of dynamic-size field: boxType=elst fieldName=%s", name)) } // GetFieldLength returns length of dynamic field func (elst *Elst) GetFieldLength(name string, ctx Context) uint { switch name { case "Entries": return uint(elst.EntryCount) } panic(fmt.Errorf("invalid name of dynamic-length field: boxType=elst fieldName=%s", name)) } func (elst *Elst) GetSegmentDuration(index int) uint64 { switch elst.GetVersion() { case 0: return uint64(elst.Entries[index].SegmentDurationV0) case 1: return elst.Entries[index].SegmentDurationV1 default: return 0 } } func (elst *Elst) GetMediaTime(index int) int64 { switch elst.GetVersion() { case 0: return int64(elst.Entries[index].MediaTimeV0) case 1: return elst.Entries[index].MediaTimeV1 default: return 0 } } /*************************** emsg ****************************/ func BoxTypeEmsg() BoxType { return StrToBoxType("emsg") } func init() { AddBoxDef(&Emsg{}, 0, 1) } // Emsg is ISOBMFF emsg box type type Emsg struct { FullBox `mp4:"0,extend"` SchemeIdUri string `mp4:"1,string"` Value string `mp4:"2,string"` Timescale uint32 `mp4:"3,size=32"` PresentationTimeDelta uint32 `mp4:"4,size=32,ver=0"` PresentationTime uint64 `mp4:"5,size=64,ver=1"` EventDuration uint32 `mp4:"6,size=32"` Id uint32 `mp4:"7,size=32"` MessageData []byte `mp4:"8,size=8,string"` } func (emsg *Emsg) OnReadField(name string, r bitio.ReadSeeker, leftBits uint64, ctx Context) (rbits uint64, override bool, err error) { if emsg.GetVersion() == 0 { return } switch name { case "SchemeIdUri", "Value": override = true return case "MessageData": emsg.SchemeIdUri, err = util.ReadString(r) if err != nil { return } emsg.Value, err = util.ReadString(r) if err != nil { return } rbits += uint64(len(emsg.SchemeIdUri)+len(emsg.Value)+2) * 8 return default: return } } func (emsg *Emsg) OnWriteField(name string, w bitio.Writer, ctx Context) (wbits uint64, override bool, err error) { if emsg.GetVersion() == 0 { return } switch name { case "SchemeIdUri", "Value": override = true return case "MessageData": if err = util.WriteString(w, emsg.SchemeIdUri); err != nil { return } if err = util.WriteString(w, emsg.Value); err != nil { return } wbits += uint64(len(emsg.SchemeIdUri)+len(emsg.Value)+2) * 8 return default: return } } // GetType returns the BoxType func (*Emsg) GetType() BoxType { return BoxTypeEmsg() } /*************************** esds ****************************/ // https://developer.apple.com/library/content/documentation/QuickTime/QTFF/QTFFChap3/qtff3.html func BoxTypeEsds() BoxType { return StrToBoxType("esds") } func init() { AddBoxDef(&Esds{}, 0) } const ( ESDescrTag = 0x03 DecoderConfigDescrTag = 0x04 DecSpecificInfoTag = 0x05 SLConfigDescrTag = 0x06 ) // Esds is ES descripter box type Esds struct { FullBox `mp4:"0,extend"` Descriptors []Descriptor `mp4:"1,array"` } // GetType returns the BoxType func (*Esds) GetType() BoxType { return BoxTypeEsds() } type Descriptor struct { BaseCustomFieldObject Tag int8 `mp4:"0,size=8"` // must be 0x03 Size uint32 `mp4:"1,varint"` ESDescriptor *ESDescriptor `mp4:"2,extend,opt=dynamic"` DecoderConfigDescriptor *DecoderConfigDescriptor `mp4:"3,extend,opt=dynamic"` Data []byte `mp4:"4,size=8,opt=dynamic,len=dynamic"` } // GetFieldLength returns length of dynamic field func (ds *Descriptor) GetFieldLength(name string, ctx Context) uint { switch name { case "Data": return uint(ds.Size) } panic(fmt.Errorf("invalid name of dynamic-length field: boxType=esds fieldName=%s", name)) } func (ds *Descriptor) IsOptFieldEnabled(name string, ctx Context) bool { switch ds.Tag { case ESDescrTag: return name == "ESDescriptor" case DecoderConfigDescrTag: return name == "DecoderConfigDescriptor" default: return name == "Data" } } // StringifyField returns field value as string func (ds *Descriptor) StringifyField(name string, indent string, depth int, ctx Context) (string, bool) { switch name { case "Tag": switch ds.Tag { case ESDescrTag: return "ESDescr", true case DecoderConfigDescrTag: return "DecoderConfigDescr", true case DecSpecificInfoTag: return "DecSpecificInfo", true case SLConfigDescrTag: return "SLConfigDescr", true default: return "", false } default: return "", false } } type ESDescriptor struct { BaseCustomFieldObject ESID uint16 `mp4:"0,size=16"` StreamDependenceFlag bool `mp4:"1,size=1"` UrlFlag bool `mp4:"2,size=1"` OcrStreamFlag bool `mp4:"3,size=1"` StreamPriority int8 `mp4:"4,size=5"` DependsOnESID uint16 `mp4:"5,size=16,opt=dynamic"` URLLength uint8 `mp4:"6,size=8,opt=dynamic"` URLString []byte `mp4:"7,size=8,len=dynamic,opt=dynamic,string"` OCRESID uint16 `mp4:"8,size=16,opt=dynamic"` } func (esds *ESDescriptor) GetFieldLength(name string, ctx Context) uint { switch name { case "URLString": return uint(esds.URLLength) } panic(fmt.Errorf("invalid name of dynamic-length field: boxType=ESDescriptor fieldName=%s", name)) } func (esds *ESDescriptor) IsOptFieldEnabled(name string, ctx Context) bool { switch name { case "DependsOnESID": return esds.StreamDependenceFlag case "URLLength", "URLString": return esds.UrlFlag case "OCRESID": return esds.OcrStreamFlag default: return false } } type DecoderConfigDescriptor struct { BaseCustomFieldObject ObjectTypeIndication byte `mp4:"0,size=8"` StreamType int8 `mp4:"1,size=6"` UpStream bool `mp4:"2,size=1"` Reserved bool `mp4:"3,size=1"` BufferSizeDB uint32 `mp4:"4,size=24"` MaxBitrate uint32 `mp4:"5,size=32"` AvgBitrate uint32 `mp4:"6,size=32"` } /*************************** fiel ****************************/ func BoxTypeFiel() BoxType { return StrToBoxType("fiel") } func init() { AddBoxDef(&Fiel{}) } type Fiel struct { Box FieldCount uint8 `mp4:"0,size=8"` FieldOrdering uint8 `mp4:"1,size=8"` } func (Fiel) GetType() BoxType { return BoxTypeFiel() } /************************ free, skip *************************/ func BoxTypeFree() BoxType { return StrToBoxType("free") } func BoxTypeSkip() BoxType { return StrToBoxType("skip") } func init() { AddBoxDef(&Free{}) AddBoxDef(&Skip{}) } type FreeSpace struct { Box Data []uint8 `mp4:"0,size=8"` } type Free FreeSpace func (*Free) GetType() BoxType { return BoxTypeFree() } type Skip FreeSpace func (*Skip) GetType() BoxType { return BoxTypeSkip() } /*************************** frma ****************************/ func BoxTypeFrma() BoxType { return StrToBoxType("frma") } func init() { AddBoxDef(&Frma{}) } // Frma is ISOBMFF frma box type type Frma struct { Box DataFormat [4]byte `mp4:"0,size=8,string"` } // GetType returns the BoxType func (*Frma) GetType() BoxType { return BoxTypeFrma() } /*************************** ftyp ****************************/ func BoxTypeFtyp() BoxType { return StrToBoxType("ftyp") } func init() { AddBoxDef(&Ftyp{}) } func BrandQT() [4]byte { return [4]byte{'q', 't', ' ', ' '} } func BrandISOM() [4]byte { return [4]byte{'i', 's', 'o', 'm'} } func BrandISO2() [4]byte { return [4]byte{'i', 's', 'o', '2'} } func BrandISO3() [4]byte { return [4]byte{'i', 's', 'o', '3'} } func BrandISO4() [4]byte { return [4]byte{'i', 's', 'o', '4'} } func BrandISO5() [4]byte { return [4]byte{'i', 's', 'o', '5'} } func BrandISO6() [4]byte { return [4]byte{'i', 's', 'o', '6'} } func BrandISO7() [4]byte { return [4]byte{'i', 's', 'o', '7'} } func BrandISO8() [4]byte { return [4]byte{'i', 's', 'o', '8'} } func BrandISO9() [4]byte { return [4]byte{'i', 's', 'o', '9'} } func BrandAVC1() [4]byte { return [4]byte{'a', 'v', 'c', '1'} } func BrandMP41() [4]byte { return [4]byte{'m', 'p', '4', '1'} } func BrandMP71() [4]byte { return [4]byte{'m', 'p', '7', '1'} } // Ftyp is ISOBMFF ftyp box type type Ftyp struct { Box MajorBrand [4]byte `mp4:"0,size=8,string"` MinorVersion uint32 `mp4:"1,size=32"` CompatibleBrands []CompatibleBrandElem `mp4:"2,size=32"` // reach to end of the box } type CompatibleBrandElem struct { CompatibleBrand [4]byte `mp4:"0,size=8,string"` } func (ftyp *Ftyp) AddCompatibleBrand(cb [4]byte) { if !ftyp.HasCompatibleBrand(cb) { ftyp.CompatibleBrands = append(ftyp.CompatibleBrands, CompatibleBrandElem{ CompatibleBrand: cb, }) } } func (ftyp *Ftyp) RemoveCompatibleBrand(cb [4]byte) { for i := 0; i < len(ftyp.CompatibleBrands); { if ftyp.CompatibleBrands[i].CompatibleBrand != cb { i++ continue } ftyp.CompatibleBrands[i] = ftyp.CompatibleBrands[len(ftyp.CompatibleBrands)-1] ftyp.CompatibleBrands = ftyp.CompatibleBrands[:len(ftyp.CompatibleBrands)-1] } } func (ftyp *Ftyp) HasCompatibleBrand(cb [4]byte) bool { for i := range ftyp.CompatibleBrands { if ftyp.CompatibleBrands[i].CompatibleBrand == cb { return true } } return false } // GetType returns the BoxType func (*Ftyp) GetType() BoxType { return BoxTypeFtyp() } /*************************** hdlr ****************************/ func BoxTypeHdlr() BoxType { return StrToBoxType("hdlr") } func init() { AddBoxDef(&Hdlr{}, 0) } // Hdlr is ISOBMFF hdlr box type type Hdlr struct { FullBox `mp4:"0,extend"` // Predefined corresponds to component_type of QuickTime. // pre_defined of ISO-14496 has always zero, // however component_type has "mhlr" or "dhlr". PreDefined uint32 `mp4:"1,size=32"` HandlerType [4]byte `mp4:"2,size=8,string"` Reserved [3]uint32 `mp4:"3,size=32,const=0"` Name string `mp4:"4,string"` } // GetType returns the BoxType func (*Hdlr) GetType() BoxType { return BoxTypeHdlr() } func (hdlr *Hdlr) OnReadField(name string, r bitio.ReadSeeker, leftBits uint64, ctx Context) (rbits uint64, override bool, err error) { switch name { case "Name": return hdlr.OnReadName(r, leftBits, ctx) default: return 0, false, nil } } func (hdlr *Hdlr) OnReadName(r bitio.ReadSeeker, leftBits uint64, ctx Context) (rbits uint64, override bool, err error) { size := leftBits / 8 if size == 0 { hdlr.Name = "" return 0, true, nil } buf := make([]byte, size) if _, err := io.ReadFull(r, buf); err != nil { return 0, false, err } plen := buf[0] if hdlr.PreDefined != 0 && size >= 2 && size == uint64(plen+1) { // Pascal-style String hdlr.Name = string(buf[1 : plen+1]) } else { // C-style String clen := 0 for _, c := range buf { if c == 0x00 { break } clen++ } hdlr.Name = string(buf[:clen]) } return leftBits, true, nil } /*************************** hvcC ****************************/ func BoxTypeHvcC() BoxType { return StrToBoxType("hvcC") } func init() { AddBoxDef(&HvcC{}) } type HEVCNalu struct { BaseCustomFieldObject Length uint16 `mp4:"0,size=16"` NALUnit []byte `mp4:"1,size=8,len=dynamic"` } func (s HEVCNalu) GetFieldLength(name string, ctx Context) uint { switch name { case "NALUnit": return uint(s.Length) } return 0 } type HEVCNaluArray struct { BaseCustomFieldObject Completeness bool `mp4:"0,size=1"` Reserved bool `mp4:"1,size=1"` NaluType uint8 `mp4:"2,size=6"` NumNalus uint16 `mp4:"3,size=16"` Nalus []HEVCNalu `mp4:"4,len=dynamic"` } func (a HEVCNaluArray) GetFieldLength(name string, ctx Context) uint { switch name { case "Nalus": return uint(a.NumNalus) } return 0 } type HvcC struct { Box ConfigurationVersion uint8 `mp4:"0,size=8"` GeneralProfileSpace uint8 `mp4:"1,size=2"` GeneralTierFlag bool `mp4:"2,size=1"` GeneralProfileIdc uint8 `mp4:"3,size=5"` GeneralProfileCompatibility [32]bool `mp4:"4,size=1"` GeneralConstraintIndicator [6]uint8 `mp4:"5,size=8"` GeneralLevelIdc uint8 `mp4:"6,size=8"` Reserved1 uint8 `mp4:"7,size=4,const=15"` MinSpatialSegmentationIdc uint16 `mp4:"8,size=12"` Reserved2 uint8 `mp4:"9,size=6,const=63"` ParallelismType uint8 `mp4:"10,size=2"` Reserved3 uint8 `mp4:"11,size=6,const=63"` ChromaFormatIdc uint8 `mp4:"12,size=2"` Reserved4 uint8 `mp4:"13,size=5,const=31"` BitDepthLumaMinus8 uint8 `mp4:"14,size=3"` Reserved5 uint8 `mp4:"15,size=5,const=31"` BitDepthChromaMinus8 uint8 `mp4:"16,size=3"` AvgFrameRate uint16 `mp4:"17,size=16"` ConstantFrameRate uint8 `mp4:"18,size=2"` NumTemporalLayers uint8 `mp4:"19,size=2"` TemporalIdNested uint8 `mp4:"20,size=2"` LengthSizeMinusOne uint8 `mp4:"21,size=2"` NumOfNaluArrays uint8 `mp4:"22,size=8"` NaluArrays []HEVCNaluArray `mp4:"23,len=dynamic"` } func (HvcC) GetType() BoxType { return BoxTypeHvcC() } func (hvcc HvcC) GetFieldLength(name string, ctx Context) uint { switch name { case "NaluArrays": return uint(hvcc.NumOfNaluArrays) } return 0 } /*************************** ilst ****************************/ func BoxTypeIlst() BoxType { return StrToBoxType("ilst") } func BoxTypeData() BoxType { return StrToBoxType("data") } var ilstMetaBoxTypes = []BoxType{ StrToBoxType("----"), StrToBoxType("aART"), StrToBoxType("akID"), StrToBoxType("apID"), StrToBoxType("atID"), StrToBoxType("cmID"), StrToBoxType("cnID"), StrToBoxType("covr"), StrToBoxType("cpil"), StrToBoxType("cprt"), StrToBoxType("desc"), StrToBoxType("disk"), StrToBoxType("egid"), StrToBoxType("geID"), StrToBoxType("gnre"), StrToBoxType("pcst"), StrToBoxType("pgap"), StrToBoxType("plID"), StrToBoxType("purd"), StrToBoxType("purl"), StrToBoxType("rtng"), StrToBoxType("sfID"), StrToBoxType("soaa"), StrToBoxType("soal"), StrToBoxType("soar"), StrToBoxType("soco"), StrToBoxType("sonm"), StrToBoxType("sosn"), StrToBoxType("stik"), StrToBoxType("tmpo"), StrToBoxType("trkn"), StrToBoxType("tven"), StrToBoxType("tves"), StrToBoxType("tvnn"), StrToBoxType("tvsh"), StrToBoxType("tvsn"), {0xA9, 'A', 'R', 'T'}, {0xA9, 'a', 'l', 'b'}, {0xA9, 'c', 'm', 't'}, {0xA9, 'c', 'o', 'm'}, {0xA9, 'd', 'a', 'y'}, {0xA9, 'g', 'e', 'n'}, {0xA9, 'g', 'r', 'p'}, {0xA9, 'n', 'a', 'm'}, {0xA9, 't', 'o', 'o'}, {0xA9, 'w', 'r', 't'}, } func IsIlstMetaBoxType(boxType BoxType) bool { for _, bt := range ilstMetaBoxTypes { if boxType == bt { return true } } return false } func init() { AddBoxDef(&Ilst{}) AddBoxDefEx(&Data{}, isUnderIlstMeta) for _, bt := range ilstMetaBoxTypes { AddAnyTypeBoxDefEx(&IlstMetaContainer{}, bt, isIlstMetaContainer) } AddAnyTypeBoxDefEx(&StringData{}, StrToBoxType("mean"), isUnderIlstFreeFormat) AddAnyTypeBoxDefEx(&StringData{}, StrToBoxType("name"), isUnderIlstFreeFormat) } type Ilst struct { Box } // GetType returns the BoxType func (*Ilst) GetType() BoxType { return BoxTypeIlst() } type IlstMetaContainer struct { AnyTypeBox } func isIlstMetaContainer(ctx Context) bool { return ctx.UnderIlst && !ctx.UnderIlstMeta } const ( DataTypeBinary = 0 DataTypeStringUTF8 = 1 DataTypeStringUTF16 = 2 DataTypeStringMac = 3 DataTypeStringJPEG = 14 DataTypeSignedIntBigEndian = 21 DataTypeFloat32BigEndian = 22 DataTypeFloat64BigEndian = 23 ) type Data struct { Box DataType uint32 `mp4:"0,size=32"` DataLang uint32 `mp4:"1,size=32"` Data []byte `mp4:"2,size=8"` } // GetType returns the BoxType func (*Data) GetType() BoxType { return BoxTypeData() } func isUnderIlstMeta(ctx Context) bool { return ctx.UnderIlstMeta } // StringifyField returns field value as string func (data *Data) StringifyField(name string, indent string, depth int, ctx Context) (string, bool) { switch name { case "DataType": switch data.DataType { case DataTypeBinary: return "BINARY", true case DataTypeStringUTF8: return "UTF8", true case DataTypeStringUTF16: return "UTF16", true case DataTypeStringMac: return "MAC_STR", true case DataTypeStringJPEG: return "JPEG", true case DataTypeSignedIntBigEndian: return "INT", true case DataTypeFloat32BigEndian: return "FLOAT32", true case DataTypeFloat64BigEndian: return "FLOAT64", true } case "Data": switch data.DataType { case DataTypeStringUTF8: return fmt.Sprintf("\"%s\"", util.EscapeUnprintables(string(data.Data))), true } } return "", false } type StringData struct { AnyTypeBox Data []byte `mp4:"0,size=8"` } // StringifyField returns field value as string func (sd *StringData) StringifyField(name string, indent string, depth int, ctx Context) (string, bool) { if name == "Data" { return fmt.Sprintf("\"%s\"", util.EscapeUnprintables(string(sd.Data))), true } return "", false } func isUnderIlstFreeFormat(ctx Context) bool { return ctx.UnderIlstFreeMeta } /*************************** mdat ****************************/ func BoxTypeMdat() BoxType { return StrToBoxType("mdat") } func init() { AddBoxDef(&Mdat{}) } // Mdat is ISOBMFF mdat box type type Mdat struct { Box Data []byte `mp4:"0,size=8"` } // GetType returns the BoxType func (*Mdat) GetType() BoxType { return BoxTypeMdat() } /*************************** mdhd ****************************/ func BoxTypeMdhd() BoxType { return StrToBoxType("mdhd") } func init() { AddBoxDef(&Mdhd{}, 0, 1) } // Mdhd is ISOBMFF mdhd box type type Mdhd struct { FullBox `mp4:"0,extend"` CreationTimeV0 uint32 `mp4:"1,size=32,ver=0"` ModificationTimeV0 uint32 `mp4:"2,size=32,ver=0"` CreationTimeV1 uint64 `mp4:"3,size=64,ver=1"` ModificationTimeV1 uint64 `mp4:"4,size=64,ver=1"` Timescale uint32 `mp4:"5,size=32"` DurationV0 uint32 `mp4:"6,size=32,ver=0"` DurationV1 uint64 `mp4:"7,size=64,ver=1"` // Pad bool `mp4:"8,size=1,hidden"` Language [3]byte `mp4:"9,size=5,iso639-2"` // ISO-639-2/T language code PreDefined uint16 `mp4:"10,size=16"` } // GetType returns the BoxType func (*Mdhd) GetType() BoxType { return BoxTypeMdhd() } func (mdhd *Mdhd) GetCreationTime() uint64 { switch mdhd.GetVersion() { case 0: return uint64(mdhd.CreationTimeV0) case 1: return mdhd.CreationTimeV1 default: return 0 } } func (mdhd *Mdhd) GetModificationTime() uint64 { switch mdhd.GetVersion() { case 0: return uint64(mdhd.ModificationTimeV0) case 1: return mdhd.ModificationTimeV1 default: return 0 } } func (mdhd *Mdhd) GetDuration() uint64 { switch mdhd.GetVersion() { case 0: return uint64(mdhd.DurationV0) case 1: return mdhd.DurationV1 default: return 0 } } /*************************** mdia ****************************/ func BoxTypeMdia() BoxType { return StrToBoxType("mdia") } func init() { AddBoxDef(&Mdia{}) } // Mdia is ISOBMFF mdia box type type Mdia struct { Box } // GetType returns the BoxType func (*Mdia) GetType() BoxType { return BoxTypeMdia() } /*************************** mehd ****************************/ func BoxTypeMehd() BoxType { return StrToBoxType("mehd") } func init() { AddBoxDef(&Mehd{}, 0, 1) } // Mehd is ISOBMFF mehd box type type Mehd struct { FullBox `mp4:"0,extend"` FragmentDurationV0 uint32 `mp4:"1,size=32,ver=0"` FragmentDurationV1 uint64 `mp4:"2,size=64,ver=1"` } // GetType returns the BoxType func (*Mehd) GetType() BoxType { return BoxTypeMehd() } func (mdhd *Mehd) GetFragmentDuration() uint64 { switch mdhd.GetVersion() { case 0: return uint64(mdhd.FragmentDurationV0) case 1: return mdhd.FragmentDurationV1 default: return 0 } } /*************************** meta ****************************/ func BoxTypeMeta() BoxType { return StrToBoxType("meta") } func init() { AddBoxDef(&Meta{}, 0) } // Meta is ISOBMFF meta box type type Meta struct { FullBox `mp4:"0,extend"` } // GetType returns the BoxType func (*Meta) GetType() BoxType { return BoxTypeMeta() } func (meta *Meta) BeforeUnmarshal(r io.ReadSeeker, size uint64, ctx Context) (n uint64, override bool, err error) { // for Apple Quick Time buf := make([]byte, 4) if _, err := io.ReadFull(r, buf); err != nil { return 0, false, err } if _, err := r.Seek(-int64(len(buf)), io.SeekCurrent); err != nil { return 0, false, err } if buf[0]|buf[1]|buf[2]|buf[3] != 0x00 { meta.Version = 0 meta.Flags = [3]byte{0, 0, 0} return 0, true, nil } return 0, false, nil } /*************************** mfhd ****************************/ func BoxTypeMfhd() BoxType { return StrToBoxType("mfhd") } func init() { AddBoxDef(&Mfhd{}, 0) } // Mfhd is ISOBMFF mfhd box type type Mfhd struct { FullBox `mp4:"0,extend"` SequenceNumber uint32 `mp4:"1,size=32"` } // GetType returns the BoxType func (*Mfhd) GetType() BoxType { return BoxTypeMfhd() } /*************************** mfra ****************************/ func BoxTypeMfra() BoxType { return StrToBoxType("mfra") } func init() { AddBoxDef(&Mfra{}) } // Mfra is ISOBMFF mfra box type type Mfra struct { Box } // GetType returns the BoxType func (*Mfra) GetType() BoxType { return BoxTypeMfra() } /*************************** mfro ****************************/ func BoxTypeMfro() BoxType { return StrToBoxType("mfro") } func init() { AddBoxDef(&Mfro{}, 0) } // Mfro is ISOBMFF mfro box type type Mfro struct { FullBox `mp4:"0,extend"` Size uint32 `mp4:"1,size=32"` } // GetType returns the BoxType func (*Mfro) GetType() BoxType { return BoxTypeMfro() } /*************************** minf ****************************/ func BoxTypeMinf() BoxType { return StrToBoxType("minf") } func init() { AddBoxDef(&Minf{}) } // Minf is ISOBMFF minf box type type Minf struct { Box } // GetType returns the BoxType func (*Minf) GetType() BoxType { return BoxTypeMinf() } /*************************** moof ****************************/ func BoxTypeMoof() BoxType { return StrToBoxType("moof") } func init() { AddBoxDef(&Moof{}) } // Moof is ISOBMFF moof box type type Moof struct { Box } // GetType returns the BoxType func (*Moof) GetType() BoxType { return BoxTypeMoof() } /*************************** moov ****************************/ func BoxTypeMoov() BoxType { return StrToBoxType("moov") } func init() { AddBoxDef(&Moov{}) } // Moov is ISOBMFF moov box type type Moov struct { Box } // GetType returns the BoxType func (*Moov) GetType() BoxType { return BoxTypeMoov() } /*************************** mvex ****************************/ func BoxTypeMvex() BoxType { return StrToBoxType("mvex") } func init() { AddBoxDef(&Mvex{}) } // Mvex is ISOBMFF mvex box type type Mvex struct { Box } // GetType returns the BoxType func (*Mvex) GetType() BoxType { return BoxTypeMvex() } /*************************** mvhd ****************************/ func BoxTypeMvhd() BoxType { return StrToBoxType("mvhd") } func init() { AddBoxDef(&Mvhd{}, 0, 1) } // Mvhd is ISOBMFF mvhd box type type Mvhd struct { FullBox `mp4:"0,extend"` CreationTimeV0 uint32 `mp4:"1,size=32,ver=0"` ModificationTimeV0 uint32 `mp4:"2,size=32,ver=0"` CreationTimeV1 uint64 `mp4:"3,size=64,ver=1"` ModificationTimeV1 uint64 `mp4:"4,size=64,ver=1"` Timescale uint32 `mp4:"5,size=32"` DurationV0 uint32 `mp4:"6,size=32,ver=0"` DurationV1 uint64 `mp4:"7,size=64,ver=1"` Rate int32 `mp4:"8,size=32"` // fixed-point 16.16 - template=0x00010000 Volume int16 `mp4:"9,size=16"` // template=0x0100 Reserved int16 `mp4:"10,size=16,const=0"` Reserved2 [2]uint32 `mp4:"11,size=32,const=0"` Matrix [9]int32 `mp4:"12,size=32,hex"` // template={ 0x00010000,0,0,0,0x00010000,0,0,0,0x40000000 } PreDefined [6]int32 `mp4:"13,size=32"` NextTrackID uint32 `mp4:"14,size=32"` } // GetType returns the BoxType func (*Mvhd) GetType() BoxType { return BoxTypeMvhd() } // StringifyField returns field value as string func (mvhd *Mvhd) StringifyField(name string, indent string, depth int, ctx Context) (string, bool) { switch name { case "Rate": return util.FormatSignedFixedFloat1616(mvhd.Rate), true default: return "", false } } func (mvhd *Mvhd) GetCreationTime() uint64 { switch mvhd.GetVersion() { case 0: return uint64(mvhd.CreationTimeV0) case 1: return mvhd.CreationTimeV1 default: return 0 } } func (mvhd *Mvhd) GetModificationTime() uint64 { switch mvhd.GetVersion() { case 0: return uint64(mvhd.ModificationTimeV0) case 1: return mvhd.ModificationTimeV1 default: return 0 } } func (mvhd *Mvhd) GetDuration() uint64 { switch mvhd.GetVersion() { case 0: return uint64(mvhd.DurationV0) case 1: return mvhd.DurationV1 default: return 0 } } // GetRate returns value of rate as float64 func (mvhd *Mvhd) GetRate() float64 { return float64(mvhd.Rate) / (1 << 16) } // GetRateInt returns value of rate as int16 func (mvhd *Mvhd) GetRateInt() int16 { return int16(mvhd.Rate >> 16) } /*************************** pssh ****************************/ func BoxTypePssh() BoxType { return StrToBoxType("pssh") } func init() { AddBoxDef(&Pssh{}, 0, 1) } // Pssh is ISOBMFF pssh box type type Pssh struct { FullBox `mp4:"0,extend"` SystemID [16]byte `mp4:"1,size=8,uuid"` KIDCount uint32 `mp4:"2,size=32,nver=0"` KIDs []PsshKID `mp4:"3,nver=0,len=dynamic,size=128"` DataSize int32 `mp4:"4,size=32"` Data []byte `mp4:"5,size=8,len=dynamic"` } type PsshKID struct { KID [16]byte `mp4:"0,size=8,uuid"` } // GetFieldLength returns length of dynamic field func (pssh *Pssh) GetFieldLength(name string, ctx Context) uint { switch name { case "KIDs": return uint(pssh.KIDCount) case "Data": return uint(pssh.DataSize) } panic(fmt.Errorf("invalid name of dynamic-length field: boxType=pssh fieldName=%s", name)) } // StringifyField returns field value as string func (pssh *Pssh) StringifyField(name string, indent string, depth int, ctx Context) (string, bool) { switch name { case "KIDs": buf := bytes.NewBuffer(nil) buf.WriteString("[") for i, e := range pssh.KIDs { if i != 0 { buf.WriteString(", ") } buf.WriteString(uuid.UUID(e.KID).String()) } buf.WriteString("]") return buf.String(), true default: return "", false } } // GetType returns the BoxType func (*Pssh) GetType() BoxType { return BoxTypePssh() } /*************************** saio ****************************/ func BoxTypeSaio() BoxType { return StrToBoxType("saio") } func init() { AddBoxDef(&Saio{}, 0, 1) } type Saio struct { FullBox `mp4:"0,extend"` AuxInfoType [4]byte `mp4:"1,size=8,opt=0x000001,string"` AuxInfoTypeParameter uint32 `mp4:"2,size=32,opt=0x000001,hex"` EntryCount uint32 `mp4:"3,size=32"` OffsetV0 []uint32 `mp4:"4,size=32,ver=0,len=dynamic"` OffsetV1 []uint64 `mp4:"5,size=64,nver=0,len=dynamic"` } func (saio *Saio) GetFieldLength(name string, ctx Context) uint { switch name { case "OffsetV0", "OffsetV1": return uint(saio.EntryCount) } panic(fmt.Errorf("invalid name of dynamic-length field: boxType=saio fieldName=%s", name)) } func (*Saio) GetType() BoxType { return BoxTypeSaio() } func (saio *Saio) GetOffset(index int) uint64 { switch saio.GetVersion() { case 0: return uint64(saio.OffsetV0[index]) case 1: return saio.OffsetV1[index] default: return 0 } } /*************************** saiz ****************************/ func BoxTypeSaiz() BoxType { return StrToBoxType("saiz") } func init() { AddBoxDef(&Saiz{}, 0) } type Saiz struct { FullBox `mp4:"0,extend"` AuxInfoType [4]byte `mp4:"1,size=8,opt=0x000001,string"` AuxInfoTypeParameter uint32 `mp4:"2,size=32,opt=0x000001,hex"` DefaultSampleInfoSize uint8 `mp4:"3,size=8,dec"` SampleCount uint32 `mp4:"4,size=32"` SampleInfoSize []uint8 `mp4:"5,size=8,opt=dynamic,len=dynamic,dec"` } func (saiz *Saiz) IsOptFieldEnabled(name string, ctx Context) bool { switch name { case "SampleInfoSize": return saiz.DefaultSampleInfoSize == 0 } return false } func (saiz *Saiz) GetFieldLength(name string, ctx Context) uint { switch name { case "SampleInfoSize": return uint(saiz.SampleCount) } panic(fmt.Errorf("invalid name of dynamic-length field: boxType=saiz fieldName=%s", name)) } func (*Saiz) GetType() BoxType { return BoxTypeSaiz() } /*********************** SampleEntry *************************/ func BoxTypeAvc1() BoxType { return StrToBoxType("avc1") } func BoxTypeEncv() BoxType { return StrToBoxType("encv") } func BoxTypeHev1() BoxType { return StrToBoxType("hev1") } func BoxTypeMp4a() BoxType { return StrToBoxType("mp4a") } func BoxTypeEnca() BoxType { return StrToBoxType("enca") } func BoxTypeAvcC() BoxType { return StrToBoxType("avcC") } func BoxTypePasp() BoxType { return StrToBoxType("pasp") } func init() { AddAnyTypeBoxDef(&VisualSampleEntry{}, BoxTypeAvc1()) AddAnyTypeBoxDef(&VisualSampleEntry{}, BoxTypeEncv()) AddAnyTypeBoxDef(&VisualSampleEntry{}, BoxTypeHev1()) AddAnyTypeBoxDef(&AudioSampleEntry{}, BoxTypeMp4a()) AddAnyTypeBoxDef(&AudioSampleEntry{}, BoxTypeEnca()) AddAnyTypeBoxDef(&AVCDecoderConfiguration{}, BoxTypeAvcC()) AddAnyTypeBoxDef(&PixelAspectRatioBox{}, BoxTypePasp()) } type SampleEntry struct { AnyTypeBox Reserved [6]uint8 `mp4:"0,size=8,const=0"` DataReferenceIndex uint16 `mp4:"1,size=16"` } type VisualSampleEntry struct { SampleEntry `mp4:"0,extend"` PreDefined uint16 `mp4:"1,size=16"` Reserved uint16 `mp4:"2,size=16,const=0"` PreDefined2 [3]uint32 `mp4:"3,size=32"` Width uint16 `mp4:"4,size=16"` Height uint16 `mp4:"5,size=16"` Horizresolution uint32 `mp4:"6,size=32"` Vertresolution uint32 `mp4:"7,size=32"` Reserved2 uint32 `mp4:"8,size=32,const=0"` FrameCount uint16 `mp4:"9,size=16"` Compressorname [32]byte `mp4:"10,size=8"` Depth uint16 `mp4:"11,size=16"` PreDefined3 int16 `mp4:"12,size=16"` } // StringifyField returns field value as string func (vse *VisualSampleEntry) StringifyField(name string, indent string, depth int, ctx Context) (string, bool) { switch name { case "Compressorname": if vse.Compressorname[0] <= 31 { return `"` + util.EscapeUnprintables(string(vse.Compressorname[1:vse.Compressorname[0]+1])) + `"`, true } return "", false default: return "", false } } type AudioSampleEntry struct { SampleEntry `mp4:"0,extend,opt=dynamic"` EntryVersion uint16 `mp4:"1,size=16,opt=dynamic"` Reserved [3]uint16 `mp4:"2,size=16,opt=dynamic,const=0"` ChannelCount uint16 `mp4:"3,size=16,opt=dynamic"` SampleSize uint16 `mp4:"4,size=16,opt=dynamic"` PreDefined uint16 `mp4:"5,size=16,opt=dynamic"` Reserved2 uint16 `mp4:"6,size=16,opt=dynamic,const=0"` SampleRate uint32 `mp4:"7,size=32,opt=dynamic"` QuickTimeData []byte `mp4:"8,size=8,opt=dynamic,len=dynamic"` } func (ase *AudioSampleEntry) IsOptFieldEnabled(name string, ctx Context) bool { if name == "QuickTimeData" { return ctx.IsQuickTimeCompatible && (ctx.UnderWave || ase.EntryVersion == 1 || ase.EntryVersion == 2) } if ctx.IsQuickTimeCompatible && ctx.UnderWave { return false } return true } func (ase *AudioSampleEntry) GetFieldLength(name string, ctx Context) uint { if name == "QuickTimeData" && ctx.IsQuickTimeCompatible { if ctx.UnderWave { return LengthUnlimited } else if ase.EntryVersion == 1 { return 16 } else if ase.EntryVersion == 2 { return 36 } } return 0 } const ( AVCBaselineProfile uint8 = 66 // 0x42 AVCMainProfile uint8 = 77 // 0x4d AVCExtendedProfile uint8 = 88 // 0x58 AVCHighProfile uint8 = 100 // 0x64 AVCHigh10Profile uint8 = 110 // 0x6e AVCHigh422Profile uint8 = 122 // 0x7a ) type AVCDecoderConfiguration struct { AnyTypeBox ConfigurationVersion uint8 `mp4:"0,size=8"` Profile uint8 `mp4:"1,size=8"` ProfileCompatibility uint8 `mp4:"2,size=8"` Level uint8 `mp4:"3,size=8"` Reserved uint8 `mp4:"4,size=6,const=63"` LengthSizeMinusOne uint8 `mp4:"5,size=2"` Reserved2 uint8 `mp4:"6,size=3,const=7"` NumOfSequenceParameterSets uint8 `mp4:"7,size=5"` SequenceParameterSets []AVCParameterSet `mp4:"8,len=dynamic"` NumOfPictureParameterSets uint8 `mp4:"9,size=8"` PictureParameterSets []AVCParameterSet `mp4:"10,len=dynamic"` HighProfileFieldsEnabled bool `mp4:"11,hidden"` Reserved3 uint8 `mp4:"12,size=6,opt=dynamic,const=63"` ChromaFormat uint8 `mp4:"13,size=2,opt=dynamic"` Reserved4 uint8 `mp4:"14,size=5,opt=dynamic,const=31"` BitDepthLumaMinus8 uint8 `mp4:"15,size=3,opt=dynamic"` Reserved5 uint8 `mp4:"16,size=5,opt=dynamic,const=31"` BitDepthChromaMinus8 uint8 `mp4:"17,size=3,opt=dynamic"` NumOfSequenceParameterSetExt uint8 `mp4:"18,size=8,opt=dynamic"` SequenceParameterSetsExt []AVCParameterSet `mp4:"19,len=dynamic,opt=dynamic"` } func (avcc *AVCDecoderConfiguration) GetFieldLength(name string, ctx Context) uint { switch name { case "SequenceParameterSets": return uint(avcc.NumOfSequenceParameterSets) case "PictureParameterSets": return uint(avcc.NumOfPictureParameterSets) case "SequenceParameterSetsExt": return uint(avcc.NumOfSequenceParameterSetExt) } return 0 } func (avcc *AVCDecoderConfiguration) IsOptFieldEnabled(name string, ctx Context) bool { switch name { case "Reserved3", "ChromaFormat", "Reserved4", "BitDepthLumaMinus8", "Reserved5", "BitDepthChromaMinus8", "NumOfSequenceParameterSetExt", "SequenceParameterSetsExt": return avcc.HighProfileFieldsEnabled } return false } func (avcc *AVCDecoderConfiguration) OnReadField(name string, r bitio.ReadSeeker, leftBits uint64, ctx Context) (rbits uint64, override bool, err error) { if name == "HighProfileFieldsEnabled" { avcc.HighProfileFieldsEnabled = leftBits >= 32 && (avcc.Profile == AVCHighProfile || avcc.Profile == AVCHigh10Profile || avcc.Profile == AVCHigh422Profile || avcc.Profile == 144) return 0, true, nil } return 0, false, nil } func (avcc *AVCDecoderConfiguration) OnWriteField(name string, w bitio.Writer, ctx Context) (wbits uint64, override bool, err error) { if name == "HighProfileFieldsEnabled" { if avcc.HighProfileFieldsEnabled && avcc.Profile != AVCHighProfile && avcc.Profile != AVCHigh10Profile && avcc.Profile != AVCHigh422Profile && avcc.Profile != 144 { return 0, false, errors.New("each values of Profile and HighProfileFieldsEnabled are inconsistent") } return 0, true, nil } return 0, false, nil } type AVCParameterSet struct { BaseCustomFieldObject Length uint16 `mp4:"0,size=16"` NALUnit []byte `mp4:"1,size=8,len=dynamic"` } func (s *AVCParameterSet) GetFieldLength(name string, ctx Context) uint { switch name { case "NALUnit": return uint(s.Length) } return 0 } type PixelAspectRatioBox struct { AnyTypeBox HSpacing uint32 `mp4:"0,size=32"` VSpacing uint32 `mp4:"1,size=32"` } /*************************** sbgp ****************************/ func BoxTypeSbgp() BoxType { return StrToBoxType("sbgp") } func init() { AddBoxDef(&Sbgp{}, 0, 1) } type Sbgp struct { FullBox `mp4:"0,extend"` GroupingType uint32 `mp4:"1,size=32"` GroupingTypeParameter uint32 `mp4:"2,size=32,ver=1"` EntryCount uint32 `mp4:"3,size=32"` Entries []SbgpEntry `mp4:"4,len=dynamic,size=64"` } type SbgpEntry struct { SampleCount uint32 `mp4:"0,size=32"` GroupDescriptionIndex uint32 `mp4:"1,size=32"` } func (sbgp *Sbgp) GetFieldLength(name string, ctx Context) uint { switch name { case "Entries": return uint(sbgp.EntryCount) } panic(fmt.Errorf("invalid name of dynamic-length field: boxType=sbgp fieldName=%s", name)) } func (*Sbgp) GetType() BoxType { return BoxTypeSbgp() } /*************************** schi ****************************/ func BoxTypeSchi() BoxType { return StrToBoxType("schi") } func init() { AddBoxDef(&Schi{}) } type Schi struct { Box } func (*Schi) GetType() BoxType { return BoxTypeSchi() } /*************************** schm ****************************/ func BoxTypeSchm() BoxType { return StrToBoxType("schm") } func init() { AddBoxDef(&Schm{}, 0) } type Schm struct { FullBox `mp4:"0,extend"` SchemeType [4]byte `mp4:"1,size=8,string"` SchemeVersion uint32 `mp4:"2,size=32,hex"` SchemeUri []byte `mp4:"3,size=8,opt=0x000001,string"` } func (*Schm) GetType() BoxType { return BoxTypeSchm() } /*************************** sdtp ****************************/ func BoxTypeSdtp() BoxType { return StrToBoxType("sdtp") } func init() { AddBoxDef(&Sdtp{}, 0) } type Sdtp struct { FullBox `mp4:"0,extend"` Samples []SdtpSampleElem `mp4:"1,size=8"` } type SdtpSampleElem struct { IsLeading uint8 `mp4:"0,size=2"` SampleDependsOn uint8 `mp4:"1,size=2"` SampleIsDependedOn uint8 `mp4:"2,size=2"` SampleHasRedundancy uint8 `mp4:"3,size=2"` } func (*Sdtp) GetType() BoxType { return BoxTypeSdtp() } /*************************** sgpd ****************************/ func BoxTypeSgpd() BoxType { return StrToBoxType("sgpd") } func init() { AddBoxDef(&Sgpd{}, 1, 2) // version 0 is deprecated by ISO/IEC 14496-12 } type Sgpd struct { FullBox `mp4:"0,extend"` GroupingType [4]byte `mp4:"1,size=8,string"` DefaultLength uint32 `mp4:"2,size=32,ver=1"` DefaultSampleDescriptionIndex uint32 `mp4:"3,size=32,ver=2"` EntryCount uint32 `mp4:"4,size=32"` RollDistances []int16 `mp4:"5,size=16,opt=dynamic"` RollDistancesL []RollDistanceWithLength `mp4:"6,size=16,opt=dynamic"` AlternativeStartupEntries []AlternativeStartupEntry `mp4:"7,size=dynamic,len=dynamic,opt=dynamic"` AlternativeStartupEntriesL []AlternativeStartupEntryL `mp4:"8,len=dynamic,opt=dynamic"` VisualRandomAccessEntries []VisualRandomAccessEntry `mp4:"9,len=dynamic,opt=dynamic"` VisualRandomAccessEntriesL []VisualRandomAccessEntryL `mp4:"10,len=dynamic,opt=dynamic"` TemporalLevelEntries []TemporalLevelEntry `mp4:"11,len=dynamic,opt=dynamic"` TemporalLevelEntriesL []TemporalLevelEntryL `mp4:"12,len=dynamic,opt=dynamic"` Unsupported []byte `mp4:"13,size=8,opt=dynamic"` } type RollDistanceWithLength struct { DescriptionLength uint32 `mp4:"0,size=32"` RollDistance int16 `mp4:"1,size=16"` } type AlternativeStartupEntry struct { BaseCustomFieldObject RollCount uint16 `mp4:"0,size=16"` FirstOutputSample uint16 `mp4:"1,size=16"` SampleOffset []uint32 `mp4:"2,size=32,len=dynamic"` Opts []AlternativeStartupEntryOpt `mp4:"3,size=32"` } type AlternativeStartupEntryL struct { DescriptionLength uint32 `mp4:"0,size=32"` AlternativeStartupEntry `mp4:"1,extend,size=dynamic"` } type AlternativeStartupEntryOpt struct { NumOutputSamples uint16 `mp4:"0,size=16"` NumTotalSamples uint16 `mp4:"1,size=16"` } type VisualRandomAccessEntry struct { NumLeadingSamplesKnown bool `mp4:"0,size=1"` NumLeadingSamples uint8 `mp4:"1,size=7"` } type VisualRandomAccessEntryL struct { DescriptionLength uint32 `mp4:"0,size=32"` VisualRandomAccessEntry `mp4:"1,extend"` } type TemporalLevelEntry struct { LevelIndependentlyDecodable bool `mp4:"0,size=1"` Reserved uint8 `mp4:"1,size=7,const=0"` } type TemporalLevelEntryL struct { DescriptionLength uint32 `mp4:"0,size=32"` TemporalLevelEntry `mp4:"1,extend"` } func (sgpd *Sgpd) GetFieldSize(name string, ctx Context) uint { switch name { case "AlternativeStartupEntries": return uint(sgpd.DefaultLength * 8) } return 0 } func (sgpd *Sgpd) GetFieldLength(name string, ctx Context) uint { switch name { case "RollDistances", "RollDistancesL", "AlternativeStartupEntries", "AlternativeStartupEntriesL", "VisualRandomAccessEntries", "VisualRandomAccessEntriesL", "TemporalLevelEntries", "TemporalLevelEntriesL": return uint(sgpd.EntryCount) } return 0 } func (sgpd *Sgpd) IsOptFieldEnabled(name string, ctx Context) bool { noDefaultLength := sgpd.Version == 1 && sgpd.DefaultLength == 0 rollDistances := sgpd.GroupingType == [4]byte{'r', 'o', 'l', 'l'} || sgpd.GroupingType == [4]byte{'p', 'r', 'o', 'l'} alternativeStartupEntries := sgpd.GroupingType == [4]byte{'a', 'l', 's', 't'} visualRandomAccessEntries := sgpd.GroupingType == [4]byte{'r', 'a', 'p', ' '} temporalLevelEntries := sgpd.GroupingType == [4]byte{'t', 'e', 'l', 'e'} switch name { case "RollDistances": return rollDistances && !noDefaultLength case "RollDistancesL": return rollDistances && noDefaultLength case "AlternativeStartupEntries": return alternativeStartupEntries && !noDefaultLength case "AlternativeStartupEntriesL": return alternativeStartupEntries && noDefaultLength case "VisualRandomAccessEntries": return visualRandomAccessEntries && !noDefaultLength case "VisualRandomAccessEntriesL": return visualRandomAccessEntries && noDefaultLength case "TemporalLevelEntries": return temporalLevelEntries && !noDefaultLength case "TemporalLevelEntriesL": return temporalLevelEntries && noDefaultLength case "Unsupported": return !rollDistances && !alternativeStartupEntries && !visualRandomAccessEntries && !temporalLevelEntries default: return false } } func (*Sgpd) GetType() BoxType { return BoxTypeSgpd() } func (entry *AlternativeStartupEntry) GetFieldLength(name string, ctx Context) uint { switch name { case "SampleOffset": return uint(entry.RollCount) } return 0 } func (entry *AlternativeStartupEntryL) GetFieldSize(name string, ctx Context) uint { switch name { case "AlternativeStartupEntry": return uint(entry.DescriptionLength * 8) } return 0 } /*************************** sidx ****************************/ func BoxTypeSidx() BoxType { return StrToBoxType("sidx") } func init() { AddBoxDef(&Sidx{}, 0, 1) } type Sidx struct { FullBox `mp4:"0,extend"` ReferenceID uint32 `mp4:"1,size=32"` Timescale uint32 `mp4:"2,size=32"` EarliestPresentationTimeV0 uint32 `mp4:"3,size=32,ver=0"` FirstOffsetV0 uint32 `mp4:"4,size=32,ver=0"` EarliestPresentationTimeV1 uint64 `mp4:"5,size=64,nver=0"` FirstOffsetV1 uint64 `mp4:"6,size=64,nver=0"` Reserved uint16 `mp4:"7,size=16,const=0"` ReferenceCount uint16 `mp4:"8,size=16"` References []SidxReference `mp4:"9,size=96,len=dynamic"` } type SidxReference struct { ReferenceType bool `mp4:"0,size=1"` ReferencedSize uint32 `mp4:"1,size=31"` SubsegmentDuration uint32 `mp4:"2,size=32"` StartsWithSAP bool `mp4:"3,size=1"` SAPType uint32 `mp4:"4,size=3"` SAPDeltaTime uint32 `mp4:"5,size=28"` } func (*Sidx) GetType() BoxType { return BoxTypeSidx() } func (sidx *Sidx) GetFieldLength(name string, ctx Context) uint { switch name { case "References": return uint(sidx.ReferenceCount) } panic(fmt.Errorf("invalid name of dynamic-length field: boxType=sidx fieldName=%s", name)) } func (sidx *Sidx) GetEarliestPresentationTime() uint64 { switch sidx.GetVersion() { case 0: return uint64(sidx.EarliestPresentationTimeV0) case 1: return sidx.EarliestPresentationTimeV1 default: return 0 } } func (sidx *Sidx) GetFirstOffset() uint64 { switch sidx.GetVersion() { case 0: return uint64(sidx.FirstOffsetV0) case 1: return sidx.FirstOffsetV1 default: return 0 } } /*************************** sinf ****************************/ func BoxTypeSinf() BoxType { return StrToBoxType("sinf") } func init() { AddBoxDef(&Sinf{}) } type Sinf struct { Box } func (*Sinf) GetType() BoxType { return BoxTypeSinf() } /*************************** smhd ****************************/ func BoxTypeSmhd() BoxType { return StrToBoxType("smhd") } func init() { AddBoxDef(&Smhd{}, 0) } type Smhd struct { FullBox `mp4:"0,extend"` Balance int16 `mp4:"1,size=16"` // fixed-point 8.8 template=0 Reserved uint16 `mp4:"2,size=16,const=0"` } func (*Smhd) GetType() BoxType { return BoxTypeSmhd() } // StringifyField returns field value as string func (smhd *Smhd) StringifyField(name string, indent string, depth int, ctx Context) (string, bool) { switch name { case "Balance": return util.FormatSignedFixedFloat88(smhd.Balance), true default: return "", false } } // GetBalance returns value of width as float32 func (smhd *Smhd) GetBalance() float32 { return float32(smhd.Balance) / (1 << 8) } // GetBalanceInt returns value of width as int8 func (smhd *Smhd) GetBalanceInt() int8 { return int8(smhd.Balance >> 8) } /*************************** stbl ****************************/ func BoxTypeStbl() BoxType { return StrToBoxType("stbl") } func init() { AddBoxDef(&Stbl{}) } // Stbl is ISOBMFF stbl box type type Stbl struct { Box } // GetType returns the BoxType func (*Stbl) GetType() BoxType { return BoxTypeStbl() } /*************************** stco ****************************/ func BoxTypeStco() BoxType { return StrToBoxType("stco") } func init() { AddBoxDef(&Stco{}, 0) } // Stco is ISOBMFF stco box type type Stco struct { FullBox `mp4:"0,extend"` EntryCount uint32 `mp4:"1,size=32"` ChunkOffset []uint32 `mp4:"2,size=32,len=dynamic"` } // GetType returns the BoxType func (*Stco) GetType() BoxType { return BoxTypeStco() } // GetFieldLength returns length of dynamic field func (stco *Stco) GetFieldLength(name string, ctx Context) uint { switch name { case "ChunkOffset": return uint(stco.EntryCount) } panic(fmt.Errorf("invalid name of dynamic-length field: boxType=stco fieldName=%s", name)) } /*************************** stsc ****************************/ func BoxTypeStsc() BoxType { return StrToBoxType("stsc") } func init() { AddBoxDef(&Stsc{}, 0) } // Stsc is ISOBMFF stsc box type type Stsc struct { FullBox `mp4:"0,extend"` EntryCount uint32 `mp4:"1,size=32"` Entries []StscEntry `mp4:"2,len=dynamic,size=96"` } type StscEntry struct { FirstChunk uint32 `mp4:"0,size=32"` SamplesPerChunk uint32 `mp4:"1,size=32"` SampleDescriptionIndex uint32 `mp4:"2,size=32"` } // GetType returns the BoxType func (*Stsc) GetType() BoxType { return BoxTypeStsc() } // GetFieldLength returns length of dynamic field func (stsc *Stsc) GetFieldLength(name string, ctx Context) uint { switch name { case "Entries": return uint(stsc.EntryCount) } panic(fmt.Errorf("invalid name of dynamic-length field: boxType=stsc fieldName=%s", name)) } /*************************** stsd ****************************/ func BoxTypeStsd() BoxType { return StrToBoxType("stsd") } func init() { AddBoxDef(&Stsd{}, 0) } // Stsd is ISOBMFF stsd box type type Stsd struct { FullBox `mp4:"0,extend"` EntryCount uint32 `mp4:"1,size=32"` } // GetType returns the BoxType func (*Stsd) GetType() BoxType { return BoxTypeStsd() } /*************************** stss ****************************/ func BoxTypeStss() BoxType { return StrToBoxType("stss") } func init() { AddBoxDef(&Stss{}, 0) } type Stss struct { FullBox `mp4:"0,extend"` EntryCount uint32 `mp4:"1,size=32"` SampleNumber []uint32 `mp4:"2,len=dynamic,size=32"` } // GetType returns the BoxType func (*Stss) GetType() BoxType { return BoxTypeStss() } // GetFieldLength returns length of dynamic field func (stss *Stss) GetFieldLength(name string, ctx Context) uint { switch name { case "SampleNumber": return uint(stss.EntryCount) } panic(fmt.Errorf("invalid name of dynamic-length field: boxType=stss fieldName=%s", name)) } /*************************** stsz ****************************/ func BoxTypeStsz() BoxType { return StrToBoxType("stsz") } func init() { AddBoxDef(&Stsz{}, 0) } // Stsz is ISOBMFF stsz box type type Stsz struct { FullBox `mp4:"0,extend"` SampleSize uint32 `mp4:"1,size=32"` SampleCount uint32 `mp4:"2,size=32"` EntrySize []uint32 `mp4:"3,size=32,len=dynamic"` } // GetType returns the BoxType func (*Stsz) GetType() BoxType { return BoxTypeStsz() } // GetFieldLength returns length of dynamic field func (stsz *Stsz) GetFieldLength(name string, ctx Context) uint { switch name { case "EntrySize": if stsz.SampleSize == 0 { return uint(stsz.SampleCount) } return 0 } panic(fmt.Errorf("invalid name of dynamic-length field: boxType=stsz fieldName=%s", name)) } /*************************** stts ****************************/ func BoxTypeStts() BoxType { return StrToBoxType("stts") } func init() { AddBoxDef(&Stts{}, 0) } // Stts is ISOBMFF stts box type type Stts struct { FullBox `mp4:"0,extend"` EntryCount uint32 `mp4:"1,size=32"` Entries []SttsEntry `mp4:"2,len=dynamic,size=64"` } type SttsEntry struct { SampleCount uint32 `mp4:"0,size=32"` SampleDelta uint32 `mp4:"1,size=32"` } // GetType returns the BoxType func (*Stts) GetType() BoxType { return BoxTypeStts() } // GetFieldLength returns length of dynamic field func (stts *Stts) GetFieldLength(name string, ctx Context) uint { switch name { case "Entries": return uint(stts.EntryCount) } panic(fmt.Errorf("invalid name of dynamic-length field: boxType=stts fieldName=%s", name)) } /*************************** styp ****************************/ func BoxTypeStyp() BoxType { return StrToBoxType("styp") } func init() { AddBoxDef(&Styp{}) } type Styp struct { Box MajorBrand [4]byte `mp4:"0,size=8,string"` MinorVersion uint32 `mp4:"1,size=32"` CompatibleBrands []CompatibleBrandElem `mp4:"2,size=32"` // reach to end of the box } func (*Styp) GetType() BoxType { return BoxTypeStyp() } /*************************** tenc ****************************/ func BoxTypeTenc() BoxType { return StrToBoxType("tenc") } func init() { AddBoxDef(&Tenc{}, 0, 1) } // Tenc is ISOBMFF tenc box type type Tenc struct { FullBox `mp4:"0,extend"` Reserved uint8 `mp4:"1,size=8,dec"` DefaultCryptByteBlock uint8 `mp4:"2,size=4,dec"` // always 0 on version 0 DefaultSkipByteBlock uint8 `mp4:"3,size=4,dec"` // always 0 on version 0 DefaultIsProtected uint8 `mp4:"4,size=8,dec"` DefaultPerSampleIVSize uint8 `mp4:"5,size=8,dec"` DefaultKID [16]byte `mp4:"6,size=8,uuid"` DefaultConstantIVSize uint8 `mp4:"7,size=8,opt=dynamic,dec"` DefaultConstantIV []byte `mp4:"8,size=8,opt=dynamic,len=dynamic"` } func (tenc *Tenc) IsOptFieldEnabled(name string, ctx Context) bool { switch name { case "DefaultConstantIVSize", "DefaultConstantIV": return tenc.DefaultIsProtected == 1 && tenc.DefaultPerSampleIVSize == 0 } return false } func (tenc *Tenc) GetFieldLength(name string, ctx Context) uint { switch name { case "DefaultConstantIV": return uint(tenc.DefaultConstantIVSize) } panic(fmt.Errorf("invalid name of dynamic-length field: boxType=tenc fieldName=%s", name)) } // GetType returns the BoxType func (*Tenc) GetType() BoxType { return BoxTypeTenc() } /*************************** tfdt ****************************/ func BoxTypeTfdt() BoxType { return StrToBoxType("tfdt") } func init() { AddBoxDef(&Tfdt{}, 0, 1) } // Tfdt is ISOBMFF tfdt box type type Tfdt struct { FullBox `mp4:"0,extend"` BaseMediaDecodeTimeV0 uint32 `mp4:"1,size=32,ver=0"` BaseMediaDecodeTimeV1 uint64 `mp4:"2,size=64,ver=1"` } // GetType returns the BoxType func (*Tfdt) GetType() BoxType { return BoxTypeTfdt() } func (tfdt *Tfdt) GetBaseMediaDecodeTime() uint64 { switch tfdt.GetVersion() { case 0: return uint64(tfdt.BaseMediaDecodeTimeV0) case 1: return tfdt.BaseMediaDecodeTimeV1 default: return 0 } } /*************************** tfhd ****************************/ func BoxTypeTfhd() BoxType { return StrToBoxType("tfhd") } func init() { AddBoxDef(&Tfhd{}, 0) } // Tfhd is ISOBMFF tfhd box type type Tfhd struct { FullBox `mp4:"0,extend"` TrackID uint32 `mp4:"1,size=32"` // optional BaseDataOffset uint64 `mp4:"2,size=64,opt=0x000001"` SampleDescriptionIndex uint32 `mp4:"3,size=32,opt=0x000002"` DefaultSampleDuration uint32 `mp4:"4,size=32,opt=0x000008"` DefaultSampleSize uint32 `mp4:"5,size=32,opt=0x000010"` DefaultSampleFlags uint32 `mp4:"6,size=32,opt=0x000020,hex"` } const ( TfhdBaseDataOffsetPresent = 0x000001 TfhdSampleDescriptionIndexPresent = 0x000002 TfhdDefaultSampleDurationPresent = 0x000008 TfhdDefaultSampleSizePresent = 0x000010 TfhdDefaultSampleFlagsPresent = 0x000020 TfhdDurationIsEmpty = 0x010000 TfhdDefaultBaseIsMoof = 0x020000 ) // GetType returns the BoxType func (*Tfhd) GetType() BoxType { return BoxTypeTfhd() } /*************************** tfra ****************************/ func BoxTypeTfra() BoxType { return StrToBoxType("tfra") } func init() { AddBoxDef(&Tfra{}, 0, 1) } // Tfra is ISOBMFF tfra box type type Tfra struct { FullBox `mp4:"0,extend"` TrackID uint32 `mp4:"1,size=32"` Reserved uint32 `mp4:"2,size=26,const=0"` LengthSizeOfTrafNum byte `mp4:"3,size=2"` LengthSizeOfTrunNum byte `mp4:"4,size=2"` LengthSizeOfSampleNum byte `mp4:"5,size=2"` NumberOfEntry uint32 `mp4:"6,size=32"` Entries []TfraEntry `mp4:"7,len=dynamic,size=dynamic"` } type TfraEntry struct { TimeV0 uint32 `mp4:"0,size=32,ver=0"` MoofOffsetV0 uint32 `mp4:"1,size=32,ver=0"` TimeV1 uint64 `mp4:"2,size=64,ver=1"` MoofOffsetV1 uint64 `mp4:"3,size=64,ver=1"` TrafNumber uint32 `mp4:"4,size=dynamic"` TrunNumber uint32 `mp4:"5,size=dynamic"` SampleNumber uint32 `mp4:"6,size=dynamic"` } // GetType returns the BoxType func (*Tfra) GetType() BoxType { return BoxTypeTfra() } // GetFieldSize returns size of dynamic field func (tfra *Tfra) GetFieldSize(name string, ctx Context) uint { switch name { case "TrafNumber": return (uint(tfra.LengthSizeOfTrafNum) + 1) * 8 case "TrunNumber": return (uint(tfra.LengthSizeOfTrunNum) + 1) * 8 case "SampleNumber": return (uint(tfra.LengthSizeOfSampleNum) + 1) * 8 case "Entries": switch tfra.GetVersion() { case 0: return 0 + /* TimeV0 */ 32 + /* MoofOffsetV0 */ 32 + /* TrafNumber */ (uint(tfra.LengthSizeOfTrafNum)+1)*8 + /* TrunNumber */ (uint(tfra.LengthSizeOfTrunNum)+1)*8 + /* SampleNumber */ (uint(tfra.LengthSizeOfSampleNum)+1)*8 case 1: return 0 + /* TimeV1 */ 64 + /* MoofOffsetV1 */ 64 + /* TrafNumber */ (uint(tfra.LengthSizeOfTrafNum)+1)*8 + /* TrunNumber */ (uint(tfra.LengthSizeOfTrunNum)+1)*8 + /* SampleNumber */ (uint(tfra.LengthSizeOfSampleNum)+1)*8 } } panic(fmt.Errorf("invalid name of dynamic-size field: boxType=tfra fieldName=%s", name)) } // GetFieldLength returns length of dynamic field func (tfra *Tfra) GetFieldLength(name string, ctx Context) uint { switch name { case "Entries": return uint(tfra.NumberOfEntry) } panic(fmt.Errorf("invalid name of dynamic-length field: boxType=tfra fieldName=%s", name)) } func (tfra *Tfra) GetTime(index int) uint64 { switch tfra.GetVersion() { case 0: return uint64(tfra.Entries[index].TimeV0) case 1: return tfra.Entries[index].TimeV1 default: return 0 } } func (tfra *Tfra) GetMoofOffset(index int) uint64 { switch tfra.GetVersion() { case 0: return uint64(tfra.Entries[index].MoofOffsetV0) case 1: return tfra.Entries[index].MoofOffsetV1 default: return 0 } } /*************************** tkhd ****************************/ func BoxTypeTkhd() BoxType { return StrToBoxType("tkhd") } func init() { AddBoxDef(&Tkhd{}, 0, 1) } // Tkhd is ISOBMFF tkhd box type type Tkhd struct { FullBox `mp4:"0,extend"` CreationTimeV0 uint32 `mp4:"1,size=32,ver=0"` ModificationTimeV0 uint32 `mp4:"2,size=32,ver=0"` CreationTimeV1 uint64 `mp4:"3,size=64,ver=1"` ModificationTimeV1 uint64 `mp4:"4,size=64,ver=1"` TrackID uint32 `mp4:"5,size=32"` Reserved0 uint32 `mp4:"6,size=32,const=0"` DurationV0 uint32 `mp4:"7,size=32,ver=0"` DurationV1 uint64 `mp4:"8,size=64,ver=1"` // Reserved1 [2]uint32 `mp4:"9,size=32,const=0"` Layer int16 `mp4:"10,size=16"` // template=0 AlternateGroup int16 `mp4:"11,size=16"` // template=0 Volume int16 `mp4:"12,size=16"` // template={if track_is_audio 0x0100 else 0} Reserved2 uint16 `mp4:"13,size=16,const=0"` Matrix [9]int32 `mp4:"14,size=32,hex"` // template={ 0x00010000,0,0,0,0x00010000,0,0,0,0x40000000 }; Width uint32 `mp4:"15,size=32"` // fixed-point 16.16 Height uint32 `mp4:"16,size=32"` // fixed-point 16.16 } // GetType returns the BoxType func (*Tkhd) GetType() BoxType { return BoxTypeTkhd() } // StringifyField returns field value as string func (tkhd *Tkhd) StringifyField(name string, indent string, depth int, ctx Context) (string, bool) { switch name { case "Width": return util.FormatUnsignedFixedFloat1616(tkhd.Width), true case "Height": return util.FormatUnsignedFixedFloat1616(tkhd.Height), true default: return "", false } } func (tkhd *Tkhd) GetCreationTime() uint64 { switch tkhd.GetVersion() { case 0: return uint64(tkhd.CreationTimeV0) case 1: return tkhd.CreationTimeV1 default: return 0 } } func (tkhd *Tkhd) GetModificationTime() uint64 { switch tkhd.GetVersion() { case 0: return uint64(tkhd.ModificationTimeV0) case 1: return tkhd.ModificationTimeV1 default: return 0 } } func (tkhd *Tkhd) GetDuration() uint64 { switch tkhd.GetVersion() { case 0: return uint64(tkhd.DurationV0) case 1: return tkhd.DurationV1 default: return 0 } } // GetWidth returns value of width as float64 func (tkhd *Tkhd) GetWidth() float64 { return float64(tkhd.Width) / (1 << 16) } // GetWidthInt returns value of width as uint16 func (tkhd *Tkhd) GetWidthInt() uint16 { return uint16(tkhd.Width >> 16) } // GetHeight returns value of height as float64 func (tkhd *Tkhd) GetHeight() float64 { return float64(tkhd.Height) / (1 << 16) } // GetHeightInt returns value of height as uint16 func (tkhd *Tkhd) GetHeightInt() uint16 { return uint16(tkhd.Height >> 16) } /*************************** traf ****************************/ func BoxTypeTraf() BoxType { return StrToBoxType("traf") } func init() { AddBoxDef(&Traf{}) } // Traf is ISOBMFF traf box type type Traf struct { Box } // GetType returns the BoxType func (*Traf) GetType() BoxType { return BoxTypeTraf() } /*************************** trak ****************************/ func BoxTypeTrak() BoxType { return StrToBoxType("trak") } func init() { AddBoxDef(&Trak{}) } // Trak is ISOBMFF trak box type type Trak struct { Box } // GetType returns the BoxType func (*Trak) GetType() BoxType { return BoxTypeTrak() } /*************************** trep ****************************/ func BoxTypeTrep() BoxType { return StrToBoxType("trep") } func init() { AddBoxDef(&Trep{}, 0) } // Trep is ISOBMFF trep box type type Trep struct { FullBox `mp4:"0,extend"` TrackID uint32 `mp4:"1,size=32"` } // GetType returns the BoxType func (*Trep) GetType() BoxType { return BoxTypeTrep() } /*************************** trex ****************************/ func BoxTypeTrex() BoxType { return StrToBoxType("trex") } func init() { AddBoxDef(&Trex{}, 0) } // Trex is ISOBMFF trex box type type Trex struct { FullBox `mp4:"0,extend"` TrackID uint32 `mp4:"1,size=32"` DefaultSampleDescriptionIndex uint32 `mp4:"2,size=32"` DefaultSampleDuration uint32 `mp4:"3,size=32"` DefaultSampleSize uint32 `mp4:"4,size=32"` DefaultSampleFlags uint32 `mp4:"5,size=32,hex"` } // GetType returns the BoxType func (*Trex) GetType() BoxType { return BoxTypeTrex() } /*************************** trun ****************************/ func BoxTypeTrun() BoxType { return StrToBoxType("trun") } func init() { AddBoxDef(&Trun{}, 0, 1) } // Trun is ISOBMFF trun box type type Trun struct { FullBox `mp4:"0,extend"` SampleCount uint32 `mp4:"1,size=32"` // optional fields DataOffset int32 `mp4:"2,size=32,opt=0x000001"` FirstSampleFlags uint32 `mp4:"3,size=32,opt=0x000004,hex"` Entries []TrunEntry `mp4:"4,len=dynamic,size=dynamic"` } type TrunEntry struct { SampleDuration uint32 `mp4:"0,size=32,opt=0x000100"` SampleSize uint32 `mp4:"1,size=32,opt=0x000200"` SampleFlags uint32 `mp4:"2,size=32,opt=0x000400,hex"` SampleCompositionTimeOffsetV0 uint32 `mp4:"3,size=32,opt=0x000800,ver=0"` SampleCompositionTimeOffsetV1 int32 `mp4:"4,size=32,opt=0x000800,nver=0"` } // GetType returns the BoxType func (*Trun) GetType() BoxType { return BoxTypeTrun() } // GetFieldSize returns size of dynamic field func (trun *Trun) GetFieldSize(name string, ctx Context) uint { switch name { case "Entries": var size uint flags := trun.GetFlags() if flags&0x100 != 0 { size += 32 // SampleDuration } if flags&0x200 != 0 { size += 32 // SampleSize } if flags&0x400 != 0 { size += 32 // SampleFlags } if flags&0x800 != 0 { size += 32 // SampleCompositionTimeOffsetV0 or V1 } return size } panic(fmt.Errorf("invalid name of dynamic-size field: boxType=trun fieldName=%s", name)) } // GetFieldLength returns length of dynamic field func (trun *Trun) GetFieldLength(name string, ctx Context) uint { switch name { case "Entries": return uint(trun.SampleCount) } panic(fmt.Errorf("invalid name of dynamic-length field: boxType=trun fieldName=%s", name)) } func (trun *Trun) GetSampleCompositionTimeOffset(index int) int64 { switch trun.GetVersion() { case 0: return int64(trun.Entries[index].SampleCompositionTimeOffsetV0) case 1: return int64(trun.Entries[index].SampleCompositionTimeOffsetV1) default: return 0 } } /*************************** udta ****************************/ func BoxTypeUdta() BoxType { return StrToBoxType("udta") } var udta3GppMetaBoxTypes = []BoxType{ StrToBoxType("titl"), StrToBoxType("dscp"), StrToBoxType("cprt"), StrToBoxType("perf"), StrToBoxType("auth"), StrToBoxType("gnre"), } func init() { AddBoxDef(&Udta{}) for _, bt := range udta3GppMetaBoxTypes { AddAnyTypeBoxDefEx(&Udta3GppString{}, bt, isUnderUdta, 0) } } // Udta is ISOBMFF udta box type type Udta struct { Box } // GetType returns the BoxType func (*Udta) GetType() BoxType { return BoxTypeUdta() } type Udta3GppString struct { AnyTypeBox FullBox `mp4:"0,extend"` Pad bool `mp4:"1,size=1,hidden"` Language [3]byte `mp4:"2,size=5,iso639-2"` // ISO-639-2/T language code Data []byte `mp4:"3,size=8,string"` } func isUnderUdta(ctx Context) bool { return ctx.UnderUdta } /*************************** vmhd ****************************/ func BoxTypeVmhd() BoxType { return StrToBoxType("vmhd") } func init() { AddBoxDef(&Vmhd{}, 0) } // Vmhd is ISOBMFF vmhd box type type Vmhd struct { FullBox `mp4:"0,extend"` Graphicsmode uint16 `mp4:"1,size=16"` // template=0 Opcolor [3]uint16 `mp4:"2,size=16"` // template={0, 0, 0} } // GetType returns the BoxType func (*Vmhd) GetType() BoxType { return BoxTypeVmhd() } /*************************** wave ****************************/ func BoxTypeWave() BoxType { return StrToBoxType("wave") } func init() { AddBoxDef(&Wave{}) } // Wave is QuickTime wave box type Wave struct { Box } // GetType returns the BoxType func (*Wave) GetType() BoxType { return BoxTypeWave() }