mirror of
https://github.com/superseriousbusiness/gotosocial.git
synced 2024-09-27 05:50:09 +00:00
2746 lines
69 KiB
Go
2746 lines
69 KiB
Go
|
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"`
|
||
|
}
|
||
|
|
||
|
/************************ 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
|
||
|
}
|
||
|
|
||
|
/*************************** 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 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(&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()
|
||
|
}
|