From 8bbb2365b1d87399ff6d93c0010e4b054dfde966 Mon Sep 17 00:00:00 2001 From: brandonocasey Date: Thu, 10 Dec 2020 16:45:39 -0500 Subject: [PATCH] feat: use vhs-utils --- lib/aac/utils.js | 49 ++--------------- lib/mp4/caption-parser.js | 2 +- lib/mp4/probe.js | 101 +++------------------------------- lib/partial/transmuxer.js | 5 +- lib/tools/mp4-inspector.js | 2 +- lib/utils/stream.js | 90 ++++-------------------------- package-lock.json | 23 ++++++-- package.json | 3 +- scripts/create-test-data.js | 2 +- test/base64-to-uint8-array.js | 17 ------ test/utils/mp4-helpers.js | 4 +- 11 files changed, 48 insertions(+), 250 deletions(-) diff --git a/lib/aac/utils.js b/lib/aac/utils.js index f95758f9..5fbc65c4 100644 --- a/lib/aac/utils.js +++ b/lib/aac/utils.js @@ -24,49 +24,8 @@ var ADTS_SAMPLING_FREQUENCIES = [ 7350 ]; -var parseId3TagSize = function(header, byteIndex) { - var - returnSize = (header[byteIndex + 6] << 21) | - (header[byteIndex + 7] << 14) | - (header[byteIndex + 8] << 7) | - (header[byteIndex + 9]), - flags = header[byteIndex + 5], - footerPresent = (flags & 16) >> 4; - - // if we get a negative returnSize clamp it to 0 - returnSize = returnSize >= 0 ? returnSize : 0; - - if (footerPresent) { - return returnSize + 20; - } - return returnSize + 10; -}; - -var getId3Offset = function(data, offset) { - if (data.length - offset < 10 || - data[offset] !== 'I'.charCodeAt(0) || - data[offset + 1] !== 'D'.charCodeAt(0) || - data[offset + 2] !== '3'.charCodeAt(0)) { - return offset; - } - - offset += parseId3TagSize(data, offset); - - return getId3Offset(data, offset); -}; - - -// TODO: use vhs-utils -var isLikelyAacData = function(data) { - var offset = getId3Offset(data, 0); - - return data.length >= offset + 2 && - (data[offset] & 0xFF) === 0xFF && - (data[offset + 1] & 0xF0) === 0xF0 && - // verify that the 2 layer bits are 0, aka this - // is not mp3 data but aac data. - (data[offset + 1] & 0x16) === 0x10; -}; +var {isLikely} = require('@videojs/vhs-utils/cjs/containers.js'); +var {getId3Size} = require('@videojs/vhs-utils/cjs/id3-helpers.js'); var parseSyncSafeInteger = function(data) { return (data[0] << 21) | @@ -182,8 +141,8 @@ var parseAacTimestamp = function(packet) { }; module.exports = { - isLikelyAacData: isLikelyAacData, - parseId3TagSize: parseId3TagSize, + isLikelyAacData: isLikely.aac, + parseId3TagSize: getId3Size, parseAdtsSize: parseAdtsSize, parseType: parseType, parseSampleRate: parseSampleRate, diff --git a/lib/mp4/caption-parser.js b/lib/mp4/caption-parser.js index 1d452d0c..abb5d358 100644 --- a/lib/mp4/caption-parser.js +++ b/lib/mp4/caption-parser.js @@ -11,7 +11,7 @@ var discardEmulationPreventionBytes = require('../tools/caption-packet-parser').discardEmulationPreventionBytes; var CaptionStream = require('../m2ts/caption-stream').CaptionStream; -var findBox = require('../mp4/find-box.js'); +var {findBox} = require('@videojs/vhs-utils/cjs/mp4-helpers.js'); var parseTfdt = require('../tools/parse-tfdt.js'); var parseTrun = require('../tools/parse-trun.js'); var parseTfhd = require('../tools/parse-tfhd.js'); diff --git a/lib/mp4/probe.js b/lib/mp4/probe.js index 339739c5..9da47a7b 100644 --- a/lib/mp4/probe.js +++ b/lib/mp4/probe.js @@ -10,7 +10,7 @@ var toUnsigned = require('../utils/bin').toUnsigned; var toHexString = require('../utils/bin').toHexString; -var findBox = require('../mp4/find-box.js'); +var {findBox, parseTracks} = require('@videojs/vhs-utils/cjs/mp4-helpers.js'); var parseType = require('../mp4/parse-type.js'); var parseTfhd = require('../tools/parse-tfhd.js'); var parseTrun = require('../tools/parse-trun.js'); @@ -36,6 +36,7 @@ var timescale, startTime, compositionStartTime, getVideoTrackIds, getTracks, * @return {object} a hash of track ids to timescale values or null if * the init segment is malformed. */ +// TODO: remove in a major version timescale = function(init) { var result = {}, @@ -208,6 +209,7 @@ compositionStartTime = function(timescales, fragment) { * * @see ISO-BMFF-12/2015, Section 8.4.3 **/ +// TODO: remove in major version. getVideoTrackIds = function(init) { var traks = findBox(init, ['moov', 'trak']); var videoTrackIds = []; @@ -236,6 +238,7 @@ getVideoTrackIds = function(init) { return videoTrackIds; }; +// TODO: remove in major version getTimescaleFromMediaHeader = function(mdhd) { // mdhd is a FullBox, meaning it will have its own version as the first byte var version = mdhd[0]; @@ -254,100 +257,10 @@ getTimescaleFromMediaHeader = function(mdhd) { * mp4 segment */ getTracks = function(init) { - var traks = findBox(init, ['moov', 'trak']); - var tracks = []; - - traks.forEach(function(trak) { - var track = {}; - var tkhd = findBox(trak, ['tkhd'])[0]; - var view, tkhdVersion; - - // id - if (tkhd) { - view = new DataView(tkhd.buffer, tkhd.byteOffset, tkhd.byteLength); - tkhdVersion = view.getUint8(0); - - track.id = (tkhdVersion === 0) ? view.getUint32(12) : view.getUint32(20); - } - - var hdlr = findBox(trak, ['mdia', 'hdlr'])[0]; - - // type - if (hdlr) { - var type = parseType(hdlr.subarray(8, 12)); - - if (type === 'vide') { - track.type = 'video'; - } else if (type === 'soun') { - track.type = 'audio'; - } else { - track.type = type; - } - } - - - // codec - var stsd = findBox(trak, ['mdia', 'minf', 'stbl', 'stsd'])[0]; - - if (stsd) { - var sampleDescriptions = stsd.subarray(8); - // gives the codec type string - track.codec = parseType(sampleDescriptions.subarray(4, 8)); - - var codecBox = findBox(sampleDescriptions, [track.codec])[0]; - var codecConfig, codecConfigType; - - if (codecBox) { - // https://tools.ietf.org/html/rfc6381#section-3.3 - if ((/^[a-z]vc[1-9]$/i).test(track.codec)) { - // we don't need anything but the "config" parameter of the - // avc1 codecBox - codecConfig = codecBox.subarray(78); - codecConfigType = parseType(codecConfig.subarray(4, 8)); - - if (codecConfigType === 'avcC' && codecConfig.length > 11) { - track.codec += '.'; - - // left padded with zeroes for single digit hex - // profile idc - track.codec += toHexString(codecConfig[9]); - // the byte containing the constraint_set flags - track.codec += toHexString(codecConfig[10]); - // level idc - track.codec += toHexString(codecConfig[11]); - } else { - // TODO: show a warning that we couldn't parse the codec - // and are using the default - track.codec = 'avc1.4d400d'; - } - } else if ((/^mp4[a,v]$/i).test(track.codec)) { - // we do not need anything but the streamDescriptor of the mp4a codecBox - codecConfig = codecBox.subarray(28); - codecConfigType = parseType(codecConfig.subarray(4, 8)); - - if (codecConfigType === 'esds' && codecConfig.length > 20 && codecConfig[19] !== 0) { - track.codec += '.' + toHexString(codecConfig[19]); - // this value is only a single digit - track.codec += '.' + toHexString((codecConfig[20] >>> 2) & 0x3f).replace(/^0/, ''); - } else { - // TODO: show a warning that we couldn't parse the codec - // and are using the default - track.codec = 'mp4a.40.2'; - } - } else { - // flac, opus, etc - track.codec = track.codec.toLowerCase(); - } - } - } - - var mdhd = findBox(trak, ['mdia', 'mdhd'])[0]; - - if (mdhd) { - track.timescale = getTimescaleFromMediaHeader(mdhd); - } + var tracks = parseTracks(init, false); - tracks.push(track); + tracks.forEach(function(track) { + track.id = track.number; }); return tracks; diff --git a/lib/partial/transmuxer.js b/lib/partial/transmuxer.js index 5c8a02e4..90729710 100644 --- a/lib/partial/transmuxer.js +++ b/lib/partial/transmuxer.js @@ -10,10 +10,7 @@ var AacStream = require('../aac/index'); var clock = require('../utils/clock'); var createPipeline = function(object) { - object.prototype = new Stream(); - object.prototype.init.call(object); - - return object; + return Object.assign(new Stream(), object); }; var tsPipeline = function(options) { diff --git a/lib/tools/mp4-inspector.js b/lib/tools/mp4-inspector.js index 08a7d2a8..53c13602 100644 --- a/lib/tools/mp4-inspector.js +++ b/lib/tools/mp4-inspector.js @@ -16,7 +16,7 @@ var return new Date(seconds * 1000 - 2082844800000); }, parseType = require('../mp4/parse-type'), - findBox = require('../mp4/find-box'), + {findBox} = require('@videojs/vhs-utils/cjs/mp4-helpers.js'), nalParse = function(avcStream) { var avcView = new DataView(avcStream.buffer, avcStream.byteOffset, avcStream.byteLength), diff --git a/lib/utils/stream.js b/lib/utils/stream.js index f40cc83e..a579816c 100644 --- a/lib/utils/stream.js +++ b/lib/utils/stream.js @@ -8,78 +8,12 @@ * Objects that inherit from streams should call init in their constructors. */ 'use strict'; +var Stream = require('@videojs/vhs-utils/cjs/stream'); -var Stream = function() { - this.init = function() { - var listeners = {}; - /** - * Add a listener for a specified event type. - * @param type {string} the event name - * @param listener {function} the callback to be invoked when an event of - * the specified type occurs - */ - this.on = function(type, listener) { - if (!listeners[type]) { - listeners[type] = []; - } - listeners[type] = listeners[type].concat(listener); - }; - /** - * Remove a listener for a specified event type. - * @param type {string} the event name - * @param listener {function} a function previously registered for this - * type of event through `on` - */ - this.off = function(type, listener) { - var index; - if (!listeners[type]) { - return false; - } - index = listeners[type].indexOf(listener); - listeners[type] = listeners[type].slice(); - listeners[type].splice(index, 1); - return index > -1; - }; - /** - * Trigger an event of the specified type on this stream. Any additional - * arguments to this function are passed as parameters to event listeners. - * @param type {string} the event name - */ - this.trigger = function(type) { - var callbacks, i, length, args; - callbacks = listeners[type]; - if (!callbacks) { - return; - } - // Slicing the arguments on every invocation of this method - // can add a significant amount of overhead. Avoid the - // intermediate object creation for the common case of a - // single callback argument - if (arguments.length === 2) { - length = callbacks.length; - for (i = 0; i < length; ++i) { - callbacks[i].call(this, arguments[1]); - } - } else { - args = []; - i = arguments.length; - for (i = 1; i < arguments.length; ++i) { - args.push(arguments[i]); - } - length = callbacks.length; - for (i = 0; i < length; ++i) { - callbacks[i].apply(this, args); - } - } - }; - /** - * Destroys the stream and cleans up. - */ - this.dispose = function() { - listeners = {}; - }; - }; -}; +var MuxStream = function() {}; + +MuxStream.prototype = new Stream(); +MuxStream.prototype.init = MuxStream.prototype.constructor; /** * Forwards all `data` events on this stream to the destination stream. The @@ -90,7 +24,7 @@ var Stream = function() { * when the current stream emits a 'done' event * @see http://nodejs.org/api/stream.html#stream_readable_pipe_destination_options */ -Stream.prototype.pipe = function(destination) { +MuxStream.prototype.pipe = function(destination) { this.on('data', function(data) { destination.push(data); }); @@ -118,24 +52,24 @@ Stream.prototype.pipe = function(destination) { // actual work. These are provided by the prototype as a sort of no-op // implementation so that we don't have to check for their existence in the // `pipe` function above. -Stream.prototype.push = function(data) { +MuxStream.prototype.push = function(data) { this.trigger('data', data); }; -Stream.prototype.flush = function(flushSource) { +MuxStream.prototype.flush = function(flushSource) { this.trigger('done', flushSource); }; -Stream.prototype.partialFlush = function(flushSource) { +MuxStream.prototype.partialFlush = function(flushSource) { this.trigger('partialdone', flushSource); }; -Stream.prototype.endTimeline = function(flushSource) { +MuxStream.prototype.endTimeline = function(flushSource) { this.trigger('endedtimeline', flushSource); }; -Stream.prototype.reset = function(flushSource) { +MuxStream.prototype.reset = function(flushSource) { this.trigger('reset', flushSource); }; -module.exports = Stream; +module.exports = MuxStream; diff --git a/package-lock.json b/package-lock.json index 2561d507..2054bb15 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1291,6 +1291,16 @@ "shx": "^0.3.3" } }, + "@videojs/vhs-utils": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@videojs/vhs-utils/-/vhs-utils-3.0.0.tgz", + "integrity": "sha512-HPgiaVB8/g7DooYFQ20uTinq4eNRHmIXGHHttK/Xwyvn19MfIpg9BfMNr9ywCvgHh0IUGrxt6P8AcmMO4xvxIA==", + "requires": { + "@babel/runtime": "^7.12.5", + "global": "^4.4.0", + "url-toolkit": "^2.2.1" + } + }, "JSONStream": { "version": "1.3.5", "resolved": "https://registry.npmjs.org/JSONStream/-/JSONStream-1.3.5.tgz", @@ -3085,8 +3095,7 @@ "dom-walk": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/dom-walk/-/dom-walk-0.1.2.tgz", - "integrity": "sha512-6QvTW9mrGeIegrFXdtQi9pk7O/nSK6lSdXW2eqUspN5LWD7UTji2Fqw5V2YLjBpHEoU9Xl/eUWNpDeZvoyOv2w==", - "dev": true + "integrity": "sha512-6QvTW9mrGeIegrFXdtQi9pk7O/nSK6lSdXW2eqUspN5LWD7UTji2Fqw5V2YLjBpHEoU9Xl/eUWNpDeZvoyOv2w==" }, "domelementtype": { "version": "1.3.1", @@ -4655,7 +4664,6 @@ "version": "4.4.0", "resolved": "https://registry.npmjs.org/global/-/global-4.4.0.tgz", "integrity": "sha512-wv/LAoHdRE3BeTGz53FAamhGlPLhlssK45usmGFThIi4XqnBmjKQ16u+RNbP7WvigRZDxUsM0J3gcQ5yicaL0w==", - "dev": true, "requires": { "min-document": "^2.19.0", "process": "^0.11.10" @@ -6848,7 +6856,6 @@ "version": "2.19.0", "resolved": "https://registry.npmjs.org/min-document/-/min-document-2.19.0.tgz", "integrity": "sha1-e9KC4/WELtKVu3SM3Z8f+iyCRoU=", - "dev": true, "requires": { "dom-walk": "^0.1.0" } @@ -7600,8 +7607,7 @@ "process": { "version": "0.11.10", "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", - "integrity": "sha1-czIwDoQBYb2j5podHZGn1LwW8YI=", - "dev": true + "integrity": "sha1-czIwDoQBYb2j5podHZGn1LwW8YI=" }, "process-nextick-args": { "version": "2.0.1", @@ -9748,6 +9754,11 @@ "dev": true, "optional": true }, + "url-toolkit": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/url-toolkit/-/url-toolkit-2.2.1.tgz", + "integrity": "sha512-8+DzgrtDZYZGhHaAop5WGVghMdCfOLGbhcArsJD0qDll71FXa7EeKxi2hilPIscn2nwMz4PRjML32Sz4JTN0Xw==" + }, "use": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/use/-/use-3.1.1.tgz", diff --git a/package.json b/package.json index 041327a9..2fa14c1b 100644 --- a/package.json +++ b/package.json @@ -91,7 +91,8 @@ "README.md": "doctoc --notitle" }, "dependencies": { - "@babel/runtime": "^7.11.2" + "@babel/runtime": "^7.11.2", + "@videojs/vhs-utils": "^3.0.0" }, "devDependencies": { "@babel/cli": "^7.11.6", diff --git a/scripts/create-test-data.js b/scripts/create-test-data.js index 283f0882..3dbea389 100644 --- a/scripts/create-test-data.js +++ b/scripts/create-test-data.js @@ -18,7 +18,7 @@ const buildSegmentString = function() { }); let segmentsFile = - `import base64ToUint8Array from '${path.join(baseDir, 'test', 'base64-to-uint8-array.js')}';\n` + + 'import base64ToUint8Array from "@videojs/vhs-utils/cjs/decode-b64-to-uint8-array.js";\n' + 'const segments = {\n'; Object.keys(segmentData).forEach((key) => { diff --git a/test/base64-to-uint8-array.js b/test/base64-to-uint8-array.js index 13f166e3..e69de29b 100644 --- a/test/base64-to-uint8-array.js +++ b/test/base64-to-uint8-array.js @@ -1,17 +0,0 @@ -var window = require('global/window'); -// TODO: use vhs-utils here - -var atob = (s) => window.atob ? window.atob(s) : Buffer.from(s, 'base64').toString('binary'); - -var base64ToUint8Array = function(base64) { - var decoded = atob(base64); - var uint8Array = new Uint8Array(new ArrayBuffer(decoded.length)); - - for (var i = 0; i < decoded.length; i++) { - uint8Array[i] = decoded.charCodeAt(i); - } - - return uint8Array; -}; - -module.exports = base64ToUint8Array; diff --git a/test/utils/mp4-helpers.js b/test/utils/mp4-helpers.js index c3468e66..7e435c1c 100644 --- a/test/utils/mp4-helpers.js +++ b/test/utils/mp4-helpers.js @@ -144,7 +144,7 @@ module.exports.sampleMoov = box('stsd', 0x01, // version 1 0x00, 0x00, 0x00, // flags - 0x00, 0x00, 0x00, 0x00, // entry_count + 0x00, 0x00, 0x00, 0x01, // entry_count box('avc1', 0x00, 0x00, 0x00, 0x00, // box content 0x00, 0x00, 0x00, 0x00, // box content @@ -243,7 +243,7 @@ module.exports.sampleMoov = box('stsd', 0x01, // version 1 0x00, 0x00, 0x00, // flags - 0x00, 0x00, 0x00, 0x00, // entry_count + 0x00, 0x00, 0x00, 0x01, // entry_count box('mp4a', 0x00, 0x00, 0x00, 0x00, // box content 0x00, 0x00, 0x00, 0x00, // box content