mirror of
https://github.com/DanielnetoDotCom/YouPHPTube
synced 2025-10-03 09:49:28 +02:00
Update
This commit is contained in:
parent
0e3fa24329
commit
4e95a0e94c
369 changed files with 16946 additions and 89882 deletions
672
node_modules/@videojs/http-streaming/dist/videojs-http-streaming.cjs.js
generated
vendored
672
node_modules/@videojs/http-streaming/dist/videojs-http-streaming.cjs.js
generated
vendored
|
@ -1,4 +1,4 @@
|
|||
/*! @name @videojs/http-streaming @version 3.6.0 @license Apache-2.0 */
|
||||
/*! @name @videojs/http-streaming @version 3.7.0 @license Apache-2.0 */
|
||||
'use strict';
|
||||
|
||||
Object.defineProperty(exports, '__esModule', { value: true });
|
||||
|
@ -834,10 +834,15 @@ const playlistEnd = function (playlist, expired, useSafeLiveEnd, liveEdgePadding
|
|||
const seekable = function (playlist, expired, liveEdgePadding) {
|
||||
const useSafeLiveEnd = true;
|
||||
const seekableStart = expired || 0;
|
||||
const seekableEnd = playlistEnd(playlist, expired, useSafeLiveEnd, liveEdgePadding);
|
||||
let seekableEnd = playlistEnd(playlist, expired, useSafeLiveEnd, liveEdgePadding);
|
||||
|
||||
if (seekableEnd === null) {
|
||||
return createTimeRanges();
|
||||
} // Clamp seekable end since it can not be less than the seekable start
|
||||
|
||||
|
||||
if (seekableEnd < seekableStart) {
|
||||
seekableEnd = seekableStart;
|
||||
}
|
||||
|
||||
return createTimeRanges(seekableStart, seekableEnd);
|
||||
|
@ -3587,7 +3592,10 @@ class DashPlaylistLoader extends EventTarget {
|
|||
}); // live playlist staleness timeout
|
||||
|
||||
this.on('mediaupdatetimeout', () => {
|
||||
this.refreshMedia_(this.media().id);
|
||||
// We handle live content steering in the playlist controller
|
||||
if (!this.media().attributes.serviceLocation) {
|
||||
this.refreshMedia_(this.media().id);
|
||||
}
|
||||
});
|
||||
this.state = 'HAVE_NOTHING';
|
||||
this.loadedPlaylists_ = {};
|
||||
|
@ -6606,19 +6614,33 @@ const workerCode$1 = transform(getWorkerString(function () {
|
|||
var nextByte = packetData[i + 1];
|
||||
var win = service.currentWindow;
|
||||
var char;
|
||||
var charCodeArray; // Use the TextDecoder if one was created for this service
|
||||
var charCodeArray; // Converts an array of bytes to a unicode hex string.
|
||||
|
||||
function toHexString(byteArray) {
|
||||
return byteArray.map(byte => {
|
||||
return ('0' + (byte & 0xFF).toString(16)).slice(-2);
|
||||
}).join('');
|
||||
}
|
||||
|
||||
if (isMultiByte) {
|
||||
charCodeArray = [currentByte, nextByte];
|
||||
i++;
|
||||
} else {
|
||||
charCodeArray = [currentByte];
|
||||
} // Use the TextDecoder if one was created for this service
|
||||
|
||||
|
||||
if (service.textDecoder_ && !isExtended) {
|
||||
if (isMultiByte) {
|
||||
charCodeArray = [currentByte, nextByte];
|
||||
i++;
|
||||
} else {
|
||||
charCodeArray = [currentByte];
|
||||
}
|
||||
|
||||
char = service.textDecoder_.decode(new Uint8Array(charCodeArray));
|
||||
} else {
|
||||
char = get708CharFromCode(extended | currentByte);
|
||||
// We assume any multi-byte char without a decoder is unicode.
|
||||
if (isMultiByte) {
|
||||
const unicode = toHexString(charCodeArray); // Takes a unicode hex string and creates a single character.
|
||||
|
||||
char = String.fromCharCode(parseInt(unicode, 16));
|
||||
} else {
|
||||
char = get708CharFromCode(extended | currentByte);
|
||||
}
|
||||
}
|
||||
|
||||
if (win.pendingNewLine && !win.isEmpty()) {
|
||||
|
@ -14438,7 +14460,7 @@ const handleSegmentBytes = ({
|
|||
segment.bytes = bytesAsUint8Array = emsgData; // Run through the CaptionParser in case there are captions.
|
||||
// Initialize CaptionParser if it hasn't been yet
|
||||
|
||||
if (!tracks.video || !data.byteLength || !segment.transmuxer) {
|
||||
if (!tracks.video || !emsgData.byteLength || !segment.transmuxer) {
|
||||
finishLoading(undefined, id3Frames);
|
||||
return;
|
||||
}
|
||||
|
@ -22206,15 +22228,11 @@ const onError = {
|
|||
*/
|
||||
AUDIO: (type, settings) => () => {
|
||||
const {
|
||||
segmentLoaders: {
|
||||
[type]: segmentLoader
|
||||
},
|
||||
mediaTypes: {
|
||||
[type]: mediaType
|
||||
},
|
||||
excludePlaylist
|
||||
} = settings;
|
||||
stopLoaders(segmentLoader, mediaType); // switch back to default audio track
|
||||
} = settings; // switch back to default audio track
|
||||
|
||||
const activeTrack = mediaType.activeTrack();
|
||||
const activeGroup = mediaType.activeGroup();
|
||||
|
@ -22255,15 +22273,11 @@ const onError = {
|
|||
*/
|
||||
SUBTITLES: (type, settings) => () => {
|
||||
const {
|
||||
segmentLoaders: {
|
||||
[type]: segmentLoader
|
||||
},
|
||||
mediaTypes: {
|
||||
[type]: mediaType
|
||||
}
|
||||
} = settings;
|
||||
videojs__default["default"].log.warn('Problem encountered loading the subtitle track.' + 'Disabling subtitle track.');
|
||||
stopLoaders(segmentLoader, mediaType);
|
||||
const track = mediaType.activeTrack();
|
||||
|
||||
if (track) {
|
||||
|
@ -22902,6 +22916,440 @@ const createMediaTypes = () => {
|
|||
return mediaTypes;
|
||||
};
|
||||
|
||||
/**
|
||||
* A utility class for setting properties and maintaining the state of the content steering manifest.
|
||||
*
|
||||
* Content Steering manifest format:
|
||||
* VERSION: number (required) currently only version 1 is supported.
|
||||
* TTL: number in seconds (optional) until the next content steering manifest reload.
|
||||
* RELOAD-URI: string (optional) uri to fetch the next content steering manifest.
|
||||
* SERVICE-LOCATION-PRIORITY or PATHWAY-PRIORITY a non empty array of unique string values.
|
||||
*/
|
||||
|
||||
class SteeringManifest {
|
||||
constructor() {
|
||||
this.priority_ = [];
|
||||
}
|
||||
|
||||
set version(number) {
|
||||
// Only version 1 is currently supported for both DASH and HLS.
|
||||
if (number === 1) {
|
||||
this.version_ = number;
|
||||
}
|
||||
}
|
||||
|
||||
set ttl(seconds) {
|
||||
// TTL = time-to-live, default = 300 seconds.
|
||||
this.ttl_ = seconds || 300;
|
||||
}
|
||||
|
||||
set reloadUri(uri) {
|
||||
if (uri) {
|
||||
// reload URI can be relative to the previous reloadUri.
|
||||
this.reloadUri_ = resolveUrl(this.reloadUri_, uri);
|
||||
}
|
||||
}
|
||||
|
||||
set priority(array) {
|
||||
// priority must be non-empty and unique values.
|
||||
if (array && array.length) {
|
||||
this.priority_ = array;
|
||||
}
|
||||
}
|
||||
|
||||
get version() {
|
||||
return this.version_;
|
||||
}
|
||||
|
||||
get ttl() {
|
||||
return this.ttl_;
|
||||
}
|
||||
|
||||
get reloadUri() {
|
||||
return this.reloadUri_;
|
||||
}
|
||||
|
||||
get priority() {
|
||||
return this.priority_;
|
||||
}
|
||||
|
||||
}
|
||||
/**
|
||||
* This class represents a content steering manifest and associated state. See both HLS and DASH specifications.
|
||||
* HLS: https://developer.apple.com/streaming/HLSContentSteeringSpecification.pdf and
|
||||
* https://datatracker.ietf.org/doc/draft-pantos-hls-rfc8216bis/ section 4.4.6.6.
|
||||
* DASH: https://dashif.org/docs/DASH-IF-CTS-00XX-Content-Steering-Community-Review.pdf
|
||||
*
|
||||
* @param {function} xhr for making a network request from the browser.
|
||||
* @param {function} bandwidth for fetching the current bandwidth from the main segment loader.
|
||||
*/
|
||||
|
||||
|
||||
class ContentSteeringController extends videojs__default["default"].EventTarget {
|
||||
constructor(xhr, bandwidth) {
|
||||
super();
|
||||
this.currentPathway = null;
|
||||
this.defaultPathway = null;
|
||||
this.queryBeforeStart = null;
|
||||
this.availablePathways_ = new Set();
|
||||
this.excludedPathways_ = new Set();
|
||||
this.steeringManifest = new SteeringManifest();
|
||||
this.proxyServerUrl_ = null;
|
||||
this.manifestType_ = null;
|
||||
this.ttlTimeout_ = null;
|
||||
this.request_ = null;
|
||||
this.excludedSteeringManifestURLs = new Set();
|
||||
this.logger_ = logger('Content Steering');
|
||||
this.xhr_ = xhr;
|
||||
this.getBandwidth_ = bandwidth;
|
||||
}
|
||||
/**
|
||||
* Assigns the content steering tag properties to the steering controller
|
||||
*
|
||||
* @param {string} baseUrl the baseURL from the manifest for resolving the steering manifest url
|
||||
* @param {Object} steeringTag the content steering tag from the main manifest
|
||||
*/
|
||||
|
||||
|
||||
assignTagProperties(baseUrl, steeringTag) {
|
||||
this.manifestType_ = steeringTag.serverUri ? 'HLS' : 'DASH'; // serverUri is HLS serverURL is DASH
|
||||
|
||||
const steeringUri = steeringTag.serverUri || steeringTag.serverURL;
|
||||
|
||||
if (!steeringUri) {
|
||||
this.logger_(`steering manifest URL is ${steeringUri}, cannot request steering manifest.`);
|
||||
this.trigger('error');
|
||||
return;
|
||||
} // Content steering manifests can be encoded as a data URI. We can decode, parse and return early if that's the case.
|
||||
|
||||
|
||||
if (steeringUri.startsWith('data:')) {
|
||||
this.decodeDataUriManifest_(steeringUri.substring(steeringUri.indexOf(',') + 1));
|
||||
return;
|
||||
} // With DASH queryBeforeStart, we want to use the steeringUri as soon as possible for the request.
|
||||
|
||||
|
||||
this.steeringManifest.reloadUri = this.queryBeforeStart ? steeringUri : resolveUrl(baseUrl, steeringUri); // pathwayId is HLS defaultServiceLocation is DASH
|
||||
|
||||
this.defaultPathway = steeringTag.pathwayId || steeringTag.defaultServiceLocation; // currently only DASH supports the following properties on <ContentSteering> tags.
|
||||
|
||||
this.queryBeforeStart = steeringTag.queryBeforeStart || false;
|
||||
this.proxyServerUrl_ = steeringTag.proxyServerURL || null; // trigger a steering event if we have a pathway from the content steering tag.
|
||||
// this tells VHS which segment pathway to start with.
|
||||
// If queryBeforeStart is true we need to wait for the steering manifest response.
|
||||
|
||||
if (this.defaultPathway && !this.queryBeforeStart) {
|
||||
this.trigger('content-steering');
|
||||
}
|
||||
|
||||
if (this.queryBeforeStart) {
|
||||
this.requestSteeringManifest(this.steeringManifest.reloadUri);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Requests the content steering manifest and parse the response. This should only be called after
|
||||
* assignTagProperties was called with a content steering tag.
|
||||
*
|
||||
* @param {string} initialUri The optional uri to make the request with.
|
||||
* If set, the request should be made with exactly what is passed in this variable.
|
||||
* This scenario is specific to DASH when the queryBeforeStart parameter is true.
|
||||
* This scenario should only happen once on initalization.
|
||||
*/
|
||||
|
||||
|
||||
requestSteeringManifest(initialUri) {
|
||||
const reloadUri = this.steeringManifest.reloadUri;
|
||||
|
||||
if (!initialUri && !reloadUri) {
|
||||
return;
|
||||
} // We currently don't support passing MPD query parameters directly to the content steering URL as this requires
|
||||
// ExtUrlQueryInfo tag support. See the DASH content steering spec section 8.1.
|
||||
// This request URI accounts for manifest URIs that have been excluded.
|
||||
|
||||
|
||||
const uri = initialUri || this.getRequestURI(reloadUri); // If there are no valid manifest URIs, we should stop content steering.
|
||||
|
||||
if (!uri) {
|
||||
this.logger_('No valid content steering manifest URIs. Stopping content steering.');
|
||||
this.trigger('error');
|
||||
this.dispose();
|
||||
return;
|
||||
}
|
||||
|
||||
this.request_ = this.xhr_({
|
||||
uri
|
||||
}, (error, errorInfo) => {
|
||||
if (error) {
|
||||
// If the client receives HTTP 410 Gone in response to a manifest request,
|
||||
// it MUST NOT issue another request for that URI for the remainder of the
|
||||
// playback session. It MAY continue to use the most-recently obtained set
|
||||
// of Pathways.
|
||||
if (errorInfo.status === 410) {
|
||||
this.logger_(`manifest request 410 ${error}.`);
|
||||
this.logger_(`There will be no more content steering requests to ${uri} this session.`);
|
||||
this.excludedSteeringManifestURLs.add(uri);
|
||||
return;
|
||||
} // If the client receives HTTP 429 Too Many Requests with a Retry-After
|
||||
// header in response to a manifest request, it SHOULD wait until the time
|
||||
// specified by the Retry-After header to reissue the request.
|
||||
|
||||
|
||||
if (errorInfo.status === 429) {
|
||||
const retrySeconds = errorInfo.responseHeaders['retry-after'];
|
||||
this.logger_(`manifest request 429 ${error}.`);
|
||||
this.logger_(`content steering will retry in ${retrySeconds} seconds.`);
|
||||
this.startTTLTimeout_(parseInt(retrySeconds, 10));
|
||||
return;
|
||||
} // If the Steering Manifest cannot be loaded and parsed correctly, the
|
||||
// client SHOULD continue to use the previous values and attempt to reload
|
||||
// it after waiting for the previously-specified TTL (or 5 minutes if
|
||||
// none).
|
||||
|
||||
|
||||
this.logger_(`manifest failed to load ${error}.`);
|
||||
this.startTTLTimeout_();
|
||||
return;
|
||||
}
|
||||
|
||||
const steeringManifestJson = JSON.parse(this.request_.responseText);
|
||||
this.startTTLTimeout_();
|
||||
this.assignSteeringProperties_(steeringManifestJson);
|
||||
});
|
||||
}
|
||||
/**
|
||||
* Set the proxy server URL and add the steering manifest url as a URI encoded parameter.
|
||||
*
|
||||
* @param {string} steeringUrl the steering manifest url
|
||||
* @return the steering manifest url to a proxy server with all parameters set
|
||||
*/
|
||||
|
||||
|
||||
setProxyServerUrl_(steeringUrl) {
|
||||
const steeringUrlObject = new window__default["default"].URL(steeringUrl);
|
||||
const proxyServerUrlObject = new window__default["default"].URL(this.proxyServerUrl_);
|
||||
proxyServerUrlObject.searchParams.set('url', encodeURI(steeringUrlObject.toString()));
|
||||
return this.setSteeringParams_(proxyServerUrlObject.toString());
|
||||
}
|
||||
/**
|
||||
* Decodes and parses the data uri encoded steering manifest
|
||||
*
|
||||
* @param {string} dataUri the data uri to be decoded and parsed.
|
||||
*/
|
||||
|
||||
|
||||
decodeDataUriManifest_(dataUri) {
|
||||
const steeringManifestJson = JSON.parse(window__default["default"].atob(dataUri));
|
||||
this.assignSteeringProperties_(steeringManifestJson);
|
||||
}
|
||||
/**
|
||||
* Set the HLS or DASH content steering manifest request query parameters. For example:
|
||||
* _HLS_pathway="<CURRENT-PATHWAY-ID>" and _HLS_throughput=<THROUGHPUT>
|
||||
* _DASH_pathway and _DASH_throughput
|
||||
*
|
||||
* @param {string} uri to add content steering server parameters to.
|
||||
* @return a new uri as a string with the added steering query parameters.
|
||||
*/
|
||||
|
||||
|
||||
setSteeringParams_(url) {
|
||||
const urlObject = new window__default["default"].URL(url);
|
||||
const path = this.getPathway();
|
||||
const networkThroughput = this.getBandwidth_();
|
||||
|
||||
if (path) {
|
||||
const pathwayKey = `_${this.manifestType_}_pathway`;
|
||||
urlObject.searchParams.set(pathwayKey, path);
|
||||
}
|
||||
|
||||
if (networkThroughput) {
|
||||
const throughputKey = `_${this.manifestType_}_throughput`;
|
||||
urlObject.searchParams.set(throughputKey, networkThroughput);
|
||||
}
|
||||
|
||||
return urlObject.toString();
|
||||
}
|
||||
/**
|
||||
* Assigns the current steering manifest properties and to the SteeringManifest object
|
||||
*
|
||||
* @param {Object} steeringJson the raw JSON steering manifest
|
||||
*/
|
||||
|
||||
|
||||
assignSteeringProperties_(steeringJson) {
|
||||
this.steeringManifest.version = steeringJson.VERSION;
|
||||
|
||||
if (!this.steeringManifest.version) {
|
||||
this.logger_(`manifest version is ${steeringJson.VERSION}, which is not supported.`);
|
||||
this.trigger('error');
|
||||
return;
|
||||
}
|
||||
|
||||
this.steeringManifest.ttl = steeringJson.TTL;
|
||||
this.steeringManifest.reloadUri = steeringJson['RELOAD-URI']; // HLS = PATHWAY-PRIORITY required. DASH = SERVICE-LOCATION-PRIORITY optional
|
||||
|
||||
this.steeringManifest.priority = steeringJson['PATHWAY-PRIORITY'] || steeringJson['SERVICE-LOCATION-PRIORITY']; // TODO: HLS handle PATHWAY-CLONES. See section 7.2 https://datatracker.ietf.org/doc/draft-pantos-hls-rfc8216bis/
|
||||
// 1. apply first pathway from the array.
|
||||
// 2. if first pathway doesn't exist in manifest, try next pathway.
|
||||
// a. if all pathways are exhausted, ignore the steering manifest priority.
|
||||
// 3. if segments fail from an established pathway, try all variants/renditions, then exclude the failed pathway.
|
||||
// a. exclude a pathway for a minimum of the last TTL duration. Meaning, from the next steering response,
|
||||
// the excluded pathway will be ignored.
|
||||
// See excludePathway usage in excludePlaylist().
|
||||
// If there are no available pathways, we need to stop content steering.
|
||||
|
||||
if (!this.availablePathways_.size) {
|
||||
this.logger_('There are no available pathways for content steering. Ending content steering.');
|
||||
this.trigger('error');
|
||||
this.dispose();
|
||||
}
|
||||
|
||||
const chooseNextPathway = pathwaysByPriority => {
|
||||
for (const path of pathwaysByPriority) {
|
||||
if (this.availablePathways_.has(path)) {
|
||||
return path;
|
||||
}
|
||||
} // If no pathway matches, ignore the manifest and choose the first available.
|
||||
|
||||
|
||||
return [...this.availablePathways_][0];
|
||||
};
|
||||
|
||||
const nextPathway = chooseNextPathway(this.steeringManifest.priority);
|
||||
|
||||
if (this.currentPathway !== nextPathway) {
|
||||
this.currentPathway = nextPathway;
|
||||
this.trigger('content-steering');
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Returns the pathway to use for steering decisions
|
||||
*
|
||||
* @return {string} returns the current pathway or the default
|
||||
*/
|
||||
|
||||
|
||||
getPathway() {
|
||||
return this.currentPathway || this.defaultPathway;
|
||||
}
|
||||
/**
|
||||
* Chooses the manifest request URI based on proxy URIs and server URLs.
|
||||
* Also accounts for exclusion on certain manifest URIs.
|
||||
*
|
||||
* @param {string} reloadUri the base uri before parameters
|
||||
*
|
||||
* @return {string} the final URI for the request to the manifest server.
|
||||
*/
|
||||
|
||||
|
||||
getRequestURI(reloadUri) {
|
||||
if (!reloadUri) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const isExcluded = uri => this.excludedSteeringManifestURLs.has(uri);
|
||||
|
||||
if (this.proxyServerUrl_) {
|
||||
const proxyURI = this.setProxyServerUrl_(reloadUri);
|
||||
|
||||
if (!isExcluded(proxyURI)) {
|
||||
return proxyURI;
|
||||
}
|
||||
}
|
||||
|
||||
const steeringURI = this.setSteeringParams_(reloadUri);
|
||||
|
||||
if (!isExcluded(steeringURI)) {
|
||||
return steeringURI;
|
||||
} // Return nothing if all valid manifest URIs are excluded.
|
||||
|
||||
|
||||
return null;
|
||||
}
|
||||
/**
|
||||
* Start the timeout for re-requesting the steering manifest at the TTL interval.
|
||||
*
|
||||
* @param {number} ttl time in seconds of the timeout. Defaults to the
|
||||
* ttl interval in the steering manifest
|
||||
*/
|
||||
|
||||
|
||||
startTTLTimeout_(ttl = this.steeringManifest.ttl) {
|
||||
// 300 (5 minutes) is the default value.
|
||||
const ttlMS = ttl * 1000;
|
||||
this.ttlTimeout_ = window__default["default"].setTimeout(() => {
|
||||
this.requestSteeringManifest();
|
||||
}, ttlMS);
|
||||
}
|
||||
/**
|
||||
* Clear the TTL timeout if necessary.
|
||||
*/
|
||||
|
||||
|
||||
clearTTLTimeout_() {
|
||||
window__default["default"].clearTimeout(this.ttlTimeout_);
|
||||
this.ttlTimeout_ = null;
|
||||
}
|
||||
/**
|
||||
* aborts any current steering xhr and sets the current request object to null
|
||||
*/
|
||||
|
||||
|
||||
abort() {
|
||||
if (this.request_) {
|
||||
this.request_.abort();
|
||||
}
|
||||
|
||||
this.request_ = null;
|
||||
}
|
||||
/**
|
||||
* aborts steering requests clears the ttl timeout and resets all properties.
|
||||
*/
|
||||
|
||||
|
||||
dispose() {
|
||||
this.off('content-steering');
|
||||
this.off('error');
|
||||
this.abort();
|
||||
this.clearTTLTimeout_();
|
||||
this.currentPathway = null;
|
||||
this.defaultPathway = null;
|
||||
this.queryBeforeStart = null;
|
||||
this.proxyServerUrl_ = null;
|
||||
this.manifestType_ = null;
|
||||
this.ttlTimeout_ = null;
|
||||
this.request_ = null;
|
||||
this.excludedSteeringManifestURLs = new Set();
|
||||
this.availablePathways_ = new Set();
|
||||
this.excludedPathways_ = new Set();
|
||||
this.steeringManifest = new SteeringManifest();
|
||||
}
|
||||
/**
|
||||
* adds a pathway to the available pathways set
|
||||
*
|
||||
* @param {string} pathway the pathway string to add
|
||||
*/
|
||||
|
||||
|
||||
addAvailablePathway(pathway) {
|
||||
if (pathway) {
|
||||
this.availablePathways_.add(pathway);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* clears all pathways from the available pathways set
|
||||
*/
|
||||
|
||||
|
||||
clearAvailablePathways() {
|
||||
this.availablePathways_.clear();
|
||||
}
|
||||
|
||||
excludePathway(pathway) {
|
||||
return this.availablePathways_.delete(pathway);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @file playlist-controller.js
|
||||
*/
|
||||
|
@ -23153,6 +23601,12 @@ class PlaylistController extends videojs__default["default"].EventTarget {
|
|||
tech.addWebVttScript_();
|
||||
})
|
||||
}), options);
|
||||
|
||||
const getBandwidth = () => {
|
||||
return this.mainSegmentLoader_.bandwidth;
|
||||
};
|
||||
|
||||
this.contentSteeringController_ = new ContentSteeringController(this.vhs_.xhr, getBandwidth);
|
||||
this.setupSegmentLoaderListeners_();
|
||||
|
||||
if (this.bufferBasedABR) {
|
||||
|
@ -23254,6 +23708,34 @@ class PlaylistController extends videojs__default["default"].EventTarget {
|
|||
|
||||
this.mainPlaylistLoader_.media(playlist, delay);
|
||||
}
|
||||
/**
|
||||
* A function that ensures we switch our playlists inside of `mediaTypes`
|
||||
* to match the current `serviceLocation` provided by the contentSteering controller.
|
||||
* We want to check media types of `AUDIO`, `SUBTITLES`, and `CLOSED-CAPTIONS`.
|
||||
*
|
||||
* This should only be called on a DASH playback scenario while using content steering.
|
||||
* This is necessary due to differences in how media in HLS manifests are generally tied to
|
||||
* a video playlist, where in DASH that is not always the case.
|
||||
*/
|
||||
|
||||
|
||||
switchMediaForDASHContentSteering_() {
|
||||
['AUDIO', 'SUBTITLES', 'CLOSED-CAPTIONS'].forEach(type => {
|
||||
const mediaType = this.mediaTypes_[type];
|
||||
const activeGroup = mediaType ? mediaType.activeGroup() : null;
|
||||
const pathway = this.contentSteeringController_.getPathway();
|
||||
|
||||
if (activeGroup && pathway) {
|
||||
// activeGroup can be an array or a single group
|
||||
const mediaPlaylists = activeGroup.length ? activeGroup[0].playlists : activeGroup.playlists;
|
||||
const dashMediaPlaylists = mediaPlaylists.filter(p => p.attributes.serviceLocation === pathway); // Switch the current active playlist to the correct CDN
|
||||
|
||||
if (dashMediaPlaylists.length) {
|
||||
this.mediaTypes_[type].activePlaylistLoader.media(dashMediaPlaylists[0]);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
/**
|
||||
* Start a timer that periodically calls checkABR_
|
||||
*
|
||||
|
@ -23420,8 +23902,9 @@ class PlaylistController extends videojs__default["default"].EventTarget {
|
|||
let updatedPlaylist = this.mainPlaylistLoader_.media();
|
||||
|
||||
if (!updatedPlaylist) {
|
||||
// exclude any variants that are not supported by the browser before selecting
|
||||
this.initContentSteeringController_(); // exclude any variants that are not supported by the browser before selecting
|
||||
// an initial media as the playlist selectors do not consider browser support
|
||||
|
||||
this.excludeUnsupportedVariants_();
|
||||
let selectedMedia;
|
||||
|
||||
|
@ -24073,10 +24556,23 @@ class PlaylistController extends videojs__default["default"].EventTarget {
|
|||
}
|
||||
|
||||
if (isFinalRendition) {
|
||||
// Since we're on the final non-excluded playlist, and we're about to exclude
|
||||
// If we're content steering, try other pathways.
|
||||
if (this.main().contentSteering) {
|
||||
const pathway = this.pathwayAttribute_(playlistToExclude); // Ignore at least 1 steering manifest refresh.
|
||||
|
||||
const reIncludeDelay = this.contentSteeringController_.steeringManifest.ttl * 1000;
|
||||
this.contentSteeringController_.excludePathway(pathway);
|
||||
this.excludeThenChangePathway_();
|
||||
setTimeout(() => {
|
||||
this.contentSteeringController_.addAvailablePathway(pathway);
|
||||
}, reIncludeDelay);
|
||||
return;
|
||||
} // Since we're on the final non-excluded playlist, and we're about to exclude
|
||||
// it, instead of erring the player or retrying this playlist, clear out the current
|
||||
// exclusion list. This allows other playlists to be attempted in case any have been
|
||||
// fixed.
|
||||
|
||||
|
||||
let reincluded = false;
|
||||
playlists.forEach(playlist => {
|
||||
// skip current playlist which is about to be excluded
|
||||
|
@ -24475,6 +24971,7 @@ class PlaylistController extends videojs__default["default"].EventTarget {
|
|||
this.decrypter_.terminate();
|
||||
this.mainPlaylistLoader_.dispose();
|
||||
this.mainSegmentLoader_.dispose();
|
||||
this.contentSteeringController_.dispose();
|
||||
|
||||
if (this.loadOnPlay_) {
|
||||
this.tech_.off('play', this.loadOnPlay_);
|
||||
|
@ -24865,6 +25362,129 @@ class PlaylistController extends videojs__default["default"].EventTarget {
|
|||
});
|
||||
}
|
||||
|
||||
pathwayAttribute_(playlist) {
|
||||
return playlist.attributes['PATHWAY-ID'] || playlist.attributes.serviceLocation;
|
||||
}
|
||||
/**
|
||||
* Initialize content steering listeners and apply the tag properties.
|
||||
*/
|
||||
|
||||
|
||||
initContentSteeringController_() {
|
||||
const initialMain = this.main();
|
||||
|
||||
if (!initialMain.contentSteering) {
|
||||
return;
|
||||
}
|
||||
|
||||
const updateSteeringValues = main => {
|
||||
for (const playlist of main.playlists) {
|
||||
this.contentSteeringController_.addAvailablePathway(this.pathwayAttribute_(playlist));
|
||||
}
|
||||
|
||||
this.contentSteeringController_.assignTagProperties(main.uri, main.contentSteering);
|
||||
};
|
||||
|
||||
updateSteeringValues(initialMain);
|
||||
this.contentSteeringController_.on('content-steering', this.excludeThenChangePathway_.bind(this)); // We need to ensure we update the content steering values when a new
|
||||
// manifest is loaded in live DASH with content steering.
|
||||
|
||||
if (this.sourceType_ === 'dash') {
|
||||
this.mainPlaylistLoader_.on('mediaupdatetimeout', () => {
|
||||
this.mainPlaylistLoader_.refreshMedia_(this.mainPlaylistLoader_.media().id); // clear past values
|
||||
|
||||
this.contentSteeringController_.abort();
|
||||
this.contentSteeringController_.clearTTLTimeout_();
|
||||
this.contentSteeringController_.clearAvailablePathways();
|
||||
updateSteeringValues(this.main());
|
||||
});
|
||||
} // Do this at startup only, after that the steering requests are managed by the Content Steering class.
|
||||
// DASH queryBeforeStart scenarios will be handled by the Content Steering class.
|
||||
|
||||
|
||||
if (!this.contentSteeringController_.queryBeforeStart) {
|
||||
this.tech_.one('canplay', () => {
|
||||
this.contentSteeringController_.requestSteeringManifest();
|
||||
});
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Simple exclude and change playlist logic for content steering.
|
||||
*/
|
||||
|
||||
|
||||
excludeThenChangePathway_() {
|
||||
const currentPathway = this.contentSteeringController_.getPathway();
|
||||
|
||||
if (!currentPathway) {
|
||||
return;
|
||||
}
|
||||
|
||||
const main = this.main();
|
||||
const playlists = main.playlists;
|
||||
const ids = new Set();
|
||||
let didEnablePlaylists = false;
|
||||
Object.keys(playlists).forEach(key => {
|
||||
const variant = playlists[key];
|
||||
const pathwayId = this.pathwayAttribute_(variant);
|
||||
const differentPathwayId = pathwayId && currentPathway !== pathwayId;
|
||||
const steeringExclusion = variant.excludeUntil === Infinity && variant.lastExcludeReason_ === 'content-steering';
|
||||
|
||||
if (steeringExclusion && !differentPathwayId) {
|
||||
delete variant.excludeUntil;
|
||||
delete variant.lastExcludeReason_;
|
||||
didEnablePlaylists = true;
|
||||
}
|
||||
|
||||
const noExcludeUntil = !variant.excludeUntil && variant.excludeUntil !== Infinity;
|
||||
const shouldExclude = !ids.has(variant.id) && differentPathwayId && noExcludeUntil;
|
||||
|
||||
if (!shouldExclude) {
|
||||
return;
|
||||
}
|
||||
|
||||
ids.add(variant.id);
|
||||
variant.excludeUntil = Infinity;
|
||||
variant.lastExcludeReason_ = 'content-steering'; // TODO: kind of spammy, maybe move this.
|
||||
|
||||
this.logger_(`excluding ${variant.id} for ${variant.lastExcludeReason_}`);
|
||||
});
|
||||
|
||||
if (this.contentSteeringController_.manifestType_ === 'DASH') {
|
||||
Object.keys(this.mediaTypes_).forEach(key => {
|
||||
const type = this.mediaTypes_[key];
|
||||
|
||||
if (type.activePlaylistLoader) {
|
||||
const currentPlaylist = type.activePlaylistLoader.media_; // Check if the current media playlist matches the current CDN
|
||||
|
||||
if (currentPlaylist && currentPlaylist.attributes.serviceLocation !== currentPathway) {
|
||||
didEnablePlaylists = true;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (didEnablePlaylists) {
|
||||
this.changeSegmentPathway_();
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Changes the current playlists for audio, video and subtitles after a new pathway
|
||||
* is chosen from content steering.
|
||||
*/
|
||||
|
||||
|
||||
changeSegmentPathway_() {
|
||||
const nextPlaylist = this.selectPlaylist();
|
||||
this.pauseLoading(); // Switch audio and text track playlists if necessary in DASH
|
||||
|
||||
if (this.contentSteeringController_.manifestType_ === 'DASH') {
|
||||
this.switchMediaForDASHContentSteering_();
|
||||
}
|
||||
|
||||
this.switchMedia_(nextPlaylist, 'content-steering');
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -25702,9 +26322,9 @@ const reloadSourceOnError = function (options) {
|
|||
initPlugin(this, options);
|
||||
};
|
||||
|
||||
var version$4 = "3.6.0";
|
||||
var version$4 = "3.7.0";
|
||||
|
||||
var version$3 = "7.0.0";
|
||||
var version$3 = "7.0.1";
|
||||
|
||||
var version$2 = "1.2.2";
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue