From a5278e6e04e33bdf4e095c34db8bad77cf1f808d Mon Sep 17 00:00:00 2001 From: Luke Curley Date: Sat, 1 Apr 2023 22:46:11 -0700 Subject: [PATCH] Load MP4Box from npm with correct typescript definitions. --- player/package.json | 3 + player/src/audio/decoder.ts | 2 +- player/src/audio/source/index.ts | 2 +- player/src/mp4/index.ts | 92 +- player/src/mp4/init.ts | 6 +- player/src/mp4/mp4box.all.js | 8247 ----------------- player/src/mp4/mp4box.d.ts | 148 + player/src/transport/index.ts | 2 - .../{types => transport}/webtransport.d.ts | 0 player/tsconfig.json | 4 - player/yarn.lock | 5 + 11 files changed, 169 insertions(+), 8342 deletions(-) delete mode 100644 player/src/mp4/mp4box.all.js create mode 100644 player/src/mp4/mp4box.d.ts rename player/src/{types => transport}/webtransport.d.ts (100%) diff --git a/player/package.json b/player/package.json index 8238f94..0fe1770 100644 --- a/player/package.json +++ b/player/package.json @@ -12,5 +12,8 @@ "@types/dom-webcodecs": "^0.1.6", "parcel": "^2.8.0", "typescript": ">=3.0.0" + }, + "dependencies": { + "mp4box": "^0.5.2" } } diff --git a/player/src/audio/decoder.ts b/player/src/audio/decoder.ts index baf76dd..388d326 100644 --- a/player/src/audio/decoder.ts +++ b/player/src/audio/decoder.ts @@ -109,7 +109,7 @@ export class Decoder { box.set(atom) // and for some reason we need to modify the underlying ArrayBuffer with offset - let buffer = box.buffer as MP4.ArrayBufferOffset + let buffer = box.buffer as MP4.ArrayBuffer buffer.fileStart = offset // Parse the data diff --git a/player/src/audio/source/index.ts b/player/src/audio/source/index.ts index 1d70385..4f2ebd0 100644 --- a/player/src/audio/source/index.ts +++ b/player/src/audio/source/index.ts @@ -56,7 +56,7 @@ export default class Source { } emit(frame: AudioData) { - for (let i = 0; i < frame.channels; i += 1) { + for (let i = 0; i < frame.numberOfChannels; i += 1) { const ring = this.channels[i] ring.set(frame, i) } diff --git a/player/src/mp4/index.ts b/player/src/mp4/index.ts index 7868f32..e05fbce 100644 --- a/player/src/mp4/index.ts +++ b/player/src/mp4/index.ts @@ -1,87 +1,11 @@ -// Wrapper around MP4Box to play nicely with MP4Box. -// I tried getting a mp4box.all.d.ts file to work but just couldn't figure it out -import { createFile, ISOFile, DataStream, BoxParser } from "./mp4box.all" - // Rename some stuff so it's on brand. -export { createFile as New, ISOFile as File, DataStream as Stream, BoxParser as Parser } - -export type ArrayBufferOffset = ArrayBuffer & {fileStart: number}; - -export interface MediaTrack { - id: number; - created: Date; - modified: Date; - movie_duration: number; - layer: number; - alternate_group: number; - volume: number; - track_width: number; - track_height: number; - timescale: number; - duration: number; - bitrate: number; - codec: string; - language: string; - nb_samples: number; -} - -export interface VideoData { - width: number; - height: number; -} - -export interface VideoTrack extends MediaTrack { - video: VideoData; -} - -export interface MP4AudioData { - sample_rate: number; - channel_count: number; - sample_size: number; -} - -export interface AudioTrack extends MediaTrack { - audio: MP4AudioData; -} - -export type Track = VideoTrack | AudioTrack; - -export interface Info { - duration: number; - timescale: number; - fragment_duration: number; - isFragmented: boolean; - isProgressive: boolean; - hasIOD: boolean; - brands: string[]; - created: Date; - modified: Date; - tracks: Track[]; - mime: string; - videoTracks: Track[]; - audioTracks: Track[]; -} - -export interface Sample { - number: number; - track_id: number; - timescale: number; - description_index: number; - description: any; - data: ArrayBuffer; - size: number; - alreadyRead: number; - duration: number; - cts: number; - dts: number; - is_sync: boolean; - is_leading: number; - depends_on: number; - is_depended_on: number; - has_redundancy: number; - degration_priority: number; - offset: number; - subsamples: any; -} +export { + createFile as New, + MP4File as File, + MP4ArrayBuffer as ArrayBuffer, + MP4Info as Info, + DataStream as Stream, + Sample, +} from "mp4box" export { Init, InitParser } from "./init" \ No newline at end of file diff --git a/player/src/mp4/init.ts b/player/src/mp4/init.ts index 0c94c7b..d36c25b 100644 --- a/player/src/mp4/init.ts +++ b/player/src/mp4/init.ts @@ -1,7 +1,7 @@ import * as MP4 from "./index" export interface Init { - raw: MP4.ArrayBufferOffset; + raw: MP4.ArrayBuffer; info: MP4.Info; } @@ -9,7 +9,7 @@ export class InitParser { mp4box: MP4.File; offset: number; - raw: MP4.ArrayBufferOffset[]; + raw: MP4.ArrayBuffer[]; info: Promise; constructor() { @@ -42,7 +42,7 @@ export class InitParser { box.set(data) // and for some reason we need to modify the underlying ArrayBuffer with fileStart - let buffer = box.buffer as MP4.ArrayBufferOffset + let buffer = box.buffer as MP4.ArrayBuffer buffer.fileStart = this.offset // Parse the data diff --git a/player/src/mp4/mp4box.all.js b/player/src/mp4/mp4box.all.js deleted file mode 100644 index 4528833..0000000 --- a/player/src/mp4/mp4box.all.js +++ /dev/null @@ -1,8247 +0,0 @@ -// file:src/log.js -/* - * Copyright (c) 2012-2013. Telecom ParisTech/TSI/MM/GPAC Cyril Concolato - * License: BSD-3-Clause (see LICENSE file) - */ -var Log = (function (){ - var start = new Date(); - var LOG_LEVEL_ERROR = 4; - var LOG_LEVEL_WARNING = 3; - var LOG_LEVEL_INFO = 2; - var LOG_LEVEL_DEBUG = 1; - var log_level = LOG_LEVEL_ERROR; - var logObject = { - setLogLevel : function(level) { - if (level == this.debug) log_level = LOG_LEVEL_DEBUG; - else if (level == this.info) log_level = LOG_LEVEL_INFO; - else if (level == this.warn) log_level = LOG_LEVEL_WARNING; - else if (level == this.error) log_level = LOG_LEVEL_ERROR; - else log_level = LOG_LEVEL_ERROR; - }, - debug : function(module, msg) { - if (console.debug === undefined) { - console.debug = console.log; - } - if (LOG_LEVEL_DEBUG >= log_level) { - console.debug("["+Log.getDurationString(new Date()-start,1000)+"]","["+module+"]",msg); - } - }, - log : function(module, msg) { - this.debug(module.msg) - }, - info : function(module, msg) { - if (LOG_LEVEL_INFO >= log_level) { - console.info("["+Log.getDurationString(new Date()-start,1000)+"]","["+module+"]",msg); - } - }, - warn : function(module, msg) { - if (LOG_LEVEL_WARNING >= log_level) { - console.warn("["+Log.getDurationString(new Date()-start,1000)+"]","["+module+"]",msg); - } - }, - error : function(module, msg) { - if (LOG_LEVEL_ERROR >= log_level) { - console.error("["+Log.getDurationString(new Date()-start,1000)+"]","["+module+"]",msg); - } - } - }; - return logObject; - })(); - -/* Helper function to print a duration value in the form H:MM:SS.MS */ -Log.getDurationString = function(duration, _timescale) { - var neg; - /* Helper function to print a number on a fixed number of digits */ - function pad(number, length) { - var str = '' + number; - var a = str.split('.'); - while (a[0].length < length) { - a[0] = '0' + a[0]; - } - return a.join('.'); - } - if (duration < 0) { - neg = true; - duration = -duration; - } else { - neg = false; - } - var timescale = _timescale || 1; - var duration_sec = duration/timescale; - var hours = Math.floor(duration_sec/3600); - duration_sec -= hours * 3600; - var minutes = Math.floor(duration_sec/60); - duration_sec -= minutes * 60; - var msec = duration_sec*1000; - duration_sec = Math.floor(duration_sec); - msec -= duration_sec*1000; - msec = Math.floor(msec); - return (neg ? "-": "")+hours+":"+pad(minutes,2)+":"+pad(duration_sec,2)+"."+pad(msec,3); -} - -/* Helper function to stringify HTML5 TimeRanges objects */ -Log.printRanges = function(ranges) { - var length = ranges.length; - if (length > 0) { - var str = ""; - for (var i = 0; i < length; i++) { - if (i > 0) str += ","; - str += "["+Log.getDurationString(ranges.start(i))+ ","+Log.getDurationString(ranges.end(i))+"]"; - } - return str; - } else { - return "(empty)"; - } -} - -if (typeof exports !== 'undefined') { - exports.Log = Log; -} -// file:src/stream.js -var MP4BoxStream = function(arrayBuffer) { - if (arrayBuffer instanceof ArrayBuffer) { - this.buffer = arrayBuffer; - this.dataview = new DataView(arrayBuffer); - } else { - throw ("Needs an array buffer"); - } - this.position = 0; -}; - -/************************************************************************* - Common API between MultiBufferStream and SimpleStream - *************************************************************************/ -MP4BoxStream.prototype.getPosition = function() { - return this.position; -} - -MP4BoxStream.prototype.getEndPosition = function() { - return this.buffer.byteLength; -} - -MP4BoxStream.prototype.getLength = function() { - return this.buffer.byteLength; -} - -MP4BoxStream.prototype.seek = function (pos) { - var npos = Math.max(0, Math.min(this.buffer.byteLength, pos)); - this.position = (isNaN(npos) || !isFinite(npos)) ? 0 : npos; - return true; -} - -MP4BoxStream.prototype.isEos = function () { - return this.getPosition() >= this.getEndPosition(); -} - -/************************************************************************* - Read methods, simimar to DataStream but simpler - *************************************************************************/ -MP4BoxStream.prototype.readAnyInt = function(size, signed) { - var res = 0; - if (this.position + size <= this.buffer.byteLength) { - switch (size) { - case 1: - if (signed) { - res = this.dataview.getInt8(this.position); - } else { - res = this.dataview.getUint8(this.position); - } - break; - case 2: - if (signed) { - res = this.dataview.getInt16(this.position); - } else { - res = this.dataview.getUint16(this.position); - } - break; - case 3: - if (signed) { - throw ("No method for reading signed 24 bits values"); - } else { - res = this.dataview.getUint8(this.position) << 16; - res |= this.dataview.getUint8(this.position+1) << 8; - res |= this.dataview.getUint8(this.position+2); - } - break; - case 4: - if (signed) { - res = this.dataview.getInt32(this.position); - } else { - res = this.dataview.getUint32(this.position); - } - break; - case 8: - if (signed) { - throw ("No method for reading signed 64 bits values"); - } else { - res = this.dataview.getUint32(this.position) << 32; - res |= this.dataview.getUint32(this.position+4); - } - break; - default: - throw ("readInt method not implemented for size: "+size); - } - this.position+= size; - return res; - } else { - throw ("Not enough bytes in buffer"); - } -} - -MP4BoxStream.prototype.readUint8 = function() { - return this.readAnyInt(1, false); -} - -MP4BoxStream.prototype.readUint16 = function() { - return this.readAnyInt(2, false); -} - -MP4BoxStream.prototype.readUint24 = function() { - return this.readAnyInt(3, false); -} - -MP4BoxStream.prototype.readUint32 = function() { - return this.readAnyInt(4, false); -} - -MP4BoxStream.prototype.readUint64 = function() { - return this.readAnyInt(8, false); -} - -MP4BoxStream.prototype.readString = function(length) { - if (this.position + length <= this.buffer.byteLength) { - var s = ""; - for (var i = 0; i < length; i++) { - s += String.fromCharCode(this.readUint8()); - } - return s; - } else { - throw ("Not enough bytes in buffer"); - } -} - -MP4BoxStream.prototype.readCString = function() { - var arr = []; - while(true) { - var b = this.readUint8(); - if (b !== 0) { - arr.push(b); - } else { - break; - } - } - return String.fromCharCode.apply(null, arr); -} - -MP4BoxStream.prototype.readInt8 = function() { - return this.readAnyInt(1, true); -} - -MP4BoxStream.prototype.readInt16 = function() { - return this.readAnyInt(2, true); -} - -MP4BoxStream.prototype.readInt32 = function() { - return this.readAnyInt(4, true); -} - -MP4BoxStream.prototype.readInt64 = function() { - return this.readAnyInt(8, false); -} - -MP4BoxStream.prototype.readUint8Array = function(length) { - var arr = new Uint8Array(length); - for (var i = 0; i < length; i++) { - arr[i] = this.readUint8(); - } - return arr; -} - -MP4BoxStream.prototype.readInt16Array = function(length) { - var arr = new Int16Array(length); - for (var i = 0; i < length; i++) { - arr[i] = this.readInt16(); - } - return arr; -} - -MP4BoxStream.prototype.readUint16Array = function(length) { - var arr = new Int16Array(length); - for (var i = 0; i < length; i++) { - arr[i] = this.readUint16(); - } - return arr; -} - -MP4BoxStream.prototype.readUint32Array = function(length) { - var arr = new Uint32Array(length); - for (var i = 0; i < length; i++) { - arr[i] = this.readUint32(); - } - return arr; -} - -MP4BoxStream.prototype.readInt32Array = function(length) { - var arr = new Int32Array(length); - for (var i = 0; i < length; i++) { - arr[i] = this.readInt32(); - } - return arr; -} - -if (typeof exports !== 'undefined') { - exports.MP4BoxStream = MP4BoxStream; -}// file:src/DataStream.js -/** - DataStream reads scalars, arrays and structs of data from an ArrayBuffer. - It's like a file-like DataView on steroids. - - @param {ArrayBuffer} arrayBuffer ArrayBuffer to read from. - @param {?Number} byteOffset Offset from arrayBuffer beginning for the DataStream. - @param {?Boolean} endianness DataStream.BIG_ENDIAN or DataStream.LITTLE_ENDIAN (the default). - */ -var DataStream = function(arrayBuffer, byteOffset, endianness) { - this._byteOffset = byteOffset || 0; - if (arrayBuffer instanceof ArrayBuffer) { - this.buffer = arrayBuffer; - } else if (typeof arrayBuffer == "object") { - this.dataView = arrayBuffer; - if (byteOffset) { - this._byteOffset += byteOffset; - } - } else { - this.buffer = new ArrayBuffer(arrayBuffer || 0); - } - this.position = 0; - this.endianness = endianness == null ? DataStream.LITTLE_ENDIAN : endianness; -}; -DataStream.prototype = {}; - -DataStream.prototype.getPosition = function() { - return this.position; -} - -/** - Internal function to resize the DataStream buffer when required. - @param {number} extra Number of bytes to add to the buffer allocation. - @return {null} - */ -DataStream.prototype._realloc = function(extra) { - if (!this._dynamicSize) { - return; - } - var req = this._byteOffset + this.position + extra; - var blen = this._buffer.byteLength; - if (req <= blen) { - if (req > this._byteLength) { - this._byteLength = req; - } - return; - } - if (blen < 1) { - blen = 1; - } - while (req > blen) { - blen *= 2; - } - var buf = new ArrayBuffer(blen); - var src = new Uint8Array(this._buffer); - var dst = new Uint8Array(buf, 0, src.length); - dst.set(src); - this.buffer = buf; - this._byteLength = req; -}; - -/** - Internal function to trim the DataStream buffer when required. - Used for stripping out the extra bytes from the backing buffer when - the virtual byteLength is smaller than the buffer byteLength (happens after - growing the buffer with writes and not filling the extra space completely). - - @return {null} - */ -DataStream.prototype._trimAlloc = function() { - if (this._byteLength == this._buffer.byteLength) { - return; - } - var buf = new ArrayBuffer(this._byteLength); - var dst = new Uint8Array(buf); - var src = new Uint8Array(this._buffer, 0, dst.length); - dst.set(src); - this.buffer = buf; -}; - - -/** - Big-endian const to use as default endianness. - @type {boolean} - */ -DataStream.BIG_ENDIAN = false; - -/** - Little-endian const to use as default endianness. - @type {boolean} - */ -DataStream.LITTLE_ENDIAN = true; - -/** - Virtual byte length of the DataStream backing buffer. - Updated to be max of original buffer size and last written size. - If dynamicSize is false is set to buffer size. - @type {number} - */ -DataStream.prototype._byteLength = 0; - -/** - Returns the byte length of the DataStream object. - @type {number} - */ -Object.defineProperty(DataStream.prototype, 'byteLength', - { get: function() { - return this._byteLength - this._byteOffset; - }}); - -/** - Set/get the backing ArrayBuffer of the DataStream object. - The setter updates the DataView to point to the new buffer. - @type {Object} - */ -Object.defineProperty(DataStream.prototype, 'buffer', - { get: function() { - this._trimAlloc(); - return this._buffer; - }, - set: function(v) { - this._buffer = v; - this._dataView = new DataView(this._buffer, this._byteOffset); - this._byteLength = this._buffer.byteLength; - } }); - -/** - Set/get the byteOffset of the DataStream object. - The setter updates the DataView to point to the new byteOffset. - @type {number} - */ -Object.defineProperty(DataStream.prototype, 'byteOffset', - { get: function() { - return this._byteOffset; - }, - set: function(v) { - this._byteOffset = v; - this._dataView = new DataView(this._buffer, this._byteOffset); - this._byteLength = this._buffer.byteLength; - } }); - -/** - Set/get the backing DataView of the DataStream object. - The setter updates the buffer and byteOffset to point to the DataView values. - @type {Object} - */ -Object.defineProperty(DataStream.prototype, 'dataView', - { get: function() { - return this._dataView; - }, - set: function(v) { - this._byteOffset = v.byteOffset; - this._buffer = v.buffer; - this._dataView = new DataView(this._buffer, this._byteOffset); - this._byteLength = this._byteOffset + v.byteLength; - } }); - -/** - Sets the DataStream read/write position to given position. - Clamps between 0 and DataStream length. - - @param {number} pos Position to seek to. - @return {null} - */ -DataStream.prototype.seek = function(pos) { - var npos = Math.max(0, Math.min(this.byteLength, pos)); - this.position = (isNaN(npos) || !isFinite(npos)) ? 0 : npos; -}; - -/** - Returns true if the DataStream seek pointer is at the end of buffer and - there's no more data to read. - - @return {boolean} True if the seek pointer is at the end of the buffer. - */ -DataStream.prototype.isEof = function() { - return (this.position >= this._byteLength); -}; - - -/** - Maps a Uint8Array into the DataStream buffer. - - Nice for quickly reading in data. - - @param {number} length Number of elements to map. - @param {?boolean} e Endianness of the data to read. - @return {Object} Uint8Array to the DataStream backing buffer. - */ -DataStream.prototype.mapUint8Array = function(length) { - this._realloc(length * 1); - var arr = new Uint8Array(this._buffer, this.byteOffset+this.position, length); - this.position += length * 1; - return arr; -}; - - -/** - Reads an Int32Array of desired length and endianness from the DataStream. - - @param {number} length Number of elements to map. - @param {?boolean} e Endianness of the data to read. - @return {Object} The read Int32Array. - */ -DataStream.prototype.readInt32Array = function(length, e) { - length = length == null ? (this.byteLength-this.position / 4) : length; - var arr = new Int32Array(length); - DataStream.memcpy(arr.buffer, 0, - this.buffer, this.byteOffset+this.position, - length*arr.BYTES_PER_ELEMENT); - DataStream.arrayToNative(arr, e == null ? this.endianness : e); - this.position += arr.byteLength; - return arr; -}; - -/** - Reads an Int16Array of desired length and endianness from the DataStream. - - @param {number} length Number of elements to map. - @param {?boolean} e Endianness of the data to read. - @return {Object} The read Int16Array. - */ -DataStream.prototype.readInt16Array = function(length, e) { - length = length == null ? (this.byteLength-this.position / 2) : length; - var arr = new Int16Array(length); - DataStream.memcpy(arr.buffer, 0, - this.buffer, this.byteOffset+this.position, - length*arr.BYTES_PER_ELEMENT); - DataStream.arrayToNative(arr, e == null ? this.endianness : e); - this.position += arr.byteLength; - return arr; -}; - -/** - Reads an Int8Array of desired length from the DataStream. - - @param {number} length Number of elements to map. - @param {?boolean} e Endianness of the data to read. - @return {Object} The read Int8Array. - */ -DataStream.prototype.readInt8Array = function(length) { - length = length == null ? (this.byteLength-this.position) : length; - var arr = new Int8Array(length); - DataStream.memcpy(arr.buffer, 0, - this.buffer, this.byteOffset+this.position, - length*arr.BYTES_PER_ELEMENT); - this.position += arr.byteLength; - return arr; -}; - -/** - Reads a Uint32Array of desired length and endianness from the DataStream. - - @param {number} length Number of elements to map. - @param {?boolean} e Endianness of the data to read. - @return {Object} The read Uint32Array. - */ -DataStream.prototype.readUint32Array = function(length, e) { - length = length == null ? (this.byteLength-this.position / 4) : length; - var arr = new Uint32Array(length); - DataStream.memcpy(arr.buffer, 0, - this.buffer, this.byteOffset+this.position, - length*arr.BYTES_PER_ELEMENT); - DataStream.arrayToNative(arr, e == null ? this.endianness : e); - this.position += arr.byteLength; - return arr; -}; - -/** - Reads a Uint16Array of desired length and endianness from the DataStream. - - @param {number} length Number of elements to map. - @param {?boolean} e Endianness of the data to read. - @return {Object} The read Uint16Array. - */ -DataStream.prototype.readUint16Array = function(length, e) { - length = length == null ? (this.byteLength-this.position / 2) : length; - var arr = new Uint16Array(length); - DataStream.memcpy(arr.buffer, 0, - this.buffer, this.byteOffset+this.position, - length*arr.BYTES_PER_ELEMENT); - DataStream.arrayToNative(arr, e == null ? this.endianness : e); - this.position += arr.byteLength; - return arr; -}; - -/** - Reads a Uint8Array of desired length from the DataStream. - - @param {number} length Number of elements to map. - @param {?boolean} e Endianness of the data to read. - @return {Object} The read Uint8Array. - */ -DataStream.prototype.readUint8Array = function(length) { - length = length == null ? (this.byteLength-this.position) : length; - var arr = new Uint8Array(length); - DataStream.memcpy(arr.buffer, 0, - this.buffer, this.byteOffset+this.position, - length*arr.BYTES_PER_ELEMENT); - this.position += arr.byteLength; - return arr; -}; - -/** - Reads a Float64Array of desired length and endianness from the DataStream. - - @param {number} length Number of elements to map. - @param {?boolean} e Endianness of the data to read. - @return {Object} The read Float64Array. - */ -DataStream.prototype.readFloat64Array = function(length, e) { - length = length == null ? (this.byteLength-this.position / 8) : length; - var arr = new Float64Array(length); - DataStream.memcpy(arr.buffer, 0, - this.buffer, this.byteOffset+this.position, - length*arr.BYTES_PER_ELEMENT); - DataStream.arrayToNative(arr, e == null ? this.endianness : e); - this.position += arr.byteLength; - return arr; -}; - -/** - Reads a Float32Array of desired length and endianness from the DataStream. - - @param {number} length Number of elements to map. - @param {?boolean} e Endianness of the data to read. - @return {Object} The read Float32Array. - */ -DataStream.prototype.readFloat32Array = function(length, e) { - length = length == null ? (this.byteLength-this.position / 4) : length; - var arr = new Float32Array(length); - DataStream.memcpy(arr.buffer, 0, - this.buffer, this.byteOffset+this.position, - length*arr.BYTES_PER_ELEMENT); - DataStream.arrayToNative(arr, e == null ? this.endianness : e); - this.position += arr.byteLength; - return arr; -}; - - -/** - Reads a 32-bit int from the DataStream with the desired endianness. - - @param {?boolean} e Endianness of the number. - @return {number} The read number. - */ -DataStream.prototype.readInt32 = function(e) { - var v = this._dataView.getInt32(this.position, e == null ? this.endianness : e); - this.position += 4; - return v; -}; - -/** - Reads a 16-bit int from the DataStream with the desired endianness. - - @param {?boolean} e Endianness of the number. - @return {number} The read number. - */ -DataStream.prototype.readInt16 = function(e) { - var v = this._dataView.getInt16(this.position, e == null ? this.endianness : e); - this.position += 2; - return v; -}; - -/** - Reads an 8-bit int from the DataStream. - - @return {number} The read number. - */ -DataStream.prototype.readInt8 = function() { - var v = this._dataView.getInt8(this.position); - this.position += 1; - return v; -}; - -/** - Reads a 32-bit unsigned int from the DataStream with the desired endianness. - - @param {?boolean} e Endianness of the number. - @return {number} The read number. - */ -DataStream.prototype.readUint32 = function(e) { - var v = this._dataView.getUint32(this.position, e == null ? this.endianness : e); - this.position += 4; - return v; -}; - -/** - Reads a 16-bit unsigned int from the DataStream with the desired endianness. - - @param {?boolean} e Endianness of the number. - @return {number} The read number. - */ -DataStream.prototype.readUint16 = function(e) { - var v = this._dataView.getUint16(this.position, e == null ? this.endianness : e); - this.position += 2; - return v; -}; - -/** - Reads an 8-bit unsigned int from the DataStream. - - @return {number} The read number. - */ -DataStream.prototype.readUint8 = function() { - var v = this._dataView.getUint8(this.position); - this.position += 1; - return v; -}; - -/** - Reads a 32-bit float from the DataStream with the desired endianness. - - @param {?boolean} e Endianness of the number. - @return {number} The read number. - */ -DataStream.prototype.readFloat32 = function(e) { - var v = this._dataView.getFloat32(this.position, e == null ? this.endianness : e); - this.position += 4; - return v; -}; - -/** - Reads a 64-bit float from the DataStream with the desired endianness. - - @param {?boolean} e Endianness of the number. - @return {number} The read number. - */ -DataStream.prototype.readFloat64 = function(e) { - var v = this._dataView.getFloat64(this.position, e == null ? this.endianness : e); - this.position += 8; - return v; -}; - -/** - Native endianness. Either DataStream.BIG_ENDIAN or DataStream.LITTLE_ENDIAN - depending on the platform endianness. - - @type {boolean} - */ -DataStream.endianness = new Int8Array(new Int16Array([1]).buffer)[0] > 0; - -/** - Copies byteLength bytes from the src buffer at srcOffset to the - dst buffer at dstOffset. - - @param {Object} dst Destination ArrayBuffer to write to. - @param {number} dstOffset Offset to the destination ArrayBuffer. - @param {Object} src Source ArrayBuffer to read from. - @param {number} srcOffset Offset to the source ArrayBuffer. - @param {number} byteLength Number of bytes to copy. - */ -DataStream.memcpy = function(dst, dstOffset, src, srcOffset, byteLength) { - var dstU8 = new Uint8Array(dst, dstOffset, byteLength); - var srcU8 = new Uint8Array(src, srcOffset, byteLength); - dstU8.set(srcU8); -}; - -/** - Converts array to native endianness in-place. - - @param {Object} array Typed array to convert. - @param {boolean} arrayIsLittleEndian True if the data in the array is - little-endian. Set false for big-endian. - @return {Object} The converted typed array. - */ -DataStream.arrayToNative = function(array, arrayIsLittleEndian) { - if (arrayIsLittleEndian == this.endianness) { - return array; - } else { - return this.flipArrayEndianness(array); - } -}; - -/** - Converts native endianness array to desired endianness in-place. - - @param {Object} array Typed array to convert. - @param {boolean} littleEndian True if the converted array should be - little-endian. Set false for big-endian. - @return {Object} The converted typed array. - */ -DataStream.nativeToEndian = function(array, littleEndian) { - if (this.endianness == littleEndian) { - return array; - } else { - return this.flipArrayEndianness(array); - } -}; - -/** - Flips typed array endianness in-place. - - @param {Object} array Typed array to flip. - @return {Object} The converted typed array. - */ -DataStream.flipArrayEndianness = function(array) { - var u8 = new Uint8Array(array.buffer, array.byteOffset, array.byteLength); - for (var i=0; ik; j--, k++) { - var tmp = u8[k]; - u8[k] = u8[j]; - u8[j] = tmp; - } - } - return array; -}; - -/** - Seek position where DataStream#readStruct ran into a problem. - Useful for debugging struct parsing. - - @type {number} - */ -DataStream.prototype.failurePosition = 0; - -String.fromCharCodeUint8 = function(uint8arr) { - var arr = []; - for (var i = 0; i < uint8arr.length; i++) { - arr[i] = uint8arr[i]; - } - return String.fromCharCode.apply(null, arr); -} -/** - Read a string of desired length and encoding from the DataStream. - - @param {number} length The length of the string to read in bytes. - @param {?string} encoding The encoding of the string data in the DataStream. - Defaults to ASCII. - @return {string} The read string. - */ -DataStream.prototype.readString = function(length, encoding) { - if (encoding == null || encoding == "ASCII") { - return String.fromCharCodeUint8.apply(null, [this.mapUint8Array(length == null ? this.byteLength-this.position : length)]); - } else { - return (new TextDecoder(encoding)).decode(this.mapUint8Array(length)); - } -}; - -/** - Read null-terminated string of desired length from the DataStream. Truncates - the returned string so that the null byte is not a part of it. - - @param {?number} length The length of the string to read. - @return {string} The read string. - */ -DataStream.prototype.readCString = function(length) { - var blen = this.byteLength-this.position; - var u8 = new Uint8Array(this._buffer, this._byteOffset + this.position); - var len = blen; - if (length != null) { - len = Math.min(length, blen); - } - for (var i = 0; i < len && u8[i] !== 0; i++); // find first zero byte - var s = String.fromCharCodeUint8.apply(null, [this.mapUint8Array(i)]); - if (length != null) { - this.position += len-i; - } else if (i != blen) { - this.position += 1; // trailing zero if not at end of buffer - } - return s; -}; - -/* - TODO: fix endianness for 24/64-bit fields - TODO: check range/support for 64-bits numbers in JavaScript -*/ -var MAX_SIZE = Math.pow(2, 32); - -DataStream.prototype.readInt64 = function () { - return (this.readInt32()*MAX_SIZE)+this.readUint32(); -} -DataStream.prototype.readUint64 = function () { - return (this.readUint32()*MAX_SIZE)+this.readUint32(); -} - -DataStream.prototype.readInt64 = function () { - return (this.readUint32()*MAX_SIZE)+this.readUint32(); -} - -DataStream.prototype.readUint24 = function () { - return (this.readUint8()<<16)+(this.readUint8()<<8)+this.readUint8(); -} - -if (typeof exports !== 'undefined') { - exports.DataStream = DataStream; -} -// file:src/DataStream-write.js -/** - Saves the DataStream contents to the given filename. - Uses Chrome's anchor download property to initiate download. - - @param {string} filename Filename to save as. - @return {null} - */ -DataStream.prototype.save = function(filename) { - var blob = new Blob([this.buffer]); - if (window.URL && URL.createObjectURL) { - var url = window.URL.createObjectURL(blob); - var a = document.createElement('a'); - // Required in Firefox: - document.body.appendChild(a); - a.setAttribute('href', url); - a.setAttribute('download', filename); - // Required in Firefox: - a.setAttribute('target', '_self'); - a.click(); - window.URL.revokeObjectURL(url); - } else { - throw("DataStream.save: Can't create object URL."); - } -}; - -/** - Whether to extend DataStream buffer when trying to write beyond its size. - If set, the buffer is reallocated to twice its current size until the - requested write fits the buffer. - @type {boolean} - */ -DataStream.prototype._dynamicSize = true; -Object.defineProperty(DataStream.prototype, 'dynamicSize', - { get: function() { - return this._dynamicSize; - }, - set: function(v) { - if (!v) { - this._trimAlloc(); - } - this._dynamicSize = v; - } }); - -/** - Internal function to trim the DataStream buffer when required. - Used for stripping out the first bytes when not needed anymore. - - @return {null} - */ -DataStream.prototype.shift = function(offset) { - var buf = new ArrayBuffer(this._byteLength-offset); - var dst = new Uint8Array(buf); - var src = new Uint8Array(this._buffer, offset, dst.length); - dst.set(src); - this.buffer = buf; - this.position -= offset; -}; - -/** - Writes an Int32Array of specified endianness to the DataStream. - - @param {Object} arr The array to write. - @param {?boolean} e Endianness of the data to write. - */ -DataStream.prototype.writeInt32Array = function(arr, e) { - this._realloc(arr.length * 4); - if (arr instanceof Int32Array && - this.byteOffset+this.position % arr.BYTES_PER_ELEMENT === 0) { - DataStream.memcpy(this._buffer, this.byteOffset+this.position, - arr.buffer, 0, - arr.byteLength); - this.mapInt32Array(arr.length, e); - } else { - for (var i=0; i>16); - this.writeUint8((v & 0x0000FF00)>>8); - this.writeUint8((v & 0x000000FF)); -} - -DataStream.prototype.adjustUint32 = function(position, value) { - var pos = this.position; - this.seek(position); - this.writeUint32(value); - this.seek(pos); -} -// file:src/DataStream-map.js -/** - Maps an Int32Array into the DataStream buffer, swizzling it to native - endianness in-place. The current offset from the start of the buffer needs to - be a multiple of element size, just like with typed array views. - - Nice for quickly reading in data. Warning: potentially modifies the buffer - contents. - - @param {number} length Number of elements to map. - @param {?boolean} e Endianness of the data to read. - @return {Object} Int32Array to the DataStream backing buffer. - */ -DataStream.prototype.mapInt32Array = function(length, e) { - this._realloc(length * 4); - var arr = new Int32Array(this._buffer, this.byteOffset+this.position, length); - DataStream.arrayToNative(arr, e == null ? this.endianness : e); - this.position += length * 4; - return arr; -}; - -/** - Maps an Int16Array into the DataStream buffer, swizzling it to native - endianness in-place. The current offset from the start of the buffer needs to - be a multiple of element size, just like with typed array views. - - Nice for quickly reading in data. Warning: potentially modifies the buffer - contents. - - @param {number} length Number of elements to map. - @param {?boolean} e Endianness of the data to read. - @return {Object} Int16Array to the DataStream backing buffer. - */ -DataStream.prototype.mapInt16Array = function(length, e) { - this._realloc(length * 2); - var arr = new Int16Array(this._buffer, this.byteOffset+this.position, length); - DataStream.arrayToNative(arr, e == null ? this.endianness : e); - this.position += length * 2; - return arr; -}; - -/** - Maps an Int8Array into the DataStream buffer. - - Nice for quickly reading in data. - - @param {number} length Number of elements to map. - @param {?boolean} e Endianness of the data to read. - @return {Object} Int8Array to the DataStream backing buffer. - */ -DataStream.prototype.mapInt8Array = function(length) { - this._realloc(length * 1); - var arr = new Int8Array(this._buffer, this.byteOffset+this.position, length); - this.position += length * 1; - return arr; -}; - -/** - Maps a Uint32Array into the DataStream buffer, swizzling it to native - endianness in-place. The current offset from the start of the buffer needs to - be a multiple of element size, just like with typed array views. - - Nice for quickly reading in data. Warning: potentially modifies the buffer - contents. - - @param {number} length Number of elements to map. - @param {?boolean} e Endianness of the data to read. - @return {Object} Uint32Array to the DataStream backing buffer. - */ -DataStream.prototype.mapUint32Array = function(length, e) { - this._realloc(length * 4); - var arr = new Uint32Array(this._buffer, this.byteOffset+this.position, length); - DataStream.arrayToNative(arr, e == null ? this.endianness : e); - this.position += length * 4; - return arr; -}; - -/** - Maps a Uint16Array into the DataStream buffer, swizzling it to native - endianness in-place. The current offset from the start of the buffer needs to - be a multiple of element size, just like with typed array views. - - Nice for quickly reading in data. Warning: potentially modifies the buffer - contents. - - @param {number} length Number of elements to map. - @param {?boolean} e Endianness of the data to read. - @return {Object} Uint16Array to the DataStream backing buffer. - */ -DataStream.prototype.mapUint16Array = function(length, e) { - this._realloc(length * 2); - var arr = new Uint16Array(this._buffer, this.byteOffset+this.position, length); - DataStream.arrayToNative(arr, e == null ? this.endianness : e); - this.position += length * 2; - return arr; -}; - -/** - Maps a Float64Array into the DataStream buffer, swizzling it to native - endianness in-place. The current offset from the start of the buffer needs to - be a multiple of element size, just like with typed array views. - - Nice for quickly reading in data. Warning: potentially modifies the buffer - contents. - - @param {number} length Number of elements to map. - @param {?boolean} e Endianness of the data to read. - @return {Object} Float64Array to the DataStream backing buffer. - */ -DataStream.prototype.mapFloat64Array = function(length, e) { - this._realloc(length * 8); - var arr = new Float64Array(this._buffer, this.byteOffset+this.position, length); - DataStream.arrayToNative(arr, e == null ? this.endianness : e); - this.position += length * 8; - return arr; -}; - -/** - Maps a Float32Array into the DataStream buffer, swizzling it to native - endianness in-place. The current offset from the start of the buffer needs to - be a multiple of element size, just like with typed array views. - - Nice for quickly reading in data. Warning: potentially modifies the buffer - contents. - - @param {number} length Number of elements to map. - @param {?boolean} e Endianness of the data to read. - @return {Object} Float32Array to the DataStream backing buffer. - */ -DataStream.prototype.mapFloat32Array = function(length, e) { - this._realloc(length * 4); - var arr = new Float32Array(this._buffer, this.byteOffset+this.position, length); - DataStream.arrayToNative(arr, e == null ? this.endianness : e); - this.position += length * 4; - return arr; -}; -// file:src/buffer.js -/** - * MultiBufferStream is a class that acts as a SimpleStream for parsing - * It holds several, possibly non-contiguous ArrayBuffer objects, each with a fileStart property - * containing the offset for the buffer data in an original/virtual file - * - * It inherits also from DataStream for all read/write/alloc operations - */ - -/** - * Constructor - */ -var MultiBufferStream = function(buffer) { - /* List of ArrayBuffers, with a fileStart property, sorted in fileStart order and non overlapping */ - this.buffers = []; - this.bufferIndex = -1; - if (buffer) { - this.insertBuffer(buffer); - this.bufferIndex = 0; - } -} -MultiBufferStream.prototype = new DataStream(new ArrayBuffer(), 0, DataStream.BIG_ENDIAN); - -/************************************************************************************ - Methods for the managnement of the buffers (insertion, removal, concatenation, ...) - ***********************************************************************************/ - -MultiBufferStream.prototype.initialized = function() { - var firstBuffer; - if (this.bufferIndex > -1) { - return true; - } else if (this.buffers.length > 0) { - firstBuffer = this.buffers[0]; - if (firstBuffer.fileStart === 0) { - this.buffer = firstBuffer; - this.bufferIndex = 0; - Log.debug("MultiBufferStream", "Stream ready for parsing"); - return true; - } else { - Log.warn("MultiBufferStream", "The first buffer should have a fileStart of 0"); - this.logBufferLevel(); - return false; - } - } else { - Log.warn("MultiBufferStream", "No buffer to start parsing from"); - this.logBufferLevel(); - return false; - } -} - -/** - * helper functions to concatenate two ArrayBuffer objects - * @param {ArrayBuffer} buffer1 - * @param {ArrayBuffer} buffer2 - * @return {ArrayBuffer} the concatenation of buffer1 and buffer2 in that order - */ -ArrayBuffer.concat = function(buffer1, buffer2) { - Log.debug("ArrayBuffer", "Trying to create a new buffer of size: "+(buffer1.byteLength + buffer2.byteLength)); - var tmp = new Uint8Array(buffer1.byteLength + buffer2.byteLength); - tmp.set(new Uint8Array(buffer1), 0); - tmp.set(new Uint8Array(buffer2), buffer1.byteLength); - return tmp.buffer; -}; - -/** - * Reduces the size of a given buffer, but taking the part between offset and offset+newlength - * @param {ArrayBuffer} buffer - * @param {Number} offset the start of new buffer - * @param {Number} newLength the length of the new buffer - * @return {ArrayBuffer} the new buffer - */ -MultiBufferStream.prototype.reduceBuffer = function(buffer, offset, newLength) { - var smallB; - smallB = new Uint8Array(newLength); - smallB.set(new Uint8Array(buffer, offset, newLength)); - smallB.buffer.fileStart = buffer.fileStart+offset; - smallB.buffer.usedBytes = 0; - return smallB.buffer; -} - -/** - * Inserts the new buffer in the sorted list of buffers, - * making sure, it is not overlapping with existing ones (possibly reducing its size). - * if the new buffer overrides/replaces the 0-th buffer (for instance because it is bigger), - * updates the DataStream buffer for parsing -*/ -MultiBufferStream.prototype.insertBuffer = function(ab) { - var to_add = true; - /* TODO: improve insertion if many buffers */ - for (var i = 0; i < this.buffers.length; i++) { - var b = this.buffers[i]; - if (ab.fileStart <= b.fileStart) { - /* the insertion position is found */ - if (ab.fileStart === b.fileStart) { - /* The new buffer overlaps with an existing buffer */ - if (ab.byteLength > b.byteLength) { - /* the new buffer is bigger than the existing one - remove the existing buffer and try again to insert - the new buffer to check overlap with the next ones */ - this.buffers.splice(i, 1); - i--; - continue; - } else { - /* the new buffer is smaller than the existing one, just drop it */ - Log.warn("MultiBufferStream", "Buffer (fileStart: "+ab.fileStart+" - Length: "+ab.byteLength+") already appended, ignoring"); - } - } else { - /* The beginning of the new buffer is not overlapping with an existing buffer - let's check the end of it */ - if (ab.fileStart + ab.byteLength <= b.fileStart) { - /* no overlap, we can add it as is */ - } else { - /* There is some overlap, cut the new buffer short, and add it*/ - ab = this.reduceBuffer(ab, 0, b.fileStart - ab.fileStart); - } - Log.debug("MultiBufferStream", "Appending new buffer (fileStart: "+ab.fileStart+" - Length: "+ab.byteLength+")"); - this.buffers.splice(i, 0, ab); - /* if this new buffer is inserted in the first place in the list of the buffer, - and the DataStream is initialized, make it the buffer used for parsing */ - if (i === 0) { - this.buffer = ab; - } - } - to_add = false; - break; - } else if (ab.fileStart < b.fileStart + b.byteLength) { - /* the new buffer overlaps its beginning with the end of the current buffer */ - var offset = b.fileStart + b.byteLength - ab.fileStart; - var newLength = ab.byteLength - offset; - if (newLength > 0) { - /* the new buffer is bigger than the current overlap, drop the overlapping part and try again inserting the remaining buffer */ - ab = this.reduceBuffer(ab, offset, newLength); - } else { - /* the content of the new buffer is entirely contained in the existing buffer, drop it entirely */ - to_add = false; - break; - } - } - } - /* if the buffer has not been added, we can add it at the end */ - if (to_add) { - Log.debug("MultiBufferStream", "Appending new buffer (fileStart: "+ab.fileStart+" - Length: "+ab.byteLength+")"); - this.buffers.push(ab); - /* if this new buffer is inserted in the first place in the list of the buffer, - and the DataStream is initialized, make it the buffer used for parsing */ - if (i === 0) { - this.buffer = ab; - } - } -} - -/** - * Displays the status of the buffers (number and used bytes) - * @param {Object} info callback method for display - */ -MultiBufferStream.prototype.logBufferLevel = function(info) { - var i; - var buffer; - var used, total; - var ranges = []; - var range; - var bufferedString = ""; - used = 0; - total = 0; - for (i = 0; i < this.buffers.length; i++) { - buffer = this.buffers[i]; - if (i === 0) { - range = {}; - ranges.push(range); - range.start = buffer.fileStart; - range.end = buffer.fileStart+buffer.byteLength; - bufferedString += "["+range.start+"-"; - } else if (range.end === buffer.fileStart) { - range.end = buffer.fileStart+buffer.byteLength; - } else { - range = {}; - range.start = buffer.fileStart; - bufferedString += (ranges[ranges.length-1].end-1)+"], ["+range.start+"-"; - range.end = buffer.fileStart+buffer.byteLength; - ranges.push(range); - } - used += buffer.usedBytes; - total += buffer.byteLength; - } - if (ranges.length > 0) { - bufferedString += (range.end-1)+"]"; - } - var log = (info ? Log.info : Log.debug) - if (this.buffers.length === 0) { - log("MultiBufferStream", "No more buffer in memory"); - } else { - log("MultiBufferStream", ""+this.buffers.length+" stored buffer(s) ("+used+"/"+total+" bytes), continuous ranges: "+bufferedString); - } -} - -MultiBufferStream.prototype.cleanBuffers = function () { - var i; - var buffer; - for (i = 0; i < this.buffers.length; i++) { - buffer = this.buffers[i]; - if (buffer.usedBytes === buffer.byteLength) { - Log.debug("MultiBufferStream", "Removing buffer #"+i); - this.buffers.splice(i, 1); - i--; - } - } -} - -MultiBufferStream.prototype.mergeNextBuffer = function() { - var next_buffer; - if (this.bufferIndex+1 < this.buffers.length) { - next_buffer = this.buffers[this.bufferIndex+1]; - if (next_buffer.fileStart === this.buffer.fileStart + this.buffer.byteLength) { - var oldLength = this.buffer.byteLength; - var oldUsedBytes = this.buffer.usedBytes; - var oldFileStart = this.buffer.fileStart; - this.buffers[this.bufferIndex] = ArrayBuffer.concat(this.buffer, next_buffer); - this.buffer = this.buffers[this.bufferIndex]; - this.buffers.splice(this.bufferIndex+1, 1); - this.buffer.usedBytes = oldUsedBytes; /* TODO: should it be += ? */ - this.buffer.fileStart = oldFileStart; - Log.debug("ISOFile", "Concatenating buffer for box parsing (length: "+oldLength+"->"+this.buffer.byteLength+")"); - return true; - } else { - return false; - } - } else { - return false; - } -} - - -/************************************************************************* - Seek-related functions - *************************************************************************/ - -/** - * Finds the buffer that holds the given file position - * @param {Boolean} fromStart indicates if the search should start from the current buffer (false) - * or from the first buffer (true) - * @param {Number} filePosition position in the file to seek to - * @param {Boolean} markAsUsed indicates if the bytes in between the current position and the seek position - * should be marked as used for garbage collection - * @return {Number} the index of the buffer holding the seeked file position, -1 if not found. - */ -MultiBufferStream.prototype.findPosition = function(fromStart, filePosition, markAsUsed) { - var i; - var abuffer = null; - var index = -1; - - /* find the buffer with the largest position smaller than the given position */ - if (fromStart === true) { - /* the reposition can be in the past, we need to check from the beginning of the list of buffers */ - i = 0; - } else { - i = this.bufferIndex; - } - - while (i < this.buffers.length) { - abuffer = this.buffers[i]; - if (abuffer.fileStart <= filePosition) { - index = i; - if (markAsUsed) { - if (abuffer.fileStart + abuffer.byteLength <= filePosition) { - abuffer.usedBytes = abuffer.byteLength; - } else { - abuffer.usedBytes = filePosition - abuffer.fileStart; - } - this.logBufferLevel(); - } - } else { - break; - } - i++; - } - - if (index !== -1) { - abuffer = this.buffers[index]; - if (abuffer.fileStart + abuffer.byteLength >= filePosition) { - Log.debug("MultiBufferStream", "Found position in existing buffer #"+index); - return index; - } else { - return -1; - } - } else { - return -1; - } -} - -/** - * Finds the largest file position contained in a buffer or in the next buffers if they are contiguous (no gap) - * starting from the given buffer index or from the current buffer if the index is not given - * - * @param {Number} inputindex Index of the buffer to start from - * @return {Number} The largest file position found in the buffers - */ -MultiBufferStream.prototype.findEndContiguousBuf = function(inputindex) { - var i; - var currentBuf; - var nextBuf; - var index = (inputindex !== undefined ? inputindex : this.bufferIndex); - currentBuf = this.buffers[index]; - /* find the end of the contiguous range of data */ - if (this.buffers.length > index+1) { - for (i = index+1; i < this.buffers.length; i++) { - nextBuf = this.buffers[i]; - if (nextBuf.fileStart === currentBuf.fileStart + currentBuf.byteLength) { - currentBuf = nextBuf; - } else { - break; - } - } - } - /* return the position of last byte in the file that we have */ - return currentBuf.fileStart + currentBuf.byteLength; -} - -/** - * Returns the largest file position contained in the buffers, larger than the given position - * @param {Number} pos the file position to start from - * @return {Number} the largest position in the current buffer or in the buffer and the next contiguous - * buffer that holds the given position - */ -MultiBufferStream.prototype.getEndFilePositionAfter = function(pos) { - var index = this.findPosition(true, pos, false); - if (index !== -1) { - return this.findEndContiguousBuf(index); - } else { - return pos; - } -} - -/************************************************************************* - Garbage collection related functions - *************************************************************************/ - -/** - * Marks a given number of bytes as used in the current buffer for garbage collection - * @param {Number} nbBytes - */ -MultiBufferStream.prototype.addUsedBytes = function(nbBytes) { - this.buffer.usedBytes += nbBytes; - this.logBufferLevel(); -} - -/** - * Marks the entire current buffer as used, ready for garbage collection - */ -MultiBufferStream.prototype.setAllUsedBytes = function() { - this.buffer.usedBytes = this.buffer.byteLength; - this.logBufferLevel(); -} - -/************************************************************************* - Common API between MultiBufferStream and SimpleStream - *************************************************************************/ - -/** - * Tries to seek to a given file position - * if possible, repositions the parsing from there and returns true - * if not possible, does not change anything and returns false - * @param {Number} filePosition position in the file to seek to - * @param {Boolean} fromStart indicates if the search should start from the current buffer (false) - * or from the first buffer (true) - * @param {Boolean} markAsUsed indicates if the bytes in between the current position and the seek position - * should be marked as used for garbage collection - * @return {Boolean} true if the seek succeeded, false otherwise - */ -MultiBufferStream.prototype.seek = function(filePosition, fromStart, markAsUsed) { - var index; - index = this.findPosition(fromStart, filePosition, markAsUsed); - if (index !== -1) { - this.buffer = this.buffers[index]; - this.bufferIndex = index; - this.position = filePosition - this.buffer.fileStart; - Log.debug("MultiBufferStream", "Repositioning parser at buffer position: "+this.position); - return true; - } else { - Log.debug("MultiBufferStream", "Position "+filePosition+" not found in buffered data"); - return false; - } -} - -/** - * Returns the current position in the file - * @return {Number} the position in the file - */ -MultiBufferStream.prototype.getPosition = function() { - if (this.bufferIndex === -1 || this.buffers[this.bufferIndex] === null) { - throw "Error accessing position in the MultiBufferStream"; - } - return this.buffers[this.bufferIndex].fileStart+this.position; -} - -/** - * Returns the length of the current buffer - * @return {Number} the length of the current buffer - */ -MultiBufferStream.prototype.getLength = function() { - return this.byteLength; -} - -MultiBufferStream.prototype.getEndPosition = function() { - if (this.bufferIndex === -1 || this.buffers[this.bufferIndex] === null) { - throw "Error accessing position in the MultiBufferStream"; - } - return this.buffers[this.bufferIndex].fileStart+this.byteLength; -} - -if (typeof exports !== 'undefined') { - exports.MultiBufferStream = MultiBufferStream; -}// file:src/descriptor.js -/* - * Copyright (c) 2012-2013. Telecom ParisTech/TSI/MM/GPAC Cyril Concolato - * License: BSD-3-Clause (see LICENSE file) - */ -var MPEG4DescriptorParser = function () { - var ES_DescrTag = 0x03; - var DecoderConfigDescrTag = 0x04; - var DecSpecificInfoTag = 0x05; - var SLConfigDescrTag = 0x06; - - var descTagToName = []; - descTagToName[ES_DescrTag] = "ES_Descriptor"; - descTagToName[DecoderConfigDescrTag] = "DecoderConfigDescriptor"; - descTagToName[DecSpecificInfoTag] = "DecoderSpecificInfo"; - descTagToName[SLConfigDescrTag] = "SLConfigDescriptor"; - - this.getDescriptorName = function(tag) { - return descTagToName[tag]; - } - - var that = this; - var classes = {}; - - this.parseOneDescriptor = function (stream) { - var hdrSize = 0; - var size = 0; - var tag; - var desc; - var byteRead; - tag = stream.readUint8(); - hdrSize++; - byteRead = stream.readUint8(); - hdrSize++; - while (byteRead & 0x80) { - size = (byteRead & 0x7F)<<7; - byteRead = stream.readUint8(); - hdrSize++; - } - size += byteRead & 0x7F; - Log.debug("MPEG4DescriptorParser", "Found "+(descTagToName[tag] || "Descriptor "+tag)+", size "+size+" at position "+stream.getPosition()); - if (descTagToName[tag]) { - desc = new classes[descTagToName[tag]](size); - } else { - desc = new classes.Descriptor(size); - } - desc.parse(stream); - return desc; - } - - classes.Descriptor = function(_tag, _size) { - this.tag = _tag; - this.size = _size; - this.descs = []; - } - - classes.Descriptor.prototype.parse = function (stream) { - this.data = stream.readUint8Array(this.size); - } - - classes.Descriptor.prototype.findDescriptor = function (tag) { - for (var i = 0; i < this.descs.length; i++) { - if (this.descs[i].tag == tag) { - return this.descs[i]; - } - } - return null; - } - - classes.Descriptor.prototype.parseRemainingDescriptors = function (stream) { - var start = stream.position; - while (stream.position < start+this.size) { - var desc = that.parseOneDescriptor(stream); - this.descs.push(desc); - } - } - - classes.ES_Descriptor = function (size) { - classes.Descriptor.call(this, ES_DescrTag, size); - } - - classes.ES_Descriptor.prototype = new classes.Descriptor(); - - classes.ES_Descriptor.prototype.parse = function(stream) { - this.ES_ID = stream.readUint16(); - this.flags = stream.readUint8(); - this.size -= 3; - if (this.flags & 0x80) { - this.dependsOn_ES_ID = stream.readUint16(); - this.size -= 2; - } else { - this.dependsOn_ES_ID = 0; - } - if (this.flags & 0x40) { - var l = stream.readUint8(); - this.URL = stream.readString(l); - this.size -= l+1; - } else { - this.URL = ""; - } - if (this.flags & 0x20) { - this.OCR_ES_ID = stream.readUint16(); - this.size -= 2; - } else { - this.OCR_ES_ID = 0; - } - this.parseRemainingDescriptors(stream); - } - - classes.ES_Descriptor.prototype.getOTI = function(stream) { - var dcd = this.findDescriptor(DecoderConfigDescrTag); - if (dcd) { - return dcd.oti; - } else { - return 0; - } - } - - classes.ES_Descriptor.prototype.getAudioConfig = function(stream) { - var dcd = this.findDescriptor(DecoderConfigDescrTag); - if (!dcd) return null; - var dsi = dcd.findDescriptor(DecSpecificInfoTag); - if (dsi && dsi.data) { - var audioObjectType = (dsi.data[0]& 0xF8) >> 3; - if (audioObjectType === 31 && dsi.data.length >= 2) { - audioObjectType = 32 + ((dsi.data[0] & 0x7) << 3) + ((dsi.data[1] & 0xE0) >> 5); - } - return audioObjectType; - } else { - return null; - } - } - - classes.DecoderConfigDescriptor = function (size) { - classes.Descriptor.call(this, DecoderConfigDescrTag, size); - } - classes.DecoderConfigDescriptor.prototype = new classes.Descriptor(); - - classes.DecoderConfigDescriptor.prototype.parse = function(stream) { - this.oti = stream.readUint8(); - this.streamType = stream.readUint8(); - this.bufferSize = stream.readUint24(); - this.maxBitrate = stream.readUint32(); - this.avgBitrate = stream.readUint32(); - this.size -= 13; - this.parseRemainingDescriptors(stream); - } - - classes.DecoderSpecificInfo = function (size) { - classes.Descriptor.call(this, DecSpecificInfoTag, size); - } - classes.DecoderSpecificInfo.prototype = new classes.Descriptor(); - - classes.SLConfigDescriptor = function (size) { - classes.Descriptor.call(this, SLConfigDescrTag, size); - } - classes.SLConfigDescriptor.prototype = new classes.Descriptor(); - - return this; -} - -if (typeof exports !== 'undefined') { - exports.MPEG4DescriptorParser = MPEG4DescriptorParser; -}// file:src/box.js -/* - * Copyright (c) 2012-2013. Telecom ParisTech/TSI/MM/GPAC Cyril Concolato - * License: BSD-3-Clause (see LICENSE file) - */ -var BoxParser = { - ERR_INVALID_DATA : -1, - ERR_NOT_ENOUGH_DATA : 0, - OK : 1, - - // Boxes to be created with default parsing - BASIC_BOXES: [ "mdat", "idat", "free", "skip", "meco", "strk" ], - FULL_BOXES: [ "hmhd", "nmhd", "iods", "xml ", "bxml", "ipro", "mere" ], - CONTAINER_BOXES: [ - [ "moov", [ "trak", "pssh" ] ], - [ "trak" ], - [ "edts" ], - [ "mdia" ], - [ "minf" ], - [ "dinf" ], - [ "stbl", [ "sgpd", "sbgp" ] ], - [ "mvex", [ "trex" ] ], - [ "moof", [ "traf" ] ], - [ "traf", [ "trun", "sgpd", "sbgp" ] ], - [ "vttc" ], - [ "tref" ], - [ "iref" ], - [ "mfra", [ "tfra" ] ], - [ "meco" ], - [ "hnti" ], - [ "hinf" ], - [ "strk" ], - [ "strd" ], - [ "sinf" ], - [ "rinf" ], - [ "schi" ], - [ "trgr" ], - [ "udta", ["kind"] ], - [ "iprp", ["ipma"] ], - [ "ipco"] - ], - // Boxes effectively created - boxCodes : [], - fullBoxCodes : [], - containerBoxCodes : [], - sampleEntryCodes : {}, - sampleGroupEntryCodes: [], - trackGroupTypes: [], - UUIDBoxes: {}, - UUIDs: [], - initialize: function() { - BoxParser.FullBox.prototype = new BoxParser.Box(); - BoxParser.ContainerBox.prototype = new BoxParser.Box(); - BoxParser.SampleEntry.prototype = new BoxParser.Box(); - BoxParser.TrackGroupTypeBox.prototype = new BoxParser.FullBox(); - - /* creating constructors for simple boxes */ - BoxParser.BASIC_BOXES.forEach(function(type) { - BoxParser.createBoxCtor(type) - }); - BoxParser.FULL_BOXES.forEach(function(type) { - BoxParser.createFullBoxCtor(type); - }); - BoxParser.CONTAINER_BOXES.forEach(function(types) { - BoxParser.createContainerBoxCtor(types[0], null, types[1]); - }); - }, - Box: function(_type, _size, _uuid) { - this.type = _type; - this.size = _size; - this.uuid = _uuid; - }, - FullBox: function(type, size, uuid) { - BoxParser.Box.call(this, type, size, uuid); - this.flags = 0; - this.version = 0; - }, - ContainerBox: function(type, size, uuid) { - BoxParser.Box.call(this, type, size, uuid); - this.boxes = []; - }, - SampleEntry: function(type, size, hdr_size, start) { - BoxParser.ContainerBox.call(this, type, size); - this.hdr_size = hdr_size; - this.start = start; - }, - SampleGroupEntry: function(type) { - this.grouping_type = type; - }, - TrackGroupTypeBox: function(type, size) { - BoxParser.FullBox.call(this, type, size); - }, - createBoxCtor: function(type, parseMethod){ - BoxParser.boxCodes.push(type); - BoxParser[type+"Box"] = function(size) { - BoxParser.Box.call(this, type, size); - } - BoxParser[type+"Box"].prototype = new BoxParser.Box(); - if (parseMethod) BoxParser[type+"Box"].prototype.parse = parseMethod; - }, - createFullBoxCtor: function(type, parseMethod) { - //BoxParser.fullBoxCodes.push(type); - BoxParser[type+"Box"] = function(size) { - BoxParser.FullBox.call(this, type, size); - } - BoxParser[type+"Box"].prototype = new BoxParser.FullBox(); - BoxParser[type+"Box"].prototype.parse = function(stream) { - this.parseFullHeader(stream); - if (parseMethod) { - parseMethod.call(this, stream); - } - }; - }, - addSubBoxArrays: function(subBoxNames) { - if (subBoxNames) { - this.subBoxNames = subBoxNames; - var nbSubBoxes = subBoxNames.length; - for (var k = 0; k parentSize) { - Log.error("BoxParser", "Box of type '"+type+"' has a size "+size+" greater than its container size "+parentSize); - return { code: BoxParser.ERR_NOT_ENOUGH_DATA, type: type, size: size, hdr_size: hdr_size, start: start }; - } - if (size !== 0 && start + size > stream.getEndPosition()) { - stream.seek(start); - Log.info("BoxParser", "Not enough data in stream to parse the entire '"+type+"' box"); - return { code: BoxParser.ERR_NOT_ENOUGH_DATA, type: type, size: size, hdr_size: hdr_size, start: start }; - } - if (headerOnly) { - return { code: BoxParser.OK, type: type, size: size, hdr_size: hdr_size, start: start }; - } else { - if (BoxParser[type+"Box"]) { - box = new BoxParser[type+"Box"](size); - } else { - if (type !== "uuid") { - Log.warn("BoxParser", "Unknown box type: '"+type+"'"); - box = new BoxParser.Box(type, size); - box.has_unparsed_data = true; - } else { - if (BoxParser.UUIDBoxes[uuid]) { - box = new BoxParser.UUIDBoxes[uuid](size); - } else { - Log.warn("BoxParser", "Unknown uuid type: '"+uuid+"'"); - box = new BoxParser.Box(type, size); - box.uuid = uuid; - box.has_unparsed_data = true; - } - } - } - } - box.hdr_size = hdr_size; - /* recording the position of the box in the input stream */ - box.start = start; - if (box.write === BoxParser.Box.prototype.write && box.type !== "mdat") { - Log.info("BoxParser", "'"+box_type+"' box writing not yet implemented, keeping unparsed data in memory for later write"); - box.parseDataAndRewind(stream); - } - box.parse(stream); - diff = stream.getPosition() - (box.start+box.size); - if (diff < 0) { - Log.warn("BoxParser", "Parsing of box '"+box_type+"' did not read the entire indicated box data size (missing "+(-diff)+" bytes), seeking forward"); - stream.seek(box.start+box.size); - } else if (diff > 0) { - Log.error("BoxParser", "Parsing of box '"+box_type+"' read "+diff+" more bytes than the indicated box data size, seeking backwards"); - if (box.size !== 0) stream.seek(box.start+box.size); - } - return { code: BoxParser.OK, box: box, size: box.size }; -} - -BoxParser.Box.prototype.parse = function(stream) { - if (this.type != "mdat") { - this.data = stream.readUint8Array(this.size-this.hdr_size); - } else { - if (this.size === 0) { - stream.seek(stream.getEndPosition()); - } else { - stream.seek(this.start+this.size); - } - } -} - -/* Used to parse a box without consuming its data, to allow detailled parsing - Useful for boxes for which a write method is not yet implemented */ -BoxParser.Box.prototype.parseDataAndRewind = function(stream) { - this.data = stream.readUint8Array(this.size-this.hdr_size); - // rewinding - stream.position -= this.size-this.hdr_size; -} - -BoxParser.FullBox.prototype.parseDataAndRewind = function(stream) { - this.parseFullHeader(stream); - this.data = stream.readUint8Array(this.size-this.hdr_size); - // restore the header size as if the full header had not been parsed - this.hdr_size -= 4; - // rewinding - stream.position -= this.size-this.hdr_size; -} - -BoxParser.FullBox.prototype.parseFullHeader = function (stream) { - this.version = stream.readUint8(); - this.flags = stream.readUint24(); - this.hdr_size += 4; -} - -BoxParser.FullBox.prototype.parse = function (stream) { - this.parseFullHeader(stream); - this.data = stream.readUint8Array(this.size-this.hdr_size); -} - -BoxParser.ContainerBox.prototype.parse = function(stream) { - var ret; - var box; - while (stream.getPosition() < this.start+this.size) { - ret = BoxParser.parseOneBox(stream, false, this.size - (stream.getPosition() - this.start)); - if (ret.code === BoxParser.OK) { - box = ret.box; - /* store the box in the 'boxes' array to preserve box order (for offset) but also store box in a property for more direct access */ - this.boxes.push(box); - if (this.subBoxNames && this.subBoxNames.indexOf(box.type) != -1) { - this[this.subBoxNames[this.subBoxNames.indexOf(box.type)]+"s"].push(box); - } else { - var box_type = box.type !== "uuid" ? box.type : box.uuid; - if (this[box_type]) { - Log.warn("Box of type "+box_type+" already stored in field of this type"); - } else { - this[box_type] = box; - } - } - } else { - return; - } - } -} - -BoxParser.Box.prototype.parseLanguage = function(stream) { - this.language = stream.readUint16(); - var chars = []; - chars[0] = (this.language>>10)&0x1F; - chars[1] = (this.language>>5)&0x1F; - chars[2] = (this.language)&0x1F; - this.languageString = String.fromCharCode(chars[0]+0x60, chars[1]+0x60, chars[2]+0x60); -} - -// file:src/parsing/sampleentries/sampleentry.js -BoxParser.SAMPLE_ENTRY_TYPE_VISUAL = "Visual"; -BoxParser.SAMPLE_ENTRY_TYPE_AUDIO = "Audio"; -BoxParser.SAMPLE_ENTRY_TYPE_HINT = "Hint"; -BoxParser.SAMPLE_ENTRY_TYPE_METADATA = "Metadata"; -BoxParser.SAMPLE_ENTRY_TYPE_SUBTITLE = "Subtitle"; -BoxParser.SAMPLE_ENTRY_TYPE_SYSTEM = "System"; -BoxParser.SAMPLE_ENTRY_TYPE_TEXT = "Text"; - -BoxParser.SampleEntry.prototype.parseHeader = function(stream) { - stream.readUint8Array(6); - this.data_reference_index = stream.readUint16(); - this.hdr_size += 8; -} - -BoxParser.SampleEntry.prototype.parse = function(stream) { - this.parseHeader(stream); - this.data = stream.readUint8Array(this.size - this.hdr_size); -} - -BoxParser.SampleEntry.prototype.parseDataAndRewind = function(stream) { - this.parseHeader(stream); - this.data = stream.readUint8Array(this.size - this.hdr_size); - // restore the header size as if the sample entry header had not been parsed - this.hdr_size -= 8; - // rewinding - stream.position -= this.size-this.hdr_size; -} - -BoxParser.SampleEntry.prototype.parseFooter = function(stream) { - BoxParser.ContainerBox.prototype.parse.call(this, stream); -} - -// Base SampleEntry types with default parsing -BoxParser.createMediaSampleEntryCtor(BoxParser.SAMPLE_ENTRY_TYPE_HINT); -BoxParser.createMediaSampleEntryCtor(BoxParser.SAMPLE_ENTRY_TYPE_METADATA); -BoxParser.createMediaSampleEntryCtor(BoxParser.SAMPLE_ENTRY_TYPE_SUBTITLE); -BoxParser.createMediaSampleEntryCtor(BoxParser.SAMPLE_ENTRY_TYPE_SYSTEM); -BoxParser.createMediaSampleEntryCtor(BoxParser.SAMPLE_ENTRY_TYPE_TEXT); - -//Base SampleEntry types for Audio and Video with specific parsing -BoxParser.createMediaSampleEntryCtor(BoxParser.SAMPLE_ENTRY_TYPE_VISUAL, function(stream) { - var compressorname_length; - this.parseHeader(stream); - stream.readUint16(); - stream.readUint16(); - stream.readUint32Array(3); - this.width = stream.readUint16(); - this.height = stream.readUint16(); - this.horizresolution = stream.readUint32(); - this.vertresolution = stream.readUint32(); - stream.readUint32(); - this.frame_count = stream.readUint16(); - compressorname_length = Math.min(31, stream.readUint8()); - this.compressorname = stream.readString(compressorname_length); - if (compressorname_length < 31) { - stream.readString(31 - compressorname_length); - } - this.depth = stream.readUint16(); - stream.readUint16(); - this.parseFooter(stream); -}); - -BoxParser.createMediaSampleEntryCtor(BoxParser.SAMPLE_ENTRY_TYPE_AUDIO, function(stream) { - this.parseHeader(stream); - stream.readUint32Array(2); - this.channel_count = stream.readUint16(); - this.samplesize = stream.readUint16(); - stream.readUint16(); - stream.readUint16(); - this.samplerate = (stream.readUint32()/(1<<16)); - this.parseFooter(stream); -}); - -// Sample entries inheriting from Audio and Video -BoxParser.createSampleEntryCtor(BoxParser.SAMPLE_ENTRY_TYPE_VISUAL, "avc1"); -BoxParser.createSampleEntryCtor(BoxParser.SAMPLE_ENTRY_TYPE_VISUAL, "avc2"); -BoxParser.createSampleEntryCtor(BoxParser.SAMPLE_ENTRY_TYPE_VISUAL, "avc3"); -BoxParser.createSampleEntryCtor(BoxParser.SAMPLE_ENTRY_TYPE_VISUAL, "avc4"); -BoxParser.createSampleEntryCtor(BoxParser.SAMPLE_ENTRY_TYPE_VISUAL, "av01"); -BoxParser.createSampleEntryCtor(BoxParser.SAMPLE_ENTRY_TYPE_VISUAL, "hvc1"); -BoxParser.createSampleEntryCtor(BoxParser.SAMPLE_ENTRY_TYPE_VISUAL, "hev1"); -BoxParser.createSampleEntryCtor(BoxParser.SAMPLE_ENTRY_TYPE_VISUAL, "vvc1"); -BoxParser.createSampleEntryCtor(BoxParser.SAMPLE_ENTRY_TYPE_VISUAL, "vvi1"); -BoxParser.createSampleEntryCtor(BoxParser.SAMPLE_ENTRY_TYPE_VISUAL, "vvs1"); -BoxParser.createSampleEntryCtor(BoxParser.SAMPLE_ENTRY_TYPE_VISUAL, "vvcN"); -BoxParser.createSampleEntryCtor(BoxParser.SAMPLE_ENTRY_TYPE_VISUAL, "vp08"); -BoxParser.createSampleEntryCtor(BoxParser.SAMPLE_ENTRY_TYPE_VISUAL, "vp09"); -BoxParser.createSampleEntryCtor(BoxParser.SAMPLE_ENTRY_TYPE_AUDIO, "mp4a"); -BoxParser.createSampleEntryCtor(BoxParser.SAMPLE_ENTRY_TYPE_AUDIO, "ac-3"); -BoxParser.createSampleEntryCtor(BoxParser.SAMPLE_ENTRY_TYPE_AUDIO, "ec-3"); -BoxParser.createSampleEntryCtor(BoxParser.SAMPLE_ENTRY_TYPE_AUDIO, "Opus"); - -// Encrypted sample entries -BoxParser.createEncryptedSampleEntryCtor(BoxParser.SAMPLE_ENTRY_TYPE_VISUAL, "encv"); -BoxParser.createEncryptedSampleEntryCtor(BoxParser.SAMPLE_ENTRY_TYPE_AUDIO, "enca"); -BoxParser.createEncryptedSampleEntryCtor(BoxParser.SAMPLE_ENTRY_TYPE_SUBTITLE, "encu"); -BoxParser.createEncryptedSampleEntryCtor(BoxParser.SAMPLE_ENTRY_TYPE_SYSTEM, "encs"); -BoxParser.createEncryptedSampleEntryCtor(BoxParser.SAMPLE_ENTRY_TYPE_TEXT, "enct"); -BoxParser.createEncryptedSampleEntryCtor(BoxParser.SAMPLE_ENTRY_TYPE_METADATA, "encm"); -// file:src/parsing/a1lx.js -BoxParser.createBoxCtor("a1lx", function(stream) { - var large_size = stream.readUint8() & 1; - var FieldLength = ((large_size & 1) + 1) * 16; - this.layer_size = []; - for (var i = 0; i < 3; i++) { - if (FieldLength == 16) { - this.layer_size[i] = stream.readUint16(); - } else { - this.layer_size[i] = stream.readUint32(); - } - } -});// file:src/parsing/a1op.js -BoxParser.createBoxCtor("a1op", function(stream) { - this.op_index = stream.readUint8(); -});// file:src/parsing/auxC.js -BoxParser.createFullBoxCtor("auxC", function(stream) { - this.aux_type = stream.readCString(); - var aux_subtype_length = this.size - this.hdr_size - (this.aux_type.length + 1); - this.aux_subtype = stream.readUint8Array(aux_subtype_length); -});// file:src/parsing/av1C.js -BoxParser.createBoxCtor("av1C", function(stream) { - var i; - var toparse; - var tmp = stream.readUint8(); - if ((tmp >> 7) & 0x1 !== 1) { - Log.error("av1C marker problem"); - return; - } - this.version = tmp & 0x7F; - if (this.version !== 1) { - Log.error("av1C version "+this.version+" not supported"); - return; - } - tmp = stream.readUint8(); - this.seq_profile = (tmp >> 5) & 0x7; - this.seq_level_idx_0 = tmp & 0x1F; - tmp = stream.readUint8(); - this.seq_tier_0 = (tmp >> 7) & 0x1; - this.high_bitdepth = (tmp >> 6) & 0x1; - this.twelve_bit = (tmp >> 5) & 0x1; - this.monochrome = (tmp >> 4) & 0x1; - this.chroma_subsampling_x = (tmp >> 3) & 0x1; - this.chroma_subsampling_y = (tmp >> 2) & 0x1; - this.chroma_sample_position = (tmp & 0x3); - tmp = stream.readUint8(); - this.reserved_1 = (tmp >> 5) & 0x7; - if (this.reserved_1 !== 0) { - Log.error("av1C reserved_1 parsing problem"); - return; - } - this.initial_presentation_delay_present = (tmp >> 4) & 0x1; - if (this.initial_presentation_delay_present === 1) { - this.initial_presentation_delay_minus_one = (tmp & 0xF); - } else { - this.reserved_2 = (tmp & 0xF); - if (this.reserved_2 !== 0) { - Log.error("av1C reserved_2 parsing problem"); - return; - } - } - - var configOBUs_length = this.size - this.hdr_size - 4; - this.configOBUs = stream.readUint8Array(configOBUs_length); -}); - -// file:src/parsing/avcC.js -BoxParser.createBoxCtor("avcC", function(stream) { - var i; - var toparse; - this.configurationVersion = stream.readUint8(); - this.AVCProfileIndication = stream.readUint8(); - this.profile_compatibility = stream.readUint8(); - this.AVCLevelIndication = stream.readUint8(); - this.lengthSizeMinusOne = (stream.readUint8() & 0x3); - this.nb_SPS_nalus = (stream.readUint8() & 0x1F); - toparse = this.size - this.hdr_size - 6; - this.SPS = []; - for (i = 0; i < this.nb_SPS_nalus; i++) { - this.SPS[i] = {}; - this.SPS[i].length = stream.readUint16(); - this.SPS[i].nalu = stream.readUint8Array(this.SPS[i].length); - toparse -= 2+this.SPS[i].length; - } - this.nb_PPS_nalus = stream.readUint8(); - toparse--; - this.PPS = []; - for (i = 0; i < this.nb_PPS_nalus; i++) { - this.PPS[i] = {}; - this.PPS[i].length = stream.readUint16(); - this.PPS[i].nalu = stream.readUint8Array(this.PPS[i].length); - toparse -= 2+this.PPS[i].length; - } - if (toparse>0) { - this.ext = stream.readUint8Array(toparse); - } -}); - -// file:src/parsing/btrt.js -BoxParser.createBoxCtor("btrt", function(stream) { - this.bufferSizeDB = stream.readUint32(); - this.maxBitrate = stream.readUint32(); - this.avgBitrate = stream.readUint32(); -}); - -// file:src/parsing/clap.js -BoxParser.createBoxCtor("clap", function(stream) { - this.cleanApertureWidthN = stream.readUint32(); - this.cleanApertureWidthD = stream.readUint32(); - this.cleanApertureHeightN = stream.readUint32(); - this.cleanApertureHeightD = stream.readUint32(); - this.horizOffN = stream.readUint32(); - this.horizOffD = stream.readUint32(); - this.vertOffN = stream.readUint32(); - this.vertOffD = stream.readUint32(); -});// file:src/parsing/clli.js -BoxParser.createBoxCtor("clli", function(stream) { - this.max_content_light_level = stream.readUint16(); - this.max_pic_average_light_level = stream.readUint16(); -}); - -// file:src/parsing/co64.js -BoxParser.createFullBoxCtor("co64", function(stream) { - var entry_count; - var i; - entry_count = stream.readUint32(); - this.chunk_offsets = []; - if (this.version === 0) { - for(i=0; i> 7; - } else if (this.colour_type === 'rICC') { - this.ICC_profile = stream.readUint8Array(this.size - 4); - } else if (this.colour_type === 'prof') { - this.ICC_profile = stream.readUint8Array(this.size - 4); - } -});// file:src/parsing/cprt.js -BoxParser.createFullBoxCtor("cprt", function (stream) { - this.parseLanguage(stream); - this.notice = stream.readCString(); -}); - -// file:src/parsing/cslg.js -BoxParser.createFullBoxCtor("cslg", function(stream) { - var entry_count; - if (this.version === 0) { - this.compositionToDTSShift = stream.readInt32(); /* signed */ - this.leastDecodeToDisplayDelta = stream.readInt32(); /* signed */ - this.greatestDecodeToDisplayDelta = stream.readInt32(); /* signed */ - this.compositionStartTime = stream.readInt32(); /* signed */ - this.compositionEndTime = stream.readInt32(); /* signed */ - } -}); - -// file:src/parsing/ctts.js -BoxParser.createFullBoxCtor("ctts", function(stream) { - var entry_count; - var i; - entry_count = stream.readUint32(); - this.sample_counts = []; - this.sample_offsets = []; - if (this.version === 0) { - for(i=0; i> 6; - this.bsid = ((tmp_byte1 >> 1) & 0x1F); - this.bsmod = ((tmp_byte1 & 0x1) << 2) | ((tmp_byte2 >> 6) & 0x3); - this.acmod = ((tmp_byte2 >> 3) & 0x7); - this.lfeon = ((tmp_byte2 >> 2) & 0x1); - this.bit_rate_code = (tmp_byte2 & 0x3) | ((tmp_byte3 >> 5) & 0x7); -}); - -// file:src/parsing/dec3.js -BoxParser.createBoxCtor("dec3", function(stream) { - var tmp_16 = stream.readUint16(); - this.data_rate = tmp_16 >> 3; - this.num_ind_sub = tmp_16 & 0x7; - this.ind_subs = []; - for (var i = 0; i < this.num_ind_sub+1; i++) { - var ind_sub = {}; - this.ind_subs.push(ind_sub); - var tmp_byte1 = stream.readUint8(); - var tmp_byte2 = stream.readUint8(); - var tmp_byte3 = stream.readUint8(); - ind_sub.fscod = tmp_byte1 >> 6; - ind_sub.bsid = ((tmp_byte1 >> 1) & 0x1F); - ind_sub.bsmod = ((tmp_byte1 & 0x1) << 4) | ((tmp_byte2 >> 4) & 0xF); - ind_sub.acmod = ((tmp_byte2 >> 1) & 0x7); - ind_sub.lfeon = (tmp_byte2 & 0x1); - ind_sub.num_dep_sub = ((tmp_byte3 >> 1) & 0xF); - if (ind_sub.num_dep_sub > 0) { - ind_sub.chan_loc = ((tmp_byte3 & 0x1) << 8) | stream.readUint8(); - } - } -}); - -// file:src/parsing/dfLa.js -BoxParser.createFullBoxCtor("dfLa", function(stream) { - var BLOCKTYPE_MASK = 0x7F; - var LASTMETADATABLOCKFLAG_MASK = 0x80; - - var boxesFound = []; - var knownBlockTypes = [ - "STREAMINFO", - "PADDING", - "APPLICATION", - "SEEKTABLE", - "VORBIS_COMMENT", - "CUESHEET", - "PICTURE", - "RESERVED" - ]; - - // dfLa is a FullBox - this.parseFullHeader(stream); - - // for (i=0; ; i++) { // to end of box - do { - var flagAndType = stream.readUint8(); - - var type = Math.min( - (flagAndType & BLOCKTYPE_MASK), - (knownBlockTypes.length - 1) - ); - - // if this is a STREAMINFO block, read the true samplerate since this - // can be different to the AudioSampleEntry samplerate. - if (!(type)) { - // read past all the other stuff - stream.readUint8Array(13); - - // extract samplerate - this.samplerate = (stream.readUint32() >> 12); - - // read to end of STREAMINFO - stream.readUint8Array(20); - } else { - // not interested in other block types so just discard length bytes - stream.readUint8Array(stream.readUint24()); - } - - boxesFound.push(knownBlockTypes[type]); - - if (!!(flagAndType & LASTMETADATABLOCKFLAG_MASK)) { - break; - } - } while (true); - - this.numMetadataBlocks = - boxesFound.length + " (" + boxesFound.join(", ") + ")"; -}); -// file:src/parsing/dimm.js -BoxParser.createBoxCtor("dimm", function(stream) { - this.bytessent = stream.readUint64(); -}); - -// file:src/parsing/dmax.js -BoxParser.createBoxCtor("dmax", function(stream) { - this.time = stream.readUint32(); -}); - -// file:src/parsing/dmed.js -BoxParser.createBoxCtor("dmed", function(stream) { - this.bytessent = stream.readUint64(); -}); - -// file:src/parsing/dOps.js -BoxParser.createBoxCtor("dOps", function(stream) { - this.Version = stream.readUint8(); - this.OutputChannelCount = stream.readUint8(); - this.PreSkip = stream.readUint16(); - this.InputSampleRate = stream.readUint32(); - this.OutputGain = stream.readInt16(); - this.ChannelMappingFamily = stream.readUint8(); - if (this.ChannelMappingFamily !== 0) { - this.StreamCount = stream.readUint8(); - this.CoupledCount = stream.readUint8(); - this.ChannelMapping = []; - for (var i = 0; i < this.OutputChannelCount; i++) { - this.ChannelMapping[i] = stream.readUint8(); - } - } -}); - -// file:src/parsing/dref.js -BoxParser.createFullBoxCtor("dref", function(stream) { - var ret; - var box; - this.entries = []; - var entry_count = stream.readUint32(); - for (var i = 0; i < entry_count; i++) { - ret = BoxParser.parseOneBox(stream, false, this.size - (stream.getPosition() - this.start)); - if (ret.code === BoxParser.OK) { - box = ret.box; - this.entries.push(box); - } else { - return; - } - } -}); - -// file:src/parsing/drep.js -BoxParser.createBoxCtor("drep", function(stream) { - this.bytessent = stream.readUint64(); -}); - -// file:src/parsing/elng.js -BoxParser.createFullBoxCtor("elng", function(stream) { - this.extended_language = stream.readString(this.size-this.hdr_size); -}); - -// file:src/parsing/elst.js -BoxParser.createFullBoxCtor("elst", function(stream) { - this.entries = []; - var entry_count = stream.readUint32(); - for (var i = 0; i < entry_count; i++) { - var entry = {}; - this.entries.push(entry); - if (this.version === 1) { - entry.segment_duration = stream.readUint64(); - entry.media_time = stream.readInt64(); - } else { - entry.segment_duration = stream.readUint32(); - entry.media_time = stream.readInt32(); - } - entry.media_rate_integer = stream.readInt16(); - entry.media_rate_fraction = stream.readInt16(); - } -}); - -// file:src/parsing/emsg.js -BoxParser.createFullBoxCtor("emsg", function(stream) { - if (this.version == 1) { - this.timescale = stream.readUint32(); - this.presentation_time = stream.readUint64(); - this.event_duration = stream.readUint32(); - this.id = stream.readUint32(); - this.scheme_id_uri = stream.readCString(); - this.value = stream.readCString(); - } else { - this.scheme_id_uri = stream.readCString(); - this.value = stream.readCString(); - this.timescale = stream.readUint32(); - this.presentation_time_delta = stream.readUint32(); - this.event_duration = stream.readUint32(); - this.id = stream.readUint32(); - } - var message_size = this.size - this.hdr_size - (4*4 + (this.scheme_id_uri.length+1) + (this.value.length+1)); - if (this.version == 1) { - message_size -= 4; - } - this.message_data = stream.readUint8Array(message_size); -}); - -// file:src/parsing/esds.js -BoxParser.createFullBoxCtor("esds", function(stream) { - var esd_data = stream.readUint8Array(this.size-this.hdr_size); - if (typeof MPEG4DescriptorParser !== "undefined") { - var esd_parser = new MPEG4DescriptorParser(); - this.esd = esd_parser.parseOneDescriptor(new DataStream(esd_data.buffer, 0, DataStream.BIG_ENDIAN)); - } -}); - -// file:src/parsing/fiel.js -BoxParser.createBoxCtor("fiel", function(stream) { - this.fieldCount = stream.readUint8(); - this.fieldOrdering = stream.readUint8(); -}); - -// file:src/parsing/frma.js -BoxParser.createBoxCtor("frma", function(stream) { - this.data_format = stream.readString(4); -}); - -// file:src/parsing/ftyp.js -BoxParser.createBoxCtor("ftyp", function(stream) { - var toparse = this.size - this.hdr_size; - this.major_brand = stream.readString(4); - this.minor_version = stream.readUint32(); - toparse -= 8; - this.compatible_brands = []; - var i = 0; - while (toparse>=4) { - this.compatible_brands[i] = stream.readString(4); - toparse -= 4; - i++; - } -}); - -// file:src/parsing/hdlr.js -BoxParser.createFullBoxCtor("hdlr", function(stream) { - if (this.version === 0) { - stream.readUint32(); - this.handler = stream.readString(4); - stream.readUint32Array(3); - this.name = stream.readString(this.size-this.hdr_size-20); - if (this.name[this.name.length-1]==='\0') { - this.name = this.name.slice(0,-1); - } - } -}); - -// file:src/parsing/hvcC.js -BoxParser.createBoxCtor("hvcC", function(stream) { - var i, j; - var nb_nalus; - var length; - var tmp_byte; - this.configurationVersion = stream.readUint8(); - tmp_byte = stream.readUint8(); - this.general_profile_space = tmp_byte >> 6; - this.general_tier_flag = (tmp_byte & 0x20) >> 5; - this.general_profile_idc = (tmp_byte & 0x1F); - this.general_profile_compatibility = stream.readUint32(); - this.general_constraint_indicator = stream.readUint8Array(6); - this.general_level_idc = stream.readUint8(); - this.min_spatial_segmentation_idc = stream.readUint16() & 0xFFF; - this.parallelismType = (stream.readUint8() & 0x3); - this.chroma_format_idc = (stream.readUint8() & 0x3); - this.bit_depth_luma_minus8 = (stream.readUint8() & 0x7); - this.bit_depth_chroma_minus8 = (stream.readUint8() & 0x7); - this.avgFrameRate = stream.readUint16(); - tmp_byte = stream.readUint8(); - this.constantFrameRate = (tmp_byte >> 6); - this.numTemporalLayers = (tmp_byte & 0XD) >> 3; - this.temporalIdNested = (tmp_byte & 0X4) >> 2; - this.lengthSizeMinusOne = (tmp_byte & 0X3); - - this.nalu_arrays = []; - var numOfArrays = stream.readUint8(); - for (i = 0; i < numOfArrays; i++) { - var nalu_array = []; - this.nalu_arrays.push(nalu_array); - tmp_byte = stream.readUint8() - nalu_array.completeness = (tmp_byte & 0x80) >> 7; - nalu_array.nalu_type = tmp_byte & 0x3F; - var numNalus = stream.readUint16(); - for (j = 0; j < numNalus; j++) { - var nalu = {} - nalu_array.push(nalu); - length = stream.readUint16(); - nalu.data = stream.readUint8Array(length); - } - } -}); - -// file:src/parsing/iinf.js -BoxParser.createFullBoxCtor("iinf", function(stream) { - var ret; - if (this.version === 0) { - this.entry_count = stream.readUint16(); - } else { - this.entry_count = stream.readUint32(); - } - this.item_infos = []; - for (var i = 0; i < this.entry_count; i++) { - ret = BoxParser.parseOneBox(stream, false, this.size - (stream.getPosition() - this.start)); - if (ret.code === BoxParser.OK) { - if (ret.box.type !== "infe") { - Log.error("BoxParser", "Expected 'infe' box, got "+ret.box.type); - } - this.item_infos[i] = ret.box; - } else { - return; - } - } -}); - -// file:src/parsing/iloc.js -BoxParser.createFullBoxCtor("iloc", function(stream) { - var byte; - byte = stream.readUint8(); - this.offset_size = (byte >> 4) & 0xF; - this.length_size = byte & 0xF; - byte = stream.readUint8(); - this.base_offset_size = (byte >> 4) & 0xF; - if (this.version === 1 || this.version === 2) { - this.index_size = byte & 0xF; - } else { - this.index_size = 0; - // reserved = byte & 0xF; - } - this.items = []; - var item_count = 0; - if (this.version < 2) { - item_count = stream.readUint16(); - } else if (this.version === 2) { - item_count = stream.readUint32(); - } else { - throw "version of iloc box not supported"; - } - for (var i = 0; i < item_count; i++) { - var item = {}; - this.items.push(item); - if (this.version < 2) { - item.item_ID = stream.readUint16(); - } else if (this.version === 2) { - item.item_ID = stream.readUint16(); - } else { - throw "version of iloc box not supported"; - } - if (this.version === 1 || this.version === 2) { - item.construction_method = (stream.readUint16() & 0xF); - } else { - item.construction_method = 0; - } - item.data_reference_index = stream.readUint16(); - switch(this.base_offset_size) { - case 0: - item.base_offset = 0; - break; - case 4: - item.base_offset = stream.readUint32(); - break; - case 8: - item.base_offset = stream.readUint64(); - break; - default: - throw "Error reading base offset size"; - } - var extent_count = stream.readUint16(); - item.extents = []; - for (var j=0; j < extent_count; j++) { - var extent = {}; - item.extents.push(extent); - if (this.version === 1 || this.version === 2) { - switch(this.index_size) { - case 0: - extent.extent_index = 0; - break; - case 4: - extent.extent_index = stream.readUint32(); - break; - case 8: - extent.extent_index = stream.readUint64(); - break; - default: - throw "Error reading extent index"; - } - } - switch(this.offset_size) { - case 0: - extent.extent_offset = 0; - break; - case 4: - extent.extent_offset = stream.readUint32(); - break; - case 8: - extent.extent_offset = stream.readUint64(); - break; - default: - throw "Error reading extent index"; - } - switch(this.length_size) { - case 0: - extent.extent_length = 0; - break; - case 4: - extent.extent_length = stream.readUint32(); - break; - case 8: - extent.extent_length = stream.readUint64(); - break; - default: - throw "Error reading extent index"; - } - } - } -}); - -// file:src/parsing/imir.js -BoxParser.createBoxCtor("imir", function(stream) { - var tmp = stream.readUint8(); - this.reserved = tmp >> 7; - this.axis = tmp & 1; -});// file:src/parsing/infe.js -BoxParser.createFullBoxCtor("infe", function(stream) { - if (this.version === 0 || this.version === 1) { - this.item_ID = stream.readUint16(); - this.item_protection_index = stream.readUint16(); - this.item_name = stream.readCString(); - this.content_type = stream.readCString(); - this.content_encoding = stream.readCString(); - } - if (this.version === 1) { - this.extension_type = stream.readString(4); - Log.warn("BoxParser", "Cannot parse extension type"); - stream.seek(this.start+this.size); - return; - } - if (this.version >= 2) { - if (this.version === 2) { - this.item_ID = stream.readUint16(); - } else if (this.version === 3) { - this.item_ID = stream.readUint32(); - } - this.item_protection_index = stream.readUint16(); - this.item_type = stream.readString(4); - this.item_name = stream.readCString(); - if (this.item_type === "mime") { - this.content_type = stream.readCString(); - this.content_encoding = stream.readCString(); - } else if (this.item_type === "uri ") { - this.item_uri_type = stream.readCString(); - } - } -}); -// file:src/parsing/ipma.js -BoxParser.createFullBoxCtor("ipma", function(stream) { - var i, j; - entry_count = stream.readUint32(); - this.associations = []; - for(i=0; i> 7) === 1; - if (this.flags & 0x1) { - p.property_index = (tmp & 0x7F) << 8 | stream.readUint8(); - } else { - p.property_index = (tmp & 0x7F); - } - } - } -}); - -// file:src/parsing/iref.js -BoxParser.createFullBoxCtor("iref", function(stream) { - var ret; - var entryCount; - var box; - this.references = []; - - while (stream.getPosition() < this.start+this.size) { - ret = BoxParser.parseOneBox(stream, true, this.size - (stream.getPosition() - this.start)); - if (ret.code === BoxParser.OK) { - if (this.version === 0) { - box = new BoxParser.SingleItemTypeReferenceBox(ret.type, ret.size, ret.hdr_size, ret.start); - } else { - box = new BoxParser.SingleItemTypeReferenceBoxLarge(ret.type, ret.size, ret.hdr_size, ret.start); - } - if (box.write === BoxParser.Box.prototype.write && box.type !== "mdat") { - Log.warn("BoxParser", box.type+" box writing not yet implemented, keeping unparsed data in memory for later write"); - box.parseDataAndRewind(stream); - } - box.parse(stream); - this.references.push(box); - } else { - return; - } - } -}); -// file:src/parsing/irot.js -BoxParser.createBoxCtor("irot", function(stream) { - this.angle = stream.readUint8() & 0x3; -}); - -// file:src/parsing/ispe.js -BoxParser.createFullBoxCtor("ispe", function(stream) { - this.image_width = stream.readUint32(); - this.image_height = stream.readUint32(); -});// file:src/parsing/kind.js -BoxParser.createFullBoxCtor("kind", function(stream) { - this.schemeURI = stream.readCString(); - this.value = stream.readCString(); -}); -// file:src/parsing/leva.js -BoxParser.createFullBoxCtor("leva", function(stream) { - var count = stream.readUint8(); - this.levels = []; - for (var i = 0; i < count; i++) { - var level = {}; - this.levels[i] = level; - level.track_ID = stream.readUint32(); - var tmp_byte = stream.readUint8(); - level.padding_flag = tmp_byte >> 7; - level.assignment_type = tmp_byte & 0x7F; - switch (level.assignment_type) { - case 0: - level.grouping_type = stream.readString(4); - break; - case 1: - level.grouping_type = stream.readString(4); - level.grouping_type_parameter = stream.readUint32(); - break; - case 2: - break; - case 3: - break; - case 4: - level.sub_track_id = stream.readUint32(); - break; - default: - Log.warn("BoxParser", "Unknown leva assignement type"); - } - } -}); - -// file:src/parsing/lsel.js -BoxParser.createBoxCtor("lsel", function(stream) { - this.layer_id = stream.readUint16(); -});// file:src/parsing/maxr.js -BoxParser.createBoxCtor("maxr", function(stream) { - this.period = stream.readUint32(); - this.bytes = stream.readUint32(); -}); - -// file:src/parsing/mdcv.js -BoxParser.createBoxCtor("mdcv", function(stream) { - this.display_primaries = []; - this.display_primaries[0] = {}; - this.display_primaries[0].x = stream.readUint16(); - this.display_primaries[0].y = stream.readUint16(); - this.display_primaries[1] = {}; - this.display_primaries[1].x = stream.readUint16(); - this.display_primaries[1].y = stream.readUint16(); - this.display_primaries[2] = {}; - this.display_primaries[2].x = stream.readUint16(); - this.display_primaries[2].y = stream.readUint16(); - this.white_point = {}; - this.white_point.x = stream.readUint16(); - this.white_point.y = stream.readUint16(); - this.max_display_mastering_luminance = stream.readUint32(); - this.min_display_mastering_luminance = stream.readUint32(); -}); - -// file:src/parsing/mdhd.js -BoxParser.createFullBoxCtor("mdhd", function(stream) { - if (this.version == 1) { - this.creation_time = stream.readUint64(); - this.modification_time = stream.readUint64(); - this.timescale = stream.readUint32(); - this.duration = stream.readUint64(); - } else { - this.creation_time = stream.readUint32(); - this.modification_time = stream.readUint32(); - this.timescale = stream.readUint32(); - this.duration = stream.readUint32(); - } - this.parseLanguage(stream); - stream.readUint16(); -}); - -// file:src/parsing/mehd.js -BoxParser.createFullBoxCtor("mehd", function(stream) { - if (this.flags & 0x1) { - Log.warn("BoxParser", "mehd box incorrectly uses flags set to 1, converting version to 1"); - this.version = 1; - } - if (this.version == 1) { - this.fragment_duration = stream.readUint64(); - } else { - this.fragment_duration = stream.readUint32(); - } -}); - -// file:src/parsing/meta.js -BoxParser.createFullBoxCtor("meta", function(stream) { - this.boxes = []; - BoxParser.ContainerBox.prototype.parse.call(this, stream); -}); -// file:src/parsing/mfhd.js -BoxParser.createFullBoxCtor("mfhd", function(stream) { - this.sequence_number = stream.readUint32(); -}); - -// file:src/parsing/mfro.js -BoxParser.createFullBoxCtor("mfro", function(stream) { - this._size = stream.readUint32(); -}); - -// file:src/parsing/mvhd.js -BoxParser.createFullBoxCtor("mvhd", function(stream) { - if (this.version == 1) { - this.creation_time = stream.readUint64(); - this.modification_time = stream.readUint64(); - this.timescale = stream.readUint32(); - this.duration = stream.readUint64(); - } else { - this.creation_time = stream.readUint32(); - this.modification_time = stream.readUint32(); - this.timescale = stream.readUint32(); - this.duration = stream.readUint32(); - } - this.rate = stream.readUint32(); - this.volume = stream.readUint16()>>8; - stream.readUint16(); - stream.readUint32Array(2); - this.matrix = stream.readUint32Array(9); - stream.readUint32Array(6); - this.next_track_id = stream.readUint32(); -}); -// file:src/parsing/npck.js -BoxParser.createBoxCtor("npck", function(stream) { - this.packetssent = stream.readUint32(); -}); - -// file:src/parsing/nump.js -BoxParser.createBoxCtor("nump", function(stream) { - this.packetssent = stream.readUint64(); -}); - -// file:src/parsing/padb.js -BoxParser.createFullBoxCtor("padb", function(stream) { - var sample_count = stream.readUint32(); - this.padbits = []; - for (var i = 0; i < Math.floor((sample_count+1)/2); i++) { - this.padbits = stream.readUint8(); - } -}); - -// file:src/parsing/pasp.js -BoxParser.createBoxCtor("pasp", function(stream) { - this.hSpacing = stream.readUint32(); - this.vSpacing = stream.readUint32(); -});// file:src/parsing/payl.js -BoxParser.createBoxCtor("payl", function(stream) { - this.text = stream.readString(this.size - this.hdr_size); -}); - -// file:src/parsing/payt.js -BoxParser.createBoxCtor("payt", function(stream) { - this.payloadID = stream.readUint32(); - var count = stream.readUint8(); - this.rtpmap_string = stream.readString(count); -}); - -// file:src/parsing/pdin.js -BoxParser.createFullBoxCtor("pdin", function(stream) { - var count = (this.size - this.hdr_size)/8; - this.rate = []; - this.initial_delay = []; - for (var i = 0; i < count; i++) { - this.rate[i] = stream.readUint32(); - this.initial_delay[i] = stream.readUint32(); - } -}); - -// file:src/parsing/pitm.js -BoxParser.createFullBoxCtor("pitm", function(stream) { - if (this.version === 0) { - this.item_id = stream.readUint16(); - } else { - this.item_id = stream.readUint32(); - } -}); - -// file:src/parsing/pixi.js -BoxParser.createFullBoxCtor("pixi", function(stream) { - var i; - this.num_channels = stream.readUint8(); - this.bits_per_channels = []; - for (i = 0; i < this.num_channels; i++) { - this.bits_per_channels[i] = stream.readUint8(); - } -}); - -// file:src/parsing/pmax.js -BoxParser.createBoxCtor("pmax", function(stream) { - this.bytes = stream.readUint32(); -}); - -// file:src/parsing/prft.js -BoxParser.createFullBoxCtor("prft", function(stream) { - this.ref_track_id = stream.readUint32(); - this.ntp_timestamp = stream.readUint64(); - if (this.version === 0) { - this.media_time = stream.readUint32(); - } else { - this.media_time = stream.readUint64(); - } -}); - -// file:src/parsing/pssh.js -BoxParser.createFullBoxCtor("pssh", function(stream) { - this.system_id = BoxParser.parseHex16(stream); - if (this.version > 0) { - var count = stream.readUint32(); - this.kid = []; - for (var i = 0; i < count; i++) { - this.kid[i] = BoxParser.parseHex16(stream); - } - } - var datasize = stream.readUint32(); - if (datasize > 0) { - this.data = stream.readUint8Array(datasize); - } -}); - -// file:src/parsing/qt/clef.js -BoxParser.createFullBoxCtor("clef", function(stream) { - this.width = stream.readUint32(); - this.height = stream.readUint32(); -});// file:src/parsing/qt/enof.js -BoxParser.createFullBoxCtor("enof", function(stream) { - this.width = stream.readUint32(); - this.height = stream.readUint32(); -});// file:src/parsing/qt/prof.js -BoxParser.createFullBoxCtor("prof", function(stream) { - this.width = stream.readUint32(); - this.height = stream.readUint32(); -});// file:src/parsing/qt/tapt.js -BoxParser.createContainerBoxCtor("tapt", null, [ "clef", "prof", "enof"]);// file:src/parsing/rtp.js -BoxParser.createBoxCtor("rtp ", function(stream) { - this.descriptionformat = stream.readString(4); - this.sdptext = stream.readString(this.size - this.hdr_size - 4); -}); - -// file:src/parsing/saio.js -BoxParser.createFullBoxCtor("saio", function(stream) { - if (this.flags & 0x1) { - this.aux_info_type = stream.readUint32(); - this.aux_info_type_parameter = stream.readUint32(); - } - var count = stream.readUint32(); - this.offset = []; - for (var i = 0; i < count; i++) { - if (this.version === 0) { - this.offset[i] = stream.readUint32(); - } else { - this.offset[i] = stream.readUint64(); - } - } -}); -// file:src/parsing/saiz.js -BoxParser.createFullBoxCtor("saiz", function(stream) { - if (this.flags & 0x1) { - this.aux_info_type = stream.readUint32(); - this.aux_info_type_parameter = stream.readUint32(); - } - this.default_sample_info_size = stream.readUint8(); - var count = stream.readUint32(); - this.sample_info_size = []; - if (this.default_sample_info_size === 0) { - for (var i = 0; i < count; i++) { - this.sample_info_size[i] = stream.readUint8(); - } - } -}); - -// file:src/parsing/sampleentries/mett.js -BoxParser.createSampleEntryCtor(BoxParser.SAMPLE_ENTRY_TYPE_METADATA, "mett", function(stream) { - this.parseHeader(stream); - this.content_encoding = stream.readCString(); - this.mime_format = stream.readCString(); - this.parseFooter(stream); -}); - -// file:src/parsing/sampleentries/metx.js -BoxParser.createSampleEntryCtor(BoxParser.SAMPLE_ENTRY_TYPE_METADATA, "metx", function(stream) { - this.parseHeader(stream); - this.content_encoding = stream.readCString(); - this.namespace = stream.readCString(); - this.schema_location = stream.readCString(); - this.parseFooter(stream); -}); - -// file:src/parsing/sampleentries/sbtt.js -BoxParser.createSampleEntryCtor(BoxParser.SAMPLE_ENTRY_TYPE_SUBTITLE, "sbtt", function(stream) { - this.parseHeader(stream); - this.content_encoding = stream.readCString(); - this.mime_format = stream.readCString(); - this.parseFooter(stream); -}); - -// file:src/parsing/sampleentries/stpp.js -BoxParser.createSampleEntryCtor(BoxParser.SAMPLE_ENTRY_TYPE_SUBTITLE, "stpp", function(stream) { - this.parseHeader(stream); - this.namespace = stream.readCString(); - this.schema_location = stream.readCString(); - this.auxiliary_mime_types = stream.readCString(); - this.parseFooter(stream); -}); - -// file:src/parsing/sampleentries/stxt.js -BoxParser.createSampleEntryCtor(BoxParser.SAMPLE_ENTRY_TYPE_SUBTITLE, "stxt", function(stream) { - this.parseHeader(stream); - this.content_encoding = stream.readCString(); - this.mime_format = stream.readCString(); - this.parseFooter(stream); -}); - -// file:src/parsing/sampleentries/tx3g.js -BoxParser.createSampleEntryCtor(BoxParser.SAMPLE_ENTRY_TYPE_SUBTITLE, "tx3g", function(stream) { - this.parseHeader(stream); - this.displayFlags = stream.readUint32(); - this.horizontal_justification = stream.readInt8(); - this.vertical_justification = stream.readInt8(); - this.bg_color_rgba = stream.readUint8Array(4); - this.box_record = stream.readInt16Array(4); - this.style_record = stream.readUint8Array(12); - this.parseFooter(stream); -}); -// file:src/parsing/sampleentries/wvtt.js -BoxParser.createSampleEntryCtor(BoxParser.SAMPLE_ENTRY_TYPE_METADATA, "wvtt", function(stream) { - this.parseHeader(stream); - this.parseFooter(stream); -}); - -// file:src/parsing/samplegroups/alst.js -BoxParser.createSampleGroupCtor("alst", function(stream) { - var i; - var roll_count = stream.readUint16(); - this.first_output_sample = stream.readUint16(); - this.sample_offset = []; - for (i = 0; i < roll_count; i++) { - this.sample_offset[i] = stream.readUint32(); - } - var remaining = this.description_length - 4 - 4*roll_count; - this.num_output_samples = []; - this.num_total_samples = []; - for (i = 0; i < remaining/4; i++) { - this.num_output_samples[i] = stream.readUint16(); - this.num_total_samples[i] = stream.readUint16(); - } -}); - -// file:src/parsing/samplegroups/avll.js -BoxParser.createSampleGroupCtor("avll", function(stream) { - this.layerNumber = stream.readUint8(); - this.accurateStatisticsFlag = stream.readUint8(); - this.avgBitRate = stream.readUint16(); - this.avgFrameRate = stream.readUint16(); -}); - -// file:src/parsing/samplegroups/avss.js -BoxParser.createSampleGroupCtor("avss", function(stream) { - this.subSequenceIdentifier = stream.readUint16(); - this.layerNumber = stream.readUint8(); - var tmp_byte = stream.readUint8(); - this.durationFlag = tmp_byte >> 7; - this.avgRateFlag = (tmp_byte >> 6) & 0x1; - if (this.durationFlag) { - this.duration = stream.readUint32(); - } - if (this.avgRateFlag) { - this.accurateStatisticsFlag = stream.readUint8(); - this.avgBitRate = stream.readUint16(); - this.avgFrameRate = stream.readUint16(); - } - this.dependency = []; - var numReferences = stream.readUint8(); - for (var i = 0; i < numReferences; i++) { - var dependencyInfo = {}; - this.dependency.push(dependencyInfo); - dependencyInfo.subSeqDirectionFlag = stream.readUint8(); - dependencyInfo.layerNumber = stream.readUint8(); - dependencyInfo.subSequenceIdentifier = stream.readUint16(); - } -}); - -// file:src/parsing/samplegroups/dtrt.js -BoxParser.createSampleGroupCtor("dtrt", function(stream) { - Log.warn("BoxParser", "Sample Group type: "+this.grouping_type+" not fully parsed"); -}); - -// file:src/parsing/samplegroups/mvif.js -BoxParser.createSampleGroupCtor("mvif", function(stream) { - Log.warn("BoxParser", "Sample Group type: "+this.grouping_type+" not fully parsed"); -}); - -// file:src/parsing/samplegroups/prol.js -BoxParser.createSampleGroupCtor("prol", function(stream) { - this.roll_distance = stream.readInt16(); -}); - -// file:src/parsing/samplegroups/rap.js -BoxParser.createSampleGroupCtor("rap ", function(stream) { - var tmp_byte = stream.readUint8(); - this.num_leading_samples_known = tmp_byte >> 7; - this.num_leading_samples = tmp_byte & 0x7F; -}); - -// file:src/parsing/samplegroups/rash.js -BoxParser.createSampleGroupCtor("rash", function(stream) { - this.operation_point_count = stream.readUint16(); - if (this.description_length !== 2+(this.operation_point_count === 1?2:this.operation_point_count*6)+9) { - Log.warn("BoxParser", "Mismatch in "+this.grouping_type+" sample group length"); - this.data = stream.readUint8Array(this.description_length-2); - } else { - if (this.operation_point_count === 1) { - this.target_rate_share = stream.readUint16(); - } else { - this.target_rate_share = []; - this.available_bitrate = []; - for (var i = 0; i < this.operation_point_count; i++) { - this.available_bitrate[i] = stream.readUint32(); - this.target_rate_share[i] = stream.readUint16(); - } - } - this.maximum_bitrate = stream.readUint32(); - this.minimum_bitrate = stream.readUint32(); - this.discard_priority = stream.readUint8(); - } -}); - -// file:src/parsing/samplegroups/roll.js -BoxParser.createSampleGroupCtor("roll", function(stream) { - this.roll_distance = stream.readInt16(); -}); - -// file:src/parsing/samplegroups/samplegroup.js -BoxParser.SampleGroupEntry.prototype.parse = function(stream) { - Log.warn("BoxParser", "Unknown Sample Group type: "+this.grouping_type); - this.data = stream.readUint8Array(this.description_length); -} - -// file:src/parsing/samplegroups/scif.js -BoxParser.createSampleGroupCtor("scif", function(stream) { - Log.warn("BoxParser", "Sample Group type: "+this.grouping_type+" not fully parsed"); -}); - -// file:src/parsing/samplegroups/scnm.js -BoxParser.createSampleGroupCtor("scnm", function(stream) { - Log.warn("BoxParser", "Sample Group type: "+this.grouping_type+" not fully parsed"); -}); - -// file:src/parsing/samplegroups/seig.js -BoxParser.createSampleGroupCtor("seig", function(stream) { - this.reserved = stream.readUint8(); - var tmp = stream.readUint8(); - this.crypt_byte_block = tmp >> 4; - this.skip_byte_block = tmp & 0xF; - this.isProtected = stream.readUint8(); - this.Per_Sample_IV_Size = stream.readUint8(); - this.KID = BoxParser.parseHex16(stream); - this.constant_IV_size = 0; - this.constant_IV = 0; - if (this.isProtected === 1 && this.Per_Sample_IV_Size === 0) { - this.constant_IV_size = stream.readUint8(); - this.constant_IV = stream.readUint8Array(this.constant_IV_size); - } -}); - -// file:src/parsing/samplegroups/stsa.js -BoxParser.createSampleGroupCtor("stsa", function(stream) { - Log.warn("BoxParser", "Sample Group type: "+this.grouping_type+" not fully parsed"); -}); - -// file:src/parsing/samplegroups/sync.js -BoxParser.createSampleGroupCtor("sync", function(stream) { - var tmp_byte = stream.readUint8(); - this.NAL_unit_type = tmp_byte & 0x3F; -}); - -// file:src/parsing/samplegroups/tele.js -BoxParser.createSampleGroupCtor("tele", function(stream) { - var tmp_byte = stream.readUint8(); - this.level_independently_decodable = tmp_byte >> 7; -}); - -// file:src/parsing/samplegroups/tsas.js -BoxParser.createSampleGroupCtor("tsas", function(stream) { - Log.warn("BoxParser", "Sample Group type: "+this.grouping_type+" not fully parsed"); -}); - -// file:src/parsing/samplegroups/tscl.js -BoxParser.createSampleGroupCtor("tscl", function(stream) { - Log.warn("BoxParser", "Sample Group type: "+this.grouping_type+" not fully parsed"); -}); - -// file:src/parsing/samplegroups/vipr.js -BoxParser.createSampleGroupCtor("vipr", function(stream) { - Log.warn("BoxParser", "Sample Group type: "+this.grouping_type+" not fully parsed"); -}); - -// file:src/parsing/sbgp.js -BoxParser.createFullBoxCtor("sbgp", function(stream) { - this.grouping_type = stream.readString(4); - if (this.version === 1) { - this.grouping_type_parameter = stream.readUint32(); - } else { - this.grouping_type_parameter = 0; - } - this.entries = []; - var entry_count = stream.readUint32(); - for (var i = 0; i < entry_count; i++) { - var entry = {}; - this.entries.push(entry); - entry.sample_count = stream.readInt32(); - entry.group_description_index = stream.readInt32(); - } -}); - -// file:src/parsing/schm.js -BoxParser.createFullBoxCtor("schm", function(stream) { - this.scheme_type = stream.readString(4); - this.scheme_version = stream.readUint32(); - if (this.flags & 0x000001) { - this.scheme_uri = stream.readString(this.size - this.hdr_size - 8); - } -}); - -// file:src/parsing/sdp.js -BoxParser.createBoxCtor("sdp ", function(stream) { - this.sdptext = stream.readString(this.size - this.hdr_size); -}); - -// file:src/parsing/sdtp.js -BoxParser.createFullBoxCtor("sdtp", function(stream) { - var tmp_byte; - var count = (this.size - this.hdr_size); - this.is_leading = []; - this.sample_depends_on = []; - this.sample_is_depended_on = []; - this.sample_has_redundancy = []; - for (var i = 0; i < count; i++) { - tmp_byte = stream.readUint8(); - this.is_leading[i] = tmp_byte >> 6; - this.sample_depends_on[i] = (tmp_byte >> 4) & 0x3; - this.sample_is_depended_on[i] = (tmp_byte >> 2) & 0x3; - this.sample_has_redundancy[i] = tmp_byte & 0x3; - } -}); - -// file:src/parsing/senc.js -// Cannot be fully parsed because Per_Sample_IV_Size needs to be known -BoxParser.createFullBoxCtor("senc" /*, function(stream) { - this.parseFullHeader(stream); - var sample_count = stream.readUint32(); - this.samples = []; - for (var i = 0; i < sample_count; i++) { - var sample = {}; - // tenc.default_Per_Sample_IV_Size or seig.Per_Sample_IV_Size - sample.InitializationVector = this.readUint8Array(Per_Sample_IV_Size*8); - if (this.flags & 0x2) { - sample.subsamples = []; - subsample_count = stream.readUint16(); - for (var j = 0; j < subsample_count; j++) { - var subsample = {}; - subsample.BytesOfClearData = stream.readUint16(); - subsample.BytesOfProtectedData = stream.readUint32(); - sample.subsamples.push(subsample); - } - } - // TODO - this.samples.push(sample); - } -}*/); -// file:src/parsing/sgpd.js -BoxParser.createFullBoxCtor("sgpd", function(stream) { - this.grouping_type = stream.readString(4); - Log.debug("BoxParser", "Found Sample Groups of type "+this.grouping_type); - if (this.version === 1) { - this.default_length = stream.readUint32(); - } else { - this.default_length = 0; - } - if (this.version >= 2) { - this.default_group_description_index = stream.readUint32(); - } - this.entries = []; - var entry_count = stream.readUint32(); - for (var i = 0; i < entry_count; i++) { - var entry; - if (BoxParser[this.grouping_type+"SampleGroupEntry"]) { - entry = new BoxParser[this.grouping_type+"SampleGroupEntry"](this.grouping_type); - } else { - entry = new BoxParser.SampleGroupEntry(this.grouping_type); - } - this.entries.push(entry); - if (this.version === 1) { - if (this.default_length === 0) { - entry.description_length = stream.readUint32(); - } else { - entry.description_length = this.default_length; - } - } else { - entry.description_length = this.default_length; - } - if (entry.write === BoxParser.SampleGroupEntry.prototype.write) { - Log.info("BoxParser", "SampleGroup for type "+this.grouping_type+" writing not yet implemented, keeping unparsed data in memory for later write"); - // storing data - entry.data = stream.readUint8Array(entry.description_length); - // rewinding - stream.position -= entry.description_length; - } - entry.parse(stream); - } -}); - -// file:src/parsing/sidx.js -BoxParser.createFullBoxCtor("sidx", function(stream) { - this.reference_ID = stream.readUint32(); - this.timescale = stream.readUint32(); - if (this.version === 0) { - this.earliest_presentation_time = stream.readUint32(); - this.first_offset = stream.readUint32(); - } else { - this.earliest_presentation_time = stream.readUint64(); - this.first_offset = stream.readUint64(); - } - stream.readUint16(); - this.references = []; - var count = stream.readUint16(); - for (var i = 0; i < count; i++) { - var ref = {}; - this.references.push(ref); - var tmp_32 = stream.readUint32(); - ref.reference_type = (tmp_32 >> 31) & 0x1; - ref.referenced_size = tmp_32 & 0x7FFFFFFF; - ref.subsegment_duration = stream.readUint32(); - tmp_32 = stream.readUint32(); - ref.starts_with_SAP = (tmp_32 >> 31) & 0x1; - ref.SAP_type = (tmp_32 >> 28) & 0x7; - ref.SAP_delta_time = tmp_32 & 0xFFFFFFF; - } -}); - -// file:src/parsing/singleitemtypereference.js -BoxParser.SingleItemTypeReferenceBox = function(type, size, hdr_size, start) { - BoxParser.Box.call(this, type, size); - this.hdr_size = hdr_size; - this.start = start; -} -BoxParser.SingleItemTypeReferenceBox.prototype = new BoxParser.Box(); -BoxParser.SingleItemTypeReferenceBox.prototype.parse = function(stream) { - this.from_item_ID = stream.readUint16(); - var count = stream.readUint16(); - this.references = []; - for(var i = 0; i < count; i++) { - this.references[i] = stream.readUint16(); - } -} - -// file:src/parsing/singleitemtypereferencelarge.js -BoxParser.SingleItemTypeReferenceBoxLarge = function(type, size, hdr_size, start) { - BoxParser.Box.call(this, type, size); - this.hdr_size = hdr_size; - this.start = start; -} -BoxParser.SingleItemTypeReferenceBoxLarge.prototype = new BoxParser.Box(); -BoxParser.SingleItemTypeReferenceBoxLarge.prototype.parse = function(stream) { - this.from_item_ID = stream.readUint32(); - var count = stream.readUint16(); - this.references = []; - for(var i = 0; i < count; i++) { - this.references[i] = stream.readUint32(); - } -} - -// file:src/parsing/SmDm.js -BoxParser.createFullBoxCtor("SmDm", function(stream) { - this.primaryRChromaticity_x = stream.readUint16(); - this.primaryRChromaticity_y = stream.readUint16(); - this.primaryGChromaticity_x = stream.readUint16(); - this.primaryGChromaticity_y = stream.readUint16(); - this.primaryBChromaticity_x = stream.readUint16(); - this.primaryBChromaticity_y = stream.readUint16(); - this.whitePointChromaticity_x = stream.readUint16(); - this.whitePointChromaticity_y = stream.readUint16(); - this.luminanceMax = stream.readUint32(); - this.luminanceMin = stream.readUint32(); -}); - -// file:src/parsing/smhd.js -BoxParser.createFullBoxCtor("smhd", function(stream) { - this.balance = stream.readUint16(); - stream.readUint16(); -}); - -// file:src/parsing/ssix.js -BoxParser.createFullBoxCtor("ssix", function(stream) { - this.subsegments = []; - var subsegment_count = stream.readUint32(); - for (var i = 0; i < subsegment_count; i++) { - var subsegment = {}; - this.subsegments.push(subsegment); - subsegment.ranges = []; - var range_count = stream.readUint32(); - for (var j = 0; j < range_count; j++) { - var range = {}; - subsegment.ranges.push(range); - range.level = stream.readUint8(); - range.range_size = stream.readUint24(); - } - } -}); - -// file:src/parsing/stco.js -BoxParser.createFullBoxCtor("stco", function(stream) { - var entry_count; - entry_count = stream.readUint32(); - this.chunk_offsets = []; - if (this.version === 0) { - for (var i = 0; i < entry_count; i++) { - this.chunk_offsets.push(stream.readUint32()); - } - } -}); - -// file:src/parsing/stdp.js -BoxParser.createFullBoxCtor("stdp", function(stream) { - var count = (this.size - this.hdr_size)/2; - this.priority = []; - for (var i = 0; i < count; i++) { - this.priority[i] = stream.readUint16(); - } -}); - -// file:src/parsing/sthd.js -BoxParser.createFullBoxCtor("sthd"); - -// file:src/parsing/stri.js -BoxParser.createFullBoxCtor("stri", function(stream) { - this.switch_group = stream.readUint16(); - this.alternate_group = stream.readUint16(); - this.sub_track_id = stream.readUint32(); - var count = (this.size - this.hdr_size - 8)/4; - this.attribute_list = []; - for (var i = 0; i < count; i++) { - this.attribute_list[i] = stream.readUint32(); - } -}); - -// file:src/parsing/stsc.js -BoxParser.createFullBoxCtor("stsc", function(stream) { - var entry_count; - var i; - entry_count = stream.readUint32(); - this.first_chunk = []; - this.samples_per_chunk = []; - this.sample_description_index = []; - if (this.version === 0) { - for(i=0; i> 4) & 0xF; - this.sample_sizes[i+1] = tmp & 0xF; - } - } else if (this.field_size === 8) { - for (i = 0; i < sample_count; i++) { - this.sample_sizes[i] = stream.readUint8(); - } - } else if (this.field_size === 16) { - for (i = 0; i < sample_count; i++) { - this.sample_sizes[i] = stream.readUint16(); - } - } else { - Log.error("BoxParser", "Error in length field in stz2 box"); - } - } -}); - -// file:src/parsing/subs.js -BoxParser.createFullBoxCtor("subs", function(stream) { - var i,j; - var entry_count; - var subsample_count; - entry_count = stream.readUint32(); - this.entries = []; - for (i = 0; i < entry_count; i++) { - var sampleInfo = {}; - this.entries[i] = sampleInfo; - sampleInfo.sample_delta = stream.readUint32(); - sampleInfo.subsamples = []; - subsample_count = stream.readUint16(); - if (subsample_count>0) { - for (j = 0; j < subsample_count; j++) { - var subsample = {}; - sampleInfo.subsamples.push(subsample); - if (this.version == 1) { - subsample.size = stream.readUint32(); - } else { - subsample.size = stream.readUint16(); - } - subsample.priority = stream.readUint8(); - subsample.discardable = stream.readUint8(); - subsample.codec_specific_parameters = stream.readUint32(); - } - } - } -}); - -// file:src/parsing/tenc.js -BoxParser.createFullBoxCtor("tenc", function(stream) { - stream.readUint8(); // reserved - if (this.version === 0) { - stream.readUint8(); - } else { - var tmp = stream.readUint8(); - this.default_crypt_byte_block = (tmp >> 4) & 0xF; - this.default_skip_byte_block = tmp & 0xF; - } - this.default_isProtected = stream.readUint8(); - this.default_Per_Sample_IV_Size = stream.readUint8(); - this.default_KID = BoxParser.parseHex16(stream); - if (this.default_isProtected === 1 && this.default_Per_Sample_IV_Size === 0) { - this.default_constant_IV_size = stream.readUint8(); - this.default_constant_IV = stream.readUint8Array(this.default_constant_IV_size); - } -});// file:src/parsing/tfdt.js -BoxParser.createFullBoxCtor("tfdt", function(stream) { - if (this.version == 1) { - this.baseMediaDecodeTime = stream.readUint64(); - } else { - this.baseMediaDecodeTime = stream.readUint32(); - } -}); - -// file:src/parsing/tfhd.js -BoxParser.createFullBoxCtor("tfhd", function(stream) { - var readBytes = 0; - this.track_id = stream.readUint32(); - if (this.size - this.hdr_size > readBytes && (this.flags & BoxParser.TFHD_FLAG_BASE_DATA_OFFSET)) { - this.base_data_offset = stream.readUint64(); - readBytes += 8; - } else { - this.base_data_offset = 0; - } - if (this.size - this.hdr_size > readBytes && (this.flags & BoxParser.TFHD_FLAG_SAMPLE_DESC)) { - this.default_sample_description_index = stream.readUint32(); - readBytes += 4; - } else { - this.default_sample_description_index = 0; - } - if (this.size - this.hdr_size > readBytes && (this.flags & BoxParser.TFHD_FLAG_SAMPLE_DUR)) { - this.default_sample_duration = stream.readUint32(); - readBytes += 4; - } else { - this.default_sample_duration = 0; - } - if (this.size - this.hdr_size > readBytes && (this.flags & BoxParser.TFHD_FLAG_SAMPLE_SIZE)) { - this.default_sample_size = stream.readUint32(); - readBytes += 4; - } else { - this.default_sample_size = 0; - } - if (this.size - this.hdr_size > readBytes && (this.flags & BoxParser.TFHD_FLAG_SAMPLE_FLAGS)) { - this.default_sample_flags = stream.readUint32(); - readBytes += 4; - } else { - this.default_sample_flags = 0; - } -}); - -// file:src/parsing/tfra.js -BoxParser.createFullBoxCtor("tfra", function(stream) { - this.track_ID = stream.readUint32(); - stream.readUint24(); - var tmp_byte = stream.readUint8(); - this.length_size_of_traf_num = (tmp_byte >> 4) & 0x3; - this.length_size_of_trun_num = (tmp_byte >> 2) & 0x3; - this.length_size_of_sample_num = (tmp_byte) & 0x3; - this.entries = []; - var number_of_entries = stream.readUint32(); - for (var i = 0; i < number_of_entries; i++) { - if (this.version === 1) { - this.time = stream.readUint64(); - this.moof_offset = stream.readUint64(); - } else { - this.time = stream.readUint32(); - this.moof_offset = stream.readUint32(); - } - this.traf_number = stream["readUint"+(8*(this.length_size_of_traf_num+1))](); - this.trun_number = stream["readUint"+(8*(this.length_size_of_trun_num+1))](); - this.sample_number = stream["readUint"+(8*(this.length_size_of_sample_num+1))](); - } -}); - -// file:src/parsing/tkhd.js -BoxParser.createFullBoxCtor("tkhd", function(stream) { - if (this.version == 1) { - this.creation_time = stream.readUint64(); - this.modification_time = stream.readUint64(); - this.track_id = stream.readUint32(); - stream.readUint32(); - this.duration = stream.readUint64(); - } else { - this.creation_time = stream.readUint32(); - this.modification_time = stream.readUint32(); - this.track_id = stream.readUint32(); - stream.readUint32(); - this.duration = stream.readUint32(); - } - stream.readUint32Array(2); - this.layer = stream.readInt16(); - this.alternate_group = stream.readInt16(); - this.volume = stream.readInt16()>>8; - stream.readUint16(); - this.matrix = stream.readInt32Array(9); - this.width = stream.readUint32(); - this.height = stream.readUint32(); -}); - -// file:src/parsing/tmax.js -BoxParser.createBoxCtor("tmax", function(stream) { - this.time = stream.readUint32(); -}); - -// file:src/parsing/tmin.js -BoxParser.createBoxCtor("tmin", function(stream) { - this.time = stream.readUint32(); -}); - -// file:src/parsing/totl.js -BoxParser.createBoxCtor("totl",function(stream) { - this.bytessent = stream.readUint32(); -}); - -// file:src/parsing/tpay.js -BoxParser.createBoxCtor("tpay", function(stream) { - this.bytessent = stream.readUint32(); -}); - -// file:src/parsing/tpyl.js -BoxParser.createBoxCtor("tpyl", function(stream) { - this.bytessent = stream.readUint64(); -}); - -// file:src/parsing/TrackGroup.js -BoxParser.TrackGroupTypeBox.prototype.parse = function(stream) { - this.parseFullHeader(stream); - this.track_group_id = stream.readUint32(); -} - -// file:src/parsing/trackgroups/msrc.js -BoxParser.createTrackGroupCtor("msrc");// file:src/parsing/TrakReference.js -BoxParser.TrackReferenceTypeBox = function(type, size, hdr_size, start) { - BoxParser.Box.call(this, type, size); - this.hdr_size = hdr_size; - this.start = start; -} -BoxParser.TrackReferenceTypeBox.prototype = new BoxParser.Box(); -BoxParser.TrackReferenceTypeBox.prototype.parse = function(stream) { - this.track_ids = stream.readUint32Array((this.size-this.hdr_size)/4); -} - -// file:src/parsing/tref.js -BoxParser.trefBox.prototype.parse = function(stream) { - var ret; - var box; - while (stream.getPosition() < this.start+this.size) { - ret = BoxParser.parseOneBox(stream, true, this.size - (stream.getPosition() - this.start)); - if (ret.code === BoxParser.OK) { - box = new BoxParser.TrackReferenceTypeBox(ret.type, ret.size, ret.hdr_size, ret.start); - if (box.write === BoxParser.Box.prototype.write && box.type !== "mdat") { - Log.info("BoxParser", "TrackReference "+box.type+" box writing not yet implemented, keeping unparsed data in memory for later write"); - box.parseDataAndRewind(stream); - } - box.parse(stream); - this.boxes.push(box); - } else { - return; - } - } -} - -// file:src/parsing/trep.js -BoxParser.createFullBoxCtor("trep", function(stream) { - this.track_ID = stream.readUint32(); - this.boxes = []; - while (stream.getPosition() < this.start+this.size) { - ret = BoxParser.parseOneBox(stream, false, this.size - (stream.getPosition() - this.start)); - if (ret.code === BoxParser.OK) { - box = ret.box; - this.boxes.push(box); - } else { - return; - } - } -}); - -// file:src/parsing/trex.js -BoxParser.createFullBoxCtor("trex", function(stream) { - this.track_id = stream.readUint32(); - this.default_sample_description_index = stream.readUint32(); - this.default_sample_duration = stream.readUint32(); - this.default_sample_size = stream.readUint32(); - this.default_sample_flags = stream.readUint32(); -}); - -// file:src/parsing/trpy.js -BoxParser.createBoxCtor("trpy", function(stream) { - this.bytessent = stream.readUint64(); -}); - -// file:src/parsing/trun.js -BoxParser.createFullBoxCtor("trun", function(stream) { - var readBytes = 0; - this.sample_count = stream.readUint32(); - readBytes+= 4; - if (this.size - this.hdr_size > readBytes && (this.flags & BoxParser.TRUN_FLAGS_DATA_OFFSET) ) { - this.data_offset = stream.readInt32(); //signed - readBytes += 4; - } else { - this.data_offset = 0; - } - if (this.size - this.hdr_size > readBytes && (this.flags & BoxParser.TRUN_FLAGS_FIRST_FLAG) ) { - this.first_sample_flags = stream.readUint32(); - readBytes += 4; - } else { - this.first_sample_flags = 0; - } - this.sample_duration = []; - this.sample_size = []; - this.sample_flags = []; - this.sample_composition_time_offset = []; - if (this.size - this.hdr_size > readBytes) { - for (var i = 0; i < this.sample_count; i++) { - if (this.flags & BoxParser.TRUN_FLAGS_DURATION) { - this.sample_duration[i] = stream.readUint32(); - } - if (this.flags & BoxParser.TRUN_FLAGS_SIZE) { - this.sample_size[i] = stream.readUint32(); - } - if (this.flags & BoxParser.TRUN_FLAGS_FLAGS) { - this.sample_flags[i] = stream.readUint32(); - } - if (this.flags & BoxParser.TRUN_FLAGS_CTS_OFFSET) { - if (this.version === 0) { - this.sample_composition_time_offset[i] = stream.readUint32(); - } else { - this.sample_composition_time_offset[i] = stream.readInt32(); //signed - } - } - } - } -}); - -// file:src/parsing/tsel.js -BoxParser.createFullBoxCtor("tsel", function(stream) { - this.switch_group = stream.readUint32(); - var count = (this.size - this.hdr_size - 4)/4; - this.attribute_list = []; - for (var i = 0; i < count; i++) { - this.attribute_list[i] = stream.readUint32(); - } -}); - -// file:src/parsing/txtC.js -BoxParser.createFullBoxCtor("txtC", function(stream) { - this.config = stream.readCString(); -}); - -// file:src/parsing/url.js -BoxParser.createFullBoxCtor("url ", function(stream) { - if (this.flags !== 0x000001) { - this.location = stream.readCString(); - } -}); - -// file:src/parsing/urn.js -BoxParser.createFullBoxCtor("urn ", function(stream) { - this.name = stream.readCString(); - if (this.size - this.hdr_size - this.name.length - 1 > 0) { - this.location = stream.readCString(); - } -}); - -// file:src/parsing/uuid/piff/piffLsm.js -BoxParser.createUUIDBox("a5d40b30e81411ddba2f0800200c9a66", true, false, function(stream) { - this.LiveServerManifest = stream.readString(this.size - this.hdr_size) - .replace(/&/g, "&") - .replace(//g, ">") - .replace(/"/g, """) - .replace(/'/g, "'"); -});// file:src/parsing/uuid/piff/piffPssh.js -BoxParser.createUUIDBox("d08a4f1810f34a82b6c832d8aba183d3", true, false, function(stream) { - this.system_id = BoxParser.parseHex16(stream); - var datasize = stream.readUint32(); - if (datasize > 0) { - this.data = stream.readUint8Array(datasize); - } -}); - -// file:src/parsing/uuid/piff/piffSenc.js -BoxParser.createUUIDBox("a2394f525a9b4f14a2446c427c648df4", true, false /*, function(stream) { - if (this.flags & 0x1) { - this.AlgorithmID = stream.readUint24(); - this.IV_size = stream.readUint8(); - this.KID = BoxParser.parseHex16(stream); - } - var sample_count = stream.readUint32(); - this.samples = []; - for (var i = 0; i < sample_count; i++) { - var sample = {}; - sample.InitializationVector = this.readUint8Array(this.IV_size*8); - if (this.flags & 0x2) { - sample.subsamples = []; - sample.NumberOfEntries = stream.readUint16(); - for (var j = 0; j < sample.NumberOfEntries; j++) { - var subsample = {}; - subsample.BytesOfClearData = stream.readUint16(); - subsample.BytesOfProtectedData = stream.readUint32(); - sample.subsamples.push(subsample); - } - } - this.samples.push(sample); - } -}*/); -// file:src/parsing/uuid/piff/piffTenc.js -BoxParser.createUUIDBox("8974dbce7be74c5184f97148f9882554", true, false, function(stream) { - this.default_AlgorithmID = stream.readUint24(); - this.default_IV_size = stream.readUint8(); - this.default_KID = BoxParser.parseHex16(stream); -});// file:src/parsing/uuid/piff/piffTfrf.js -BoxParser.createUUIDBox("d4807ef2ca3946958e5426cb9e46a79f", true, false, function(stream) { - this.fragment_count = stream.readUint8(); - this.entries = []; - - for (var i = 0; i < this.fragment_count; i++) { - var entry = {}; - var absolute_time = 0; - var absolute_duration = 0; - - if (this.version === 1) { - absolute_time = stream.readUint64(); - absolute_duration = stream.readUint64(); - } else { - absolute_time = stream.readUint32(); - absolute_duration = stream.readUint32(); - } - - entry.absolute_time = absolute_time; - entry.absolute_duration = absolute_duration; - - this.entries.push(entry); - } -});// file:src/parsing/uuid/piff/piffTfxd.js -BoxParser.createUUIDBox("6d1d9b0542d544e680e2141daff757b2", true, false, function(stream) { - if (this.version === 1) { - this.absolute_time = stream.readUint64(); - this.duration = stream.readUint64(); - } else { - this.absolute_time = stream.readUint32(); - this.duration = stream.readUint32(); - } -});// file:src/parsing/vmhd.js -BoxParser.createFullBoxCtor("vmhd", function(stream) { - this.graphicsmode = stream.readUint16(); - this.opcolor = stream.readUint16Array(3); -}); - -// file:src/parsing/vpcC.js -BoxParser.createFullBoxCtor("vpcC", function (stream) { - var tmp; - if (this.version === 1) { - this.profile = stream.readUint8(); - this.level = stream.readUint8(); - tmp = stream.readUint8(); - this.bitDepth = tmp >> 4; - this.chromaSubsampling = (tmp >> 1) & 0x7; - this.videoFullRangeFlag = tmp & 0x1; - this.colourPrimaries = stream.readUint8(); - this.transferCharacteristics = stream.readUint8(); - this.matrixCoefficients = stream.readUint8(); - this.codecIntializationDataSize = stream.readUint16(); - this.codecIntializationData = stream.readUint8Array(this.codecIntializationDataSize); - } else { - this.profile = stream.readUint8(); - this.level = stream.readUint8(); - tmp = stream.readUint8(); - this.bitDepth = (tmp >> 4) & 0xF; - this.colorSpace = tmp & 0xF; - tmp = stream.readUint8(); - this.chromaSubsampling = (tmp >> 4) & 0xF; - this.transferFunction = (tmp >> 1) & 0x7; - this.videoFullRangeFlag = tmp & 0x1; - this.codecIntializationDataSize = stream.readUint16(); - this.codecIntializationData = stream.readUint8Array(this.codecIntializationDataSize); - } -});// file:src/parsing/vttC.js -BoxParser.createBoxCtor("vttC", function(stream) { - this.text = stream.readString(this.size - this.hdr_size); -}); - -// file:src/parsing/vvcC.js -BoxParser.createFullBoxCtor("vvcC", function (stream) { - var i, j; - - // helper object to simplify extracting individual bits - var bitReader = { - held_bits: undefined, - num_held_bits: 0, - - stream_read_1_bytes: function (strm) { - this.held_bits = strm.readUint8(); - this.num_held_bits = 1 * 8; - }, - stream_read_2_bytes: function (strm) { - this.held_bits = strm.readUint16(); - this.num_held_bits = 2 * 8; - }, - - extract_bits: function (num_bits) { - var ret = (this.held_bits >> (this.num_held_bits - num_bits)) & ((1 << num_bits) - 1); - this.num_held_bits -= num_bits; - return ret; - } - }; - - // VvcDecoderConfigurationRecord - bitReader.stream_read_1_bytes(stream); - bitReader.extract_bits(5); // reserved - this.lengthSizeMinusOne = bitReader.extract_bits(2); - this.ptl_present_flag = bitReader.extract_bits(1); - - if (this.ptl_present_flag) { - bitReader.stream_read_2_bytes(stream); - this.ols_idx = bitReader.extract_bits(9); - this.num_sublayers = bitReader.extract_bits(3); - this.constant_frame_rate = bitReader.extract_bits(2); - this.chroma_format_idc = bitReader.extract_bits(2); - - bitReader.stream_read_1_bytes(stream); - this.bit_depth_minus8 = bitReader.extract_bits(3); - bitReader.extract_bits(5); // reserved - - // VvcPTLRecord - { - bitReader.stream_read_2_bytes(stream); - bitReader.extract_bits(2); // reserved - this.num_bytes_constraint_info = bitReader.extract_bits(6); - this.general_profile_idc = bitReader.extract_bits(7); - this.general_tier_flag = bitReader.extract_bits(1); - - this.general_level_idc = stream.readUint8(); - - bitReader.stream_read_1_bytes(stream); - this.ptl_frame_only_constraint_flag = bitReader.extract_bits(1); - this.ptl_multilayer_enabled_flag = bitReader.extract_bits(1); - - this.general_constraint_info = new Uint8Array(this.num_bytes_constraint_info); - if (this.num_bytes_constraint_info) { - for (i = 0; i < this.num_bytes_constraint_info - 1; i++) { - var cnstr1 = bitReader.extract_bits(6); - bitReader.stream_read_1_bytes(stream); - var cnstr2 = bitReader.extract_bits(2); - - this.general_constraint_info[i] = ((cnstr1 << 2) | cnstr2); - } - this.general_constraint_info[this.num_bytes_constraint_info - 1] = bitReader.extract_bits(6); - } else { - //forbidden in spec! - bitReader.extract_bits(6); - } - - bitReader.stream_read_1_bytes(stream); - this.ptl_sublayer_present_mask = 0; - for (j = this.num_sublayers - 2; j >= 0; --j) { - var val = bitReader.extract_bits(1); - this.ptl_sublayer_present_mask |= val << j; - } - for (j = this.num_sublayers; j <= 8 && this.num_sublayers > 1; ++j) { - bitReader.extract_bits(1); // ptl_reserved_zero_bit - } - - for (j = this.num_sublayers - 2; j >= 0; --j) { - if (this.ptl_sublayer_present_mask & (1 << j)) { - this.sublayer_level_idc[j] = stream.readUint8(); - } - } - - this.ptl_num_sub_profiles = stream.readUint8(); - this.general_sub_profile_idc = []; - if (this.ptl_num_sub_profiles) { - for (i = 0; i < this.ptl_num_sub_profiles; i++) { - this.general_sub_profile_idc.push(stream.readUint32()); - } - } - } // end VvcPTLRecord - - this.max_picture_width = stream.readUint16(); - this.max_picture_height = stream.readUint16(); - this.avg_frame_rate = stream.readUint16(); - } - - var VVC_NALU_OPI = 12; - var VVC_NALU_DEC_PARAM = 13; - - this.nalu_arrays = []; - var num_of_arrays = stream.readUint8(); - for (i = 0; i < num_of_arrays; i++) { - var nalu_array = []; - this.nalu_arrays.push(nalu_array); - - bitReader.stream_read_1_bytes(stream); - nalu_array.completeness = bitReader.extract_bits(1); - bitReader.extract_bits(2); // reserved - nalu_array.nalu_type = bitReader.extract_bits(5); - - var numNalus = 1; - if (nalu_array.nalu_type != VVC_NALU_DEC_PARAM && nalu_array.nalu_type != VVC_NALU_OPI) { - numNalus = stream.readUint16(); - } - - for (j = 0; j < numNalus; j++) { - var len = stream.readUint16(); - nalu_array.push({ - data: stream.readUint8Array(len), - length: len - }); - } - } -}); -// file:src/parsing/vvnC.js -BoxParser.createFullBoxCtor("vvnC", function (stream) { - // VvcNALUConfigBox - var tmp = strm.readUint8(); - this.lengthSizeMinusOne = (tmp & 0x3); -}); -// file:src/box-codecs.js -BoxParser.SampleEntry.prototype.isVideo = function() { - return false; -} - -BoxParser.SampleEntry.prototype.isAudio = function() { - return false; -} - -BoxParser.SampleEntry.prototype.isSubtitle = function() { - return false; -} - -BoxParser.SampleEntry.prototype.isMetadata = function() { - return false; -} - -BoxParser.SampleEntry.prototype.isHint = function() { - return false; -} - -BoxParser.SampleEntry.prototype.getCodec = function() { - return this.type.replace('.',''); -} - -BoxParser.SampleEntry.prototype.getWidth = function() { - return ""; -} - -BoxParser.SampleEntry.prototype.getHeight = function() { - return ""; -} - -BoxParser.SampleEntry.prototype.getChannelCount = function() { - return ""; -} - -BoxParser.SampleEntry.prototype.getSampleRate = function() { - return ""; -} - -BoxParser.SampleEntry.prototype.getSampleSize = function() { - return ""; -} - -BoxParser.VisualSampleEntry.prototype.isVideo = function() { - return true; -} - -BoxParser.VisualSampleEntry.prototype.getWidth = function() { - return this.width; -} - -BoxParser.VisualSampleEntry.prototype.getHeight = function() { - return this.height; -} - -BoxParser.AudioSampleEntry.prototype.isAudio = function() { - return true; -} - -BoxParser.AudioSampleEntry.prototype.getChannelCount = function() { - return this.channel_count; -} - -BoxParser.AudioSampleEntry.prototype.getSampleRate = function() { - return this.samplerate; -} - -BoxParser.AudioSampleEntry.prototype.getSampleSize = function() { - return this.samplesize; -} - -BoxParser.SubtitleSampleEntry.prototype.isSubtitle = function() { - return true; -} - -BoxParser.MetadataSampleEntry.prototype.isMetadata = function() { - return true; -} - - -BoxParser.decimalToHex = function(d, padding) { - var hex = Number(d).toString(16); - padding = typeof (padding) === "undefined" || padding === null ? padding = 2 : padding; - while (hex.length < padding) { - hex = "0" + hex; - } - return hex; -} - -BoxParser.avc1SampleEntry.prototype.getCodec = -BoxParser.avc2SampleEntry.prototype.getCodec = -BoxParser.avc3SampleEntry.prototype.getCodec = -BoxParser.avc4SampleEntry.prototype.getCodec = function() { - var baseCodec = BoxParser.SampleEntry.prototype.getCodec.call(this); - if (this.avcC) { - return baseCodec+"."+BoxParser.decimalToHex(this.avcC.AVCProfileIndication)+ - ""+BoxParser.decimalToHex(this.avcC.profile_compatibility)+ - ""+BoxParser.decimalToHex(this.avcC.AVCLevelIndication); - } else { - return baseCodec; - } -} - -BoxParser.hev1SampleEntry.prototype.getCodec = -BoxParser.hvc1SampleEntry.prototype.getCodec = function() { - var i; - var baseCodec = BoxParser.SampleEntry.prototype.getCodec.call(this); - if (this.hvcC) { - baseCodec += '.'; - switch (this.hvcC.general_profile_space) { - case 0: - baseCodec += ''; - break; - case 1: - baseCodec += 'A'; - break; - case 2: - baseCodec += 'B'; - break; - case 3: - baseCodec += 'C'; - break; - } - baseCodec += this.hvcC.general_profile_idc; - baseCodec += '.'; - var val = this.hvcC.general_profile_compatibility; - var reversed = 0; - for (i=0; i<32; i++) { - reversed |= val & 1; - if (i==31) break; - reversed <<= 1; - val >>=1; - } - baseCodec += BoxParser.decimalToHex(reversed, 0); - baseCodec += '.'; - if (this.hvcC.general_tier_flag === 0) { - baseCodec += 'L'; - } else { - baseCodec += 'H'; - } - baseCodec += this.hvcC.general_level_idc; - var hasByte = false; - var constraint_string = ""; - for (i = 5; i >= 0; i--) { - if (this.hvcC.general_constraint_indicator[i] || hasByte) { - constraint_string = "."+BoxParser.decimalToHex(this.hvcC.general_constraint_indicator[i], 0)+constraint_string; - hasByte = true; - } - } - baseCodec += constraint_string; - } - return baseCodec; -} - -BoxParser.vvc1SampleEntry.prototype.getCodec = -BoxParser.vvi1SampleEntry.prototype.getCodec = function () { - var i; - var baseCodec = BoxParser.SampleEntry.prototype.getCodec.call(this); - if (this.vvcC) { - baseCodec += '.' + this.vvcC.general_profile_idc; - if (this.vvcC.general_tier_flag) { - baseCodec += '.H'; - } else { - baseCodec += '.L'; - } - baseCodec += this.vvcC.general_level_idc; - - var constraint_string = ""; - if (this.vvcC.general_constraint_info) { - var bytes = []; - var byte = 0; - byte |= this.vvcC.ptl_frame_only_constraint << 7; - byte |= this.vvcC.ptl_multilayer_enabled << 6; - var last_nonzero; - for (i = 0; i < this.vvcC.general_constraint_info.length; ++i) { - byte |= (this.vvcC.general_constraint_info[i] >> 2) & 0x3f; - bytes.push(byte); - if (byte) { - last_nonzero = i; - } - - byte = (this.vvcC.general_constraint_info[i] >> 2) & 0x03; - } - - if (last_nonzero === undefined) { - constraint_string = ".CA"; - } - else { - constraint_string = ".C" - var base32_chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567"; - var held_bits = 0; - var num_held_bits = 0; - for (i = 0; i <= last_nonzero; ++i) { - held_bits = (held_bits << 8) | bytes[i]; - num_held_bits += 8; - - while (num_held_bits >= 5) { - var val = (held_bits >> (num_held_bits - 5)) & 0x1f; - constraint_string += base32_chars[val]; - - num_held_bits -= 5; - held_bits &= (1 << num_held_bits) - 1; - } - } - if (num_held_bits) { - held_bits <<= (5 - num_held_bits); // right-pad with zeros to 5 bits (is this correct?) - constraint_string += base32_chars[held_bits & 0x1f]; - } - } - } - baseCodec += constraint_string; - } - return baseCodec; -} - -BoxParser.mp4aSampleEntry.prototype.getCodec = function() { - var baseCodec = BoxParser.SampleEntry.prototype.getCodec.call(this); - if (this.esds && this.esds.esd) { - var oti = this.esds.esd.getOTI(); - var dsi = this.esds.esd.getAudioConfig(); - return baseCodec+"."+BoxParser.decimalToHex(oti)+(dsi ? "."+dsi: ""); - } else { - return baseCodec; - } -} - -BoxParser.stxtSampleEntry.prototype.getCodec = function() { - var baseCodec = BoxParser.SampleEntry.prototype.getCodec.call(this); - if(this.mime_format) { - return baseCodec + "." + this.mime_format; - } else { - return baseCodec - } -} - -BoxParser.vp08SampleEntry.prototype.getCodec = -BoxParser.vp09SampleEntry.prototype.getCodec = function() { - var baseCodec = BoxParser.SampleEntry.prototype.getCodec.call(this); - var level = this.vpcC.level; - if (level == 0) { - level = "00"; - } - var bitDepth = this.vpcC.bitDepth; - if (bitDepth == 8) { - bitDepth = "08"; - } - return baseCodec + ".0" + this.vpcC.profile + "." + level + "." + bitDepth; -} - -BoxParser.av01SampleEntry.prototype.getCodec = function() { - var baseCodec = BoxParser.SampleEntry.prototype.getCodec.call(this); - var level = this.av1C.seq_level_idx_0; - if (level < 10) { - level = "0" + level; - } - var bitdepth; - if (this.av1C.seq_profile === 2 && this.av1C.high_bitdepth === 1) { - bitdepth = (this.av1C.twelve_bit === 1) ? "12" : "10"; - } else if ( this.av1C.seq_profile <= 2 ) { - bitdepth = (this.av1C.high_bitdepth === 1) ? "10" : "08"; - } - // TODO need to parse the SH to find color config - return baseCodec+"."+this.av1C.seq_profile+"."+level+(this.av1C.seq_tier_0?"H":"M")+"."+bitdepth;//+"."+this.av1C.monochrome+"."+this.av1C.chroma_subsampling_x+""+this.av1C.chroma_subsampling_y+""+this.av1C.chroma_sample_position; -} -// file:src/box-write.js -/* - * Copyright (c) Telecom ParisTech/TSI/MM/GPAC Cyril Concolato - * License: BSD-3-Clause (see LICENSE file) - */ -BoxParser.Box.prototype.writeHeader = function(stream, msg) { - this.size += 8; - if (this.size > MAX_SIZE) { - this.size += 8; - } - if (this.type === "uuid") { - this.size += 16; - } - Log.debug("BoxWriter", "Writing box "+this.type+" of size: "+this.size+" at position "+stream.getPosition()+(msg || "")); - if (this.size > MAX_SIZE) { - stream.writeUint32(1); - } else { - this.sizePosition = stream.getPosition(); - stream.writeUint32(this.size); - } - stream.writeString(this.type, null, 4); - if (this.type === "uuid") { - stream.writeUint8Array(this.uuid); - } - if (this.size > MAX_SIZE) { - stream.writeUint64(this.size); - } -} - -BoxParser.FullBox.prototype.writeHeader = function(stream) { - this.size += 4; - BoxParser.Box.prototype.writeHeader.call(this, stream, " v="+this.version+" f="+this.flags); - stream.writeUint8(this.version); - stream.writeUint24(this.flags); -} - -BoxParser.Box.prototype.write = function(stream) { - if (this.type === "mdat") { - /* TODO: fix this */ - if (this.data) { - this.size = this.data.length; - this.writeHeader(stream); - stream.writeUint8Array(this.data); - } - } else { - this.size = (this.data ? this.data.length : 0); - this.writeHeader(stream); - if (this.data) { - stream.writeUint8Array(this.data); - } - } -} - -BoxParser.ContainerBox.prototype.write = function(stream) { - this.size = 0; - this.writeHeader(stream); - for (var i=0; i= 2) { - stream.writeUint32(this.default_sample_description_index); - } - stream.writeUint32(this.entries.length); - for (i = 0; i < this.entries.length; i++) { - entry = this.entries[i]; - if (this.version === 1) { - if (this.default_length === 0) { - stream.writeUint32(entry.description_length); - } - } - entry.write(stream); - } -} - - -// file:src/writing/sidx.js -BoxParser.sidxBox.prototype.write = function(stream) { - this.version = 0; - this.flags = 0; - this.size = 4*4+2+2+12*this.references.length; - this.writeHeader(stream); - stream.writeUint32(this.reference_ID); - stream.writeUint32(this.timescale); - stream.writeUint32(this.earliest_presentation_time); - stream.writeUint32(this.first_offset); - stream.writeUint16(0); - stream.writeUint16(this.references.length); - for (var i = 0; i < this.references.length; i++) { - var ref = this.references[i]; - stream.writeUint32(ref.reference_type << 31 | ref.referenced_size); - stream.writeUint32(ref.subsegment_duration); - stream.writeUint32(ref.starts_with_SAP << 31 | ref.SAP_type << 28 | ref.SAP_delta_time); - } -} - -// file:src/writing/smhd.js -BoxParser.smhdBox.prototype.write = function(stream) { - var i; - this.version = 0; - this.flags = 1; - this.size = 4; - this.writeHeader(stream); - stream.writeUint16(this.balance); - stream.writeUint16(0); -} -// file:src/writing/stco.js -BoxParser.stcoBox.prototype.write = function(stream) { - this.version = 0; - this.flags = 0; - this.size = 4+4*this.chunk_offsets.length; - this.writeHeader(stream); - stream.writeUint32(this.chunk_offsets.length); - stream.writeUint32Array(this.chunk_offsets); -} - -// file:src/writing/stsc.js -BoxParser.stscBox.prototype.write = function(stream) { - var i; - this.version = 0; - this.flags = 0; - this.size = 4+12*this.first_chunk.length; - this.writeHeader(stream); - stream.writeUint32(this.first_chunk.length); - for(i=0; i 0) { - i = 0; - while (i+1 < this.sample_sizes.length) { - if (this.sample_sizes[i+1] !== this.sample_sizes[0]) { - constant = false; - break; - } else { - i++; - } - } - } else { - constant = false; - } - this.size = 8; - if (!constant) { - this.size += 4*this.sample_sizes.length; - } - this.writeHeader(stream); - if (!constant) { - stream.writeUint32(0); - } else { - stream.writeUint32(this.sample_sizes[0]); - } - stream.writeUint32(this.sample_sizes.length); - if (!constant) { - stream.writeUint32Array(this.sample_sizes); - } -} - -// file:src/writing/stts.js -BoxParser.sttsBox.prototype.write = function(stream) { - var i; - this.version = 0; - this.flags = 0; - this.size = 4+8*this.sample_counts.length; - this.writeHeader(stream); - stream.writeUint32(this.sample_counts.length); - for(i=0; i UINT32_MAX ? 1 : 0; - this.flags = 0; - this.size = 4; - if (this.version === 1) { - this.size += 4; - } - this.writeHeader(stream); - if (this.version === 1) { - stream.writeUint64(this.baseMediaDecodeTime); - } else { - stream.writeUint32(this.baseMediaDecodeTime); - } -} - -// file:src/writing/tfhd.js -BoxParser.tfhdBox.prototype.write = function(stream) { - this.version = 0; - this.size = 4; - if (this.flags & BoxParser.TFHD_FLAG_BASE_DATA_OFFSET) { - this.size += 8; - } - if (this.flags & BoxParser.TFHD_FLAG_SAMPLE_DESC) { - this.size += 4; - } - if (this.flags & BoxParser.TFHD_FLAG_SAMPLE_DUR) { - this.size += 4; - } - if (this.flags & BoxParser.TFHD_FLAG_SAMPLE_SIZE) { - this.size += 4; - } - if (this.flags & BoxParser.TFHD_FLAG_SAMPLE_FLAGS) { - this.size += 4; - } - this.writeHeader(stream); - stream.writeUint32(this.track_id); - if (this.flags & BoxParser.TFHD_FLAG_BASE_DATA_OFFSET) { - stream.writeUint64(this.base_data_offset); - } - if (this.flags & BoxParser.TFHD_FLAG_SAMPLE_DESC) { - stream.writeUint32(this.default_sample_description_index); - } - if (this.flags & BoxParser.TFHD_FLAG_SAMPLE_DUR) { - stream.writeUint32(this.default_sample_duration); - } - if (this.flags & BoxParser.TFHD_FLAG_SAMPLE_SIZE) { - stream.writeUint32(this.default_sample_size); - } - if (this.flags & BoxParser.TFHD_FLAG_SAMPLE_FLAGS) { - stream.writeUint32(this.default_sample_flags); - } -} - -// file:src/writing/tkhd.js -BoxParser.tkhdBox.prototype.write = function(stream) { - this.version = 0; - //this.flags = 0; - this.size = 4*18+2*4; - this.writeHeader(stream); - stream.writeUint32(this.creation_time); - stream.writeUint32(this.modification_time); - stream.writeUint32(this.track_id); - stream.writeUint32(0); - stream.writeUint32(this.duration); - stream.writeUint32(0); - stream.writeUint32(0); - stream.writeInt16(this.layer); - stream.writeInt16(this.alternate_group); - stream.writeInt16(this.volume<<8); - stream.writeUint16(0); - stream.writeInt32Array(this.matrix); - stream.writeUint32(this.width); - stream.writeUint32(this.height); -} - -// file:src/writing/trex.js -BoxParser.trexBox.prototype.write = function(stream) { - this.version = 0; - this.flags = 0; - this.size = 4*5; - this.writeHeader(stream); - stream.writeUint32(this.track_id); - stream.writeUint32(this.default_sample_description_index); - stream.writeUint32(this.default_sample_duration); - stream.writeUint32(this.default_sample_size); - stream.writeUint32(this.default_sample_flags); -} - -// file:src/writing/trun.js -BoxParser.trunBox.prototype.write = function(stream) { - this.version = 0; - this.size = 4; - if (this.flags & BoxParser.TRUN_FLAGS_DATA_OFFSET) { - this.size += 4; - } - if (this.flags & BoxParser.TRUN_FLAGS_FIRST_FLAG) { - this.size += 4; - } - if (this.flags & BoxParser.TRUN_FLAGS_DURATION) { - this.size += 4*this.sample_duration.length; - } - if (this.flags & BoxParser.TRUN_FLAGS_SIZE) { - this.size += 4*this.sample_size.length; - } - if (this.flags & BoxParser.TRUN_FLAGS_FLAGS) { - this.size += 4*this.sample_flags.length; - } - if (this.flags & BoxParser.TRUN_FLAGS_CTS_OFFSET) { - this.size += 4*this.sample_composition_time_offset.length; - } - this.writeHeader(stream); - stream.writeUint32(this.sample_count); - if (this.flags & BoxParser.TRUN_FLAGS_DATA_OFFSET) { - this.data_offset_position = stream.getPosition(); - stream.writeInt32(this.data_offset); //signed - } - if (this.flags & BoxParser.TRUN_FLAGS_FIRST_FLAG) { - stream.writeUint32(this.first_sample_flags); - } - for (var i = 0; i < this.sample_count; i++) { - if (this.flags & BoxParser.TRUN_FLAGS_DURATION) { - stream.writeUint32(this.sample_duration[i]); - } - if (this.flags & BoxParser.TRUN_FLAGS_SIZE) { - stream.writeUint32(this.sample_size[i]); - } - if (this.flags & BoxParser.TRUN_FLAGS_FLAGS) { - stream.writeUint32(this.sample_flags[i]); - } - if (this.flags & BoxParser.TRUN_FLAGS_CTS_OFFSET) { - if (this.version === 0) { - stream.writeUint32(this.sample_composition_time_offset[i]); - } else { - stream.writeInt32(this.sample_composition_time_offset[i]); //signed - } - } - } -} - -// file:src/writing/url.js -BoxParser["url Box"].prototype.write = function(stream) { - this.version = 0; - if (this.location) { - this.flags = 0; - this.size = this.location.length+1; - } else { - this.flags = 0x000001; - this.size = 0; - } - this.writeHeader(stream); - if (this.location) { - stream.writeCString(this.location); - } -} - -// file:src/writing/urn.js -BoxParser["urn Box"].prototype.write = function(stream) { - this.version = 0; - this.flags = 0; - this.size = this.name.length+1+(this.location ? this.location.length+1 : 0); - this.writeHeader(stream); - stream.writeCString(this.name); - if (this.location) { - stream.writeCString(this.location); - } -} - -// file:src/writing/vmhd.js -BoxParser.vmhdBox.prototype.write = function(stream) { - var i; - this.version = 0; - this.flags = 1; - this.size = 8; - this.writeHeader(stream); - stream.writeUint16(this.graphicsmode); - stream.writeUint16Array(this.opcolor); -} - -// file:src/box-unpack.js -/* - * Copyright (c) Telecom ParisTech/TSI/MM/GPAC Cyril Concolato - * License: BSD-3-Clause (see LICENSE file) - */ -BoxParser.cttsBox.prototype.unpack = function(samples) { - var i, j, k; - k = 0; - for (i = 0; i < this.sample_counts.length; i++) { - for (j = 0; j < this.sample_counts[i]; j++) { - samples[k].pts = samples[k].dts + this.sample_offsets[i]; - k++; - } - } -} - -BoxParser.sttsBox.prototype.unpack = function(samples) { - var i, j, k; - k = 0; - for (i = 0; i < this.sample_counts.length; i++) { - for (j = 0; j < this.sample_counts[i]; j++) { - if (k === 0) { - samples[k].dts = 0; - } else { - samples[k].dts = samples[k-1].dts + this.sample_deltas[i]; - } - k++; - } - } -} - -BoxParser.stcoBox.prototype.unpack = function(samples) { - var i; - for (i = 0; i < this.chunk_offsets.length; i++) { - samples[i].offset = this.chunk_offsets[i]; - } -} - -BoxParser.stscBox.prototype.unpack = function(samples) { - var i, j, k, l, m; - l = 0; - m = 0; - for (i = 0; i < this.first_chunk.length; i++) { - for (j = 0; j < (i+1 < this.first_chunk.length ? this.first_chunk[i+1] : Infinity); j++) { - m++; - for (k = 0; k < this.samples_per_chunk[i]; k++) { - if (samples[l]) { - samples[l].description_index = this.sample_description_index[i]; - samples[l].chunk_index = m; - } else { - return; - } - l++; - } - } - } -} - -BoxParser.stszBox.prototype.unpack = function(samples) { - var i; - for (i = 0; i < this.sample_sizes.length; i++) { - samples[i].size = this.sample_sizes[i]; - } -} -// file:src/box-diff.js - -BoxParser.DIFF_BOXES_PROP_NAMES = [ "boxes", "entries", "references", "subsamples", - "items", "item_infos", "extents", "associations", - "subsegments", "ranges", "seekLists", "seekPoints", - "esd", "levels"]; - -BoxParser.DIFF_PRIMITIVE_ARRAY_PROP_NAMES = [ "compatible_brands", "matrix", "opcolor", "sample_counts", "sample_counts", "sample_deltas", -"first_chunk", "samples_per_chunk", "sample_sizes", "chunk_offsets", "sample_offsets", "sample_description_index", "sample_duration" ]; - -BoxParser.boxEqualFields = function(box_a, box_b) { - if (box_a && !box_b) return false; - var prop; - for (prop in box_a) { - if (BoxParser.DIFF_BOXES_PROP_NAMES.indexOf(prop) > -1) { - continue; - // } else if (excluded_fields && excluded_fields.indexOf(prop) > -1) { - // continue; - } else if (box_a[prop] instanceof BoxParser.Box || box_b[prop] instanceof BoxParser.Box) { - continue; - } else if (typeof box_a[prop] === "undefined" || typeof box_b[prop] === "undefined") { - continue; - } else if (typeof box_a[prop] === "function" || typeof box_b[prop] === "function") { - continue; - } else if ( - (box_a.subBoxNames && box_a.subBoxNames.indexOf(prop.slice(0,4)) > -1) || - (box_b.subBoxNames && box_b.subBoxNames.indexOf(prop.slice(0,4)) > -1)) { - continue; - } else { - if (prop === "data" || prop === "start" || prop === "size" || prop === "creation_time" || prop === "modification_time") { - continue; - } else if (BoxParser.DIFF_PRIMITIVE_ARRAY_PROP_NAMES.indexOf(prop) > -1) { - continue; - } else { - if (box_a[prop] !== box_b[prop]) { - return false; - } - } - } - } - return true; -} - -BoxParser.boxEqual = function(box_a, box_b) { - if (!BoxParser.boxEqualFields(box_a, box_b)) { - return false; - } - for (var j = 0; j < BoxParser.DIFF_BOXES_PROP_NAMES.length; j++) { - var name = BoxParser.DIFF_BOXES_PROP_NAMES[j]; - if (box_a[name] && box_b[name]) { - if (!BoxParser.boxEqual(box_a[name], box_b[name])) { - return false; - } - } - } - return true; -}// file:src/text-mp4.js -/* - * Copyright (c) 2012-2013. Telecom ParisTech/TSI/MM/GPAC Cyril Concolato - * License: BSD-3-Clause (see LICENSE file) - */ -var VTTin4Parser = function() { -} - -VTTin4Parser.prototype.parseSample = function(data) { - var cues, cue; - var stream = new MP4BoxStream(data.buffer); - cues = []; - while (!stream.isEos()) { - cue = BoxParser.parseOneBox(stream, false); - if (cue.code === BoxParser.OK && cue.box.type === "vttc") { - cues.push(cue.box); - } - } - return cues; -} - -VTTin4Parser.prototype.getText = function (startTime, endTime, data) { - function pad(n, width, z) { - z = z || '0'; - n = n + ''; - return n.length >= width ? n : new Array(width - n.length + 1).join(z) + n; - } - function secToTimestamp(insec) { - var h = Math.floor(insec/3600); - var m = Math.floor((insec - h*3600)/60); - var s = Math.floor(insec - h*3600 - m*60); - var ms = Math.floor((insec - h*3600 - m*60 - s)*1000); - return ""+pad(h, 2)+":"+pad(m,2)+":"+pad(s, 2)+"."+pad(ms, 3); - } - var cues = this.parseSample(data); - var string = ""; - for (var i = 0; i < cues.length; i++) { - var cueIn4 = cues[i]; - string += secToTimestamp(startTime)+" --> "+secToTimestamp(endTime)+"\r\n"; - string += cueIn4.payl.text; - } - return string; -} - -var XMLSubtitlein4Parser = function() { -} - -XMLSubtitlein4Parser.prototype.parseSample = function(sample) { - var res = {}; - var i; - res.resources = []; - var stream = new MP4BoxStream(sample.data.buffer); - if (!sample.subsamples || sample.subsamples.length === 0) { - res.documentString = stream.readString(sample.data.length); - } else { - res.documentString = stream.readString(sample.subsamples[0].size); - if (sample.subsamples.length > 1) { - for (i = 1; i < sample.subsamples.length; i++) { - res.resources[i] = stream.readUint8Array(sample.subsamples[i].size); - } - } - } - if (typeof (DOMParser) !== "undefined") { - res.document = (new DOMParser()).parseFromString(res.documentString, "application/xml"); - } - return res; -} - -var Textin4Parser = function() { -} - -Textin4Parser.prototype.parseSample = function(sample) { - var textString; - var stream = new MP4BoxStream(sample.data.buffer); - textString = stream.readString(sample.data.length); - return textString; -} - -Textin4Parser.prototype.parseConfig = function(data) { - var textString; - var stream = new MP4BoxStream(data.buffer); - stream.readUint32(); // version & flags - textString = stream.readCString(); - return textString; -} - -if (typeof exports !== 'undefined') { - exports.XMLSubtitlein4Parser = XMLSubtitlein4Parser; - exports.Textin4Parser = Textin4Parser; -} -// file:src/isofile.js -/* - * Copyright (c) 2012-2013. Telecom ParisTech/TSI/MM/GPAC Cyril Concolato - * License: BSD-3-Clause (see LICENSE file) - */ -var ISOFile = function (stream) { - /* MutiBufferStream object used to parse boxes */ - this.stream = stream || new MultiBufferStream(); - /* Array of all boxes (in order) found in the file */ - this.boxes = []; - /* Array of all mdats */ - this.mdats = []; - /* Array of all moofs */ - this.moofs = []; - /* Boolean indicating if the file is compatible with progressive parsing (moov first) */ - this.isProgressive = false; - /* Boolean used to fire moov start event only once */ - this.moovStartFound = false; - /* Callback called when the moov parsing starts */ - this.onMoovStart = null; - /* Boolean keeping track of the call to onMoovStart, to avoid double calls */ - this.moovStartSent = false; - /* Callback called when the moov is entirely parsed */ - this.onReady = null; - /* Boolean keeping track of the call to onReady, to avoid double calls */ - this.readySent = false; - /* Callback to call when segments are ready */ - this.onSegment = null; - /* Callback to call when samples are ready */ - this.onSamples = null; - /* Callback to call when there is an error in the parsing or processing of samples */ - this.onError = null; - /* Boolean indicating if the moov box run-length encoded tables of sample information have been processed */ - this.sampleListBuilt = false; - /* Array of Track objects for which fragmentation of samples is requested */ - this.fragmentedTracks = []; - /* Array of Track objects for which extraction of samples is requested */ - this.extractedTracks = []; - /* Boolean indicating that fragmention is ready */ - this.isFragmentationInitialized = false; - /* Boolean indicating that fragmented has started */ - this.sampleProcessingStarted = false; - /* Number of the next 'moof' to generate when fragmenting */ - this.nextMoofNumber = 0; - /* Boolean indicating if the initial list of items has been produced */ - this.itemListBuilt = false; - /* Callback called when the sidx box is entirely parsed */ - this.onSidx = null; - /* Boolean keeping track of the call to onSidx, to avoid double calls */ - this.sidxSent = false; -} - -ISOFile.prototype.setSegmentOptions = function(id, user, options) { - var trak = this.getTrackById(id); - if (trak) { - var fragTrack = {}; - this.fragmentedTracks.push(fragTrack); - fragTrack.id = id; - fragTrack.user = user; - fragTrack.trak = trak; - trak.nextSample = 0; - fragTrack.segmentStream = null; - fragTrack.nb_samples = 1000; - fragTrack.rapAlignement = true; - if (options) { - if (options.nbSamples) fragTrack.nb_samples = options.nbSamples; - if (options.rapAlignement) fragTrack.rapAlignement = options.rapAlignement; - } - } -} - -ISOFile.prototype.unsetSegmentOptions = function(id) { - var index = -1; - for (var i = 0; i < this.fragmentedTracks.length; i++) { - var fragTrack = this.fragmentedTracks[i]; - if (fragTrack.id == id) { - index = i; - } - } - if (index > -1) { - this.fragmentedTracks.splice(index, 1); - } -} - -ISOFile.prototype.setExtractionOptions = function(id, user, options) { - var trak = this.getTrackById(id); - if (trak) { - var extractTrack = {}; - this.extractedTracks.push(extractTrack); - extractTrack.id = id; - extractTrack.user = user; - extractTrack.trak = trak; - trak.nextSample = 0; - extractTrack.nb_samples = 1000; - extractTrack.samples = []; - if (options) { - if (options.nbSamples) extractTrack.nb_samples = options.nbSamples; - } - } -} - -ISOFile.prototype.unsetExtractionOptions = function(id) { - var index = -1; - for (var i = 0; i < this.extractedTracks.length; i++) { - var extractTrack = this.extractedTracks[i]; - if (extractTrack.id == id) { - index = i; - } - } - if (index > -1) { - this.extractedTracks.splice(index, 1); - } -} - -ISOFile.prototype.parse = function() { - var found; - var ret; - var box; - var parseBoxHeadersOnly = false; - - if (this.restoreParsePosition) { - if (!this.restoreParsePosition()) { - return; - } - } - - while (true) { - - if (this.hasIncompleteMdat && this.hasIncompleteMdat()) { - if (this.processIncompleteMdat()) { - continue; - } else { - return; - } - } else { - if (this.saveParsePosition) { - this.saveParsePosition(); - } - ret = BoxParser.parseOneBox(this.stream, parseBoxHeadersOnly); - if (ret.code === BoxParser.ERR_NOT_ENOUGH_DATA) { - if (this.processIncompleteBox) { - if (this.processIncompleteBox(ret)) { - continue; - } else { - return; - } - } else { - return; - } - } else { - var box_type; - /* the box is entirely parsed */ - box = ret.box; - box_type = (box.type !== "uuid" ? box.type : box.uuid); - /* store the box in the 'boxes' array to preserve box order (for file rewrite if needed) */ - this.boxes.push(box); - /* but also store box in a property for more direct access */ - switch (box_type) { - case "mdat": - this.mdats.push(box); - break; - case "moof": - this.moofs.push(box); - break; - case "moov": - this.moovStartFound = true; - if (this.mdats.length === 0) { - this.isProgressive = true; - } - /* no break */ - /* falls through */ - default: - if (this[box_type] !== undefined) { - Log.warn("ISOFile", "Duplicate Box of type: "+box_type+", overriding previous occurrence"); - } - this[box_type] = box; - break; - } - if (this.updateUsedBytes) { - this.updateUsedBytes(box, ret); - } - } - } - } -} - -ISOFile.prototype.checkBuffer = function (ab) { - if (ab === null || ab === undefined) { - throw("Buffer must be defined and non empty"); - } - if (ab.fileStart === undefined) { - throw("Buffer must have a fileStart property"); - } - if (ab.byteLength === 0) { - Log.warn("ISOFile", "Ignoring empty buffer (fileStart: "+ab.fileStart+")"); - this.stream.logBufferLevel(); - return false; - } - Log.info("ISOFile", "Processing buffer (fileStart: "+ab.fileStart+")"); - - /* mark the bytes in the buffer as not being used yet */ - ab.usedBytes = 0; - this.stream.insertBuffer(ab); - this.stream.logBufferLevel(); - - if (!this.stream.initialized()) { - Log.warn("ISOFile", "Not ready to start parsing"); - return false; - } - return true; -} - -/* Processes a new ArrayBuffer (with a fileStart property) - Returns the next expected file position, or undefined if not ready to parse */ -ISOFile.prototype.appendBuffer = function(ab, last) { - var nextFileStart; - if (!this.checkBuffer(ab)) { - return; - } - - /* Parse whatever is in the existing buffers */ - this.parse(); - - /* Check if the moovStart callback needs to be called */ - if (this.moovStartFound && !this.moovStartSent) { - this.moovStartSent = true; - if (this.onMoovStart) this.onMoovStart(); - } - - if (this.moov) { - /* A moov box has been entirely parsed */ - - /* if this is the first call after the moov is found we initialize the list of samples (may be empty in fragmented files) */ - if (!this.sampleListBuilt) { - this.buildSampleLists(); - this.sampleListBuilt = true; - } - - /* We update the sample information if there are any new moof boxes */ - this.updateSampleLists(); - - /* If the application needs to be informed that the 'moov' has been found, - we create the information object and callback the application */ - if (this.onReady && !this.readySent) { - this.readySent = true; - this.onReady(this.getInfo()); - } - - /* See if any sample extraction or segment creation needs to be done with the available samples */ - this.processSamples(last); - - /* Inform about the best range to fetch next */ - if (this.nextSeekPosition) { - nextFileStart = this.nextSeekPosition; - this.nextSeekPosition = undefined; - } else { - nextFileStart = this.nextParsePosition; - } - if (this.stream.getEndFilePositionAfter) { - nextFileStart = this.stream.getEndFilePositionAfter(nextFileStart); - } - } else { - if (this.nextParsePosition) { - /* moov has not been parsed but the first buffer was received, - the next fetch should probably be the next box start */ - nextFileStart = this.nextParsePosition; - } else { - /* No valid buffer has been parsed yet, we cannot know what to parse next */ - nextFileStart = 0; - } - } - if (this.sidx) { - if (this.onSidx && !this.sidxSent) { - this.onSidx(this.sidx); - this.sidxSent = true; - } - } - if (this.meta) { - if (this.flattenItemInfo && !this.itemListBuilt) { - this.flattenItemInfo(); - this.itemListBuilt = true; - } - if (this.processItems) { - this.processItems(this.onItem); - } - } - - if (this.stream.cleanBuffers) { - Log.info("ISOFile", "Done processing buffer (fileStart: "+ab.fileStart+") - next buffer to fetch should have a fileStart position of "+nextFileStart); - this.stream.logBufferLevel(); - this.stream.cleanBuffers(); - this.stream.logBufferLevel(true); - Log.info("ISOFile", "Sample data size in memory: "+this.getAllocatedSampleDataSize()); - } - return nextFileStart; -} - -ISOFile.prototype.getInfo = function() { - var i, j; - var movie = {}; - var trak; - var track; - var ref; - var sample_desc; - var _1904 = (new Date('1904-01-01T00:00:00Z').getTime()); - - if (this.moov) { - movie.hasMoov = true; - movie.duration = this.moov.mvhd.duration; - movie.timescale = this.moov.mvhd.timescale; - movie.isFragmented = (this.moov.mvex != null); - if (movie.isFragmented && this.moov.mvex.mehd) { - movie.fragment_duration = this.moov.mvex.mehd.fragment_duration; - } - movie.isProgressive = this.isProgressive; - movie.hasIOD = (this.moov.iods != null); - movie.brands = []; - movie.brands.push(this.ftyp.major_brand); - movie.brands = movie.brands.concat(this.ftyp.compatible_brands); - movie.created = new Date(_1904+this.moov.mvhd.creation_time*1000); - movie.modified = new Date(_1904+this.moov.mvhd.modification_time*1000); - movie.tracks = []; - movie.audioTracks = []; - movie.videoTracks = []; - movie.subtitleTracks = []; - movie.metadataTracks = []; - movie.hintTracks = []; - movie.otherTracks = []; - for (i = 0; i < this.moov.traks.length; i++) { - trak = this.moov.traks[i]; - sample_desc = trak.mdia.minf.stbl.stsd.entries[0]; - track = {}; - movie.tracks.push(track); - track.id = trak.tkhd.track_id; - track.name = trak.mdia.hdlr.name; - track.references = []; - if (trak.tref) { - for (j = 0; j < trak.tref.boxes.length; j++) { - ref = {}; - track.references.push(ref); - ref.type = trak.tref.boxes[j].type; - ref.track_ids = trak.tref.boxes[j].track_ids; - } - } - if (trak.edts) { - track.edits = trak.edts.elst.entries; - } - track.created = new Date(_1904+trak.tkhd.creation_time*1000); - track.modified = new Date(_1904+trak.tkhd.modification_time*1000); - track.movie_duration = trak.tkhd.duration; - track.movie_timescale = movie.timescale; - track.layer = trak.tkhd.layer; - track.alternate_group = trak.tkhd.alternate_group; - track.volume = trak.tkhd.volume; - track.matrix = trak.tkhd.matrix; - track.track_width = trak.tkhd.width/(1<<16); - track.track_height = trak.tkhd.height/(1<<16); - track.timescale = trak.mdia.mdhd.timescale; - track.cts_shift = trak.mdia.minf.stbl.cslg; - track.duration = trak.mdia.mdhd.duration; - track.samples_duration = trak.samples_duration; - track.codec = sample_desc.getCodec(); - track.kind = (trak.udta && trak.udta.kinds.length ? trak.udta.kinds[0] : { schemeURI: "", value: ""}); - track.language = (trak.mdia.elng ? trak.mdia.elng.extended_language : trak.mdia.mdhd.languageString); - track.nb_samples = trak.samples.length; - track.size = trak.samples_size; - track.bitrate = (track.size*8*track.timescale)/track.samples_duration; - if (sample_desc.isAudio()) { - track.type = "audio"; - movie.audioTracks.push(track); - track.audio = {}; - track.audio.sample_rate = sample_desc.getSampleRate(); - track.audio.channel_count = sample_desc.getChannelCount(); - track.audio.sample_size = sample_desc.getSampleSize(); - } else if (sample_desc.isVideo()) { - track.type = "video"; - movie.videoTracks.push(track); - track.video = {}; - track.video.width = sample_desc.getWidth(); - track.video.height = sample_desc.getHeight(); - } else if (sample_desc.isSubtitle()) { - track.type = "subtitles"; - movie.subtitleTracks.push(track); - } else if (sample_desc.isHint()) { - track.type = "metadata"; - movie.hintTracks.push(track); - } else if (sample_desc.isMetadata()) { - track.type = "metadata"; - movie.metadataTracks.push(track); - } else { - track.type = "metadata"; - movie.otherTracks.push(track); - } - } - } else { - movie.hasMoov = false; - } - movie.mime = ""; - if (movie.hasMoov && movie.tracks) { - if (movie.videoTracks && movie.videoTracks.length > 0) { - movie.mime += 'video/mp4; codecs=\"'; - } else if (movie.audioTracks && movie.audioTracks.length > 0) { - movie.mime += 'audio/mp4; codecs=\"'; - } else { - movie.mime += 'application/mp4; codecs=\"'; - } - for (i = 0; i < movie.tracks.length; i++) { - if (i !== 0) movie.mime += ','; - movie.mime+= movie.tracks[i].codec; - } - movie.mime += '\"; profiles=\"'; - movie.mime += this.ftyp.compatible_brands.join(); - movie.mime += '\"'; - } - return movie; -} - -ISOFile.prototype.processSamples = function(last) { - var i; - var trak; - if (!this.sampleProcessingStarted) return; - - /* For each track marked for fragmentation, - check if the next sample is there (i.e. if the sample information is known (i.e. moof has arrived) and if it has been downloaded) - and create a fragment with it */ - if (this.isFragmentationInitialized && this.onSegment !== null) { - for (i = 0; i < this.fragmentedTracks.length; i++) { - var fragTrak = this.fragmentedTracks[i]; - trak = fragTrak.trak; - while (trak.nextSample < trak.samples.length && this.sampleProcessingStarted) { - /* The sample information is there (either because the file is not fragmented and this is not the last sample, - or because the file is fragmented and the moof for that sample has been received */ - Log.debug("ISOFile", "Creating media fragment on track #"+fragTrak.id +" for sample "+trak.nextSample); - var result = this.createFragment(fragTrak.id, trak.nextSample, fragTrak.segmentStream); - if (result) { - fragTrak.segmentStream = result; - trak.nextSample++; - } else { - /* The fragment could not be created because the media data is not there (not downloaded), wait for it */ - break; - } - /* A fragment is created by sample, but the segment is the accumulation in the buffer of these fragments. - It is flushed only as requested by the application (nb_samples) to avoid too many callbacks */ - if (trak.nextSample % fragTrak.nb_samples === 0 || (last || trak.nextSample >= trak.samples.length)) { - Log.info("ISOFile", "Sending fragmented data on track #"+fragTrak.id+" for samples ["+Math.max(0,trak.nextSample-fragTrak.nb_samples)+","+(trak.nextSample-1)+"]"); - Log.info("ISOFile", "Sample data size in memory: "+this.getAllocatedSampleDataSize()); - if (this.onSegment) { - this.onSegment(fragTrak.id, fragTrak.user, fragTrak.segmentStream.buffer, trak.nextSample, (last || trak.nextSample >= trak.samples.length)); - } - /* force the creation of a new buffer */ - fragTrak.segmentStream = null; - if (fragTrak !== this.fragmentedTracks[i]) { - /* make sure we can stop fragmentation if needed */ - break; - } - } - } - } - } - - if (this.onSamples !== null) { - /* For each track marked for data export, - check if the next sample is there (i.e. has been downloaded) and send it */ - for (i = 0; i < this.extractedTracks.length; i++) { - var extractTrak = this.extractedTracks[i]; - trak = extractTrak.trak; - while (trak.nextSample < trak.samples.length && this.sampleProcessingStarted) { - Log.debug("ISOFile", "Exporting on track #"+extractTrak.id +" sample #"+trak.nextSample); - var sample = this.getSample(trak, trak.nextSample); - if (sample) { - trak.nextSample++; - extractTrak.samples.push(sample); - } else { - break; - } - if (trak.nextSample % extractTrak.nb_samples === 0 || trak.nextSample >= trak.samples.length) { - Log.debug("ISOFile", "Sending samples on track #"+extractTrak.id+" for sample "+trak.nextSample); - if (this.onSamples) { - this.onSamples(extractTrak.id, extractTrak.user, extractTrak.samples); - } - extractTrak.samples = []; - if (extractTrak !== this.extractedTracks[i]) { - /* check if the extraction needs to be stopped */ - break; - } - } - } - } - } -} - -/* Find and return specific boxes using recursion and early return */ -ISOFile.prototype.getBox = function(type) { - var result = this.getBoxes(type, true); - return (result.length ? result[0] : null); -} - -ISOFile.prototype.getBoxes = function(type, returnEarly) { - var result = []; - ISOFile._sweep.call(this, type, result, returnEarly); - return result; -} - -ISOFile._sweep = function(type, result, returnEarly) { - if (this.type && this.type == type) result.push(this); - for (var box in this.boxes) { - if (result.length && returnEarly) return; - ISOFile._sweep.call(this.boxes[box], type, result, returnEarly); - } -} - -ISOFile.prototype.getTrackSamplesInfo = function(track_id) { - var track = this.getTrackById(track_id); - if (track) { - return track.samples; - } else { - return; - } -} - -ISOFile.prototype.getTrackSample = function(track_id, number) { - var track = this.getTrackById(track_id); - var sample = this.getSample(track, number); - return sample; -} - -/* Called by the application to release the resources associated to samples already forwarded to the application */ -ISOFile.prototype.releaseUsedSamples = function (id, sampleNum) { - var size = 0; - var trak = this.getTrackById(id); - if (!trak.lastValidSample) trak.lastValidSample = 0; - for (var i = trak.lastValidSample; i < sampleNum; i++) { - size+=this.releaseSample(trak, i); - } - Log.info("ISOFile", "Track #"+id+" released samples up to "+sampleNum+" (released size: "+size+", remaining: "+this.samplesDataSize+")"); - trak.lastValidSample = sampleNum; -} - -ISOFile.prototype.start = function() { - this.sampleProcessingStarted = true; - this.processSamples(false); -} - -ISOFile.prototype.stop = function() { - this.sampleProcessingStarted = false; -} - -/* Called by the application to flush the remaining samples (e.g. once the download is finished or when no more samples will be added) */ -ISOFile.prototype.flush = function() { - Log.info("ISOFile", "Flushing remaining samples"); - this.updateSampleLists(); - this.processSamples(true); - this.stream.cleanBuffers(); - this.stream.logBufferLevel(true); -} - -/* Finds the byte offset for a given time on a given track - also returns the time of the previous rap */ -ISOFile.prototype.seekTrack = function(time, useRap, trak) { - var j; - var sample; - var seek_offset = Infinity; - var rap_seek_sample_num = 0; - var seek_sample_num = 0; - var timescale; - - if (trak.samples.length === 0) { - Log.info("ISOFile", "No sample in track, cannot seek! Using time "+Log.getDurationString(0, 1) +" and offset: "+0); - return { offset: 0, time: 0 }; - } - - for (j = 0; j < trak.samples.length; j++) { - sample = trak.samples[j]; - if (j === 0) { - seek_sample_num = 0; - timescale = sample.timescale; - } else if (sample.cts > time * sample.timescale) { - seek_sample_num = j-1; - break; - } - if (useRap && sample.is_sync) { - rap_seek_sample_num = j; - } - } - if (useRap) { - seek_sample_num = rap_seek_sample_num; - } - time = trak.samples[seek_sample_num].cts; - trak.nextSample = seek_sample_num; - while (trak.samples[seek_sample_num].alreadyRead === trak.samples[seek_sample_num].size) { - // No remaining samples to look for, all are downloaded. - if (!trak.samples[seek_sample_num + 1]) { - break; - } - seek_sample_num++; - } - seek_offset = trak.samples[seek_sample_num].offset+trak.samples[seek_sample_num].alreadyRead; - Log.info("ISOFile", "Seeking to "+(useRap ? "RAP": "")+" sample #"+trak.nextSample+" on track "+trak.tkhd.track_id+", time "+Log.getDurationString(time, timescale) +" and offset: "+seek_offset); - return { offset: seek_offset, time: time/timescale }; -} - -/* Finds the byte offset in the file corresponding to the given time or to the time of the previous RAP */ -ISOFile.prototype.seek = function(time, useRap) { - var moov = this.moov; - var trak; - var trak_seek_info; - var i; - var seek_info = { offset: Infinity, time: Infinity }; - if (!this.moov) { - throw "Cannot seek: moov not received!"; - } else { - for (i = 0; i -1) { - media_type = mediaType; - break; - } - } - } - switch(media_type) { - case "Visual": - minf.add("vmhd").set("graphicsmode",0).set("opcolor", [ 0, 0, 0 ]); - sample_description_entry.set("width", options.width) - .set("height", options.height) - .set("horizresolution", 0x48<<16) - .set("vertresolution", 0x48<<16) - .set("frame_count", 1) - .set("compressorname", options.type+" Compressor") - .set("depth", 0x18); - if (options.avcDecoderConfigRecord) { - var avcC = new BoxParser.avcCBox(); - var stream = new MP4BoxStream(options.avcDecoderConfigRecord); - avcC.parse(stream); - sample_description_entry.addBox(avcC); - } - break; - case "Audio": - minf.add("smhd").set("balance", options.balance || 0); - sample_description_entry.set("channel_count", options.channel_count || 2) - .set("samplesize", options.samplesize || 16) - .set("samplerate", options.samplerate || 1<<16); - break; - case "Hint": - minf.add("hmhd"); // TODO: add properties - break; - case "Subtitle": - minf.add("sthd"); - switch (options.type) { - case "stpp": - sample_description_entry.set("namespace", options.namespace || "nonamespace") - .set("schema_location", options.schema_location || "") - .set("auxiliary_mime_types", options.auxiliary_mime_types || ""); - break; - } - break; - case "Metadata": - minf.add("nmhd"); - break; - case "System": - minf.add("nmhd"); - break; - default: - minf.add("nmhd"); - break; - } - if (options.description) { - sample_description_entry.addBox(options.description); - } - if (options.description_boxes) { - options.description_boxes.forEach(function (b) { - sample_description_entry.addBox(b); - }); - } - minf.add("dinf").add("dref").addEntry((new BoxParser["url Box"]()).set("flags", 0x1)); - var stbl = minf.add("stbl"); - stbl.add("stsd").addEntry(sample_description_entry); - stbl.add("stts").set("sample_counts", []) - .set("sample_deltas", []); - stbl.add("stsc").set("first_chunk", []) - .set("samples_per_chunk", []) - .set("sample_description_index", []); - stbl.add("stco").set("chunk_offsets", []); - stbl.add("stsz").set("sample_sizes", []); - - this.moov.mvex.add("trex").set("track_id", options.id) - .set("default_sample_description_index", options.default_sample_description_index || 1) - .set("default_sample_duration", options.default_sample_duration || 0) - .set("default_sample_size", options.default_sample_size || 0) - .set("default_sample_flags", options.default_sample_flags || 0); - this.buildTrakSampleLists(trak); - return options.id; -} - -BoxParser.Box.prototype.computeSize = function(stream_) { - var stream = stream_ || new DataStream(); - stream.endianness = DataStream.BIG_ENDIAN; - this.write(stream); -} - -ISOFile.prototype.addSample = function (track_id, data, _options) { - var options = _options || {}; - var sample = {}; - var trak = this.getTrackById(track_id); - if (trak === null) return; - sample.number = trak.samples.length; - sample.track_id = trak.tkhd.track_id; - sample.timescale = trak.mdia.mdhd.timescale; - sample.description_index = (options.sample_description_index ? options.sample_description_index - 1: 0); - sample.description = trak.mdia.minf.stbl.stsd.entries[sample.description_index]; - sample.data = data; - sample.size = data.byteLength; - sample.alreadyRead = sample.size; - sample.duration = options.duration || 1; - sample.cts = options.cts || 0; - sample.dts = options.dts || 0; - sample.is_sync = options.is_sync || false; - sample.is_leading = options.is_leading || 0; - sample.depends_on = options.depends_on || 0; - sample.is_depended_on = options.is_depended_on || 0; - sample.has_redundancy = options.has_redundancy || 0; - sample.degradation_priority = options.degradation_priority || 0; - sample.offset = 0; - sample.subsamples = options.subsamples; - trak.samples.push(sample); - trak.samples_size += sample.size; - trak.samples_duration += sample.duration; - if (!trak.first_dts) { - trak.first_dts = options.dts; - } - - this.processSamples(); - - var moof = this.createSingleSampleMoof(sample); - this.addBox(moof); - moof.computeSize(); - /* adjusting the data_offset now that the moof size is known*/ - moof.trafs[0].truns[0].data_offset = moof.size+8; //8 is mdat header - this.add("mdat").data = new Uint8Array(data); - return sample; -} - -ISOFile.prototype.createSingleSampleMoof = function(sample) { - var sample_flags = 0; - if (sample.is_sync) - sample_flags = (1 << 25); // sample_depends_on_none (I picture) - else - sample_flags = (1 << 16); // non-sync - - var moof = new BoxParser.moofBox(); - moof.add("mfhd").set("sequence_number", this.nextMoofNumber); - this.nextMoofNumber++; - var traf = moof.add("traf"); - var trak = this.getTrackById(sample.track_id); - traf.add("tfhd").set("track_id", sample.track_id) - .set("flags", BoxParser.TFHD_FLAG_DEFAULT_BASE_IS_MOOF); - traf.add("tfdt").set("baseMediaDecodeTime", (sample.dts - (trak.first_dts || 0))); - traf.add("trun").set("flags", BoxParser.TRUN_FLAGS_DATA_OFFSET | BoxParser.TRUN_FLAGS_DURATION | - BoxParser.TRUN_FLAGS_SIZE | BoxParser.TRUN_FLAGS_FLAGS | - BoxParser.TRUN_FLAGS_CTS_OFFSET) - .set("data_offset",0) - .set("first_sample_flags",0) - .set("sample_count",1) - .set("sample_duration",[sample.duration]) - .set("sample_size",[sample.size]) - .set("sample_flags",[sample_flags]) - .set("sample_composition_time_offset", [sample.cts - sample.dts]); - return moof; -} - -// file:src/isofile-sample-processing.js -/* Index of the last moof box received */ -ISOFile.prototype.lastMoofIndex = 0; - -/* size of the buffers allocated for samples */ -ISOFile.prototype.samplesDataSize = 0; - -/* Resets all sample tables */ -ISOFile.prototype.resetTables = function () { - var i; - var trak, stco, stsc, stsz, stts, ctts, stss; - this.initial_duration = this.moov.mvhd.duration; - this.moov.mvhd.duration = 0; - for (i = 0; i < this.moov.traks.length; i++) { - trak = this.moov.traks[i]; - trak.tkhd.duration = 0; - trak.mdia.mdhd.duration = 0; - stco = trak.mdia.minf.stbl.stco || trak.mdia.minf.stbl.co64; - stco.chunk_offsets = []; - stsc = trak.mdia.minf.stbl.stsc; - stsc.first_chunk = []; - stsc.samples_per_chunk = []; - stsc.sample_description_index = []; - stsz = trak.mdia.minf.stbl.stsz || trak.mdia.minf.stbl.stz2; - stsz.sample_sizes = []; - stts = trak.mdia.minf.stbl.stts; - stts.sample_counts = []; - stts.sample_deltas = []; - ctts = trak.mdia.minf.stbl.ctts; - if (ctts) { - ctts.sample_counts = []; - ctts.sample_offsets = []; - } - stss = trak.mdia.minf.stbl.stss; - var k = trak.mdia.minf.stbl.boxes.indexOf(stss); - if (k != -1) trak.mdia.minf.stbl.boxes[k] = null; - } -} - -ISOFile.initSampleGroups = function(trak, traf, sbgps, trak_sgpds, traf_sgpds) { - var l; - var k; - var sample_groups_info; - var sample_group_info; - var sample_group_key; - function SampleGroupInfo(_type, _parameter, _sbgp) { - this.grouping_type = _type; - this.grouping_type_parameter = _parameter; - this.sbgp = _sbgp; - this.last_sample_in_run = -1; - this.entry_index = -1; - } - if (traf) { - traf.sample_groups_info = []; - } - if (!trak.sample_groups_info) { - trak.sample_groups_info = []; - } - for (k = 0; k < sbgps.length; k++) { - sample_group_key = sbgps[k].grouping_type +"/"+ sbgps[k].grouping_type_parameter; - sample_group_info = new SampleGroupInfo(sbgps[k].grouping_type, sbgps[k].grouping_type_parameter, sbgps[k]); - if (traf) { - traf.sample_groups_info[sample_group_key] = sample_group_info; - } - if (!trak.sample_groups_info[sample_group_key]) { - trak.sample_groups_info[sample_group_key] = sample_group_info; - } - for (l=0; l = 2) { - sample_group_key = trak_sgpds[k].grouping_type +"/0"; - sample_group_info = new SampleGroupInfo(trak_sgpds[k].grouping_type, 0); - if (!trak.sample_groups_info[sample_group_key]) { - trak.sample_groups_info[sample_group_key] = sample_group_info; - } - } - } - } else { - if (traf_sgpds) { - for (k = 0; k < traf_sgpds.length; k++) { - if (!traf_sgpds[k].used && traf_sgpds[k].version >= 2) { - sample_group_key = traf_sgpds[k].grouping_type +"/0"; - sample_group_info = new SampleGroupInfo(traf_sgpds[k].grouping_type, 0); - sample_group_info.is_fragment = true; - if (!traf.sample_groups_info[sample_group_key]) { - traf.sample_groups_info[sample_group_key] = sample_group_info; - } - } - } - } - } -} - -ISOFile.setSampleGroupProperties = function(trak, sample, sample_number, sample_groups_info) { - var k; - var index; - sample.sample_groups = []; - for (k in sample_groups_info) { - sample.sample_groups[k] = {}; - sample.sample_groups[k].grouping_type = sample_groups_info[k].grouping_type; - sample.sample_groups[k].grouping_type_parameter = sample_groups_info[k].grouping_type_parameter; - if (sample_number >= sample_groups_info[k].last_sample_in_run) { - if (sample_groups_info[k].last_sample_in_run < 0) { - sample_groups_info[k].last_sample_in_run = 0; - } - sample_groups_info[k].entry_index++; - if (sample_groups_info[k].entry_index <= sample_groups_info[k].sbgp.entries.length - 1) { - sample_groups_info[k].last_sample_in_run += sample_groups_info[k].sbgp.entries[sample_groups_info[k].entry_index].sample_count; - } - } - if (sample_groups_info[k].entry_index <= sample_groups_info[k].sbgp.entries.length - 1) { - sample.sample_groups[k].group_description_index = sample_groups_info[k].sbgp.entries[sample_groups_info[k].entry_index].group_description_index; - } else { - sample.sample_groups[k].group_description_index = -1; // special value for not defined - } - if (sample.sample_groups[k].group_description_index !== 0) { - var description; - if (sample_groups_info[k].fragment_description) { - description = sample_groups_info[k].fragment_description; - } else { - description = sample_groups_info[k].description; - } - if (sample.sample_groups[k].group_description_index > 0) { - if (sample.sample_groups[k].group_description_index > 65535) { - index = (sample.sample_groups[k].group_description_index >> 16)-1; - } else { - index = sample.sample_groups[k].group_description_index-1; - } - if (description && index >= 0) { - sample.sample_groups[k].description = description.entries[index]; - } - } else { - if (description && description.version >= 2) { - if (description.default_group_description_index > 0) { - sample.sample_groups[k].description = description.entries[description.default_group_description_index-1]; - } - } - } - } - } -} - -ISOFile.process_sdtp = function (sdtp, sample, number) { - if (!sample) { - return; - } - if (sdtp) { - sample.is_leading = sdtp.is_leading[number]; - sample.depends_on = sdtp.sample_depends_on[number]; - sample.is_depended_on = sdtp.sample_is_depended_on[number]; - sample.has_redundancy = sdtp.sample_has_redundancy[number]; - } else { - sample.is_leading = 0; - sample.depends_on = 0; - sample.is_depended_on = 0 - sample.has_redundancy = 0; - } -} - -/* Build initial sample list from sample tables */ -ISOFile.prototype.buildSampleLists = function() { - var i; - var trak; - for (i = 0; i < this.moov.traks.length; i++) { - trak = this.moov.traks[i]; - this.buildTrakSampleLists(trak); - } -} - -ISOFile.prototype.buildTrakSampleLists = function(trak) { - var j, k; - var stco, stsc, stsz, stts, ctts, stss, stsd, subs, sbgps, sgpds, stdp; - var chunk_run_index, chunk_index, last_chunk_in_run, offset_in_chunk, last_sample_in_chunk; - var last_sample_in_stts_run, stts_run_index, last_sample_in_ctts_run, ctts_run_index, last_stss_index, last_subs_index, subs_entry_index, last_subs_sample_index; - - trak.samples = []; - trak.samples_duration = 0; - trak.samples_size = 0; - stco = trak.mdia.minf.stbl.stco || trak.mdia.minf.stbl.co64; - stsc = trak.mdia.minf.stbl.stsc; - stsz = trak.mdia.minf.stbl.stsz || trak.mdia.minf.stbl.stz2; - stts = trak.mdia.minf.stbl.stts; - ctts = trak.mdia.minf.stbl.ctts; - stss = trak.mdia.minf.stbl.stss; - stsd = trak.mdia.minf.stbl.stsd; - subs = trak.mdia.minf.stbl.subs; - stdp = trak.mdia.minf.stbl.stdp; - sbgps = trak.mdia.minf.stbl.sbgps; - sgpds = trak.mdia.minf.stbl.sgpds; - - last_sample_in_stts_run = -1; - stts_run_index = -1; - last_sample_in_ctts_run = -1; - ctts_run_index = -1; - last_stss_index = 0; - subs_entry_index = 0; - last_subs_sample_index = 0; - - ISOFile.initSampleGroups(trak, null, sbgps, sgpds); - - if (typeof stsz === "undefined") { - return; - } - - /* we build the samples one by one and compute their properties */ - for (j = 0; j < stsz.sample_sizes.length; j++) { - var sample = {}; - sample.number = j; - sample.track_id = trak.tkhd.track_id; - sample.timescale = trak.mdia.mdhd.timescale; - sample.alreadyRead = 0; - trak.samples[j] = sample; - /* size can be known directly */ - sample.size = stsz.sample_sizes[j]; - trak.samples_size += sample.size; - /* computing chunk-based properties (offset, sample description index)*/ - if (j === 0) { - chunk_index = 1; /* the first sample is in the first chunk (chunk indexes are 1-based) */ - chunk_run_index = 0; /* the first chunk is the first entry in the first_chunk table */ - sample.chunk_index = chunk_index; - sample.chunk_run_index = chunk_run_index; - last_sample_in_chunk = stsc.samples_per_chunk[chunk_run_index]; - offset_in_chunk = 0; - - /* Is there another entry in the first_chunk table ? */ - if (chunk_run_index + 1 < stsc.first_chunk.length) { - /* The last chunk in the run is the chunk before the next first chunk */ - last_chunk_in_run = stsc.first_chunk[chunk_run_index+1]-1; - } else { - /* There is only one entry in the table, it is valid for all future chunks*/ - last_chunk_in_run = Infinity; - } - } else { - if (j < last_sample_in_chunk) { - /* the sample is still in the current chunk */ - sample.chunk_index = chunk_index; - sample.chunk_run_index = chunk_run_index; - } else { - /* the sample is in the next chunk */ - chunk_index++; - sample.chunk_index = chunk_index; - /* reset the accumulated offset in the chunk */ - offset_in_chunk = 0; - if (chunk_index <= last_chunk_in_run) { - /* stay in the same entry of the first_chunk table */ - /* chunk_run_index unmodified */ - } else { - chunk_run_index++; - /* Is there another entry in the first_chunk table ? */ - if (chunk_run_index + 1 < stsc.first_chunk.length) { - /* The last chunk in the run is the chunk before the next first chunk */ - last_chunk_in_run = stsc.first_chunk[chunk_run_index+1]-1; - } else { - /* There is only one entry in the table, it is valid for all future chunks*/ - last_chunk_in_run = Infinity; - } - - } - sample.chunk_run_index = chunk_run_index; - last_sample_in_chunk += stsc.samples_per_chunk[chunk_run_index]; - } - } - - sample.description_index = stsc.sample_description_index[sample.chunk_run_index]-1; - sample.description = stsd.entries[sample.description_index]; - sample.offset = stco.chunk_offsets[sample.chunk_index-1] + offset_in_chunk; /* chunk indexes are 1-based */ - offset_in_chunk += sample.size; - - /* setting dts, cts, duration and rap flags */ - if (j > last_sample_in_stts_run) { - stts_run_index++; - if (last_sample_in_stts_run < 0) { - last_sample_in_stts_run = 0; - } - last_sample_in_stts_run += stts.sample_counts[stts_run_index]; - } - if (j > 0) { - trak.samples[j-1].duration = stts.sample_deltas[stts_run_index]; - trak.samples_duration += trak.samples[j-1].duration; - sample.dts = trak.samples[j-1].dts + trak.samples[j-1].duration; - } else { - sample.dts = 0; - } - if (ctts) { - if (j >= last_sample_in_ctts_run) { - ctts_run_index++; - if (last_sample_in_ctts_run < 0) { - last_sample_in_ctts_run = 0; - } - last_sample_in_ctts_run += ctts.sample_counts[ctts_run_index]; - } - sample.cts = trak.samples[j].dts + ctts.sample_offsets[ctts_run_index]; - } else { - sample.cts = sample.dts; - } - if (stss) { - if (j == stss.sample_numbers[last_stss_index] - 1) { // sample numbers are 1-based - sample.is_sync = true; - last_stss_index++; - } else { - sample.is_sync = false; - sample.degradation_priority = 0; - } - if (subs) { - if (subs.entries[subs_entry_index].sample_delta + last_subs_sample_index == j+1) { - sample.subsamples = subs.entries[subs_entry_index].subsamples; - last_subs_sample_index += subs.entries[subs_entry_index].sample_delta; - subs_entry_index++; - } - } - } else { - sample.is_sync = true; - } - ISOFile.process_sdtp(trak.mdia.minf.stbl.sdtp, sample, sample.number); - if (stdp) { - sample.degradation_priority = stdp.priority[j]; - } else { - sample.degradation_priority = 0; - } - if (subs) { - if (subs.entries[subs_entry_index].sample_delta + last_subs_sample_index == j) { - sample.subsamples = subs.entries[subs_entry_index].subsamples; - last_subs_sample_index += subs.entries[subs_entry_index].sample_delta; - } - } - if (sbgps.length > 0 || sgpds.length > 0) { - ISOFile.setSampleGroupProperties(trak, sample, j, trak.sample_groups_info); - } - } - if (j>0) { - trak.samples[j-1].duration = Math.max(trak.mdia.mdhd.duration - trak.samples[j-1].dts, 0); - trak.samples_duration += trak.samples[j-1].duration; - } -} - -/* Update sample list when new 'moof' boxes are received */ -ISOFile.prototype.updateSampleLists = function() { - var i, j, k; - var default_sample_description_index, default_sample_duration, default_sample_size, default_sample_flags; - var last_run_position; - var box, moof, traf, trak, trex; - var sample; - var sample_flags; - - if (this.moov === undefined) { - return; - } - /* if the input file is fragmented and fetched in multiple downloads, we need to update the list of samples */ - while (this.lastMoofIndex < this.moofs.length) { - box = this.moofs[this.lastMoofIndex]; - this.lastMoofIndex++; - if (box.type == "moof") { - moof = box; - for (i = 0; i < moof.trafs.length; i++) { - traf = moof.trafs[i]; - trak = this.getTrackById(traf.tfhd.track_id); - trex = this.getTrexById(traf.tfhd.track_id); - if (traf.tfhd.flags & BoxParser.TFHD_FLAG_SAMPLE_DESC) { - default_sample_description_index = traf.tfhd.default_sample_description_index; - } else { - default_sample_description_index = (trex ? trex.default_sample_description_index: 1); - } - if (traf.tfhd.flags & BoxParser.TFHD_FLAG_SAMPLE_DUR) { - default_sample_duration = traf.tfhd.default_sample_duration; - } else { - default_sample_duration = (trex ? trex.default_sample_duration : 0); - } - if (traf.tfhd.flags & BoxParser.TFHD_FLAG_SAMPLE_SIZE) { - default_sample_size = traf.tfhd.default_sample_size; - } else { - default_sample_size = (trex ? trex.default_sample_size : 0); - } - if (traf.tfhd.flags & BoxParser.TFHD_FLAG_SAMPLE_FLAGS) { - default_sample_flags = traf.tfhd.default_sample_flags; - } else { - default_sample_flags = (trex ? trex.default_sample_flags : 0); - } - traf.sample_number = 0; - /* process sample groups */ - if (traf.sbgps.length > 0) { - ISOFile.initSampleGroups(trak, traf, traf.sbgps, trak.mdia.minf.stbl.sgpds, traf.sgpds); - } - for (j = 0; j < traf.truns.length; j++) { - var trun = traf.truns[j]; - for (k = 0; k < trun.sample_count; k++) { - sample = {}; - sample.moof_number = this.lastMoofIndex; - sample.number_in_traf = traf.sample_number; - traf.sample_number++; - sample.number = trak.samples.length; - traf.first_sample_index = trak.samples.length; - trak.samples.push(sample); - sample.track_id = trak.tkhd.track_id; - sample.timescale = trak.mdia.mdhd.timescale; - sample.description_index = default_sample_description_index-1; - sample.description = trak.mdia.minf.stbl.stsd.entries[sample.description_index]; - sample.size = default_sample_size; - if (trun.flags & BoxParser.TRUN_FLAGS_SIZE) { - sample.size = trun.sample_size[k]; - } - trak.samples_size += sample.size; - sample.duration = default_sample_duration; - if (trun.flags & BoxParser.TRUN_FLAGS_DURATION) { - sample.duration = trun.sample_duration[k]; - } - trak.samples_duration += sample.duration; - if (trak.first_traf_merged || k > 0) { - sample.dts = trak.samples[trak.samples.length-2].dts+trak.samples[trak.samples.length-2].duration; - } else { - if (traf.tfdt) { - sample.dts = traf.tfdt.baseMediaDecodeTime; - } else { - sample.dts = 0; - } - trak.first_traf_merged = true; - } - sample.cts = sample.dts; - if (trun.flags & BoxParser.TRUN_FLAGS_CTS_OFFSET) { - sample.cts = sample.dts + trun.sample_composition_time_offset[k]; - } - sample_flags = default_sample_flags; - if (trun.flags & BoxParser.TRUN_FLAGS_FLAGS) { - sample_flags = trun.sample_flags[k]; - } else if (k === 0 && (trun.flags & BoxParser.TRUN_FLAGS_FIRST_FLAG)) { - sample_flags = trun.first_sample_flags; - } - sample.is_sync = ((sample_flags >> 16 & 0x1) ? false : true); - sample.is_leading = (sample_flags >> 26 & 0x3); - sample.depends_on = (sample_flags >> 24 & 0x3); - sample.is_depended_on = (sample_flags >> 22 & 0x3); - sample.has_redundancy = (sample_flags >> 20 & 0x3); - sample.degradation_priority = (sample_flags & 0xFFFF); - //ISOFile.process_sdtp(traf.sdtp, sample, sample.number_in_traf); - var bdop = (traf.tfhd.flags & BoxParser.TFHD_FLAG_BASE_DATA_OFFSET) ? true : false; - var dbim = (traf.tfhd.flags & BoxParser.TFHD_FLAG_DEFAULT_BASE_IS_MOOF) ? true : false; - var dop = (trun.flags & BoxParser.TRUN_FLAGS_DATA_OFFSET) ? true : false; - var bdo = 0; - if (!bdop) { - if (!dbim) { - if (j === 0) { // the first track in the movie fragment - bdo = moof.start; // the position of the first byte of the enclosing Movie Fragment Box - } else { - bdo = last_run_position; // end of the data defined by the preceding *track* (irrespective of the track id) fragment in the moof - } - } else { - bdo = moof.start; - } - } else { - bdo = traf.tfhd.base_data_offset; - } - if (j === 0 && k === 0) { - if (dop) { - sample.offset = bdo + trun.data_offset; // If the data-offset is present, it is relative to the base-data-offset established in the track fragment header - } else { - sample.offset = bdo; // the data for this run starts the base-data-offset defined by the track fragment header - } - } else { - sample.offset = last_run_position; // this run starts immediately after the data of the previous run - } - last_run_position = sample.offset + sample.size; - if (traf.sbgps.length > 0 || traf.sgpds.length > 0 || - trak.mdia.minf.stbl.sbgps.length > 0 || trak.mdia.minf.stbl.sgpds.length > 0) { - ISOFile.setSampleGroupProperties(trak, sample, sample.number_in_traf, traf.sample_groups_info); - } - } - } - if (traf.subs) { - trak.has_fragment_subsamples = true; - var sample_index = traf.first_sample_index; - for (j = 0; j < traf.subs.entries.length; j++) { - sample_index += traf.subs.entries[j].sample_delta; - sample = trak.samples[sample_index-1]; - sample.subsamples = traf.subs.entries[j].subsamples; - } - } - } - } - } -} - -/* Try to get sample data for a given sample: - returns null if not found - returns the same sample if already requested - */ -ISOFile.prototype.getSample = function(trak, sampleNum) { - var buffer; - var sample = trak.samples[sampleNum]; - - if (!this.moov) { - return null; - } - - if (!sample.data) { - /* Not yet fetched */ - sample.data = new Uint8Array(sample.size); - sample.alreadyRead = 0; - this.samplesDataSize += sample.size; - Log.debug("ISOFile", "Allocating sample #"+sampleNum+" on track #"+trak.tkhd.track_id+" of size "+sample.size+" (total: "+this.samplesDataSize+")"); - } else if (sample.alreadyRead == sample.size) { - /* Already fetched entirely */ - return sample; - } - - /* The sample has only been partially fetched, we need to check in all buffers */ - while(true) { - var index = this.stream.findPosition(true, sample.offset + sample.alreadyRead, false); - if (index > -1) { - buffer = this.stream.buffers[index]; - var lengthAfterStart = buffer.byteLength - (sample.offset + sample.alreadyRead - buffer.fileStart); - if (sample.size - sample.alreadyRead <= lengthAfterStart) { - /* the (rest of the) sample is entirely contained in this buffer */ - - Log.debug("ISOFile","Getting sample #"+sampleNum+" data (alreadyRead: "+sample.alreadyRead+" offset: "+ - (sample.offset+sample.alreadyRead - buffer.fileStart)+" read size: "+(sample.size - sample.alreadyRead)+" full size: "+sample.size+")"); - - DataStream.memcpy(sample.data.buffer, sample.alreadyRead, - buffer, sample.offset+sample.alreadyRead - buffer.fileStart, sample.size - sample.alreadyRead); - - /* update the number of bytes used in this buffer and check if it needs to be removed */ - buffer.usedBytes += sample.size - sample.alreadyRead; - this.stream.logBufferLevel(); - - sample.alreadyRead = sample.size; - - return sample; - } else { - /* the sample does not end in this buffer */ - - if (lengthAfterStart === 0) return null; - - Log.debug("ISOFile","Getting sample #"+sampleNum+" partial data (alreadyRead: "+sample.alreadyRead+" offset: "+ - (sample.offset+sample.alreadyRead - buffer.fileStart)+" read size: "+lengthAfterStart+" full size: "+sample.size+")"); - - DataStream.memcpy(sample.data.buffer, sample.alreadyRead, - buffer, sample.offset+sample.alreadyRead - buffer.fileStart, lengthAfterStart); - sample.alreadyRead += lengthAfterStart; - - /* update the number of bytes used in this buffer and check if it needs to be removed */ - buffer.usedBytes += lengthAfterStart; - this.stream.logBufferLevel(); - - /* keep looking in the next buffer */ - } - } else { - return null; - } - } -} - -/* Release the memory used to store the data of the sample */ -ISOFile.prototype.releaseSample = function(trak, sampleNum) { - var sample = trak.samples[sampleNum]; - if (sample.data) { - this.samplesDataSize -= sample.size; - sample.data = null; - sample.alreadyRead = 0; - return sample.size; - } else { - return 0; - } -} - -ISOFile.prototype.getAllocatedSampleDataSize = function() { - return this.samplesDataSize; -} - -/* Builds the MIME Type 'codecs' sub-parameters for the whole file */ -ISOFile.prototype.getCodecs = function() { - var i; - var codecs = ""; - for (i = 0; i < this.moov.traks.length; i++) { - var trak = this.moov.traks[i]; - if (i>0) { - codecs+=","; - } - codecs += trak.mdia.minf.stbl.stsd.entries[0].getCodec(); - } - return codecs; -} - -/* Helper function */ -ISOFile.prototype.getTrexById = function(id) { - var i; - if (!this.moov || !this.moov.mvex) return null; - for (i = 0; i < this.moov.mvex.trexs.length; i++) { - var trex = this.moov.mvex.trexs[i]; - if (trex.track_id == id) return trex; - } - return null; -} - -/* Helper function */ -ISOFile.prototype.getTrackById = function(id) { - if (this.moov === undefined) { - return null; - } - for (var j = 0; j < this.moov.traks.length; j++) { - var trak = this.moov.traks[j]; - if (trak.tkhd.track_id == id) return trak; - } - return null; -} -// file:src/isofile-item-processing.js -ISOFile.prototype.items = []; -/* size of the buffers allocated for samples */ -ISOFile.prototype.itemsDataSize = 0; - -ISOFile.prototype.flattenItemInfo = function() { - var items = this.items; - var i, j; - var item; - var meta = this.meta; - if (meta === null || meta === undefined) return; - if (meta.hdlr === undefined) return; - if (meta.iinf === undefined) return; - for (i = 0; i < meta.iinf.item_infos.length; i++) { - item = {}; - item.id = meta.iinf.item_infos[i].item_ID; - items[item.id] = item; - item.ref_to = []; - item.name = meta.iinf.item_infos[i].item_name; - if (meta.iinf.item_infos[i].protection_index > 0) { - item.protection = meta.ipro.protections[meta.iinf.item_infos[i].protection_index-1]; - } - if (meta.iinf.item_infos[i].item_type) { - item.type = meta.iinf.item_infos[i].item_type; - } else { - item.type = "mime"; - } - item.content_type = meta.iinf.item_infos[i].content_type; - item.content_encoding = meta.iinf.item_infos[i].content_encoding; - } - if (meta.iloc) { - for(i = 0; i < meta.iloc.items.length; i++) { - var offset; - var itemloc = meta.iloc.items[i]; - item = items[itemloc.item_ID]; - if (itemloc.data_reference_index !== 0) { - Log.warn("Item storage with reference to other files: not supported"); - item.source = meta.dinf.boxes[itemloc.data_reference_index-1]; - } - switch(itemloc.construction_method) { - case 0: // offset into the file referenced by the data reference index - break; - case 1: // offset into the idat box of this meta box - Log.warn("Item storage with construction_method : not supported"); - break; - case 2: // offset into another item - Log.warn("Item storage with construction_method : not supported"); - break; - } - item.extents = []; - item.size = 0; - for (j = 0; j < itemloc.extents.length; j++) { - item.extents[j] = {}; - item.extents[j].offset = itemloc.extents[j].extent_offset + itemloc.base_offset; - item.extents[j].length = itemloc.extents[j].extent_length; - item.extents[j].alreadyRead = 0; - item.size += item.extents[j].length; - } - } - } - if (meta.pitm) { - items[meta.pitm.item_id].primary = true; - } - if (meta.iref) { - for (i=0; i 0 && propEntry.property_index-1 < meta.iprp.ipco.boxes.length) { - var propbox = meta.iprp.ipco.boxes[propEntry.property_index-1]; - item.properties[propbox.type] = propbox; - item.properties.boxes.push(propbox); - } - } - } - } - } -} - -ISOFile.prototype.getItem = function(item_id) { - var buffer; - var item; - - if (!this.meta) { - return null; - } - - item = this.items[item_id]; - if (!item.data && item.size) { - /* Not yet fetched */ - item.data = new Uint8Array(item.size); - item.alreadyRead = 0; - this.itemsDataSize += item.size; - Log.debug("ISOFile", "Allocating item #"+item_id+" of size "+item.size+" (total: "+this.itemsDataSize+")"); - } else if (item.alreadyRead === item.size) { - /* Already fetched entirely */ - return item; - } - - /* The item has only been partially fetched, we need to check in all buffers to find the remaining extents*/ - - for (var i = 0; i < item.extents.length; i++) { - var extent = item.extents[i]; - if (extent.alreadyRead === extent.length) { - continue; - } else { - var index = this.stream.findPosition(true, extent.offset + extent.alreadyRead, false); - if (index > -1) { - buffer = this.stream.buffers[index]; - var lengthAfterStart = buffer.byteLength - (extent.offset + extent.alreadyRead - buffer.fileStart); - if (extent.length - extent.alreadyRead <= lengthAfterStart) { - /* the (rest of the) extent is entirely contained in this buffer */ - - Log.debug("ISOFile","Getting item #"+item_id+" extent #"+i+" data (alreadyRead: "+extent.alreadyRead+ - " offset: "+(extent.offset+extent.alreadyRead - buffer.fileStart)+" read size: "+(extent.length - extent.alreadyRead)+ - " full extent size: "+extent.length+" full item size: "+item.size+")"); - - DataStream.memcpy(item.data.buffer, item.alreadyRead, - buffer, extent.offset+extent.alreadyRead - buffer.fileStart, extent.length - extent.alreadyRead); - - /* update the number of bytes used in this buffer and check if it needs to be removed */ - buffer.usedBytes += extent.length - extent.alreadyRead; - this.stream.logBufferLevel(); - - item.alreadyRead += (extent.length - extent.alreadyRead); - extent.alreadyRead = extent.length; - } else { - /* the sample does not end in this buffer */ - - Log.debug("ISOFile","Getting item #"+item_id+" extent #"+i+" partial data (alreadyRead: "+extent.alreadyRead+" offset: "+ - (extent.offset+extent.alreadyRead - buffer.fileStart)+" read size: "+lengthAfterStart+ - " full extent size: "+extent.length+" full item size: "+item.size+")"); - - DataStream.memcpy(item.data.buffer, item.alreadyRead, - buffer, extent.offset+extent.alreadyRead - buffer.fileStart, lengthAfterStart); - extent.alreadyRead += lengthAfterStart; - item.alreadyRead += lengthAfterStart; - - /* update the number of bytes used in this buffer and check if it needs to be removed */ - buffer.usedBytes += lengthAfterStart; - this.stream.logBufferLevel(); - return null; - } - } else { - return null; - } - } - } - if (item.alreadyRead === item.size) { - /* fetched entirely */ - return item; - } else { - return null; - } -} - -/* Release the memory used to store the data of the item */ -ISOFile.prototype.releaseItem = function(item_id) { - var item = this.items[item_id]; - if (item.data) { - this.itemsDataSize -= item.size; - item.data = null; - item.alreadyRead = 0; - for (var i = 0; i < item.extents.length; i++) { - var extent = item.extents[i]; - extent.alreadyRead = 0; - } - return item.size; - } else { - return 0; - } -} - - -ISOFile.prototype.processItems = function(callback) { - for(var i in this.items) { - var item = this.items[i]; - this.getItem(item.id); - if (callback && !item.sent) { - callback(item); - item.sent = true; - item.data = null; - } - } -} - -ISOFile.prototype.hasItem = function(name) { - for(var i in this.items) { - var item = this.items[i]; - if (item.name === name) { - return item.id; - } - } - return -1; -} - -ISOFile.prototype.getMetaHandler = function() { - if (!this.meta) { - return null; - } else { - return this.meta.hdlr.handler; - } -} - -ISOFile.prototype.getPrimaryItem = function() { - if (!this.meta || !this.meta.pitm) { - return null; - } else { - return this.getItem(this.meta.pitm.item_id); - } -} - -ISOFile.prototype.itemToFragmentedTrackFile = function(_options) { - var options = _options || {}; - var item = null; - if (options.itemId) { - item = this.getItem(options.itemId); - } else { - item = this.getPrimaryItem(); - } - if (item == null) return null; - - var file = new ISOFile(); - file.discardMdatData = false; - // assuming the track type is the same as the item type - var trackOptions = { type: item.type, description_boxes: item.properties.boxes}; - if (item.properties.ispe) { - trackOptions.width = item.properties.ispe.image_width; - trackOptions.height = item.properties.ispe.image_height; - } - var trackId = file.addTrack(trackOptions); - if (trackId) { - file.addSample(trackId, item.data); - return file; - } else { - return null; - } -} - -// file:src/isofile-write.js -/* Rewrite the entire file */ -ISOFile.prototype.write = function(outstream) { - for (var i=0; i0 ? this.moov.traks[i].samples[0].duration: 0)); - initSegs.push(seg); - } - return initSegs; -} - -// file:src/box-print.js -/* - * Copyright (c) Telecom ParisTech/TSI/MM/GPAC Cyril Concolato - * License: BSD-3-Clause (see LICENSE file) - */ -BoxParser.Box.prototype.printHeader = function(output) { - this.size += 8; - if (this.size > MAX_SIZE) { - this.size += 8; - } - if (this.type === "uuid") { - this.size += 16; - } - output.log(output.indent+"size:"+this.size); - output.log(output.indent+"type:"+this.type); -} - -BoxParser.FullBox.prototype.printHeader = function(output) { - this.size += 4; - BoxParser.Box.prototype.printHeader.call(this, output); - output.log(output.indent+"version:"+this.version); - output.log(output.indent+"flags:"+this.flags); -} - -BoxParser.Box.prototype.print = function(output) { - this.printHeader(output); -} - -BoxParser.ContainerBox.prototype.print = function(output) { - this.printHeader(output); - for (var i=0; i>8)); - output.log(output.indent+"matrix: "+this.matrix.join(", ")); - output.log(output.indent+"next_track_id: "+this.next_track_id); -} - -BoxParser.tkhdBox.prototype.print = function(output) { - BoxParser.FullBox.prototype.printHeader.call(this, output); - output.log(output.indent+"creation_time: "+this.creation_time); - output.log(output.indent+"modification_time: "+this.modification_time); - output.log(output.indent+"track_id: "+this.track_id); - output.log(output.indent+"duration: "+this.duration); - output.log(output.indent+"volume: "+(this.volume>>8)); - output.log(output.indent+"matrix: "+this.matrix.join(", ")); - output.log(output.indent+"layer: "+this.layer); - output.log(output.indent+"alternate_group: "+this.alternate_group); - output.log(output.indent+"width: "+this.width); - output.log(output.indent+"height: "+this.height); -}// file:src/mp4box.js -/* - * Copyright (c) 2012-2013. Telecom ParisTech/TSI/MM/GPAC Cyril Concolato - * License: BSD-3-Clause (see LICENSE file) - */ -var MP4Box = {}; - -MP4Box.createFile = function (_keepMdatData, _stream) { - /* Boolean indicating if bytes containing media data should be kept in memory */ - var keepMdatData = (_keepMdatData !== undefined ? _keepMdatData : true); - var file = new ISOFile(_stream); - file.discardMdatData = (keepMdatData ? false : true); - return file; -} - -if (typeof exports !== 'undefined') { - exports.createFile = MP4Box.createFile; -} diff --git a/player/src/mp4/mp4box.d.ts b/player/src/mp4/mp4box.d.ts new file mode 100644 index 0000000..7ce91f7 --- /dev/null +++ b/player/src/mp4/mp4box.d.ts @@ -0,0 +1,148 @@ +// https://github.com/gpac/mp4box.js/issues/233 + +declare module "mp4box" { + interface MP4MediaTrack { + id: number; + created: Date; + modified: Date; + movie_duration: number; + layer: number; + alternate_group: number; + volume: number; + track_width: number; + track_height: number; + timescale: number; + duration: number; + bitrate: number; + codec: string; + language: string; + nb_samples: number; + } + + interface MP4VideoData { + width: number; + height: number; + } + + interface MP4VideoTrack extends MP4MediaTrack { + video: MP4VideoData; + } + + interface MP4AudioData { + sample_rate: number; + channel_count: number; + sample_size: number; + } + + interface MP4AudioTrack extends MP4MediaTrack { + audio: MP4AudioData; + } + + type MP4Track = MP4VideoTrack | MP4AudioTrack; + + export interface MP4Info { + duration: number; + timescale: number; + fragment_duration: number; + isFragmented: boolean; + isProgressive: boolean; + hasIOD: boolean; + brands: string[]; + created: Date; + modified: Date; + tracks: MP4Track[]; + mime: string; + audioTracks: MP4AudioTrack[]; + videoTracks: MP4VideoTrack[]; + } + + export type MP4ArrayBuffer = ArrayBuffer & {fileStart: number}; + + export interface MP4File { + onMoovStart?: () => void; + onReady?: (info: MP4Info) => void; + onError?: (e: string) => void; + onSamples?: (id: number, user: any, samples: Sample[]) => void; + + appendBuffer(data: MP4ArrayBuffer): number; + start(): void; + stop(): void; + flush(): void; + + setExtractionOptions(id: number, user: any, options: ExtractionOptions): void; + } + + export function createFile(): MP4File; + + export interface Sample { + number: number; + track_id: number; + timescale: number; + description_index: number; + description: any; + data: ArrayBuffer; + size: number; + alreadyRead: number; + duration: number; + cts: number; + dts: number; + is_sync: boolean; + is_leading: number; + depends_on: number; + is_depended_on: number; + has_redundancy: number; + degration_priority: number; + offset: number; + subsamples: any; + } + + export interface ExtractionOptions { + nbSamples: number; + } + + const BIG_ENDIAN: boolean; + const LITTLE_ENDIAN: boolean; + + export class DataStream { + constructor(buffer: ArrayBuffer, byteOffset?: number, littleEndian?: boolean); + getPosition(): number; + + get byteLength(): number; + get buffer(): ArrayBuffer; + set buffer(v: ArrayBuffer); + get byteOffset(): number; + set byteOffset(v: number); + get dataView(): DataView; + set dataView(v: DataView); + + seek(pos: number): void; + isEof(): boolean; + + mapUint8Array(length: number): Uint8Array; + readInt32Array(length: number, littleEndian: boolean): Int32Array; + readInt16Array(length: number, littleEndian: boolean): Int16Array; + readInt8(length: number): Int8Array; + readUint32Array(length: number, littleEndian: boolean): Uint32Array; + readUint16Array(length: number, littleEndian: boolean): Uint16Array; + readUint8(length: number): Uint8Array; + readFloat64Array(length: number, littleEndian: boolean): Float64Array; + readFloat32Array(length: number, littleEndian: boolean): Float32Array; + + readInt32(littleEndian: boolean): number; + readInt16(littleEndian: boolean): number; + readInt8(): number; + readUint32(littleEndian: boolean): number; + readUint16(littleEndian: boolean): number; + readUint8(): number; + readFloat32(littleEndian: boolean): number; + readFloat64(littleEndian: boolean): number; + + endianness: boolean; + + memcpy(dst: ArrayBufferLike, dstOffset: number, src: ArrayBufferLike, srcOffset: number, byteLength: number): void; + + // TODO I got bored porting the remaining functions + } + + export { }; +} \ No newline at end of file diff --git a/player/src/transport/index.ts b/player/src/transport/index.ts index 39243c0..32330ee 100644 --- a/player/src/transport/index.ts +++ b/player/src/transport/index.ts @@ -8,8 +8,6 @@ import Video from "../video" // @ts-ignore bundler embeds data import fingerprint from 'bundle-text:./fingerprint.hex'; -/// - export interface PlayerInit { url: string; canvas: HTMLCanvasElement; diff --git a/player/src/types/webtransport.d.ts b/player/src/transport/webtransport.d.ts similarity index 100% rename from player/src/types/webtransport.d.ts rename to player/src/transport/webtransport.d.ts diff --git a/player/tsconfig.json b/player/tsconfig.json index 5fce1da..9fcdf11 100644 --- a/player/tsconfig.json +++ b/player/tsconfig.json @@ -7,9 +7,5 @@ "module": "es2022", "moduleResolution": "node", "strict": true, - "typeRoots": [ - "src/types" - ], - "allowJs": true } } \ No newline at end of file diff --git a/player/yarn.lock b/player/yarn.lock index c4a13b7..34f2706 100644 --- a/player/yarn.lock +++ b/player/yarn.lock @@ -1196,6 +1196,11 @@ micromatch@^4.0.5: braces "^3.0.2" picomatch "^2.3.1" +mp4box@^0.5.2: + version "0.5.2" + resolved "https://registry.yarnpkg.com/mp4box/-/mp4box-0.5.2.tgz#6a2d36fdd0e2d3f2f2bee446d2067edf0b3871bc" + integrity sha512-zRmGlvxy+YdW3Dmt+TR4xPHynbxwXtAQDTN/Fo9N3LMxaUlB2C5KmZpzYyGKy4c7k4Jf3RCR0A2pm9SZELOLXw== + msgpackr-extract@^3.0.1: version "3.0.2" resolved "https://registry.yarnpkg.com/msgpackr-extract/-/msgpackr-extract-3.0.2.tgz#e05ec1bb4453ddf020551bcd5daaf0092a2c279d"