mp4.go (3530B)
1 package mp4 2 3 import ( 4 "errors" 5 "fmt" 6 "reflect" 7 "strings" 8 ) 9 10 var ErrBoxInfoNotFound = errors.New("box info not found") 11 12 // BoxType is mpeg box type 13 type BoxType [4]byte 14 15 func StrToBoxType(code string) BoxType { 16 if len(code) != 4 { 17 panic(fmt.Errorf("invalid box type id length: [%s]", code)) 18 } 19 return BoxType{code[0], code[1], code[2], code[3]} 20 } 21 22 func (boxType BoxType) String() string { 23 if isPrintable(boxType[0]) && isPrintable(boxType[1]) && isPrintable(boxType[2]) && isPrintable(boxType[3]) { 24 s := string([]byte{boxType[0], boxType[1], boxType[2], boxType[3]}) 25 s = strings.ReplaceAll(s, string([]byte{0xa9}), "(c)") 26 return s 27 } 28 return fmt.Sprintf("0x%02x%02x%02x%02x", boxType[0], boxType[1], boxType[2], boxType[3]) 29 } 30 31 func isASCII(c byte) bool { 32 return c >= 0x20 && c <= 0x7e 33 } 34 35 func isPrintable(c byte) bool { 36 return isASCII(c) || c == 0xa9 37 } 38 39 func (lhs BoxType) MatchWith(rhs BoxType) bool { 40 if lhs == boxTypeAny || rhs == boxTypeAny { 41 return true 42 } 43 return lhs == rhs 44 } 45 46 var boxTypeAny = BoxType{0x00, 0x00, 0x00, 0x00} 47 48 func BoxTypeAny() BoxType { 49 return boxTypeAny 50 } 51 52 type boxDef struct { 53 dataType reflect.Type 54 versions []uint8 55 isTarget func(Context) bool 56 fields []*field 57 } 58 59 var boxMap = make(map[BoxType][]boxDef, 64) 60 61 func AddBoxDef(payload IBox, versions ...uint8) { 62 boxMap[payload.GetType()] = append(boxMap[payload.GetType()], boxDef{ 63 dataType: reflect.TypeOf(payload).Elem(), 64 versions: versions, 65 fields: buildFields(payload), 66 }) 67 } 68 69 func AddBoxDefEx(payload IBox, isTarget func(Context) bool, versions ...uint8) { 70 boxMap[payload.GetType()] = append(boxMap[payload.GetType()], boxDef{ 71 dataType: reflect.TypeOf(payload).Elem(), 72 versions: versions, 73 isTarget: isTarget, 74 fields: buildFields(payload), 75 }) 76 } 77 78 func AddAnyTypeBoxDef(payload IAnyType, boxType BoxType, versions ...uint8) { 79 boxMap[boxType] = append(boxMap[boxType], boxDef{ 80 dataType: reflect.TypeOf(payload).Elem(), 81 versions: versions, 82 fields: buildFields(payload), 83 }) 84 } 85 86 func AddAnyTypeBoxDefEx(payload IAnyType, boxType BoxType, isTarget func(Context) bool, versions ...uint8) { 87 boxMap[boxType] = append(boxMap[boxType], boxDef{ 88 dataType: reflect.TypeOf(payload).Elem(), 89 versions: versions, 90 isTarget: isTarget, 91 fields: buildFields(payload), 92 }) 93 } 94 95 func (boxType BoxType) getBoxDef(ctx Context) *boxDef { 96 boxDefs := boxMap[boxType] 97 for i := len(boxDefs) - 1; i >= 0; i-- { 98 boxDef := &boxDefs[i] 99 if boxDef.isTarget == nil || boxDef.isTarget(ctx) { 100 return boxDef 101 } 102 } 103 return nil 104 } 105 106 func (boxType BoxType) IsSupported(ctx Context) bool { 107 return boxType.getBoxDef(ctx) != nil 108 } 109 110 func (boxType BoxType) New(ctx Context) (IBox, error) { 111 boxDef := boxType.getBoxDef(ctx) 112 if boxDef == nil { 113 return nil, ErrBoxInfoNotFound 114 } 115 116 box, ok := reflect.New(boxDef.dataType).Interface().(IBox) 117 if !ok { 118 return nil, fmt.Errorf("box type not implements IBox interface: %s", boxType.String()) 119 } 120 121 anyTypeBox, ok := box.(IAnyType) 122 if ok { 123 anyTypeBox.SetType(boxType) 124 } 125 126 return box, nil 127 } 128 129 func (boxType BoxType) GetSupportedVersions(ctx Context) ([]uint8, error) { 130 boxDef := boxType.getBoxDef(ctx) 131 if boxDef == nil { 132 return nil, ErrBoxInfoNotFound 133 } 134 return boxDef.versions, nil 135 } 136 137 func (boxType BoxType) IsSupportedVersion(ver uint8, ctx Context) bool { 138 boxDef := boxType.getBoxDef(ctx) 139 if boxDef == nil { 140 return false 141 } 142 if len(boxDef.versions) == 0 { 143 return true 144 } 145 for _, sver := range boxDef.versions { 146 if ver == sver { 147 return true 148 } 149 } 150 return false 151 }