Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore: vhs utils update #1036

Merged
merged 10 commits into from
Jan 14, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1,977 changes: 860 additions & 1,117 deletions package-lock.json

Large diffs are not rendered by default.

15 changes: 8 additions & 7 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -56,29 +56,30 @@
],
"dependencies": {
"@babel/runtime": "^7.12.5",
"@videojs/vhs-utils": "^2.3.0",
"aes-decrypter": "3.1.0",
"@videojs/vhs-utils": "^3.0.0",
"aes-decrypter": "3.1.2",
"global": "^4.4.0",
"m3u8-parser": "4.5.0",
"mpd-parser": "0.15.0",
"m3u8-parser": "4.5.2",
"mpd-parser": "0.15.2",
"mux.js": "5.8.0",
"video.js": "^6 || ^7"
},
"devDependencies": {
"@gkatsev/rollup-plugin-bundle-worker": "^1.0.2",
"@rollup/plugin-replace": "^2.3.4",
"@videojs/generator-helpers": "~2.0.1",
"d3": "^3.4.8",
"es5-shim": "^4.5.13",
"es6-shim": "^0.35.5",
"jsdoc": "BrandonOCasey/jsdoc#feat/plugin-from-cli",
"jsdoc": "~3.6.6",
"karma": "^5.2.3",
"lodash": "^4.17.4",
"lodash-compat": "^3.10.0",
"nomnoml": "^0.3.0",
"rollup": "^2.34.2",
"rollup": "^2.36.1",
"shelljs": "^0.8.4",
"sinon": "^8.1.1",
"url-toolkit": "^2.1.3",
"url-toolkit": "^2.2.1",
"videojs-contrib-eme": "^3.8.0",
"videojs-contrib-quality-levels": "^2.0.4",
"videojs-generate-karma-config": "~7.0.0",
Expand Down
8 changes: 8 additions & 0 deletions scripts/rollup.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ const generate = require('videojs-generate-rollup-config');
const worker = require('@gkatsev/rollup-plugin-bundle-worker');
const {terser} = require('rollup-plugin-terser');
const createTestData = require('./create-test-data.js');
const replace = require('@rollup/plugin-replace');

// see https://github.com/videojs/videojs-generate-rollup-config
// for options
Expand Down Expand Up @@ -35,11 +36,18 @@ const options = {
if (defaults.test.indexOf('istanbul') !== -1) {
defaults.test.splice(defaults.test.indexOf('istanbul'), 1);
}
defaults.module.unshift('replace');

return defaults;
},
primedPlugins(defaults) {
return Object.assign(defaults, {
replace: replace({
// single quote replace
"require('@videojs/vhs-utils/es": "require('@videojs/vhs-utils/cjs",
// double quote replace
'require("@videojs/vhs-utils/es': 'require("@videojs/vhs-utils/cjs'
}),
worker: worker(),
uglify: terser({
output: {comments: 'some'},
Expand Down
2 changes: 1 addition & 1 deletion scripts/sources.json
Original file line number Diff line number Diff line change
Expand Up @@ -335,7 +335,7 @@
"features": []
},
{
"name": "(Not Working) ts one valid codec among many invalid",
"name": "ts one valid codec among many invalid",
"uri": "https://d2zihajmogu5jn.cloudfront.net/ts-one-valid-many-invalid-codecs/master.m3u8",
"mimetype": "application/x-mpegurl",
"features": []
Expand Down
2 changes: 1 addition & 1 deletion src/dash-playlist-loader.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import {
addPropertiesToMaster
} from './manifest';
import containerRequest from './util/container-request.js';
import {toUint8} from '@videojs/vhs-utils/dist/byte-helpers';
import {toUint8} from '@videojs/vhs-utils/es/byte-helpers';

const { EventTarget, mergeOptions } = videojs;

Expand Down
80 changes: 36 additions & 44 deletions src/master-playlist-controller.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,8 @@ import {
muxerSupportsCodec,
DEFAULT_AUDIO_CODEC,
DEFAULT_VIDEO_CODEC
} from '@videojs/vhs-utils/dist/codecs.js';
import { codecsForPlaylist } from './util/codecs.js';
} from '@videojs/vhs-utils/es/codecs.js';
import { codecsForPlaylist, unwrapCodecList, codecCount } from './util/codecs.js';
import { createMediaTypes, setupMediaGroups } from './media-groups';
import logger from './util/logger';

Expand Down Expand Up @@ -1562,8 +1562,8 @@ export class MasterPlaylistController extends videojs.EventTarget {
const switchMessages = [];

['video', 'audio'].forEach((type) => {
const newCodec = (parseCodecs(this.sourceUpdater_.codecs[type] || '')[type] || {}).type;
const oldCodec = (parseCodecs(codecs[type] || '')[type] || {}).type;
const newCodec = (parseCodecs(this.sourceUpdater_.codecs[type] || '')[0] || {}).type;
const oldCodec = (parseCodecs(codecs[type] || '')[0] || {}).type;

if (newCodec && oldCodec && newCodec.toLowerCase() !== oldCodec.toLowerCase()) {
switchMessages.push(`"${this.sourceUpdater_.codecs[type]}" -> "${codecs[type]}"`);
Expand Down Expand Up @@ -1642,22 +1642,20 @@ export class MasterPlaylistController extends videojs.EventTarget {
const unsupported = [];

if (codecs.audio && !muxerSupportsCodec(codecs.audio) && !browserSupportsCodec(codecs.audio)) {
variant.excludeUntil = Infinity;
unsupported.push(`audio codec ${codecs.audio}`);
}

if (codecs.video && !muxerSupportsCodec(codecs.video) && !browserSupportsCodec(codecs.video)) {
variant.excludeUntil = Infinity;
unsupported.push(`video codec ${codecs.video}`);
}

if (codecs.text && codecs.text === 'stpp.ttml.im1t') {
variant.excludeUntil = Infinity;
unsupported.push(`text codec ${codecs.text}`);
}

if (unsupported.length) {
this.logger_(`excluding ${variant.id} as codecs ${unsupported.join(', ')} are unsupported`);
variant.excludeUntil = Infinity;
this.logger_(`excluding ${variant.id} for unsupported: ${unsupported.join(', ')}`);
}
});
}
Expand All @@ -1677,67 +1675,61 @@ export class MasterPlaylistController extends videojs.EventTarget {
* @private
*/
excludeIncompatibleVariants_(codecString) {
const codecs = parseCodecs(codecString);
const codecCount = Object.keys(codecs).length;
const ids = [];
const playlists = this.master().playlists;
const codecs = unwrapCodecList(parseCodecs(codecString));
const codecCount_ = codecCount(codecs);
const videoDetails = codecs.video && parseCodecs(codecs.video)[0] || null;
const audioDetails = codecs.audio && parseCodecs(codecs.audio)[0] || null;

Object.keys(playlists).forEach((key) => {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

loop through all playlists for sure

const variant = playlists[key];

this.master().playlists.forEach((variant) => {
// skip variants that are already blacklisted forever
if (variant.excludeUntil === Infinity) {
// check if we already processed this playlist.
// or it if it is already excluded forever.
if (ids.indexOf(variant.id) !== -1 || variant.excludeUntil === Infinity) {
return;
}
/* TODO: Decide whether two codecs should be assumed here.
* Right now, for playlists that don't specify codecs, VHS assumes
* that there are two (one for audio and one for video).
* Although this is often the case, this may lead to broken behavior
* if the playlist only has one codec. It may be better in the future
* to decide at time of segment download how many tracks there are and
* determine the proper codecs. This will come at a cost of potentially
* more bandwidth, but will be a more robust approach than the assumption here.
*/

let variantCodecs = {};
let variantCodecCount = 2;

ids.push(variant.id);
const blacklistReasons = [];

// get codecs from the playlist for this variant
const variantCodecStrings = codecsForPlaylist(this.masterPlaylistLoader_.master, variant);

if (variantCodecStrings.audio || variantCodecStrings.video) {
const variantCodecString = [variantCodecStrings.video, variantCodecStrings.audio]
.filter(Boolean)
.join(',');
const variantCodecs = codecsForPlaylist(this.masterPlaylistLoader_.master, variant);
const variantCodecCount = codecCount(variantCodecs);

variantCodecs = parseCodecs(variantCodecString);
variantCodecCount = Object.keys(variantCodecs).length;
// if no codecs are listed, we cannot determine that this
// variant is incompatible. Wait for mux.js to probe
if (!variantCodecs.audio && !variantCodecs.video) {
return;
}

// TODO: we can support this by removing the
// old media source and creating a new one, but it will take some work.
// The number of streams cannot change
if (variantCodecCount !== codecCount) {
blacklistReasons.push(`codec count "${variantCodecCount}" !== "${codecCount}"`);
variant.excludeUntil = Infinity;
if (variantCodecCount !== codecCount_) {
blacklistReasons.push(`codec count "${variantCodecCount}" !== "${codecCount_}"`);
}

// only exclude playlists by codec change, if codecs cannot switch
// during playback.
if (!this.sourceUpdater_.canChangeType()) {
const variantVideoDetails = variantCodecs.video && parseCodecs(variantCodecs.video)[0] || null;
const variantAudioDetails = variantCodecs.audio && parseCodecs(variantCodecs.audio)[0] || null;

// the video codec cannot change
if (variantCodecs.video && codecs.video &&
variantCodecs.video.type.toLowerCase() !== codecs.video.type.toLowerCase()) {
blacklistReasons.push(`video codec "${variantCodecs.video.type}" !== "${codecs.video.type}"`);
variant.excludeUntil = Infinity;
if (variantVideoDetails && videoDetails && variantVideoDetails.type.toLowerCase() !== videoDetails.type.toLowerCase()) {
blacklistReasons.push(`video codec "${variantVideoDetails.type}" !== "${videoDetails.type}"`);
}

// the audio codec cannot change
if (variantCodecs.audio && codecs.audio &&
variantCodecs.audio.type.toLowerCase() !== codecs.audio.type.toLowerCase()) {
variant.excludeUntil = Infinity;
blacklistReasons.push(`audio codec "${variantCodecs.audio.type}" !== "${codecs.audio.type}"`);
if (variantAudioDetails && audioDetails && variantAudioDetails.type.toLowerCase() !== audioDetails.type.toLowerCase()) {
blacklistReasons.push(`audio codec "${variantAudioDetails.type}" !== "${audioDetails.type}"`);
}
}

if (blacklistReasons.length) {
variant.excludeUntil = Infinity;
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

moved the exclude down here so we don't do it in every if.

this.logger_(`blacklisting ${variant.id}: ${blacklistReasons.join(' && ')}`);
}
});
Expand Down
2 changes: 1 addition & 1 deletion src/media-segment-request.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { segmentXhrHeaders } from './xhr';
import {
detectContainerForBytes,
isLikelyFmp4MediaSegment
} from '@videojs/vhs-utils/dist/containers';
} from '@videojs/vhs-utils/es/containers';

export const REQUEST_ERRORS = {
FAILURE: 2,
Expand Down
2 changes: 1 addition & 1 deletion src/resolve-url.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
* @file resolve-url.js - Handling how URLs are resolved and manipulated
*/

import _resolveUrl from '@videojs/vhs-utils/dist/resolve-url.js';
import _resolveUrl from '@videojs/vhs-utils/es/resolve-url.js';

export const resolveUrl = _resolveUrl;

Expand Down
2 changes: 1 addition & 1 deletion src/source-updater.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import videojs from 'video.js';
import logger from './util/logger';
import noop from './util/noop';
import { bufferIntersection } from './ranges.js';
import {getMimeForCodec} from '@videojs/vhs-utils/dist/codecs.js';
import {getMimeForCodec} from '@videojs/vhs-utils/es/codecs.js';
import window from 'global/window';
import toTitleCase from './util/to-title-case.js';

Expand Down
68 changes: 43 additions & 25 deletions src/util/codecs.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,10 @@ import {
translateLegacyCodec,
parseCodecs,
codecsFromDefault
} from '@videojs/vhs-utils/dist/codecs.js';
} from '@videojs/vhs-utils/es/codecs.js';
import logger from './logger.js';

const logFn = logger('CodecUtils');

/**
* Returns a set of codec strings parsed from the playlist or the default
Expand Down Expand Up @@ -55,6 +58,41 @@ export const isMuxed = (master, media) => {
return false;
};

export const unwrapCodecList = function(codecList) {
const codecs = {};

codecList.forEach(({mediaType, type, details}) => {
codecs[mediaType] = codecs[mediaType] || [];
codecs[mediaType].push(translateLegacyCodec(`${type}${details}`));
});

Object.keys(codecs).forEach(function(mediaType) {
if (codecs[mediaType].length > 1) {
logFn(`multiple ${mediaType} codecs found as attributes: ${codecs[mediaType].join(', ')}. Setting playlist codecs to null so that we wait for mux.js to probe segments for real codecs.`);
codecs[mediaType] = null;
return;
}

codecs[mediaType] = codecs[mediaType][0];
});

return codecs;
};

export const codecCount = function(codecObj) {
let count = 0;

if (codecObj.audio) {
count++;
}

if (codecObj.video) {
count++;
}

return count;
};

/**
* Calculates the codec strings for a working configuration of
* SourceBuffers to play variant streams in a master playlist. If
Expand All @@ -69,7 +107,7 @@ export const isMuxed = (master, media) => {
*/
export const codecsForPlaylist = function(master, media) {
const mediaAttributes = media.attributes || {};
const codecInfo = getCodecs(media) || {};
const codecInfo = unwrapCodecList(getCodecs(media) || []);

// HLS with multiple-audio tracks must always get an audio codec.
// Put another way, there is no way to have a video-only multiple-audio HLS!
Expand All @@ -78,33 +116,13 @@ export const codecsForPlaylist = function(master, media) {
// It is possible for codecs to be specified on the audio media group playlist but
// not on the rendition playlist. This is mostly the case for DASH, where audio and
// video are always separate (and separately specified).
const defaultCodecs = codecsFromDefault(master, mediaAttributes.AUDIO);
const defaultCodecs = unwrapCodecList(codecsFromDefault(master, mediaAttributes.AUDIO) || []);

if (defaultCodecs) {
if (defaultCodecs.audio) {
codecInfo.audio = defaultCodecs.audio;
}

}
}

const codecs = {};

if (codecInfo.video) {
codecs.video = translateLegacyCodec(`${codecInfo.video.type}${codecInfo.video.details}`);
}

if (codecInfo.audio) {
codecs.audio = translateLegacyCodec(`${codecInfo.audio.type}${codecInfo.audio.details}`);
}

if (codecInfo.text) {
codecs.text = codecInfo.text.type;
}

if (codecInfo.unknown) {
codecs.unknown = codecInfo.unknown;
}

return codecs;
return codecInfo;
};

5 changes: 3 additions & 2 deletions src/util/container-request.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import {detectContainerForBytes, getId3Offset} from '@videojs/vhs-utils/dist/containers';
import {stringToBytes, concatTypedArrays} from '@videojs/vhs-utils/dist/byte-helpers';
import {getId3Offset} from '@videojs/vhs-utils/es/id3-helpers';
import {detectContainerForBytes} from '@videojs/vhs-utils/es/containers';
import {stringToBytes, concatTypedArrays} from '@videojs/vhs-utils/es/byte-helpers';
import {callbackWrapper} from '../xhr';

// calls back if the request is readyState DONE
Expand Down
4 changes: 2 additions & 2 deletions src/videojs-http-streaming.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import window from 'global/window';
import PlaylistLoader from './playlist-loader';
import Playlist from './playlist';
import xhrFactory from './xhr';
import { simpleTypeFromSourceType } from '@videojs/vhs-utils/dist/media-types.js';
import { simpleTypeFromSourceType } from '@videojs/vhs-utils/es/media-types.js';
import * as utils from './bin-utils';
import {
getProgramTime,
Expand All @@ -30,7 +30,7 @@ import {
comparePlaylistBandwidth,
comparePlaylistResolution
} from './playlist-selectors.js';
import {isAudioCodec, isVideoCodec, browserSupportsCodec} from '@videojs/vhs-utils/dist/codecs.js';
import {isAudioCodec, isVideoCodec, browserSupportsCodec} from '@videojs/vhs-utils/es/codecs.js';
import logger from './util/logger';
import {SAFE_TIME_DELTA} from './ranges';

Expand Down
Loading