Files
Zos/Skills/@be/node_modules/hls.js/lib/demux/mp4demuxer.js

323 lines
12 KiB
JavaScript

'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); /**
* MP4 demuxer
*/
//import {logger} from '../utils/logger';
var _events = require('../events');
var _events2 = _interopRequireDefault(_events);
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
var UINT32_MAX = Math.pow(2, 32) - 1;
var MP4Demuxer = function () {
function MP4Demuxer(observer, remuxer) {
_classCallCheck(this, MP4Demuxer);
this.observer = observer;
this.remuxer = remuxer;
}
_createClass(MP4Demuxer, [{
key: 'resetTimeStamp',
value: function resetTimeStamp(initPTS) {
this.initPTS = initPTS;
}
}, {
key: 'resetInitSegment',
value: function resetInitSegment(initSegment, audioCodec, videoCodec, duration) {
//jshint unused:false
if (initSegment && initSegment.byteLength) {
var initData = this.initData = MP4Demuxer.parseInitSegment(initSegment);
var tracks = {};
if (initData.audio) {
tracks.audio = { container: 'audio/mp4', codec: audioCodec, initSegment: duration ? initSegment : null };
}
if (initData.video) {
tracks.video = { container: 'video/mp4', codec: videoCodec, initSegment: duration ? initSegment : null };
}
this.observer.trigger(_events2.default.FRAG_PARSING_INIT_SEGMENT, { tracks: tracks });
} else {
if (audioCodec) {
this.audioCodec = audioCodec;
}
if (videoCodec) {
this.videoCodec = videoCodec;
}
}
}
}, {
key: 'append',
// feed incoming data to the front of the parsing pipeline
value: function append(data, timeOffset, contiguous, accurateTimeOffset) {
var initData = this.initData;
if (!initData) {
this.resetInitSegment(data, this.audioCodec, this.videoCodec);
initData = this.initData;
}
var startDTS = void 0,
initPTS = this.initPTS;
if (initPTS === undefined) {
var _startDTS = MP4Demuxer.getStartDTS(initData, data);
this.initPTS = initPTS = _startDTS - timeOffset;
this.observer.trigger(_events2.default.INIT_PTS_FOUND, { initPTS: initPTS });
}
MP4Demuxer.offsetStartDTS(initData, data, initPTS);
startDTS = MP4Demuxer.getStartDTS(initData, data);
this.remuxer.remux(initData.audio, initData.video, null, null, startDTS, contiguous, accurateTimeOffset, data);
}
}, {
key: 'destroy',
value: function destroy() {}
}], [{
key: 'probe',
value: function probe(data) {
if (data.length >= 8) {
var dataType = MP4Demuxer.bin2str(data.subarray(4, 8));
return ['moof', 'ftyp', 'styp'].indexOf(dataType) >= 0;
}
return false;
}
}, {
key: 'bin2str',
value: function bin2str(buffer) {
return String.fromCharCode.apply(null, buffer);
}
}, {
key: 'readUint32',
value: function readUint32(buffer, offset) {
if (buffer.data) {
offset += buffer.start;
buffer = buffer.data;
}
var val = buffer[offset] << 24 | buffer[offset + 1] << 16 | buffer[offset + 2] << 8 | buffer[offset + 3];
return val < 0 ? 4294967296 + val : val;
}
}, {
key: 'writeUint32',
value: function writeUint32(buffer, offset, value) {
if (buffer.data) {
offset += buffer.start;
buffer = buffer.data;
}
buffer[offset] = value >> 24;
buffer[offset + 1] = value >> 16 & 0xff;
buffer[offset + 2] = value >> 8 & 0xff;
buffer[offset + 3] = value & 0xff;
}
// Find the data for a box specified by its path
}, {
key: 'findBox',
value: function findBox(data, path) {
var results = [],
i,
size,
type,
end,
subresults,
start,
endbox;
if (data.data) {
start = data.start;
end = data.end;
data = data.data;
} else {
start = 0;
end = data.byteLength;
}
if (!path.length) {
// short-circuit the search for empty paths
return null;
}
for (i = start; i < end;) {
size = MP4Demuxer.readUint32(data, i);
type = MP4Demuxer.bin2str(data.subarray(i + 4, i + 8));
endbox = size > 1 ? i + size : end;
if (type === path[0]) {
if (path.length === 1) {
// this is the end of the path and we've found the box we were
// looking for
results.push({ data: data, start: i + 8, end: endbox });
} else {
// recursively search for the next box along the path
subresults = MP4Demuxer.findBox({ data: data, start: i + 8, end: endbox }, path.slice(1));
if (subresults.length) {
results = results.concat(subresults);
}
}
}
i = endbox;
}
// we've finished searching all of data
return results;
}
/**
* Parses an MP4 initialization segment and extracts stream type and
* timescale values for any declared tracks. Timescale values indicate the
* number of clock ticks per second to assume for time-based values
* elsewhere in the MP4.
*
* To determine the start time of an MP4, you need two pieces of
* information: the timescale unit and the earliest base media decode
* time. Multiple timescales can be specified within an MP4 but the
* base media decode time is always expressed in the timescale from
* the media header box for the track:
* ```
* moov > trak > mdia > mdhd.timescale
* moov > trak > mdia > hdlr
* ```
* @param init {Uint8Array} the bytes of the init segment
* @return {object} a hash of track type to timescale values or null if
* the init segment is malformed.
*/
}, {
key: 'parseInitSegment',
value: function parseInitSegment(initSegment) {
var result = [];
var traks = MP4Demuxer.findBox(initSegment, ['moov', 'trak']);
traks.forEach(function (trak) {
var tkhd = MP4Demuxer.findBox(trak, ['tkhd'])[0];
if (tkhd) {
var version = tkhd.data[tkhd.start];
var index = version === 0 ? 12 : 20;
var trackId = MP4Demuxer.readUint32(tkhd, index);
var mdhd = MP4Demuxer.findBox(trak, ['mdia', 'mdhd'])[0];
if (mdhd) {
version = mdhd.data[mdhd.start];
index = version === 0 ? 12 : 20;
var timescale = MP4Demuxer.readUint32(mdhd, index);
var hdlr = MP4Demuxer.findBox(trak, ['mdia', 'hdlr'])[0];
if (hdlr) {
var hdlrType = MP4Demuxer.bin2str(hdlr.data.subarray(hdlr.start + 8, hdlr.start + 12));
var type = { 'soun': 'audio', 'vide': 'video' }[hdlrType];
if (type) {
result[trackId] = { timescale: timescale, type: type };
result[type] = { timescale: timescale, id: trackId };
}
}
}
}
});
return result;
}
/**
* Determine the base media decode start time, in seconds, for an MP4
* fragment. If multiple fragments are specified, the earliest time is
* returned.
*
* The base media decode time can be parsed from track fragment
* metadata:
* ```
* moof > traf > tfdt.baseMediaDecodeTime
* ```
* It requires the timescale value from the mdhd to interpret.
*
* @param timescale {object} a hash of track ids to timescale values.
* @return {number} the earliest base media decode start time for the
* fragment, in seconds
*/
}, {
key: 'getStartDTS',
value: function getStartDTS(initData, fragment) {
var trafs, baseTimes, result;
// we need info from two childrend of each track fragment box
trafs = MP4Demuxer.findBox(fragment, ['moof', 'traf']);
// determine the start times for each track
baseTimes = [].concat.apply([], trafs.map(function (traf) {
return MP4Demuxer.findBox(traf, ['tfhd']).map(function (tfhd) {
var id, scale, baseTime;
// get the track id from the tfhd
id = MP4Demuxer.readUint32(tfhd, 4);
// assume a 90kHz clock if no timescale was specified
scale = initData[id].timescale || 90e3;
// get the base media decode time from the tfdt
baseTime = MP4Demuxer.findBox(traf, ['tfdt']).map(function (tfdt) {
var version, result;
version = tfdt.data[tfdt.start];
result = MP4Demuxer.readUint32(tfdt, 4);
if (version === 1) {
result *= Math.pow(2, 32);
result += MP4Demuxer.readUint32(tfdt, 8);
}
return result;
})[0];
baseTime = baseTime || Infinity;
// convert base time to seconds
return baseTime / scale;
});
}));
// return the minimum
result = Math.min.apply(null, baseTimes);
return isFinite(result) ? result : 0;
}
}, {
key: 'offsetStartDTS',
value: function offsetStartDTS(initData, fragment, timeOffset) {
MP4Demuxer.findBox(fragment, ['moof', 'traf']).map(function (traf) {
return MP4Demuxer.findBox(traf, ['tfhd']).map(function (tfhd) {
// get the track id from the tfhd
var id = MP4Demuxer.readUint32(tfhd, 4);
// assume a 90kHz clock if no timescale was specified
var timescale = initData[id].timescale || 90e3;
// get the base media decode time from the tfdt
MP4Demuxer.findBox(traf, ['tfdt']).map(function (tfdt) {
var version = tfdt.data[tfdt.start];
var baseMediaDecodeTime = MP4Demuxer.readUint32(tfdt, 4);
if (version === 0) {
MP4Demuxer.writeUint32(tfdt, 4, baseMediaDecodeTime - timeOffset * timescale);
} else {
baseMediaDecodeTime *= Math.pow(2, 32);
baseMediaDecodeTime += MP4Demuxer.readUint32(tfdt, 8);
baseMediaDecodeTime -= timeOffset * timescale;
var upper = Math.floor(baseMediaDecodeTime / (UINT32_MAX + 1));
var lower = Math.floor(baseMediaDecodeTime % (UINT32_MAX + 1));
MP4Demuxer.writeUint32(tfdt, 4, upper);
MP4Demuxer.writeUint32(tfdt, 8, lower);
}
});
});
});
}
}]);
return MP4Demuxer;
}();
exports.default = MP4Demuxer;