field.go (5697B)
1 package mp4 2 3 import ( 4 "fmt" 5 "os" 6 "reflect" 7 "sort" 8 "strconv" 9 "strings" 10 ) 11 12 type ( 13 stringType uint8 14 fieldFlag uint16 15 ) 16 17 const ( 18 stringType_C stringType = iota 19 stringType_C_P 20 21 fieldString fieldFlag = 1 << iota // 0 22 fieldExtend // 1 23 fieldDec // 2 24 fieldHex // 3 25 fieldISO639_2 // 4 26 fieldUUID // 5 27 fieldHidden // 6 28 fieldOptDynamic // 7 29 fieldVarint // 8 30 fieldSizeDynamic // 9 31 fieldLengthDynamic // 10 32 ) 33 34 type field struct { 35 children []*field 36 name string 37 cnst string 38 order int 39 optFlag uint32 40 nOptFlag uint32 41 size uint 42 length uint 43 flags fieldFlag 44 strType stringType 45 version uint8 46 nVersion uint8 47 } 48 49 func (f *field) set(flag fieldFlag) { 50 f.flags |= flag 51 } 52 53 func (f *field) is(flag fieldFlag) bool { 54 return f.flags&flag != 0 55 } 56 57 func buildFields(box IImmutableBox) []*field { 58 t := reflect.TypeOf(box).Elem() 59 return buildFieldsStruct(t) 60 } 61 62 func buildFieldsStruct(t reflect.Type) []*field { 63 fs := make([]*field, 0, 8) 64 for i := 0; i < t.NumField(); i++ { 65 ft := t.Field(i).Type 66 tag, ok := t.Field(i).Tag.Lookup("mp4") 67 if !ok { 68 continue 69 } 70 f := buildField(t.Field(i).Name, tag) 71 f.children = buildFieldsAny(ft) 72 fs = append(fs, f) 73 } 74 sort.SliceStable(fs, func(i, j int) bool { 75 return fs[i].order < fs[j].order 76 }) 77 return fs 78 } 79 80 func buildFieldsAny(t reflect.Type) []*field { 81 switch t.Kind() { 82 case reflect.Struct: 83 return buildFieldsStruct(t) 84 case reflect.Ptr, reflect.Array, reflect.Slice: 85 return buildFieldsAny(t.Elem()) 86 default: 87 return nil 88 } 89 } 90 91 func buildField(fieldName string, tag string) *field { 92 f := &field{ 93 name: fieldName, 94 } 95 tagMap := parseFieldTag(tag) 96 for key, val := range tagMap { 97 if val != "" { 98 continue 99 } 100 if order, err := strconv.Atoi(key); err == nil { 101 f.order = order 102 break 103 } 104 } 105 106 if val, contained := tagMap["string"]; contained { 107 f.set(fieldString) 108 if val == "c_p" { 109 f.strType = stringType_C_P 110 fmt.Fprint(os.Stderr, "go-mp4: string=c_p tag is deprecated!! See https://github.com/abema/go-mp4/issues/76\n") 111 } 112 } 113 114 if _, contained := tagMap["varint"]; contained { 115 f.set(fieldVarint) 116 } 117 118 if val, contained := tagMap["opt"]; contained { 119 if val == "dynamic" { 120 f.set(fieldOptDynamic) 121 } else { 122 base := 10 123 if strings.HasPrefix(val, "0x") { 124 val = val[2:] 125 base = 16 126 } 127 opt, err := strconv.ParseUint(val, base, 32) 128 if err != nil { 129 panic(err) 130 } 131 f.optFlag = uint32(opt) 132 } 133 } 134 135 if val, contained := tagMap["nopt"]; contained { 136 base := 10 137 if strings.HasPrefix(val, "0x") { 138 val = val[2:] 139 base = 16 140 } 141 nopt, err := strconv.ParseUint(val, base, 32) 142 if err != nil { 143 panic(err) 144 } 145 f.nOptFlag = uint32(nopt) 146 } 147 148 if _, contained := tagMap["extend"]; contained { 149 f.set(fieldExtend) 150 } 151 152 if _, contained := tagMap["dec"]; contained { 153 f.set(fieldDec) 154 } 155 156 if _, contained := tagMap["hex"]; contained { 157 f.set(fieldHex) 158 } 159 160 if _, contained := tagMap["iso639-2"]; contained { 161 f.set(fieldISO639_2) 162 } 163 164 if _, contained := tagMap["uuid"]; contained { 165 f.set(fieldUUID) 166 } 167 168 if _, contained := tagMap["hidden"]; contained { 169 f.set(fieldHidden) 170 } 171 172 if val, contained := tagMap["const"]; contained { 173 f.cnst = val 174 } 175 176 f.version = anyVersion 177 if val, contained := tagMap["ver"]; contained { 178 ver, err := strconv.Atoi(val) 179 if err != nil { 180 panic(err) 181 } 182 f.version = uint8(ver) 183 } 184 185 f.nVersion = anyVersion 186 if val, contained := tagMap["nver"]; contained { 187 ver, err := strconv.Atoi(val) 188 if err != nil { 189 panic(err) 190 } 191 f.nVersion = uint8(ver) 192 } 193 194 if val, contained := tagMap["size"]; contained { 195 if val == "dynamic" { 196 f.set(fieldSizeDynamic) 197 } else { 198 size, err := strconv.ParseUint(val, 10, 32) 199 if err != nil { 200 panic(err) 201 } 202 f.size = uint(size) 203 } 204 } 205 206 f.length = LengthUnlimited 207 if val, contained := tagMap["len"]; contained { 208 if val == "dynamic" { 209 f.set(fieldLengthDynamic) 210 } else { 211 l, err := strconv.ParseUint(val, 10, 32) 212 if err != nil { 213 panic(err) 214 } 215 f.length = uint(l) 216 } 217 } 218 219 return f 220 } 221 222 func parseFieldTag(str string) map[string]string { 223 tag := make(map[string]string, 8) 224 225 list := strings.Split(str, ",") 226 for _, e := range list { 227 kv := strings.SplitN(e, "=", 2) 228 if len(kv) == 2 { 229 tag[strings.Trim(kv[0], " ")] = strings.Trim(kv[1], " ") 230 } else { 231 tag[strings.Trim(kv[0], " ")] = "" 232 } 233 } 234 235 return tag 236 } 237 238 type fieldInstance struct { 239 field 240 cfo ICustomFieldObject 241 } 242 243 func resolveFieldInstance(f *field, box IImmutableBox, parent reflect.Value, ctx Context) *fieldInstance { 244 fi := fieldInstance{ 245 field: *f, 246 } 247 248 cfo, ok := parent.Addr().Interface().(ICustomFieldObject) 249 if ok { 250 fi.cfo = cfo 251 } else { 252 fi.cfo = box 253 } 254 255 if fi.is(fieldSizeDynamic) { 256 fi.size = fi.cfo.GetFieldSize(f.name, ctx) 257 } 258 259 if fi.is(fieldLengthDynamic) { 260 fi.length = fi.cfo.GetFieldLength(f.name, ctx) 261 } 262 263 return &fi 264 } 265 266 func isTargetField(box IImmutableBox, fi *fieldInstance, ctx Context) bool { 267 if box.GetVersion() != anyVersion { 268 if fi.version != anyVersion && box.GetVersion() != fi.version { 269 return false 270 } 271 272 if fi.nVersion != anyVersion && box.GetVersion() == fi.nVersion { 273 return false 274 } 275 } 276 277 if fi.optFlag != 0 && box.GetFlags()&fi.optFlag == 0 { 278 return false 279 } 280 281 if fi.nOptFlag != 0 && box.GetFlags()&fi.nOptFlag != 0 { 282 return false 283 } 284 285 if fi.is(fieldOptDynamic) && !fi.cfo.IsOptFieldEnabled(fi.name, ctx) { 286 return false 287 } 288 289 return true 290 }