2696 lines
85 KiB
HTML

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport"
content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
<title>play</title>
</head>
<style>
html{
margin:0 0;
padding: 0 0;
}
body{
margin:0 0;
padding: 0 0;
}
</style>
<body>
<video autoplay muted style="width: 100vw;height: 100vh;" poster="images/loader-thumb.jpg" id="player">
</video>
<script>
(function (global, factory) {
typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory(require('stream')) :
typeof define === 'function' && define.amd ? define(['stream'], factory) :
(global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.JMuxer = factory(global.stream));
})(this, (function (stream) { 'use strict';
function _arrayLikeToArray(r, a) {
(null == a || a > r.length) && (a = r.length);
for (var e = 0, n = Array(a); e < a; e++) n[e] = r[e];
return n;
}
function _arrayWithHoles(r) {
if (Array.isArray(r)) return r;
}
function _assertThisInitialized(e) {
if (void 0 === e) throw new ReferenceError("this hasn't been initialised - super() hasn't been called");
return e;
}
function _callSuper(t, o, e) {
return o = _getPrototypeOf(o), _possibleConstructorReturn(t, _isNativeReflectConstruct() ? Reflect.construct(o, e || [], _getPrototypeOf(t).constructor) : o.apply(t, e));
}
function _classCallCheck(a, n) {
if (!(a instanceof n)) throw new TypeError("Cannot call a class as a function");
}
function _defineProperties(e, r) {
for (var t = 0; t < r.length; t++) {
var o = r[t];
o.enumerable = o.enumerable || !1, o.configurable = !0, "value" in o && (o.writable = !0), Object.defineProperty(e, _toPropertyKey(o.key), o);
}
}
function _createClass(e, r, t) {
return r && _defineProperties(e.prototype, r), t && _defineProperties(e, t), Object.defineProperty(e, "prototype", {
writable: !1
}), e;
}
function _createForOfIteratorHelper(r, e) {
var t = "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"];
if (!t) {
if (Array.isArray(r) || (t = _unsupportedIterableToArray(r)) || e && r && "number" == typeof r.length) {
t && (r = t);
var n = 0,
F = function () {};
return {
s: F,
n: function () {
return n >= r.length ? {
done: !0
} : {
done: !1,
value: r[n++]
};
},
e: function (r) {
throw r;
},
f: F
};
}
throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.");
}
var o,
a = !0,
u = !1;
return {
s: function () {
t = t.call(r);
},
n: function () {
var r = t.next();
return a = r.done, r;
},
e: function (r) {
u = !0, o = r;
},
f: function () {
try {
a || null == t.return || t.return();
} finally {
if (u) throw o;
}
}
};
}
function _defineProperty(e, r, t) {
return (r = _toPropertyKey(r)) in e ? Object.defineProperty(e, r, {
value: t,
enumerable: !0,
configurable: !0,
writable: !0
}) : e[r] = t, e;
}
function _getPrototypeOf(t) {
return _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf.bind() : function (t) {
return t.__proto__ || Object.getPrototypeOf(t);
}, _getPrototypeOf(t);
}
function _inherits(t, e) {
if ("function" != typeof e && null !== e) throw new TypeError("Super expression must either be null or a function");
t.prototype = Object.create(e && e.prototype, {
constructor: {
value: t,
writable: !0,
configurable: !0
}
}), Object.defineProperty(t, "prototype", {
writable: !1
}), e && _setPrototypeOf(t, e);
}
function _isNativeReflectConstruct() {
try {
var t = !Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {}));
} catch (t) {}
return (_isNativeReflectConstruct = function () {
return !!t;
})();
}
function _iterableToArrayLimit(r, l) {
var t = null == r ? null : "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"];
if (null != t) {
var e,
n,
i,
u,
a = [],
f = !0,
o = !1;
try {
if (i = (t = t.call(r)).next, 0 === l) {
if (Object(t) !== t) return;
f = !1;
} else for (; !(f = (e = i.call(t)).done) && (a.push(e.value), a.length !== l); f = !0);
} catch (r) {
o = !0, n = r;
} finally {
try {
if (!f && null != t.return && (u = t.return(), Object(u) !== u)) return;
} finally {
if (o) throw n;
}
}
return a;
}
}
function _nonIterableRest() {
throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.");
}
function _possibleConstructorReturn(t, e) {
if (e && ("object" == typeof e || "function" == typeof e)) return e;
if (void 0 !== e) throw new TypeError("Derived constructors may only return object or undefined");
return _assertThisInitialized(t);
}
function _setPrototypeOf(t, e) {
return _setPrototypeOf = Object.setPrototypeOf ? Object.setPrototypeOf.bind() : function (t, e) {
return t.__proto__ = e, t;
}, _setPrototypeOf(t, e);
}
function _slicedToArray(r, e) {
return _arrayWithHoles(r) || _iterableToArrayLimit(r, e) || _unsupportedIterableToArray(r, e) || _nonIterableRest();
}
function _toPrimitive(t, r) {
if ("object" != typeof t || !t) return t;
var e = t[Symbol.toPrimitive];
if (void 0 !== e) {
var i = e.call(t, r || "default");
if ("object" != typeof i) return i;
throw new TypeError("@@toPrimitive must return a primitive value.");
}
return ("string" === r ? String : Number)(t);
}
function _toPropertyKey(t) {
var i = _toPrimitive(t, "string");
return "symbol" == typeof i ? i : i + "";
}
function _typeof(o) {
"@babel/helpers - typeof";
return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) {
return typeof o;
} : function (o) {
return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o;
}, _typeof(o);
}
function _unsupportedIterableToArray(r, a) {
if (r) {
if ("string" == typeof r) return _arrayLikeToArray(r, a);
var t = {}.toString.call(r).slice(8, -1);
return "Object" === t && r.constructor && (t = r.constructor.name), "Map" === t || "Set" === t ? Array.from(r) : "Arguments" === t || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t) ? _arrayLikeToArray(r, a) : void 0;
}
}
var logger;
var errorLogger;
function setLogger() {
/*eslint-disable */
logger = console.log;
errorLogger = console.error;
/*eslint-enable */
}
function log(message) {
if (logger) {
for (var _len = arguments.length, optionalParams = new Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
optionalParams[_key - 1] = arguments[_key];
}
logger.apply(void 0, [message].concat(optionalParams));
}
}
function error(message) {
if (errorLogger) {
for (var _len2 = arguments.length, optionalParams = new Array(_len2 > 1 ? _len2 - 1 : 0), _key2 = 1; _key2 < _len2; _key2++) {
optionalParams[_key2 - 1] = arguments[_key2];
}
errorLogger.apply(void 0, [message].concat(optionalParams));
}
}
var NALU = /*#__PURE__*/function () {
function NALU(data) {
_classCallCheck(this, NALU);
this.payload = data;
this.nri = (this.payload[0] & 0x60) >> 5; // nal_ref_idc
this.ntype = this.payload[0] & 0x1f;
this.isvcl = this.ntype == 1 || this.ntype == 5;
this.stype = ''; // slice_type
this.isfmb = false; // first_mb_in_slice
}
return _createClass(NALU, [{
key: "toString",
value: function toString() {
return "".concat(NALU.type(this), ": NRI: ").concat(this.getNri());
}
}, {
key: "getNri",
value: function getNri() {
return this.nri;
}
}, {
key: "type",
value: function type() {
return this.ntype;
}
}, {
key: "isKeyframe",
value: function isKeyframe() {
return this.ntype === NALU.IDR;
}
}, {
key: "getPayload",
value: function getPayload() {
return this.payload;
}
}, {
key: "getPayloadSize",
value: function getPayloadSize() {
return this.payload.byteLength;
}
}, {
key: "getSize",
value: function getSize() {
return 4 + this.getPayloadSize();
}
}, {
key: "getData",
value: function getData() {
var result = new Uint8Array(this.getSize());
var view = new DataView(result.buffer);
view.setUint32(0, this.getSize() - 4);
result.set(this.getPayload(), 4);
return result;
}
}], [{
key: "NDR",
get: function get() {
return 1;
}
}, {
key: "IDR",
get: function get() {
return 5;
}
}, {
key: "SEI",
get: function get() {
return 6;
}
}, {
key: "SPS",
get: function get() {
return 7;
}
}, {
key: "PPS",
get: function get() {
return 8;
}
}, {
key: "AUD",
get: function get() {
return 9;
}
}, {
key: "TYPES",
get: function get() {
return _defineProperty(_defineProperty(_defineProperty(_defineProperty(_defineProperty(_defineProperty({}, NALU.IDR, 'IDR'), NALU.SEI, 'SEI'), NALU.SPS, 'SPS'), NALU.PPS, 'PPS'), NALU.NDR, 'NDR'), NALU.AUD, 'AUD');
}
}, {
key: "type",
value: function type(nalu) {
if (nalu.ntype in NALU.TYPES) {
return NALU.TYPES[nalu.ntype];
} else {
return 'UNKNOWN';
}
}
}]);
}();
function appendByteArray(buffer1, buffer2) {
var tmp = new Uint8Array((buffer1.byteLength | 0) + (buffer2.byteLength | 0));
tmp.set(buffer1, 0);
tmp.set(buffer2, buffer1.byteLength | 0);
return tmp;
}
function secToTime(sec) {
var seconds,
hours,
minutes,
result = '';
seconds = Math.floor(sec);
hours = parseInt(seconds / 3600, 10) % 24;
minutes = parseInt(seconds / 60, 10) % 60;
seconds = seconds < 0 ? 0 : seconds % 60;
if (hours > 0) {
result += (hours < 10 ? '0' + hours : hours) + ':';
}
result += (minutes < 10 ? '0' + minutes : minutes) + ':' + (seconds < 10 ? '0' + seconds : seconds);
return result;
}
/**
* Parser for exponential Golomb codes, a variable-bitwidth number encoding scheme used by h264.
*/
var ExpGolomb = /*#__PURE__*/function () {
function ExpGolomb(data) {
_classCallCheck(this, ExpGolomb);
this.data = data;
this.index = 0;
this.bitLength = data.byteLength * 8;
}
return _createClass(ExpGolomb, [{
key: "setData",
value: function setData(data) {
this.data = data;
this.index = 0;
this.bitLength = data.byteLength * 8;
}
}, {
key: "bitsAvailable",
get: function get() {
return this.bitLength - this.index;
}
}, {
key: "skipBits",
value: function skipBits(size) {
// console.log(` skip bits: size=${size}, ${this.index}.`);
if (this.bitsAvailable < size) {
//throw new Error('no bytes available');
return false;
}
this.index += size;
}
}, {
key: "readBits",
value: function readBits(size) {
var moveIndex = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
// console.log(` read bits: size=${size}, ${this.index}.`);
var result = this.getBits(size, this.index, moveIndex);
// console.log(` read bits: result=${result}`);
return result;
}
}, {
key: "getBits",
value: function getBits(size, offsetBits) {
var moveIndex = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : true;
if (this.bitsAvailable < size) {
//throw new Error('no bytes available');
return 0;
}
var offset = offsetBits % 8;
var _byte = this.data[offsetBits / 8 | 0] & 0xff >>> offset;
var bits = 8 - offset;
if (bits >= size) {
if (moveIndex) {
this.index += size;
}
return _byte >> bits - size;
} else {
if (moveIndex) {
this.index += bits;
}
var nextSize = size - bits;
return _byte << nextSize | this.getBits(nextSize, offsetBits + bits, moveIndex);
}
}
}, {
key: "skipLZ",
value: function skipLZ() {
var leadingZeroCount;
for (leadingZeroCount = 0; leadingZeroCount < this.bitLength - this.index; ++leadingZeroCount) {
if (this.getBits(1, this.index + leadingZeroCount, false) !== 0) {
// console.log(` skip LZ : size=${leadingZeroCount}, ${this.index}.`);
this.index += leadingZeroCount;
return leadingZeroCount;
}
}
return leadingZeroCount;
}
}, {
key: "skipUEG",
value: function skipUEG() {
this.skipBits(1 + this.skipLZ());
}
}, {
key: "skipEG",
value: function skipEG() {
this.skipBits(1 + this.skipLZ());
}
}, {
key: "readUEG",
value: function readUEG() {
var prefix = this.skipLZ();
return this.readBits(prefix + 1) - 1;
}
}, {
key: "readEG",
value: function readEG() {
var value = this.readUEG();
if (0x01 & value) {
// the number is odd if the low order bit is set
return 1 + value >>> 1; // add 1 to make it even, and divide by 2
} else {
return -1 * (value >>> 1); // divide by two then make it negative
}
}
}, {
key: "readBoolean",
value: function readBoolean() {
return this.readBits(1) === 1;
}
}, {
key: "readUByte",
value: function readUByte() {
var numberOfBytes = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 1;
return this.readBits(numberOfBytes * 8);
}
}, {
key: "readUShort",
value: function readUShort() {
return this.readBits(16);
}
}, {
key: "readUInt",
value: function readUInt() {
return this.readBits(32);
}
}]);
}();
var H264Parser = /*#__PURE__*/function () {
function H264Parser(remuxer) {
_classCallCheck(this, H264Parser);
this.remuxer = remuxer;
this.track = remuxer.mp4track;
}
return _createClass(H264Parser, [{
key: "parseSPS",
value: function parseSPS(sps) {
var config = H264Parser.readSPS(new Uint8Array(sps));
this.track.fps = config.fps;
this.track.width = config.width;
this.track.height = config.height;
this.track.sps = [new Uint8Array(sps)];
this.track.codec = 'avc1.';
var codecarray = new DataView(sps.buffer, sps.byteOffset + 1, 4);
for (var i = 0; i < 3; ++i) {
var h = codecarray.getUint8(i).toString(16);
if (h.length < 2) {
h = '0' + h;
}
this.track.codec += h;
}
}
}, {
key: "parsePPS",
value: function parsePPS(pps) {
this.track.pps = [new Uint8Array(pps)];
}
}, {
key: "parseNAL",
value: function parseNAL(unit) {
if (!unit) return false;
var push = false;
switch (unit.type()) {
case NALU.IDR:
case NALU.NDR:
push = true;
break;
case NALU.PPS:
if (!this.track.pps) {
this.parsePPS(unit.getPayload());
if (!this.remuxer.readyToDecode && this.track.pps && this.track.sps) {
this.remuxer.readyToDecode = true;
}
}
push = true;
break;
case NALU.SPS:
if (!this.track.sps) {
this.parseSPS(unit.getPayload());
if (!this.remuxer.readyToDecode && this.track.pps && this.track.sps) {
this.remuxer.readyToDecode = true;
}
}
push = true;
break;
case NALU.AUD:
log('AUD - ignoing');
break;
case NALU.SEI:
log('SEI - ignoing');
break;
}
return push;
}
}], [{
key: "extractNALu",
value: function extractNALu(buffer) {
var i = 0,
length = buffer.byteLength,
value,
state = 0,
result = [],
left,
lastIndex = 0;
while (i < length) {
value = buffer[i++];
// finding 3 or 4-byte start codes (00 00 01 OR 00 00 00 01)
switch (state) {
case 0:
if (value === 0) {
state = 1;
}
break;
case 1:
if (value === 0) {
state = 2;
} else {
state = 0;
}
break;
case 2:
case 3:
if (value === 0) {
state = 3;
} else if (value === 1 && i < length) {
if (lastIndex != i - state - 1) {
result.push(buffer.subarray(lastIndex, i - state - 1));
}
lastIndex = i;
state = 0;
} else {
state = 0;
}
break;
}
}
if (lastIndex < length) {
left = buffer.subarray(lastIndex, length);
}
return [result, left];
}
/**
* Advance the ExpGolomb decoder past a scaling list. The scaling
* list is optionally transmitted as part of a sequence parameter
* set and is not relevant to transmuxing.
* @param decoder {ExpGolomb} exp golomb decoder
* @param count {number} the number of entries in this scaling list
* @see Recommendation ITU-T H.264, Section 7.3.2.1.1.1
*/
}, {
key: "skipScalingList",
value: function skipScalingList(decoder, count) {
var lastScale = 8,
nextScale = 8,
deltaScale;
for (var j = 0; j < count; j++) {
if (nextScale !== 0) {
deltaScale = decoder.readEG();
nextScale = (lastScale + deltaScale + 256) % 256;
}
lastScale = nextScale === 0 ? lastScale : nextScale;
}
}
/**
* Read a sequence parameter set and return some interesting video
* properties. A sequence parameter set is the H264 metadata that
* describes the properties of upcoming video frames.
* @param data {Uint8Array} the bytes of a sequence parameter set
* @return {object} an object with configuration parsed from the
* sequence parameter set, including the dimensions of the
* associated video frames.
*/
}, {
key: "readSPS",
value: function readSPS(data) {
var decoder = new ExpGolomb(data);
var frameCropLeftOffset = 0,
frameCropRightOffset = 0,
frameCropTopOffset = 0,
frameCropBottomOffset = 0,
sarScale = 1,
profileIdc,
numRefFramesInPicOrderCntCycle,
picWidthInMbsMinus1,
picHeightInMapUnitsMinus1,
frameMbsOnlyFlag,
scalingListCount,
fps = 0;
decoder.readUByte(); // skip NAL header
// rewrite NAL
var rbsp = [],
hdr_bytes = 1,
nal_bytes = data.byteLength;
for (var i = hdr_bytes; i < nal_bytes; i++) {
if (i + 2 < nal_bytes && decoder.readBits(24, false) === 0x000003) {
rbsp.push(decoder.readBits(8));
rbsp.push(decoder.readBits(8));
i += 2;
// emulation_prevention_three_byte
decoder.readBits(8);
} else {
rbsp.push(decoder.readBits(8));
}
}
decoder.setData(new Uint8Array(rbsp));
// end of rewrite data
profileIdc = decoder.readUByte(); // profile_idc
decoder.readBits(5); // constraint_set[0-4]_flag, u(5)
decoder.skipBits(3); // reserved_zero_3bits u(3),
decoder.readUByte(); // level_idc u(8)
decoder.skipUEG(); // seq_parameter_set_id
// some profiles have more optional data we don't need
if (profileIdc === 100 || profileIdc === 110 || profileIdc === 122 || profileIdc === 244 || profileIdc === 44 || profileIdc === 83 || profileIdc === 86 || profileIdc === 118 || profileIdc === 128) {
var chromaFormatIdc = decoder.readUEG();
if (chromaFormatIdc === 3) {
decoder.skipBits(1); // separate_colour_plane_flag
}
decoder.skipUEG(); // bit_depth_luma_minus8
decoder.skipUEG(); // bit_depth_chroma_minus8
decoder.skipBits(1); // qpprime_y_zero_transform_bypass_flag
if (decoder.readBoolean()) {
// seq_scaling_matrix_present_flag
scalingListCount = chromaFormatIdc !== 3 ? 8 : 12;
for (var _i = 0; _i < scalingListCount; ++_i) {
if (decoder.readBoolean()) {
// seq_scaling_list_present_flag[ i ]
if (_i < 6) {
H264Parser.skipScalingList(decoder, 16);
} else {
H264Parser.skipScalingList(decoder, 64);
}
}
}
}
}
decoder.skipUEG(); // log2_max_frame_num_minus4
var picOrderCntType = decoder.readUEG();
if (picOrderCntType === 0) {
decoder.readUEG(); // log2_max_pic_order_cnt_lsb_minus4
} else if (picOrderCntType === 1) {
decoder.skipBits(1); // delta_pic_order_always_zero_flag
decoder.skipEG(); // offset_for_non_ref_pic
decoder.skipEG(); // offset_for_top_to_bottom_field
numRefFramesInPicOrderCntCycle = decoder.readUEG();
for (var _i2 = 0; _i2 < numRefFramesInPicOrderCntCycle; ++_i2) {
decoder.skipEG(); // offset_for_ref_frame[ i ]
}
}
decoder.skipUEG(); // max_num_ref_frames
decoder.skipBits(1); // gaps_in_frame_num_value_allowed_flag
picWidthInMbsMinus1 = decoder.readUEG();
picHeightInMapUnitsMinus1 = decoder.readUEG();
frameMbsOnlyFlag = decoder.readBits(1);
if (frameMbsOnlyFlag === 0) {
decoder.skipBits(1); // mb_adaptive_frame_field_flag
}
decoder.skipBits(1); // direct_8x8_inference_flag
if (decoder.readBoolean()) {
// frame_cropping_flag
frameCropLeftOffset = decoder.readUEG();
frameCropRightOffset = decoder.readUEG();
frameCropTopOffset = decoder.readUEG();
frameCropBottomOffset = decoder.readUEG();
}
if (decoder.readBoolean()) {
// vui_parameters_present_flag
if (decoder.readBoolean()) {
// aspect_ratio_info_present_flag
var sarRatio;
var aspectRatioIdc = decoder.readUByte();
switch (aspectRatioIdc) {
case 1:
sarRatio = [1, 1];
break;
case 2:
sarRatio = [12, 11];
break;
case 3:
sarRatio = [10, 11];
break;
case 4:
sarRatio = [16, 11];
break;
case 5:
sarRatio = [40, 33];
break;
case 6:
sarRatio = [24, 11];
break;
case 7:
sarRatio = [20, 11];
break;
case 8:
sarRatio = [32, 11];
break;
case 9:
sarRatio = [80, 33];
break;
case 10:
sarRatio = [18, 11];
break;
case 11:
sarRatio = [15, 11];
break;
case 12:
sarRatio = [64, 33];
break;
case 13:
sarRatio = [160, 99];
break;
case 14:
sarRatio = [4, 3];
break;
case 15:
sarRatio = [3, 2];
break;
case 16:
sarRatio = [2, 1];
break;
case 255:
{
sarRatio = [decoder.readUByte() << 8 | decoder.readUByte(), decoder.readUByte() << 8 | decoder.readUByte()];
break;
}
}
if (sarRatio && sarRatio[0] > 0 && sarRatio[1] > 0) {
sarScale = sarRatio[0] / sarRatio[1];
}
}
if (decoder.readBoolean()) {
decoder.skipBits(1);
}
if (decoder.readBoolean()) {
decoder.skipBits(4);
if (decoder.readBoolean()) {
decoder.skipBits(24);
}
}
if (decoder.readBoolean()) {
decoder.skipUEG();
decoder.skipUEG();
}
if (decoder.readBoolean()) {
var unitsInTick = decoder.readUInt();
var timeScale = decoder.readUInt();
var fixedFrameRate = decoder.readBoolean();
var frameDuration = timeScale / (2 * unitsInTick);
if (fixedFrameRate) {
fps = frameDuration;
}
}
}
return {
fps: fps > 0 ? fps : undefined,
width: Math.ceil(((picWidthInMbsMinus1 + 1) * 16 - frameCropLeftOffset * 2 - frameCropRightOffset * 2) * sarScale),
height: (2 - frameMbsOnlyFlag) * (picHeightInMapUnitsMinus1 + 1) * 16 - (frameMbsOnlyFlag ? 2 : 4) * (frameCropTopOffset + frameCropBottomOffset)
};
}
}, {
key: "parseHeader",
value: function parseHeader(unit) {
var decoder = new ExpGolomb(unit.getPayload());
// skip NALu type
decoder.readUByte();
unit.isfmb = decoder.readUEG() === 0;
unit.stype = decoder.readUEG();
}
}]);
}();
var AACParser = /*#__PURE__*/function () {
function AACParser(remuxer) {
_classCallCheck(this, AACParser);
this.remuxer = remuxer;
this.track = remuxer.mp4track;
}
return _createClass(AACParser, [{
key: "extractAAC",
value: function extractAAC(buffer) {
var i = 0,
length = buffer.byteLength,
result = [],
headerLength,
frameLength;
if (!AACParser.isAACPattern(buffer)) {
error('Invalid ADTS audio format');
return result;
}
headerLength = AACParser.getHeaderLength(buffer);
if (!this.aacHeader) {
this.aacHeader = buffer.subarray(0, headerLength);
}
while (i < length) {
frameLength = AACParser.getFrameLength(buffer);
result.push(buffer.subarray(headerLength, frameLength));
buffer = buffer.slice(frameLength);
i += frameLength;
}
return result;
}
}, {
key: "setAACConfig",
value: function setAACConfig() {
var objectType,
sampleIndex,
channelCount,
config = new Uint8Array(2),
headerData = this.aacHeader;
if (!headerData) return;
objectType = ((headerData[2] & 0xC0) >>> 6) + 1;
sampleIndex = (headerData[2] & 0x3C) >>> 2;
channelCount = (headerData[2] & 0x01) << 2;
channelCount |= (headerData[3] & 0xC0) >>> 6;
/* refer to http://wiki.multimedia.cx/index.php?title=MPEG-4_Audio#Audio_Specific_Config */
config[0] = objectType << 3;
config[0] |= (sampleIndex & 0x0E) >> 1;
config[1] |= (sampleIndex & 0x01) << 7;
config[1] |= channelCount << 3;
this.track.codec = 'mp4a.40.' + objectType;
this.track.channelCount = channelCount;
this.track.config = config;
this.remuxer.readyToDecode = true;
}
}], [{
key: "samplingRateMap",
get: function get() {
return [96000, 88200, 64000, 48000, 44100, 32000, 24000, 22050, 16000, 12000, 11025, 8000, 7350];
}
}, {
key: "getHeaderLength",
value: function getHeaderLength(data) {
return data[1] & 0x01 ? 7 : 9; // without CRC 7 and with CRC 9 Refs: https://wiki.multimedia.cx/index.php?title=ADTS
}
}, {
key: "getFrameLength",
value: function getFrameLength(data) {
return (data[3] & 0x03) << 11 | data[4] << 3 | (data[5] & 0xE0) >>> 5; // 13 bits length ref: https://wiki.multimedia.cx/index.php?title=ADTS
}
}, {
key: "isAACPattern",
value: function isAACPattern(data) {
return data[0] === 0xff && (data[1] & 0xf0) === 0xf0 && (data[1] & 0x06) === 0x00;
}
}]);
}();
var Event = /*#__PURE__*/function () {
function Event(type) {
_classCallCheck(this, Event);
this.listener = {};
this.type = type | '';
}
return _createClass(Event, [{
key: "on",
value: function on(event, fn) {
if (!this.listener[event]) {
this.listener[event] = [];
}
this.listener[event].push(fn);
return true;
}
}, {
key: "off",
value: function off(event, fn) {
if (this.listener[event]) {
var index = this.listener[event].indexOf(fn);
if (index > -1) {
this.listener[event].splice(index, 1);
}
return true;
}
return false;
}
}, {
key: "offAll",
value: function offAll() {
this.listener = {};
}
}, {
key: "dispatch",
value: function dispatch(event, data) {
if (this.listener[event]) {
this.listener[event].map(function (each) {
each.apply(null, [data]);
});
return true;
}
return false;
}
}]);
}();
/**
* Generate MP4 Box
* taken from: https://github.com/dailymotion/hls.js
*/
var MP4 = /*#__PURE__*/function () {
function MP4() {
_classCallCheck(this, MP4);
}
return _createClass(MP4, null, [{
key: "init",
value: function init() {
MP4.types = {
avc1: [],
// codingname
avcC: [],
btrt: [],
dinf: [],
dref: [],
esds: [],
ftyp: [],
hdlr: [],
mdat: [],
mdhd: [],
mdia: [],
mfhd: [],
minf: [],
moof: [],
moov: [],
mp4a: [],
mvex: [],
mvhd: [],
sdtp: [],
stbl: [],
stco: [],
stsc: [],
stsd: [],
stsz: [],
stts: [],
tfdt: [],
tfhd: [],
traf: [],
trak: [],
trun: [],
trex: [],
tkhd: [],
vmhd: [],
smhd: []
};
var i;
for (i in MP4.types) {
if (MP4.types.hasOwnProperty(i)) {
MP4.types[i] = [i.charCodeAt(0), i.charCodeAt(1), i.charCodeAt(2), i.charCodeAt(3)];
}
}
var videoHdlr = new Uint8Array([0x00,
// version 0
0x00, 0x00, 0x00,
// flags
0x00, 0x00, 0x00, 0x00,
// pre_defined
0x76, 0x69, 0x64, 0x65,
// handler_type: 'vide'
0x00, 0x00, 0x00, 0x00,
// reserved
0x00, 0x00, 0x00, 0x00,
// reserved
0x00, 0x00, 0x00, 0x00,
// reserved
0x56, 0x69, 0x64, 0x65, 0x6f, 0x48, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x72, 0x00 // name: 'VideoHandler'
]);
var audioHdlr = new Uint8Array([0x00,
// version 0
0x00, 0x00, 0x00,
// flags
0x00, 0x00, 0x00, 0x00,
// pre_defined
0x73, 0x6f, 0x75, 0x6e,
// handler_type: 'soun'
0x00, 0x00, 0x00, 0x00,
// reserved
0x00, 0x00, 0x00, 0x00,
// reserved
0x00, 0x00, 0x00, 0x00,
// reserved
0x53, 0x6f, 0x75, 0x6e, 0x64, 0x48, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x72, 0x00 // name: 'SoundHandler'
]);
MP4.HDLR_TYPES = {
video: videoHdlr,
audio: audioHdlr
};
var dref = new Uint8Array([0x00,
// version 0
0x00, 0x00, 0x00,
// flags
0x00, 0x00, 0x00, 0x01,
// entry_count
0x00, 0x00, 0x00, 0x0c,
// entry_size
0x75, 0x72, 0x6c, 0x20,
// 'url' type
0x00,
// version 0
0x00, 0x00, 0x01 // entry_flags
]);
var stco = new Uint8Array([0x00,
// version
0x00, 0x00, 0x00,
// flags
0x00, 0x00, 0x00, 0x00 // entry_count
]);
MP4.STTS = MP4.STSC = MP4.STCO = stco;
MP4.STSZ = new Uint8Array([0x00,
// version
0x00, 0x00, 0x00,
// flags
0x00, 0x00, 0x00, 0x00,
// sample_size
0x00, 0x00, 0x00, 0x00 // sample_count
]);
MP4.VMHD = new Uint8Array([0x00,
// version
0x00, 0x00, 0x01,
// flags
0x00, 0x00,
// graphicsmode
0x00, 0x00, 0x00, 0x00, 0x00, 0x00 // opcolor
]);
MP4.SMHD = new Uint8Array([0x00,
// version
0x00, 0x00, 0x00,
// flags
0x00, 0x00,
// balance
0x00, 0x00 // reserved
]);
MP4.STSD = new Uint8Array([0x00,
// version 0
0x00, 0x00, 0x00,
// flags
0x00, 0x00, 0x00, 0x01]); // entry_count
var majorBrand = new Uint8Array([105, 115, 111, 109]); // isom
var avc1Brand = new Uint8Array([97, 118, 99, 49]); // avc1
var minorVersion = new Uint8Array([0, 0, 0, 1]);
MP4.FTYP = MP4.box(MP4.types.ftyp, majorBrand, minorVersion, majorBrand, avc1Brand);
MP4.DINF = MP4.box(MP4.types.dinf, MP4.box(MP4.types.dref, dref));
}
}, {
key: "box",
value: function box(type) {
for (var _len = arguments.length, payload = new Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
payload[_key - 1] = arguments[_key];
}
var size = 8,
i = payload.length,
len = i,
result;
// calculate the total size we need to allocate
while (i--) {
size += payload[i].byteLength;
}
result = new Uint8Array(size);
result[0] = size >> 24 & 0xff;
result[1] = size >> 16 & 0xff;
result[2] = size >> 8 & 0xff;
result[3] = size & 0xff;
result.set(type, 4);
// copy the payload into the result
for (i = 0, size = 8; i < len; ++i) {
// copy payload[i] array @ offset size
result.set(payload[i], size);
size += payload[i].byteLength;
}
return result;
}
}, {
key: "hdlr",
value: function hdlr(type) {
return MP4.box(MP4.types.hdlr, MP4.HDLR_TYPES[type]);
}
}, {
key: "mdat",
value: function mdat(data) {
return MP4.box(MP4.types.mdat, data);
}
}, {
key: "mdhd",
value: function mdhd(timescale, duration) {
return MP4.box(MP4.types.mdhd, new Uint8Array([0x00,
// version 0
0x00, 0x00, 0x00,
// flags
0x00, 0x00, 0x00, 0x02,
// creation_time
0x00, 0x00, 0x00, 0x03,
// modification_time
timescale >> 24 & 0xFF, timescale >> 16 & 0xFF, timescale >> 8 & 0xFF, timescale & 0xFF,
// timescale
duration >> 24, duration >> 16 & 0xFF, duration >> 8 & 0xFF, duration & 0xFF,
// duration
0x55, 0xc4,
// 'und' language (undetermined)
0x00, 0x00]));
}
}, {
key: "mdia",
value: function mdia(track) {
return MP4.box(MP4.types.mdia, MP4.mdhd(track.timescale, track.duration), MP4.hdlr(track.type), MP4.minf(track));
}
}, {
key: "mfhd",
value: function mfhd(sequenceNumber) {
return MP4.box(MP4.types.mfhd, new Uint8Array([0x00, 0x00, 0x00, 0x00,
// flags
sequenceNumber >> 24, sequenceNumber >> 16 & 0xFF, sequenceNumber >> 8 & 0xFF, sequenceNumber & 0xFF // sequence_number
]));
}
}, {
key: "minf",
value: function minf(track) {
if (track.type === 'audio') {
return MP4.box(MP4.types.minf, MP4.box(MP4.types.smhd, MP4.SMHD), MP4.DINF, MP4.stbl(track));
} else {
return MP4.box(MP4.types.minf, MP4.box(MP4.types.vmhd, MP4.VMHD), MP4.DINF, MP4.stbl(track));
}
}
}, {
key: "moof",
value: function moof(sn, baseMediaDecodeTime, track) {
return MP4.box(MP4.types.moof, MP4.mfhd(sn), MP4.traf(track, baseMediaDecodeTime));
}
/**
* @param tracks... (optional) {array} the tracks associated with this movie
*/
}, {
key: "moov",
value: function moov(tracks, duration, timescale) {
var i = tracks.length,
boxes = [];
while (i--) {
boxes[i] = MP4.trak(tracks[i]);
}
return MP4.box.apply(null, [MP4.types.moov, MP4.mvhd(timescale, duration)].concat(boxes).concat(MP4.mvex(tracks)));
}
}, {
key: "mvex",
value: function mvex(tracks) {
var i = tracks.length,
boxes = [];
while (i--) {
boxes[i] = MP4.trex(tracks[i]);
}
return MP4.box.apply(null, [MP4.types.mvex].concat(boxes));
}
}, {
key: "mvhd",
value: function mvhd(timescale, duration) {
var bytes = new Uint8Array([0x00,
// version 0
0x00, 0x00, 0x00,
// flags
0x00, 0x00, 0x00, 0x01,
// creation_time
0x00, 0x00, 0x00, 0x02,
// modification_time
timescale >> 24 & 0xFF, timescale >> 16 & 0xFF, timescale >> 8 & 0xFF, timescale & 0xFF,
// timescale
duration >> 24 & 0xFF, duration >> 16 & 0xFF, duration >> 8 & 0xFF, duration & 0xFF,
// duration
0x00, 0x01, 0x00, 0x00,
// 1.0 rate
0x01, 0x00,
// 1.0 volume
0x00, 0x00,
// reserved
0x00, 0x00, 0x00, 0x00,
// reserved
0x00, 0x00, 0x00, 0x00,
// reserved
0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00,
// transformation: unity matrix
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
// pre_defined
0xff, 0xff, 0xff, 0xff // next_track_ID
]);
return MP4.box(MP4.types.mvhd, bytes);
}
}, {
key: "sdtp",
value: function sdtp(track) {
var samples = track.samples || [],
bytes = new Uint8Array(4 + samples.length),
flags,
i;
// leave the full box header (4 bytes) all zero
// write the sample table
for (i = 0; i < samples.length; i++) {
flags = samples[i].flags;
bytes[i + 4] = flags.dependsOn << 4 | flags.isDependedOn << 2 | flags.hasRedundancy;
}
return MP4.box(MP4.types.sdtp, bytes);
}
}, {
key: "stbl",
value: function stbl(track) {
return MP4.box(MP4.types.stbl, MP4.stsd(track), MP4.box(MP4.types.stts, MP4.STTS), MP4.box(MP4.types.stsc, MP4.STSC), MP4.box(MP4.types.stsz, MP4.STSZ), MP4.box(MP4.types.stco, MP4.STCO));
}
}, {
key: "avc1",
value: function avc1(track) {
var sps = [],
pps = [],
i,
data,
len;
// assemble the SPSs
for (i = 0; i < track.sps.length; i++) {
data = track.sps[i];
len = data.byteLength;
sps.push(len >>> 8 & 0xFF);
sps.push(len & 0xFF);
sps = sps.concat(Array.prototype.slice.call(data)); // SPS
}
// assemble the PPSs
for (i = 0; i < track.pps.length; i++) {
data = track.pps[i];
len = data.byteLength;
pps.push(len >>> 8 & 0xFF);
pps.push(len & 0xFF);
pps = pps.concat(Array.prototype.slice.call(data));
}
var avcc = MP4.box(MP4.types.avcC, new Uint8Array([0x01,
// version
sps[3],
// profile
sps[4],
// profile compat
sps[5],
// level
0xfc | 3,
// lengthSizeMinusOne, hard-coded to 4 bytes
0xE0 | track.sps.length // 3bit reserved (111) + numOfSequenceParameterSets
].concat(sps).concat([track.pps.length // numOfPictureParameterSets
]).concat(pps))),
// "PPS"
width = track.width,
height = track.height;
// console.log('avcc:' + Hex.hexDump(avcc));
return MP4.box(MP4.types.avc1, new Uint8Array([0x00, 0x00, 0x00,
// reserved
0x00, 0x00, 0x00,
// reserved
0x00, 0x01,
// data_reference_index
0x00, 0x00,
// pre_defined
0x00, 0x00,
// reserved
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
// pre_defined
width >> 8 & 0xFF, width & 0xff,
// width
height >> 8 & 0xFF, height & 0xff,
// height
0x00, 0x48, 0x00, 0x00,
// horizresolution
0x00, 0x48, 0x00, 0x00,
// vertresolution
0x00, 0x00, 0x00, 0x00,
// reserved
0x00, 0x01,
// frame_count
0x12, 0x62, 0x69, 0x6E, 0x65,
// binelpro.ru
0x6C, 0x70, 0x72, 0x6F, 0x2E, 0x72, 0x75, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
// compressorname
0x00, 0x18,
// depth = 24
0x11, 0x11]),
// pre_defined = -1
avcc, MP4.box(MP4.types.btrt, new Uint8Array([0x00, 0x1c, 0x9c, 0x80,
// bufferSizeDB
0x00, 0x2d, 0xc6, 0xc0,
// maxBitrate
0x00, 0x2d, 0xc6, 0xc0])) // avgBitrate
);
}
}, {
key: "esds",
value: function esds(track) {
var configlen = track.config.byteLength;
var data = new Uint8Array(26 + configlen + 3);
data.set([0x00,
// version 0
0x00, 0x00, 0x00,
// flags
0x03,
// descriptor_type
0x17 + configlen,
// length
0x00, 0x01,
// es_id
0x00,
// stream_priority
0x04,
// descriptor_type
0x0f + configlen,
// length
0x40,
// codec : mpeg4_audio
0x15,
// stream_type
0x00, 0x00, 0x00,
// buffer_size
0x00, 0x00, 0x00, 0x00,
// maxBitrate
0x00, 0x00, 0x00, 0x00,
// avgBitrate
0x05,
// descriptor_type
configlen]);
data.set(track.config, 26);
data.set([0x06, 0x01, 0x02], 26 + configlen);
// return new Uint8Array([
// 0x00, // version 0
// 0x00, 0x00, 0x00, // flags
//
// 0x03, // descriptor_type
// 0x17+configlen, // length
// 0x00, 0x01, //es_id
// 0x00, // stream_priority
//
// 0x04, // descriptor_type
// 0x0f+configlen, // length
// 0x40, //codec : mpeg4_audio
// 0x15, // stream_type
// 0x00, 0x00, 0x00, // buffer_size
// 0x00, 0x00, 0x00, 0x00, // maxBitrate
// 0x00, 0x00, 0x00, 0x00, // avgBitrate
//
// 0x05 // descriptor_type
// ].concat([configlen]).concat(track.config).concat([0x06, 0x01, 0x02])); // GASpecificConfig)); // length + audio config descriptor
return data;
}
}, {
key: "mp4a",
value: function mp4a(track) {
var audiosamplerate = track.audiosamplerate;
return MP4.box(MP4.types.mp4a, new Uint8Array([0x00, 0x00, 0x00,
// reserved
0x00, 0x00, 0x00,
// reserved
0x00, 0x01,
// data_reference_index
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
// reserved
0x00, track.channelCount,
// channelcount
0x00, 0x10,
// sampleSize:16bits
0x00, 0x00,
// pre_defined
0x00, 0x00,
// reserved2
audiosamplerate >> 8 & 0xFF, audiosamplerate & 0xff,
//
0x00, 0x00]), MP4.box(MP4.types.esds, MP4.esds(track)));
}
}, {
key: "stsd",
value: function stsd(track) {
if (track.type === 'audio') {
return MP4.box(MP4.types.stsd, MP4.STSD, MP4.mp4a(track));
} else {
return MP4.box(MP4.types.stsd, MP4.STSD, MP4.avc1(track));
}
}
}, {
key: "tkhd",
value: function tkhd(track) {
var id = track.id,
duration = track.duration,
width = track.width,
height = track.height,
volume = track.volume;
return MP4.box(MP4.types.tkhd, new Uint8Array([0x00,
// version 0
0x00, 0x00, 0x07,
// flags
0x00, 0x00, 0x00, 0x00,
// creation_time
0x00, 0x00, 0x00, 0x00,
// modification_time
id >> 24 & 0xFF, id >> 16 & 0xFF, id >> 8 & 0xFF, id & 0xFF,
// track_ID
0x00, 0x00, 0x00, 0x00,
// reserved
duration >> 24, duration >> 16 & 0xFF, duration >> 8 & 0xFF, duration & 0xFF,
// duration
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
// reserved
0x00, 0x00,
// layer
0x00, 0x00,
// alternate_group
volume >> 0 & 0xff, volume % 1 * 10 >> 0 & 0xff,
// track volume // FIXME
0x00, 0x00,
// reserved
0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00,
// transformation: unity matrix
width >> 8 & 0xFF, width & 0xFF, 0x00, 0x00,
// width
height >> 8 & 0xFF, height & 0xFF, 0x00, 0x00 // height
]));
}
}, {
key: "traf",
value: function traf(track, baseMediaDecodeTime) {
var sampleDependencyTable = MP4.sdtp(track),
id = track.id;
return MP4.box(MP4.types.traf, MP4.box(MP4.types.tfhd, new Uint8Array([0x00,
// version 0
0x00, 0x00, 0x00,
// flags
id >> 24, id >> 16 & 0XFF, id >> 8 & 0XFF, id & 0xFF // track_ID
])), MP4.box(MP4.types.tfdt, new Uint8Array([0x00,
// version 0
0x00, 0x00, 0x00,
// flags
baseMediaDecodeTime >> 24, baseMediaDecodeTime >> 16 & 0XFF, baseMediaDecodeTime >> 8 & 0XFF, baseMediaDecodeTime & 0xFF // baseMediaDecodeTime
])), MP4.trun(track, sampleDependencyTable.length + 16 +
// tfhd
16 +
// tfdt
8 +
// traf header
16 +
// mfhd
8 +
// moof header
8),
// mdat header
sampleDependencyTable);
}
/**
* Generate a track box.
* @param track {object} a track definition
* @return {Uint8Array} the track box
*/
}, {
key: "trak",
value: function trak(track) {
track.duration = track.duration || 0xffffffff;
return MP4.box(MP4.types.trak, MP4.tkhd(track), MP4.mdia(track));
}
}, {
key: "trex",
value: function trex(track) {
var id = track.id;
return MP4.box(MP4.types.trex, new Uint8Array([0x00,
// version 0
0x00, 0x00, 0x00,
// flags
id >> 24, id >> 16 & 0XFF, id >> 8 & 0XFF, id & 0xFF,
// track_ID
0x00, 0x00, 0x00, 0x01,
// default_sample_description_index
0x00, 0x00, 0x00, 0x00,
// default_sample_duration
0x00, 0x00, 0x00, 0x00,
// default_sample_size
0x00, 0x01, 0x00, 0x01 // default_sample_flags
]));
}
}, {
key: "trun",
value: function trun(track, offset) {
var samples = track.samples || [],
len = samples.length,
arraylen = 12 + 16 * len,
array = new Uint8Array(arraylen),
i,
sample,
duration,
size,
flags,
cts;
offset += 8 + arraylen;
array.set([0x00,
// version 0
0x00, 0x0f, 0x01,
// flags
len >>> 24 & 0xFF, len >>> 16 & 0xFF, len >>> 8 & 0xFF, len & 0xFF,
// sample_count
offset >>> 24 & 0xFF, offset >>> 16 & 0xFF, offset >>> 8 & 0xFF, offset & 0xFF // data_offset
], 0);
for (i = 0; i < len; i++) {
sample = samples[i];
duration = sample.duration;
size = sample.size;
flags = sample.flags;
cts = sample.cts;
array.set([duration >>> 24 & 0xFF, duration >>> 16 & 0xFF, duration >>> 8 & 0xFF, duration & 0xFF,
// sample_duration
size >>> 24 & 0xFF, size >>> 16 & 0xFF, size >>> 8 & 0xFF, size & 0xFF,
// sample_size
flags.isLeading << 2 | flags.dependsOn, flags.isDependedOn << 6 | flags.hasRedundancy << 4 | flags.paddingValue << 1 | flags.isNonSync, flags.degradPrio & 0xF0 << 8, flags.degradPrio & 0x0F,
// sample_flags
cts >>> 24 & 0xFF, cts >>> 16 & 0xFF, cts >>> 8 & 0xFF, cts & 0xFF // sample_composition_time_offset
], 12 + 16 * i);
}
return MP4.box(MP4.types.trun, array);
}
}, {
key: "initSegment",
value: function initSegment(tracks, duration, timescale) {
if (!MP4.types) {
MP4.init();
}
var movie = MP4.moov(tracks, duration, timescale),
result;
result = new Uint8Array(MP4.FTYP.byteLength + movie.byteLength);
result.set(MP4.FTYP);
result.set(movie, MP4.FTYP.byteLength);
return result;
}
}]);
}();
var track_id = 1;
var BaseRemuxer = /*#__PURE__*/function () {
function BaseRemuxer() {
_classCallCheck(this, BaseRemuxer);
}
return _createClass(BaseRemuxer, [{
key: "flush",
value: function flush() {
this.mp4track.len = 0;
this.mp4track.samples = [];
}
}, {
key: "isReady",
value: function isReady() {
if (!this.readyToDecode || !this.samples.length) return null;
return true;
}
}], [{
key: "getTrackID",
value: function getTrackID() {
return track_id++;
}
}]);
}();
var AACRemuxer = /*#__PURE__*/function (_BaseRemuxer) {
function AACRemuxer(timescale) {
var _this;
_classCallCheck(this, AACRemuxer);
_this = _callSuper(this, AACRemuxer);
_this.readyToDecode = false;
_this.nextDts = 0;
_this.dts = 0;
_this.mp4track = {
id: BaseRemuxer.getTrackID(),
type: 'audio',
channelCount: 0,
len: 0,
fragmented: true,
timescale: timescale,
duration: timescale,
samples: [],
config: '',
codec: ''
};
_this.samples = [];
_this.aac = new AACParser(_this);
return _this;
}
_inherits(AACRemuxer, _BaseRemuxer);
return _createClass(AACRemuxer, [{
key: "resetTrack",
value: function resetTrack() {
this.readyToDecode = false;
this.mp4track.codec = '';
this.mp4track.channelCount = '';
this.mp4track.config = '';
this.mp4track.timescale = this.timescale;
this.nextDts = 0;
this.dts = 0;
}
}, {
key: "remux",
value: function remux(frames) {
if (frames.length > 0) {
for (var i = 0; i < frames.length; i++) {
var frame = frames[i];
var payload = frame.units;
var size = payload.byteLength;
this.samples.push({
units: payload,
size: size,
duration: frame.duration
});
this.mp4track.len += size;
if (!this.readyToDecode) {
this.aac.setAACConfig();
}
}
}
}
}, {
key: "getPayload",
value: function getPayload() {
if (!this.isReady()) {
return null;
}
var payload = new Uint8Array(this.mp4track.len);
var offset = 0;
var samples = this.mp4track.samples;
var mp4Sample, duration;
this.dts = this.nextDts;
while (this.samples.length) {
var sample = this.samples.shift();
sample.units;
duration = sample.duration;
if (duration <= 0) {
log("remuxer: invalid sample duration at DTS: ".concat(this.nextDts, " :").concat(duration));
this.mp4track.len -= sample.size;
continue;
}
this.nextDts += duration;
mp4Sample = {
size: sample.size,
duration: duration,
cts: 0,
flags: {
isLeading: 0,
isDependedOn: 0,
hasRedundancy: 0,
degradPrio: 0,
dependsOn: 1
}
};
payload.set(sample.units, offset);
offset += sample.size;
samples.push(mp4Sample);
}
if (!samples.length) return null;
return new Uint8Array(payload.buffer, 0, this.mp4track.len);
}
}, {
key: "getAacParser",
value: function getAacParser() {
return this.aac;
}
}]);
}(BaseRemuxer);
var H264Remuxer = /*#__PURE__*/function (_BaseRemuxer) {
function H264Remuxer(timescale) {
var _this;
_classCallCheck(this, H264Remuxer);
_this = _callSuper(this, H264Remuxer);
_this.readyToDecode = false;
_this.nextDts = 0;
_this.dts = 0;
_this.mp4track = {
id: BaseRemuxer.getTrackID(),
type: 'video',
len: 0,
fragmented: true,
sps: '',
pps: '',
fps: 30,
width: 0,
height: 0,
timescale: timescale,
duration: timescale,
samples: []
};
_this.samples = [];
_this.h264 = new H264Parser(_this);
return _this;
}
_inherits(H264Remuxer, _BaseRemuxer);
return _createClass(H264Remuxer, [{
key: "resetTrack",
value: function resetTrack() {
this.readyToDecode = false;
this.mp4track.sps = '';
this.mp4track.pps = '';
this.nextDts = 0;
this.dts = 0;
}
}, {
key: "remux",
value: function remux(frames) {
var _iterator = _createForOfIteratorHelper(frames),
_step;
try {
for (_iterator.s(); !(_step = _iterator.n()).done;) {
var frame = _step.value;
var units = [];
var size = 0;
var _iterator2 = _createForOfIteratorHelper(frame.units),
_step2;
try {
for (_iterator2.s(); !(_step2 = _iterator2.n()).done;) {
var unit = _step2.value;
if (this.h264.parseNAL(unit)) {
units.push(unit);
size += unit.getSize();
}
}
} catch (err) {
_iterator2.e(err);
} finally {
_iterator2.f();
}
if (units.length > 0 && this.readyToDecode) {
this.mp4track.len += size;
this.samples.push({
units: units,
size: size,
keyFrame: frame.keyFrame,
duration: frame.duration,
compositionTimeOffset: frame.compositionTimeOffset
});
}
}
} catch (err) {
_iterator.e(err);
} finally {
_iterator.f();
}
}
}, {
key: "getPayload",
value: function getPayload() {
if (!this.isReady()) {
return null;
}
var payload = new Uint8Array(this.mp4track.len);
var offset = 0;
var samples = this.mp4track.samples;
var mp4Sample, duration;
this.dts = this.nextDts;
while (this.samples.length) {
var sample = this.samples.shift(),
units = sample.units;
duration = sample.duration;
if (duration <= 0) {
log("remuxer: invalid sample duration at DTS: ".concat(this.nextDts, " :").concat(duration));
this.mp4track.len -= sample.size;
continue;
}
this.nextDts += duration;
mp4Sample = {
size: sample.size,
duration: duration,
cts: sample.compositionTimeOffset || 0,
flags: {
isLeading: 0,
isDependedOn: 0,
hasRedundancy: 0,
degradPrio: 0,
isNonSync: sample.keyFrame ? 0 : 1,
dependsOn: sample.keyFrame ? 2 : 1
}
};
var _iterator3 = _createForOfIteratorHelper(units),
_step3;
try {
for (_iterator3.s(); !(_step3 = _iterator3.n()).done;) {
var unit = _step3.value;
payload.set(unit.getData(), offset);
offset += unit.getSize();
}
} catch (err) {
_iterator3.e(err);
} finally {
_iterator3.f();
}
samples.push(mp4Sample);
}
if (!samples.length) return null;
return new Uint8Array(payload.buffer, 0, this.mp4track.len);
}
}]);
}(BaseRemuxer);
var RemuxController = /*#__PURE__*/function (_Event) {
function RemuxController(env) {
var _this;
_classCallCheck(this, RemuxController);
_this = _callSuper(this, RemuxController, ['remuxer']);
_this.initialized = false;
_this.trackTypes = [];
_this.tracks = {};
_this.seq = 1;
_this.env = env;
_this.timescale = 1000;
_this.mediaDuration = 0;
_this.aacParser = null;
return _this;
}
_inherits(RemuxController, _Event);
return _createClass(RemuxController, [{
key: "addTrack",
value: function addTrack(type) {
if (type === 'video' || type === 'both') {
this.tracks.video = new H264Remuxer(this.timescale);
this.trackTypes.push('video');
}
if (type === 'audio' || type === 'both') {
var aacRemuxer = new AACRemuxer(this.timescale);
this.aacParser = aacRemuxer.getAacParser();
this.tracks.audio = aacRemuxer;
this.trackTypes.push('audio');
}
}
}, {
key: "reset",
value: function reset() {
var _iterator = _createForOfIteratorHelper(this.trackTypes),
_step;
try {
for (_iterator.s(); !(_step = _iterator.n()).done;) {
var type = _step.value;
this.tracks[type].resetTrack();
}
} catch (err) {
_iterator.e(err);
} finally {
_iterator.f();
}
this.initialized = false;
}
}, {
key: "destroy",
value: function destroy() {
this.tracks = {};
this.offAll();
}
}, {
key: "flush",
value: function flush() {
if (!this.initialized) {
if (this.isReady()) {
this.dispatch('ready');
this.initSegment();
this.initialized = true;
this.flush();
}
} else {
var _iterator2 = _createForOfIteratorHelper(this.trackTypes),
_step2;
try {
for (_iterator2.s(); !(_step2 = _iterator2.n()).done;) {
var type = _step2.value;
var track = this.tracks[type];
var pay = track.getPayload();
if (pay && pay.byteLength) {
var moof = MP4.moof(this.seq, track.dts, track.mp4track);
var mdat = MP4.mdat(pay);
var payload = appendByteArray(moof, mdat);
var data = {
type: type,
payload: payload,
dts: track.dts
};
if (type === 'video') {
data.fps = track.mp4track.fps;
}
this.dispatch('buffer', data);
var duration = secToTime(track.dts / this.timescale);
log("put segment (".concat(type, "): dts: ").concat(track.dts, " frames: ").concat(track.mp4track.samples.length, " second: ").concat(duration));
track.flush();
this.seq++;
}
}
} catch (err) {
_iterator2.e(err);
} finally {
_iterator2.f();
}
}
}
}, {
key: "initSegment",
value: function initSegment() {
var tracks = [];
var _iterator3 = _createForOfIteratorHelper(this.trackTypes),
_step3;
try {
for (_iterator3.s(); !(_step3 = _iterator3.n()).done;) {
var type = _step3.value;
var track = this.tracks[type];
if (this.env == 'browser') {
var _data = {
type: type,
payload: MP4.initSegment([track.mp4track], this.mediaDuration, this.timescale)
};
this.dispatch('buffer', _data);
} else {
tracks.push(track.mp4track);
}
}
} catch (err) {
_iterator3.e(err);
} finally {
_iterator3.f();
}
if (this.env == 'node') {
var data = {
type: 'all',
payload: MP4.initSegment(tracks, this.mediaDuration, this.timescale)
};
this.dispatch('buffer', data);
}
log('Initial segment generated.');
}
}, {
key: "isReady",
value: function isReady() {
var _iterator4 = _createForOfIteratorHelper(this.trackTypes),
_step4;
try {
for (_iterator4.s(); !(_step4 = _iterator4.n()).done;) {
var type = _step4.value;
if (!this.tracks[type].readyToDecode || !this.tracks[type].samples.length) return false;
}
} catch (err) {
_iterator4.e(err);
} finally {
_iterator4.f();
}
return true;
}
}, {
key: "remux",
value: function remux(data) {
var _iterator5 = _createForOfIteratorHelper(this.trackTypes),
_step5;
try {
for (_iterator5.s(); !(_step5 = _iterator5.n()).done;) {
var type = _step5.value;
var frames = data[type];
if (type === 'audio' && this.tracks.video && !this.tracks.video.readyToDecode) continue; /* if video is present, don't add audio until video get ready */
if (frames.length > 0) {
this.tracks[type].remux(frames);
}
}
} catch (err) {
_iterator5.e(err);
} finally {
_iterator5.f();
}
this.flush();
}
}]);
}(Event);
var BufferController = /*#__PURE__*/function (_Event) {
function BufferController(sourceBuffer, type) {
var _this;
_classCallCheck(this, BufferController);
_this = _callSuper(this, BufferController, ['buffer']);
_this.type = type;
_this.queue = new Uint8Array();
_this.cleaning = false;
_this.pendingCleaning = 0;
_this.cleanOffset = 30;
_this.cleanRanges = [];
_this.sourceBuffer = sourceBuffer;
_this.sourceBuffer.addEventListener('updateend', function () {
if (_this.pendingCleaning > 0) {
_this.initCleanup(_this.pendingCleaning);
_this.pendingCleaning = 0;
}
_this.cleaning = false;
if (_this.cleanRanges.length) {
_this.doCleanup();
return;
}
});
_this.sourceBuffer.addEventListener('error', function () {
_this.dispatch('error', {
type: _this.type,
name: 'buffer',
error: 'buffer error'
});
});
return _this;
}
_inherits(BufferController, _Event);
return _createClass(BufferController, [{
key: "destroy",
value: function destroy() {
this.queue = null;
this.sourceBuffer = null;
this.offAll();
}
}, {
key: "doCleanup",
value: function doCleanup() {
if (!this.cleanRanges.length) {
this.cleaning = false;
return;
}
var range = this.cleanRanges.shift();
log("".concat(this.type, " remove range [").concat(range[0], " - ").concat(range[1], ")"));
this.cleaning = true;
this.sourceBuffer.remove(range[0], range[1]);
}
}, {
key: "initCleanup",
value: function initCleanup(cleanMaxLimit) {
try {
if (this.sourceBuffer.updating) {
this.pendingCleaning = cleanMaxLimit;
return;
}
if (this.sourceBuffer.buffered && this.sourceBuffer.buffered.length && !this.cleaning) {
for (var i = 0; i < this.sourceBuffer.buffered.length; ++i) {
var start = this.sourceBuffer.buffered.start(i);
var end = this.sourceBuffer.buffered.end(i);
if (cleanMaxLimit - start > this.cleanOffset) {
end = cleanMaxLimit - this.cleanOffset;
if (start < end) {
this.cleanRanges.push([start, end]);
}
}
}
this.doCleanup();
}
} catch (e) {
error("Error occured while cleaning ".concat(this.type, " buffer - ").concat(e.name, ": ").concat(e.message));
}
}
}, {
key: "doAppend",
value: function doAppend() {
if (!this.queue.length) return;
if (!this.sourceBuffer || this.sourceBuffer.updating) return;
try {
this.sourceBuffer.appendBuffer(this.queue);
this.queue = new Uint8Array();
} catch (e) {
var name = 'unexpectedError';
if (e.name === 'QuotaExceededError') {
log("".concat(this.type, " buffer quota full"));
name = 'QuotaExceeded';
} else {
error("Error occured while appending ".concat(this.type, " buffer - ").concat(e.name, ": ").concat(e.message));
name = 'InvalidStateError';
}
this.dispatch('error', {
type: this.type,
name: name,
error: 'buffer error'
});
}
}
}, {
key: "feed",
value: function feed(data) {
this.queue = appendByteArray(this.queue, data);
}
}]);
}(Event);
var JMuxer = /*#__PURE__*/function (_Event) {
function JMuxer(options) {
var _this;
_classCallCheck(this, JMuxer);
_this = _callSuper(this, JMuxer, ['jmuxer']);
_this.isReset = false;
var defaults = {
node: '',
mode: 'both',
// both, audio, video
flushingTime: 500,
maxDelay: 500,
clearBuffer: true,
fps: 30,
readFpsFromTrack: false,
// set true to fetch fps value from NALu
debug: false,
onReady: function onReady() {},
// function called when MSE is ready to accept frames
onData: function onData() {},
// function called when data is ready to be sent
onError: function onError() {},
// function called when jmuxer encounters any buffer related errors
onMissingVideoFrames: function onMissingVideoFrames() {},
// function called when jmuxer encounters any missing video frames
onMissingAudioFrames: function onMissingAudioFrames() {} // function called when jmuxer encounters any missing audio frames
};
_this.options = Object.assign({}, defaults, options);
_this.env = (typeof process === "undefined" ? "undefined" : _typeof(process)) === 'object' && typeof window === 'undefined' ? 'node' : 'browser';
if (_this.options.debug) {
setLogger();
}
if (!_this.options.fps) {
_this.options.fps = 30;
}
_this.frameDuration = 1000 / _this.options.fps | 0;
_this.remuxController = new RemuxController(_this.env);
_this.remuxController.addTrack(_this.options.mode);
_this.initData();
/* events callback */
_this.remuxController.on('buffer', _this.onBuffer.bind(_this));
if (_this.env == 'browser') {
_this.remuxController.on('ready', _this.createBuffer.bind(_this));
_this.initBrowser();
}
return _this;
}
_inherits(JMuxer, _Event);
return _createClass(JMuxer, [{
key: "initData",
value: function initData() {
this.lastCleaningTime = Date.now();
this.kfPosition = [];
this.kfCounter = 0;
this.pendingUnits = {};
this.remainingData = new Uint8Array();
this.startInterval();
}
}, {
key: "initBrowser",
value: function initBrowser() {
if (typeof this.options.node === 'string' && this.options.node == '') {
error('no video element were found to render, provide a valid video element');
}
this.node = typeof this.options.node === 'string' ? document.getElementById(this.options.node) : this.options.node;
this.mseReady = false;
this.setupMSE();
}
}, {
key: "createStream",
value: function createStream() {
var feed = this.feed.bind(this);
var destroy = this.destroy.bind(this);
this.stream = new stream.Duplex({
writableObjectMode: true,
read: function read(size) {},
write: function write(data, encoding, callback) {
feed(data);
callback();
},
"final": function final(callback) {
destroy();
callback();
}
});
return this.stream;
}
}, {
key: "setupMSE",
value: function setupMSE() {
window.MediaSource = window.MediaSource || window.WebKitMediaSource || window.ManagedMediaSource;
if (!window.MediaSource) {
throw 'Oops! Browser does not support Media Source Extension or Managed Media Source (IOS 17+).';
}
this.isMSESupported = !!window.MediaSource;
this.mediaSource = new window.MediaSource();
this.url = URL.createObjectURL(this.mediaSource);
if (window.MediaSource === window.ManagedMediaSource) {
try {
this.node.removeAttribute('src');
// ManagedMediaSource will not open without disableRemotePlayback set to false or source alternatives
this.node.disableRemotePlayback = true;
var source = document.createElement('source');
source.type = 'video/mp4';
source.src = this.url;
this.node.appendChild(source);
this.node.load();
} catch (error) {
this.node.src = this.url;
}
} else {
this.node.src = this.url;
}
this.mseEnded = false;
this.mediaSource.addEventListener('sourceopen', this.onMSEOpen.bind(this));
this.mediaSource.addEventListener('sourceclose', this.onMSEClose.bind(this));
this.mediaSource.addEventListener('webkitsourceopen', this.onMSEOpen.bind(this));
this.mediaSource.addEventListener('webkitsourceclose', this.onMSEClose.bind(this));
}
}, {
key: "endMSE",
value: function endMSE() {
if (!this.mseEnded) {
try {
this.mseEnded = true;
this.mediaSource.endOfStream();
} catch (e) {
error('mediasource is not available to end');
}
}
}
}, {
key: "feed",
value: function feed(data) {
var remux = false,
slices,
left,
duration,
chunks = {
video: [],
audio: []
};
if (!data || !this.remuxController) return;
duration = data.duration ? parseInt(data.duration) : 0;
if (data.video) {
data.video = appendByteArray(this.remainingData, data.video);
var _H264Parser$extractNA = H264Parser.extractNALu(data.video);
var _H264Parser$extractNA2 = _slicedToArray(_H264Parser$extractNA, 2);
slices = _H264Parser$extractNA2[0];
left = _H264Parser$extractNA2[1];
this.remainingData = left || new Uint8Array();
if (slices.length > 0) {
chunks.video = this.getVideoFrames(slices, duration, data.compositionTimeOffset);
remux = true;
} else {
error('Failed to extract any NAL units from video data:', left);
if (typeof this.options.onMissingVideoFrames === 'function') {
this.options.onMissingVideoFrames.call(null, data);
}
return;
}
}
if (data.audio) {
slices = this.remuxController.aacParser.extractAAC(data.audio);
if (slices.length > 0) {
chunks.audio = this.getAudioFrames(slices, duration);
remux = true;
} else {
error('Failed to extract audio data from:', data.audio);
if (typeof this.options.onMissingAudioFrames === 'function') {
this.options.onMissingAudioFrames.call(null, data);
}
return;
}
}
if (!remux) {
error('Input object must have video and/or audio property. Make sure it is a valid typed array');
return;
}
this.remuxController.remux(chunks);
}
}, {
key: "getVideoFrames",
value: function getVideoFrames(nalus, duration, compositionTimeOffset) {
var _this2 = this;
var units = [],
frames = [],
fd = 0,
tt = 0,
keyFrame = false,
vcl = false;
if (this.pendingUnits.units) {
units = this.pendingUnits.units;
vcl = this.pendingUnits.vcl;
keyFrame = this.pendingUnits.keyFrame;
this.pendingUnits = {};
}
var _iterator = _createForOfIteratorHelper(nalus),
_step;
try {
for (_iterator.s(); !(_step = _iterator.n()).done;) {
var nalu = _step.value;
var unit = new NALU(nalu);
if (unit.type() === NALU.IDR || unit.type() === NALU.NDR) {
H264Parser.parseHeader(unit);
}
if (units.length && vcl && (unit.isfmb || !unit.isvcl)) {
frames.push({
units: units,
keyFrame: keyFrame
});
units = [];
keyFrame = false;
vcl = false;
}
units.push(unit);
keyFrame = keyFrame || unit.isKeyframe();
vcl = vcl || unit.isvcl;
}
} catch (err) {
_iterator.e(err);
} finally {
_iterator.f();
}
if (units.length) {
// lets keep indecisive nalus as pending in case of fixed fps
if (!duration) {
this.pendingUnits = {
units: units,
keyFrame: keyFrame,
vcl: vcl
};
} else if (vcl) {
frames.push({
units: units,
keyFrame: keyFrame
});
} else {
var last = frames.length - 1;
if (last >= 0) {
frames[last].units = frames[last].units.concat(units);
}
}
}
fd = duration ? duration / frames.length | 0 : this.frameDuration;
tt = duration ? duration - fd * frames.length : 0;
frames.map(function (frame) {
frame.duration = fd;
frame.compositionTimeOffset = compositionTimeOffset;
if (tt > 0) {
frame.duration++;
tt--;
}
_this2.kfCounter++;
if (frame.keyFrame && _this2.options.clearBuffer) {
_this2.kfPosition.push(_this2.kfCounter * fd / 1000);
}
});
log("jmuxer: No. of frames of the last chunk: ".concat(frames.length));
return frames;
}
}, {
key: "getAudioFrames",
value: function getAudioFrames(aacFrames, duration) {
var frames = [],
fd = 0,
tt = 0;
var _iterator2 = _createForOfIteratorHelper(aacFrames),
_step2;
try {
for (_iterator2.s(); !(_step2 = _iterator2.n()).done;) {
var units = _step2.value;
frames.push({
units: units
});
}
} catch (err) {
_iterator2.e(err);
} finally {
_iterator2.f();
}
fd = duration ? duration / frames.length | 0 : this.frameDuration;
tt = duration ? duration - fd * frames.length : 0;
frames.map(function (frame) {
frame.duration = fd;
if (tt > 0) {
frame.duration++;
tt--;
}
});
return frames;
}
}, {
key: "destroy",
value: function destroy() {
this.stopInterval();
if (this.stream) {
this.remuxController.flush();
this.stream.push(null);
this.stream = null;
}
if (this.remuxController) {
this.remuxController.destroy();
this.remuxController = null;
}
if (this.bufferControllers) {
for (var type in this.bufferControllers) {
this.bufferControllers[type].destroy();
}
this.bufferControllers = null;
this.endMSE();
}
this.node = false;
this.mseReady = false;
this.videoStarted = false;
this.mediaSource = null;
}
}, {
key: "reset",
value: function reset() {
this.stopInterval();
this.isReset = true;
this.node.pause();
if (this.remuxController) {
this.remuxController.reset();
}
if (this.bufferControllers) {
for (var type in this.bufferControllers) {
this.bufferControllers[type].destroy();
}
this.bufferControllers = null;
this.endMSE();
}
this.initData();
if (this.env == 'browser') {
this.initBrowser();
}
log('JMuxer was reset');
}
}, {
key: "createBuffer",
value: function createBuffer() {
if (!this.mseReady || !this.remuxController || !this.remuxController.isReady() || this.bufferControllers) return;
this.bufferControllers = {};
for (var type in this.remuxController.tracks) {
var track = this.remuxController.tracks[type];
if (!JMuxer.isSupported("".concat(type, "/mp4; codecs=\"").concat(track.mp4track.codec, "\""))) {
error('Browser does not support codec');
return false;
}
var sb = this.mediaSource.addSourceBuffer("".concat(type, "/mp4; codecs=\"").concat(track.mp4track.codec, "\""));
this.bufferControllers[type] = new BufferController(sb, type);
this.bufferControllers[type].on('error', this.onBufferError.bind(this));
}
}
}, {
key: "startInterval",
value: function startInterval() {
var _this3 = this;
this.interval = setInterval(function () {
if (_this3.options.flushingTime) {
_this3.applyAndClearBuffer();
} else if (_this3.bufferControllers) {
_this3.cancelDelay();
}
}, this.options.flushingTime || 1000);
}
}, {
key: "stopInterval",
value: function stopInterval() {
if (this.interval) {
clearInterval(this.interval);
}
}
}, {
key: "cancelDelay",
value: function cancelDelay() {
if (this.node.buffered && this.node.buffered.length > 0 && !this.node.seeking) {
var end = this.node.buffered.end(0);
if (end - this.node.currentTime > this.options.maxDelay / 1000) {
console.log('delay');
this.node.currentTime = end - 0.001;
}
}
}
}, {
key: "releaseBuffer",
value: function releaseBuffer() {
for (var type in this.bufferControllers) {
this.bufferControllers[type].doAppend();
}
}
}, {
key: "applyAndClearBuffer",
value: function applyAndClearBuffer() {
if (this.bufferControllers) {
this.releaseBuffer();
this.clearBuffer();
}
}
}, {
key: "getSafeClearOffsetOfBuffer",
value: function getSafeClearOffsetOfBuffer(offset) {
var maxLimit = this.options.mode === 'audio' && offset || 0,
adjacentOffset;
for (var i = 0; i < this.kfPosition.length; i++) {
if (this.kfPosition[i] >= offset) {
break;
}
adjacentOffset = this.kfPosition[i];
}
if (adjacentOffset) {
this.kfPosition = this.kfPosition.filter(function (kfDelimiter) {
if (kfDelimiter < adjacentOffset) {
maxLimit = kfDelimiter;
}
return kfDelimiter >= adjacentOffset;
});
}
return maxLimit;
}
}, {
key: "clearBuffer",
value: function clearBuffer() {
if (this.options.clearBuffer && Date.now() - this.lastCleaningTime > 10000) {
for (var type in this.bufferControllers) {
var cleanMaxLimit = this.getSafeClearOffsetOfBuffer(this.node.currentTime);
this.bufferControllers[type].initCleanup(cleanMaxLimit);
}
this.lastCleaningTime = Date.now();
}
}
}, {
key: "onBuffer",
value: function onBuffer(data) {
if (this.options.readFpsFromTrack && typeof data.fps !== 'undefined' && this.options.fps != data.fps) {
this.options.fps = data.fps;
this.frameDuration = Math.ceil(1000 / data.fps);
log("JMuxer changed FPS to ".concat(data.fps, " from track data"));
}
if (this.env == 'browser') {
if (this.bufferControllers && this.bufferControllers[data.type]) {
this.bufferControllers[data.type].feed(data.payload);
}
} else if (this.stream) {
this.stream.push(data.payload);
}
if (this.onData) {
this.onData(data.payload);
}
if (this.options.flushingTime === 0) {
this.applyAndClearBuffer();
}
}
/* Events on MSE */
}, {
key: "onMSEOpen",
value: function onMSEOpen() {
this.mseReady = true;
URL.revokeObjectURL(this.url);
// this.createBuffer();
if (typeof this.options.onReady === 'function') {
this.options.onReady.call(null, this.isReset);
}
}
}, {
key: "onMSEClose",
value: function onMSEClose() {
this.mseReady = false;
this.videoStarted = false;
}
}, {
key: "onBufferError",
value: function onBufferError(data) {
if (data.name == 'QuotaExceeded') {
log("JMuxer cleaning ".concat(data.type, " buffer due to QuotaExceeded error"));
this.bufferControllers[data.type].initCleanup(this.node.currentTime);
return;
} else if (data.name == 'InvalidStateError') {
log('JMuxer is reseting due to InvalidStateError');
this.reset();
} else {
this.endMSE();
}
if (typeof this.options.onError === 'function') {
this.options.onError.call(null, data);
}
}
}], [{
key: "isSupported",
value: function isSupported(codec) {
return window.MediaSource && window.MediaSource.isTypeSupported(codec);
}
}]);
}(Event);
return JMuxer;
}));
</script>
<script>
if (typeof JMuxer === 'undefined') {
console.error("JMuxer is not defined. Check if jmuxer.min.js is loaded correctly.");
} else {
console.log("JMuxer loaded successfully.");
}
let jmuxer;
window.onload = function() {
try {
jmuxer = new JMuxer({
node: 'player',
mode: 'video',
flushingTime: 1000,
fps: 22,
debug: true,
});
console.log("JMuxer initialized.");
} catch (e) {
console.error("Error initializing JMuxer:", e);
}
};
// Feed data from Flutter
function feedDataFromFlutter(data) {
if (typeof jmuxer !== 'undefined') {
try {
jmuxer.feed({
video: new Uint8Array(data),
});
} catch (e) {
console.error('Error feeding data to JMuxer:', e);
}
} else {
console.error('JMuxer instance is not initialized.');
}
}
// Optional: notify Flutter
function notifyFlutter(message) {
if (window.Flutter) {
window.Flutter.postMessage(message);
} else {
console.log("Flutter interface not found. Message: " + message);
}
}
</script>
</body>
</html>