rx-player
Version:
Canal+ HTML5 Video Player
181 lines (180 loc) • 7.64 kB
JavaScript
"use strict";
var __values = (this && this.__values) || function(o) {
var s = typeof Symbol === "function" && Symbol.iterator, m = s && o[s], i = 0;
if (m) return m.call(o);
if (o && typeof o.length === "number") return {
next: function () {
if (o && i >= o.length) o = void 0;
return { value: o && o[i++], done: !o };
}
};
throw new TypeError(s ? "Object is not iterable." : "Symbol.iterator is not defined.");
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.default = removeDolbyVisionConfigData;
var log_1 = require("../../../log");
var byte_parsing_1 = require("../../../utils/byte_parsing");
var get_box_1 = require("./get_box");
/**
* Replace every dvcC/dvvC/dvwC box in an ISOBMFF segment with FREE boxes.
* Useful to strip Dolby Vision configuration when the decoder cannot handle it.
*
* Unlike PSSH (which sits directly under `moov`), DV config boxes are nested
* deep inside each track: moov > trak > mdia > minf > stbl > stsd > <codec> > dvcC|dvvC|dvwC
* There may be multiple `trak` boxes, so we walk all of them.
*
* @param {Uint8Array} data - the ISOBMFF segment
* @returns {void}
*/
function removeDolbyVisionConfigData(data) {
var moov = (0, get_box_1.getBoxContent)(data, 0x6d6f6f76 /* moov */);
if (moov === null) {
return;
}
// Walk every trak box inside moov
var trakStart = 0;
while (trakStart < moov.length) {
var trakOffsets = void 0;
try {
trakOffsets = (0, get_box_1.getBoxOffsets)(moov.subarray(trakStart), 0x7472616b /* trak */);
}
catch (e) {
var err = e instanceof Error ? e : "";
log_1.default.warn("isobmff", "Error while iterating trak boxes in ISOBMFF", err);
return;
}
if (trakOffsets === null) {
break;
}
var trak = (0, get_box_1.getBoxContent)(moov.subarray(trakStart), 0x7472616b /* trak */);
if (trak !== null) {
removeDvcCFromTrak(trak);
}
// Advance past this trak box to find the next one
trakStart += trakOffsets[2];
}
}
/**
* Walk the stsd box inside a trak and replace any dvcC/dvvC/dvwC boxes
* with free boxes.
* @param {Uint8Array} trak - content of a trak box
*/
function removeDvcCFromTrak(trak) {
var e_1, _a;
var DV_BOX_TYPES = [
0x64766343 /* dvcC */, 0x64767643 /* dvvC */, 0x64767743 /* dvwC */,
];
// Descend: trak > mdia > minf > stbl > stsd
var mdia = (0, get_box_1.getBoxContent)(trak, 0x6d646961 /* mdia */);
if (mdia === null) {
return;
}
var minf = (0, get_box_1.getBoxContent)(mdia, 0x6d696e66 /* minf */);
if (minf === null) {
return;
}
var stbl = (0, get_box_1.getBoxContent)(minf, 0x7374626c /* stbl */);
if (stbl === null) {
return;
}
var stsd = (0, get_box_1.getBoxContent)(stbl, 0x73747364 /* stsd */);
if (stsd === null) {
return;
}
var stsdChildren = stsd.subarray(8);
// Each codec box (avc1, hvc1, av01 …) may contain a dvcC/dvvC/dvwC child.
var codecStart = 0;
while (codecStart < stsdChildren.length) {
if (codecStart + 8 > stsdChildren.length) {
break;
}
var codecBoxSize = (0, byte_parsing_1.be4toi)(stsdChildren, codecStart);
var codecBoxType = (0, byte_parsing_1.be4toi)(stsdChildren, codecStart + 4);
if (codecBoxSize < 8 || codecStart + codecBoxSize > stsdChildren.length) {
break;
}
// The codec box is a VisualSampleEntry (or a wrapper like encv/enca).
// Its layout is: size(4)+type(4)+reserved(6)+data_ref_idx(2) = 16 bytes,
// then codec-specific fixed fields before child boxes begin.
// We scan forward from offset 16 (past the mandatory SampleEntry header)
// looking for the first plausible box boundary, then walk child boxes from
// there.
var codecBox = stsdChildren.subarray(codecStart, codecStart + codecBoxSize);
// Find where children start: scan from offset 16 for a size+fourcc that
// fits entirely within the box.
var childrenStart = findNextChildrenStartOffset(codecBox);
if (childrenStart === null) {
codecStart += codecBoxSize;
continue;
}
var codecChildren = codecBox.subarray(childrenStart);
try {
// Now search for and erase DV boxes
for (var DV_BOX_TYPES_1 = (e_1 = void 0, __values(DV_BOX_TYPES)), DV_BOX_TYPES_1_1 = DV_BOX_TYPES_1.next(); !DV_BOX_TYPES_1_1.done; DV_BOX_TYPES_1_1 = DV_BOX_TYPES_1.next()) {
var dvBoxType = DV_BOX_TYPES_1_1.value;
var dvStart = 0;
while (dvStart < codecChildren.length) {
var dvOffsets = void 0;
try {
dvOffsets = (0, get_box_1.getBoxOffsets)(codecChildren.subarray(dvStart), dvBoxType);
}
catch (e) {
var err = e instanceof Error ? e : "";
log_1.default.warn("isobmff", "Error while patching out dvcC/dvvC/dvwC from ISOBMFF", err);
break;
}
if (dvOffsets === null) {
break;
}
if (log_1.default.hasLevel("DEBUG")) {
log_1.default.debug("isobmff", "Found '".concat((0, get_box_1.boxTypeToFourCC)(dvBoxType), "' inside '").concat((0, get_box_1.boxTypeToFourCC)(codecBoxType), "', overwriting with 'free'"));
}
// Overwrite the 4-byte box type with `free` (0x66726565)
var typeByteOffset = dvStart + dvOffsets[0] + 4;
codecChildren[typeByteOffset] = 0x66; /* f */
codecChildren[typeByteOffset + 1] = 0x72; /* r */
codecChildren[typeByteOffset + 2] = 0x65; /* e */
codecChildren[typeByteOffset + 3] = 0x65; /* e */
// Advance past this box (size of box relative to the current slice)
dvStart += dvOffsets[2] - dvOffsets[0];
}
}
}
catch (e_1_1) { e_1 = { error: e_1_1 }; }
finally {
try {
if (DV_BOX_TYPES_1_1 && !DV_BOX_TYPES_1_1.done && (_a = DV_BOX_TYPES_1.return)) _a.call(DV_BOX_TYPES_1);
}
finally { if (e_1) throw e_1.error; }
}
codecStart += codecBoxSize;
}
}
/**
* Scan a codec box (VisualSampleEntry or wrapper) to find the byte offset
* where child boxes begin. We skip the mandatory SampleEntry header (16 bytes)
* and then look for the first offset whose size+type looks like a valid box
* that fits within the parent.
*/
function findNextChildrenStartOffset(box) {
// SampleEntry header: size(4)+type(4)+reserved(6)+data_ref_idx(2) = 16 bytes minimum.
// Children can only start after that.
for (var off = 16; off + 8 <= box.length; off++) {
var size = (0, byte_parsing_1.be4toi)(box, off);
if (size < 8 || off + size > box.length) {
continue;
}
// Check that the four-byte type is all printable ASCII as a sanity check.
var allPrint = true;
for (var i = off + 4; i < off + 8; i++) {
if (box[i] < 0x20 || box[i] > 0x7e) {
allPrint = false;
break;
}
}
if (allPrint) {
return off;
}
}
return null;
}