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

Feature capabilities filter #3512

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 change: 1 addition & 0 deletions index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,7 @@ declare namespace dashjs {
useAppendWindow?: boolean,
manifestUpdateRetryInterval?: number;
stallThreshold?: number;
filterUnsupportedEssentialProperties?: true
liveCatchup?: {
minDrift?: number;
maxDrift?: number;
Expand Down
3 changes: 3 additions & 0 deletions src/core/Settings.js
Original file line number Diff line number Diff line change
Expand Up @@ -298,6 +298,8 @@ import {HTTPRequest} from '../streaming/vo/metrics/HTTPRequest';
* downloading the next manifest once the minimumUpdatePeriod time has
* @property {number} [stallThreshold=0.5]
* Stall threshold used in BufferController.js to determine whether a track should still be changed and which buffer range to prune.
* @property {boolean} [filterUnsupportedEssentialProperties=true]
* Enable to filter all the AdaptationSets and Representations which contain an unsupported <EssentialProperty> element
* @property {module:Settings~CachingInfoSettings} [lastBitrateCachingInfo={enabled: true, ttl: 360000}]
* Set to false if you would like to disable the last known bit rate from being stored during playback and used
* to set the initial bit rate for subsequent playback within the expiration window.
Expand Down Expand Up @@ -447,6 +449,7 @@ function Settings() {
useAppendWindow: true,
manifestUpdateRetryInterval: 100,
stallThreshold: 0.5,
filterUnsupportedEssentialProperties: true,
liveCatchup: {
minDrift: 0.02,
maxDrift: 0,
Expand Down
29 changes: 29 additions & 0 deletions src/dash/DashAdapter.js
Original file line number Diff line number Diff line change
Expand Up @@ -378,6 +378,28 @@ function DashAdapter() {
return realAdaptation;
}

/**
* Return all EssentialProperties of a Representation
* @param {object} representation
* @return {array}
*/
function getEssentialPropertiesForRepresentation(representation) {
try {
return dashManifestModel.getEssentialPropertiesForRepresentation(representation);
} catch (e) {
return [];
}
}

/**
* Returns the period by index
* @param {number} index
* @return {object}
*/
function getRealPeriodByIndex(index) {
return dashManifestModel.getRealPeriodForIndex(index, voPeriods[0].mpd.manifest);
}

/**
* Returns all voRepresentations for a given mediaInfo
* @param {object} mediaInfo
Expand Down Expand Up @@ -725,6 +747,10 @@ function DashAdapter() {
return null;
}

function getIsTypeOf(adaptation, type) {
return dashManifestModel.getIsTypeOf(adaptation, type);
}

function reset() {
voPeriods = [];
voAdaptations = {};
Expand Down Expand Up @@ -940,6 +966,8 @@ function DashAdapter() {
getAllMediaInfoForType: getAllMediaInfoForType,
getAdaptationForType: getAdaptationForType,
getRealAdaptation: getRealAdaptation,
getRealPeriodByIndex,
getEssentialPropertiesForRepresentation,
getVoRepresentations: getVoRepresentations,
getEventsFor: getEventsFor,
getEvent: getEvent,
Expand All @@ -950,6 +978,7 @@ function DashAdapter() {
getUTCTimingSources: getUTCTimingSources,
getSuggestedPresentationDelay: getSuggestedPresentationDelay,
getAvailabilityStartTime: getAvailabilityStartTime,
getIsTypeOf,
getIsDynamic: getIsDynamic,
getDuration: getDuration,
getRegularPeriods: getRegularPeriods,
Expand Down
16 changes: 16 additions & 0 deletions src/dash/models/DashManifestModel.js
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,19 @@ function DashManifestModel() {
return manifest && manifest.Period_asArray && isInteger(periodIndex) ? manifest.Period_asArray[periodIndex] ? manifest.Period_asArray[periodIndex].AdaptationSet_asArray : [] : [];
}

function getRealPeriods(manifest) {
return manifest && manifest.Period_asArray ? manifest.Period_asArray : [];
}

function getRealPeriodForIndex(index, manifest) {
const realPeriods = getRealPeriods(manifest);
if (realPeriods.length > 0 && isInteger(index)) {
return realPeriods[index];
} else {
return null;
}
}

function getAdaptationForId(id, manifest, periodIndex) {
const realAdaptations = getRealAdaptations(manifest, periodIndex);
let i,
Expand Down Expand Up @@ -1114,6 +1127,8 @@ function DashManifestModel() {
getIndexForAdaptation: getIndexForAdaptation,
getAdaptationForId: getAdaptationForId,
getAdaptationsForType: getAdaptationsForType,
getRealPeriods,
getRealPeriodForIndex,
getCodec: getCodec,
getMimeType: getMimeType,
getKID: getKID,
Expand All @@ -1132,6 +1147,7 @@ function DashManifestModel() {
getRegularPeriods: getRegularPeriods,
getMpd: getMpd,
getEventsForPeriod: getEventsForPeriod,
getEssentialPropertiesForRepresentation,
getEventStreamForAdaptationSet: getEventStreamForAdaptationSet,
getEventStreamForRepresentation: getEventStreamForRepresentation,
getUTCTimingSources: getUTCTimingSources,
Expand Down
54 changes: 36 additions & 18 deletions src/streaming/MediaPlayer.js
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ import BaseURLController from './controllers/BaseURLController';
import ManifestLoader from './ManifestLoader';
import ErrorHandler from './utils/ErrorHandler';
import Capabilities from './utils/Capabilities';
import CapabilitiesFilter from './utils/CapabilitiesFilter';
import TextTracks from './text/TextTracks';
import RequestModifier from './utils/RequestModifier';
import TextController from './text/TextController';
Expand All @@ -61,7 +62,7 @@ import Settings from '../core/Settings';
import {
getVersionString
}
from './../core/Version';
from './../core/Version';

//Dash
import SegmentBaseController from '../dash/controllers/SegmentBaseController';
Expand All @@ -74,7 +75,7 @@ import {
import BASE64 from '../../externals/base64';
import ISOBoxer from 'codem-isoboxer';
import DashJSError from './vo/DashJSError';
import { checkParameterType } from './utils/SupervisorTools';
import {checkParameterType} from './utils/SupervisorTools';
import ManifestUpdater from './ManifestUpdater';
import URLUtils from '../streaming/utils/URLUtils';
import BoxParser from './utils/BoxParser';
Expand All @@ -84,6 +85,7 @@ import BoxParser from './utils/BoxParser';
* The media types
* @typedef {("video" | "audio" | "text" | "fragmentedText" | "embeddedText" | "image")} MediaType
*/

/* jscs:enable */

/**
Expand All @@ -94,35 +96,35 @@ import BoxParser from './utils/BoxParser';
*/
function MediaPlayer() {
/**
* @constant {string} STREAMING_NOT_INITIALIZED_ERROR error string thrown when a function is called before the dash.js has been fully initialized
* @inner
*/
* @constant {string} STREAMING_NOT_INITIALIZED_ERROR error string thrown when a function is called before the dash.js has been fully initialized
* @inner
*/
const STREAMING_NOT_INITIALIZED_ERROR = 'You must first call initialize() and set a source before calling this method';
/**
* @constant {string} PLAYBACK_NOT_INITIALIZED_ERROR error string thrown when a function is called before the dash.js has been fully initialized
* @inner
*/
* @constant {string} PLAYBACK_NOT_INITIALIZED_ERROR error string thrown when a function is called before the dash.js has been fully initialized
* @inner
*/
const PLAYBACK_NOT_INITIALIZED_ERROR = 'You must first call initialize() and set a valid source and view before calling this method';
/**
* @constant {string} ELEMENT_NOT_ATTACHED_ERROR error string thrown when a function is called before the dash.js has received a reference of an HTML5 video element
* @inner
*/
* @constant {string} ELEMENT_NOT_ATTACHED_ERROR error string thrown when a function is called before the dash.js has received a reference of an HTML5 video element
* @inner
*/
const ELEMENT_NOT_ATTACHED_ERROR = 'You must first call attachView() to set the video element before calling this method';
/**
* @constant {string} SOURCE_NOT_ATTACHED_ERROR error string thrown when a function is called before the dash.js has received a valid source stream.
* @inner
*/
* @constant {string} SOURCE_NOT_ATTACHED_ERROR error string thrown when a function is called before the dash.js has received a valid source stream.
* @inner
*/
const SOURCE_NOT_ATTACHED_ERROR = 'You must first call attachSource() with a valid source before calling this method';
/**
* @constant {string} MEDIA_PLAYER_NOT_INITIALIZED_ERROR error string thrown when a function is called before the dash.js has been fully initialized.
* @inner
*/
* @constant {string} MEDIA_PLAYER_NOT_INITIALIZED_ERROR error string thrown when a function is called before the dash.js has been fully initialized.
* @inner
*/
const MEDIA_PLAYER_NOT_INITIALIZED_ERROR = 'MediaPlayer not initialized!';

const context = this.context;
const eventBus = EventBus(context).getInstance();
let settings = Settings(context).getInstance();
const debug = Debug(context).getInstance({settings: settings});
const debug = Debug(context).getInstance({ settings: settings });

let instance,
logger,
Expand All @@ -145,6 +147,7 @@ function MediaPlayer() {
errHandler,
baseURLController,
capabilities,
capabilitiesFilter,
streamController,
gapController,
playbackController,
Expand Down Expand Up @@ -195,6 +198,9 @@ function MediaPlayer() {
if (config.capabilities) {
capabilities = config.capabilities;
}
if (config.capabilitiesFilter) {
capabilitiesFilter = config.capabilitiesFilter;
}
if (config.streamController) {
streamController = config.streamController;
}
Expand Down Expand Up @@ -242,6 +248,7 @@ function MediaPlayer() {
if (!capabilities) {
capabilities = Capabilities(context).getInstance();
}

errHandler = ErrorHandler(context).getInstance();

if (!capabilities.supportsMediaSource()) {
Expand Down Expand Up @@ -281,6 +288,10 @@ function MediaPlayer() {
gapController = GapController(context).getInstance();
}

if (!capabilitiesFilter) {
capabilitiesFilter = CapabilitiesFilter(context).getInstance();
}

adapter = DashAdapter(context).getInstance();

manifestModel = ManifestModel(context).getInstance();
Expand Down Expand Up @@ -1959,8 +1970,15 @@ function MediaPlayer() {
streamController = StreamController(context).getInstance();
}

capabilitiesFilter.setConfig({
capabilities,
adapter,
settings
});

streamController.setConfig({
capabilities: capabilities,
capabilitiesFilter,
manifestLoader: manifestLoader,
manifestModel: manifestModel,
mediaPlayerModel: mediaPlayerModel,
Expand Down
59 changes: 5 additions & 54 deletions src/streaming/Stream.js
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ function Stream(config) {
const manifestUpdater = config.manifestUpdater;
const adapter = config.adapter;
const capabilities = config.capabilities;
const capabilitiesFilter = config.capabilitiesFilter;
const errHandler = config.errHandler;
const timelineConverter = config.timelineConverter;
const dashMetrics = config.dashMetrics;
Expand Down Expand Up @@ -86,17 +87,6 @@ function Stream(config) {
isEndedEventSignaled,
trackChangedEvent;

const codecCompatibilityTable = [
{
'codec': 'avc1',
'compatibleCodecs': ['avc3']
},
{
'codec': 'avc3',
'compatibleCodecs': ['avc1']
}
];

function setup() {
debug = Debug(context).getInstance();
logger = debug.getLogger(instance);
Expand Down Expand Up @@ -549,8 +539,7 @@ function Stream(config) {

isUpdating = true;

filterCodecs(Constants.VIDEO);
filterCodecs(Constants.AUDIO);
capabilitiesFilter.filterUnsupportedFeaturesOfPeriod( streamInfo);

if (!element || (element && (/^VIDEO$/i).test(element.nodeName))) {
initializeMediaForType(Constants.VIDEO, mediaSource);
Expand Down Expand Up @@ -582,8 +571,7 @@ function Stream(config) {
function initializeAfterPreload() {
isUpdating = true;
checkConfig();
filterCodecs(Constants.VIDEO);
filterCodecs(Constants.AUDIO);
capabilitiesFilter.filterUnsupportedFeaturesOfPeriod(streamInfo);

isMediaInitialized = true;
isUpdating = false;
Expand All @@ -596,25 +584,6 @@ function Stream(config) {
}
}

function filterCodecs(type) {
const realAdaptation = adapter.getAdaptationForType(streamInfo ? streamInfo.index : null, type, streamInfo);

if (!realAdaptation || !Array.isArray(realAdaptation.Representation_asArray)) return;

// Filter codecs that are not supported
realAdaptation.Representation_asArray = realAdaptation.Representation_asArray.filter((_, i) => {
// keep at least codec from lowest representation
if (i === 0) return true;

const codec = adapter.getCodec(realAdaptation, i, true);
if (!capabilities.supportsCodec(codec)) {
logger.error('[Stream] codec not supported: ' + codec);
return false;
}
return true;
});
}

function checkIfInitializationCompleted() {
const ln = streamProcessors.length;
const hasError = !!updateError.audio || !!updateError.video;
Expand Down Expand Up @@ -764,8 +733,7 @@ function Stream(config) {
addInlineEvents();
}

filterCodecs(Constants.VIDEO);
filterCodecs(Constants.AUDIO);
capabilitiesFilter.filterUnsupportedFeaturesOfPeriod(streamInfo);

for (let i = 0, ln = streamProcessors.length; i < ln; i++) {
let streamProcessor = streamProcessors[i];
Expand Down Expand Up @@ -866,27 +834,10 @@ function Stream(config) {
return oldCodecs.indexOf(newCodec) > -1;
});

const partialCodecMatch = newCodecs.some((newCodec) => oldCodecs.some((oldCodec) => codecRootCompatibleWithCodec(oldCodec, newCodec)));
const partialCodecMatch = newCodecs.some((newCodec) => oldCodecs.some((oldCodec) => capabilities.codecRootCompatibleWithCodec(oldCodec, newCodec)));
return codecMatch || (partialCodecMatch && sameMimeType);
}

// Check if the root of the old codec is the same as the new one, or if it's declared as compatible in the compat table
function codecRootCompatibleWithCodec(codec1, codec2) {
const codecRoot = codec1.split('.')[0];
const rootCompatible = codec2.indexOf(codecRoot) === 0;
let compatTableCodec;
for (let i = 0; i < codecCompatibilityTable.length; i++) {
if (codecCompatibilityTable[i].codec === codecRoot) {
compatTableCodec = codecCompatibilityTable[i];
break;
}
}
if (compatTableCodec) {
return rootCompatible || compatTableCodec.compatibleCodecs.some((compatibleCodec) => codec2.indexOf(compatibleCodec) === 0);
}
return rootCompatible;
}

function setPreloaded(value) {
preloaded = value;
}
Expand Down
Loading