UNPKG

rx-player

Version:
527 lines (493 loc) 12.3 kB
/** * Copyright 2015 CANAL+ Group * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ import { createBox } from "../../../parsers/containers/isobmff"; import { be2toi, be4toi, concat, itobe2, itobe4, itobe8, } from "../../../utils/byte_parsing"; import { hexToBytes, strToUtf8 } from "../../../utils/string_parsing"; /** * @param {Number} width * @param {Number} height * @param {Number} hRes - horizontal resolution, eg 72 * @param {Number} vRes - vertical resolution, eg 72 * @param {string} encName * @param {Number} colorDepth - eg 24 * @param {Uint8Array} avcc - Uint8Array representing the avcC atom * @returns {Uint8Array} */ function createAVC1Box( width: number, height: number, hRes: number, vRes: number, encName: string, colorDepth: number, avcc: Uint8Array, ): Uint8Array { return createBox( "avc1", concat( 6, // 6 bytes reserved itobe2(1), 16, // drefIdx + QuickTime reserved, zeroes itobe2(width), // size 2 w itobe2(height), // size 2 h itobe2(hRes), 2, // reso 4 h itobe2(vRes), 2 + 4, // reso 4 v + QuickTime reserved, zeroes [0, 1, encName.length], // frame count (default 1) strToUtf8(encName), // 1byte len + encoder name str 31 - encName.length, // + padding itobe2(colorDepth), // color depth [0xff, 0xff], // reserved ones avcc, // avcc atom, ), ); } /** * @param {Number} width * @param {Number} height * @param {Number} hRes - horizontal resolution, eg 72 * @param {Number} vRes - vertical resolution, eg 72 * @param {string} encName * @param {Number} colorDepth - eg 24 * @param {Uint8Array} avcc - Uint8Array representing the avcC atom * @param {Uint8Array} sinf - Uint8Array representing the sinf atom * @returns {Uint8Array} */ function createENCVBox( width: number, height: number, hRes: number, vRes: number, encName: string, colorDepth: number, avcc: Uint8Array, sinf: Uint8Array, ): Uint8Array { return createBox( "encv", concat( 6, // 6 bytes reserved itobe2(1), 16, // drefIdx + QuickTime reserved, zeroes itobe2(width), // size 2 w itobe2(height), // size 2 h itobe2(hRes), 2, // reso 4 h itobe2(vRes), 2 + 4, // reso 4 v + QuickTime reserved, zeroes [0, 1, encName.length], // frame count (default 1) strToUtf8(encName), // 1byte len + encoder name str 31 - encName.length, // + padding itobe2(colorDepth), // color depth [0xff, 0xff], // reserved ones avcc, // avcc atom, sinf, ), ); } /** * @param {Number} drefIdx * @param {Number} channelsCount * @param {Number} sampleSize * @param {Number} packetSize * @param {Number} sampleRate * @param {Uint8Array} esds - Uint8Array representing the esds atom * @returns {Uint8Array} */ function createMP4ABox( drefIdx: number, channelsCount: number, sampleSize: number, packetSize: number, sampleRate: number, esds: Uint8Array, ): Uint8Array { return createBox( "mp4a", concat( 6, itobe2(drefIdx), 8, itobe2(channelsCount), itobe2(sampleSize), 2, itobe2(packetSize), itobe2(sampleRate), 2, esds, ), ); } /** * @param {Number} drefIdx * @param {Number} channelsCount * @param {Number} sampleSize * @param {Number} packetSize * @param {Number} sampleRate * @param {Uint8Array} esds - Uint8Array representing the esds atom * @param {Uint8Array} [sinf] - Uint8Array representing the sinf atom, * only if name == "enca" * @returns {Uint8Array} */ function createENCABox( drefIdx: number, channelsCount: number, sampleSize: number, packetSize: number, sampleRate: number, esds: Uint8Array, sinf: Uint8Array, ): Uint8Array { return createBox( "enca", concat( 6, itobe2(drefIdx), 8, itobe2(channelsCount), itobe2(sampleSize), 2, itobe2(packetSize), itobe2(sampleRate), 2, esds, sinf, ), ); } /** * @param {Uint8Array} url * @returns {Uint8Array} */ function createDREFBox(url: Uint8Array): Uint8Array { // only one description here... FIXME return createBox("dref", concat(7, [1], url)); } /** * @param {string} majorBrand * @param {Array.<string>} brands * @returns {Uint8Array} */ function createFTYPBox(majorBrand: string, brands: string[]): Uint8Array { const content = concat( ...[strToUtf8(majorBrand), [0, 0, 0, 1]].concat(brands.map(strToUtf8)), ); return createBox("ftyp", content); } /** * @param {string} schemeType - four letters (eg "cenc" for Common Encryption) * @param {Number} schemeVersion - eg 65536 * @returns {Uint8Array} */ function createSCHMBox(schemeType: string, schemeVersion: number): Uint8Array { return createBox("schm", concat(4, strToUtf8(schemeType), itobe4(schemeVersion))); } /** * Create tfdt box from a decoding time. * @param {number} decodeTime * @returns {Uint8Array} */ function createTfdtBox(decodeTime: number): Uint8Array { return createBox("tfdt", concat([1, 0, 0, 0], itobe8(decodeTime))); } /** * @returns {Uint8Array} */ function createVMHDBox(): Uint8Array { const arr = new Uint8Array(12); arr[3] = 1; // QuickTime... return createBox("vmhd", arr); } /** * @param {Number} trackId * @returns {Uint8Array} */ function createTREXBox(trackId: number): Uint8Array { // default sample desc idx = 1 return createBox("trex", concat(4, itobe4(trackId), [0, 0, 0, 1], 12)); } /** * @param {Number} length * @returns {Uint8Array} */ function createFreeBox(length: number): Uint8Array { return createBox("free", new Uint8Array(length - 8)); } /** * @param {Number} stream * @param {string} codecPrivateData - hex string * @returns {Uint8Array} */ function createESDSBox(stream: number, codecPrivateData: string): Uint8Array { return createBox( "esds", concat( 4, [0x03, 0x19], itobe2(stream), [0x00, 0x04, 0x11, 0x40, 0x15], 11, [0x05, 0x02], hexToBytes(codecPrivateData), [0x06, 0x01, 0x02], ), ); } /** * @param {string} dataFormat - four letters (eg "avc1") * @returns {Uint8Array} */ function createFRMABox(dataFormat: string): Uint8Array { return createBox("frma", strToUtf8(dataFormat)); } /** * @param {Uint8Array} sps * @param {Uint8Array} pps * @param {Number} nalLen - NAL Unit length: 1, 2 or 4 bytes * eg: avcc(0x4d, 0x40, 0x0d, 4, 0xe1, "674d400d96560c0efcb80a70505050a0", * 1, "68ef3880") * @returns {Uint8Array} */ function createAVCCBox(sps: Uint8Array, pps: Uint8Array, nalLen: number): Uint8Array { let nal; if (nalLen === 2) { nal = 0x1; } else if (nalLen === 4) { nal = 0x3; } else { nal = 0x0; } // Deduce AVC Profile from SPS const h264Profile = sps[1]; const h264CompatibleProfile = sps[2]; const h264Level = sps[3]; return createBox( "avcC", concat( [1, h264Profile, h264CompatibleProfile, h264Level, (0x3f << 2) | nal, 0xe0 | 1], itobe2(sps.length), sps, [1], itobe2(pps.length), pps, ), ); } /** * @param {string} type - "video"/"audio"/"hint" * @returns {Uint8Array} */ function createHDLRBox(type: "audio" | "video" | "hint"): Uint8Array { let name; let handlerName; switch (type) { case "video": name = "vide"; handlerName = "VideoHandler"; break; case "audio": name = "soun"; handlerName = "SoundHandler"; break; default: name = "hint"; handlerName = ""; break; } return createBox( "hdlr", concat( 8, strToUtf8(name), 12, strToUtf8(handlerName), 1, // handler name is C-style string (0 terminated) ), ); } /** * @param {number} timescale * @returns {Uint8Array} */ function createMDHDBox(timescale: number): Uint8Array { return createBox("mdhd", concat(12, itobe4(timescale), 8)); } /** * @param {Number} timescale * @param {Number} trackId * @returns {Uint8Array} */ function createMVHDBox(timescale: number, trackId: number): Uint8Array { return createBox( "mvhd", concat( 12, itobe4(timescale), 4, [0, 1], 2, // we assume rate = 1; [1, 0], 10, // we assume volume = 100%; [0, 1], 14, // default matrix [0, 1], 14, // default matrix [64, 0, 0, 0], 26, itobe2(trackId + 1), // next trackId (=trackId + 1); ), ); } /** * @param {Uint8Array} mfhd * @param {Uint8Array} tfhd * @param {Uint8Array} tfdt * @param {Uint8Array} trun * @returns {Uint8Array} */ function createSAIOBox( mfhd: Uint8Array, tfhd: Uint8Array, tfdt: Uint8Array, trun: Uint8Array, ): Uint8Array { return createBox( "saio", concat( 4, [0, 0, 0, 1], // ?? itobe4(mfhd.length + tfhd.length + tfdt.length + trun.length + 8 + 8 + 8 + 8), ), ); } /** * @param {Uint8Array} sencContent - including 8 bytes flags and entries count * @returns {Uint8Array} */ function createSAIZBox(sencContent: Uint8Array): Uint8Array { if (sencContent.length === 0) { return createBox("saiz", new Uint8Array(0)); } const flags = be4toi(sencContent, 0); const entries = be4toi(sencContent, 4); const arr = new Uint8Array(entries + 9); arr.set(itobe4(entries), 5); let i = 9; let j = 8; let pairsCnt; let pairsLen; while (j < sencContent.length) { j += 8; // assuming IV is 8 bytes TODO handle 16 bytes IV // if we have extradata for each entry if ((flags & 0x2) === 0x2) { pairsLen = 2; pairsCnt = be2toi(sencContent, j); j += pairsCnt * 6 + 2; } else { pairsCnt = 0; pairsLen = 0; } arr[i] = pairsCnt * 6 + 8 + pairsLen; i++; } return createBox("saiz", arr); } /** * @returns {Uint8Array} */ function createSMHDBox(): Uint8Array { return createBox("smhd", new Uint8Array(8)); } /** * @param {Array.<Uint8Array>} reps - arrays of Uint8Array, * typically [avc1] or [encv, avc1] * @returns {Uint8Array} */ function createSTSDBox(reps: Uint8Array[]): Uint8Array { // only one description here... FIXME const arrBase: Array<Uint8Array | number[] | number> = [7, [reps.length]]; return createBox("stsd", concat(...arrBase.concat(reps))); } /** * @param {Number} width * @param {Number} height * @param {Number} trackId * @returns {Uint8Array} */ function createTKHDBox(width: number, height: number, trackId: number): Uint8Array { return createBox( "tkhd", concat( itobe4(1 + 2 + 4), 8, // we assume track is enabled, // in media and in preview. itobe4(trackId), 20, // we assume trackId = 1; [1, 0, 0, 0], // we assume volume = 100%; [0, 1, 0, 0], 12, // default matrix [0, 1, 0, 0], 12, // default matrix [64, 0, 0, 0], // ?? itobe2(width), 2, // width (TODO handle fixed) itobe2(height), 2, // height (TODO handle fixed) ), ); } /** * @param {Number} algId - eg 1 * @param {Number} ivSize - eg 8 * @param {string} keyId - Hex KID 93789920e8d6520098577df8f2dd5546 * @returns {Uint8Array} */ function createTENCBox(algId: number, ivSize: number, keyId: Uint8Array): Uint8Array { return createBox("tenc", concat(6, [algId, ivSize], keyId)); } export { createAVC1Box, createAVCCBox, createDREFBox, createENCABox, createENCVBox, createESDSBox, createFRMABox, createFTYPBox, createFreeBox, createHDLRBox, createMDHDBox, createMP4ABox, createMVHDBox, createSAIOBox, createSAIZBox, createSCHMBox, createSMHDBox, createSTSDBox, createTENCBox, createTKHDBox, createTREXBox, createTfdtBox, createVMHDBox, };