Skip to content

Commit

Permalink
refactor: dedupe code into modules for smaller vhs bundle (videojs#345)
Browse files Browse the repository at this point in the history
  • Loading branch information
brandonocasey authored Jun 16, 2020
1 parent 073e203 commit 040c506
Show file tree
Hide file tree
Showing 10 changed files with 312 additions and 270 deletions.
21 changes: 12 additions & 9 deletions lib/mp4/caption-parser.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,10 @@

var discardEmulationPreventionBytes = require('../tools/caption-packet-parser').discardEmulationPreventionBytes;
var CaptionStream = require('../m2ts/caption-stream').CaptionStream;
var inspect = require('../tools/mp4-inspector');
var findBox = require('../mp4/find-box.js');
var parseTfdt = require('../tools/parse-tfdt.js');
var parseTrun = require('../tools/parse-trun.js');
var parseTfhd = require('../tools/parse-tfhd.js');

/**
* Maps an offset in the mdat to a sample based on the the size of the samples.
Expand Down Expand Up @@ -133,7 +136,7 @@ var parseSamples = function(truns, baseMediaDecodeTime, tfhd) {
// Note: We currently do not parse the sample table as well
// as the trun. It's possible some sources will require this.
// moov > trak > mdia > minf > stbl
var trackRun = inspect.parseTrun(trun);
var trackRun = parseTrun(trun);
var samples = trackRun.samples;

samples.forEach(function(sample) {
Expand Down Expand Up @@ -169,9 +172,9 @@ var parseSamples = function(truns, baseMediaDecodeTime, tfhd) {
**/
var parseCaptionNals = function(segment, videoTrackId) {
// To get the samples
var trafs = inspect.findBox(segment, ['moof', 'traf']);
var trafs = findBox(segment, ['moof', 'traf']);
// To get SEI NAL units
var mdats = inspect.findBox(segment, ['mdat']);
var mdats = findBox(segment, ['mdat']);
var captionNals = {};
var mdatTrafPairs = [];

Expand All @@ -187,14 +190,14 @@ var parseCaptionNals = function(segment, videoTrackId) {
mdatTrafPairs.forEach(function(pair) {
var mdat = pair.mdat;
var traf = pair.traf;
var tfhd = inspect.findBox(traf, ['tfhd']);
var tfhd = findBox(traf, ['tfhd']);
// Exactly 1 tfhd per traf
var headerInfo = inspect.parseTfhd(tfhd[0]);
var headerInfo = parseTfhd(tfhd[0]);
var trackId = headerInfo.trackId;
var tfdt = inspect.findBox(traf, ['tfdt']);
var tfdt = findBox(traf, ['tfdt']);
// Either 0 or 1 tfdt per traf
var baseMediaDecodeTime = (tfdt.length > 0) ? inspect.parseTfdt(tfdt[0]).baseMediaDecodeTime : 0;
var truns = inspect.findBox(traf, ['trun']);
var baseMediaDecodeTime = (tfdt.length > 0) ? parseTfdt(tfdt[0]).baseMediaDecodeTime : 0;
var truns = findBox(traf, ['trun']);
var samples;
var seiNals;

Expand Down
44 changes: 44 additions & 0 deletions lib/mp4/find-box.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
var toUnsigned = require('../utils/bin').toUnsigned;
var parseType = require('./parse-type.js');

var findBox = function(data, path) {
var results = [],
i, size, type, end, subresults;

if (!path.length) {
// short-circuit the search for empty paths
return null;
}

for (i = 0; i < data.byteLength;) {
size = toUnsigned(data[i] << 24 |
data[i + 1] << 16 |
data[i + 2] << 8 |
data[i + 3]);

type = parseType(data.subarray(i + 4, i + 8));

end = size > 1 ? i + size : data.byteLength;

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.subarray(i + 8, end));
} else {
// recursively search for the next box along the path
subresults = findBox(data.subarray(i + 8, end), path.slice(1));
if (subresults.length) {
results = results.concat(subresults);
}
}
}
i = end;
}

// we've finished searching all of data
return results;
};

module.exports = findBox;

11 changes: 11 additions & 0 deletions lib/mp4/parse-type.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
var parseType = function(buffer) {
var result = '';
result += String.fromCharCode(buffer[0]);
result += String.fromCharCode(buffer[1]);
result += String.fromCharCode(buffer[2]);
result += String.fromCharCode(buffer[3]);
return result;
};


module.exports = parseType;
82 changes: 49 additions & 33 deletions lib/mp4/probe.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,11 @@

var toUnsigned = require('../utils/bin').toUnsigned;
var toHexString = require('../utils/bin').toHexString;
var mp4Inspector = require('../tools/mp4-inspector.js');
var findBox = require('../mp4/find-box.js');
var parseType = require('../mp4/parse-type.js');
var parseTfhd = require('../tools/parse-tfhd.js');
var parseTrun = require('../tools/parse-trun.js');
var parseTfdt = require('../tools/parse-tfdt.js');
var timescale, startTime, compositionStartTime, getVideoTrackIds, getTracks,
getTimescaleFromMediaHeader;

Expand All @@ -35,13 +39,13 @@ var timescale, startTime, compositionStartTime, getVideoTrackIds, getTracks,
timescale = function(init) {
var
result = {},
traks = mp4Inspector.findBox(init, ['moov', 'trak']);
traks = findBox(init, ['moov', 'trak']);

// mdhd timescale
return traks.reduce(function(result, trak) {
var tkhd, version, index, id, mdhd;

tkhd = mp4Inspector.findBox(trak, ['tkhd'])[0];
tkhd = findBox(trak, ['tkhd'])[0];
if (!tkhd) {
return null;
}
Expand All @@ -52,7 +56,7 @@ timescale = function(init) {
tkhd[index + 2] << 8 |
tkhd[index + 3]);

mdhd = mp4Inspector.findBox(trak, ['mdia', 'mdhd'])[0];
mdhd = findBox(trak, ['mdia', 'mdhd'])[0];
if (!mdhd) {
return null;
}
Expand Down Expand Up @@ -86,11 +90,11 @@ startTime = function(timescale, fragment) {
var trafs, baseTimes, result;

// we need info from two childrend of each track fragment box
trafs = mp4Inspector.findBox(fragment, ['moof', 'traf']);
trafs = findBox(fragment, ['moof', 'traf']);

// determine the start times for each track
baseTimes = [].concat.apply([], trafs.map(function(traf) {
return mp4Inspector.findBox(traf, ['tfhd']).map(function(tfhd) {
return findBox(traf, ['tfhd']).map(function(tfhd) {
var id, scale, baseTime;

// get the track id from the tfhd
Expand All @@ -102,7 +106,7 @@ startTime = function(timescale, fragment) {
scale = timescale[id] || 90e3;

// get the base media decode time from the tfdt
baseTime = mp4Inspector.findBox(traf, ['tfdt']).map(function(tfdt) {
baseTime = findBox(traf, ['tfdt']).map(function(tfdt) {
var version, result;

version = tfdt[0];
Expand Down Expand Up @@ -151,7 +155,7 @@ startTime = function(timescale, fragment) {
* @return {number} the composition start time for the fragment, in seconds
**/
compositionStartTime = function(timescales, fragment) {
var trafBoxes = mp4Inspector.findBox(fragment, ['moof', 'traf']);
var trafBoxes = findBox(fragment, ['moof', 'traf']);
var baseMediaDecodeTime = 0;
var compositionTimeOffset = 0;
var trackId;
Expand All @@ -160,15 +164,27 @@ compositionStartTime = function(timescales, fragment) {
// The spec states that track run samples contained within a `traf` box are contiguous, but
// it does not explicitly state whether the `traf` boxes themselves are contiguous.
// We will assume that they are, so we only need the first to calculate start time.
var parsedTraf = mp4Inspector.parseTraf(trafBoxes[0]);

for (var i = 0; i < parsedTraf.boxes.length; i++) {
if (parsedTraf.boxes[i].type === 'tfhd') {
trackId = parsedTraf.boxes[i].trackId;
} else if (parsedTraf.boxes[i].type === 'tfdt') {
baseMediaDecodeTime = parsedTraf.boxes[i].baseMediaDecodeTime;
} else if (parsedTraf.boxes[i].type === 'trun' && parsedTraf.boxes[i].samples.length) {
compositionTimeOffset = parsedTraf.boxes[i].samples[0].compositionTimeOffset || 0;
var tfhd = findBox(trafBoxes[0], ['tfhd'])[0];
var trun = findBox(trafBoxes[0], ['trun'])[0];
var tfdt = findBox(trafBoxes[0], ['tfdt'])[0];

if (tfhd) {
var parsedTfhd = parseTfhd(tfhd);

trackId = parsedTfhd.trackId;
}

if (tfdt) {
var parsedTfdt = parseTfdt(tfdt);

baseMediaDecodeTime = parsedTfdt.baseMediaDecodeTime;
}

if (trun) {
var parsedTrun = parseTrun(trun);

if (parsedTrun.samples && parsedTrun.samples.length) {
compositionTimeOffset = parsedTrun.samples[0].compositionTimeOffset || 0;
}
}
}
Expand All @@ -193,15 +209,15 @@ compositionStartTime = function(timescales, fragment) {
* @see ISO-BMFF-12/2015, Section 8.4.3
**/
getVideoTrackIds = function(init) {
var traks = mp4Inspector.findBox(init, ['moov', 'trak']);
var traks = findBox(init, ['moov', 'trak']);
var videoTrackIds = [];

traks.forEach(function(trak) {
var hdlrs = mp4Inspector.findBox(trak, ['mdia', 'hdlr']);
var tkhds = mp4Inspector.findBox(trak, ['tkhd']);
var hdlrs = findBox(trak, ['mdia', 'hdlr']);
var tkhds = findBox(trak, ['tkhd']);

hdlrs.forEach(function(hdlr, index) {
var handlerType = mp4Inspector.parseType(hdlr.subarray(8, 12));
var handlerType = parseType(hdlr.subarray(8, 12));
var tkhd = tkhds[index];
var view;
var version;
Expand Down Expand Up @@ -238,12 +254,12 @@ getTimescaleFromMediaHeader = function(mdhd) {
* mp4 segment
*/
getTracks = function(init) {
var traks = mp4Inspector.findBox(init, ['moov', 'trak']);
var traks = findBox(init, ['moov', 'trak']);
var tracks = [];

traks.forEach(function(trak) {
var track = {};
var tkhd = mp4Inspector.findBox(trak, ['tkhd'])[0];
var tkhd = findBox(trak, ['tkhd'])[0];
var view, tkhdVersion;

// id
Expand All @@ -254,11 +270,11 @@ getTracks = function(init) {
track.id = (tkhdVersion === 0) ? view.getUint32(12) : view.getUint32(20);
}

var hdlr = mp4Inspector.findBox(trak, ['mdia', 'hdlr'])[0];
var hdlr = findBox(trak, ['mdia', 'hdlr'])[0];

// type
if (hdlr) {
var type = mp4Inspector.parseType(hdlr.subarray(8, 12));
var type = parseType(hdlr.subarray(8, 12));

if (type === 'vide') {
track.type = 'video';
Expand All @@ -271,14 +287,14 @@ getTracks = function(init) {


// codec
var stsd = mp4Inspector.findBox(trak, ['mdia', 'minf', 'stbl', 'stsd'])[0];
var stsd = findBox(trak, ['mdia', 'minf', 'stbl', 'stsd'])[0];

if (stsd) {
var sampleDescriptions = stsd.subarray(8);
// gives the codec type string
track.codec = mp4Inspector.parseType(sampleDescriptions.subarray(4, 8));
track.codec = parseType(sampleDescriptions.subarray(4, 8));

var codecBox = mp4Inspector.findBox(sampleDescriptions, [track.codec])[0];
var codecBox = findBox(sampleDescriptions, [track.codec])[0];
var codecConfig, codecConfigType;

if (codecBox) {
Expand All @@ -287,7 +303,7 @@ getTracks = function(init) {
// we don't need anything but the "config" parameter of the
// avc1 codecBox
codecConfig = codecBox.subarray(78);
codecConfigType = mp4Inspector.parseType(codecConfig.subarray(4, 8));
codecConfigType = parseType(codecConfig.subarray(4, 8));

if (codecConfigType === 'avcC' && codecConfig.length > 11) {
track.codec += '.';
Expand All @@ -307,7 +323,7 @@ getTracks = function(init) {
} 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 = mp4Inspector.parseType(codecConfig.subarray(4, 8));
codecConfigType = parseType(codecConfig.subarray(4, 8));

if (codecConfigType === 'esds' && codecConfig.length > 20 && codecConfig[19] !== 0) {
track.codec += '.' + toHexString(codecConfig[19]);
Expand All @@ -324,7 +340,7 @@ getTracks = function(init) {
}
}

var mdhd = mp4Inspector.findBox(trak, ['mdia', 'mdhd'])[0];
var mdhd = findBox(trak, ['mdia', 'mdhd'])[0];

if (mdhd) {
track.timescale = getTimescaleFromMediaHeader(mdhd);
Expand All @@ -338,8 +354,8 @@ getTracks = function(init) {

module.exports = {
// export mp4 inspector's findBox and parseType for backwards compatibility
findBox: mp4Inspector.findBox,
parseType: mp4Inspector.parseType,
findBox: findBox,
parseType: parseType,
timescale: timescale,
startTime: startTime,
compositionStartTime: compositionStartTime,
Expand Down
Loading

0 comments on commit 040c506

Please sign in to comment.