Skip to content

Commit

Permalink
feat: use vhs-utils
Browse files Browse the repository at this point in the history
  • Loading branch information
brandonocasey committed Dec 18, 2020
1 parent cc6d0b3 commit b1e6a8f
Show file tree
Hide file tree
Showing 11 changed files with 48 additions and 250 deletions.
49 changes: 4 additions & 45 deletions lib/aac/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -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) |
Expand Down Expand Up @@ -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,
Expand Down
2 changes: 1 addition & 1 deletion lib/mp4/caption-parser.js
Original file line number Diff line number Diff line change
Expand Up @@ -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');
Expand Down
101 changes: 7 additions & 94 deletions lib/mp4/probe.js
Original file line number Diff line number Diff line change
Expand Up @@ -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');
Expand All @@ -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 = {},
Expand Down Expand Up @@ -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 = [];
Expand Down Expand Up @@ -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];
Expand All @@ -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;
Expand Down
5 changes: 1 addition & 4 deletions lib/partial/transmuxer.js
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down
2 changes: 1 addition & 1 deletion lib/tools/mp4-inspector.js
Original file line number Diff line number Diff line change
Expand Up @@ -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),
Expand Down
90 changes: 12 additions & 78 deletions lib/utils/stream.js
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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);
});
Expand Down Expand Up @@ -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;

0 comments on commit b1e6a8f

Please sign in to comment.