├── README.md ├── atom ├── atom.go ├── dumper.go ├── genStruct.js ├── genStruct.sh ├── otherStruct.go ├── reader.go ├── struct.go ├── types.go ├── utils.go └── writer.go ├── demuxer.go ├── example └── example.go ├── isom ├── isom.go └── isom_test.go ├── muxer.go └── track.go /README.md: -------------------------------------------------------------------------------- 1 | # A pure golang mp4 library 2 | 3 | Provides mp4 reader/writer and mp4 atom manipulations functions. 4 | 5 | Open a mp4 file and read the first sample: 6 | ```go 7 | file, _ := os.Open("test.mp4") 8 | demuxer := &mp4.Demuxer{R: file} 9 | demuxer.ReadHeader() 10 | pts, dts, isKeyFrame, data, err := demuxer.TrackH264.ReadSample() 11 | ``` 12 | 13 | do some seeking: 14 | 15 | ```go 16 | demuxer.TrackH264.SeekToTime(2.0) 17 | ``` 18 | 19 | demuxer demo code [here](https://github.com/nareix/mp4/blob/master/example/example.go#L11) 20 | 21 | the library also provide atom struct decoding/encoding functions( 22 | learn more about mp4 atoms [here](https://developer.apple.com/library/mac/documentation/QuickTime/QTFF/QTFFChap2/qtff2.html) 23 | ) 24 | 25 | you can access atom structs via `Demuxer.TrackH264.TrackAtom`. for example: 26 | 27 | ```go 28 | // Get the raw TimeScale field inside `mvhd` atom 29 | fmt.Println(demuxer.TrackH264.TrackAtom.Media.Header.TimeScale) 30 | ``` 31 | 32 | for more see Atom API Docs 33 | 34 | # Documentation 35 | 36 | [API Docs](https://godoc.org/github.com/nareix/mp4) 37 | 38 | [Atom API Docs](https://godoc.org/github.com/nareix/mp4/atom) 39 | -------------------------------------------------------------------------------- /atom/atom.go: -------------------------------------------------------------------------------- 1 | 2 | package atom 3 | 4 | -------------------------------------------------------------------------------- /atom/dumper.go: -------------------------------------------------------------------------------- 1 | 2 | package atom 3 | 4 | import ( 5 | "io" 6 | "fmt" 7 | "strings" 8 | "encoding/hex" 9 | ) 10 | 11 | type Walker interface { 12 | FilterArrayItem(string,string,int,int) bool 13 | ArrayLeft(int,int) 14 | StartStruct(string) 15 | EndStruct() 16 | Name(string) 17 | Int(int) 18 | Int64(int64) 19 | HexInt(int) 20 | Fixed(Fixed) 21 | String(string) 22 | Bytes([]byte) 23 | TimeStamp(TimeStamp) 24 | Println(msg ...interface{}) 25 | } 26 | 27 | type Dumper struct { 28 | W io.Writer 29 | depth int 30 | name string 31 | arrlen int 32 | arridx int 33 | } 34 | 35 | func (self Dumper) tab() string { 36 | return strings.Repeat(" ", self.depth*2) 37 | } 38 | 39 | func (self Dumper) Println(msg ...interface{}) { 40 | fmt.Fprintln(self.W, self.tab()+fmt.Sprint(msg...)) 41 | } 42 | 43 | func (self *Dumper) ArrayLeft(i int, n int) { 44 | self.Println(fmt.Sprintf("... total %d elements", n)) 45 | } 46 | 47 | func (self *Dumper) FilterArrayItem(name string, field string, i int, n int) bool { 48 | if n > 20 && i > 20 { 49 | return false 50 | } 51 | return true 52 | } 53 | 54 | func (self *Dumper) EndArray() { 55 | } 56 | 57 | func (self *Dumper) StartStruct(name string) { 58 | self.depth++ 59 | self.Println(fmt.Sprintf("[%s]", name)) 60 | } 61 | 62 | func (self *Dumper) EndStruct() { 63 | self.depth-- 64 | } 65 | 66 | func (self *Dumper) Name(name string) { 67 | self.name = name 68 | } 69 | 70 | func (self Dumper) Int(val int) { 71 | self.Int64(int64(val)) 72 | } 73 | 74 | func (self Dumper) Int64(val int64) { 75 | self.Println(fmt.Sprintf("%s: %d", self.name, val)) 76 | } 77 | 78 | func (self Dumper) HexInt(val int) { 79 | self.Println(fmt.Sprintf("%s: %x", self.name, val)) 80 | } 81 | 82 | func (self Dumper) String(val string) { 83 | self.Println(fmt.Sprintf("%s: %s", self.name, val)) 84 | } 85 | 86 | func (self Dumper) Fixed(val Fixed) { 87 | self.Println(fmt.Sprintf("%s: %d", self.name, FixedToInt(val))) 88 | } 89 | 90 | func (self Dumper) Bytes(val []byte) { 91 | self.Println(fmt.Sprintf("%s: %s", self.name, hex.EncodeToString(val))) 92 | } 93 | 94 | func (self Dumper) TimeStamp(val TimeStamp) { 95 | self.Println(fmt.Sprintf("%s: %d", self.name, int(val))) 96 | } 97 | 98 | -------------------------------------------------------------------------------- /atom/genStruct.js: -------------------------------------------------------------------------------- 1 | 2 | var uc = x => x && x.substr(0,1).toUpperCase()+x.slice(1); 3 | Array.prototype.nonull = function () { 4 | return this.filter(x => x); 5 | }; 6 | 7 | var atoms = { 8 | movie: { 9 | cc4: 'moov', 10 | fields: [ 11 | ['$atoms', [ 12 | ['header', '*movieHeader'], 13 | ['iods', '*iods'], 14 | ['tracks', '[]*track'], 15 | ]], 16 | ], 17 | }, 18 | 19 | iods: { 20 | cc4: 'iods', 21 | fields: [ 22 | ['data', '[]byte'], 23 | ], 24 | }, 25 | 26 | movieHeader: { 27 | cc4: 'mvhd', 28 | fields: [ 29 | ['version', 'int8'], 30 | ['flags', 'int24'], 31 | ['createTime', 'TimeStamp32'], 32 | ['modifyTime', 'TimeStamp32'], 33 | ['timeScale', 'int32'], 34 | ['duration', 'int32'], 35 | ['preferredRate', 'Fixed32'], 36 | ['preferredVolume', 'Fixed16'], 37 | ['_', '[10]byte'], 38 | ['matrix', '[9]int32'], 39 | ['previewTime', 'TimeStamp32'], 40 | ['previewDuration', 'TimeStamp32'], 41 | ['posterTime', 'TimeStamp32'], 42 | ['selectionTime', 'TimeStamp32'], 43 | ['selectionDuration', 'TimeStamp32'], 44 | ['currentTime', 'TimeStamp32'], 45 | ['nextTrackId', 'int32'], 46 | ], 47 | }, 48 | 49 | track: { 50 | cc4: 'trak', 51 | fields: [ 52 | ['$atoms', [ 53 | ['header', '*trackHeader'], 54 | ['media', '*media'], 55 | ]], 56 | ], 57 | }, 58 | 59 | trackHeader: { 60 | cc4: 'tkhd', 61 | fields: [ 62 | ['version', 'int8'], 63 | ['flags', 'int24'], 64 | ['createTime', 'TimeStamp32'], 65 | ['modifyTime', 'TimeStamp32'], 66 | ['trackId', 'int32'], 67 | ['_', '[4]byte'], 68 | ['duration', 'int32'], 69 | ['_', '[8]byte'], 70 | ['layer', 'int16'], 71 | ['alternateGroup', 'int16'], 72 | ['volume', 'Fixed16'], 73 | ['_', '[2]byte'], 74 | ['matrix', '[9]int32'], 75 | ['trackWidth', 'Fixed32'], 76 | ['trackHeight', 'Fixed32'], 77 | ], 78 | }, 79 | 80 | handlerRefer: { 81 | cc4: 'hdlr', 82 | fields: [ 83 | ['version', 'int8'], 84 | ['flags', 'int24'], 85 | ['type', '[4]char'], 86 | ['subType', '[4]char'], 87 | ['name', '[]char'], 88 | ], 89 | }, 90 | 91 | media: { 92 | cc4: 'mdia', 93 | fields: [ 94 | ['$atoms', [ 95 | ['header', '*mediaHeader'], 96 | ['handler', '*handlerRefer'], 97 | ['info', '*mediaInfo'], 98 | ]], 99 | ], 100 | }, 101 | 102 | mediaHeader: { 103 | cc4: 'mdhd', 104 | fields: [ 105 | ['version', 'int8'], 106 | ['flags', 'int24'], 107 | ['createTime', 'TimeStamp32'], 108 | ['modifyTime', 'TimeStamp32'], 109 | ['timeScale', 'int32'], 110 | ['duration', 'int32'], 111 | ['language', 'int16'], 112 | ['quality', 'int16'], 113 | ], 114 | }, 115 | 116 | mediaInfo: { 117 | cc4: 'minf', 118 | fields: [ 119 | ['$atoms', [ 120 | ['sound', '*soundMediaInfo'], 121 | ['video', '*videoMediaInfo'], 122 | ['data', '*dataInfo'], 123 | ['sample', '*sampleTable'], 124 | ]], 125 | ], 126 | }, 127 | 128 | dataInfo: { 129 | cc4: 'dinf', 130 | fields: [ 131 | ['$atoms', [ 132 | ['refer', '*dataRefer'], 133 | ]], 134 | ], 135 | }, 136 | 137 | dataRefer: { 138 | cc4: 'dref', 139 | fields: [ 140 | ['version', 'int8'], 141 | ['flags', 'int24'], 142 | 143 | ['$atomsCount', 'int32'], 144 | ['$atoms', [ 145 | ['url', '*dataReferUrl'], 146 | ]], 147 | ], 148 | }, 149 | 150 | dataReferUrl: { 151 | cc4: 'url ', 152 | fields: [ 153 | ['version', 'int8'], 154 | ['flags', 'int24'], 155 | ], 156 | }, 157 | 158 | soundMediaInfo: { 159 | cc4: 'smhd', 160 | fields: [ 161 | ['version', 'int8'], 162 | ['flags', 'int24'], 163 | ['balance', 'int16'], 164 | ['_', 'int16'], 165 | ], 166 | }, 167 | 168 | videoMediaInfo: { 169 | cc4: 'vmhd', 170 | fields: [ 171 | ['version', 'int8'], 172 | ['flags', 'int24'], 173 | ['graphicsMode', 'int16'], 174 | ['opcolor', '[3]int16'], 175 | ], 176 | }, 177 | 178 | sampleTable: { 179 | cc4: 'stbl', 180 | fields: [ 181 | ['$atoms', [ 182 | ['sampleDesc', '*sampleDesc'], 183 | ['timeToSample', '*timeToSample'], 184 | ['compositionOffset', '*compositionOffset'], 185 | ['sampleToChunk', '*sampleToChunk'], 186 | ['syncSample', '*syncSample'], 187 | ['chunkOffset', '*chunkOffset'], 188 | ['sampleSize', '*sampleSize'], 189 | ]], 190 | ], 191 | }, 192 | 193 | sampleDesc: { 194 | cc4: 'stsd', 195 | fields: [ 196 | ['version', 'int8'], 197 | ['_', '[3]byte'], 198 | ['$atomsCount', 'int32'], 199 | ['$atoms', [ 200 | ['avc1Desc', '*avc1Desc'], 201 | ['mp4aDesc', '*mp4aDesc'], 202 | ]], 203 | ], 204 | }, 205 | 206 | mp4aDesc: { 207 | cc4: 'mp4a', 208 | fields: [ 209 | ['_', '[6]byte'], 210 | ['dataRefIdx', 'int16'], 211 | ['version', 'int16'], 212 | ['revisionLevel', 'int16'], 213 | ['vendor', 'int32'], 214 | ['numberOfChannels', 'int16'], 215 | ['sampleSize', 'int16'], 216 | ['compressionId', 'int16'], 217 | ['_', 'int16'], 218 | ['sampleRate', 'Fixed32'], 219 | ['$atoms', [ 220 | ['conf', '*elemStreamDesc'], 221 | ]], 222 | ], 223 | }, 224 | 225 | elemStreamDesc: { 226 | cc4: 'esds', 227 | fields: [ 228 | ['version', 'int32'], 229 | ['data', '[]byte'], 230 | ], 231 | }, 232 | 233 | avc1Desc: { 234 | cc4: 'avc1', 235 | fields: [ 236 | ['_', '[6]byte'], 237 | ['dataRefIdx', 'int16'], 238 | ['version', 'int16'], 239 | ['revision', 'int16'], 240 | ['vendor', 'int32'], 241 | ['temporalQuality', 'int32'], 242 | ['spatialQuality', 'int32'], 243 | ['width', 'int16'], 244 | ['height', 'int16'], 245 | ['horizontalResolution', 'Fixed32'], 246 | ['vorizontalResolution', 'Fixed32'], 247 | ['_', 'int32'], 248 | ['frameCount', 'int16'], 249 | ['compressorName', '[32]char'], 250 | ['depth', 'int16'], 251 | ['colorTableId', 'int16'], 252 | 253 | ['$atoms', [ 254 | ['conf', '*avc1Conf'], 255 | ]], 256 | ], 257 | }, 258 | 259 | avc1Conf: { 260 | cc4: 'avcC', 261 | fields: [ 262 | ['record', 'AVCDecoderConfRecord'], 263 | ], 264 | }, 265 | 266 | timeToSample: { 267 | cc4: 'stts', 268 | fields: [ 269 | ['version', 'int8'], 270 | ['flags', 'int24'], 271 | ['entries', '[int32]timeToSampleEntry'], 272 | ], 273 | }, 274 | 275 | timeToSampleEntry: { 276 | fields: [ 277 | ['count', 'int32'], 278 | ['duration', 'int32'], 279 | ], 280 | }, 281 | 282 | sampleToChunk: { 283 | cc4: 'stsc', 284 | fields: [ 285 | ['version', 'int8'], 286 | ['flags', 'int24'], 287 | ['entries', '[int32]sampleToChunkEntry'], 288 | ], 289 | }, 290 | 291 | sampleToChunkEntry: { 292 | fields: [ 293 | ['firstChunk', 'int32'], 294 | ['samplesPerChunk', 'int32'], 295 | ['sampleDescId', 'int32'], 296 | ], 297 | }, 298 | 299 | compositionOffset: { 300 | cc4: 'ctts', 301 | fields: [ 302 | ['version', 'int8'], 303 | ['flags', 'int24'], 304 | ['entries', '[int32]compositionOffsetEntry'], 305 | ], 306 | }, 307 | 308 | compositionOffsetEntry: { 309 | fields: [ 310 | ['count', 'int32'], 311 | ['offset', 'int32'], 312 | ], 313 | }, 314 | 315 | syncSample: { 316 | cc4: 'stss', 317 | fields: [ 318 | ['version', 'int8'], 319 | ['flags', 'int24'], 320 | ['entries', '[int32]int32'], 321 | ], 322 | }, 323 | 324 | sampleSize: { 325 | cc4: 'stsz', 326 | fields: [ 327 | ['version', 'int8'], 328 | ['flags', 'int24'], 329 | ['sampleSize', 'int32'], 330 | ['entries', '[int32]int32'], 331 | ], 332 | }, 333 | 334 | chunkOffset: { 335 | cc4: 'stco', 336 | fields: [ 337 | ['version', 'int8'], 338 | ['flags', 'int24'], 339 | ['entries', '[int32]int32'], 340 | ], 341 | }, 342 | 343 | movieFrag: { 344 | cc4: 'moof', 345 | fields: [ 346 | ['$atoms', [ 347 | ['header', '*movieFragHeader'], 348 | ['tracks', '[]*trackFrag'], 349 | ]], 350 | ], 351 | }, 352 | 353 | trackFragDecodeTime: { 354 | cc4: 'tfdt', 355 | }, 356 | 357 | movieFragHeader: { 358 | cc4: 'mfhd', 359 | fields: [ 360 | ['version', 'int8'], 361 | ['flags', 'int24'], 362 | ['seqNum', 'int32'], 363 | ], 364 | }, 365 | 366 | trackFrag: { 367 | cc4: 'traf', 368 | fields: [ 369 | ['$atoms', [ 370 | ['header', '*trackFragHeader'], 371 | ['decodeTime', '*trackFragDecodeTime'], 372 | ['run', '*trackFragRun'], 373 | ]], 374 | ], 375 | }, 376 | 377 | trackFragRun: { 378 | cc4: 'trun', 379 | }, 380 | 381 | trackFragHeader: { 382 | cc4: 'tfhd', 383 | }, 384 | 385 | /* 386 | // need hand write 387 | trackFragRun: { 388 | cc4: 'trun', 389 | fields: [ 390 | ['version', 'int8'], 391 | ['flags', 'int24'], 392 | ['sampleCount', 'int32'], 393 | ['dataOffset', 'int32'], 394 | ['entries', '[]int32'], 395 | ], 396 | }, 397 | 398 | trackFragHeader: { 399 | cc4: 'tfhd', 400 | fields: [ 401 | ['version', 'int8'], 402 | ['flags', 'int24'], 403 | ['id', 'int32'], 404 | ['sampleDescriptionIndex', 'int32'], 405 | ['_', '[12]byte'], 406 | ], 407 | }, 408 | */ 409 | }; 410 | 411 | var DeclReadFunc = (opts) => { 412 | var stmts = []; 413 | 414 | var DebugStmt = type => `// ${JSON.stringify(type)}`; 415 | 416 | var ReadArr = (name, type) => { 417 | return [ 418 | //StrStmt('// ReadArr'), 419 | //DebugStmt(type), 420 | type.varcount && [ 421 | DeclVar('count', 'int'), 422 | CallCheckAssign('ReadInt', ['r', type.varcount], ['count']), 423 | `${name} = make(${typeStr(type)}, count)`, 424 | ], 425 | For(RangeN('i', type.varcount ? 'count' : type.count), [ 426 | ReadCommnType(name+'[i]', type), 427 | ]), 428 | ]; 429 | }; 430 | 431 | var elemTypeStr = type => typeStr(Object.assign({}, type, {arr: false})); 432 | var ReadAtoms = fields => [ 433 | For(`r.N > 0`, [ 434 | DeclVar('cc4', 'string'), 435 | DeclVar('ar', '*io.LimitedReader'), 436 | CallCheckAssign('ReadAtomHeader', ['r', '""'], ['ar', 'cc4']), 437 | Switch('cc4', fields.map(field => [ 438 | `"${atoms[field.type.struct].cc4}"`, [ 439 | field.type.arr ? [ 440 | DeclVar('item', elemTypeStr(field.type)), 441 | CallCheckAssign('Read'+field.type.Struct, ['ar'], ['item']), 442 | `self.${field.name} = append(self.${field.name}, item)`, 443 | ] : [ 444 | CallCheckAssign('Read'+field.type.Struct, ['ar'], [`self.${field.name}`]), 445 | ], 446 | ] 447 | ]), showlog && [`log.Println("skip", cc4)`]), 448 | CallCheckAssign('ReadDummy', ['ar', 'int(ar.N)'], ['_']), 449 | ]) 450 | ]; 451 | 452 | var ReadCommnType = (name, type) => { 453 | if (type.struct) 454 | return CallCheckAssign( 455 | 'Read'+type.Struct, ['r'], [name]); 456 | return [ 457 | //DebugStmt(type), 458 | CallCheckAssign( 459 | 'Read'+type.fn, ['r', type.len||'int(r.N)'], [name]), 460 | ] 461 | }; 462 | 463 | var ReadField = (name, type) => { 464 | if (name == '_') 465 | return CallCheckAssign('ReadDummy', ['r', type.len], ['_']); 466 | if (name == '$atoms') 467 | return ReadAtoms(type.list); 468 | if (name == '$atomsCount') 469 | return CallCheckAssign('ReadDummy', ['r', type.len], ['_']); 470 | if (type.arr && type.fn != 'Bytes') 471 | return ReadArr('self.'+name, type); 472 | return ReadCommnType('self.'+name, type); 473 | }; 474 | 475 | var ReadFields = () => opts.fields.map(field => { 476 | var name = field.name; 477 | var type = field.type; 478 | return ReadField(name, type); 479 | }).nonull(); 480 | 481 | var ptr = opts.cc4; 482 | 483 | return Func( 484 | 'Read'+opts.type, 485 | [['r', '*io.LimitedReader']], 486 | [[ptr?'res':'self', (ptr?'*':'')+opts.type], ['err', 'error']], 487 | [ 488 | ptr && `self := &${opts.type}{}`, 489 | ReadFields(), 490 | ptr && `res = self`, 491 | ] 492 | ); 493 | }; 494 | 495 | var DeclWriteFunc = (opts) => { 496 | var SavePos = [ 497 | DeclVar('aw', '*Writer'), 498 | CallCheckAssign('WriteAtomHeader', ['w', `"${opts.cc4}"`], ['aw']), 499 | `w = aw`, 500 | ]; 501 | 502 | var RestorePosSetSize = [ 503 | CallCheckAssign('aw.Close', [], []), 504 | ]; 505 | 506 | var WriteAtoms = fields => fields.map(field => { 507 | var name = 'self.'+field.name; 508 | return [ 509 | `if ${name} != nil {`, 510 | field.type.arr ? WriteArr(name, field.type) : WriteCommnType(name, field.type), 511 | atomsCount && `${atomsCount.name}++`, 512 | `}`, 513 | ]; 514 | }); 515 | 516 | var WriteArr = (name, type) => { 517 | return [ 518 | type.varcount && CallCheckAssign('WriteInt', ['w', `len(${name})`, type.varcount], []), 519 | For(`_, elem := range ${name}`, [ 520 | WriteCommnType('elem', type), 521 | ]), 522 | ]; 523 | }; 524 | 525 | var WriteCommnType = (name, type) => { 526 | if (type.struct) 527 | return CallCheckAssign( 528 | 'Write'+type.Struct, ['w', name], []); 529 | return [ 530 | CallCheckAssign( 531 | 'Write'+type.fn, ['w', name, type.len||`len(${name})`], []), 532 | ] 533 | }; 534 | 535 | var atomsCount; 536 | 537 | var WriteAtomsCountStart = (type) => { 538 | atomsCount = { 539 | name: 'atomsCount', 540 | namePos: 'atomsCountPos', 541 | type: type, 542 | } 543 | return [ 544 | DeclVar(atomsCount.name, 'int'), 545 | DeclVar(atomsCount.namePos, 'int64'), 546 | CallCheckAssign('WriteEmptyInt', ['w', type.len], [atomsCount.namePos]), 547 | ]; 548 | }; 549 | 550 | var WriteAtomsCountEnd = (type) => { 551 | return [ 552 | CallCheckAssign('RefillInt', 553 | ['w', atomsCount.namePos, atomsCount.name, atomsCount.type.len], 554 | [] 555 | ), 556 | ]; 557 | }; 558 | 559 | var WriteField = (name, type) => { 560 | if (name == '_') 561 | return CallCheckAssign('WriteDummy', ['w', type.len], []); 562 | if (name == '$atoms') 563 | return WriteAtoms(type.list); 564 | if (name == '$atomsCount') 565 | return WriteAtomsCountStart(type); 566 | if (type.arr && type.fn != 'Bytes') 567 | return WriteArr('self.'+name, type); 568 | return WriteCommnType('self.'+name, type); 569 | }; 570 | 571 | var WriteFields = () => opts.fields 572 | .map(field => WriteField(field.name, field.type)) 573 | .concat(atomsCount && WriteAtomsCountEnd()) 574 | 575 | return Func( 576 | 'Write'+opts.type, 577 | [['w', 'io.WriteSeeker'], ['self', (opts.cc4?'*':'')+opts.type]], 578 | [['err', 'error']], 579 | [ 580 | opts.cc4 && SavePos, 581 | WriteFields(), 582 | opts.cc4 && RestorePosSetSize, 583 | ] 584 | ); 585 | }; 586 | 587 | var DeclDumpFunc = (opts) => { 588 | var dumpStruct = (name, type) => { 589 | if (type.ptr) 590 | return If(`${name} != nil`, Call('Walk'+type.Struct, ['w', name])); 591 | return Call('Walk'+type.Struct, ['w', name]); 592 | }; 593 | 594 | var dumpArr = (name, type, id) => { 595 | return [ 596 | //Call('w.StartArray', [`"${id}"`, `len(${name})`]), 597 | For(`i, item := range(${name})`, If( 598 | `w.FilterArrayItem("${opts.type}", "${id}", i, len(${name}))`, 599 | dumpCommonType('item', type, id), 600 | [`w.ArrayLeft(i, len(${name}))`, 'break'] 601 | )), 602 | //Call('w.EndArray', []), 603 | ]; 604 | }; 605 | 606 | var dumpCommonType = (name, type, id) => { 607 | if (type.struct) 608 | return dumpStruct(name, type); 609 | return [ 610 | Call('w.Name', [`"${id}"`]), 611 | Call('w.'+type.fn, [name]), 612 | ]; 613 | }; 614 | 615 | var dumpField = (name, type, noarr) => { 616 | if (name == '_') 617 | return; 618 | if (name == '$atomsCount') 619 | return; 620 | if (name == '$atoms') { 621 | return type.list.map(field => dumpField(field.name, field.type)); 622 | } 623 | if (!noarr && type.arr && type.fn != 'Bytes') 624 | return dumpArr('self.'+name, type, name); 625 | return dumpCommonType('self.'+name, type, name); 626 | }; 627 | 628 | var dumpFields = fields => 629 | [ Call('w.StartStruct', [`"${opts.type}"`]) ] 630 | .concat(fields.map(field => dumpField(field.name, field.type))) 631 | .concat([Call('w.EndStruct', [])]); 632 | 633 | return Func( 634 | 'Walk'+opts.type, 635 | [['w', 'Walker'], ['self', (opts.cc4?'*':'')+opts.type]], 636 | [], 637 | dumpFields(opts.fields) 638 | ) 639 | }; 640 | 641 | var D = (cls, ...fields) => { 642 | global[cls] = (...args) => { 643 | var obj = {cls: cls}; 644 | fields.forEach((k, i) => obj[k] = args[i]); 645 | return obj; 646 | }; 647 | }; 648 | 649 | D('Func', 'name', 'args', 'rets', 'body'); 650 | D('If', 'cond', 'action', 'else'); 651 | D('Call', 'fn', 'args'); 652 | D('CallCheckAssign', 'fn', 'args', 'rets', 'action'); 653 | D('DeclVar', 'name', 'type'); 654 | D('For', 'cond', 'body'); 655 | D('RangeN', 'i', 'n'); 656 | D('DeclStruct', 'name', 'body'); 657 | D('StrStmt', 'content'); 658 | D('Switch', 'cond', 'cases', 'default'); 659 | 660 | var showlog = false; 661 | var S = s => s && s || ''; 662 | 663 | var dumpFn = f => { 664 | var dumpArgs = x => x.map(x => x.join(' ')).join(','); 665 | return `func ${f.name}(${dumpArgs(f.args)}) (${dumpArgs(f.rets)}) { 666 | ${S(showlog && 'log.Println("'+f.name+'")')} 667 | ${dumpStmts(f.body)} 668 | return 669 | }`; 670 | }; 671 | 672 | var dumpStmts = stmt => { 673 | if (typeof(stmt) == 'string') { 674 | return stmt; 675 | } else if (stmt instanceof Array) { 676 | return stmt.nonull().map(dumpStmts).join('\n'); 677 | } else if (stmt.cls == 'If') { 678 | var s = `if ${stmt.cond} { 679 | ${dumpStmts(stmt.action)} 680 | }`; 681 | if (stmt.else) { 682 | s += ` else { 683 | ${dumpStmts(stmt.else)} 684 | }`; 685 | } 686 | return s; 687 | } else if (stmt.cls == 'Call') { 688 | return `${stmt.fn}(${stmt.args.join(',')})`; 689 | } else if (stmt.cls == 'CallCheckAssign') { 690 | return `if ${stmt.rets.concat(['err']).join(',')} = ${stmt.fn}(${stmt.args.join(',')}); err != nil { 691 | ${stmt.action ? stmt.action : 'return'} 692 | }`; 693 | } else if (stmt.cls == 'DeclVar') { 694 | return `var ${stmt.name} ${stmt.type}`; 695 | } else if (stmt.cls == 'For') { 696 | return `for ${dumpStmts(stmt.cond)} { 697 | ${dumpStmts(stmt.body)} 698 | }`; 699 | } else if (stmt.cls == 'RangeN') { 700 | return `${stmt.i} := 0; ${stmt.i} < ${stmt.n}; ${stmt.i}++`; 701 | } else if (stmt.cls == 'DeclStruct') { 702 | return `type ${stmt.name} struct { 703 | ${stmt.body.map(line => line.join(' ')).join('\n')} 704 | }`; 705 | } else if (stmt.cls == 'Func') { 706 | return dumpFn(stmt); 707 | } else if (stmt.cls == 'StrStmt') { 708 | return stmt.content; 709 | } else if (stmt.cls == 'Switch') { 710 | var dumpCase = c => `case ${c[0]}: { ${dumpStmts(c[1])} }`; 711 | var dumpDefault = c => `default: { ${dumpStmts(c)} }`; 712 | return `switch ${stmt.cond} { 713 | ${stmt.cases.map(dumpCase).join('\n')} 714 | ${stmt.default && dumpDefault(stmt.default) || ''} 715 | }`; 716 | } 717 | }; 718 | 719 | var parseType = s => { 720 | var r = {}; 721 | var bracket = /^\[(.*)\]/; 722 | var lenDiv = 8; 723 | var types = /^(int|TimeStamp|byte|Fixed|char)/; 724 | var number = /^[0-9]+/; 725 | 726 | if (s.match(bracket)) { 727 | var count = s.match(bracket)[1]; 728 | if (count.substr(0,3) == 'int') { 729 | r.varcount = +count.substr(3)/8; 730 | } else { 731 | r.count = +count; 732 | } 733 | r.arr = true; 734 | s = s.replace(bracket, ''); 735 | } 736 | 737 | if (s.substr(0,1) == '*') { 738 | r.ptr = true; 739 | s = s.slice(1); 740 | } 741 | 742 | if (s.match(types)) { 743 | r.type = s.match(types)[0]; 744 | r.fn = uc(r.type); 745 | s = s.replace(types, ''); 746 | } 747 | 748 | if (r.type == 'byte' && r.arr) { 749 | r.len = r.count; 750 | r.fn = 'Bytes'; 751 | } 752 | 753 | if (r.type == 'char' && r.arr) { 754 | r.len = r.count; 755 | r.fn = 'String'; 756 | r.type = 'string'; 757 | r.arr = false; 758 | lenDiv = 1; 759 | } 760 | 761 | if (s.match(number)) { 762 | r.len = +s.match(number)[0]/lenDiv; 763 | s = s.replace(number, ''); 764 | } 765 | 766 | if (s != '') { 767 | r.struct = s; 768 | r.Struct = uc(s); 769 | } 770 | return r; 771 | }; 772 | 773 | var typeStr = (t) => { 774 | var s = ''; 775 | if (t.arr) 776 | s += '['+(t.count||'')+']'; 777 | if (t.ptr) 778 | s += '*'; 779 | if (t.struct) 780 | s += t.Struct; 781 | if (t.type) 782 | s += t.type; 783 | return s; 784 | }; 785 | 786 | var nameShouldHide = (name) => name == '_' 787 | 788 | var allStmts = () => { 789 | var stmts = []; 790 | 791 | var parseFields = fields => fields.map(field => { 792 | return { 793 | name: uc(field[0]), 794 | type: field[0] == '$atoms' ? {list: parseFields(field[1])} : parseType(field[1]), 795 | }; 796 | }); 797 | 798 | var genStructFields = fields => fields.map(field => { 799 | if (field.name == '_') 800 | return; 801 | if (field.name == '$atomsCount') 802 | return; 803 | if (field.name == '$atoms') 804 | return field.type.list; 805 | return [field]; 806 | }).nonull().reduce((prev, cur) => prev.concat(cur)).map(field => [ 807 | field.name, typeStr(field.type)]); 808 | 809 | for (var k in atoms) { 810 | var atom = atoms[k]; 811 | var name = uc(k); 812 | 813 | if (atom.fields == null) 814 | continue; 815 | 816 | var fields = parseFields(atom.fields); 817 | 818 | stmts = stmts.concat([ 819 | DeclStruct(name, genStructFields(fields)), 820 | 821 | DeclReadFunc({ 822 | type: name, 823 | fields: fields, 824 | cc4: atom.cc4, 825 | }), 826 | 827 | DeclWriteFunc({ 828 | type: name, 829 | fields: fields, 830 | cc4: atom.cc4, 831 | }), 832 | 833 | DeclDumpFunc({ 834 | type: name, 835 | fields: fields, 836 | cc4: atom.cc4, 837 | }), 838 | ]); 839 | } 840 | 841 | return stmts; 842 | }; 843 | 844 | console.log(` 845 | // THIS FILE IS AUTO GENERATED 846 | package atom 847 | import ( 848 | "io" 849 | ${showlog && '"log"' || ''} 850 | ) 851 | `, dumpStmts(allStmts())); 852 | 853 | -------------------------------------------------------------------------------- /atom/genStruct.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | node --harmony_rest_parameters genStruct.js > struct.go && gofmt -w struct.go && go build . 4 | 5 | -------------------------------------------------------------------------------- /atom/otherStruct.go: -------------------------------------------------------------------------------- 1 | package atom 2 | 3 | import ( 4 | "bytes" 5 | "fmt" 6 | "io" 7 | "github.com/nareix/bits" 8 | ) 9 | 10 | type H264SPSInfo struct { 11 | ProfileIdc uint 12 | LevelIdc uint 13 | 14 | MbWidth uint 15 | MbHeight uint 16 | 17 | CropLeft uint 18 | CropRight uint 19 | CropTop uint 20 | CropBottom uint 21 | 22 | Width uint 23 | Height uint 24 | } 25 | 26 | func ParseH264SPS(data []byte) (res *H264SPSInfo, err error) { 27 | r := &bits.GolombBitReader{ 28 | R: bytes.NewReader(data), 29 | } 30 | 31 | self := &H264SPSInfo{} 32 | 33 | if self.ProfileIdc, err = r.ReadBits(8); err != nil { 34 | return 35 | } 36 | 37 | // constraint_set0_flag-constraint_set6_flag,reserved_zero_2bits 38 | if _, err = r.ReadBits(8); err != nil { 39 | return 40 | } 41 | 42 | // level_idc 43 | if self.LevelIdc, err = r.ReadBits(8); err != nil { 44 | return 45 | } 46 | 47 | // seq_parameter_set_id 48 | if _, err = r.ReadExponentialGolombCode(); err != nil { 49 | return 50 | } 51 | 52 | if self.ProfileIdc == 100 || self.ProfileIdc == 110 || 53 | self.ProfileIdc == 122 || self.ProfileIdc == 244 || 54 | self.ProfileIdc == 44 || self.ProfileIdc == 83 || 55 | self.ProfileIdc == 86 || self.ProfileIdc == 118 { 56 | 57 | var chroma_format_idc uint 58 | if chroma_format_idc, err = r.ReadExponentialGolombCode(); err != nil { 59 | return 60 | } 61 | 62 | if chroma_format_idc == 3 { 63 | // residual_colour_transform_flag 64 | if _, err = r.ReadBit(); err != nil { 65 | return 66 | } 67 | } 68 | 69 | // bit_depth_luma_minus8 70 | if _, err = r.ReadExponentialGolombCode(); err != nil { 71 | return 72 | } 73 | // bit_depth_chroma_minus8 74 | if _, err = r.ReadExponentialGolombCode(); err != nil { 75 | return 76 | } 77 | // qpprime_y_zero_transform_bypass_flag 78 | if _, err = r.ReadBit(); err != nil { 79 | return 80 | } 81 | 82 | var seq_scaling_matrix_present_flag uint 83 | if seq_scaling_matrix_present_flag, err = r.ReadBit(); err != nil { 84 | return 85 | } 86 | 87 | if seq_scaling_matrix_present_flag != 0 { 88 | for i := 0; i < 8; i++ { 89 | var seq_scaling_list_present_flag uint 90 | if seq_scaling_list_present_flag, err = r.ReadBit(); err != nil { 91 | return 92 | } 93 | if seq_scaling_list_present_flag != 0 { 94 | var sizeOfScalingList uint 95 | if i < 6 { 96 | sizeOfScalingList = 16 97 | } else { 98 | sizeOfScalingList = 64 99 | } 100 | lastScale := uint(8) 101 | nextScale := uint(8) 102 | for j := uint(0); j < sizeOfScalingList; j++ { 103 | if nextScale != 0 { 104 | var delta_scale uint 105 | if delta_scale, err = r.ReadSE(); err != nil { 106 | return 107 | } 108 | nextScale = (lastScale + delta_scale + 256) % 256 109 | } 110 | if nextScale != 0 { 111 | lastScale = nextScale 112 | } 113 | } 114 | } 115 | } 116 | } 117 | } 118 | 119 | // log2_max_frame_num_minus4 120 | if _, err = r.ReadExponentialGolombCode(); err != nil { 121 | return 122 | } 123 | 124 | var pic_order_cnt_type uint 125 | if pic_order_cnt_type, err = r.ReadExponentialGolombCode(); err != nil { 126 | return 127 | } 128 | if pic_order_cnt_type == 0 { 129 | // log2_max_pic_order_cnt_lsb_minus4 130 | if _, err = r.ReadExponentialGolombCode(); err != nil { 131 | return 132 | } 133 | } else if pic_order_cnt_type == 1 { 134 | // delta_pic_order_always_zero_flag 135 | if _, err = r.ReadBit(); err != nil { 136 | return 137 | } 138 | // offset_for_non_ref_pic 139 | if _, err = r.ReadSE(); err != nil { 140 | return 141 | } 142 | // offset_for_top_to_bottom_field 143 | if _, err = r.ReadSE(); err != nil { 144 | return 145 | } 146 | var num_ref_frames_in_pic_order_cnt_cycle uint 147 | if num_ref_frames_in_pic_order_cnt_cycle, err = r.ReadExponentialGolombCode(); err != nil { 148 | return 149 | } 150 | for i := uint(0); i < num_ref_frames_in_pic_order_cnt_cycle; i++ { 151 | if _, err = r.ReadSE(); err != nil { 152 | return 153 | } 154 | } 155 | } 156 | 157 | // max_num_ref_frames 158 | if _, err = r.ReadExponentialGolombCode(); err != nil { 159 | return 160 | } 161 | 162 | // gaps_in_frame_num_value_allowed_flag 163 | if _, err = r.ReadBit(); err != nil { 164 | return 165 | } 166 | 167 | if self.MbWidth, err = r.ReadExponentialGolombCode(); err != nil { 168 | return 169 | } 170 | self.MbWidth++ 171 | 172 | if self.MbHeight, err = r.ReadExponentialGolombCode(); err != nil { 173 | return 174 | } 175 | self.MbHeight++ 176 | 177 | var frame_mbs_only_flag uint 178 | if frame_mbs_only_flag, err = r.ReadBit(); err != nil { 179 | return 180 | } 181 | if frame_mbs_only_flag == 0 { 182 | // mb_adaptive_frame_field_flag 183 | if _, err = r.ReadBit(); err != nil { 184 | return 185 | } 186 | } 187 | 188 | // direct_8x8_inference_flag 189 | if _, err = r.ReadBit(); err != nil { 190 | return 191 | } 192 | 193 | var frame_cropping_flag uint 194 | if frame_cropping_flag, err = r.ReadBit(); err != nil { 195 | return 196 | } 197 | if frame_cropping_flag != 0 { 198 | if self.CropLeft, err = r.ReadExponentialGolombCode(); err != nil { 199 | return 200 | } 201 | if self.CropRight, err = r.ReadExponentialGolombCode(); err != nil { 202 | return 203 | } 204 | if self.CropTop, err = r.ReadExponentialGolombCode(); err != nil { 205 | return 206 | } 207 | if self.CropBottom, err = r.ReadExponentialGolombCode(); err != nil { 208 | return 209 | } 210 | } 211 | 212 | self.Width = (self.MbWidth * 16) - self.CropLeft*2 - self.CropRight*2 213 | self.Height = ((2 - frame_mbs_only_flag) * self.MbHeight * 16) - self.CropTop*2 - self.CropBottom*2 214 | 215 | res = self 216 | return 217 | } 218 | 219 | type AVCDecoderConfRecord struct { 220 | AVCProfileIndication int 221 | ProfileCompatibility int 222 | AVCLevelIndication int 223 | LengthSizeMinusOne int 224 | SPS [][]byte 225 | PPS [][]byte 226 | } 227 | 228 | func CreateAVCDecoderConfRecord( 229 | SPS []byte, 230 | PPS []byte, 231 | ) (self AVCDecoderConfRecord, err error) { 232 | if len(SPS) < 4 { 233 | err = fmt.Errorf("invalid SPS data") 234 | return 235 | } 236 | self.AVCProfileIndication = int(SPS[1]) 237 | self.ProfileCompatibility = int(SPS[2]) 238 | self.AVCLevelIndication = int(SPS[3]) 239 | self.SPS = [][]byte{SPS} 240 | self.PPS = [][]byte{PPS} 241 | self.LengthSizeMinusOne = 3 242 | return 243 | } 244 | 245 | func WriteAVCDecoderConfRecord(w io.Writer, self AVCDecoderConfRecord) (err error) { 246 | if err = WriteInt(w, 1, 1); err != nil { 247 | return 248 | } 249 | if err = WriteInt(w, self.AVCProfileIndication, 1); err != nil { 250 | return 251 | } 252 | if err = WriteInt(w, self.ProfileCompatibility, 1); err != nil { 253 | return 254 | } 255 | if err = WriteInt(w, self.AVCLevelIndication, 1); err != nil { 256 | return 257 | } 258 | if err = WriteInt(w, self.LengthSizeMinusOne|0xfc, 1); err != nil { 259 | return 260 | } 261 | 262 | if err = WriteInt(w, len(self.SPS)|0xe0, 1); err != nil { 263 | return 264 | } 265 | for _, data := range self.SPS { 266 | if err = WriteInt(w, len(data), 2); err != nil { 267 | return 268 | } 269 | if err = WriteBytes(w, data, len(data)); err != nil { 270 | return 271 | } 272 | } 273 | 274 | if err = WriteInt(w, len(self.PPS), 1); err != nil { 275 | return 276 | } 277 | for _, data := range self.PPS { 278 | if err = WriteInt(w, len(data), 2); err != nil { 279 | return 280 | } 281 | if err = WriteBytes(w, data, len(data)); err != nil { 282 | return 283 | } 284 | } 285 | 286 | return 287 | } 288 | 289 | func WalkAVCDecoderConfRecord(w Walker, self AVCDecoderConfRecord) { 290 | } 291 | 292 | func ReadAVCDecoderConfRecord(r *io.LimitedReader) (self AVCDecoderConfRecord, err error) { 293 | if _, err = ReadDummy(r, 1); err != nil { 294 | return 295 | } 296 | if self.AVCProfileIndication, err = ReadInt(r, 1); err != nil { 297 | return 298 | } 299 | if self.ProfileCompatibility, err = ReadInt(r, 1); err != nil { 300 | return 301 | } 302 | if self.AVCLevelIndication, err = ReadInt(r, 1); err != nil { 303 | return 304 | } 305 | if self.LengthSizeMinusOne, err = ReadInt(r, 1); err != nil { 306 | return 307 | } 308 | self.LengthSizeMinusOne &= 0x03 309 | 310 | var n, length int 311 | var data []byte 312 | 313 | if n, err = ReadInt(r, 1); err != nil { 314 | return 315 | } 316 | n &= 0x1f 317 | for i := 0; i < n; i++ { 318 | if length, err = ReadInt(r, 2); err != nil { 319 | return 320 | } 321 | if data, err = ReadBytes(r, length); err != nil { 322 | return 323 | } 324 | self.SPS = append(self.SPS, data) 325 | } 326 | 327 | if n, err = ReadInt(r, 1); err != nil { 328 | return 329 | } 330 | for i := 0; i < n; i++ { 331 | if length, err = ReadInt(r, 2); err != nil { 332 | return 333 | } 334 | if data, err = ReadBytes(r, length); err != nil { 335 | return 336 | } 337 | self.PPS = append(self.PPS, data) 338 | } 339 | 340 | return 341 | } 342 | 343 | func WriteSampleByNALU(w io.Writer, nalu []byte) (size int, err error) { 344 | if err = WriteInt(w, len(nalu), 4); err != nil { 345 | return 346 | } 347 | size += 4 348 | if _, err = w.Write(nalu); err != nil { 349 | return 350 | } 351 | size += len(nalu) 352 | return 353 | } 354 | 355 | const ( 356 | TFHD_BASE_DATA_OFFSET = 0x01 357 | TFHD_STSD_ID = 0x02 358 | TFHD_DEFAULT_DURATION = 0x08 359 | TFHD_DEFAULT_SIZE = 0x10 360 | TFHD_DEFAULT_FLAGS = 0x20 361 | TFHD_DURATION_IS_EMPTY = 0x010000 362 | TFHD_DEFAULT_BASE_IS_MOOF = 0x020000 363 | ) 364 | 365 | type TrackFragHeader struct { 366 | Version int 367 | Flags int 368 | Id int 369 | DefaultSize int 370 | DefaultDuration int 371 | DefaultFlags int 372 | BaseDataOffset int64 373 | StsdId int 374 | } 375 | 376 | func WalkTrackFragHeader(w Walker, self *TrackFragHeader) { 377 | w.StartStruct("TrackFragHeader") 378 | w.Name("Flags") 379 | w.HexInt(self.Flags) 380 | w.Name("Id") 381 | w.Int(self.Id) 382 | w.Name("DefaultDuration") 383 | w.Int(self.DefaultDuration) 384 | w.Name("DefaultSize") 385 | w.Int(self.DefaultSize) 386 | w.Name("DefaultFlags") 387 | w.HexInt(self.DefaultFlags) 388 | w.EndStruct() 389 | } 390 | 391 | func WriteTrackFragHeader(w io.WriteSeeker, self *TrackFragHeader) (err error) { 392 | panic("unimplmented") 393 | return 394 | } 395 | 396 | func ReadTrackFragHeader(r *io.LimitedReader) (res *TrackFragHeader, err error) { 397 | self := &TrackFragHeader{} 398 | 399 | if self.Version, err = ReadInt(r, 1); err != nil { 400 | return 401 | } 402 | if self.Flags, err = ReadInt(r, 3); err != nil { 403 | return 404 | } 405 | if self.Id, err = ReadInt(r, 4); err != nil { 406 | return 407 | } 408 | 409 | if self.Flags&TFHD_BASE_DATA_OFFSET != 0 { 410 | if self.BaseDataOffset, err = bits.ReadInt64BE(r, 64); err != nil { 411 | return 412 | } 413 | } 414 | 415 | if self.Flags&TFHD_STSD_ID != 0 { 416 | if self.StsdId, err = ReadInt(r, 4); err != nil { 417 | return 418 | } 419 | } 420 | 421 | if self.Flags&TFHD_DEFAULT_DURATION != 0 { 422 | if self.DefaultDuration, err = ReadInt(r, 4); err != nil { 423 | return 424 | } 425 | } 426 | 427 | if self.Flags&TFHD_DEFAULT_SIZE != 0 { 428 | if self.DefaultSize, err = ReadInt(r, 4); err != nil { 429 | return 430 | } 431 | } 432 | 433 | if self.Flags&TFHD_DEFAULT_FLAGS != 0 { 434 | if self.DefaultFlags,err = ReadInt(r, 4); err != nil { 435 | return 436 | } 437 | } 438 | 439 | res = self 440 | return 441 | } 442 | 443 | const ( 444 | TRUN_DATA_OFFSET = 0x01 445 | TRUN_FIRST_SAMPLE_FLAGS = 0x04 446 | TRUN_SAMPLE_DURATION = 0x100 447 | TRUN_SAMPLE_SIZE = 0x200 448 | TRUN_SAMPLE_FLAGS = 0x400 449 | TRUN_SAMPLE_CTS = 0x800 450 | ) 451 | 452 | type TrackFragRunEntry struct { 453 | Duration int 454 | Size int 455 | Flags int 456 | Cts int 457 | } 458 | 459 | type TrackFragRun struct { 460 | Version int 461 | Flags int 462 | FirstSampleFlags int 463 | DataOffset int 464 | Entries []TrackFragRunEntry 465 | } 466 | 467 | func WalkTrackFragRun(w Walker, self *TrackFragRun) { 468 | w.StartStruct("TrackFragRun") 469 | w.Name("Flags") 470 | w.HexInt(self.Flags) 471 | w.Name("FirstSampleFlags") 472 | w.HexInt(self.FirstSampleFlags) 473 | w.Name("DataOffset") 474 | w.Int(self.DataOffset) 475 | w.Name("EntriesCount") 476 | w.Int(len(self.Entries)) 477 | for i := 0; i < 10 && i < len(self.Entries); i++ { 478 | entry := self.Entries[i] 479 | w.Println(fmt.Sprintf("Entry[%d] Flags=%x Duration=%d Size=%d Cts=%d", 480 | i, entry.Flags, entry.Duration, entry.Size, entry.Cts)) 481 | } 482 | w.EndStruct() 483 | } 484 | 485 | func WriteTrackFragRun(w io.WriteSeeker, self *TrackFragRun) (err error) { 486 | panic("unimplmented") 487 | return 488 | } 489 | 490 | func ReadTrackFragRun(r *io.LimitedReader) (res *TrackFragRun, err error) { 491 | self := &TrackFragRun{} 492 | 493 | if self.Version, err = ReadInt(r, 1); err != nil { 494 | return 495 | } 496 | if self.Flags, err = ReadInt(r, 3); err != nil { 497 | return 498 | } 499 | 500 | var count int 501 | if count, err = ReadInt(r, 4); err != nil { 502 | return 503 | } 504 | 505 | if self.Flags&TRUN_DATA_OFFSET != 0 { 506 | if self.DataOffset, err = ReadInt(r, 4); err != nil { 507 | return 508 | } 509 | } 510 | if self.Flags&TRUN_FIRST_SAMPLE_FLAGS != 0 { 511 | if self.FirstSampleFlags, err = ReadInt(r, 4); err != nil { 512 | return 513 | } 514 | } 515 | 516 | for i := 0; i < count; i++ { 517 | var flags int 518 | 519 | if i > 0 { 520 | flags = self.Flags 521 | } else { 522 | flags = self.FirstSampleFlags 523 | } 524 | 525 | entry := TrackFragRunEntry{} 526 | if flags&TRUN_SAMPLE_DURATION != 0 { 527 | if entry.Duration, err = ReadInt(r, 4); err != nil { 528 | return 529 | } 530 | } 531 | if flags&TRUN_SAMPLE_SIZE != 0 { 532 | if entry.Size, err = ReadInt(r, 4); err != nil { 533 | return 534 | } 535 | } 536 | if flags&TRUN_SAMPLE_FLAGS != 0 { 537 | if entry.Flags, err = ReadInt(r, 4); err != nil { 538 | return 539 | } 540 | } 541 | if flags&TRUN_SAMPLE_CTS != 0 { 542 | if entry.Cts, err = ReadInt(r, 4); err != nil { 543 | return 544 | } 545 | } 546 | 547 | self.Entries = append(self.Entries, entry) 548 | } 549 | 550 | res = self 551 | return 552 | } 553 | 554 | type TrackFragDecodeTime struct { 555 | Version int 556 | Flags int 557 | Time int64 558 | } 559 | 560 | func ReadTrackFragDecodeTime(r *io.LimitedReader) (res *TrackFragDecodeTime, err error) { 561 | 562 | self := &TrackFragDecodeTime{} 563 | if self.Version, err = ReadInt(r, 1); err != nil { 564 | return 565 | } 566 | if self.Flags, err = ReadInt(r, 3); err != nil { 567 | return 568 | } 569 | if self.Version != 0 { 570 | if self.Time, err = bits.ReadInt64BE(r, 64); err != nil { 571 | return 572 | } 573 | } else { 574 | if self.Time, err = bits.ReadInt64BE(r, 32); err != nil { 575 | return 576 | } 577 | } 578 | res = self 579 | return 580 | } 581 | 582 | func WriteTrackFragDecodeTime(w io.WriteSeeker, self *TrackFragDecodeTime) (err error) { 583 | var aw *Writer 584 | if aw, err = WriteAtomHeader(w, "tfdt"); err != nil { 585 | return 586 | } 587 | w = aw 588 | if err = WriteInt(w, self.Version, 1); err != nil { 589 | return 590 | } 591 | if err = WriteInt(w, self.Flags, 3); err != nil { 592 | return 593 | } 594 | if self.Version != 0 { 595 | if err = bits.WriteInt64BE(w, self.Time, 64); err != nil { 596 | return 597 | } 598 | } else { 599 | if err = bits.WriteInt64BE(w, self.Time, 32); err != nil { 600 | return 601 | } 602 | } 603 | if err = aw.Close(); err != nil { 604 | return 605 | } 606 | return 607 | } 608 | 609 | func WalkTrackFragDecodeTime(w Walker, self *TrackFragDecodeTime) { 610 | w.StartStruct("TrackFragDecodeTime") 611 | w.Name("Version") 612 | w.Int(self.Version) 613 | w.Name("Flags") 614 | w.Int(self.Flags) 615 | w.Name("Time") 616 | w.Int64(self.Time) 617 | w.EndStruct() 618 | return 619 | } 620 | 621 | -------------------------------------------------------------------------------- /atom/reader.go: -------------------------------------------------------------------------------- 1 | 2 | package atom 3 | 4 | import ( 5 | "io" 6 | "io/ioutil" 7 | "log" 8 | ) 9 | 10 | func ReadBytes(r io.Reader, n int) (res []byte, err error) { 11 | res = make([]byte, n) 12 | if n, err = r.Read(res); err != nil { 13 | return 14 | } 15 | return 16 | } 17 | 18 | func ReadUInt(r io.Reader, n int) (res uint, err error) { 19 | var b []byte 20 | if b, err = ReadBytes(r, n); err != nil { 21 | return 22 | } 23 | for i := 0; i < n; i++ { 24 | res <<= 8 25 | res += uint(b[i]) 26 | } 27 | return 28 | } 29 | 30 | func ReadInt(r io.Reader, n int) (res int, err error) { 31 | var uval uint 32 | if uval, err = ReadUInt(r, n); err != nil { 33 | return 34 | } 35 | if uval&(1< 0 { 18 | var cc4 string 19 | var ar *io.LimitedReader 20 | if ar, cc4, err = ReadAtomHeader(r, ""); err != nil { 21 | return 22 | } 23 | switch cc4 { 24 | case "mvhd": 25 | { 26 | if self.Header, err = ReadMovieHeader(ar); err != nil { 27 | return 28 | } 29 | } 30 | case "iods": 31 | { 32 | if self.Iods, err = ReadIods(ar); err != nil { 33 | return 34 | } 35 | } 36 | case "trak": 37 | { 38 | var item *Track 39 | if item, err = ReadTrack(ar); err != nil { 40 | return 41 | } 42 | self.Tracks = append(self.Tracks, item) 43 | } 44 | 45 | } 46 | if _, err = ReadDummy(ar, int(ar.N)); err != nil { 47 | return 48 | } 49 | } 50 | res = self 51 | return 52 | } 53 | func WriteMovie(w io.WriteSeeker, self *Movie) (err error) { 54 | 55 | var aw *Writer 56 | if aw, err = WriteAtomHeader(w, "moov"); err != nil { 57 | return 58 | } 59 | w = aw 60 | if self.Header != nil { 61 | if err = WriteMovieHeader(w, self.Header); err != nil { 62 | return 63 | } 64 | } 65 | if self.Iods != nil { 66 | if err = WriteIods(w, self.Iods); err != nil { 67 | return 68 | } 69 | } 70 | if self.Tracks != nil { 71 | for _, elem := range self.Tracks { 72 | if err = WriteTrack(w, elem); err != nil { 73 | return 74 | } 75 | } 76 | } 77 | if err = aw.Close(); err != nil { 78 | return 79 | } 80 | return 81 | } 82 | func WalkMovie(w Walker, self *Movie) { 83 | 84 | w.StartStruct("Movie") 85 | if self.Header != nil { 86 | WalkMovieHeader(w, self.Header) 87 | } 88 | if self.Iods != nil { 89 | WalkIods(w, self.Iods) 90 | } 91 | for i, item := range self.Tracks { 92 | if w.FilterArrayItem("Movie", "Tracks", i, len(self.Tracks)) { 93 | if item != nil { 94 | WalkTrack(w, item) 95 | } 96 | } else { 97 | w.ArrayLeft(i, len(self.Tracks)) 98 | break 99 | } 100 | } 101 | w.EndStruct() 102 | return 103 | } 104 | 105 | type Iods struct { 106 | Data []byte 107 | } 108 | 109 | func ReadIods(r *io.LimitedReader) (res *Iods, err error) { 110 | 111 | self := &Iods{} 112 | if self.Data, err = ReadBytes(r, int(r.N)); err != nil { 113 | return 114 | } 115 | res = self 116 | return 117 | } 118 | func WriteIods(w io.WriteSeeker, self *Iods) (err error) { 119 | 120 | var aw *Writer 121 | if aw, err = WriteAtomHeader(w, "iods"); err != nil { 122 | return 123 | } 124 | w = aw 125 | if err = WriteBytes(w, self.Data, len(self.Data)); err != nil { 126 | return 127 | } 128 | if err = aw.Close(); err != nil { 129 | return 130 | } 131 | return 132 | } 133 | func WalkIods(w Walker, self *Iods) { 134 | 135 | w.StartStruct("Iods") 136 | w.Name("Data") 137 | w.Bytes(self.Data) 138 | w.EndStruct() 139 | return 140 | } 141 | 142 | type MovieHeader struct { 143 | Version int 144 | Flags int 145 | CreateTime TimeStamp 146 | ModifyTime TimeStamp 147 | TimeScale int 148 | Duration int 149 | PreferredRate Fixed 150 | PreferredVolume Fixed 151 | Matrix [9]int 152 | PreviewTime TimeStamp 153 | PreviewDuration TimeStamp 154 | PosterTime TimeStamp 155 | SelectionTime TimeStamp 156 | SelectionDuration TimeStamp 157 | CurrentTime TimeStamp 158 | NextTrackId int 159 | } 160 | 161 | func ReadMovieHeader(r *io.LimitedReader) (res *MovieHeader, err error) { 162 | 163 | self := &MovieHeader{} 164 | if self.Version, err = ReadInt(r, 1); err != nil { 165 | return 166 | } 167 | if self.Flags, err = ReadInt(r, 3); err != nil { 168 | return 169 | } 170 | if self.CreateTime, err = ReadTimeStamp(r, 4); err != nil { 171 | return 172 | } 173 | if self.ModifyTime, err = ReadTimeStamp(r, 4); err != nil { 174 | return 175 | } 176 | if self.TimeScale, err = ReadInt(r, 4); err != nil { 177 | return 178 | } 179 | if self.Duration, err = ReadInt(r, 4); err != nil { 180 | return 181 | } 182 | if self.PreferredRate, err = ReadFixed(r, 4); err != nil { 183 | return 184 | } 185 | if self.PreferredVolume, err = ReadFixed(r, 2); err != nil { 186 | return 187 | } 188 | if _, err = ReadDummy(r, 10); err != nil { 189 | return 190 | } 191 | for i := 0; i < 9; i++ { 192 | if self.Matrix[i], err = ReadInt(r, 4); err != nil { 193 | return 194 | } 195 | } 196 | if self.PreviewTime, err = ReadTimeStamp(r, 4); err != nil { 197 | return 198 | } 199 | if self.PreviewDuration, err = ReadTimeStamp(r, 4); err != nil { 200 | return 201 | } 202 | if self.PosterTime, err = ReadTimeStamp(r, 4); err != nil { 203 | return 204 | } 205 | if self.SelectionTime, err = ReadTimeStamp(r, 4); err != nil { 206 | return 207 | } 208 | if self.SelectionDuration, err = ReadTimeStamp(r, 4); err != nil { 209 | return 210 | } 211 | if self.CurrentTime, err = ReadTimeStamp(r, 4); err != nil { 212 | return 213 | } 214 | if self.NextTrackId, err = ReadInt(r, 4); err != nil { 215 | return 216 | } 217 | res = self 218 | return 219 | } 220 | func WriteMovieHeader(w io.WriteSeeker, self *MovieHeader) (err error) { 221 | 222 | var aw *Writer 223 | if aw, err = WriteAtomHeader(w, "mvhd"); err != nil { 224 | return 225 | } 226 | w = aw 227 | if err = WriteInt(w, self.Version, 1); err != nil { 228 | return 229 | } 230 | if err = WriteInt(w, self.Flags, 3); err != nil { 231 | return 232 | } 233 | if err = WriteTimeStamp(w, self.CreateTime, 4); err != nil { 234 | return 235 | } 236 | if err = WriteTimeStamp(w, self.ModifyTime, 4); err != nil { 237 | return 238 | } 239 | if err = WriteInt(w, self.TimeScale, 4); err != nil { 240 | return 241 | } 242 | if err = WriteInt(w, self.Duration, 4); err != nil { 243 | return 244 | } 245 | if err = WriteFixed(w, self.PreferredRate, 4); err != nil { 246 | return 247 | } 248 | if err = WriteFixed(w, self.PreferredVolume, 2); err != nil { 249 | return 250 | } 251 | if err = WriteDummy(w, 10); err != nil { 252 | return 253 | } 254 | for _, elem := range self.Matrix { 255 | if err = WriteInt(w, elem, 4); err != nil { 256 | return 257 | } 258 | } 259 | if err = WriteTimeStamp(w, self.PreviewTime, 4); err != nil { 260 | return 261 | } 262 | if err = WriteTimeStamp(w, self.PreviewDuration, 4); err != nil { 263 | return 264 | } 265 | if err = WriteTimeStamp(w, self.PosterTime, 4); err != nil { 266 | return 267 | } 268 | if err = WriteTimeStamp(w, self.SelectionTime, 4); err != nil { 269 | return 270 | } 271 | if err = WriteTimeStamp(w, self.SelectionDuration, 4); err != nil { 272 | return 273 | } 274 | if err = WriteTimeStamp(w, self.CurrentTime, 4); err != nil { 275 | return 276 | } 277 | if err = WriteInt(w, self.NextTrackId, 4); err != nil { 278 | return 279 | } 280 | if err = aw.Close(); err != nil { 281 | return 282 | } 283 | return 284 | } 285 | func WalkMovieHeader(w Walker, self *MovieHeader) { 286 | 287 | w.StartStruct("MovieHeader") 288 | w.Name("Version") 289 | w.Int(self.Version) 290 | w.Name("Flags") 291 | w.Int(self.Flags) 292 | w.Name("CreateTime") 293 | w.TimeStamp(self.CreateTime) 294 | w.Name("ModifyTime") 295 | w.TimeStamp(self.ModifyTime) 296 | w.Name("TimeScale") 297 | w.Int(self.TimeScale) 298 | w.Name("Duration") 299 | w.Int(self.Duration) 300 | w.Name("PreferredRate") 301 | w.Fixed(self.PreferredRate) 302 | w.Name("PreferredVolume") 303 | w.Fixed(self.PreferredVolume) 304 | for i, item := range self.Matrix { 305 | if w.FilterArrayItem("MovieHeader", "Matrix", i, len(self.Matrix)) { 306 | w.Name("Matrix") 307 | w.Int(item) 308 | } else { 309 | w.ArrayLeft(i, len(self.Matrix)) 310 | break 311 | } 312 | } 313 | w.Name("PreviewTime") 314 | w.TimeStamp(self.PreviewTime) 315 | w.Name("PreviewDuration") 316 | w.TimeStamp(self.PreviewDuration) 317 | w.Name("PosterTime") 318 | w.TimeStamp(self.PosterTime) 319 | w.Name("SelectionTime") 320 | w.TimeStamp(self.SelectionTime) 321 | w.Name("SelectionDuration") 322 | w.TimeStamp(self.SelectionDuration) 323 | w.Name("CurrentTime") 324 | w.TimeStamp(self.CurrentTime) 325 | w.Name("NextTrackId") 326 | w.Int(self.NextTrackId) 327 | w.EndStruct() 328 | return 329 | } 330 | 331 | type Track struct { 332 | Header *TrackHeader 333 | Media *Media 334 | } 335 | 336 | func ReadTrack(r *io.LimitedReader) (res *Track, err error) { 337 | 338 | self := &Track{} 339 | for r.N > 0 { 340 | var cc4 string 341 | var ar *io.LimitedReader 342 | if ar, cc4, err = ReadAtomHeader(r, ""); err != nil { 343 | return 344 | } 345 | switch cc4 { 346 | case "tkhd": 347 | { 348 | if self.Header, err = ReadTrackHeader(ar); err != nil { 349 | return 350 | } 351 | } 352 | case "mdia": 353 | { 354 | if self.Media, err = ReadMedia(ar); err != nil { 355 | return 356 | } 357 | } 358 | 359 | } 360 | if _, err = ReadDummy(ar, int(ar.N)); err != nil { 361 | return 362 | } 363 | } 364 | res = self 365 | return 366 | } 367 | func WriteTrack(w io.WriteSeeker, self *Track) (err error) { 368 | 369 | var aw *Writer 370 | if aw, err = WriteAtomHeader(w, "trak"); err != nil { 371 | return 372 | } 373 | w = aw 374 | if self.Header != nil { 375 | if err = WriteTrackHeader(w, self.Header); err != nil { 376 | return 377 | } 378 | } 379 | if self.Media != nil { 380 | if err = WriteMedia(w, self.Media); err != nil { 381 | return 382 | } 383 | } 384 | if err = aw.Close(); err != nil { 385 | return 386 | } 387 | return 388 | } 389 | func WalkTrack(w Walker, self *Track) { 390 | 391 | w.StartStruct("Track") 392 | if self.Header != nil { 393 | WalkTrackHeader(w, self.Header) 394 | } 395 | if self.Media != nil { 396 | WalkMedia(w, self.Media) 397 | } 398 | w.EndStruct() 399 | return 400 | } 401 | 402 | type TrackHeader struct { 403 | Version int 404 | Flags int 405 | CreateTime TimeStamp 406 | ModifyTime TimeStamp 407 | TrackId int 408 | Duration int 409 | Layer int 410 | AlternateGroup int 411 | Volume Fixed 412 | Matrix [9]int 413 | TrackWidth Fixed 414 | TrackHeight Fixed 415 | } 416 | 417 | func ReadTrackHeader(r *io.LimitedReader) (res *TrackHeader, err error) { 418 | 419 | self := &TrackHeader{} 420 | if self.Version, err = ReadInt(r, 1); err != nil { 421 | return 422 | } 423 | if self.Flags, err = ReadInt(r, 3); err != nil { 424 | return 425 | } 426 | if self.CreateTime, err = ReadTimeStamp(r, 4); err != nil { 427 | return 428 | } 429 | if self.ModifyTime, err = ReadTimeStamp(r, 4); err != nil { 430 | return 431 | } 432 | if self.TrackId, err = ReadInt(r, 4); err != nil { 433 | return 434 | } 435 | if _, err = ReadDummy(r, 4); err != nil { 436 | return 437 | } 438 | if self.Duration, err = ReadInt(r, 4); err != nil { 439 | return 440 | } 441 | if _, err = ReadDummy(r, 8); err != nil { 442 | return 443 | } 444 | if self.Layer, err = ReadInt(r, 2); err != nil { 445 | return 446 | } 447 | if self.AlternateGroup, err = ReadInt(r, 2); err != nil { 448 | return 449 | } 450 | if self.Volume, err = ReadFixed(r, 2); err != nil { 451 | return 452 | } 453 | if _, err = ReadDummy(r, 2); err != nil { 454 | return 455 | } 456 | for i := 0; i < 9; i++ { 457 | if self.Matrix[i], err = ReadInt(r, 4); err != nil { 458 | return 459 | } 460 | } 461 | if self.TrackWidth, err = ReadFixed(r, 4); err != nil { 462 | return 463 | } 464 | if self.TrackHeight, err = ReadFixed(r, 4); err != nil { 465 | return 466 | } 467 | res = self 468 | return 469 | } 470 | func WriteTrackHeader(w io.WriteSeeker, self *TrackHeader) (err error) { 471 | 472 | var aw *Writer 473 | if aw, err = WriteAtomHeader(w, "tkhd"); err != nil { 474 | return 475 | } 476 | w = aw 477 | if err = WriteInt(w, self.Version, 1); err != nil { 478 | return 479 | } 480 | if err = WriteInt(w, self.Flags, 3); err != nil { 481 | return 482 | } 483 | if err = WriteTimeStamp(w, self.CreateTime, 4); err != nil { 484 | return 485 | } 486 | if err = WriteTimeStamp(w, self.ModifyTime, 4); err != nil { 487 | return 488 | } 489 | if err = WriteInt(w, self.TrackId, 4); err != nil { 490 | return 491 | } 492 | if err = WriteDummy(w, 4); err != nil { 493 | return 494 | } 495 | if err = WriteInt(w, self.Duration, 4); err != nil { 496 | return 497 | } 498 | if err = WriteDummy(w, 8); err != nil { 499 | return 500 | } 501 | if err = WriteInt(w, self.Layer, 2); err != nil { 502 | return 503 | } 504 | if err = WriteInt(w, self.AlternateGroup, 2); err != nil { 505 | return 506 | } 507 | if err = WriteFixed(w, self.Volume, 2); err != nil { 508 | return 509 | } 510 | if err = WriteDummy(w, 2); err != nil { 511 | return 512 | } 513 | for _, elem := range self.Matrix { 514 | if err = WriteInt(w, elem, 4); err != nil { 515 | return 516 | } 517 | } 518 | if err = WriteFixed(w, self.TrackWidth, 4); err != nil { 519 | return 520 | } 521 | if err = WriteFixed(w, self.TrackHeight, 4); err != nil { 522 | return 523 | } 524 | if err = aw.Close(); err != nil { 525 | return 526 | } 527 | return 528 | } 529 | func WalkTrackHeader(w Walker, self *TrackHeader) { 530 | 531 | w.StartStruct("TrackHeader") 532 | w.Name("Version") 533 | w.Int(self.Version) 534 | w.Name("Flags") 535 | w.Int(self.Flags) 536 | w.Name("CreateTime") 537 | w.TimeStamp(self.CreateTime) 538 | w.Name("ModifyTime") 539 | w.TimeStamp(self.ModifyTime) 540 | w.Name("TrackId") 541 | w.Int(self.TrackId) 542 | w.Name("Duration") 543 | w.Int(self.Duration) 544 | w.Name("Layer") 545 | w.Int(self.Layer) 546 | w.Name("AlternateGroup") 547 | w.Int(self.AlternateGroup) 548 | w.Name("Volume") 549 | w.Fixed(self.Volume) 550 | for i, item := range self.Matrix { 551 | if w.FilterArrayItem("TrackHeader", "Matrix", i, len(self.Matrix)) { 552 | w.Name("Matrix") 553 | w.Int(item) 554 | } else { 555 | w.ArrayLeft(i, len(self.Matrix)) 556 | break 557 | } 558 | } 559 | w.Name("TrackWidth") 560 | w.Fixed(self.TrackWidth) 561 | w.Name("TrackHeight") 562 | w.Fixed(self.TrackHeight) 563 | w.EndStruct() 564 | return 565 | } 566 | 567 | type HandlerRefer struct { 568 | Version int 569 | Flags int 570 | Type string 571 | SubType string 572 | Name string 573 | } 574 | 575 | func ReadHandlerRefer(r *io.LimitedReader) (res *HandlerRefer, err error) { 576 | 577 | self := &HandlerRefer{} 578 | if self.Version, err = ReadInt(r, 1); err != nil { 579 | return 580 | } 581 | if self.Flags, err = ReadInt(r, 3); err != nil { 582 | return 583 | } 584 | if self.Type, err = ReadString(r, 4); err != nil { 585 | return 586 | } 587 | if self.SubType, err = ReadString(r, 4); err != nil { 588 | return 589 | } 590 | if self.Name, err = ReadString(r, int(r.N)); err != nil { 591 | return 592 | } 593 | res = self 594 | return 595 | } 596 | func WriteHandlerRefer(w io.WriteSeeker, self *HandlerRefer) (err error) { 597 | 598 | var aw *Writer 599 | if aw, err = WriteAtomHeader(w, "hdlr"); err != nil { 600 | return 601 | } 602 | w = aw 603 | if err = WriteInt(w, self.Version, 1); err != nil { 604 | return 605 | } 606 | if err = WriteInt(w, self.Flags, 3); err != nil { 607 | return 608 | } 609 | if err = WriteString(w, self.Type, 4); err != nil { 610 | return 611 | } 612 | if err = WriteString(w, self.SubType, 4); err != nil { 613 | return 614 | } 615 | if err = WriteString(w, self.Name, len(self.Name)); err != nil { 616 | return 617 | } 618 | if err = aw.Close(); err != nil { 619 | return 620 | } 621 | return 622 | } 623 | func WalkHandlerRefer(w Walker, self *HandlerRefer) { 624 | 625 | w.StartStruct("HandlerRefer") 626 | w.Name("Version") 627 | w.Int(self.Version) 628 | w.Name("Flags") 629 | w.Int(self.Flags) 630 | w.Name("Type") 631 | w.String(self.Type) 632 | w.Name("SubType") 633 | w.String(self.SubType) 634 | w.Name("Name") 635 | w.String(self.Name) 636 | w.EndStruct() 637 | return 638 | } 639 | 640 | type Media struct { 641 | Header *MediaHeader 642 | Handler *HandlerRefer 643 | Info *MediaInfo 644 | } 645 | 646 | func ReadMedia(r *io.LimitedReader) (res *Media, err error) { 647 | 648 | self := &Media{} 649 | for r.N > 0 { 650 | var cc4 string 651 | var ar *io.LimitedReader 652 | if ar, cc4, err = ReadAtomHeader(r, ""); err != nil { 653 | return 654 | } 655 | switch cc4 { 656 | case "mdhd": 657 | { 658 | if self.Header, err = ReadMediaHeader(ar); err != nil { 659 | return 660 | } 661 | } 662 | case "hdlr": 663 | { 664 | if self.Handler, err = ReadHandlerRefer(ar); err != nil { 665 | return 666 | } 667 | } 668 | case "minf": 669 | { 670 | if self.Info, err = ReadMediaInfo(ar); err != nil { 671 | return 672 | } 673 | } 674 | 675 | } 676 | if _, err = ReadDummy(ar, int(ar.N)); err != nil { 677 | return 678 | } 679 | } 680 | res = self 681 | return 682 | } 683 | func WriteMedia(w io.WriteSeeker, self *Media) (err error) { 684 | 685 | var aw *Writer 686 | if aw, err = WriteAtomHeader(w, "mdia"); err != nil { 687 | return 688 | } 689 | w = aw 690 | if self.Header != nil { 691 | if err = WriteMediaHeader(w, self.Header); err != nil { 692 | return 693 | } 694 | } 695 | if self.Handler != nil { 696 | if err = WriteHandlerRefer(w, self.Handler); err != nil { 697 | return 698 | } 699 | } 700 | if self.Info != nil { 701 | if err = WriteMediaInfo(w, self.Info); err != nil { 702 | return 703 | } 704 | } 705 | if err = aw.Close(); err != nil { 706 | return 707 | } 708 | return 709 | } 710 | func WalkMedia(w Walker, self *Media) { 711 | 712 | w.StartStruct("Media") 713 | if self.Header != nil { 714 | WalkMediaHeader(w, self.Header) 715 | } 716 | if self.Handler != nil { 717 | WalkHandlerRefer(w, self.Handler) 718 | } 719 | if self.Info != nil { 720 | WalkMediaInfo(w, self.Info) 721 | } 722 | w.EndStruct() 723 | return 724 | } 725 | 726 | type MediaHeader struct { 727 | Version int 728 | Flags int 729 | CreateTime TimeStamp 730 | ModifyTime TimeStamp 731 | TimeScale int 732 | Duration int 733 | Language int 734 | Quality int 735 | } 736 | 737 | func ReadMediaHeader(r *io.LimitedReader) (res *MediaHeader, err error) { 738 | 739 | self := &MediaHeader{} 740 | if self.Version, err = ReadInt(r, 1); err != nil { 741 | return 742 | } 743 | if self.Flags, err = ReadInt(r, 3); err != nil { 744 | return 745 | } 746 | if self.CreateTime, err = ReadTimeStamp(r, 4); err != nil { 747 | return 748 | } 749 | if self.ModifyTime, err = ReadTimeStamp(r, 4); err != nil { 750 | return 751 | } 752 | if self.TimeScale, err = ReadInt(r, 4); err != nil { 753 | return 754 | } 755 | if self.Duration, err = ReadInt(r, 4); err != nil { 756 | return 757 | } 758 | if self.Language, err = ReadInt(r, 2); err != nil { 759 | return 760 | } 761 | if self.Quality, err = ReadInt(r, 2); err != nil { 762 | return 763 | } 764 | res = self 765 | return 766 | } 767 | func WriteMediaHeader(w io.WriteSeeker, self *MediaHeader) (err error) { 768 | 769 | var aw *Writer 770 | if aw, err = WriteAtomHeader(w, "mdhd"); err != nil { 771 | return 772 | } 773 | w = aw 774 | if err = WriteInt(w, self.Version, 1); err != nil { 775 | return 776 | } 777 | if err = WriteInt(w, self.Flags, 3); err != nil { 778 | return 779 | } 780 | if err = WriteTimeStamp(w, self.CreateTime, 4); err != nil { 781 | return 782 | } 783 | if err = WriteTimeStamp(w, self.ModifyTime, 4); err != nil { 784 | return 785 | } 786 | if err = WriteInt(w, self.TimeScale, 4); err != nil { 787 | return 788 | } 789 | if err = WriteInt(w, self.Duration, 4); err != nil { 790 | return 791 | } 792 | if err = WriteInt(w, self.Language, 2); err != nil { 793 | return 794 | } 795 | if err = WriteInt(w, self.Quality, 2); err != nil { 796 | return 797 | } 798 | if err = aw.Close(); err != nil { 799 | return 800 | } 801 | return 802 | } 803 | func WalkMediaHeader(w Walker, self *MediaHeader) { 804 | 805 | w.StartStruct("MediaHeader") 806 | w.Name("Version") 807 | w.Int(self.Version) 808 | w.Name("Flags") 809 | w.Int(self.Flags) 810 | w.Name("CreateTime") 811 | w.TimeStamp(self.CreateTime) 812 | w.Name("ModifyTime") 813 | w.TimeStamp(self.ModifyTime) 814 | w.Name("TimeScale") 815 | w.Int(self.TimeScale) 816 | w.Name("Duration") 817 | w.Int(self.Duration) 818 | w.Name("Language") 819 | w.Int(self.Language) 820 | w.Name("Quality") 821 | w.Int(self.Quality) 822 | w.EndStruct() 823 | return 824 | } 825 | 826 | type MediaInfo struct { 827 | Sound *SoundMediaInfo 828 | Video *VideoMediaInfo 829 | Data *DataInfo 830 | Sample *SampleTable 831 | } 832 | 833 | func ReadMediaInfo(r *io.LimitedReader) (res *MediaInfo, err error) { 834 | 835 | self := &MediaInfo{} 836 | for r.N > 0 { 837 | var cc4 string 838 | var ar *io.LimitedReader 839 | if ar, cc4, err = ReadAtomHeader(r, ""); err != nil { 840 | return 841 | } 842 | switch cc4 { 843 | case "smhd": 844 | { 845 | if self.Sound, err = ReadSoundMediaInfo(ar); err != nil { 846 | return 847 | } 848 | } 849 | case "vmhd": 850 | { 851 | if self.Video, err = ReadVideoMediaInfo(ar); err != nil { 852 | return 853 | } 854 | } 855 | case "dinf": 856 | { 857 | if self.Data, err = ReadDataInfo(ar); err != nil { 858 | return 859 | } 860 | } 861 | case "stbl": 862 | { 863 | if self.Sample, err = ReadSampleTable(ar); err != nil { 864 | return 865 | } 866 | } 867 | 868 | } 869 | if _, err = ReadDummy(ar, int(ar.N)); err != nil { 870 | return 871 | } 872 | } 873 | res = self 874 | return 875 | } 876 | func WriteMediaInfo(w io.WriteSeeker, self *MediaInfo) (err error) { 877 | 878 | var aw *Writer 879 | if aw, err = WriteAtomHeader(w, "minf"); err != nil { 880 | return 881 | } 882 | w = aw 883 | if self.Sound != nil { 884 | if err = WriteSoundMediaInfo(w, self.Sound); err != nil { 885 | return 886 | } 887 | } 888 | if self.Video != nil { 889 | if err = WriteVideoMediaInfo(w, self.Video); err != nil { 890 | return 891 | } 892 | } 893 | if self.Data != nil { 894 | if err = WriteDataInfo(w, self.Data); err != nil { 895 | return 896 | } 897 | } 898 | if self.Sample != nil { 899 | if err = WriteSampleTable(w, self.Sample); err != nil { 900 | return 901 | } 902 | } 903 | if err = aw.Close(); err != nil { 904 | return 905 | } 906 | return 907 | } 908 | func WalkMediaInfo(w Walker, self *MediaInfo) { 909 | 910 | w.StartStruct("MediaInfo") 911 | if self.Sound != nil { 912 | WalkSoundMediaInfo(w, self.Sound) 913 | } 914 | if self.Video != nil { 915 | WalkVideoMediaInfo(w, self.Video) 916 | } 917 | if self.Data != nil { 918 | WalkDataInfo(w, self.Data) 919 | } 920 | if self.Sample != nil { 921 | WalkSampleTable(w, self.Sample) 922 | } 923 | w.EndStruct() 924 | return 925 | } 926 | 927 | type DataInfo struct { 928 | Refer *DataRefer 929 | } 930 | 931 | func ReadDataInfo(r *io.LimitedReader) (res *DataInfo, err error) { 932 | 933 | self := &DataInfo{} 934 | for r.N > 0 { 935 | var cc4 string 936 | var ar *io.LimitedReader 937 | if ar, cc4, err = ReadAtomHeader(r, ""); err != nil { 938 | return 939 | } 940 | switch cc4 { 941 | case "dref": 942 | { 943 | if self.Refer, err = ReadDataRefer(ar); err != nil { 944 | return 945 | } 946 | } 947 | 948 | } 949 | if _, err = ReadDummy(ar, int(ar.N)); err != nil { 950 | return 951 | } 952 | } 953 | res = self 954 | return 955 | } 956 | func WriteDataInfo(w io.WriteSeeker, self *DataInfo) (err error) { 957 | 958 | var aw *Writer 959 | if aw, err = WriteAtomHeader(w, "dinf"); err != nil { 960 | return 961 | } 962 | w = aw 963 | if self.Refer != nil { 964 | if err = WriteDataRefer(w, self.Refer); err != nil { 965 | return 966 | } 967 | } 968 | if err = aw.Close(); err != nil { 969 | return 970 | } 971 | return 972 | } 973 | func WalkDataInfo(w Walker, self *DataInfo) { 974 | 975 | w.StartStruct("DataInfo") 976 | if self.Refer != nil { 977 | WalkDataRefer(w, self.Refer) 978 | } 979 | w.EndStruct() 980 | return 981 | } 982 | 983 | type DataRefer struct { 984 | Version int 985 | Flags int 986 | Url *DataReferUrl 987 | } 988 | 989 | func ReadDataRefer(r *io.LimitedReader) (res *DataRefer, err error) { 990 | 991 | self := &DataRefer{} 992 | if self.Version, err = ReadInt(r, 1); err != nil { 993 | return 994 | } 995 | if self.Flags, err = ReadInt(r, 3); err != nil { 996 | return 997 | } 998 | if _, err = ReadDummy(r, 4); err != nil { 999 | return 1000 | } 1001 | for r.N > 0 { 1002 | var cc4 string 1003 | var ar *io.LimitedReader 1004 | if ar, cc4, err = ReadAtomHeader(r, ""); err != nil { 1005 | return 1006 | } 1007 | switch cc4 { 1008 | case "url ": 1009 | { 1010 | if self.Url, err = ReadDataReferUrl(ar); err != nil { 1011 | return 1012 | } 1013 | } 1014 | 1015 | } 1016 | if _, err = ReadDummy(ar, int(ar.N)); err != nil { 1017 | return 1018 | } 1019 | } 1020 | res = self 1021 | return 1022 | } 1023 | func WriteDataRefer(w io.WriteSeeker, self *DataRefer) (err error) { 1024 | 1025 | var aw *Writer 1026 | if aw, err = WriteAtomHeader(w, "dref"); err != nil { 1027 | return 1028 | } 1029 | w = aw 1030 | if err = WriteInt(w, self.Version, 1); err != nil { 1031 | return 1032 | } 1033 | if err = WriteInt(w, self.Flags, 3); err != nil { 1034 | return 1035 | } 1036 | var atomsCount int 1037 | var atomsCountPos int64 1038 | if atomsCountPos, err = WriteEmptyInt(w, 4); err != nil { 1039 | return 1040 | } 1041 | if self.Url != nil { 1042 | if err = WriteDataReferUrl(w, self.Url); err != nil { 1043 | return 1044 | } 1045 | atomsCount++ 1046 | } 1047 | if err = RefillInt(w, atomsCountPos, atomsCount, 4); err != nil { 1048 | return 1049 | } 1050 | if err = aw.Close(); err != nil { 1051 | return 1052 | } 1053 | return 1054 | } 1055 | func WalkDataRefer(w Walker, self *DataRefer) { 1056 | 1057 | w.StartStruct("DataRefer") 1058 | w.Name("Version") 1059 | w.Int(self.Version) 1060 | w.Name("Flags") 1061 | w.Int(self.Flags) 1062 | if self.Url != nil { 1063 | WalkDataReferUrl(w, self.Url) 1064 | } 1065 | w.EndStruct() 1066 | return 1067 | } 1068 | 1069 | type DataReferUrl struct { 1070 | Version int 1071 | Flags int 1072 | } 1073 | 1074 | func ReadDataReferUrl(r *io.LimitedReader) (res *DataReferUrl, err error) { 1075 | 1076 | self := &DataReferUrl{} 1077 | if self.Version, err = ReadInt(r, 1); err != nil { 1078 | return 1079 | } 1080 | if self.Flags, err = ReadInt(r, 3); err != nil { 1081 | return 1082 | } 1083 | res = self 1084 | return 1085 | } 1086 | func WriteDataReferUrl(w io.WriteSeeker, self *DataReferUrl) (err error) { 1087 | 1088 | var aw *Writer 1089 | if aw, err = WriteAtomHeader(w, "url "); err != nil { 1090 | return 1091 | } 1092 | w = aw 1093 | if err = WriteInt(w, self.Version, 1); err != nil { 1094 | return 1095 | } 1096 | if err = WriteInt(w, self.Flags, 3); err != nil { 1097 | return 1098 | } 1099 | if err = aw.Close(); err != nil { 1100 | return 1101 | } 1102 | return 1103 | } 1104 | func WalkDataReferUrl(w Walker, self *DataReferUrl) { 1105 | 1106 | w.StartStruct("DataReferUrl") 1107 | w.Name("Version") 1108 | w.Int(self.Version) 1109 | w.Name("Flags") 1110 | w.Int(self.Flags) 1111 | w.EndStruct() 1112 | return 1113 | } 1114 | 1115 | type SoundMediaInfo struct { 1116 | Version int 1117 | Flags int 1118 | Balance int 1119 | } 1120 | 1121 | func ReadSoundMediaInfo(r *io.LimitedReader) (res *SoundMediaInfo, err error) { 1122 | 1123 | self := &SoundMediaInfo{} 1124 | if self.Version, err = ReadInt(r, 1); err != nil { 1125 | return 1126 | } 1127 | if self.Flags, err = ReadInt(r, 3); err != nil { 1128 | return 1129 | } 1130 | if self.Balance, err = ReadInt(r, 2); err != nil { 1131 | return 1132 | } 1133 | if _, err = ReadDummy(r, 2); err != nil { 1134 | return 1135 | } 1136 | res = self 1137 | return 1138 | } 1139 | func WriteSoundMediaInfo(w io.WriteSeeker, self *SoundMediaInfo) (err error) { 1140 | 1141 | var aw *Writer 1142 | if aw, err = WriteAtomHeader(w, "smhd"); err != nil { 1143 | return 1144 | } 1145 | w = aw 1146 | if err = WriteInt(w, self.Version, 1); err != nil { 1147 | return 1148 | } 1149 | if err = WriteInt(w, self.Flags, 3); err != nil { 1150 | return 1151 | } 1152 | if err = WriteInt(w, self.Balance, 2); err != nil { 1153 | return 1154 | } 1155 | if err = WriteDummy(w, 2); err != nil { 1156 | return 1157 | } 1158 | if err = aw.Close(); err != nil { 1159 | return 1160 | } 1161 | return 1162 | } 1163 | func WalkSoundMediaInfo(w Walker, self *SoundMediaInfo) { 1164 | 1165 | w.StartStruct("SoundMediaInfo") 1166 | w.Name("Version") 1167 | w.Int(self.Version) 1168 | w.Name("Flags") 1169 | w.Int(self.Flags) 1170 | w.Name("Balance") 1171 | w.Int(self.Balance) 1172 | w.EndStruct() 1173 | return 1174 | } 1175 | 1176 | type VideoMediaInfo struct { 1177 | Version int 1178 | Flags int 1179 | GraphicsMode int 1180 | Opcolor [3]int 1181 | } 1182 | 1183 | func ReadVideoMediaInfo(r *io.LimitedReader) (res *VideoMediaInfo, err error) { 1184 | 1185 | self := &VideoMediaInfo{} 1186 | if self.Version, err = ReadInt(r, 1); err != nil { 1187 | return 1188 | } 1189 | if self.Flags, err = ReadInt(r, 3); err != nil { 1190 | return 1191 | } 1192 | if self.GraphicsMode, err = ReadInt(r, 2); err != nil { 1193 | return 1194 | } 1195 | for i := 0; i < 3; i++ { 1196 | if self.Opcolor[i], err = ReadInt(r, 2); err != nil { 1197 | return 1198 | } 1199 | } 1200 | res = self 1201 | return 1202 | } 1203 | func WriteVideoMediaInfo(w io.WriteSeeker, self *VideoMediaInfo) (err error) { 1204 | 1205 | var aw *Writer 1206 | if aw, err = WriteAtomHeader(w, "vmhd"); err != nil { 1207 | return 1208 | } 1209 | w = aw 1210 | if err = WriteInt(w, self.Version, 1); err != nil { 1211 | return 1212 | } 1213 | if err = WriteInt(w, self.Flags, 3); err != nil { 1214 | return 1215 | } 1216 | if err = WriteInt(w, self.GraphicsMode, 2); err != nil { 1217 | return 1218 | } 1219 | for _, elem := range self.Opcolor { 1220 | if err = WriteInt(w, elem, 2); err != nil { 1221 | return 1222 | } 1223 | } 1224 | if err = aw.Close(); err != nil { 1225 | return 1226 | } 1227 | return 1228 | } 1229 | func WalkVideoMediaInfo(w Walker, self *VideoMediaInfo) { 1230 | 1231 | w.StartStruct("VideoMediaInfo") 1232 | w.Name("Version") 1233 | w.Int(self.Version) 1234 | w.Name("Flags") 1235 | w.Int(self.Flags) 1236 | w.Name("GraphicsMode") 1237 | w.Int(self.GraphicsMode) 1238 | for i, item := range self.Opcolor { 1239 | if w.FilterArrayItem("VideoMediaInfo", "Opcolor", i, len(self.Opcolor)) { 1240 | w.Name("Opcolor") 1241 | w.Int(item) 1242 | } else { 1243 | w.ArrayLeft(i, len(self.Opcolor)) 1244 | break 1245 | } 1246 | } 1247 | w.EndStruct() 1248 | return 1249 | } 1250 | 1251 | type SampleTable struct { 1252 | SampleDesc *SampleDesc 1253 | TimeToSample *TimeToSample 1254 | CompositionOffset *CompositionOffset 1255 | SampleToChunk *SampleToChunk 1256 | SyncSample *SyncSample 1257 | ChunkOffset *ChunkOffset 1258 | SampleSize *SampleSize 1259 | } 1260 | 1261 | func ReadSampleTable(r *io.LimitedReader) (res *SampleTable, err error) { 1262 | 1263 | self := &SampleTable{} 1264 | for r.N > 0 { 1265 | var cc4 string 1266 | var ar *io.LimitedReader 1267 | if ar, cc4, err = ReadAtomHeader(r, ""); err != nil { 1268 | return 1269 | } 1270 | switch cc4 { 1271 | case "stsd": 1272 | { 1273 | if self.SampleDesc, err = ReadSampleDesc(ar); err != nil { 1274 | return 1275 | } 1276 | } 1277 | case "stts": 1278 | { 1279 | if self.TimeToSample, err = ReadTimeToSample(ar); err != nil { 1280 | return 1281 | } 1282 | } 1283 | case "ctts": 1284 | { 1285 | if self.CompositionOffset, err = ReadCompositionOffset(ar); err != nil { 1286 | return 1287 | } 1288 | } 1289 | case "stsc": 1290 | { 1291 | if self.SampleToChunk, err = ReadSampleToChunk(ar); err != nil { 1292 | return 1293 | } 1294 | } 1295 | case "stss": 1296 | { 1297 | if self.SyncSample, err = ReadSyncSample(ar); err != nil { 1298 | return 1299 | } 1300 | } 1301 | case "stco": 1302 | { 1303 | if self.ChunkOffset, err = ReadChunkOffset(ar); err != nil { 1304 | return 1305 | } 1306 | } 1307 | case "stsz": 1308 | { 1309 | if self.SampleSize, err = ReadSampleSize(ar); err != nil { 1310 | return 1311 | } 1312 | } 1313 | 1314 | } 1315 | if _, err = ReadDummy(ar, int(ar.N)); err != nil { 1316 | return 1317 | } 1318 | } 1319 | res = self 1320 | return 1321 | } 1322 | func WriteSampleTable(w io.WriteSeeker, self *SampleTable) (err error) { 1323 | 1324 | var aw *Writer 1325 | if aw, err = WriteAtomHeader(w, "stbl"); err != nil { 1326 | return 1327 | } 1328 | w = aw 1329 | if self.SampleDesc != nil { 1330 | if err = WriteSampleDesc(w, self.SampleDesc); err != nil { 1331 | return 1332 | } 1333 | } 1334 | if self.TimeToSample != nil { 1335 | if err = WriteTimeToSample(w, self.TimeToSample); err != nil { 1336 | return 1337 | } 1338 | } 1339 | if self.CompositionOffset != nil { 1340 | if err = WriteCompositionOffset(w, self.CompositionOffset); err != nil { 1341 | return 1342 | } 1343 | } 1344 | if self.SampleToChunk != nil { 1345 | if err = WriteSampleToChunk(w, self.SampleToChunk); err != nil { 1346 | return 1347 | } 1348 | } 1349 | if self.SyncSample != nil { 1350 | if err = WriteSyncSample(w, self.SyncSample); err != nil { 1351 | return 1352 | } 1353 | } 1354 | if self.ChunkOffset != nil { 1355 | if err = WriteChunkOffset(w, self.ChunkOffset); err != nil { 1356 | return 1357 | } 1358 | } 1359 | if self.SampleSize != nil { 1360 | if err = WriteSampleSize(w, self.SampleSize); err != nil { 1361 | return 1362 | } 1363 | } 1364 | if err = aw.Close(); err != nil { 1365 | return 1366 | } 1367 | return 1368 | } 1369 | func WalkSampleTable(w Walker, self *SampleTable) { 1370 | 1371 | w.StartStruct("SampleTable") 1372 | if self.SampleDesc != nil { 1373 | WalkSampleDesc(w, self.SampleDesc) 1374 | } 1375 | if self.TimeToSample != nil { 1376 | WalkTimeToSample(w, self.TimeToSample) 1377 | } 1378 | if self.CompositionOffset != nil { 1379 | WalkCompositionOffset(w, self.CompositionOffset) 1380 | } 1381 | if self.SampleToChunk != nil { 1382 | WalkSampleToChunk(w, self.SampleToChunk) 1383 | } 1384 | if self.SyncSample != nil { 1385 | WalkSyncSample(w, self.SyncSample) 1386 | } 1387 | if self.ChunkOffset != nil { 1388 | WalkChunkOffset(w, self.ChunkOffset) 1389 | } 1390 | if self.SampleSize != nil { 1391 | WalkSampleSize(w, self.SampleSize) 1392 | } 1393 | w.EndStruct() 1394 | return 1395 | } 1396 | 1397 | type SampleDesc struct { 1398 | Version int 1399 | Avc1Desc *Avc1Desc 1400 | Mp4aDesc *Mp4aDesc 1401 | } 1402 | 1403 | func ReadSampleDesc(r *io.LimitedReader) (res *SampleDesc, err error) { 1404 | 1405 | self := &SampleDesc{} 1406 | if self.Version, err = ReadInt(r, 1); err != nil { 1407 | return 1408 | } 1409 | if _, err = ReadDummy(r, 3); err != nil { 1410 | return 1411 | } 1412 | if _, err = ReadDummy(r, 4); err != nil { 1413 | return 1414 | } 1415 | for r.N > 0 { 1416 | var cc4 string 1417 | var ar *io.LimitedReader 1418 | if ar, cc4, err = ReadAtomHeader(r, ""); err != nil { 1419 | return 1420 | } 1421 | switch cc4 { 1422 | case "avc1": 1423 | { 1424 | if self.Avc1Desc, err = ReadAvc1Desc(ar); err != nil { 1425 | return 1426 | } 1427 | } 1428 | case "mp4a": 1429 | { 1430 | if self.Mp4aDesc, err = ReadMp4aDesc(ar); err != nil { 1431 | return 1432 | } 1433 | } 1434 | 1435 | } 1436 | if _, err = ReadDummy(ar, int(ar.N)); err != nil { 1437 | return 1438 | } 1439 | } 1440 | res = self 1441 | return 1442 | } 1443 | func WriteSampleDesc(w io.WriteSeeker, self *SampleDesc) (err error) { 1444 | 1445 | var aw *Writer 1446 | if aw, err = WriteAtomHeader(w, "stsd"); err != nil { 1447 | return 1448 | } 1449 | w = aw 1450 | if err = WriteInt(w, self.Version, 1); err != nil { 1451 | return 1452 | } 1453 | if err = WriteDummy(w, 3); err != nil { 1454 | return 1455 | } 1456 | var atomsCount int 1457 | var atomsCountPos int64 1458 | if atomsCountPos, err = WriteEmptyInt(w, 4); err != nil { 1459 | return 1460 | } 1461 | if self.Avc1Desc != nil { 1462 | if err = WriteAvc1Desc(w, self.Avc1Desc); err != nil { 1463 | return 1464 | } 1465 | atomsCount++ 1466 | } 1467 | if self.Mp4aDesc != nil { 1468 | if err = WriteMp4aDesc(w, self.Mp4aDesc); err != nil { 1469 | return 1470 | } 1471 | atomsCount++ 1472 | } 1473 | if err = RefillInt(w, atomsCountPos, atomsCount, 4); err != nil { 1474 | return 1475 | } 1476 | if err = aw.Close(); err != nil { 1477 | return 1478 | } 1479 | return 1480 | } 1481 | func WalkSampleDesc(w Walker, self *SampleDesc) { 1482 | 1483 | w.StartStruct("SampleDesc") 1484 | w.Name("Version") 1485 | w.Int(self.Version) 1486 | if self.Avc1Desc != nil { 1487 | WalkAvc1Desc(w, self.Avc1Desc) 1488 | } 1489 | if self.Mp4aDesc != nil { 1490 | WalkMp4aDesc(w, self.Mp4aDesc) 1491 | } 1492 | w.EndStruct() 1493 | return 1494 | } 1495 | 1496 | type Mp4aDesc struct { 1497 | DataRefIdx int 1498 | Version int 1499 | RevisionLevel int 1500 | Vendor int 1501 | NumberOfChannels int 1502 | SampleSize int 1503 | CompressionId int 1504 | SampleRate Fixed 1505 | Conf *ElemStreamDesc 1506 | } 1507 | 1508 | func ReadMp4aDesc(r *io.LimitedReader) (res *Mp4aDesc, err error) { 1509 | 1510 | self := &Mp4aDesc{} 1511 | if _, err = ReadDummy(r, 6); err != nil { 1512 | return 1513 | } 1514 | if self.DataRefIdx, err = ReadInt(r, 2); err != nil { 1515 | return 1516 | } 1517 | if self.Version, err = ReadInt(r, 2); err != nil { 1518 | return 1519 | } 1520 | if self.RevisionLevel, err = ReadInt(r, 2); err != nil { 1521 | return 1522 | } 1523 | if self.Vendor, err = ReadInt(r, 4); err != nil { 1524 | return 1525 | } 1526 | if self.NumberOfChannels, err = ReadInt(r, 2); err != nil { 1527 | return 1528 | } 1529 | if self.SampleSize, err = ReadInt(r, 2); err != nil { 1530 | return 1531 | } 1532 | if self.CompressionId, err = ReadInt(r, 2); err != nil { 1533 | return 1534 | } 1535 | if _, err = ReadDummy(r, 2); err != nil { 1536 | return 1537 | } 1538 | if self.SampleRate, err = ReadFixed(r, 4); err != nil { 1539 | return 1540 | } 1541 | for r.N > 0 { 1542 | var cc4 string 1543 | var ar *io.LimitedReader 1544 | if ar, cc4, err = ReadAtomHeader(r, ""); err != nil { 1545 | return 1546 | } 1547 | switch cc4 { 1548 | case "esds": 1549 | { 1550 | if self.Conf, err = ReadElemStreamDesc(ar); err != nil { 1551 | return 1552 | } 1553 | } 1554 | 1555 | } 1556 | if _, err = ReadDummy(ar, int(ar.N)); err != nil { 1557 | return 1558 | } 1559 | } 1560 | res = self 1561 | return 1562 | } 1563 | func WriteMp4aDesc(w io.WriteSeeker, self *Mp4aDesc) (err error) { 1564 | 1565 | var aw *Writer 1566 | if aw, err = WriteAtomHeader(w, "mp4a"); err != nil { 1567 | return 1568 | } 1569 | w = aw 1570 | if err = WriteDummy(w, 6); err != nil { 1571 | return 1572 | } 1573 | if err = WriteInt(w, self.DataRefIdx, 2); err != nil { 1574 | return 1575 | } 1576 | if err = WriteInt(w, self.Version, 2); err != nil { 1577 | return 1578 | } 1579 | if err = WriteInt(w, self.RevisionLevel, 2); err != nil { 1580 | return 1581 | } 1582 | if err = WriteInt(w, self.Vendor, 4); err != nil { 1583 | return 1584 | } 1585 | if err = WriteInt(w, self.NumberOfChannels, 2); err != nil { 1586 | return 1587 | } 1588 | if err = WriteInt(w, self.SampleSize, 2); err != nil { 1589 | return 1590 | } 1591 | if err = WriteInt(w, self.CompressionId, 2); err != nil { 1592 | return 1593 | } 1594 | if err = WriteDummy(w, 2); err != nil { 1595 | return 1596 | } 1597 | if err = WriteFixed(w, self.SampleRate, 4); err != nil { 1598 | return 1599 | } 1600 | if self.Conf != nil { 1601 | if err = WriteElemStreamDesc(w, self.Conf); err != nil { 1602 | return 1603 | } 1604 | } 1605 | if err = aw.Close(); err != nil { 1606 | return 1607 | } 1608 | return 1609 | } 1610 | func WalkMp4aDesc(w Walker, self *Mp4aDesc) { 1611 | 1612 | w.StartStruct("Mp4aDesc") 1613 | w.Name("DataRefIdx") 1614 | w.Int(self.DataRefIdx) 1615 | w.Name("Version") 1616 | w.Int(self.Version) 1617 | w.Name("RevisionLevel") 1618 | w.Int(self.RevisionLevel) 1619 | w.Name("Vendor") 1620 | w.Int(self.Vendor) 1621 | w.Name("NumberOfChannels") 1622 | w.Int(self.NumberOfChannels) 1623 | w.Name("SampleSize") 1624 | w.Int(self.SampleSize) 1625 | w.Name("CompressionId") 1626 | w.Int(self.CompressionId) 1627 | w.Name("SampleRate") 1628 | w.Fixed(self.SampleRate) 1629 | if self.Conf != nil { 1630 | WalkElemStreamDesc(w, self.Conf) 1631 | } 1632 | w.EndStruct() 1633 | return 1634 | } 1635 | 1636 | type ElemStreamDesc struct { 1637 | Version int 1638 | Data []byte 1639 | } 1640 | 1641 | func ReadElemStreamDesc(r *io.LimitedReader) (res *ElemStreamDesc, err error) { 1642 | 1643 | self := &ElemStreamDesc{} 1644 | if self.Version, err = ReadInt(r, 4); err != nil { 1645 | return 1646 | } 1647 | if self.Data, err = ReadBytes(r, int(r.N)); err != nil { 1648 | return 1649 | } 1650 | res = self 1651 | return 1652 | } 1653 | func WriteElemStreamDesc(w io.WriteSeeker, self *ElemStreamDesc) (err error) { 1654 | 1655 | var aw *Writer 1656 | if aw, err = WriteAtomHeader(w, "esds"); err != nil { 1657 | return 1658 | } 1659 | w = aw 1660 | if err = WriteInt(w, self.Version, 4); err != nil { 1661 | return 1662 | } 1663 | if err = WriteBytes(w, self.Data, len(self.Data)); err != nil { 1664 | return 1665 | } 1666 | if err = aw.Close(); err != nil { 1667 | return 1668 | } 1669 | return 1670 | } 1671 | func WalkElemStreamDesc(w Walker, self *ElemStreamDesc) { 1672 | 1673 | w.StartStruct("ElemStreamDesc") 1674 | w.Name("Version") 1675 | w.Int(self.Version) 1676 | w.Name("Data") 1677 | w.Bytes(self.Data) 1678 | w.EndStruct() 1679 | return 1680 | } 1681 | 1682 | type Avc1Desc struct { 1683 | DataRefIdx int 1684 | Version int 1685 | Revision int 1686 | Vendor int 1687 | TemporalQuality int 1688 | SpatialQuality int 1689 | Width int 1690 | Height int 1691 | HorizontalResolution Fixed 1692 | VorizontalResolution Fixed 1693 | FrameCount int 1694 | CompressorName string 1695 | Depth int 1696 | ColorTableId int 1697 | Conf *Avc1Conf 1698 | } 1699 | 1700 | func ReadAvc1Desc(r *io.LimitedReader) (res *Avc1Desc, err error) { 1701 | 1702 | self := &Avc1Desc{} 1703 | if _, err = ReadDummy(r, 6); err != nil { 1704 | return 1705 | } 1706 | if self.DataRefIdx, err = ReadInt(r, 2); err != nil { 1707 | return 1708 | } 1709 | if self.Version, err = ReadInt(r, 2); err != nil { 1710 | return 1711 | } 1712 | if self.Revision, err = ReadInt(r, 2); err != nil { 1713 | return 1714 | } 1715 | if self.Vendor, err = ReadInt(r, 4); err != nil { 1716 | return 1717 | } 1718 | if self.TemporalQuality, err = ReadInt(r, 4); err != nil { 1719 | return 1720 | } 1721 | if self.SpatialQuality, err = ReadInt(r, 4); err != nil { 1722 | return 1723 | } 1724 | if self.Width, err = ReadInt(r, 2); err != nil { 1725 | return 1726 | } 1727 | if self.Height, err = ReadInt(r, 2); err != nil { 1728 | return 1729 | } 1730 | if self.HorizontalResolution, err = ReadFixed(r, 4); err != nil { 1731 | return 1732 | } 1733 | if self.VorizontalResolution, err = ReadFixed(r, 4); err != nil { 1734 | return 1735 | } 1736 | if _, err = ReadDummy(r, 4); err != nil { 1737 | return 1738 | } 1739 | if self.FrameCount, err = ReadInt(r, 2); err != nil { 1740 | return 1741 | } 1742 | if self.CompressorName, err = ReadString(r, 32); err != nil { 1743 | return 1744 | } 1745 | if self.Depth, err = ReadInt(r, 2); err != nil { 1746 | return 1747 | } 1748 | if self.ColorTableId, err = ReadInt(r, 2); err != nil { 1749 | return 1750 | } 1751 | for r.N > 0 { 1752 | var cc4 string 1753 | var ar *io.LimitedReader 1754 | if ar, cc4, err = ReadAtomHeader(r, ""); err != nil { 1755 | return 1756 | } 1757 | switch cc4 { 1758 | case "avcC": 1759 | { 1760 | if self.Conf, err = ReadAvc1Conf(ar); err != nil { 1761 | return 1762 | } 1763 | } 1764 | 1765 | } 1766 | if _, err = ReadDummy(ar, int(ar.N)); err != nil { 1767 | return 1768 | } 1769 | } 1770 | res = self 1771 | return 1772 | } 1773 | func WriteAvc1Desc(w io.WriteSeeker, self *Avc1Desc) (err error) { 1774 | 1775 | var aw *Writer 1776 | if aw, err = WriteAtomHeader(w, "avc1"); err != nil { 1777 | return 1778 | } 1779 | w = aw 1780 | if err = WriteDummy(w, 6); err != nil { 1781 | return 1782 | } 1783 | if err = WriteInt(w, self.DataRefIdx, 2); err != nil { 1784 | return 1785 | } 1786 | if err = WriteInt(w, self.Version, 2); err != nil { 1787 | return 1788 | } 1789 | if err = WriteInt(w, self.Revision, 2); err != nil { 1790 | return 1791 | } 1792 | if err = WriteInt(w, self.Vendor, 4); err != nil { 1793 | return 1794 | } 1795 | if err = WriteInt(w, self.TemporalQuality, 4); err != nil { 1796 | return 1797 | } 1798 | if err = WriteInt(w, self.SpatialQuality, 4); err != nil { 1799 | return 1800 | } 1801 | if err = WriteInt(w, self.Width, 2); err != nil { 1802 | return 1803 | } 1804 | if err = WriteInt(w, self.Height, 2); err != nil { 1805 | return 1806 | } 1807 | if err = WriteFixed(w, self.HorizontalResolution, 4); err != nil { 1808 | return 1809 | } 1810 | if err = WriteFixed(w, self.VorizontalResolution, 4); err != nil { 1811 | return 1812 | } 1813 | if err = WriteDummy(w, 4); err != nil { 1814 | return 1815 | } 1816 | if err = WriteInt(w, self.FrameCount, 2); err != nil { 1817 | return 1818 | } 1819 | if err = WriteString(w, self.CompressorName, 32); err != nil { 1820 | return 1821 | } 1822 | if err = WriteInt(w, self.Depth, 2); err != nil { 1823 | return 1824 | } 1825 | if err = WriteInt(w, self.ColorTableId, 2); err != nil { 1826 | return 1827 | } 1828 | if self.Conf != nil { 1829 | if err = WriteAvc1Conf(w, self.Conf); err != nil { 1830 | return 1831 | } 1832 | } 1833 | if err = aw.Close(); err != nil { 1834 | return 1835 | } 1836 | return 1837 | } 1838 | func WalkAvc1Desc(w Walker, self *Avc1Desc) { 1839 | 1840 | w.StartStruct("Avc1Desc") 1841 | w.Name("DataRefIdx") 1842 | w.Int(self.DataRefIdx) 1843 | w.Name("Version") 1844 | w.Int(self.Version) 1845 | w.Name("Revision") 1846 | w.Int(self.Revision) 1847 | w.Name("Vendor") 1848 | w.Int(self.Vendor) 1849 | w.Name("TemporalQuality") 1850 | w.Int(self.TemporalQuality) 1851 | w.Name("SpatialQuality") 1852 | w.Int(self.SpatialQuality) 1853 | w.Name("Width") 1854 | w.Int(self.Width) 1855 | w.Name("Height") 1856 | w.Int(self.Height) 1857 | w.Name("HorizontalResolution") 1858 | w.Fixed(self.HorizontalResolution) 1859 | w.Name("VorizontalResolution") 1860 | w.Fixed(self.VorizontalResolution) 1861 | w.Name("FrameCount") 1862 | w.Int(self.FrameCount) 1863 | w.Name("CompressorName") 1864 | w.String(self.CompressorName) 1865 | w.Name("Depth") 1866 | w.Int(self.Depth) 1867 | w.Name("ColorTableId") 1868 | w.Int(self.ColorTableId) 1869 | if self.Conf != nil { 1870 | WalkAvc1Conf(w, self.Conf) 1871 | } 1872 | w.EndStruct() 1873 | return 1874 | } 1875 | 1876 | type Avc1Conf struct { 1877 | Record AVCDecoderConfRecord 1878 | } 1879 | 1880 | func ReadAvc1Conf(r *io.LimitedReader) (res *Avc1Conf, err error) { 1881 | 1882 | self := &Avc1Conf{} 1883 | if self.Record, err = ReadAVCDecoderConfRecord(r); err != nil { 1884 | return 1885 | } 1886 | res = self 1887 | return 1888 | } 1889 | func WriteAvc1Conf(w io.WriteSeeker, self *Avc1Conf) (err error) { 1890 | 1891 | var aw *Writer 1892 | if aw, err = WriteAtomHeader(w, "avcC"); err != nil { 1893 | return 1894 | } 1895 | w = aw 1896 | if err = WriteAVCDecoderConfRecord(w, self.Record); err != nil { 1897 | return 1898 | } 1899 | if err = aw.Close(); err != nil { 1900 | return 1901 | } 1902 | return 1903 | } 1904 | func WalkAvc1Conf(w Walker, self *Avc1Conf) { 1905 | 1906 | w.StartStruct("Avc1Conf") 1907 | WalkAVCDecoderConfRecord(w, self.Record) 1908 | w.EndStruct() 1909 | return 1910 | } 1911 | 1912 | type TimeToSample struct { 1913 | Version int 1914 | Flags int 1915 | Entries []TimeToSampleEntry 1916 | } 1917 | 1918 | func ReadTimeToSample(r *io.LimitedReader) (res *TimeToSample, err error) { 1919 | 1920 | self := &TimeToSample{} 1921 | if self.Version, err = ReadInt(r, 1); err != nil { 1922 | return 1923 | } 1924 | if self.Flags, err = ReadInt(r, 3); err != nil { 1925 | return 1926 | } 1927 | var count int 1928 | if count, err = ReadInt(r, 4); err != nil { 1929 | return 1930 | } 1931 | self.Entries = make([]TimeToSampleEntry, count) 1932 | for i := 0; i < count; i++ { 1933 | if self.Entries[i], err = ReadTimeToSampleEntry(r); err != nil { 1934 | return 1935 | } 1936 | } 1937 | res = self 1938 | return 1939 | } 1940 | func WriteTimeToSample(w io.WriteSeeker, self *TimeToSample) (err error) { 1941 | 1942 | var aw *Writer 1943 | if aw, err = WriteAtomHeader(w, "stts"); err != nil { 1944 | return 1945 | } 1946 | w = aw 1947 | if err = WriteInt(w, self.Version, 1); err != nil { 1948 | return 1949 | } 1950 | if err = WriteInt(w, self.Flags, 3); err != nil { 1951 | return 1952 | } 1953 | if err = WriteInt(w, len(self.Entries), 4); err != nil { 1954 | return 1955 | } 1956 | for _, elem := range self.Entries { 1957 | if err = WriteTimeToSampleEntry(w, elem); err != nil { 1958 | return 1959 | } 1960 | } 1961 | if err = aw.Close(); err != nil { 1962 | return 1963 | } 1964 | return 1965 | } 1966 | func WalkTimeToSample(w Walker, self *TimeToSample) { 1967 | 1968 | w.StartStruct("TimeToSample") 1969 | w.Name("Version") 1970 | w.Int(self.Version) 1971 | w.Name("Flags") 1972 | w.Int(self.Flags) 1973 | for i, item := range self.Entries { 1974 | if w.FilterArrayItem("TimeToSample", "Entries", i, len(self.Entries)) { 1975 | WalkTimeToSampleEntry(w, item) 1976 | } else { 1977 | w.ArrayLeft(i, len(self.Entries)) 1978 | break 1979 | } 1980 | } 1981 | w.EndStruct() 1982 | return 1983 | } 1984 | 1985 | type TimeToSampleEntry struct { 1986 | Count int 1987 | Duration int 1988 | } 1989 | 1990 | func ReadTimeToSampleEntry(r *io.LimitedReader) (self TimeToSampleEntry, err error) { 1991 | 1992 | if self.Count, err = ReadInt(r, 4); err != nil { 1993 | return 1994 | } 1995 | if self.Duration, err = ReadInt(r, 4); err != nil { 1996 | return 1997 | } 1998 | return 1999 | } 2000 | func WriteTimeToSampleEntry(w io.WriteSeeker, self TimeToSampleEntry) (err error) { 2001 | 2002 | if err = WriteInt(w, self.Count, 4); err != nil { 2003 | return 2004 | } 2005 | if err = WriteInt(w, self.Duration, 4); err != nil { 2006 | return 2007 | } 2008 | return 2009 | } 2010 | func WalkTimeToSampleEntry(w Walker, self TimeToSampleEntry) { 2011 | 2012 | w.StartStruct("TimeToSampleEntry") 2013 | w.Name("Count") 2014 | w.Int(self.Count) 2015 | w.Name("Duration") 2016 | w.Int(self.Duration) 2017 | w.EndStruct() 2018 | return 2019 | } 2020 | 2021 | type SampleToChunk struct { 2022 | Version int 2023 | Flags int 2024 | Entries []SampleToChunkEntry 2025 | } 2026 | 2027 | func ReadSampleToChunk(r *io.LimitedReader) (res *SampleToChunk, err error) { 2028 | 2029 | self := &SampleToChunk{} 2030 | if self.Version, err = ReadInt(r, 1); err != nil { 2031 | return 2032 | } 2033 | if self.Flags, err = ReadInt(r, 3); err != nil { 2034 | return 2035 | } 2036 | var count int 2037 | if count, err = ReadInt(r, 4); err != nil { 2038 | return 2039 | } 2040 | self.Entries = make([]SampleToChunkEntry, count) 2041 | for i := 0; i < count; i++ { 2042 | if self.Entries[i], err = ReadSampleToChunkEntry(r); err != nil { 2043 | return 2044 | } 2045 | } 2046 | res = self 2047 | return 2048 | } 2049 | func WriteSampleToChunk(w io.WriteSeeker, self *SampleToChunk) (err error) { 2050 | 2051 | var aw *Writer 2052 | if aw, err = WriteAtomHeader(w, "stsc"); err != nil { 2053 | return 2054 | } 2055 | w = aw 2056 | if err = WriteInt(w, self.Version, 1); err != nil { 2057 | return 2058 | } 2059 | if err = WriteInt(w, self.Flags, 3); err != nil { 2060 | return 2061 | } 2062 | if err = WriteInt(w, len(self.Entries), 4); err != nil { 2063 | return 2064 | } 2065 | for _, elem := range self.Entries { 2066 | if err = WriteSampleToChunkEntry(w, elem); err != nil { 2067 | return 2068 | } 2069 | } 2070 | if err = aw.Close(); err != nil { 2071 | return 2072 | } 2073 | return 2074 | } 2075 | func WalkSampleToChunk(w Walker, self *SampleToChunk) { 2076 | 2077 | w.StartStruct("SampleToChunk") 2078 | w.Name("Version") 2079 | w.Int(self.Version) 2080 | w.Name("Flags") 2081 | w.Int(self.Flags) 2082 | for i, item := range self.Entries { 2083 | if w.FilterArrayItem("SampleToChunk", "Entries", i, len(self.Entries)) { 2084 | WalkSampleToChunkEntry(w, item) 2085 | } else { 2086 | w.ArrayLeft(i, len(self.Entries)) 2087 | break 2088 | } 2089 | } 2090 | w.EndStruct() 2091 | return 2092 | } 2093 | 2094 | type SampleToChunkEntry struct { 2095 | FirstChunk int 2096 | SamplesPerChunk int 2097 | SampleDescId int 2098 | } 2099 | 2100 | func ReadSampleToChunkEntry(r *io.LimitedReader) (self SampleToChunkEntry, err error) { 2101 | 2102 | if self.FirstChunk, err = ReadInt(r, 4); err != nil { 2103 | return 2104 | } 2105 | if self.SamplesPerChunk, err = ReadInt(r, 4); err != nil { 2106 | return 2107 | } 2108 | if self.SampleDescId, err = ReadInt(r, 4); err != nil { 2109 | return 2110 | } 2111 | return 2112 | } 2113 | func WriteSampleToChunkEntry(w io.WriteSeeker, self SampleToChunkEntry) (err error) { 2114 | 2115 | if err = WriteInt(w, self.FirstChunk, 4); err != nil { 2116 | return 2117 | } 2118 | if err = WriteInt(w, self.SamplesPerChunk, 4); err != nil { 2119 | return 2120 | } 2121 | if err = WriteInt(w, self.SampleDescId, 4); err != nil { 2122 | return 2123 | } 2124 | return 2125 | } 2126 | func WalkSampleToChunkEntry(w Walker, self SampleToChunkEntry) { 2127 | 2128 | w.StartStruct("SampleToChunkEntry") 2129 | w.Name("FirstChunk") 2130 | w.Int(self.FirstChunk) 2131 | w.Name("SamplesPerChunk") 2132 | w.Int(self.SamplesPerChunk) 2133 | w.Name("SampleDescId") 2134 | w.Int(self.SampleDescId) 2135 | w.EndStruct() 2136 | return 2137 | } 2138 | 2139 | type CompositionOffset struct { 2140 | Version int 2141 | Flags int 2142 | Entries []CompositionOffsetEntry 2143 | } 2144 | 2145 | func ReadCompositionOffset(r *io.LimitedReader) (res *CompositionOffset, err error) { 2146 | 2147 | self := &CompositionOffset{} 2148 | if self.Version, err = ReadInt(r, 1); err != nil { 2149 | return 2150 | } 2151 | if self.Flags, err = ReadInt(r, 3); err != nil { 2152 | return 2153 | } 2154 | var count int 2155 | if count, err = ReadInt(r, 4); err != nil { 2156 | return 2157 | } 2158 | self.Entries = make([]CompositionOffsetEntry, count) 2159 | for i := 0; i < count; i++ { 2160 | if self.Entries[i], err = ReadCompositionOffsetEntry(r); err != nil { 2161 | return 2162 | } 2163 | } 2164 | res = self 2165 | return 2166 | } 2167 | func WriteCompositionOffset(w io.WriteSeeker, self *CompositionOffset) (err error) { 2168 | 2169 | var aw *Writer 2170 | if aw, err = WriteAtomHeader(w, "ctts"); err != nil { 2171 | return 2172 | } 2173 | w = aw 2174 | if err = WriteInt(w, self.Version, 1); err != nil { 2175 | return 2176 | } 2177 | if err = WriteInt(w, self.Flags, 3); err != nil { 2178 | return 2179 | } 2180 | if err = WriteInt(w, len(self.Entries), 4); err != nil { 2181 | return 2182 | } 2183 | for _, elem := range self.Entries { 2184 | if err = WriteCompositionOffsetEntry(w, elem); err != nil { 2185 | return 2186 | } 2187 | } 2188 | if err = aw.Close(); err != nil { 2189 | return 2190 | } 2191 | return 2192 | } 2193 | func WalkCompositionOffset(w Walker, self *CompositionOffset) { 2194 | 2195 | w.StartStruct("CompositionOffset") 2196 | w.Name("Version") 2197 | w.Int(self.Version) 2198 | w.Name("Flags") 2199 | w.Int(self.Flags) 2200 | for i, item := range self.Entries { 2201 | if w.FilterArrayItem("CompositionOffset", "Entries", i, len(self.Entries)) { 2202 | WalkCompositionOffsetEntry(w, item) 2203 | } else { 2204 | w.ArrayLeft(i, len(self.Entries)) 2205 | break 2206 | } 2207 | } 2208 | w.EndStruct() 2209 | return 2210 | } 2211 | 2212 | type CompositionOffsetEntry struct { 2213 | Count int 2214 | Offset int 2215 | } 2216 | 2217 | func ReadCompositionOffsetEntry(r *io.LimitedReader) (self CompositionOffsetEntry, err error) { 2218 | 2219 | if self.Count, err = ReadInt(r, 4); err != nil { 2220 | return 2221 | } 2222 | if self.Offset, err = ReadInt(r, 4); err != nil { 2223 | return 2224 | } 2225 | return 2226 | } 2227 | func WriteCompositionOffsetEntry(w io.WriteSeeker, self CompositionOffsetEntry) (err error) { 2228 | 2229 | if err = WriteInt(w, self.Count, 4); err != nil { 2230 | return 2231 | } 2232 | if err = WriteInt(w, self.Offset, 4); err != nil { 2233 | return 2234 | } 2235 | return 2236 | } 2237 | func WalkCompositionOffsetEntry(w Walker, self CompositionOffsetEntry) { 2238 | 2239 | w.StartStruct("CompositionOffsetEntry") 2240 | w.Name("Count") 2241 | w.Int(self.Count) 2242 | w.Name("Offset") 2243 | w.Int(self.Offset) 2244 | w.EndStruct() 2245 | return 2246 | } 2247 | 2248 | type SyncSample struct { 2249 | Version int 2250 | Flags int 2251 | Entries []int 2252 | } 2253 | 2254 | func ReadSyncSample(r *io.LimitedReader) (res *SyncSample, err error) { 2255 | 2256 | self := &SyncSample{} 2257 | if self.Version, err = ReadInt(r, 1); err != nil { 2258 | return 2259 | } 2260 | if self.Flags, err = ReadInt(r, 3); err != nil { 2261 | return 2262 | } 2263 | var count int 2264 | if count, err = ReadInt(r, 4); err != nil { 2265 | return 2266 | } 2267 | self.Entries = make([]int, count) 2268 | for i := 0; i < count; i++ { 2269 | if self.Entries[i], err = ReadInt(r, 4); err != nil { 2270 | return 2271 | } 2272 | } 2273 | res = self 2274 | return 2275 | } 2276 | func WriteSyncSample(w io.WriteSeeker, self *SyncSample) (err error) { 2277 | 2278 | var aw *Writer 2279 | if aw, err = WriteAtomHeader(w, "stss"); err != nil { 2280 | return 2281 | } 2282 | w = aw 2283 | if err = WriteInt(w, self.Version, 1); err != nil { 2284 | return 2285 | } 2286 | if err = WriteInt(w, self.Flags, 3); err != nil { 2287 | return 2288 | } 2289 | if err = WriteInt(w, len(self.Entries), 4); err != nil { 2290 | return 2291 | } 2292 | for _, elem := range self.Entries { 2293 | if err = WriteInt(w, elem, 4); err != nil { 2294 | return 2295 | } 2296 | } 2297 | if err = aw.Close(); err != nil { 2298 | return 2299 | } 2300 | return 2301 | } 2302 | func WalkSyncSample(w Walker, self *SyncSample) { 2303 | 2304 | w.StartStruct("SyncSample") 2305 | w.Name("Version") 2306 | w.Int(self.Version) 2307 | w.Name("Flags") 2308 | w.Int(self.Flags) 2309 | for i, item := range self.Entries { 2310 | if w.FilterArrayItem("SyncSample", "Entries", i, len(self.Entries)) { 2311 | w.Name("Entries") 2312 | w.Int(item) 2313 | } else { 2314 | w.ArrayLeft(i, len(self.Entries)) 2315 | break 2316 | } 2317 | } 2318 | w.EndStruct() 2319 | return 2320 | } 2321 | 2322 | type SampleSize struct { 2323 | Version int 2324 | Flags int 2325 | SampleSize int 2326 | Entries []int 2327 | } 2328 | 2329 | func ReadSampleSize(r *io.LimitedReader) (res *SampleSize, err error) { 2330 | 2331 | self := &SampleSize{} 2332 | if self.Version, err = ReadInt(r, 1); err != nil { 2333 | return 2334 | } 2335 | if self.Flags, err = ReadInt(r, 3); err != nil { 2336 | return 2337 | } 2338 | if self.SampleSize, err = ReadInt(r, 4); err != nil { 2339 | return 2340 | } 2341 | var count int 2342 | if count, err = ReadInt(r, 4); err != nil { 2343 | return 2344 | } 2345 | self.Entries = make([]int, count) 2346 | for i := 0; i < count; i++ { 2347 | if self.Entries[i], err = ReadInt(r, 4); err != nil { 2348 | return 2349 | } 2350 | } 2351 | res = self 2352 | return 2353 | } 2354 | func WriteSampleSize(w io.WriteSeeker, self *SampleSize) (err error) { 2355 | 2356 | var aw *Writer 2357 | if aw, err = WriteAtomHeader(w, "stsz"); err != nil { 2358 | return 2359 | } 2360 | w = aw 2361 | if err = WriteInt(w, self.Version, 1); err != nil { 2362 | return 2363 | } 2364 | if err = WriteInt(w, self.Flags, 3); err != nil { 2365 | return 2366 | } 2367 | if err = WriteInt(w, self.SampleSize, 4); err != nil { 2368 | return 2369 | } 2370 | if err = WriteInt(w, len(self.Entries), 4); err != nil { 2371 | return 2372 | } 2373 | for _, elem := range self.Entries { 2374 | if err = WriteInt(w, elem, 4); err != nil { 2375 | return 2376 | } 2377 | } 2378 | if err = aw.Close(); err != nil { 2379 | return 2380 | } 2381 | return 2382 | } 2383 | func WalkSampleSize(w Walker, self *SampleSize) { 2384 | 2385 | w.StartStruct("SampleSize") 2386 | w.Name("Version") 2387 | w.Int(self.Version) 2388 | w.Name("Flags") 2389 | w.Int(self.Flags) 2390 | w.Name("SampleSize") 2391 | w.Int(self.SampleSize) 2392 | for i, item := range self.Entries { 2393 | if w.FilterArrayItem("SampleSize", "Entries", i, len(self.Entries)) { 2394 | w.Name("Entries") 2395 | w.Int(item) 2396 | } else { 2397 | w.ArrayLeft(i, len(self.Entries)) 2398 | break 2399 | } 2400 | } 2401 | w.EndStruct() 2402 | return 2403 | } 2404 | 2405 | type ChunkOffset struct { 2406 | Version int 2407 | Flags int 2408 | Entries []int 2409 | } 2410 | 2411 | func ReadChunkOffset(r *io.LimitedReader) (res *ChunkOffset, err error) { 2412 | 2413 | self := &ChunkOffset{} 2414 | if self.Version, err = ReadInt(r, 1); err != nil { 2415 | return 2416 | } 2417 | if self.Flags, err = ReadInt(r, 3); err != nil { 2418 | return 2419 | } 2420 | var count int 2421 | if count, err = ReadInt(r, 4); err != nil { 2422 | return 2423 | } 2424 | self.Entries = make([]int, count) 2425 | for i := 0; i < count; i++ { 2426 | if self.Entries[i], err = ReadInt(r, 4); err != nil { 2427 | return 2428 | } 2429 | } 2430 | res = self 2431 | return 2432 | } 2433 | func WriteChunkOffset(w io.WriteSeeker, self *ChunkOffset) (err error) { 2434 | 2435 | var aw *Writer 2436 | if aw, err = WriteAtomHeader(w, "stco"); err != nil { 2437 | return 2438 | } 2439 | w = aw 2440 | if err = WriteInt(w, self.Version, 1); err != nil { 2441 | return 2442 | } 2443 | if err = WriteInt(w, self.Flags, 3); err != nil { 2444 | return 2445 | } 2446 | if err = WriteInt(w, len(self.Entries), 4); err != nil { 2447 | return 2448 | } 2449 | for _, elem := range self.Entries { 2450 | if err = WriteInt(w, elem, 4); err != nil { 2451 | return 2452 | } 2453 | } 2454 | if err = aw.Close(); err != nil { 2455 | return 2456 | } 2457 | return 2458 | } 2459 | func WalkChunkOffset(w Walker, self *ChunkOffset) { 2460 | 2461 | w.StartStruct("ChunkOffset") 2462 | w.Name("Version") 2463 | w.Int(self.Version) 2464 | w.Name("Flags") 2465 | w.Int(self.Flags) 2466 | for i, item := range self.Entries { 2467 | if w.FilterArrayItem("ChunkOffset", "Entries", i, len(self.Entries)) { 2468 | w.Name("Entries") 2469 | w.Int(item) 2470 | } else { 2471 | w.ArrayLeft(i, len(self.Entries)) 2472 | break 2473 | } 2474 | } 2475 | w.EndStruct() 2476 | return 2477 | } 2478 | 2479 | type MovieFrag struct { 2480 | Header *MovieFragHeader 2481 | Tracks []*TrackFrag 2482 | } 2483 | 2484 | func ReadMovieFrag(r *io.LimitedReader) (res *MovieFrag, err error) { 2485 | 2486 | self := &MovieFrag{} 2487 | for r.N > 0 { 2488 | var cc4 string 2489 | var ar *io.LimitedReader 2490 | if ar, cc4, err = ReadAtomHeader(r, ""); err != nil { 2491 | return 2492 | } 2493 | switch cc4 { 2494 | case "mfhd": 2495 | { 2496 | if self.Header, err = ReadMovieFragHeader(ar); err != nil { 2497 | return 2498 | } 2499 | } 2500 | case "traf": 2501 | { 2502 | var item *TrackFrag 2503 | if item, err = ReadTrackFrag(ar); err != nil { 2504 | return 2505 | } 2506 | self.Tracks = append(self.Tracks, item) 2507 | } 2508 | 2509 | } 2510 | if _, err = ReadDummy(ar, int(ar.N)); err != nil { 2511 | return 2512 | } 2513 | } 2514 | res = self 2515 | return 2516 | } 2517 | func WriteMovieFrag(w io.WriteSeeker, self *MovieFrag) (err error) { 2518 | 2519 | var aw *Writer 2520 | if aw, err = WriteAtomHeader(w, "moof"); err != nil { 2521 | return 2522 | } 2523 | w = aw 2524 | if self.Header != nil { 2525 | if err = WriteMovieFragHeader(w, self.Header); err != nil { 2526 | return 2527 | } 2528 | } 2529 | if self.Tracks != nil { 2530 | for _, elem := range self.Tracks { 2531 | if err = WriteTrackFrag(w, elem); err != nil { 2532 | return 2533 | } 2534 | } 2535 | } 2536 | if err = aw.Close(); err != nil { 2537 | return 2538 | } 2539 | return 2540 | } 2541 | func WalkMovieFrag(w Walker, self *MovieFrag) { 2542 | 2543 | w.StartStruct("MovieFrag") 2544 | if self.Header != nil { 2545 | WalkMovieFragHeader(w, self.Header) 2546 | } 2547 | for i, item := range self.Tracks { 2548 | if w.FilterArrayItem("MovieFrag", "Tracks", i, len(self.Tracks)) { 2549 | if item != nil { 2550 | WalkTrackFrag(w, item) 2551 | } 2552 | } else { 2553 | w.ArrayLeft(i, len(self.Tracks)) 2554 | break 2555 | } 2556 | } 2557 | w.EndStruct() 2558 | return 2559 | } 2560 | 2561 | type MovieFragHeader struct { 2562 | Version int 2563 | Flags int 2564 | SeqNum int 2565 | } 2566 | 2567 | func ReadMovieFragHeader(r *io.LimitedReader) (res *MovieFragHeader, err error) { 2568 | 2569 | self := &MovieFragHeader{} 2570 | if self.Version, err = ReadInt(r, 1); err != nil { 2571 | return 2572 | } 2573 | if self.Flags, err = ReadInt(r, 3); err != nil { 2574 | return 2575 | } 2576 | if self.SeqNum, err = ReadInt(r, 4); err != nil { 2577 | return 2578 | } 2579 | res = self 2580 | return 2581 | } 2582 | func WriteMovieFragHeader(w io.WriteSeeker, self *MovieFragHeader) (err error) { 2583 | 2584 | var aw *Writer 2585 | if aw, err = WriteAtomHeader(w, "mfhd"); err != nil { 2586 | return 2587 | } 2588 | w = aw 2589 | if err = WriteInt(w, self.Version, 1); err != nil { 2590 | return 2591 | } 2592 | if err = WriteInt(w, self.Flags, 3); err != nil { 2593 | return 2594 | } 2595 | if err = WriteInt(w, self.SeqNum, 4); err != nil { 2596 | return 2597 | } 2598 | if err = aw.Close(); err != nil { 2599 | return 2600 | } 2601 | return 2602 | } 2603 | func WalkMovieFragHeader(w Walker, self *MovieFragHeader) { 2604 | 2605 | w.StartStruct("MovieFragHeader") 2606 | w.Name("Version") 2607 | w.Int(self.Version) 2608 | w.Name("Flags") 2609 | w.Int(self.Flags) 2610 | w.Name("SeqNum") 2611 | w.Int(self.SeqNum) 2612 | w.EndStruct() 2613 | return 2614 | } 2615 | 2616 | type TrackFrag struct { 2617 | Header *TrackFragHeader 2618 | DecodeTime *TrackFragDecodeTime 2619 | Run *TrackFragRun 2620 | } 2621 | 2622 | func ReadTrackFrag(r *io.LimitedReader) (res *TrackFrag, err error) { 2623 | 2624 | self := &TrackFrag{} 2625 | for r.N > 0 { 2626 | var cc4 string 2627 | var ar *io.LimitedReader 2628 | if ar, cc4, err = ReadAtomHeader(r, ""); err != nil { 2629 | return 2630 | } 2631 | switch cc4 { 2632 | case "tfhd": 2633 | { 2634 | if self.Header, err = ReadTrackFragHeader(ar); err != nil { 2635 | return 2636 | } 2637 | } 2638 | case "tfdt": 2639 | { 2640 | if self.DecodeTime, err = ReadTrackFragDecodeTime(ar); err != nil { 2641 | return 2642 | } 2643 | } 2644 | case "trun": 2645 | { 2646 | if self.Run, err = ReadTrackFragRun(ar); err != nil { 2647 | return 2648 | } 2649 | } 2650 | 2651 | } 2652 | if _, err = ReadDummy(ar, int(ar.N)); err != nil { 2653 | return 2654 | } 2655 | } 2656 | res = self 2657 | return 2658 | } 2659 | func WriteTrackFrag(w io.WriteSeeker, self *TrackFrag) (err error) { 2660 | 2661 | var aw *Writer 2662 | if aw, err = WriteAtomHeader(w, "traf"); err != nil { 2663 | return 2664 | } 2665 | w = aw 2666 | if self.Header != nil { 2667 | if err = WriteTrackFragHeader(w, self.Header); err != nil { 2668 | return 2669 | } 2670 | } 2671 | if self.DecodeTime != nil { 2672 | if err = WriteTrackFragDecodeTime(w, self.DecodeTime); err != nil { 2673 | return 2674 | } 2675 | } 2676 | if self.Run != nil { 2677 | if err = WriteTrackFragRun(w, self.Run); err != nil { 2678 | return 2679 | } 2680 | } 2681 | if err = aw.Close(); err != nil { 2682 | return 2683 | } 2684 | return 2685 | } 2686 | func WalkTrackFrag(w Walker, self *TrackFrag) { 2687 | 2688 | w.StartStruct("TrackFrag") 2689 | if self.Header != nil { 2690 | WalkTrackFragHeader(w, self.Header) 2691 | } 2692 | if self.DecodeTime != nil { 2693 | WalkTrackFragDecodeTime(w, self.DecodeTime) 2694 | } 2695 | if self.Run != nil { 2696 | WalkTrackFragRun(w, self.Run) 2697 | } 2698 | w.EndStruct() 2699 | return 2700 | } 2701 | -------------------------------------------------------------------------------- /atom/types.go: -------------------------------------------------------------------------------- 1 | 2 | package atom 3 | 4 | type Fixed uint32 5 | type TimeStamp uint32 6 | 7 | func IntToFixed(val int) Fixed { 8 | return Fixed(val<<16) 9 | } 10 | 11 | func FixedToInt(val Fixed) int { 12 | return int(val>>16) 13 | } 14 | 15 | -------------------------------------------------------------------------------- /atom/utils.go: -------------------------------------------------------------------------------- 1 | 2 | package atom 3 | 4 | func GetAVCDecoderConfRecordByTrack(track *Track) (record *AVCDecoderConfRecord) { 5 | if media := track.Media; media != nil { 6 | if info := media.Info; info != nil { 7 | if sample := info.Sample; sample != nil { 8 | if desc := sample.SampleDesc; desc != nil { 9 | if avc1 := desc.Avc1Desc; avc1 != nil { 10 | if conf := avc1.Conf; conf != nil { 11 | return &conf.Record 12 | } 13 | } 14 | } 15 | } 16 | } 17 | } 18 | return 19 | } 20 | 21 | func GetMp4aDescByTrack(track *Track) (mp4a *Mp4aDesc) { 22 | if media := track.Media; media != nil { 23 | if info := media.Info; info != nil { 24 | if sample := info.Sample; sample != nil { 25 | if desc := sample.SampleDesc; desc != nil { 26 | if mp4a = desc.Mp4aDesc; mp4a != nil { 27 | return 28 | } 29 | } 30 | } 31 | } 32 | } 33 | return 34 | } 35 | 36 | -------------------------------------------------------------------------------- /atom/writer.go: -------------------------------------------------------------------------------- 1 | 2 | package atom 3 | 4 | import ( 5 | "io" 6 | "log" 7 | ) 8 | 9 | func WriteBytes(w io.Writer, b []byte, n int) (err error) { 10 | if len(b) < n { 11 | b = append(b, make([]byte, n-len(b))...) 12 | } 13 | _, err = w.Write(b[:n]) 14 | return 15 | } 16 | 17 | func WriteUInt(w io.Writer, val uint, n int) (err error) { 18 | var b [8]byte 19 | for i := n-1; i >= 0; i-- { 20 | b[i] = byte(val) 21 | val >>= 8 22 | } 23 | return WriteBytes(w, b[:], n) 24 | } 25 | 26 | func WriteInt(w io.Writer, val int, n int) (err error) { 27 | var uval uint 28 | if val < 0 { 29 | uval = uint((1<>8 41 | } else if n == 4 { 42 | uval = uint(val) 43 | } else { 44 | panic("only fixed32 and fixed16 is supported") 45 | } 46 | 47 | return WriteUInt(w, uval, n) 48 | } 49 | 50 | func WriteTimeStamp(w io.Writer, ts TimeStamp, n int) (err error) { 51 | return WriteUInt(w, uint(ts), n) 52 | } 53 | 54 | func WriteString(w io.Writer, val string, n int) (err error) { 55 | return WriteBytes(w, []byte(val), n) 56 | } 57 | 58 | func WriteDummy(w io.Writer, n int) (err error) { 59 | return WriteBytes(w, []byte{}, n) 60 | } 61 | 62 | type Writer struct { 63 | io.WriteSeeker 64 | sizePos int64 65 | } 66 | 67 | func WriteEmptyInt(w io.WriteSeeker, n int) (pos int64, err error) { 68 | if pos, err = w.Seek(0, 1); err != nil { 69 | return 70 | } 71 | if err = WriteInt(w, 0, n); err != nil { 72 | return 73 | } 74 | return 75 | } 76 | 77 | func RefillInt(w io.WriteSeeker, pos int64, val int, n int) (err error) { 78 | var curPos int64 79 | if curPos, err = w.Seek(0, 1); err != nil { 80 | return 81 | } 82 | if _, err = w.Seek(pos, 0); err != nil { 83 | return 84 | } 85 | if err = WriteInt(w, val, n); err != nil { 86 | return 87 | } 88 | if _, err = w.Seek(curPos, 0); err != nil { 89 | return 90 | } 91 | return 92 | } 93 | 94 | func (self *Writer) Close() (err error) { 95 | var curPos int64 96 | if curPos, err = self.Seek(0, 1); err != nil { 97 | return 98 | } 99 | if err = RefillInt(self, self.sizePos, int(curPos - self.sizePos), 4); err != nil { 100 | return 101 | } 102 | if false { 103 | log.Println("writeback", self.sizePos, curPos, curPos-self.sizePos) 104 | } 105 | return 106 | } 107 | 108 | func WriteAtomHeader(w io.WriteSeeker, cc4 string) (res *Writer, err error) { 109 | self := &Writer{WriteSeeker: w} 110 | 111 | if self.sizePos, err = WriteEmptyInt(w, 4); err != nil { 112 | return 113 | } 114 | if err = WriteString(self, cc4, 4); err != nil { 115 | return 116 | } 117 | 118 | res = self 119 | return 120 | } 121 | 122 | -------------------------------------------------------------------------------- /demuxer.go: -------------------------------------------------------------------------------- 1 | 2 | package mp4 3 | 4 | import ( 5 | "github.com/nareix/mp4/atom" 6 | "github.com/nareix/mp4/isom" 7 | "bytes" 8 | "fmt" 9 | "io" 10 | ) 11 | 12 | type Demuxer struct { 13 | R io.ReadSeeker 14 | Tracks []*Track 15 | TrackH264 *Track 16 | TrackAAC *Track 17 | MovieAtom *atom.Movie 18 | } 19 | 20 | func (self *Demuxer) ReadHeader() (err error) { 21 | var N int64 22 | var moov *atom.Movie 23 | 24 | if N, err = self.R.Seek(0, 2); err != nil { 25 | return 26 | } 27 | if _, err = self.R.Seek(0, 0); err != nil { 28 | return 29 | } 30 | 31 | lr := &io.LimitedReader{R: self.R, N: N} 32 | for lr.N > 0 { 33 | var ar *io.LimitedReader 34 | 35 | var cc4 string 36 | if ar, cc4, err = atom.ReadAtomHeader(lr, ""); err != nil { 37 | return 38 | } 39 | 40 | if cc4 == "moov" { 41 | if moov, err = atom.ReadMovie(ar); err != nil { 42 | return 43 | } 44 | } 45 | 46 | if _, err = atom.ReadDummy(lr, int(ar.N)); err != nil { 47 | return 48 | } 49 | } 50 | 51 | if moov == nil { 52 | err = fmt.Errorf("'moov' atom not found") 53 | return 54 | } 55 | self.MovieAtom = moov 56 | 57 | self.Tracks = []*Track{} 58 | for _, atrack := range(moov.Tracks) { 59 | track := &Track{ 60 | TrackAtom: atrack, 61 | r: self.R, 62 | } 63 | if atrack.Media != nil && atrack.Media.Info != nil && atrack.Media.Info.Sample != nil { 64 | track.sample = atrack.Media.Info.Sample 65 | } else { 66 | err = fmt.Errorf("sample table not found") 67 | return 68 | } 69 | if record := atom.GetAVCDecoderConfRecordByTrack(atrack); record != nil { 70 | if len(record.PPS) > 0 { 71 | track.pps = record.PPS[0] 72 | } 73 | if len(record.SPS) > 0 { 74 | track.sps = record.SPS[0] 75 | } 76 | track.Type = H264 77 | self.TrackH264 = track 78 | self.Tracks = append(self.Tracks, track) 79 | } else if mp4a := atom.GetMp4aDescByTrack(atrack); mp4a != nil && mp4a.Conf != nil { 80 | if config, err := isom.ReadElemStreamDescAAC(bytes.NewReader(mp4a.Conf.Data)); err == nil { 81 | track.mpeg4AudioConfig = config.Complete() 82 | track.Type = AAC 83 | self.TrackAAC = track 84 | self.Tracks = append(self.Tracks, track) 85 | } 86 | } 87 | } 88 | 89 | return 90 | } 91 | 92 | func (self *Track) setSampleIndex(index int) (err error) { 93 | found := false 94 | start := 0 95 | self.chunkGroupIndex = 0 96 | 97 | for self.chunkIndex = range(self.sample.ChunkOffset.Entries) { 98 | n := self.sample.SampleToChunk.Entries[self.chunkGroupIndex].SamplesPerChunk 99 | if index >= start && index < start+n { 100 | found = true 101 | self.sampleIndexInChunk = index-start 102 | break 103 | } 104 | start += n 105 | if self.chunkGroupIndex+1 < len(self.sample.SampleToChunk.Entries) && 106 | self.chunkIndex+1 == self.sample.SampleToChunk.Entries[self.chunkGroupIndex+1].FirstChunk { 107 | self.chunkGroupIndex++ 108 | } 109 | } 110 | if !found { 111 | err = io.EOF 112 | return 113 | } 114 | 115 | if self.sample.SampleSize.SampleSize != 0 { 116 | self.sampleOffsetInChunk = int64(self.sampleIndexInChunk*self.sample.SampleSize.SampleSize) 117 | } else { 118 | if index >= len(self.sample.SampleSize.Entries) { 119 | err = io.EOF 120 | return 121 | } 122 | self.sampleOffsetInChunk = int64(0) 123 | for i := index-self.sampleIndexInChunk; i < index; i++ { 124 | self.sampleOffsetInChunk += int64(self.sample.SampleSize.Entries[i]) 125 | } 126 | } 127 | 128 | self.dts = int64(0) 129 | start = 0 130 | found = false 131 | self.sttsEntryIndex = 0 132 | for self.sttsEntryIndex < len(self.sample.TimeToSample.Entries) { 133 | entry := self.sample.TimeToSample.Entries[self.sttsEntryIndex] 134 | n := entry.Count 135 | if index >= start && index < start+n { 136 | self.sampleIndexInSttsEntry = index-start 137 | self.dts += int64((index-start)*entry.Duration) 138 | break 139 | } 140 | start += n 141 | self.dts += int64(n*entry.Duration) 142 | self.sttsEntryIndex++ 143 | } 144 | if !found { 145 | err = io.EOF 146 | return 147 | } 148 | 149 | if self.sample.CompositionOffset != nil && len(self.sample.CompositionOffset.Entries) > 0 { 150 | start = 0 151 | found = false 152 | self.cttsEntryIndex = 0 153 | for self.cttsEntryIndex < len(self.sample.CompositionOffset.Entries) { 154 | n := self.sample.CompositionOffset.Entries[self.cttsEntryIndex].Count 155 | if index >= start && index < start+n { 156 | self.sampleIndexInCttsEntry = index-start 157 | break 158 | } 159 | start += n 160 | self.cttsEntryIndex++ 161 | } 162 | if !found { 163 | err = io.EOF 164 | return 165 | } 166 | } 167 | 168 | if self.sample.SyncSample != nil { 169 | self.syncSampleIndex = 0 170 | for self.syncSampleIndex < len(self.sample.SyncSample.Entries)-1 { 171 | if self.sample.SyncSample.Entries[self.syncSampleIndex+1]-1 > index { 172 | break 173 | } 174 | self.syncSampleIndex++ 175 | } 176 | } 177 | 178 | self.sampleIndex = index 179 | return 180 | } 181 | 182 | func (self *Track) isSampleValid() bool { 183 | if self.chunkIndex >= len(self.sample.ChunkOffset.Entries) { 184 | return false 185 | } 186 | if self.chunkGroupIndex >= len(self.sample.SampleToChunk.Entries) { 187 | return false 188 | } 189 | if self.sttsEntryIndex >= len(self.sample.TimeToSample.Entries) { 190 | return false 191 | } 192 | if self.sample.CompositionOffset != nil && len(self.sample.CompositionOffset.Entries) > 0 { 193 | if self.cttsEntryIndex >= len(self.sample.CompositionOffset.Entries) { 194 | return false 195 | } 196 | } 197 | if self.sample.SyncSample != nil { 198 | if self.syncSampleIndex >= len(self.sample.SyncSample.Entries) { 199 | return false 200 | } 201 | } 202 | if self.sample.SampleSize.SampleSize != 0 { 203 | if self.sampleIndex >= len(self.sample.SampleSize.Entries) { 204 | return false 205 | } 206 | } 207 | return true 208 | } 209 | 210 | func (self *Track) incSampleIndex() { 211 | self.sampleIndexInChunk++ 212 | if self.sampleIndexInChunk == self.sample.SampleToChunk.Entries[self.chunkGroupIndex].SamplesPerChunk { 213 | self.chunkIndex++ 214 | self.sampleIndexInChunk = 0 215 | self.sampleOffsetInChunk = int64(0) 216 | } else { 217 | if self.sample.SampleSize.SampleSize != 0 { 218 | self.sampleOffsetInChunk += int64(self.sample.SampleSize.SampleSize) 219 | } else { 220 | self.sampleOffsetInChunk += int64(self.sample.SampleSize.Entries[self.sampleIndex]) 221 | } 222 | } 223 | 224 | if self.chunkGroupIndex+1 < len(self.sample.SampleToChunk.Entries) && 225 | self.chunkIndex+1 == self.sample.SampleToChunk.Entries[self.chunkGroupIndex+1].FirstChunk { 226 | self.chunkGroupIndex++ 227 | } 228 | 229 | sttsEntry := self.sample.TimeToSample.Entries[self.sttsEntryIndex] 230 | self.sampleIndexInSttsEntry++ 231 | self.dts += int64(sttsEntry.Duration) 232 | if self.sampleIndexInSttsEntry == sttsEntry.Count { 233 | self.sampleIndexInSttsEntry = 0 234 | self.sttsEntryIndex++ 235 | } 236 | 237 | if self.sample.CompositionOffset != nil && len(self.sample.CompositionOffset.Entries) > 0 { 238 | self.sampleIndexInCttsEntry++ 239 | if self.sampleIndexInCttsEntry == self.sample.CompositionOffset.Entries[self.cttsEntryIndex].Count { 240 | self.sampleIndexInCttsEntry = 0 241 | self.cttsEntryIndex++ 242 | } 243 | } 244 | 245 | if self.sample.SyncSample != nil { 246 | entries := self.sample.SyncSample.Entries 247 | if self.syncSampleIndex+1 < len(entries) && entries[self.syncSampleIndex+1]-1 == self.sampleIndex+1 { 248 | self.syncSampleIndex++ 249 | } 250 | } 251 | 252 | self.sampleIndex++ 253 | } 254 | 255 | func (self *Track) SampleCount() int { 256 | if self.sample.SampleSize.SampleSize == 0 { 257 | chunkGroupIndex := 0 258 | count := 0 259 | for chunkIndex := range(self.sample.ChunkOffset.Entries) { 260 | n := self.sample.SampleToChunk.Entries[chunkGroupIndex].SamplesPerChunk 261 | count += n 262 | if chunkGroupIndex+1 < len(self.sample.SampleToChunk.Entries) && 263 | chunkIndex+1 == self.sample.SampleToChunk.Entries[chunkGroupIndex+1].FirstChunk { 264 | chunkGroupIndex++ 265 | } 266 | } 267 | return count 268 | } else { 269 | return len(self.sample.SampleSize.Entries) 270 | } 271 | } 272 | 273 | func (self *Track) ReadSample() (pts int64, dts int64, isKeyFrame bool, data []byte, err error) { 274 | if !self.isSampleValid() { 275 | err = io.EOF 276 | return 277 | } 278 | 279 | chunkOffset := self.sample.ChunkOffset.Entries[self.chunkIndex] 280 | sampleSize := 0 281 | if self.sample.SampleSize.SampleSize != 0 { 282 | sampleSize = self.sample.SampleSize.SampleSize 283 | } else { 284 | sampleSize = self.sample.SampleSize.Entries[self.sampleIndex] 285 | } 286 | 287 | sampleOffset := int64(chunkOffset)+self.sampleOffsetInChunk 288 | if _, err = self.r.Seek(int64(sampleOffset), 0); err != nil { 289 | return 290 | } 291 | 292 | data = make([]byte, sampleSize) 293 | if _, err = self.r.Read(data); err != nil { 294 | return 295 | } 296 | 297 | if self.sample.SyncSample != nil { 298 | if self.sample.SyncSample.Entries[self.syncSampleIndex]-1 == self.sampleIndex { 299 | isKeyFrame = true 300 | } 301 | } 302 | 303 | //println("pts/dts", self.ptsEntryIndex, self.dtsEntryIndex) 304 | dts = self.dts 305 | if self.sample.CompositionOffset != nil && len(self.sample.CompositionOffset.Entries) > 0 { 306 | pts = self.dts+int64(self.sample.CompositionOffset.Entries[self.cttsEntryIndex].Offset) 307 | } else { 308 | pts = dts 309 | } 310 | 311 | self.incSampleIndex() 312 | return 313 | } 314 | 315 | func (self *Track) Duration() float64 { 316 | total := int64(0) 317 | for _, entry := range(self.sample.TimeToSample.Entries) { 318 | total += int64(entry.Duration*entry.Count) 319 | } 320 | return float64(total)/float64(self.TrackAtom.Media.Header.TimeScale) 321 | } 322 | 323 | func (self *Track) CurTime() float64 { 324 | return self.TsToTime(self.dts) 325 | } 326 | 327 | func (self *Track) CurTs() int64 { 328 | return self.dts 329 | } 330 | 331 | func (self *Track) CurSampleIndex() int { 332 | return self.sampleIndex 333 | } 334 | 335 | func (self *Track) SeekToTime(time float64) error { 336 | index := self.TimeToSampleIndex(time) 337 | return self.setSampleIndex(index) 338 | } 339 | 340 | func (self *Track) SeekToSampleIndex(index int) error { 341 | return self.setSampleIndex(index) 342 | } 343 | 344 | func (self *Track) TimeToSampleIndex(time float64) int { 345 | targetTs := self.TimeToTs(time) 346 | targetIndex := 0 347 | 348 | startTs := int64(0) 349 | endTs := int64(0) 350 | startIndex := 0 351 | endIndex := 0 352 | found := false 353 | for _, entry := range(self.sample.TimeToSample.Entries) { 354 | endTs = startTs+int64(entry.Count*entry.Duration) 355 | endIndex = startIndex+entry.Count 356 | if targetTs >= startTs && targetTs < endTs { 357 | targetIndex = startIndex+int((targetTs-startTs)/int64(entry.Duration)) 358 | found = true 359 | } 360 | startTs = endTs 361 | startIndex = endIndex 362 | } 363 | if !found { 364 | if targetTs < 0 { 365 | targetIndex = 0 366 | } else { 367 | targetIndex = endIndex-1 368 | } 369 | } 370 | 371 | if self.sample.SyncSample != nil { 372 | entries := self.sample.SyncSample.Entries 373 | for i := len(entries)-1; i >= 0; i-- { 374 | if entries[i]-1 < targetIndex { 375 | targetIndex = entries[i]-1 376 | break 377 | } 378 | } 379 | } 380 | 381 | return targetIndex 382 | } 383 | 384 | func (self *Track) TimeToTs(time float64) int64 { 385 | return int64(time*float64(self.TrackAtom.Media.Header.TimeScale)) 386 | } 387 | 388 | func (self *Track) TsToTime(ts int64) float64 { 389 | return float64(ts)/float64(self.TrackAtom.Media.Header.TimeScale) 390 | } 391 | 392 | func (self *Track) TimeScale() int64 { 393 | return int64(self.TrackAtom.Media.Header.TimeScale) 394 | } 395 | 396 | func (self *Track) GetH264PPSAndSPS() (pps, sps []byte) { 397 | return self.pps, self.sps 398 | } 399 | 400 | func (self *Track) GetMPEG4AudioConfig() isom.MPEG4AudioConfig { 401 | return self.mpeg4AudioConfig 402 | } 403 | 404 | -------------------------------------------------------------------------------- /example/example.go: -------------------------------------------------------------------------------- 1 | 2 | package main 3 | 4 | import ( 5 | "github.com/nareix/mp4" 6 | "os" 7 | "fmt" 8 | "encoding/hex" 9 | ) 10 | 11 | func DemuxExample() { 12 | file, _ := os.Open("test.mp4") 13 | demuxer := &mp4.Demuxer{R: file} 14 | demuxer.ReadHeader() 15 | 16 | fmt.Println("Total tracks: ", len(demuxer.Tracks)) 17 | fmt.Println("Duration: ", demuxer.TrackH264.Duration()) 18 | 19 | count := demuxer.TrackH264.SampleCount() 20 | fmt.Println("SampleCount: ", count) 21 | 22 | demuxer.TrackH264.SeekToTime(2.3) 23 | 24 | var sample []byte 25 | for i := 0; i < 5; i++ { 26 | pts, dts, isKeyFrame, data, err := demuxer.TrackH264.ReadSample() 27 | fmt.Println("sample #", 28 | i, pts, dts, isKeyFrame, len(data), 29 | demuxer.TrackH264.CurTime(), 30 | err, 31 | ) 32 | if i == 3 { 33 | sample = data 34 | } 35 | } 36 | fmt.Println("Sample H264 frame:") 37 | fmt.Print(hex.Dump(sample)) 38 | 39 | fmt.Println("Duration(AAC): ", demuxer.TrackAAC.Duration()) 40 | fmt.Println("SampleCount(AAC): ", demuxer.TrackAAC.SampleCount()) 41 | demuxer.TrackAAC.SeekToTime(1.3) 42 | 43 | for i := 0; i < 5; i++ { 44 | pts, dts, isKeyFrame, data, err := demuxer.TrackAAC.ReadSample() 45 | fmt.Println("sample(AAC) #", 46 | i, pts, dts, isKeyFrame, len(data), 47 | demuxer.TrackAAC.CurTime(), 48 | err, 49 | ) 50 | if i == 1 { 51 | sample = data 52 | } 53 | } 54 | fmt.Println("Sample AAC frame:") 55 | fmt.Print(hex.Dump(sample)) 56 | } 57 | 58 | func MuxExample() { 59 | infile, _ := os.Open("test.mp4") 60 | outfile, _ := os.Create("test.out.mp4") 61 | 62 | demuxer := &mp4.Demuxer{R: infile} 63 | demuxer.ReadHeader() 64 | 65 | muxer := &mp4.Muxer{W: outfile} 66 | muxer.AddH264Track() 67 | muxer.TrackH264.SetH264PPS(demuxer.TrackH264.GetH264PPS()) 68 | muxer.TrackH264.SetH264SPS(demuxer.TrackH264.GetH264SPS()) 69 | muxer.TrackH264.SetTimeScale(demuxer.TrackH264.TimeScale()) 70 | 71 | muxer.WriteHeader() 72 | for { 73 | pts, dts, isKeyFrame, data, err := demuxer.TrackH264.ReadSample() 74 | if err != nil { 75 | break 76 | } 77 | fmt.Println("sample #", dts, pts) 78 | muxer.TrackH264.WriteSample(pts, dts, isKeyFrame, data) 79 | } 80 | 81 | muxer.WriteTrailer() 82 | outfile.Close() 83 | } 84 | 85 | func main() { 86 | //DemuxExample() 87 | MuxExample() 88 | } 89 | 90 | -------------------------------------------------------------------------------- /isom/isom.go: -------------------------------------------------------------------------------- 1 | package isom 2 | 3 | import ( 4 | "bytes" 5 | "fmt" 6 | "github.com/nareix/bits" 7 | "io" 8 | "io/ioutil" 9 | ) 10 | 11 | // copied from libavformat/isom.h 12 | const ( 13 | MP4ESDescrTag = 3 14 | MP4DecConfigDescrTag = 4 15 | MP4DecSpecificDescrTag = 5 16 | ) 17 | 18 | var debugReader = false 19 | var debugWriter = false 20 | 21 | // copied from libavcodec/mpeg4audio.h 22 | const ( 23 | AOT_AAC_MAIN = 1 + iota ///< Y Main 24 | AOT_AAC_LC ///< Y Low Complexity 25 | AOT_AAC_SSR ///< N (code in SoC repo) Scalable Sample Rate 26 | AOT_AAC_LTP ///< Y Long Term Prediction 27 | AOT_SBR ///< Y Spectral Band Replication 28 | AOT_AAC_SCALABLE ///< N Scalable 29 | AOT_TWINVQ ///< N Twin Vector Quantizer 30 | AOT_CELP ///< N Code Excited Linear Prediction 31 | AOT_HVXC ///< N Harmonic Vector eXcitation Coding 32 | AOT_TTSI = 12 + iota ///< N Text-To-Speech Interface 33 | AOT_MAINSYNTH ///< N Main Synthesis 34 | AOT_WAVESYNTH ///< N Wavetable Synthesis 35 | AOT_MIDI ///< N General MIDI 36 | AOT_SAFX ///< N Algorithmic Synthesis and Audio Effects 37 | AOT_ER_AAC_LC ///< N Error Resilient Low Complexity 38 | AOT_ER_AAC_LTP = 19 + iota ///< N Error Resilient Long Term Prediction 39 | AOT_ER_AAC_SCALABLE ///< N Error Resilient Scalable 40 | AOT_ER_TWINVQ ///< N Error Resilient Twin Vector Quantizer 41 | AOT_ER_BSAC ///< N Error Resilient Bit-Sliced Arithmetic Coding 42 | AOT_ER_AAC_LD ///< N Error Resilient Low Delay 43 | AOT_ER_CELP ///< N Error Resilient Code Excited Linear Prediction 44 | AOT_ER_HVXC ///< N Error Resilient Harmonic Vector eXcitation Coding 45 | AOT_ER_HILN ///< N Error Resilient Harmonic and Individual Lines plus Noise 46 | AOT_ER_PARAM ///< N Error Resilient Parametric 47 | AOT_SSC ///< N SinuSoidal Coding 48 | AOT_PS ///< N Parametric Stereo 49 | AOT_SURROUND ///< N MPEG Surround 50 | AOT_ESCAPE ///< Y Escape Value 51 | AOT_L1 ///< Y Layer 1 52 | AOT_L2 ///< Y Layer 2 53 | AOT_L3 ///< Y Layer 3 54 | AOT_DST ///< N Direct Stream Transfer 55 | AOT_ALS ///< Y Audio LosslesS 56 | AOT_SLS ///< N Scalable LosslesS 57 | AOT_SLS_NON_CORE ///< N Scalable LosslesS (non core) 58 | AOT_ER_AAC_ELD ///< N Error Resilient Enhanced Low Delay 59 | AOT_SMR_SIMPLE ///< N Symbolic Music Representation Simple 60 | AOT_SMR_MAIN ///< N Symbolic Music Representation Main 61 | AOT_USAC_NOSBR ///< N Unified Speech and Audio Coding (no SBR) 62 | AOT_SAOC ///< N Spatial Audio Object Coding 63 | AOT_LD_SURROUND ///< N Low Delay MPEG Surround 64 | AOT_USAC ///< N Unified Speech and Audio Coding 65 | ) 66 | 67 | type MPEG4AudioConfig struct { 68 | SampleRate int 69 | ChannelCount int 70 | ObjectType uint 71 | SampleRateIndex uint 72 | ChannelConfig uint 73 | } 74 | 75 | var sampleRateTable = []int{ 76 | 96000, 88200, 64000, 48000, 44100, 32000, 77 | 24000, 22050, 16000, 12000, 11025, 8000, 7350, 78 | } 79 | 80 | var chanConfigTable = []int{ 81 | 0, 1, 2, 3, 4, 5, 6, 8, 82 | } 83 | 84 | func IsADTSFrame(frames []byte) bool { 85 | return len(frames) > 7 && frames[0]==0xff&&frames[1]&0xf0==0xf0 86 | } 87 | 88 | func ReadADTSFrame(frame []byte) (config MPEG4AudioConfig, payload []byte, samples int, framelen int, err error) { 89 | if !IsADTSFrame(frame) { 90 | err = fmt.Errorf("not adts frame") 91 | return 92 | } 93 | config.ObjectType = uint(frame[2]>>6)+1 94 | config.SampleRateIndex = uint(frame[2]>>2&0xf) 95 | config.ChannelConfig = uint(frame[2]<<2&0x4|frame[3]>>6&0x3) 96 | framelen = int(frame[3]&0x3)<<11|int(frame[4])<<3|int(frame[5]>>5) 97 | samples = (int(frame[6]&0x3)+1)*1024 98 | hdrlen := 7 99 | if frame[1]&0x1 == 0 { 100 | hdrlen = 9 101 | } 102 | if framelen < hdrlen || len(frame) < framelen { 103 | err = fmt.Errorf("invalid adts header length") 104 | return 105 | } 106 | payload = frame[hdrlen:framelen] 107 | return 108 | } 109 | 110 | func MakeADTSHeader(config MPEG4AudioConfig, samples int, payloadLength int) (header []byte) { 111 | payloadLength += 7 112 | //AAAAAAAA AAAABCCD EEFFFFGH HHIJKLMM MMMMMMMM MMMOOOOO OOOOOOPP (QQQQQQQQ QQQQQQQQ) 113 | header = []byte{0xff,0xf1,0x50,0x80,0x043,0xff,0xcd} 114 | //config.ObjectType = uint(frames[2]>>6)+1 115 | //config.SampleRateIndex = uint(frames[2]>>2&0xf) 116 | //config.ChannelConfig = uint(frames[2]<<2&0x4|frames[3]>>6&0x3) 117 | header[2] = (byte(config.ObjectType-1)&0x3)<<6|(byte(config.SampleRateIndex)&0xf)<<2|byte(config.ChannelConfig>>2)&0x1 118 | header[3] = header[3]&0x3f|byte(config.ChannelConfig&0x3)<<6 119 | header[3] = header[3]&0xfc|byte(payloadLength>>11)&0x3 120 | header[4] = byte(payloadLength>>3) 121 | header[5] = header[5]&0x1f|(byte(payloadLength)&0x7)<<5 122 | header[6] = header[6]&0xfc|byte(samples/1024-1) 123 | return 124 | } 125 | 126 | func ExtractADTSFrames(frames []byte) (config MPEG4AudioConfig, payload []byte, samples int, err error) { 127 | for len(frames) > 0 { 128 | var n, framelen int 129 | if config, payload, n, framelen, err = ReadADTSFrame(frames); err != nil { 130 | return 131 | } 132 | frames = frames[framelen:] 133 | samples += n 134 | } 135 | return 136 | } 137 | 138 | func ReadADTSHeader(data []byte) (config MPEG4AudioConfig, frameLength int) { 139 | br := &bits.Reader{R: bytes.NewReader(data)} 140 | var i uint 141 | 142 | //Structure 143 | //AAAAAAAA AAAABCCD EEFFFFGH HHIJKLMM MMMMMMMM MMMOOOOO OOOOOOPP (QQQQQQQQ QQQQQQQQ) 144 | //Header consists of 7 or 9 bytes (without or with CRC). 145 | 146 | // 2 bytes 147 | //A 12 syncword 0xFFF, all bits must be 1 148 | br.ReadBits(12) 149 | //B 1 MPEG Version: 0 for MPEG-4, 1 for MPEG-2 150 | br.ReadBits(1) 151 | //C 2 Layer: always 0 152 | br.ReadBits(2) 153 | //D 1 protection absent, Warning, set to 1 if there is no CRC and 0 if there is CRC 154 | br.ReadBits(1) 155 | 156 | //E 2 profile, the MPEG-4 Audio Object Type minus 1 157 | config.ObjectType, _ = br.ReadBits(2) 158 | config.ObjectType++ 159 | //F 4 MPEG-4 Sampling Frequency Index (15 is forbidden) 160 | config.SampleRateIndex, _ = br.ReadBits(4) 161 | //G 1 private bit, guaranteed never to be used by MPEG, set to 0 when encoding, ignore when decoding 162 | br.ReadBits(1) 163 | //H 3 MPEG-4 Channel Configuration (in the case of 0, the channel configuration is sent via an inband PCE) 164 | config.ChannelConfig, _ = br.ReadBits(3) 165 | //I 1 originality, set to 0 when encoding, ignore when decoding 166 | br.ReadBits(1) 167 | //J 1 home, set to 0 when encoding, ignore when decoding 168 | br.ReadBits(1) 169 | //K 1 copyrighted id bit, the next bit of a centrally registered copyright identifier, set to 0 when encoding, ignore when decoding 170 | br.ReadBits(1) 171 | //L 1 copyright id start, signals that this frame's copyright id bit is the first bit of the copyright id, set to 0 when encoding, ignore when decoding 172 | br.ReadBits(1) 173 | 174 | //M 13 frame length, this value must include 7 or 9 bytes of header length: FrameLength = (ProtectionAbsent == 1 ? 7 : 9) + size(AACFrame) 175 | i, _ = br.ReadBits(13) 176 | frameLength = int(i) 177 | //O 11 Buffer fullness 178 | br.ReadBits(11) 179 | //P 2 Number of AAC frames (RDBs) in ADTS frame minus 1, for maximum compatibility always use 1 AAC frame per ADTS frame 180 | br.ReadBits(2) 181 | 182 | //Q 16 CRC if protection absent is 0 183 | return 184 | } 185 | 186 | func readObjectType(r *bits.Reader) (objectType uint, err error) { 187 | if objectType, err = r.ReadBits(5); err != nil { 188 | return 189 | } 190 | if objectType == AOT_ESCAPE { 191 | var i uint 192 | if i, err = r.ReadBits(6); err != nil { 193 | return 194 | } 195 | objectType = 32 + i 196 | } 197 | return 198 | } 199 | 200 | func writeObjectType(w *bits.Writer, objectType uint) (err error) { 201 | if objectType >= 32 { 202 | if err = w.WriteBits(AOT_ESCAPE, 5); err != nil { 203 | return 204 | } 205 | if err = w.WriteBits(objectType-32, 6); err != nil { 206 | return 207 | } 208 | } else { 209 | if err = w.WriteBits(objectType, 5); err != nil { 210 | return 211 | } 212 | } 213 | return 214 | } 215 | 216 | func readSampleRateIndex(r *bits.Reader) (index uint, err error) { 217 | if index, err = r.ReadBits(4); err != nil { 218 | return 219 | } 220 | if index == 0xf { 221 | if index, err = r.ReadBits(24); err != nil { 222 | return 223 | } 224 | } 225 | return 226 | } 227 | 228 | func writeSampleRateIndex(w *bits.Writer, index uint) (err error) { 229 | if index >= 0xf { 230 | if err = w.WriteBits(0xf, 4); err != nil { 231 | return 232 | } 233 | if err = w.WriteBits(index, 24); err != nil { 234 | return 235 | } 236 | } else { 237 | if err = w.WriteBits(index, 4); err != nil { 238 | return 239 | } 240 | } 241 | return 242 | } 243 | 244 | func (self MPEG4AudioConfig) IsValid() bool { 245 | return self.ObjectType > 0 246 | } 247 | 248 | func (self MPEG4AudioConfig) Complete() (config MPEG4AudioConfig) { 249 | config = self 250 | if int(config.SampleRateIndex) < len(sampleRateTable) { 251 | config.SampleRate = sampleRateTable[config.SampleRateIndex] 252 | } 253 | if int(config.ChannelConfig) < len(chanConfigTable) { 254 | config.ChannelCount = chanConfigTable[config.ChannelConfig] 255 | } 256 | return 257 | } 258 | 259 | // copied from libavcodec/mpeg4audio.c avpriv_mpeg4audio_get_config() 260 | func ReadMPEG4AudioConfig(r io.Reader) (config MPEG4AudioConfig, err error) { 261 | br := &bits.Reader{R: r} 262 | 263 | if config.ObjectType, err = readObjectType(br); err != nil { 264 | return 265 | } 266 | if config.SampleRateIndex, err = readSampleRateIndex(br); err != nil { 267 | return 268 | } 269 | if config.ChannelConfig, err = br.ReadBits(4); err != nil { 270 | return 271 | } 272 | return 273 | } 274 | 275 | func WriteMPEG4AudioConfig(w io.Writer, config MPEG4AudioConfig) (err error) { 276 | bw := &bits.Writer{W: w} 277 | 278 | if err = writeObjectType(bw, config.ObjectType); err != nil { 279 | return 280 | } 281 | 282 | if config.SampleRateIndex == 0 { 283 | for i, rate := range sampleRateTable { 284 | if rate == config.SampleRate { 285 | config.SampleRateIndex = uint(i) 286 | } 287 | } 288 | } 289 | if err = writeSampleRateIndex(bw, config.SampleRateIndex); err != nil { 290 | return 291 | } 292 | 293 | if config.ChannelConfig == 0 { 294 | for i, count := range chanConfigTable { 295 | if count == config.ChannelCount { 296 | config.ChannelConfig = uint(i) 297 | } 298 | } 299 | } 300 | if err = bw.WriteBits(config.ChannelConfig, 4); err != nil { 301 | return 302 | } 303 | 304 | if err = bw.FlushBits(); err != nil { 305 | return 306 | } 307 | return 308 | } 309 | 310 | func readDesc(r io.Reader) (tag uint, data []byte, err error) { 311 | if tag, err = bits.ReadUIntBE(r, 8); err != nil { 312 | return 313 | } 314 | var length uint 315 | for i := 0; i < 4; i++ { 316 | var c uint 317 | if c, err = bits.ReadUIntBE(r, 8); err != nil { 318 | return 319 | } 320 | length = (length << 7) | (c & 0x7f) 321 | if c&0x80 == 0 { 322 | break 323 | } 324 | } 325 | data = make([]byte, length) 326 | if _, err = r.Read(data); err != nil { 327 | return 328 | } 329 | return 330 | } 331 | 332 | func writeDesc(w io.Writer, tag uint, data []byte) (err error) { 333 | if err = bits.WriteUIntBE(w, tag, 8); err != nil { 334 | return 335 | } 336 | length := uint(len(data)) 337 | for i := 3; i > 0; i-- { 338 | if err = bits.WriteUIntBE(w, (length>>uint(7*i))&0x7f|0x80, 8); err != nil { 339 | return 340 | } 341 | } 342 | if err = bits.WriteUIntBE(w, length&0x7f, 8); err != nil { 343 | return 344 | } 345 | if _, err = w.Write(data); err != nil { 346 | return 347 | } 348 | return 349 | } 350 | 351 | func readESDesc(r io.Reader) (err error) { 352 | var ES_ID uint 353 | // ES_ID 354 | if ES_ID, err = bits.ReadUIntBE(r, 16); err != nil { 355 | return 356 | } 357 | var flags uint 358 | if flags, err = bits.ReadUIntBE(r, 8); err != nil { 359 | return 360 | } 361 | //streamDependenceFlag 362 | if flags&0x80 != 0 { 363 | if _, err = bits.ReadUIntBE(r, 16); err != nil { 364 | return 365 | } 366 | } 367 | //URL_Flag 368 | if flags&0x40 != 0 { 369 | var length uint 370 | if length, err = bits.ReadUIntBE(r, 8); err != nil { 371 | return 372 | } 373 | if _, err = io.CopyN(ioutil.Discard, r, int64(length)); err != nil { 374 | return 375 | } 376 | } 377 | //OCRstreamFlag 378 | if flags&0x20 != 0 { 379 | if _, err = bits.ReadUIntBE(r, 16); err != nil { 380 | return 381 | } 382 | } 383 | if debugReader { 384 | println("readESDesc:", ES_ID, flags) 385 | } 386 | return 387 | } 388 | 389 | func writeESDesc(w io.Writer, ES_ID uint) (err error) { 390 | // ES_ID 391 | if err = bits.WriteUIntBE(w, ES_ID, 16); err != nil { 392 | return 393 | } 394 | // flags 395 | if err = bits.WriteUIntBE(w, 0, 8); err != nil { 396 | return 397 | } 398 | return 399 | } 400 | 401 | func readDescByTag(r io.Reader, targetTag uint) (data []byte, err error) { 402 | var found bool 403 | for { 404 | if tag, _data, err := readDesc(r); err != nil { 405 | break 406 | } else { 407 | if tag == targetTag { 408 | data = _data 409 | found = true 410 | } 411 | if debugReader { 412 | println("readDescByTag:", tag, len(_data)) 413 | } 414 | } 415 | } 416 | if !found { 417 | err = fmt.Errorf("tag not found") 418 | return 419 | } 420 | return 421 | } 422 | 423 | // copied from libavformat/isom.c ff_mp4_read_dec_config_descr() 424 | func readDecConfDesc(r io.Reader) (decConfig []byte, err error) { 425 | var objectId uint 426 | var streamType uint 427 | var bufSize uint 428 | var maxBitrate uint 429 | var avgBitrate uint 430 | 431 | // objectId 432 | if objectId, err = bits.ReadUIntBE(r, 8); err != nil { 433 | return 434 | } 435 | // streamType 436 | if streamType, err = bits.ReadUIntBE(r, 8); err != nil { 437 | return 438 | } 439 | // buffer size db 440 | if bufSize, err = bits.ReadUIntBE(r, 24); err != nil { 441 | return 442 | } 443 | // max bitrate 444 | if maxBitrate, err = bits.ReadUIntBE(r, 32); err != nil { 445 | return 446 | } 447 | // avg bitrate 448 | if avgBitrate, err = bits.ReadUIntBE(r, 32); err != nil { 449 | return 450 | } 451 | 452 | if debugReader { 453 | println("readDecConfDesc:", objectId, streamType, bufSize, maxBitrate, avgBitrate) 454 | } 455 | 456 | if decConfig, err = readDescByTag(r, MP4DecSpecificDescrTag); err != nil { 457 | return 458 | } 459 | return 460 | } 461 | 462 | // copied from libavformat/movenc.c mov_write_esds_tag() 463 | func writeDecConfDesc(w io.Writer, objectId uint, streamType uint, decConfig []byte) (err error) { 464 | // objectId 465 | if err = bits.WriteUIntBE(w, objectId, 8); err != nil { 466 | return 467 | } 468 | // streamType 469 | if err = bits.WriteUIntBE(w, streamType, 8); err != nil { 470 | return 471 | } 472 | // buffer size db 473 | if err = bits.WriteUIntBE(w, 0, 24); err != nil { 474 | return 475 | } 476 | // max bitrate 477 | if err = bits.WriteUIntBE(w, 200000, 32); err != nil { 478 | return 479 | } 480 | // avg bitrate 481 | if err = bits.WriteUIntBE(w, 0, 32); err != nil { 482 | return 483 | } 484 | if err = writeDesc(w, MP4DecSpecificDescrTag, decConfig); err != nil { 485 | return 486 | } 487 | return 488 | } 489 | 490 | // copied from libavformat/mov.c ff_mov_read_esds() 491 | func ReadElemStreamDesc(r io.Reader) (decConfig []byte, err error) { 492 | if debugReader { 493 | println("ReadElemStreamDesc: start") 494 | } 495 | 496 | var data []byte 497 | if data, err = readDescByTag(r, MP4ESDescrTag); err != nil { 498 | return 499 | } 500 | r = bytes.NewReader(data) 501 | 502 | if err = readESDesc(r); err != nil { 503 | return 504 | } 505 | 506 | if data, err = readDescByTag(r, MP4DecConfigDescrTag); err != nil { 507 | return 508 | } 509 | r = bytes.NewReader(data) 510 | 511 | if decConfig, err = readDecConfDesc(r); err != nil { 512 | return 513 | } 514 | 515 | if debugReader { 516 | println("ReadElemStreamDesc: end") 517 | } 518 | return 519 | } 520 | 521 | func ReadElemStreamDescAAC(r io.Reader) (config MPEG4AudioConfig, err error) { 522 | var data []byte 523 | if data, err = ReadElemStreamDesc(r); err != nil { 524 | return 525 | } 526 | if debugReader { 527 | println("decConfig: ", len(data)) 528 | } 529 | if config, err = ReadMPEG4AudioConfig(bytes.NewReader(data)); err != nil { 530 | return 531 | } 532 | return 533 | } 534 | 535 | func WriteElemStreamDescAAC(w io.Writer, config MPEG4AudioConfig, trackId uint) (err error) { 536 | // MP4ESDescrTag(ESDesc MP4DecConfigDescrTag(objectId streamType bufSize avgBitrate MP4DecSpecificDescrTag(decConfig))) 537 | 538 | buf := &bytes.Buffer{} 539 | WriteMPEG4AudioConfig(buf, config) 540 | data := buf.Bytes() 541 | 542 | buf = &bytes.Buffer{} 543 | // 0x40 = ObjectType AAC 544 | // 0x15 = Audiostream 545 | writeDecConfDesc(buf, 0x40, 0x15, data) 546 | data = buf.Bytes() 547 | 548 | buf = &bytes.Buffer{} 549 | writeDesc(buf, MP4DecConfigDescrTag, data) // 4 550 | data = buf.Bytes() 551 | 552 | buf = &bytes.Buffer{} 553 | writeESDesc(buf, trackId) 554 | buf.Write(data) 555 | writeDesc(buf, 0x06, []byte{0x02}) 556 | data = buf.Bytes() 557 | 558 | buf = &bytes.Buffer{} 559 | writeDesc(buf, MP4ESDescrTag, data) // 3 560 | data = buf.Bytes() 561 | 562 | if _, err = w.Write(data); err != nil { 563 | return 564 | } 565 | return 566 | } 567 | -------------------------------------------------------------------------------- /isom/isom_test.go: -------------------------------------------------------------------------------- 1 | package isom 2 | 3 | import ( 4 | "bytes" 5 | "encoding/hex" 6 | "testing" 7 | ) 8 | 9 | func TestReadElemStreamDesc(t *testing.T) { 10 | debugReader = true 11 | debugWriter = true 12 | 13 | var err error 14 | 15 | data, _ := hex.DecodeString("03808080220002000480808014401500000000030d400000000005808080021210068080800102") 16 | t.Logf("elemDesc=%x", data) 17 | t.Logf("length=%d", len(data)) 18 | 19 | var aconfig MPEG4AudioConfig 20 | if aconfig, err = ReadElemStreamDescAAC(bytes.NewReader(data)); err != nil { 21 | t.Error(err) 22 | } 23 | aconfig = aconfig.Complete() 24 | t.Logf("aconfig=%v", aconfig) 25 | 26 | bw := &bytes.Buffer{} 27 | WriteMPEG4AudioConfig(bw, aconfig) 28 | 29 | bw = &bytes.Buffer{} 30 | WriteElemStreamDescAAC(bw, aconfig, 2) 31 | t.Logf("elemDesc=%x", bw.Bytes()) 32 | data = bw.Bytes() 33 | t.Logf("length=%d", len(data)) 34 | 35 | if aconfig, err = ReadElemStreamDescAAC(bytes.NewReader(data)); err != nil { 36 | t.Error(err) 37 | } 38 | t.Logf("aconfig=%v", aconfig.Complete()) 39 | 40 | //00000000 ff f1 50 80 04 3f fc de 04 00 00 6c 69 62 66 61 |..P..?.....libfa| 41 | //00000010 61 63 20 31 2e 32 38 00 00 42 40 93 20 04 32 00 |ac 1.28..B@. .2.| 42 | //00000020 47 ff f1 50 80 05 1f fc 21 42 fe ed b2 5c a8 00 |G..P....!B...\..| 43 | data, _ = hex.DecodeString("fff15080043ffcde040000") 44 | var n, framelen int 45 | aconfig, _, n, _, _ = ReadADTSFrame(data) 46 | t.Logf("%v n=%d", aconfig.Complete(), n) 47 | 48 | data = MakeADTSHeader(aconfig, 1024*3, 33) 49 | data = append(data, []byte{1,2,3,4,5}...) 50 | t.Logf("%x", data) 51 | aconfig, _, n, framelen, err = ReadADTSFrame(data) 52 | t.Logf("%v n=%d framelen=%d err=%v", aconfig.Complete(), n, framelen, err) 53 | } 54 | -------------------------------------------------------------------------------- /muxer.go: -------------------------------------------------------------------------------- 1 | 2 | package mp4 3 | 4 | import ( 5 | "github.com/nareix/mp4/atom" 6 | "github.com/nareix/mp4/isom" 7 | "github.com/nareix/codec/h264parser" 8 | "io" 9 | "bytes" 10 | "fmt" 11 | ) 12 | 13 | type Muxer struct { 14 | W io.WriteSeeker 15 | Tracks []*Track 16 | TrackH264 *Track 17 | TrackAAC *Track 18 | 19 | mdatWriter *atom.Writer 20 | } 21 | 22 | func (self *Muxer) newTrack() *Track { 23 | track := &Track{} 24 | 25 | track.sample = &atom.SampleTable{ 26 | SampleDesc: &atom.SampleDesc{}, 27 | TimeToSample: &atom.TimeToSample{}, 28 | SampleToChunk: &atom.SampleToChunk{ 29 | Entries: []atom.SampleToChunkEntry{ 30 | { 31 | FirstChunk: 1, 32 | SampleDescId: 1, 33 | SamplesPerChunk: 1, 34 | }, 35 | }, 36 | }, 37 | SampleSize: &atom.SampleSize{}, 38 | ChunkOffset: &atom.ChunkOffset{}, 39 | } 40 | 41 | track.TrackAtom = &atom.Track{ 42 | Header: &atom.TrackHeader{ 43 | TrackId: len(self.Tracks)+1, 44 | Flags: 0x0003, // Track enabled | Track in movie 45 | Duration: 0, // fill later 46 | Matrix: [9]int{0x10000, 0, 0, 0, 0x10000, 0, 0, 0, 0x40000000}, 47 | }, 48 | Media: &atom.Media{ 49 | Header: &atom.MediaHeader{ 50 | TimeScale: 0, // fill later 51 | Duration: 0, // fill later 52 | Language: 21956, 53 | }, 54 | Info: &atom.MediaInfo{ 55 | Sample: track.sample, 56 | Data: &atom.DataInfo{ 57 | Refer: &atom.DataRefer{ 58 | Url: &atom.DataReferUrl{ 59 | Flags: 0x000001, // Self reference 60 | }, 61 | }, 62 | }, 63 | }, 64 | }, 65 | } 66 | 67 | track.muxer = self 68 | self.Tracks = append(self.Tracks, track) 69 | 70 | return track 71 | } 72 | 73 | func (self *Muxer) AddAACTrack() (track *Track) { 74 | track = self.newTrack() 75 | self.TrackAAC = track 76 | track.Type = AAC 77 | track.sample.SampleDesc.Mp4aDesc = &atom.Mp4aDesc{ 78 | DataRefIdx: 1, 79 | NumberOfChannels: 0, // fill later 80 | SampleSize: 0, // fill later 81 | SampleRate: 0, // fill later 82 | Conf: &atom.ElemStreamDesc{}, 83 | } 84 | track.TrackAtom.Header.Volume = atom.IntToFixed(1) 85 | track.TrackAtom.Header.AlternateGroup = 1 86 | track.TrackAtom.Media.Handler = &atom.HandlerRefer{ 87 | SubType: "soun", 88 | Name: "Sound Handler", 89 | } 90 | track.TrackAtom.Media.Info.Sound = &atom.SoundMediaInfo{} 91 | return 92 | } 93 | 94 | func (self *Muxer) AddH264Track() (track *Track) { 95 | track = self.newTrack() 96 | self.TrackH264 = track 97 | track.Type = H264 98 | track.sample.SampleDesc.Avc1Desc = &atom.Avc1Desc{ 99 | DataRefIdx: 1, 100 | HorizontalResolution: 72, 101 | VorizontalResolution: 72, 102 | Width: 0, // fill later 103 | Height: 0, // fill later 104 | FrameCount: 1, 105 | Depth: 24, 106 | ColorTableId: -1, 107 | Conf: &atom.Avc1Conf{}, 108 | } 109 | track.sample.SyncSample = &atom.SyncSample{} 110 | track.TrackAtom.Media.Handler = &atom.HandlerRefer{ 111 | SubType: "vide", 112 | Name: "Video Media Handler", 113 | } 114 | track.sample.CompositionOffset = &atom.CompositionOffset{} 115 | track.TrackAtom.Media.Info.Video = &atom.VideoMediaInfo{ 116 | Flags: 0x000001, 117 | } 118 | return 119 | } 120 | 121 | func (self *Muxer) writeMdat(data []byte) (pos int64, err error) { 122 | if pos, err = self.mdatWriter.Seek(0, 1); err != nil { 123 | return 124 | } 125 | _, err = self.mdatWriter.Write(data) 126 | return 127 | } 128 | 129 | func (self *Muxer) WriteHeader() (err error) { 130 | if self.mdatWriter, err = atom.WriteAtomHeader(self.W, "mdat"); err != nil { 131 | return 132 | } 133 | return 134 | } 135 | 136 | func (self *Track) SetH264PPSAndSPS(pps, sps []byte) { 137 | self.pps, self.sps = pps, sps 138 | } 139 | 140 | func (self *Track) SetMPEG4AudioConfig(config isom.MPEG4AudioConfig) { 141 | self.mpeg4AudioConfig = config 142 | } 143 | 144 | func (self *Track) SetTimeScale(timeScale int64) { 145 | self.TrackAtom.Media.Header.TimeScale = int(timeScale) 146 | return 147 | } 148 | 149 | func (self *Track) WriteSample(pts int64, dts int64, isKeyFrame bool, frame []byte) (err error) { 150 | if self.Type == AAC && isom.IsADTSFrame(frame) { 151 | config := self.mpeg4AudioConfig.Complete() 152 | if config.SampleRate == 0 { 153 | err = fmt.Errorf("invalid sample rate") 154 | return 155 | } 156 | for len(frame) > 0 { 157 | var payload []byte 158 | var samples int 159 | var framelen int 160 | if _, payload, samples, framelen, err = isom.ReadADTSFrame(frame); err != nil { 161 | return 162 | } 163 | delta := int64(samples)*self.TimeScale()/int64(config.SampleRate) 164 | pts += delta 165 | dts += delta 166 | frame = frame[framelen:] 167 | if self.writeSample(pts, dts, isKeyFrame, payload); err != nil { 168 | return 169 | } 170 | } 171 | } 172 | 173 | return self.writeSample(pts, dts, isKeyFrame, frame) 174 | } 175 | 176 | func (self *Track) writeSample(pts int64, dts int64, isKeyFrame bool, data []byte) (err error) { 177 | var filePos int64 178 | var sampleSize int 179 | 180 | if filePos, err = self.muxer.mdatWriter.Seek(0, 1); err != nil { 181 | return 182 | } 183 | 184 | if self.Type == H264 { 185 | nalus, _ := h264parser.SplitNALUs(data) 186 | h264parser.WalkNALUsAVCC(nalus, func(b []byte) { 187 | sampleSize += len(b) 188 | _, err = self.muxer.mdatWriter.Write(b) 189 | }) 190 | if err != nil { 191 | return 192 | } 193 | } else { 194 | sampleSize = len(data) 195 | if _, err = self.muxer.mdatWriter.Write(data); err != nil { 196 | return 197 | } 198 | } 199 | 200 | if isKeyFrame && self.sample.SyncSample != nil { 201 | self.sample.SyncSample.Entries = append(self.sample.SyncSample.Entries, self.sampleIndex+1) 202 | } 203 | 204 | if self.sampleIndex > 0 { 205 | if dts <= self.lastDts { 206 | err = fmt.Errorf("dts must be incremental") 207 | return 208 | } 209 | duration := int(dts-self.lastDts) 210 | if self.sttsEntry == nil || duration != self.sttsEntry.Duration { 211 | self.sample.TimeToSample.Entries = append(self.sample.TimeToSample.Entries, atom.TimeToSampleEntry{Duration: duration}) 212 | self.sttsEntry = &self.sample.TimeToSample.Entries[len(self.sample.TimeToSample.Entries)-1] 213 | } 214 | self.sttsEntry.Count++ 215 | } 216 | 217 | if self.sample.CompositionOffset != nil { 218 | if pts < dts { 219 | err = fmt.Errorf("pts must greater than dts") 220 | return 221 | } 222 | offset := int(pts-dts) 223 | if self.cttsEntry == nil || offset != self.cttsEntry.Offset { 224 | table := self.sample.CompositionOffset 225 | table.Entries = append(table.Entries, atom.CompositionOffsetEntry{Offset: offset}) 226 | self.cttsEntry = &table.Entries[len(table.Entries)-1] 227 | } 228 | self.cttsEntry.Count++ 229 | } 230 | 231 | self.lastDts = dts 232 | self.sampleIndex++ 233 | self.sample.ChunkOffset.Entries = append(self.sample.ChunkOffset.Entries, int(filePos)) 234 | self.sample.SampleSize.Entries = append(self.sample.SampleSize.Entries, sampleSize) 235 | 236 | return 237 | } 238 | 239 | func (self *Track) fillTrackAtom() (err error) { 240 | if self.sampleIndex > 0 { 241 | self.sttsEntry.Count++ 242 | } 243 | 244 | if self.Type == H264 { 245 | self.sample.SampleDesc.Avc1Desc.Conf.Record, err = atom.CreateAVCDecoderConfRecord( 246 | self.sps, 247 | self.pps, 248 | ) 249 | if err != nil { 250 | return 251 | } 252 | var info *atom.H264SPSInfo 253 | if info, err = atom.ParseH264SPS(self.sps[1:]); err != nil { 254 | return 255 | } 256 | self.sample.SampleDesc.Avc1Desc.Width = int(info.Width) 257 | self.sample.SampleDesc.Avc1Desc.Height = int(info.Height) 258 | self.TrackAtom.Header.TrackWidth = atom.IntToFixed(int(info.Width)) 259 | self.TrackAtom.Header.TrackHeight = atom.IntToFixed(int(info.Height)) 260 | self.TrackAtom.Media.Header.Duration = int(self.lastDts) 261 | } else if self.Type == AAC { 262 | if !self.mpeg4AudioConfig.IsValid() { 263 | err = fmt.Errorf("invalie MPEG4AudioConfig") 264 | return 265 | } 266 | buf := &bytes.Buffer{} 267 | config := self.mpeg4AudioConfig.Complete() 268 | if err = isom.WriteElemStreamDescAAC(buf, config, uint(self.TrackAtom.Header.TrackId)); err != nil { 269 | return 270 | } 271 | self.sample.SampleDesc.Mp4aDesc.Conf.Data = buf.Bytes() 272 | self.sample.SampleDesc.Mp4aDesc.NumberOfChannels = config.ChannelCount 273 | self.sample.SampleDesc.Mp4aDesc.SampleSize = config.ChannelCount*8 274 | self.sample.SampleDesc.Mp4aDesc.SampleRate = atom.IntToFixed(config.SampleRate) 275 | self.TrackAtom.Media.Header.Duration = int(self.lastDts) 276 | } 277 | return 278 | } 279 | 280 | func (self *Muxer) WriteTrailer() (err error) { 281 | moov := &atom.Movie{} 282 | moov.Header = &atom.MovieHeader{ 283 | PreferredRate: atom.IntToFixed(1), 284 | PreferredVolume: atom.IntToFixed(1), 285 | Matrix: [9]int{0x10000, 0, 0, 0, 0x10000, 0, 0, 0, 0x40000000}, 286 | NextTrackId: 2, 287 | } 288 | 289 | maxDur := float64(0) 290 | timeScale := 10000 291 | for _, track := range(self.Tracks) { 292 | if err = track.fillTrackAtom(); err != nil { 293 | return 294 | } 295 | dur := track.Duration() 296 | track.TrackAtom.Header.Duration = int(float64(timeScale)*dur) 297 | if dur > maxDur { 298 | maxDur = dur 299 | } 300 | moov.Tracks = append(moov.Tracks, track.TrackAtom) 301 | } 302 | moov.Header.TimeScale = timeScale 303 | moov.Header.Duration = int(float64(timeScale)*maxDur) 304 | 305 | if err = self.mdatWriter.Close(); err != nil { 306 | return 307 | } 308 | if err = atom.WriteMovie(self.W, moov); err != nil { 309 | return 310 | } 311 | 312 | return 313 | } 314 | 315 | /* 316 | type SimpleH264Writer struct { 317 | W io.WriteSeeker 318 | 319 | TimeScale int 320 | sps []byte 321 | pps []byte 322 | 323 | Width int 324 | Height int 325 | 326 | duration int 327 | 328 | sample *atom.SampleTable 329 | sampleToChunk *atom.SampleToChunkEntry 330 | sampleIdx int 331 | timeToSample *atom.TimeToSampleEntry 332 | 333 | mdatWriter *atom.Writer 334 | } 335 | 336 | func (self *SimpleH264Writer) prepare() (err error) { 337 | if self.mdatWriter, err = atom.WriteAtomHeader(self.W, "mdat"); err != nil { 338 | return 339 | } 340 | 341 | if len(self.sps) == 0 { 342 | err = fmt.Errorf("invalid sps") 343 | return 344 | } 345 | 346 | if len(self.pps) == 0 { 347 | err = fmt.Errorf("invalid pps") 348 | return 349 | } 350 | 351 | if self.Width == 0 || self.Height == 0 { 352 | var info *atom.H264spsInfo 353 | if info, err = atom.ParseH264sps(self.sps[1:]); err != nil { 354 | return 355 | } 356 | self.Width = int(info.Width) 357 | self.Height = int(info.Height) 358 | } 359 | 360 | self.sampleIdx = 1 361 | 362 | self.sample = &atom.SampleTable{ 363 | SampleDesc: &atom.SampleDesc{ 364 | Avc1Desc: &atom.Avc1Desc{ 365 | DataRefIdx: 1, 366 | HorizontalResolution: 72, 367 | VorizontalResolution: 72, 368 | Width: self.Width, 369 | Height: self.Height, 370 | FrameCount: 1, 371 | Depth: 24, 372 | ColorTableId: -1, 373 | Conf: &atom.Avc1Conf{}, 374 | }, 375 | }, 376 | TimeToSample: &atom.TimeToSample{}, 377 | SampleToChunk: &atom.SampleToChunk{ 378 | Entries: []atom.SampleToChunkEntry{ 379 | { 380 | FirstChunk: 1, 381 | SampleDescId: 1, 382 | }, 383 | }, 384 | }, 385 | SampleSize: &atom.SampleSize{}, 386 | ChunkOffset: &atom.ChunkOffset{ 387 | Entries: []int{8}, 388 | }, 389 | SyncSample: &atom.SyncSample{}, 390 | } 391 | self.sampleToChunk = &self.sample.SampleToChunk.Entries[0] 392 | 393 | return 394 | } 395 | 396 | func (self *SimpleH264Writer) WriteSample(sync bool, duration int, data []byte) (err error) { 397 | return self.writeSample(false, sync, duration, data) 398 | } 399 | 400 | func (self *SimpleH264Writer) WriteNALU(sync bool, duration int, data []byte) (err error) { 401 | return self.writeSample(true, sync, duration, data) 402 | } 403 | 404 | func splitNALUByStartCode(data []byte) (out [][]byte) { 405 | last := 0 406 | for i := 0; i < len(data)-3; { 407 | if data[i] == 0 && data[i+1] == 0 && data[i+2] == 1 { 408 | out = append(out, data[last:i]) 409 | i += 3 410 | last = i 411 | } else { 412 | i++ 413 | } 414 | } 415 | out = append(out, data[last:]) 416 | return 417 | } 418 | 419 | func (self *SimpleH264Writer) writeSample(isNALU, sync bool, duration int, data []byte) (err error) { 420 | if self.mdatWriter == nil { 421 | if err = self.prepare(); err != nil { 422 | return 423 | } 424 | } 425 | 426 | var sampleSize int 427 | 428 | if isNALU { 429 | if sampleSize, err = atom.WriteSampleByNALU(self.mdatWriter, data); err != nil { 430 | return 431 | } 432 | } else { 433 | sampleSize = len(data) 434 | if _, err = self.mdatWriter.Write(data); err != nil { 435 | return 436 | } 437 | } 438 | 439 | if sync { 440 | self.sample.SyncSample.Entries = append(self.sample.SyncSample.Entries, self.sampleIdx) 441 | } 442 | 443 | if self.timeToSample != nil && duration != self.timeToSample.Duration { 444 | self.sample.TimeToSample.Entries = append(self.sample.TimeToSample.Entries, *self.timeToSample) 445 | self.timeToSample = nil 446 | } 447 | if self.timeToSample == nil { 448 | self.timeToSample = &atom.TimeToSampleEntry{ 449 | Duration: duration, 450 | } 451 | } 452 | 453 | self.duration += duration 454 | self.sampleIdx++ 455 | self.timeToSample.Count++ 456 | self.sampleToChunk.SamplesPerChunk++ 457 | self.sample.SampleSize.Entries = append(self.sample.SampleSize.Entries, sampleSize) 458 | 459 | return 460 | } 461 | 462 | func (self *SimpleH264Writer) Finish() (err error) { 463 | self.sample.SampleDesc.Avc1Desc.Conf.Record, err = atom.CreateAVCDecoderConfRecord( 464 | self.sps, 465 | self.pps, 466 | ) 467 | if err != nil { 468 | return 469 | } 470 | 471 | if self.timeToSample != nil { 472 | self.sample.TimeToSample.Entries = append( 473 | self.sample.TimeToSample.Entries, 474 | *self.timeToSample, 475 | ) 476 | } 477 | 478 | if err = self.mdatWriter.Close(); err != nil { 479 | return 480 | } 481 | 482 | moov := &atom.Movie{} 483 | moov.Header = &atom.MovieHeader{ 484 | TimeScale: self.TimeScale, 485 | Duration: self.duration, 486 | PreferredRate: atom.IntToFixed(1), 487 | PreferredVolume: atom.IntToFixed(1), 488 | Matrix: [9]int{0x10000, 0, 0, 0, 0x10000, 0, 0, 0, 0x40000000}, 489 | NextTrackId: 2, 490 | } 491 | 492 | track := &atom.Track{ 493 | Header: &atom.TrackHeader{ 494 | TrackId: 1, 495 | Flags: 0x0003, // Track enabled | Track in movie 496 | Duration: self.duration, 497 | Volume: atom.IntToFixed(1), 498 | Matrix: [9]int{0x10000, 0, 0, 0, 0x10000, 0, 0, 0, 0x40000000}, 499 | TrackWidth: atom.IntToFixed(self.Width), 500 | TrackHeight: atom.IntToFixed(self.Height), 501 | }, 502 | 503 | Media: &atom.Media{ 504 | Header: &atom.MediaHeader{ 505 | TimeScale: self.TimeScale, 506 | Duration: self.duration, 507 | }, 508 | Info: &atom.MediaInfo{ 509 | Video: &atom.VideoMediaInfo{ 510 | Flags: 0x000001, 511 | }, 512 | Sample: self.sample, 513 | Data: &atom.DataInfo{ 514 | Refer: &atom.DataRefer{ 515 | Url: &atom.DataReferUrl{ 516 | Flags: 0x000001, // Self reference 517 | }, 518 | }, 519 | }, 520 | }, 521 | Handler: &atom.HandlerRefer{ 522 | SubType: "vide", 523 | Name: "Video Media Handler", 524 | }, 525 | }, 526 | } 527 | moov.Tracks = append(moov.Tracks, track) 528 | 529 | if err = atom.WriteMovie(self.W, moov); err != nil { 530 | return 531 | } 532 | 533 | return 534 | } 535 | */ 536 | 537 | -------------------------------------------------------------------------------- /track.go: -------------------------------------------------------------------------------- 1 | 2 | package mp4 3 | 4 | import ( 5 | "github.com/nareix/mp4/atom" 6 | "github.com/nareix/mp4/isom" 7 | "io" 8 | ) 9 | 10 | const ( 11 | H264 = 1 12 | AAC = 2 13 | ) 14 | 15 | type Track struct { 16 | Type int 17 | TrackAtom *atom.Track 18 | r io.ReadSeeker 19 | 20 | sps []byte 21 | pps []byte 22 | 23 | mpeg4AudioConfig isom.MPEG4AudioConfig 24 | 25 | sample *atom.SampleTable 26 | sampleIndex int 27 | 28 | sampleOffsetInChunk int64 29 | syncSampleIndex int 30 | 31 | dts int64 32 | sttsEntryIndex int 33 | sampleIndexInSttsEntry int 34 | 35 | cttsEntryIndex int 36 | sampleIndexInCttsEntry int 37 | 38 | chunkGroupIndex int 39 | chunkIndex int 40 | sampleIndexInChunk int 41 | 42 | sttsEntry *atom.TimeToSampleEntry 43 | cttsEntry *atom.CompositionOffsetEntry 44 | muxer *Muxer 45 | lastDts int64 46 | } 47 | 48 | --------------------------------------------------------------------------------