├── .gitignore ├── LICENSE ├── README.md ├── boxes.js ├── descriptor.js ├── index.js └── package.json /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .DS_Store 3 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Mathias Buus and John Hiesey 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![abstract-encoding](https://img.shields.io/badge/abstract--encoding-compliant-brightgreen.svg?style=flat)](https://github.com/mafintosh/abstract-encoding) 2 | 3 | # mp4-box-encoding 4 | 5 | This module provides encoders and decoders with the 6 | [abstract encoding](https://github.com/mafintosh/abstract-encoding) interface. 7 | 8 | The module exports the interface for a generic box, including all headers and 9 | children (for container boxes) Encodings for many leaf (non-container) boxes, 10 | without headers, is available keyed by the box type: 11 | 12 | ``` js 13 | var box = require('mp4-box-encoding') 14 | 15 | var buffer = fs.readFileSync('myvideo.mp4') 16 | // decode any box including headers 17 | // decode the entire moov box and its children 18 | var moov = box.decode(buffer.slice(24, 236989)) 19 | 20 | var moov.mfhd.mtime = new Date() // Change the modification time 21 | 22 | // now this is an encoding of the modified moov box 23 | var moofBuffer = box.encode(moov) 24 | 25 | // decode the contents of just the stts box 26 | var stts = box.decode(buffer.slice(609, 625)) 27 | ``` 28 | 29 | These encodings are factored out of [mp4-stream](https://github.com/mafintosh/mp4-stream). 30 | 31 | ## License 32 | 33 | MIT -------------------------------------------------------------------------------- /boxes.js: -------------------------------------------------------------------------------- 1 | // This is an intentionally recursive require. I don't like it either. 2 | var Box = require('./index') 3 | var Descriptor = require('./descriptor') 4 | var uint64be = require('uint64be') 5 | 6 | var TIME_OFFSET = 2082844800000 7 | 8 | /* 9 | TODO: 10 | test these 11 | add new box versions 12 | */ 13 | 14 | // These have 'version' and 'flags' fields in the headers 15 | exports.fullBoxes = {} 16 | var fullBoxes = [ 17 | 'mvhd', 18 | 'tkhd', 19 | 'mdhd', 20 | 'vmhd', 21 | 'smhd', 22 | 'stsd', 23 | 'esds', 24 | 'stsz', 25 | 'stco', 26 | 'co64', 27 | 'stss', 28 | 'stts', 29 | 'ctts', 30 | 'stsc', 31 | 'dref', 32 | 'elst', 33 | 'hdlr', 34 | 'mehd', 35 | 'trex', 36 | 'mfhd', 37 | 'tfhd', 38 | 'tfdt', 39 | 'trun' 40 | ] 41 | fullBoxes.forEach(function (type) { 42 | exports.fullBoxes[type] = true 43 | }) 44 | 45 | exports.ftyp = {} 46 | exports.ftyp.encode = function (box, buf, offset) { 47 | buf = buf ? buf.slice(offset) : Buffer.alloc(exports.ftyp.encodingLength(box)) 48 | var brands = box.compatibleBrands || [] 49 | buf.write(box.brand, 0, 4, 'ascii') 50 | buf.writeUInt32BE(box.brandVersion, 4) 51 | for (var i = 0; i < brands.length; i++) buf.write(brands[i], 8 + (i * 4), 4, 'ascii') 52 | exports.ftyp.encode.bytes = 8 + brands.length * 4 53 | return buf 54 | } 55 | exports.ftyp.decode = function (buf, offset) { 56 | buf = buf.slice(offset) 57 | var brand = buf.toString('ascii', 0, 4) 58 | var version = buf.readUInt32BE(4) 59 | var compatibleBrands = [] 60 | for (var i = 8; i < buf.length; i += 4) compatibleBrands.push(buf.toString('ascii', i, i + 4)) 61 | return { 62 | brand: brand, 63 | brandVersion: version, 64 | compatibleBrands: compatibleBrands 65 | } 66 | } 67 | exports.ftyp.encodingLength = function (box) { 68 | return 8 + (box.compatibleBrands || []).length * 4 69 | } 70 | 71 | exports.mvhd = {} 72 | exports.mvhd.encode = function (box, buf, offset) { 73 | buf = buf ? buf.slice(offset) : Buffer.alloc(96) 74 | writeDate(box.ctime || new Date(), buf, 0) 75 | writeDate(box.mtime || new Date(), buf, 4) 76 | buf.writeUInt32BE(box.timeScale || 0, 8) 77 | buf.writeUInt32BE(box.duration || 0, 12) 78 | writeFixed32(box.preferredRate || 0, buf, 16) 79 | writeFixed16(box.preferredVolume || 0, buf, 20) 80 | writeReserved(buf, 22, 32) 81 | writeMatrix(box.matrix, buf, 32) 82 | buf.writeUInt32BE(box.previewTime || 0, 68) 83 | buf.writeUInt32BE(box.previewDuration || 0, 72) 84 | buf.writeUInt32BE(box.posterTime || 0, 76) 85 | buf.writeUInt32BE(box.selectionTime || 0, 80) 86 | buf.writeUInt32BE(box.selectionDuration || 0, 84) 87 | buf.writeUInt32BE(box.currentTime || 0, 88) 88 | buf.writeUInt32BE(box.nextTrackId || 0, 92) 89 | exports.mvhd.encode.bytes = 96 90 | return buf 91 | } 92 | exports.mvhd.decode = function (buf, offset) { 93 | buf = buf.slice(offset) 94 | return { 95 | ctime: readDate(buf, 0), 96 | mtime: readDate(buf, 4), 97 | timeScale: buf.readUInt32BE(8), 98 | duration: buf.readUInt32BE(12), 99 | preferredRate: readFixed32(buf, 16), 100 | preferredVolume: readFixed16(buf, 20), 101 | matrix: readMatrix(buf.slice(32, 68)), 102 | previewTime: buf.readUInt32BE(68), 103 | previewDuration: buf.readUInt32BE(72), 104 | posterTime: buf.readUInt32BE(76), 105 | selectionTime: buf.readUInt32BE(80), 106 | selectionDuration: buf.readUInt32BE(84), 107 | currentTime: buf.readUInt32BE(88), 108 | nextTrackId: buf.readUInt32BE(92) 109 | } 110 | } 111 | exports.mvhd.encodingLength = function (box) { 112 | return 96 113 | } 114 | 115 | exports.tkhd = {} 116 | exports.tkhd.encode = function (box, buf, offset) { 117 | buf = buf ? buf.slice(offset) : Buffer.alloc(80) 118 | writeDate(box.ctime || new Date(), buf, 0) 119 | writeDate(box.mtime || new Date(), buf, 4) 120 | buf.writeUInt32BE(box.trackId || 0, 8) 121 | writeReserved(buf, 12, 16) 122 | buf.writeUInt32BE(box.duration || 0, 16) 123 | writeReserved(buf, 20, 28) 124 | buf.writeUInt16BE(box.layer || 0, 28) 125 | buf.writeUInt16BE(box.alternateGroup || 0, 30) 126 | buf.writeUInt16BE(box.volume || 0, 32) 127 | writeMatrix(box.matrix, buf, 36) 128 | buf.writeUInt32BE(box.trackWidth || 0, 72) 129 | buf.writeUInt32BE(box.trackHeight || 0, 76) 130 | exports.tkhd.encode.bytes = 80 131 | return buf 132 | } 133 | exports.tkhd.decode = function (buf, offset) { 134 | buf = buf.slice(offset) 135 | return { 136 | ctime: readDate(buf, 0), 137 | mtime: readDate(buf, 4), 138 | trackId: buf.readUInt32BE(8), 139 | duration: buf.readUInt32BE(16), 140 | layer: buf.readUInt16BE(28), 141 | alternateGroup: buf.readUInt16BE(30), 142 | volume: buf.readUInt16BE(32), 143 | matrix: readMatrix(buf.slice(36, 72)), 144 | trackWidth: buf.readUInt32BE(72), 145 | trackHeight: buf.readUInt32BE(76) 146 | } 147 | } 148 | exports.tkhd.encodingLength = function (box) { 149 | return 80 150 | } 151 | 152 | exports.mdhd = {} 153 | exports.mdhd.encode = function (box, buf, offset) { 154 | if (box.version === 1) { 155 | buf = buf ? buf.slice(offset) : Buffer.alloc(32) 156 | writeDate64(box.ctime || new Date(), buf, 0) 157 | writeDate64(box.mtime || new Date(), buf, 8) 158 | buf.writeUInt32BE(box.timeScale || 0, 16) 159 | // Node only supports integer <= 48bit. Waiting for BigInt! 160 | buf.writeUIntBE(box.duration || 0, 20, 6) 161 | buf.writeUInt16BE(box.language || 0, 28) 162 | buf.writeUInt16BE(box.quality || 0, 30) 163 | exports.mdhd.encode.bytes = 32 164 | return buf 165 | } 166 | 167 | buf = buf ? buf.slice(offset) : Buffer.alloc(20) 168 | writeDate(box.ctime || new Date(), buf, 0) 169 | writeDate(box.mtime || new Date(), buf, 4) 170 | buf.writeUInt32BE(box.timeScale || 0, 8) 171 | buf.writeUInt32BE(box.duration || 0, 12) 172 | buf.writeUInt16BE(box.language || 0, 16) 173 | buf.writeUInt16BE(box.quality || 0, 18) 174 | exports.mdhd.encode.bytes = 20 175 | return buf 176 | } 177 | 178 | exports.mdhd.decode = function (buf, offset, end) { 179 | buf = buf.slice(offset) 180 | 181 | var version1 = (end - offset) !== 20 182 | 183 | // In version 1 creation time and modification time are unsigned long 184 | if (version1) { 185 | return { 186 | ctime: readDate64(buf, 0), 187 | mtime: readDate64(buf, 8), 188 | timeScale: buf.readUInt32BE(16), 189 | // Node only supports integer <= 48bit. Waiting for BigInt! 190 | duration: buf.readUIntBE(20, 6), 191 | language: buf.readUInt16BE(28), 192 | quality: buf.readUInt16BE(30) 193 | } 194 | } 195 | 196 | return { 197 | ctime: readDate(buf, 0), 198 | mtime: readDate(buf, 4), 199 | timeScale: buf.readUInt32BE(8), 200 | duration: buf.readUInt32BE(12), 201 | language: buf.readUInt16BE(16), 202 | quality: buf.readUInt16BE(18) 203 | } 204 | } 205 | exports.mdhd.encodingLength = function (box) { 206 | if (box.version === 1) return 32 207 | 208 | return 20 209 | } 210 | 211 | exports.vmhd = {} 212 | exports.vmhd.encode = function (box, buf, offset) { 213 | buf = buf ? buf.slice(offset) : Buffer.alloc(8) 214 | buf.writeUInt16BE(box.graphicsMode || 0, 0) 215 | var opcolor = box.opcolor || [0, 0, 0] 216 | buf.writeUInt16BE(opcolor[0], 2) 217 | buf.writeUInt16BE(opcolor[1], 4) 218 | buf.writeUInt16BE(opcolor[2], 6) 219 | exports.vmhd.encode.bytes = 8 220 | return buf 221 | } 222 | exports.vmhd.decode = function (buf, offset) { 223 | buf = buf.slice(offset) 224 | return { 225 | graphicsMode: buf.readUInt16BE(0), 226 | opcolor: [buf.readUInt16BE(2), buf.readUInt16BE(4), buf.readUInt16BE(6)] 227 | } 228 | } 229 | exports.vmhd.encodingLength = function (box) { 230 | return 8 231 | } 232 | 233 | exports.smhd = {} 234 | exports.smhd.encode = function (box, buf, offset) { 235 | buf = buf ? buf.slice(offset) : Buffer.alloc(4) 236 | buf.writeUInt16BE(box.balance || 0, 0) 237 | writeReserved(buf, 2, 4) 238 | exports.smhd.encode.bytes = 4 239 | return buf 240 | } 241 | exports.smhd.decode = function (buf, offset) { 242 | buf = buf.slice(offset) 243 | return { 244 | balance: buf.readUInt16BE(0) 245 | } 246 | } 247 | exports.smhd.encodingLength = function (box) { 248 | return 4 249 | } 250 | 251 | exports.stsd = {} 252 | exports.stsd.encode = function (box, buf, offset) { 253 | buf = buf ? buf.slice(offset) : Buffer.alloc(exports.stsd.encodingLength(box)) 254 | var entries = box.entries || [] 255 | 256 | buf.writeUInt32BE(entries.length, 0) 257 | 258 | var ptr = 4 259 | for (var i = 0; i < entries.length; i++) { 260 | var entry = entries[i] 261 | Box.encode(entry, buf, ptr) 262 | ptr += Box.encode.bytes 263 | } 264 | 265 | exports.stsd.encode.bytes = ptr 266 | return buf 267 | } 268 | exports.stsd.decode = function (buf, offset, end) { 269 | buf = buf.slice(offset) 270 | var num = buf.readUInt32BE(0) 271 | var entries = new Array(num) 272 | var ptr = 4 273 | 274 | for (var i = 0; i < num; i++) { 275 | var entry = Box.decode(buf, ptr, end) 276 | entries[i] = entry 277 | ptr += entry.length 278 | } 279 | 280 | return { 281 | entries: entries 282 | } 283 | } 284 | exports.stsd.encodingLength = function (box) { 285 | var totalSize = 4 286 | if (!box.entries) return totalSize 287 | for (var i = 0; i < box.entries.length; i++) { 288 | totalSize += Box.encodingLength(box.entries[i]) 289 | } 290 | return totalSize 291 | } 292 | 293 | exports.avc1 = exports.VisualSampleEntry = {} 294 | exports.VisualSampleEntry.encode = function (box, buf, offset) { 295 | buf = buf ? buf.slice(offset) : Buffer.alloc(exports.VisualSampleEntry.encodingLength(box)) 296 | 297 | writeReserved(buf, 0, 6) 298 | buf.writeUInt16BE(box.dataReferenceIndex || 0, 6) 299 | writeReserved(buf, 8, 24) 300 | buf.writeUInt16BE(box.width || 0, 24) 301 | buf.writeUInt16BE(box.height || 0, 26) 302 | buf.writeUInt32BE(box.hResolution || 0x480000, 28) 303 | buf.writeUInt32BE(box.vResolution || 0x480000, 32) 304 | writeReserved(buf, 36, 40) 305 | buf.writeUInt16BE(box.frameCount || 1, 40) 306 | var compressorName = box.compressorName || '' 307 | var nameLen = Math.min(compressorName.length, 31) 308 | buf.writeUInt8(nameLen, 42) 309 | buf.write(compressorName, 43, nameLen, 'utf8') 310 | buf.writeUInt16BE(box.depth || 0x18, 74) 311 | buf.writeInt16BE(-1, 76) 312 | 313 | var ptr = 78 314 | var children = box.children || [] 315 | children.forEach(function (child) { 316 | Box.encode(child, buf, ptr) 317 | ptr += Box.encode.bytes 318 | }) 319 | exports.VisualSampleEntry.encode.bytes = ptr 320 | } 321 | exports.VisualSampleEntry.decode = function (buf, offset, end) { 322 | buf = buf.slice(offset) 323 | var length = end - offset 324 | var nameLen = Math.min(buf.readUInt8(42), 31) 325 | var box = { 326 | dataReferenceIndex: buf.readUInt16BE(6), 327 | width: buf.readUInt16BE(24), 328 | height: buf.readUInt16BE(26), 329 | hResolution: buf.readUInt32BE(28), 330 | vResolution: buf.readUInt32BE(32), 331 | frameCount: buf.readUInt16BE(40), 332 | compressorName: buf.toString('utf8', 43, 43 + nameLen), 333 | depth: buf.readUInt16BE(74), 334 | children: [] 335 | } 336 | 337 | var ptr = 78 338 | while (length - ptr >= 8) { 339 | var child = Box.decode(buf, ptr, length) 340 | box.children.push(child) 341 | box[child.type] = child 342 | ptr += child.length 343 | } 344 | 345 | return box 346 | } 347 | exports.VisualSampleEntry.encodingLength = function (box) { 348 | var len = 78 349 | var children = box.children || [] 350 | children.forEach(function (child) { 351 | len += Box.encodingLength(child) 352 | }) 353 | return len 354 | } 355 | 356 | exports.avcC = {} 357 | exports.avcC.encode = function (box, buf, offset) { 358 | buf = buf ? buf.slice(offset) : Buffer.alloc(box.buffer.length) 359 | 360 | box.buffer.copy(buf) 361 | exports.avcC.encode.bytes = box.buffer.length 362 | } 363 | exports.avcC.decode = function (buf, offset, end) { 364 | buf = buf.slice(offset, end) 365 | 366 | return { 367 | mimeCodec: buf.toString('hex', 1, 4), 368 | buffer: Buffer.from(buf) 369 | } 370 | } 371 | exports.avcC.encodingLength = function (box) { 372 | return box.buffer.length 373 | } 374 | 375 | exports.mp4a = exports.AudioSampleEntry = {} 376 | exports.AudioSampleEntry.encode = function (box, buf, offset) { 377 | buf = buf ? buf.slice(offset) : Buffer.alloc(exports.AudioSampleEntry.encodingLength(box)) 378 | 379 | writeReserved(buf, 0, 6) 380 | buf.writeUInt16BE(box.dataReferenceIndex || 0, 6) 381 | writeReserved(buf, 8, 16) 382 | buf.writeUInt16BE(box.channelCount || 2, 16) 383 | buf.writeUInt16BE(box.sampleSize || 16, 18) 384 | writeReserved(buf, 20, 24) 385 | buf.writeUInt32BE(box.sampleRate || 0, 24) 386 | 387 | var ptr = 28 388 | var children = box.children || [] 389 | children.forEach(function (child) { 390 | Box.encode(child, buf, ptr) 391 | ptr += Box.encode.bytes 392 | }) 393 | exports.AudioSampleEntry.encode.bytes = ptr 394 | } 395 | exports.AudioSampleEntry.decode = function (buf, offset, end) { 396 | buf = buf.slice(offset, end) 397 | var length = end - offset 398 | var box = { 399 | dataReferenceIndex: buf.readUInt16BE(6), 400 | channelCount: buf.readUInt16BE(16), 401 | sampleSize: buf.readUInt16BE(18), 402 | sampleRate: buf.readUInt32BE(24), 403 | children: [] 404 | } 405 | 406 | var ptr = 28 407 | while (length - ptr >= 8) { 408 | var child = Box.decode(buf, ptr, length) 409 | box.children.push(child) 410 | box[child.type] = child 411 | ptr += child.length 412 | } 413 | 414 | return box 415 | } 416 | exports.AudioSampleEntry.encodingLength = function (box) { 417 | var len = 28 418 | var children = box.children || [] 419 | children.forEach(function (child) { 420 | len += Box.encodingLength(child) 421 | }) 422 | return len 423 | } 424 | 425 | exports.esds = {} 426 | exports.esds.encode = function (box, buf, offset) { 427 | buf = buf ? buf.slice(offset) : Buffer.alloc(box.buffer.length) 428 | 429 | box.buffer.copy(buf, 0) 430 | exports.esds.encode.bytes = box.buffer.length 431 | } 432 | exports.esds.decode = function (buf, offset, end) { 433 | buf = buf.slice(offset, end) 434 | 435 | var desc = Descriptor.Descriptor.decode(buf, 0, buf.length) 436 | var esd = (desc.tagName === 'ESDescriptor') ? desc : {} 437 | var dcd = esd.DecoderConfigDescriptor || {} 438 | var oti = dcd.oti || 0 439 | var dsi = dcd.DecoderSpecificInfo 440 | var audioConfig = dsi ? (dsi.buffer.readUInt8(0) & 0xf8) >> 3 : 0 441 | 442 | var mimeCodec = null 443 | if (oti) { 444 | mimeCodec = oti.toString(16) 445 | if (audioConfig) { 446 | mimeCodec += '.' + audioConfig 447 | } 448 | } 449 | 450 | return { 451 | mimeCodec: mimeCodec, 452 | buffer: Buffer.from(buf.slice(0)) 453 | } 454 | } 455 | exports.esds.encodingLength = function (box) { 456 | return box.buffer.length 457 | } 458 | 459 | // TODO: integrate the two versions in a saner way 460 | exports.stsz = {} 461 | exports.stsz.encode = function (box, buf, offset) { 462 | var entries = box.entries || [] 463 | buf = buf ? buf.slice(offset) : Buffer.alloc(exports.stsz.encodingLength(box)) 464 | 465 | buf.writeUInt32BE(0, 0) 466 | buf.writeUInt32BE(entries.length, 4) 467 | 468 | for (var i = 0; i < entries.length; i++) { 469 | buf.writeUInt32BE(entries[i], i * 4 + 8) 470 | } 471 | 472 | exports.stsz.encode.bytes = 8 + entries.length * 4 473 | return buf 474 | } 475 | exports.stsz.decode = function (buf, offset) { 476 | buf = buf.slice(offset) 477 | var size = buf.readUInt32BE(0) 478 | var num = buf.readUInt32BE(4) 479 | var entries = new Array(num) 480 | 481 | for (var i = 0; i < num; i++) { 482 | if (size === 0) { 483 | entries[i] = buf.readUInt32BE(i * 4 + 8) 484 | } else { 485 | entries[i] = size 486 | } 487 | } 488 | 489 | return { 490 | entries: entries 491 | } 492 | } 493 | exports.stsz.encodingLength = function (box) { 494 | return 8 + box.entries.length * 4 495 | } 496 | 497 | exports.stss = 498 | exports.stco = {} 499 | exports.stco.encode = function (box, buf, offset) { 500 | var entries = box.entries || [] 501 | buf = buf ? buf.slice(offset) : Buffer.alloc(exports.stco.encodingLength(box)) 502 | 503 | buf.writeUInt32BE(entries.length, 0) 504 | 505 | for (var i = 0; i < entries.length; i++) { 506 | buf.writeUInt32BE(entries[i], i * 4 + 4) 507 | } 508 | 509 | exports.stco.encode.bytes = 4 + entries.length * 4 510 | return buf 511 | } 512 | exports.stco.decode = function (buf, offset) { 513 | buf = buf.slice(offset) 514 | var num = buf.readUInt32BE(0) 515 | var entries = new Array(num) 516 | 517 | for (var i = 0; i < num; i++) { 518 | entries[i] = buf.readUInt32BE(i * 4 + 4) 519 | } 520 | 521 | return { 522 | entries: entries 523 | } 524 | } 525 | exports.stco.encodingLength = function (box) { 526 | return 4 + box.entries.length * 4 527 | } 528 | 529 | exports.co64 = {} 530 | exports.co64.encode = function (box, buf, offset) { 531 | var entries = box.entries || [] 532 | buf = buf ? buf.slice(offset) : Buffer.alloc(exports.co64.encodingLength(box)) 533 | 534 | buf.writeUInt32BE(entries.length, 0) 535 | 536 | for (var i = 0; i < entries.length; i++) { 537 | uint64be.encode(entries[i], buf, i * 8 + 4) 538 | } 539 | 540 | exports.co64.encode.bytes = 4 + entries.length * 8 541 | return buf 542 | } 543 | exports.co64.decode = function (buf, offset) { 544 | buf = buf.slice(offset) 545 | var num = buf.readUInt32BE(0) 546 | var entries = new Array(num) 547 | 548 | for (var i = 0; i < num; i++) { 549 | entries[i] = uint64be.decode(buf, i * 8 + 4) 550 | } 551 | 552 | return { 553 | entries: entries 554 | } 555 | } 556 | exports.co64.encodingLength = function (box) { 557 | return 4 + box.entries.length * 8 558 | } 559 | 560 | exports.stts = {} 561 | exports.stts.encode = function (box, buf, offset) { 562 | var entries = box.entries || [] 563 | buf = buf ? buf.slice(offset) : Buffer.alloc(exports.stts.encodingLength(box)) 564 | 565 | buf.writeUInt32BE(entries.length, 0) 566 | 567 | for (var i = 0; i < entries.length; i++) { 568 | var ptr = i * 8 + 4 569 | buf.writeUInt32BE(entries[i].count || 0, ptr) 570 | buf.writeUInt32BE(entries[i].duration || 0, ptr + 4) 571 | } 572 | 573 | exports.stts.encode.bytes = 4 + box.entries.length * 8 574 | return buf 575 | } 576 | exports.stts.decode = function (buf, offset) { 577 | buf = buf.slice(offset) 578 | var num = buf.readUInt32BE(0) 579 | var entries = new Array(num) 580 | 581 | for (var i = 0; i < num; i++) { 582 | var ptr = i * 8 + 4 583 | entries[i] = { 584 | count: buf.readUInt32BE(ptr), 585 | duration: buf.readUInt32BE(ptr + 4) 586 | } 587 | } 588 | 589 | return { 590 | entries: entries 591 | } 592 | } 593 | exports.stts.encodingLength = function (box) { 594 | return 4 + box.entries.length * 8 595 | } 596 | 597 | exports.ctts = {} 598 | exports.ctts.encode = function (box, buf, offset) { 599 | var entries = box.entries || [] 600 | buf = buf ? buf.slice(offset) : Buffer.alloc(exports.ctts.encodingLength(box)) 601 | 602 | buf.writeUInt32BE(entries.length, 0) 603 | 604 | for (var i = 0; i < entries.length; i++) { 605 | var ptr = i * 8 + 4 606 | buf.writeUInt32BE(entries[i].count || 0, ptr) 607 | buf.writeUInt32BE(entries[i].compositionOffset || 0, ptr + 4) 608 | } 609 | 610 | exports.ctts.encode.bytes = 4 + entries.length * 8 611 | return buf 612 | } 613 | exports.ctts.decode = function (buf, offset) { 614 | buf = buf.slice(offset) 615 | var num = buf.readUInt32BE(0) 616 | var entries = new Array(num) 617 | 618 | for (var i = 0; i < num; i++) { 619 | var ptr = i * 8 + 4 620 | entries[i] = { 621 | count: buf.readUInt32BE(ptr), 622 | compositionOffset: buf.readInt32BE(ptr + 4) 623 | } 624 | } 625 | 626 | return { 627 | entries: entries 628 | } 629 | } 630 | exports.ctts.encodingLength = function (box) { 631 | return 4 + box.entries.length * 8 632 | } 633 | 634 | exports.stsc = {} 635 | exports.stsc.encode = function (box, buf, offset) { 636 | var entries = box.entries || [] 637 | buf = buf ? buf.slice(offset) : Buffer.alloc(exports.stsc.encodingLength(box)) 638 | 639 | buf.writeUInt32BE(entries.length, 0) 640 | 641 | for (var i = 0; i < entries.length; i++) { 642 | var ptr = i * 12 + 4 643 | buf.writeUInt32BE(entries[i].firstChunk || 0, ptr) 644 | buf.writeUInt32BE(entries[i].samplesPerChunk || 0, ptr + 4) 645 | buf.writeUInt32BE(entries[i].sampleDescriptionId || 0, ptr + 8) 646 | } 647 | 648 | exports.stsc.encode.bytes = 4 + entries.length * 12 649 | return buf 650 | } 651 | exports.stsc.decode = function (buf, offset) { 652 | buf = buf.slice(offset) 653 | var num = buf.readUInt32BE(0) 654 | var entries = new Array(num) 655 | 656 | for (var i = 0; i < num; i++) { 657 | var ptr = i * 12 + 4 658 | entries[i] = { 659 | firstChunk: buf.readUInt32BE(ptr), 660 | samplesPerChunk: buf.readUInt32BE(ptr + 4), 661 | sampleDescriptionId: buf.readUInt32BE(ptr + 8) 662 | } 663 | } 664 | 665 | return { 666 | entries: entries 667 | } 668 | } 669 | exports.stsc.encodingLength = function (box) { 670 | return 4 + box.entries.length * 12 671 | } 672 | 673 | exports.dref = {} 674 | exports.dref.encode = function (box, buf, offset) { 675 | buf = buf ? buf.slice(offset) : Buffer.alloc(exports.dref.encodingLength(box)) 676 | var entries = box.entries || [] 677 | 678 | buf.writeUInt32BE(entries.length, 0) 679 | 680 | var ptr = 4 681 | for (var i = 0; i < entries.length; i++) { 682 | var entry = entries[i] 683 | var size = (entry.buf ? entry.buf.length : 0) + 4 + 4 684 | 685 | buf.writeUInt32BE(size, ptr) 686 | ptr += 4 687 | 688 | buf.write(entry.type, ptr, 4, 'ascii') 689 | ptr += 4 690 | 691 | if (entry.buf) { 692 | entry.buf.copy(buf, ptr) 693 | ptr += entry.buf.length 694 | } 695 | } 696 | 697 | exports.dref.encode.bytes = ptr 698 | return buf 699 | } 700 | exports.dref.decode = function (buf, offset) { 701 | buf = buf.slice(offset) 702 | var num = buf.readUInt32BE(0) 703 | var entries = new Array(num) 704 | var ptr = 4 705 | 706 | for (var i = 0; i < num; i++) { 707 | var size = buf.readUInt32BE(ptr) 708 | var type = buf.toString('ascii', ptr + 4, ptr + 8) 709 | var tmp = buf.slice(ptr + 8, ptr + size) 710 | ptr += size 711 | 712 | entries[i] = { 713 | type: type, 714 | buf: tmp 715 | } 716 | } 717 | 718 | return { 719 | entries: entries 720 | } 721 | } 722 | exports.dref.encodingLength = function (box) { 723 | var totalSize = 4 724 | if (!box.entries) return totalSize 725 | for (var i = 0; i < box.entries.length; i++) { 726 | var buf = box.entries[i].buf 727 | totalSize += (buf ? buf.length : 0) + 4 + 4 728 | } 729 | return totalSize 730 | } 731 | 732 | exports.elst = {} 733 | exports.elst.encode = function (box, buf, offset) { 734 | var entries = box.entries || [] 735 | buf = buf ? buf.slice(offset) : Buffer.alloc(exports.elst.encodingLength(box)) 736 | 737 | buf.writeUInt32BE(entries.length, 0) 738 | 739 | for (var i = 0; i < entries.length; i++) { 740 | var ptr = i * 12 + 4 741 | buf.writeUInt32BE(entries[i].trackDuration || 0, ptr) 742 | buf.writeUInt32BE(entries[i].mediaTime || 0, ptr + 4) 743 | writeFixed32(entries[i].mediaRate || 0, buf, ptr + 8) 744 | } 745 | 746 | exports.elst.encode.bytes = 4 + entries.length * 12 747 | return buf 748 | } 749 | exports.elst.decode = function (buf, offset) { 750 | buf = buf.slice(offset) 751 | var num = buf.readUInt32BE(0) 752 | var entries = new Array(num) 753 | 754 | for (var i = 0; i < num; i++) { 755 | var ptr = i * 12 + 4 756 | entries[i] = { 757 | trackDuration: buf.readUInt32BE(ptr), 758 | mediaTime: buf.readInt32BE(ptr + 4), 759 | mediaRate: readFixed32(buf, ptr + 8) 760 | } 761 | } 762 | 763 | return { 764 | entries: entries 765 | } 766 | } 767 | exports.elst.encodingLength = function (box) { 768 | return 4 + box.entries.length * 12 769 | } 770 | 771 | exports.hdlr = {} 772 | exports.hdlr.encode = function (box, buf, offset) { 773 | buf = buf ? buf.slice(offset) : Buffer.alloc(exports.hdlr.encodingLength(box)) 774 | 775 | var len = 21 + (box.name || '').length 776 | buf.fill(0, 0, len) 777 | 778 | buf.write(box.handlerType || '', 4, 4, 'ascii') 779 | writeString(box.name || '', buf, 20) 780 | 781 | exports.hdlr.encode.bytes = len 782 | return buf 783 | } 784 | exports.hdlr.decode = function (buf, offset, end) { 785 | buf = buf.slice(offset) 786 | return { 787 | handlerType: buf.toString('ascii', 4, 8), 788 | name: readString(buf, 20, end) 789 | } 790 | } 791 | exports.hdlr.encodingLength = function (box) { 792 | return 21 + (box.name || '').length 793 | } 794 | 795 | exports.mehd = {} 796 | exports.mehd.encode = function (box, buf, offset) { 797 | buf = buf ? buf.slice(offset) : Buffer.alloc(4) 798 | 799 | buf.writeUInt32BE(box.fragmentDuration || 0, 0) 800 | exports.mehd.encode.bytes = 4 801 | return buf 802 | } 803 | exports.mehd.decode = function (buf, offset) { 804 | buf = buf.slice(offset) 805 | return { 806 | fragmentDuration: buf.readUInt32BE(0) 807 | } 808 | } 809 | exports.mehd.encodingLength = function (box) { 810 | return 4 811 | } 812 | 813 | exports.trex = {} 814 | exports.trex.encode = function (box, buf, offset) { 815 | buf = buf ? buf.slice(offset) : Buffer.alloc(20) 816 | 817 | buf.writeUInt32BE(box.trackId || 0, 0) 818 | buf.writeUInt32BE(box.defaultSampleDescriptionIndex || 0, 4) 819 | buf.writeUInt32BE(box.defaultSampleDuration || 0, 8) 820 | buf.writeUInt32BE(box.defaultSampleSize || 0, 12) 821 | buf.writeUInt32BE(box.defaultSampleFlags || 0, 16) 822 | exports.trex.encode.bytes = 20 823 | return buf 824 | } 825 | exports.trex.decode = function (buf, offset) { 826 | buf = buf.slice(offset) 827 | return { 828 | trackId: buf.readUInt32BE(0), 829 | defaultSampleDescriptionIndex: buf.readUInt32BE(4), 830 | defaultSampleDuration: buf.readUInt32BE(8), 831 | defaultSampleSize: buf.readUInt32BE(12), 832 | defaultSampleFlags: buf.readUInt32BE(16) 833 | } 834 | } 835 | exports.trex.encodingLength = function (box) { 836 | return 20 837 | } 838 | 839 | exports.mfhd = {} 840 | exports.mfhd.encode = function (box, buf, offset) { 841 | buf = buf ? buf.slice(offset) : Buffer.alloc(4) 842 | 843 | buf.writeUInt32BE(box.sequenceNumber || 0, 0) 844 | exports.mfhd.encode.bytes = 4 845 | return buf 846 | } 847 | exports.mfhd.decode = function (buf, offset) { 848 | return { 849 | sequenceNumber: buf.readUInt32BE(0) 850 | } 851 | } 852 | exports.mfhd.encodingLength = function (box) { 853 | return 4 854 | } 855 | 856 | exports.tfhd = {} 857 | exports.tfhd.encode = function (box, buf, offset) { 858 | buf = buf ? buf.slice(offset) : Buffer.alloc(4) 859 | buf.writeUInt32BE(box.trackId, 0) 860 | exports.tfhd.encode.bytes = 4 861 | return buf 862 | } 863 | exports.tfhd.decode = function (buf, offset) { 864 | // TODO: this 865 | } 866 | exports.tfhd.encodingLength = function (box) { 867 | // TODO: this is wrong! 868 | return 4 869 | } 870 | 871 | exports.tfdt = {} 872 | exports.tfdt.encode = function (box, buf, offset) { 873 | buf = buf ? buf.slice(offset) : Buffer.alloc(4) 874 | 875 | buf.writeUInt32BE(box.baseMediaDecodeTime || 0, 0) 876 | exports.tfdt.encode.bytes = 4 877 | return buf 878 | } 879 | exports.tfdt.decode = function (buf, offset) { 880 | // TODO: this 881 | } 882 | exports.tfdt.encodingLength = function (box) { 883 | return 4 884 | } 885 | 886 | exports.trun = {} 887 | exports.trun.encode = function (box, buf, offset) { 888 | buf = buf ? buf.slice(offset) : Buffer.alloc(8 + box.entries.length * 16) 889 | 890 | // TODO: this is wrong 891 | buf.writeUInt32BE(box.entries.length, 0) 892 | buf.writeInt32BE(box.dataOffset, 4) 893 | var ptr = 8 894 | for (var i = 0; i < box.entries.length; i++) { 895 | var entry = box.entries[i] 896 | buf.writeUInt32BE(entry.sampleDuration, ptr) 897 | ptr += 4 898 | 899 | buf.writeUInt32BE(entry.sampleSize, ptr) 900 | ptr += 4 901 | 902 | buf.writeUInt32BE(entry.sampleFlags, ptr) 903 | ptr += 4 904 | 905 | if ((box.version || 0) === 0) { 906 | buf.writeUInt32BE(entry.sampleCompositionTimeOffset, ptr) 907 | } else { 908 | buf.writeInt32BE(entry.sampleCompositionTimeOffset, ptr) 909 | } 910 | ptr += 4 911 | } 912 | exports.trun.encode.bytes = ptr 913 | } 914 | exports.trun.decode = function (buf, offset) { 915 | // TODO: this 916 | } 917 | exports.trun.encodingLength = function (box) { 918 | // TODO: this is wrong 919 | return 8 + box.entries.length * 16 920 | } 921 | 922 | exports.mdat = {} 923 | exports.mdat.encode = function (box, buf, offset) { 924 | if (box.buffer) { 925 | box.buffer.copy(buf, offset) 926 | exports.mdat.encode.bytes = box.buffer.length 927 | } else { 928 | exports.mdat.encode.bytes = exports.mdat.encodingLength(box) 929 | } 930 | } 931 | exports.mdat.decode = function (buf, start, end) { 932 | return { 933 | buffer: Buffer.from(buf.slice(start, end)) 934 | } 935 | } 936 | exports.mdat.encodingLength = function (box) { 937 | return box.buffer ? box.buffer.length : box.contentLength 938 | } 939 | 940 | function writeReserved (buf, offset, end) { 941 | for (var i = offset; i < end; i++) buf[i] = 0 942 | } 943 | 944 | function writeDate (date, buf, offset) { 945 | buf.writeUInt32BE(Math.floor((date.getTime() + TIME_OFFSET) / 1000), offset) 946 | } 947 | 948 | function writeDate64 (date, buf, offset) { 949 | // Node only supports integer <= 48bit. Waiting for BigInt! 950 | buf.writeUIntBE(Math.floor((date.getTime() + TIME_OFFSET) / 1000), offset, 6) 951 | } 952 | 953 | // TODO: think something is wrong here 954 | function writeFixed32 (num, buf, offset) { 955 | buf.writeUInt16BE(Math.floor(num) % (256 * 256), offset) 956 | buf.writeUInt16BE(Math.floor(num * 256 * 256) % (256 * 256), offset + 2) 957 | } 958 | 959 | function writeFixed16 (num, buf, offset) { 960 | buf[offset] = Math.floor(num) % 256 961 | buf[offset + 1] = Math.floor(num * 256) % 256 962 | } 963 | 964 | function writeMatrix (list, buf, offset) { 965 | if (!list) list = [0, 0, 0, 0, 0, 0, 0, 0, 0] 966 | for (var i = 0; i < list.length; i++) { 967 | writeFixed32(list[i], buf, offset + i * 4) 968 | } 969 | } 970 | 971 | function writeString (str, buf, offset) { 972 | var strBuffer = Buffer.from(str, 'utf8') 973 | strBuffer.copy(buf, offset) 974 | buf[offset + strBuffer.length] = 0 975 | } 976 | 977 | function readMatrix (buf) { 978 | var list = new Array(buf.length / 4) 979 | for (var i = 0; i < list.length; i++) list[i] = readFixed32(buf, i * 4) 980 | return list 981 | } 982 | 983 | function readDate64 (buf, offset) { 984 | // Node only supports integer <= 48bit. Waiting for BigInt! 985 | return new Date(buf.readUIntBE(offset, 6) * 1000 - TIME_OFFSET) 986 | } 987 | 988 | function readDate (buf, offset) { 989 | return new Date(buf.readUInt32BE(offset) * 1000 - TIME_OFFSET) 990 | } 991 | 992 | function readFixed32 (buf, offset) { 993 | return buf.readUInt16BE(offset) + buf.readUInt16BE(offset + 2) / (256 * 256) 994 | } 995 | 996 | function readFixed16 (buf, offset) { 997 | return buf[offset] + buf[offset + 1] / 256 998 | } 999 | 1000 | function readString (buf, offset, length) { 1001 | var i 1002 | for (i = 0; i < length; i++) { 1003 | if (buf[offset + i] === 0) { 1004 | break 1005 | } 1006 | } 1007 | return buf.toString('utf8', offset, offset + i) 1008 | } 1009 | -------------------------------------------------------------------------------- /descriptor.js: -------------------------------------------------------------------------------- 1 | var tagToName = { 2 | 0x03: 'ESDescriptor', 3 | 0x04: 'DecoderConfigDescriptor', 4 | 0x05: 'DecoderSpecificInfo', 5 | 0x06: 'SLConfigDescriptor' 6 | } 7 | 8 | exports.Descriptor = {} 9 | exports.Descriptor.decode = function (buf, start, end) { 10 | var tag = buf.readUInt8(start) 11 | var ptr = start + 1 12 | var lenByte 13 | var len = 0 14 | do { 15 | lenByte = buf.readUInt8(ptr++) 16 | len = (len << 7) | (lenByte & 0x7f) 17 | } while (lenByte & 0x80) 18 | 19 | var obj 20 | var tagName = tagToName[tag] // May be undefined; that's ok 21 | if (exports[tagName]) { 22 | obj = exports[tagName].decode(buf, ptr, end) 23 | } else { 24 | obj = { 25 | buffer: Buffer.from(buf.slice(ptr, ptr + len)) 26 | } 27 | } 28 | 29 | obj.tag = tag 30 | obj.tagName = tagName 31 | obj.length = (ptr - start) + len 32 | obj.contentsLen = len 33 | return obj 34 | } 35 | 36 | exports.DescriptorArray = {} 37 | exports.DescriptorArray.decode = function (buf, start, end) { 38 | var ptr = start 39 | var obj = {} 40 | while (ptr + 2 <= end) { 41 | var descriptor = exports.Descriptor.decode(buf, ptr, end) 42 | ptr += descriptor.length 43 | var tagName = tagToName[descriptor.tag] || ('Descriptor' + descriptor.tag) 44 | obj[tagName] = descriptor 45 | } 46 | return obj 47 | } 48 | 49 | exports.ESDescriptor = {} 50 | exports.ESDescriptor.decode = function (buf, start, end) { 51 | var flags = buf.readUInt8(start + 2) 52 | var ptr = start + 3 53 | if (flags & 0x80) { 54 | ptr += 2 55 | } 56 | if (flags & 0x40) { 57 | var len = buf.readUInt8(ptr) 58 | ptr += len + 1 59 | } 60 | if (flags & 0x20) { 61 | ptr += 2 62 | } 63 | return exports.DescriptorArray.decode(buf, ptr, end) 64 | } 65 | 66 | exports.DecoderConfigDescriptor = {} 67 | exports.DecoderConfigDescriptor.decode = function (buf, start, end) { 68 | var oti = buf.readUInt8(start) 69 | var obj = exports.DescriptorArray.decode(buf, start + 13, end) 70 | obj.oti = oti 71 | return obj 72 | } 73 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | // var assert = require('assert') 2 | var uint64be = require('uint64be') 3 | 4 | var boxes = require('./boxes') 5 | 6 | var UINT32_MAX = 4294967295 7 | 8 | var Box = exports 9 | 10 | /* 11 | * Lists the proper order for boxes inside containers. 12 | * Five-character names ending in 's' indicate arrays instead of single elements. 13 | */ 14 | var containers = exports.containers = { 15 | 'moov': ['mvhd', 'meta', 'traks', 'mvex'], 16 | 'trak': ['tkhd', 'tref', 'trgr', 'edts', 'meta', 'mdia', 'udta'], 17 | 'edts': ['elst'], 18 | 'mdia': ['mdhd', 'hdlr', 'elng', 'minf'], 19 | 'minf': ['vmhd', 'smhd', 'hmhd', 'sthd', 'nmhd', 'dinf', 'stbl'], 20 | 'dinf': ['dref'], 21 | 'stbl': ['stsd', 'stts', 'ctts', 'cslg', 'stsc', 'stsz', 'stz2', 'stco', 'co64', 'stss', 'stsh', 'padb', 'stdp', 'sdtp', 'sbgps', 'sgpds', 'subss', 'saizs', 'saios'], 22 | 'mvex': ['mehd', 'trexs', 'leva'], 23 | 'moof': ['mfhd', 'meta', 'trafs'], 24 | 'traf': ['tfhd', 'tfdt', 'trun', 'sbgps', 'sgpds', 'subss', 'saizs', 'saios', 'meta'] 25 | } 26 | 27 | Box.encode = function (obj, buffer, offset) { 28 | Box.encodingLength(obj) // sets every level appropriately 29 | offset = offset || 0 30 | buffer = buffer || Buffer.alloc(obj.length) 31 | return Box._encode(obj, buffer, offset) 32 | } 33 | 34 | Box._encode = function (obj, buffer, offset) { 35 | var type = obj.type 36 | var len = obj.length 37 | if (len > UINT32_MAX) { 38 | len = 1 39 | } 40 | buffer.writeUInt32BE(len, offset) 41 | buffer.write(obj.type, offset + 4, 4, 'ascii') 42 | var ptr = offset + 8 43 | if (len === 1) { 44 | uint64be.encode(obj.length, buffer, ptr) 45 | ptr += 8 46 | } 47 | if (boxes.fullBoxes[type]) { 48 | buffer.writeUInt32BE(obj.flags || 0, ptr) 49 | buffer.writeUInt8(obj.version || 0, ptr) 50 | ptr += 4 51 | } 52 | 53 | if (containers[type]) { 54 | var contents = containers[type] 55 | contents.forEach(function (childType) { 56 | if (childType.length === 5) { 57 | var entry = obj[childType] || [] 58 | childType = childType.substr(0, 4) 59 | entry.forEach(function (child) { 60 | Box._encode(child, buffer, ptr) 61 | ptr += Box.encode.bytes 62 | }) 63 | } else if (obj[childType]) { 64 | Box._encode(obj[childType], buffer, ptr) 65 | ptr += Box.encode.bytes 66 | } 67 | }) 68 | if (obj.otherBoxes) { 69 | obj.otherBoxes.forEach(function (child) { 70 | Box._encode(child, buffer, ptr) 71 | ptr += Box.encode.bytes 72 | }) 73 | } 74 | } else if (boxes[type]) { 75 | var encode = boxes[type].encode 76 | encode(obj, buffer, ptr) 77 | ptr += encode.bytes 78 | } else if (obj.buffer) { 79 | var buf = obj.buffer 80 | buf.copy(buffer, ptr) 81 | ptr += obj.buffer.length 82 | } else { 83 | throw new Error('Either `type` must be set to a known type (not\'' + type + '\') or `buffer` must be set') 84 | } 85 | 86 | Box.encode.bytes = ptr - offset 87 | // assert.equal(ptr - offset, obj.length, 'Error encoding \'' + type + '\': wrote ' + ptr - offset + ' bytes, expecting ' + obj.length) 88 | return buffer 89 | } 90 | 91 | /* 92 | * Returns an object with `type` and `size` fields, 93 | * or if there isn't enough data, returns the total 94 | * number of bytes needed to read the headers 95 | */ 96 | Box.readHeaders = function (buffer, start, end) { 97 | start = start || 0 98 | end = end || buffer.length 99 | if (end - start < 8) { 100 | return 8 101 | } 102 | 103 | var len = buffer.readUInt32BE(start) 104 | var type = buffer.toString('ascii', start + 4, start + 8) 105 | var ptr = start + 8 106 | 107 | if (len === 1) { 108 | if (end - start < 16) { 109 | return 16 110 | } 111 | 112 | len = uint64be.decode(buffer, ptr) 113 | ptr += 8 114 | } 115 | 116 | var version 117 | var flags 118 | if (boxes.fullBoxes[type]) { 119 | version = buffer.readUInt8(ptr) 120 | flags = buffer.readUInt32BE(ptr) & 0xffffff 121 | ptr += 4 122 | } 123 | 124 | return { 125 | length: len, 126 | headersLen: ptr - start, 127 | contentLen: len - (ptr - start), 128 | type: type, 129 | version: version, 130 | flags: flags 131 | } 132 | } 133 | 134 | Box.decode = function (buffer, start, end) { 135 | start = start || 0 136 | end = end || buffer.length 137 | var headers = Box.readHeaders(buffer, start, end) 138 | if (!headers || headers.length > end - start) { 139 | throw new Error('Data too short') 140 | } 141 | 142 | return Box.decodeWithoutHeaders(headers, buffer, start + headers.headersLen, start + headers.length) 143 | } 144 | 145 | Box.decodeWithoutHeaders = function (headers, buffer, start, end) { 146 | start = start || 0 147 | end = end || buffer.length 148 | var type = headers.type 149 | var obj = {} 150 | if (containers[type]) { 151 | obj.otherBoxes = [] 152 | var contents = containers[type] 153 | var ptr = start 154 | while (end - ptr >= 8) { 155 | var child = Box.decode(buffer, ptr, end) 156 | ptr += child.length 157 | if (contents.indexOf(child.type) >= 0) { 158 | obj[child.type] = child 159 | } else if (contents.indexOf(child.type + 's') >= 0) { 160 | var childType = child.type + 's' 161 | var entry = obj[childType] = obj[childType] || [] 162 | entry.push(child) 163 | } else { 164 | obj.otherBoxes.push(child) 165 | } 166 | } 167 | } else if (boxes[type]) { 168 | var decode = boxes[type].decode 169 | obj = decode(buffer, start, end) 170 | } else { 171 | obj.buffer = Buffer.from(buffer.slice(start, end)) 172 | } 173 | 174 | obj.length = headers.length 175 | obj.contentLen = headers.contentLen 176 | obj.type = headers.type 177 | obj.version = headers.version 178 | obj.flags = headers.flags 179 | return obj 180 | } 181 | 182 | Box.encodingLength = function (obj) { 183 | var type = obj.type 184 | 185 | var len = 8 186 | if (boxes.fullBoxes[type]) { 187 | len += 4 188 | } 189 | 190 | if (containers[type]) { 191 | var contents = containers[type] 192 | contents.forEach(function (childType) { 193 | if (childType.length === 5) { 194 | var entry = obj[childType] || [] 195 | childType = childType.substr(0, 4) 196 | entry.forEach(function (child) { 197 | child.type = childType 198 | len += Box.encodingLength(child) 199 | }) 200 | } else if (obj[childType]) { 201 | var child = obj[childType] 202 | child.type = childType 203 | len += Box.encodingLength(child) 204 | } 205 | }) 206 | if (obj.otherBoxes) { 207 | obj.otherBoxes.forEach(function (child) { 208 | len += Box.encodingLength(child) 209 | }) 210 | } 211 | } else if (boxes[type]) { 212 | len += boxes[type].encodingLength(obj) 213 | } else if (obj.buffer) { 214 | len += obj.buffer.length 215 | } else { 216 | throw new Error('Either `type` must be set to a known type (not\'' + type + '\') or `buffer` must be set') 217 | } 218 | 219 | if (len > UINT32_MAX) { 220 | len += 8 221 | } 222 | 223 | obj.length = len 224 | return len 225 | } 226 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "mp4-box-encoding", 3 | "version": "1.4.1", 4 | "description": "mp4 box parser/unparser using abstract-encoding", 5 | "main": "index.js", 6 | "repository": { 7 | "type": "git", 8 | "url": "git://github.com/jhiesey/mp4-box-encoding.git" 9 | }, 10 | "scripts": { 11 | "test": "standard" 12 | }, 13 | "keywords": [ 14 | "mp4", 15 | "box", 16 | "parse", 17 | "unparse" 18 | ], 19 | "author": "John Hiesey", 20 | "license": "MIT", 21 | "dependencies": { 22 | "uint64be": "^2.0.2" 23 | }, 24 | "devDependencies": { 25 | "standard": "^11.0.1", 26 | "tape": "^4.9.1" 27 | } 28 | } 29 | --------------------------------------------------------------------------------