mirror of
https://github.com/DanielnetoDotCom/YouPHPTube
synced 2025-10-04 02:09:22 +02:00
also npm update
This commit is contained in:
parent
b6d47e94c8
commit
65f15c7e46
2882 changed files with 382239 additions and 10785 deletions
25
node_modules/mpd-parser/CHANGELOG.md
generated
vendored
25
node_modules/mpd-parser/CHANGELOG.md
generated
vendored
|
@ -1,3 +1,28 @@
|
|||
<a name="1.2.2"></a>
|
||||
## [1.2.2](https://github.com/videojs/mpd-parser/compare/v1.2.1...v1.2.2) (2023-08-30)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* improve serviceLocation for content steering ([#177](https://github.com/videojs/mpd-parser/issues/177)) ([fee1870](https://github.com/videojs/mpd-parser/commit/fee1870))
|
||||
|
||||
<a name="1.2.1"></a>
|
||||
## [1.2.1](https://github.com/videojs/mpd-parser/compare/v1.2.0...v1.2.1) (2023-08-16)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* use manifest uri in base urls ([#176](https://github.com/videojs/mpd-parser/issues/176)) ([5faf232](https://github.com/videojs/mpd-parser/commit/5faf232))
|
||||
|
||||
<a name="1.2.0"></a>
|
||||
# [1.2.0](https://github.com/videojs/mpd-parser/compare/v1.1.1...v1.2.0) (2023-08-15)
|
||||
|
||||
### Features
|
||||
|
||||
* parse content steering info ([#174](https://github.com/videojs/mpd-parser/issues/174)) ([0156528](https://github.com/videojs/mpd-parser/commit/0156528))
|
||||
|
||||
### Chores
|
||||
|
||||
* Update CI and release workflows ([#173](https://github.com/videojs/mpd-parser/issues/173)) ([9ece4ae](https://github.com/videojs/mpd-parser/commit/9ece4ae))
|
||||
|
||||
<a name="1.1.1"></a>
|
||||
## [1.1.1](https://github.com/videojs/mpd-parser/compare/v1.1.0...v1.1.1) (2023-03-31)
|
||||
|
||||
|
|
11
node_modules/mpd-parser/README.md
generated
vendored
11
node_modules/mpd-parser/README.md
generated
vendored
|
@ -41,7 +41,10 @@ const manifestUri = 'https://example.com/dash.xml';
|
|||
const res = await fetch(manifestUri);
|
||||
const manifest = await res.text();
|
||||
|
||||
var parsedManifest = mpdParser.parse(manifest, { manifestUri });
|
||||
// A callback function to handle events like errors or warnings
|
||||
const eventHandler = ({ type, message }) => console.log(`${type}: ${message}`);
|
||||
|
||||
var parsedManifest = mpdParser.parse(manifest, { manifestUri, eventHandler });
|
||||
```
|
||||
|
||||
If dealing with a live stream, then on subsequent calls to parse, the previously parsed
|
||||
|
@ -63,6 +66,12 @@ The parser ouputs a plain javascript object with the following structure:
|
|||
```js
|
||||
Manifest {
|
||||
allowCache: boolean,
|
||||
contentSteering: {
|
||||
defaultServiceLocation: string,
|
||||
proxyServerURL: string,
|
||||
queryBeforeStart: boolean,
|
||||
serverURL: string
|
||||
},
|
||||
endList: boolean,
|
||||
mediaSequence: number,
|
||||
discontinuitySequence: number,
|
||||
|
|
219
node_modules/mpd-parser/dist/mpd-parser.cjs.js
generated
vendored
219
node_modules/mpd-parser/dist/mpd-parser.cjs.js
generated
vendored
|
@ -1,4 +1,4 @@
|
|||
/*! @name mpd-parser @version 1.1.1 @license Apache-2.0 */
|
||||
/*! @name mpd-parser @version 1.2.2 @license Apache-2.0 */
|
||||
'use strict';
|
||||
|
||||
Object.defineProperty(exports, '__esModule', { value: true });
|
||||
|
@ -15,7 +15,7 @@ var resolveUrl__default = /*#__PURE__*/_interopDefaultLegacy(resolveUrl);
|
|||
var window__default = /*#__PURE__*/_interopDefaultLegacy(window);
|
||||
var decodeB64ToUint8Array__default = /*#__PURE__*/_interopDefaultLegacy(decodeB64ToUint8Array);
|
||||
|
||||
var version = "1.1.1";
|
||||
var version = "1.2.2";
|
||||
|
||||
const isObject = obj => {
|
||||
return !!obj && typeof obj === 'object';
|
||||
|
@ -91,6 +91,7 @@ const union = (lists, keyFunction) => {
|
|||
|
||||
var errors = {
|
||||
INVALID_NUMBER_OF_PERIOD: 'INVALID_NUMBER_OF_PERIOD',
|
||||
INVALID_NUMBER_OF_CONTENT_STEERING: 'INVALID_NUMBER_OF_CONTENT_STEERING',
|
||||
DASH_EMPTY_MANIFEST: 'DASH_EMPTY_MANIFEST',
|
||||
DASH_INVALID_XML: 'DASH_INVALID_XML',
|
||||
NO_BASE_URL: 'NO_BASE_URL',
|
||||
|
@ -757,43 +758,56 @@ const positionManifestOnTimeline = ({
|
|||
const generateSidxKey = sidx => sidx && sidx.uri + '-' + byteRangeToString(sidx.byterange);
|
||||
|
||||
const mergeDiscontiguousPlaylists = playlists => {
|
||||
const mergedPlaylists = values(playlists.reduce((acc, playlist) => {
|
||||
// assuming playlist IDs are the same across periods
|
||||
// TODO: handle multiperiod where representation sets are not the same
|
||||
// across periods
|
||||
const name = playlist.attributes.id + (playlist.attributes.lang || '');
|
||||
|
||||
if (!acc[name]) {
|
||||
// First Period
|
||||
acc[name] = playlist;
|
||||
acc[name].attributes.timelineStarts = [];
|
||||
} else {
|
||||
// Subsequent Periods
|
||||
if (playlist.segments) {
|
||||
// first segment of subsequent periods signal a discontinuity
|
||||
if (playlist.segments[0]) {
|
||||
playlist.segments[0].discontinuity = true;
|
||||
}
|
||||
|
||||
acc[name].segments.push(...playlist.segments);
|
||||
} // bubble up contentProtection, this assumes all DRM content
|
||||
// has the same contentProtection
|
||||
|
||||
|
||||
if (playlist.attributes.contentProtection) {
|
||||
acc[name].attributes.contentProtection = playlist.attributes.contentProtection;
|
||||
}
|
||||
// Break out playlists into groups based on their baseUrl
|
||||
const playlistsByBaseUrl = playlists.reduce(function (acc, cur) {
|
||||
if (!acc[cur.attributes.baseUrl]) {
|
||||
acc[cur.attributes.baseUrl] = [];
|
||||
}
|
||||
|
||||
acc[name].attributes.timelineStarts.push({
|
||||
// Although they represent the same number, it's important to have both to make it
|
||||
// compatible with HLS potentially having a similar attribute.
|
||||
start: playlist.attributes.periodStart,
|
||||
timeline: playlist.attributes.periodStart
|
||||
});
|
||||
acc[cur.attributes.baseUrl].push(cur);
|
||||
return acc;
|
||||
}, {}));
|
||||
return mergedPlaylists.map(playlist => {
|
||||
}, {});
|
||||
let allPlaylists = [];
|
||||
Object.values(playlistsByBaseUrl).forEach(playlistGroup => {
|
||||
const mergedPlaylists = values(playlistGroup.reduce((acc, playlist) => {
|
||||
// assuming playlist IDs are the same across periods
|
||||
// TODO: handle multiperiod where representation sets are not the same
|
||||
// across periods
|
||||
const name = playlist.attributes.id + (playlist.attributes.lang || '');
|
||||
|
||||
if (!acc[name]) {
|
||||
// First Period
|
||||
acc[name] = playlist;
|
||||
acc[name].attributes.timelineStarts = [];
|
||||
} else {
|
||||
// Subsequent Periods
|
||||
if (playlist.segments) {
|
||||
// first segment of subsequent periods signal a discontinuity
|
||||
if (playlist.segments[0]) {
|
||||
playlist.segments[0].discontinuity = true;
|
||||
}
|
||||
|
||||
acc[name].segments.push(...playlist.segments);
|
||||
} // bubble up contentProtection, this assumes all DRM content
|
||||
// has the same contentProtection
|
||||
|
||||
|
||||
if (playlist.attributes.contentProtection) {
|
||||
acc[name].attributes.contentProtection = playlist.attributes.contentProtection;
|
||||
}
|
||||
}
|
||||
|
||||
acc[name].attributes.timelineStarts.push({
|
||||
// Although they represent the same number, it's important to have both to make it
|
||||
// compatible with HLS potentially having a similar attribute.
|
||||
start: playlist.attributes.periodStart,
|
||||
timeline: playlist.attributes.periodStart
|
||||
});
|
||||
return acc;
|
||||
}, {}));
|
||||
allPlaylists = allPlaylists.concat(mergedPlaylists);
|
||||
});
|
||||
return allPlaylists.map(playlist => {
|
||||
playlist.discontinuityStarts = findIndexes(playlist.segments || [], 'discontinuity');
|
||||
return playlist;
|
||||
});
|
||||
|
@ -838,7 +852,7 @@ const formatAudioPlaylist = ({
|
|||
uri: '',
|
||||
endList: attributes.type === 'static',
|
||||
timeline: attributes.periodStart,
|
||||
resolvedUri: '',
|
||||
resolvedUri: attributes.baseUrl || '',
|
||||
targetDuration: attributes.duration,
|
||||
discontinuitySequence,
|
||||
discontinuityStarts,
|
||||
|
@ -851,6 +865,10 @@ const formatAudioPlaylist = ({
|
|||
playlist.contentProtection = attributes.contentProtection;
|
||||
}
|
||||
|
||||
if (attributes.serviceLocation) {
|
||||
playlist.attributes.serviceLocation = attributes.serviceLocation;
|
||||
}
|
||||
|
||||
if (sidx) {
|
||||
playlist.sidx = sidx;
|
||||
}
|
||||
|
@ -892,7 +910,7 @@ const formatVttPlaylist = ({
|
|||
m3u8Attributes.CODECS = attributes.codecs;
|
||||
}
|
||||
|
||||
return {
|
||||
const vttPlaylist = {
|
||||
attributes: m3u8Attributes,
|
||||
uri: '',
|
||||
endList: attributes.type === 'static',
|
||||
|
@ -905,6 +923,12 @@ const formatVttPlaylist = ({
|
|||
mediaSequence,
|
||||
segments
|
||||
};
|
||||
|
||||
if (attributes.serviceLocation) {
|
||||
vttPlaylist.attributes.serviceLocation = attributes.serviceLocation;
|
||||
}
|
||||
|
||||
return vttPlaylist;
|
||||
};
|
||||
const organizeAudioPlaylists = (playlists, sidxMapping = {}, isAudioOnly = false) => {
|
||||
let mainPlaylist;
|
||||
|
@ -1019,7 +1043,7 @@ const formatVideoPlaylist = ({
|
|||
uri: '',
|
||||
endList: attributes.type === 'static',
|
||||
timeline: attributes.periodStart,
|
||||
resolvedUri: '',
|
||||
resolvedUri: attributes.baseUrl || '',
|
||||
targetDuration: attributes.duration,
|
||||
discontinuityStarts,
|
||||
timelineStarts: attributes.timelineStarts,
|
||||
|
@ -1034,6 +1058,10 @@ const formatVideoPlaylist = ({
|
|||
playlist.contentProtection = attributes.contentProtection;
|
||||
}
|
||||
|
||||
if (attributes.serviceLocation) {
|
||||
playlist.attributes.serviceLocation = attributes.serviceLocation;
|
||||
}
|
||||
|
||||
if (sidx) {
|
||||
playlist.sidx = sidx;
|
||||
}
|
||||
|
@ -1126,6 +1154,7 @@ const flattenMediaGroupPlaylists = mediaGroupObject => {
|
|||
const toM3u8 = ({
|
||||
dashPlaylists,
|
||||
locations,
|
||||
contentSteering,
|
||||
sidxMapping = {},
|
||||
previousManifest,
|
||||
eventStream
|
||||
|
@ -1169,6 +1198,10 @@ const toM3u8 = ({
|
|||
manifest.locations = locations;
|
||||
}
|
||||
|
||||
if (contentSteering) {
|
||||
manifest.contentSteering = contentSteering;
|
||||
}
|
||||
|
||||
if (type === 'dynamic') {
|
||||
manifest.suggestedPresentationDelay = suggestedPresentationDelay;
|
||||
}
|
||||
|
@ -2003,22 +2036,33 @@ const keySystemsMap = {
|
|||
/**
|
||||
* Builds a list of urls that is the product of the reference urls and BaseURL values
|
||||
*
|
||||
* @param {string[]} referenceUrls
|
||||
* List of reference urls to resolve to
|
||||
* @param {Object[]} references
|
||||
* List of objects containing the reference URL as well as its attributes
|
||||
* @param {Node[]} baseUrlElements
|
||||
* List of BaseURL nodes from the mpd
|
||||
* @return {string[]}
|
||||
* List of resolved urls
|
||||
* @return {Object[]}
|
||||
* List of objects with resolved urls and attributes
|
||||
*/
|
||||
|
||||
const buildBaseUrls = (referenceUrls, baseUrlElements) => {
|
||||
const buildBaseUrls = (references, baseUrlElements) => {
|
||||
if (!baseUrlElements.length) {
|
||||
return referenceUrls;
|
||||
return references;
|
||||
}
|
||||
|
||||
return flatten(referenceUrls.map(function (reference) {
|
||||
return flatten(references.map(function (reference) {
|
||||
return baseUrlElements.map(function (baseUrlElement) {
|
||||
return resolveUrl__default['default'](reference, getContent(baseUrlElement));
|
||||
const initialBaseUrl = getContent(baseUrlElement);
|
||||
const resolvedBaseUrl = resolveUrl__default['default'](reference.baseUrl, initialBaseUrl);
|
||||
const finalBaseUrl = merge(parseAttributes(baseUrlElement), {
|
||||
baseUrl: resolvedBaseUrl
|
||||
}); // If the URL is resolved, we want to get the serviceLocation from the reference
|
||||
// assuming there is no serviceLocation on the initialBaseUrl
|
||||
|
||||
if (resolvedBaseUrl !== initialBaseUrl && !finalBaseUrl.serviceLocation && reference.serviceLocation) {
|
||||
finalBaseUrl.serviceLocation = reference.serviceLocation;
|
||||
}
|
||||
|
||||
return finalBaseUrl;
|
||||
});
|
||||
}));
|
||||
};
|
||||
|
@ -2120,8 +2164,9 @@ const getSegmentInformation = adaptationSet => {
|
|||
*
|
||||
* @param {Object} adaptationSetAttributes
|
||||
* Contains attributes inherited by the AdaptationSet
|
||||
* @param {string[]} adaptationSetBaseUrls
|
||||
* Contains list of resolved base urls inherited by the AdaptationSet
|
||||
* @param {Object[]} adaptationSetBaseUrls
|
||||
* List of objects containing resolved base URLs and attributes
|
||||
* inherited by the AdaptationSet
|
||||
* @param {SegmentInformation} adaptationSetSegmentInfo
|
||||
* Contains Segment information for the AdaptationSet
|
||||
* @return {inheritBaseUrlsCallback}
|
||||
|
@ -2136,9 +2181,7 @@ const inheritBaseUrls = (adaptationSetAttributes, adaptationSetBaseUrls, adaptat
|
|||
return repBaseUrls.map(baseUrl => {
|
||||
return {
|
||||
segmentInfo: merge(adaptationSetSegmentInfo, representationSegmentInfo),
|
||||
attributes: merge(attributes, {
|
||||
baseUrl
|
||||
})
|
||||
attributes: merge(attributes, baseUrl)
|
||||
};
|
||||
});
|
||||
};
|
||||
|
@ -2305,8 +2348,9 @@ const toEventStream = period => {
|
|||
*
|
||||
* @param {Object} periodAttributes
|
||||
* Contains attributes inherited by the Period
|
||||
* @param {string[]} periodBaseUrls
|
||||
* Contains list of resolved base urls inherited by the Period
|
||||
* @param {Object[]} periodBaseUrls
|
||||
* Contains list of objects with resolved base urls and attributes
|
||||
* inherited by the Period
|
||||
* @param {string[]} periodSegmentInfo
|
||||
* Contains Segment Information at the period level
|
||||
* @return {toRepresentationsCallback}
|
||||
|
@ -2382,8 +2426,9 @@ const toRepresentations = (periodAttributes, periodBaseUrls, periodSegmentInfo)
|
|||
*
|
||||
* @param {Object} mpdAttributes
|
||||
* Contains attributes inherited by the mpd
|
||||
* @param {string[]} mpdBaseUrls
|
||||
* Contains list of resolved base urls inherited by the mpd
|
||||
* @param {Object[]} mpdBaseUrls
|
||||
* Contains list of objects with resolved base urls and attributes
|
||||
* inherited by the mpd
|
||||
* @return {toAdaptationSetsCallback}
|
||||
* Callback map function
|
||||
*/
|
||||
|
@ -2402,6 +2447,43 @@ const toAdaptationSets = (mpdAttributes, mpdBaseUrls) => (period, index) => {
|
|||
const periodSegmentInfo = getSegmentInformation(period.node);
|
||||
return flatten(adaptationSets.map(toRepresentations(periodAttributes, periodBaseUrls, periodSegmentInfo)));
|
||||
};
|
||||
/**
|
||||
* Tranforms an array of content steering nodes into an object
|
||||
* containing CDN content steering information from the MPD manifest.
|
||||
*
|
||||
* For more information on the DASH spec for Content Steering parsing, see:
|
||||
* https://dashif.org/docs/DASH-IF-CTS-00XX-Content-Steering-Community-Review.pdf
|
||||
*
|
||||
* @param {Node[]} contentSteeringNodes
|
||||
* Content steering nodes
|
||||
* @param {Function} eventHandler
|
||||
* The event handler passed into the parser options to handle warnings
|
||||
* @return {Object}
|
||||
* Object containing content steering data
|
||||
*/
|
||||
|
||||
const generateContentSteeringInformation = (contentSteeringNodes, eventHandler) => {
|
||||
// If there are more than one ContentSteering tags, throw an error
|
||||
if (contentSteeringNodes.length > 1) {
|
||||
eventHandler({
|
||||
type: 'warn',
|
||||
message: 'The MPD manifest should contain no more than one ContentSteering tag'
|
||||
});
|
||||
} // Return a null value if there are no ContentSteering tags
|
||||
|
||||
|
||||
if (!contentSteeringNodes.length) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const infoFromContentSteeringTag = merge({
|
||||
serverURL: getContent(contentSteeringNodes[0])
|
||||
}, parseAttributes(contentSteeringNodes[0])); // Converts `queryBeforeStart` to a boolean, as well as setting the default value
|
||||
// to `false` if it doesn't exist
|
||||
|
||||
infoFromContentSteeringTag.queryBeforeStart = infoFromContentSteeringTag.queryBeforeStart === 'true';
|
||||
return infoFromContentSteeringTag;
|
||||
};
|
||||
/**
|
||||
* Gets Period@start property for a given period.
|
||||
*
|
||||
|
@ -2481,7 +2563,14 @@ const inheritAttributes = (mpd, options = {}) => {
|
|||
const {
|
||||
manifestUri = '',
|
||||
NOW = Date.now(),
|
||||
clientOffset = 0
|
||||
clientOffset = 0,
|
||||
// TODO: For now, we are expecting an eventHandler callback function
|
||||
// to be passed into the mpd parser as an option.
|
||||
// In the future, we should enable stream parsing by using the Stream class from vhs-utils.
|
||||
// This will support new features including a standardized event handler.
|
||||
// See the m3u8 parser for examples of how stream parsing is currently used for HLS parsing.
|
||||
// https://github.com/videojs/vhs-utils/blob/88d6e10c631e57a5af02c5a62bc7376cd456b4f5/src/stream.js#L9
|
||||
eventHandler = function () {}
|
||||
} = options;
|
||||
const periodNodes = findChildren(mpd, 'Period');
|
||||
|
||||
|
@ -2491,7 +2580,10 @@ const inheritAttributes = (mpd, options = {}) => {
|
|||
|
||||
const locations = findChildren(mpd, 'Location');
|
||||
const mpdAttributes = parseAttributes(mpd);
|
||||
const mpdBaseUrls = buildBaseUrls([manifestUri], findChildren(mpd, 'BaseURL')); // See DASH spec section 5.3.1.2, Semantics of MPD element. Default type to 'static'.
|
||||
const mpdBaseUrls = buildBaseUrls([{
|
||||
baseUrl: manifestUri
|
||||
}], findChildren(mpd, 'BaseURL'));
|
||||
const contentSteeringNodes = findChildren(mpd, 'ContentSteering'); // See DASH spec section 5.3.1.2, Semantics of MPD element. Default type to 'static'.
|
||||
|
||||
mpdAttributes.type = mpdAttributes.type || 'static';
|
||||
mpdAttributes.sourceDuration = mpdAttributes.mediaPresentationDuration || 0;
|
||||
|
@ -2524,6 +2616,14 @@ const inheritAttributes = (mpd, options = {}) => {
|
|||
});
|
||||
return {
|
||||
locations: mpdAttributes.locations,
|
||||
contentSteeringInfo: generateContentSteeringInformation(contentSteeringNodes, eventHandler),
|
||||
// TODO: There are occurences where this `representationInfo` array contains undesired
|
||||
// duplicates. This generally occurs when there are multiple BaseURL nodes that are
|
||||
// direct children of the MPD node. When we attempt to resolve URLs from a combination of the
|
||||
// parent BaseURL and a child BaseURL, and the value does not resolve,
|
||||
// we end up returning the child BaseURL multiple times.
|
||||
// We need to determine a way to remove these duplicates in a safe way.
|
||||
// See: https://github.com/videojs/mpd-parser/pull/17#discussion_r162750527
|
||||
representationInfo: flatten(periods.map(toAdaptationSets(mpdAttributes, mpdBaseUrls))),
|
||||
eventStream: flatten(periods.map(toEventStream))
|
||||
};
|
||||
|
@ -2541,7 +2641,7 @@ const stringToMpdXml = manifestString => {
|
|||
try {
|
||||
xml = parser.parseFromString(manifestString, 'application/xml');
|
||||
mpd = xml && xml.documentElement.tagName === 'MPD' ? xml.documentElement : null;
|
||||
} catch (e) {// ie 11 throwsw on invalid xml
|
||||
} catch (e) {// ie 11 throws on invalid xml
|
||||
}
|
||||
|
||||
if (!mpd || mpd && mpd.getElementsByTagName('parsererror').length > 0) {
|
||||
|
@ -2619,6 +2719,7 @@ const parse = (manifestString, options = {}) => {
|
|||
return toM3u8({
|
||||
dashPlaylists: playlists,
|
||||
locations: parsedManifestInfo.locations,
|
||||
contentSteering: parsedManifestInfo.contentSteeringInfo,
|
||||
sidxMapping: options.sidxMapping,
|
||||
previousManifest: options.previousManifest,
|
||||
eventStream: parsedManifestInfo.eventStream
|
||||
|
|
219
node_modules/mpd-parser/dist/mpd-parser.es.js
generated
vendored
219
node_modules/mpd-parser/dist/mpd-parser.es.js
generated
vendored
|
@ -1,11 +1,11 @@
|
|||
/*! @name mpd-parser @version 1.1.1 @license Apache-2.0 */
|
||||
/*! @name mpd-parser @version 1.2.2 @license Apache-2.0 */
|
||||
import resolveUrl from '@videojs/vhs-utils/es/resolve-url';
|
||||
import window from 'global/window';
|
||||
import { forEachMediaGroup } from '@videojs/vhs-utils/es/media-groups';
|
||||
import decodeB64ToUint8Array from '@videojs/vhs-utils/es/decode-b64-to-uint8-array';
|
||||
import { DOMParser } from '@xmldom/xmldom';
|
||||
|
||||
var version = "1.1.1";
|
||||
var version = "1.2.2";
|
||||
|
||||
const isObject = obj => {
|
||||
return !!obj && typeof obj === 'object';
|
||||
|
@ -81,6 +81,7 @@ const union = (lists, keyFunction) => {
|
|||
|
||||
var errors = {
|
||||
INVALID_NUMBER_OF_PERIOD: 'INVALID_NUMBER_OF_PERIOD',
|
||||
INVALID_NUMBER_OF_CONTENT_STEERING: 'INVALID_NUMBER_OF_CONTENT_STEERING',
|
||||
DASH_EMPTY_MANIFEST: 'DASH_EMPTY_MANIFEST',
|
||||
DASH_INVALID_XML: 'DASH_INVALID_XML',
|
||||
NO_BASE_URL: 'NO_BASE_URL',
|
||||
|
@ -747,43 +748,56 @@ const positionManifestOnTimeline = ({
|
|||
const generateSidxKey = sidx => sidx && sidx.uri + '-' + byteRangeToString(sidx.byterange);
|
||||
|
||||
const mergeDiscontiguousPlaylists = playlists => {
|
||||
const mergedPlaylists = values(playlists.reduce((acc, playlist) => {
|
||||
// assuming playlist IDs are the same across periods
|
||||
// TODO: handle multiperiod where representation sets are not the same
|
||||
// across periods
|
||||
const name = playlist.attributes.id + (playlist.attributes.lang || '');
|
||||
|
||||
if (!acc[name]) {
|
||||
// First Period
|
||||
acc[name] = playlist;
|
||||
acc[name].attributes.timelineStarts = [];
|
||||
} else {
|
||||
// Subsequent Periods
|
||||
if (playlist.segments) {
|
||||
// first segment of subsequent periods signal a discontinuity
|
||||
if (playlist.segments[0]) {
|
||||
playlist.segments[0].discontinuity = true;
|
||||
}
|
||||
|
||||
acc[name].segments.push(...playlist.segments);
|
||||
} // bubble up contentProtection, this assumes all DRM content
|
||||
// has the same contentProtection
|
||||
|
||||
|
||||
if (playlist.attributes.contentProtection) {
|
||||
acc[name].attributes.contentProtection = playlist.attributes.contentProtection;
|
||||
}
|
||||
// Break out playlists into groups based on their baseUrl
|
||||
const playlistsByBaseUrl = playlists.reduce(function (acc, cur) {
|
||||
if (!acc[cur.attributes.baseUrl]) {
|
||||
acc[cur.attributes.baseUrl] = [];
|
||||
}
|
||||
|
||||
acc[name].attributes.timelineStarts.push({
|
||||
// Although they represent the same number, it's important to have both to make it
|
||||
// compatible with HLS potentially having a similar attribute.
|
||||
start: playlist.attributes.periodStart,
|
||||
timeline: playlist.attributes.periodStart
|
||||
});
|
||||
acc[cur.attributes.baseUrl].push(cur);
|
||||
return acc;
|
||||
}, {}));
|
||||
return mergedPlaylists.map(playlist => {
|
||||
}, {});
|
||||
let allPlaylists = [];
|
||||
Object.values(playlistsByBaseUrl).forEach(playlistGroup => {
|
||||
const mergedPlaylists = values(playlistGroup.reduce((acc, playlist) => {
|
||||
// assuming playlist IDs are the same across periods
|
||||
// TODO: handle multiperiod where representation sets are not the same
|
||||
// across periods
|
||||
const name = playlist.attributes.id + (playlist.attributes.lang || '');
|
||||
|
||||
if (!acc[name]) {
|
||||
// First Period
|
||||
acc[name] = playlist;
|
||||
acc[name].attributes.timelineStarts = [];
|
||||
} else {
|
||||
// Subsequent Periods
|
||||
if (playlist.segments) {
|
||||
// first segment of subsequent periods signal a discontinuity
|
||||
if (playlist.segments[0]) {
|
||||
playlist.segments[0].discontinuity = true;
|
||||
}
|
||||
|
||||
acc[name].segments.push(...playlist.segments);
|
||||
} // bubble up contentProtection, this assumes all DRM content
|
||||
// has the same contentProtection
|
||||
|
||||
|
||||
if (playlist.attributes.contentProtection) {
|
||||
acc[name].attributes.contentProtection = playlist.attributes.contentProtection;
|
||||
}
|
||||
}
|
||||
|
||||
acc[name].attributes.timelineStarts.push({
|
||||
// Although they represent the same number, it's important to have both to make it
|
||||
// compatible with HLS potentially having a similar attribute.
|
||||
start: playlist.attributes.periodStart,
|
||||
timeline: playlist.attributes.periodStart
|
||||
});
|
||||
return acc;
|
||||
}, {}));
|
||||
allPlaylists = allPlaylists.concat(mergedPlaylists);
|
||||
});
|
||||
return allPlaylists.map(playlist => {
|
||||
playlist.discontinuityStarts = findIndexes(playlist.segments || [], 'discontinuity');
|
||||
return playlist;
|
||||
});
|
||||
|
@ -828,7 +842,7 @@ const formatAudioPlaylist = ({
|
|||
uri: '',
|
||||
endList: attributes.type === 'static',
|
||||
timeline: attributes.periodStart,
|
||||
resolvedUri: '',
|
||||
resolvedUri: attributes.baseUrl || '',
|
||||
targetDuration: attributes.duration,
|
||||
discontinuitySequence,
|
||||
discontinuityStarts,
|
||||
|
@ -841,6 +855,10 @@ const formatAudioPlaylist = ({
|
|||
playlist.contentProtection = attributes.contentProtection;
|
||||
}
|
||||
|
||||
if (attributes.serviceLocation) {
|
||||
playlist.attributes.serviceLocation = attributes.serviceLocation;
|
||||
}
|
||||
|
||||
if (sidx) {
|
||||
playlist.sidx = sidx;
|
||||
}
|
||||
|
@ -882,7 +900,7 @@ const formatVttPlaylist = ({
|
|||
m3u8Attributes.CODECS = attributes.codecs;
|
||||
}
|
||||
|
||||
return {
|
||||
const vttPlaylist = {
|
||||
attributes: m3u8Attributes,
|
||||
uri: '',
|
||||
endList: attributes.type === 'static',
|
||||
|
@ -895,6 +913,12 @@ const formatVttPlaylist = ({
|
|||
mediaSequence,
|
||||
segments
|
||||
};
|
||||
|
||||
if (attributes.serviceLocation) {
|
||||
vttPlaylist.attributes.serviceLocation = attributes.serviceLocation;
|
||||
}
|
||||
|
||||
return vttPlaylist;
|
||||
};
|
||||
const organizeAudioPlaylists = (playlists, sidxMapping = {}, isAudioOnly = false) => {
|
||||
let mainPlaylist;
|
||||
|
@ -1009,7 +1033,7 @@ const formatVideoPlaylist = ({
|
|||
uri: '',
|
||||
endList: attributes.type === 'static',
|
||||
timeline: attributes.periodStart,
|
||||
resolvedUri: '',
|
||||
resolvedUri: attributes.baseUrl || '',
|
||||
targetDuration: attributes.duration,
|
||||
discontinuityStarts,
|
||||
timelineStarts: attributes.timelineStarts,
|
||||
|
@ -1024,6 +1048,10 @@ const formatVideoPlaylist = ({
|
|||
playlist.contentProtection = attributes.contentProtection;
|
||||
}
|
||||
|
||||
if (attributes.serviceLocation) {
|
||||
playlist.attributes.serviceLocation = attributes.serviceLocation;
|
||||
}
|
||||
|
||||
if (sidx) {
|
||||
playlist.sidx = sidx;
|
||||
}
|
||||
|
@ -1116,6 +1144,7 @@ const flattenMediaGroupPlaylists = mediaGroupObject => {
|
|||
const toM3u8 = ({
|
||||
dashPlaylists,
|
||||
locations,
|
||||
contentSteering,
|
||||
sidxMapping = {},
|
||||
previousManifest,
|
||||
eventStream
|
||||
|
@ -1159,6 +1188,10 @@ const toM3u8 = ({
|
|||
manifest.locations = locations;
|
||||
}
|
||||
|
||||
if (contentSteering) {
|
||||
manifest.contentSteering = contentSteering;
|
||||
}
|
||||
|
||||
if (type === 'dynamic') {
|
||||
manifest.suggestedPresentationDelay = suggestedPresentationDelay;
|
||||
}
|
||||
|
@ -1993,22 +2026,33 @@ const keySystemsMap = {
|
|||
/**
|
||||
* Builds a list of urls that is the product of the reference urls and BaseURL values
|
||||
*
|
||||
* @param {string[]} referenceUrls
|
||||
* List of reference urls to resolve to
|
||||
* @param {Object[]} references
|
||||
* List of objects containing the reference URL as well as its attributes
|
||||
* @param {Node[]} baseUrlElements
|
||||
* List of BaseURL nodes from the mpd
|
||||
* @return {string[]}
|
||||
* List of resolved urls
|
||||
* @return {Object[]}
|
||||
* List of objects with resolved urls and attributes
|
||||
*/
|
||||
|
||||
const buildBaseUrls = (referenceUrls, baseUrlElements) => {
|
||||
const buildBaseUrls = (references, baseUrlElements) => {
|
||||
if (!baseUrlElements.length) {
|
||||
return referenceUrls;
|
||||
return references;
|
||||
}
|
||||
|
||||
return flatten(referenceUrls.map(function (reference) {
|
||||
return flatten(references.map(function (reference) {
|
||||
return baseUrlElements.map(function (baseUrlElement) {
|
||||
return resolveUrl(reference, getContent(baseUrlElement));
|
||||
const initialBaseUrl = getContent(baseUrlElement);
|
||||
const resolvedBaseUrl = resolveUrl(reference.baseUrl, initialBaseUrl);
|
||||
const finalBaseUrl = merge(parseAttributes(baseUrlElement), {
|
||||
baseUrl: resolvedBaseUrl
|
||||
}); // If the URL is resolved, we want to get the serviceLocation from the reference
|
||||
// assuming there is no serviceLocation on the initialBaseUrl
|
||||
|
||||
if (resolvedBaseUrl !== initialBaseUrl && !finalBaseUrl.serviceLocation && reference.serviceLocation) {
|
||||
finalBaseUrl.serviceLocation = reference.serviceLocation;
|
||||
}
|
||||
|
||||
return finalBaseUrl;
|
||||
});
|
||||
}));
|
||||
};
|
||||
|
@ -2110,8 +2154,9 @@ const getSegmentInformation = adaptationSet => {
|
|||
*
|
||||
* @param {Object} adaptationSetAttributes
|
||||
* Contains attributes inherited by the AdaptationSet
|
||||
* @param {string[]} adaptationSetBaseUrls
|
||||
* Contains list of resolved base urls inherited by the AdaptationSet
|
||||
* @param {Object[]} adaptationSetBaseUrls
|
||||
* List of objects containing resolved base URLs and attributes
|
||||
* inherited by the AdaptationSet
|
||||
* @param {SegmentInformation} adaptationSetSegmentInfo
|
||||
* Contains Segment information for the AdaptationSet
|
||||
* @return {inheritBaseUrlsCallback}
|
||||
|
@ -2126,9 +2171,7 @@ const inheritBaseUrls = (adaptationSetAttributes, adaptationSetBaseUrls, adaptat
|
|||
return repBaseUrls.map(baseUrl => {
|
||||
return {
|
||||
segmentInfo: merge(adaptationSetSegmentInfo, representationSegmentInfo),
|
||||
attributes: merge(attributes, {
|
||||
baseUrl
|
||||
})
|
||||
attributes: merge(attributes, baseUrl)
|
||||
};
|
||||
});
|
||||
};
|
||||
|
@ -2295,8 +2338,9 @@ const toEventStream = period => {
|
|||
*
|
||||
* @param {Object} periodAttributes
|
||||
* Contains attributes inherited by the Period
|
||||
* @param {string[]} periodBaseUrls
|
||||
* Contains list of resolved base urls inherited by the Period
|
||||
* @param {Object[]} periodBaseUrls
|
||||
* Contains list of objects with resolved base urls and attributes
|
||||
* inherited by the Period
|
||||
* @param {string[]} periodSegmentInfo
|
||||
* Contains Segment Information at the period level
|
||||
* @return {toRepresentationsCallback}
|
||||
|
@ -2372,8 +2416,9 @@ const toRepresentations = (periodAttributes, periodBaseUrls, periodSegmentInfo)
|
|||
*
|
||||
* @param {Object} mpdAttributes
|
||||
* Contains attributes inherited by the mpd
|
||||
* @param {string[]} mpdBaseUrls
|
||||
* Contains list of resolved base urls inherited by the mpd
|
||||
* @param {Object[]} mpdBaseUrls
|
||||
* Contains list of objects with resolved base urls and attributes
|
||||
* inherited by the mpd
|
||||
* @return {toAdaptationSetsCallback}
|
||||
* Callback map function
|
||||
*/
|
||||
|
@ -2392,6 +2437,43 @@ const toAdaptationSets = (mpdAttributes, mpdBaseUrls) => (period, index) => {
|
|||
const periodSegmentInfo = getSegmentInformation(period.node);
|
||||
return flatten(adaptationSets.map(toRepresentations(periodAttributes, periodBaseUrls, periodSegmentInfo)));
|
||||
};
|
||||
/**
|
||||
* Tranforms an array of content steering nodes into an object
|
||||
* containing CDN content steering information from the MPD manifest.
|
||||
*
|
||||
* For more information on the DASH spec for Content Steering parsing, see:
|
||||
* https://dashif.org/docs/DASH-IF-CTS-00XX-Content-Steering-Community-Review.pdf
|
||||
*
|
||||
* @param {Node[]} contentSteeringNodes
|
||||
* Content steering nodes
|
||||
* @param {Function} eventHandler
|
||||
* The event handler passed into the parser options to handle warnings
|
||||
* @return {Object}
|
||||
* Object containing content steering data
|
||||
*/
|
||||
|
||||
const generateContentSteeringInformation = (contentSteeringNodes, eventHandler) => {
|
||||
// If there are more than one ContentSteering tags, throw an error
|
||||
if (contentSteeringNodes.length > 1) {
|
||||
eventHandler({
|
||||
type: 'warn',
|
||||
message: 'The MPD manifest should contain no more than one ContentSteering tag'
|
||||
});
|
||||
} // Return a null value if there are no ContentSteering tags
|
||||
|
||||
|
||||
if (!contentSteeringNodes.length) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const infoFromContentSteeringTag = merge({
|
||||
serverURL: getContent(contentSteeringNodes[0])
|
||||
}, parseAttributes(contentSteeringNodes[0])); // Converts `queryBeforeStart` to a boolean, as well as setting the default value
|
||||
// to `false` if it doesn't exist
|
||||
|
||||
infoFromContentSteeringTag.queryBeforeStart = infoFromContentSteeringTag.queryBeforeStart === 'true';
|
||||
return infoFromContentSteeringTag;
|
||||
};
|
||||
/**
|
||||
* Gets Period@start property for a given period.
|
||||
*
|
||||
|
@ -2471,7 +2553,14 @@ const inheritAttributes = (mpd, options = {}) => {
|
|||
const {
|
||||
manifestUri = '',
|
||||
NOW = Date.now(),
|
||||
clientOffset = 0
|
||||
clientOffset = 0,
|
||||
// TODO: For now, we are expecting an eventHandler callback function
|
||||
// to be passed into the mpd parser as an option.
|
||||
// In the future, we should enable stream parsing by using the Stream class from vhs-utils.
|
||||
// This will support new features including a standardized event handler.
|
||||
// See the m3u8 parser for examples of how stream parsing is currently used for HLS parsing.
|
||||
// https://github.com/videojs/vhs-utils/blob/88d6e10c631e57a5af02c5a62bc7376cd456b4f5/src/stream.js#L9
|
||||
eventHandler = function () {}
|
||||
} = options;
|
||||
const periodNodes = findChildren(mpd, 'Period');
|
||||
|
||||
|
@ -2481,7 +2570,10 @@ const inheritAttributes = (mpd, options = {}) => {
|
|||
|
||||
const locations = findChildren(mpd, 'Location');
|
||||
const mpdAttributes = parseAttributes(mpd);
|
||||
const mpdBaseUrls = buildBaseUrls([manifestUri], findChildren(mpd, 'BaseURL')); // See DASH spec section 5.3.1.2, Semantics of MPD element. Default type to 'static'.
|
||||
const mpdBaseUrls = buildBaseUrls([{
|
||||
baseUrl: manifestUri
|
||||
}], findChildren(mpd, 'BaseURL'));
|
||||
const contentSteeringNodes = findChildren(mpd, 'ContentSteering'); // See DASH spec section 5.3.1.2, Semantics of MPD element. Default type to 'static'.
|
||||
|
||||
mpdAttributes.type = mpdAttributes.type || 'static';
|
||||
mpdAttributes.sourceDuration = mpdAttributes.mediaPresentationDuration || 0;
|
||||
|
@ -2514,6 +2606,14 @@ const inheritAttributes = (mpd, options = {}) => {
|
|||
});
|
||||
return {
|
||||
locations: mpdAttributes.locations,
|
||||
contentSteeringInfo: generateContentSteeringInformation(contentSteeringNodes, eventHandler),
|
||||
// TODO: There are occurences where this `representationInfo` array contains undesired
|
||||
// duplicates. This generally occurs when there are multiple BaseURL nodes that are
|
||||
// direct children of the MPD node. When we attempt to resolve URLs from a combination of the
|
||||
// parent BaseURL and a child BaseURL, and the value does not resolve,
|
||||
// we end up returning the child BaseURL multiple times.
|
||||
// We need to determine a way to remove these duplicates in a safe way.
|
||||
// See: https://github.com/videojs/mpd-parser/pull/17#discussion_r162750527
|
||||
representationInfo: flatten(periods.map(toAdaptationSets(mpdAttributes, mpdBaseUrls))),
|
||||
eventStream: flatten(periods.map(toEventStream))
|
||||
};
|
||||
|
@ -2531,7 +2631,7 @@ const stringToMpdXml = manifestString => {
|
|||
try {
|
||||
xml = parser.parseFromString(manifestString, 'application/xml');
|
||||
mpd = xml && xml.documentElement.tagName === 'MPD' ? xml.documentElement : null;
|
||||
} catch (e) {// ie 11 throwsw on invalid xml
|
||||
} catch (e) {// ie 11 throws on invalid xml
|
||||
}
|
||||
|
||||
if (!mpd || mpd && mpd.getElementsByTagName('parsererror').length > 0) {
|
||||
|
@ -2609,6 +2709,7 @@ const parse = (manifestString, options = {}) => {
|
|||
return toM3u8({
|
||||
dashPlaylists: playlists,
|
||||
locations: parsedManifestInfo.locations,
|
||||
contentSteering: parsedManifestInfo.contentSteeringInfo,
|
||||
sidxMapping: options.sidxMapping,
|
||||
previousManifest: options.previousManifest,
|
||||
eventStream: parsedManifestInfo.eventStream
|
||||
|
|
219
node_modules/mpd-parser/dist/mpd-parser.js
generated
vendored
219
node_modules/mpd-parser/dist/mpd-parser.js
generated
vendored
|
@ -1,11 +1,11 @@
|
|||
/*! @name mpd-parser @version 1.1.1 @license Apache-2.0 */
|
||||
/*! @name mpd-parser @version 1.2.2 @license Apache-2.0 */
|
||||
(function (global, factory) {
|
||||
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('@xmldom/xmldom')) :
|
||||
typeof define === 'function' && define.amd ? define(['exports', '@xmldom/xmldom'], factory) :
|
||||
(global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.mpdParser = {}, global.window));
|
||||
}(this, (function (exports, xmldom) { 'use strict';
|
||||
|
||||
var version = "1.1.1";
|
||||
var version = "1.2.2";
|
||||
|
||||
const isObject = obj => {
|
||||
return !!obj && typeof obj === 'object';
|
||||
|
@ -81,6 +81,7 @@
|
|||
|
||||
var errors = {
|
||||
INVALID_NUMBER_OF_PERIOD: 'INVALID_NUMBER_OF_PERIOD',
|
||||
INVALID_NUMBER_OF_CONTENT_STEERING: 'INVALID_NUMBER_OF_CONTENT_STEERING',
|
||||
DASH_EMPTY_MANIFEST: 'DASH_EMPTY_MANIFEST',
|
||||
DASH_INVALID_XML: 'DASH_INVALID_XML',
|
||||
NO_BASE_URL: 'NO_BASE_URL',
|
||||
|
@ -987,43 +988,56 @@
|
|||
const generateSidxKey = sidx => sidx && sidx.uri + '-' + byteRangeToString(sidx.byterange);
|
||||
|
||||
const mergeDiscontiguousPlaylists = playlists => {
|
||||
const mergedPlaylists = values(playlists.reduce((acc, playlist) => {
|
||||
// assuming playlist IDs are the same across periods
|
||||
// TODO: handle multiperiod where representation sets are not the same
|
||||
// across periods
|
||||
const name = playlist.attributes.id + (playlist.attributes.lang || '');
|
||||
|
||||
if (!acc[name]) {
|
||||
// First Period
|
||||
acc[name] = playlist;
|
||||
acc[name].attributes.timelineStarts = [];
|
||||
} else {
|
||||
// Subsequent Periods
|
||||
if (playlist.segments) {
|
||||
// first segment of subsequent periods signal a discontinuity
|
||||
if (playlist.segments[0]) {
|
||||
playlist.segments[0].discontinuity = true;
|
||||
}
|
||||
|
||||
acc[name].segments.push(...playlist.segments);
|
||||
} // bubble up contentProtection, this assumes all DRM content
|
||||
// has the same contentProtection
|
||||
|
||||
|
||||
if (playlist.attributes.contentProtection) {
|
||||
acc[name].attributes.contentProtection = playlist.attributes.contentProtection;
|
||||
}
|
||||
// Break out playlists into groups based on their baseUrl
|
||||
const playlistsByBaseUrl = playlists.reduce(function (acc, cur) {
|
||||
if (!acc[cur.attributes.baseUrl]) {
|
||||
acc[cur.attributes.baseUrl] = [];
|
||||
}
|
||||
|
||||
acc[name].attributes.timelineStarts.push({
|
||||
// Although they represent the same number, it's important to have both to make it
|
||||
// compatible with HLS potentially having a similar attribute.
|
||||
start: playlist.attributes.periodStart,
|
||||
timeline: playlist.attributes.periodStart
|
||||
});
|
||||
acc[cur.attributes.baseUrl].push(cur);
|
||||
return acc;
|
||||
}, {}));
|
||||
return mergedPlaylists.map(playlist => {
|
||||
}, {});
|
||||
let allPlaylists = [];
|
||||
Object.values(playlistsByBaseUrl).forEach(playlistGroup => {
|
||||
const mergedPlaylists = values(playlistGroup.reduce((acc, playlist) => {
|
||||
// assuming playlist IDs are the same across periods
|
||||
// TODO: handle multiperiod where representation sets are not the same
|
||||
// across periods
|
||||
const name = playlist.attributes.id + (playlist.attributes.lang || '');
|
||||
|
||||
if (!acc[name]) {
|
||||
// First Period
|
||||
acc[name] = playlist;
|
||||
acc[name].attributes.timelineStarts = [];
|
||||
} else {
|
||||
// Subsequent Periods
|
||||
if (playlist.segments) {
|
||||
// first segment of subsequent periods signal a discontinuity
|
||||
if (playlist.segments[0]) {
|
||||
playlist.segments[0].discontinuity = true;
|
||||
}
|
||||
|
||||
acc[name].segments.push(...playlist.segments);
|
||||
} // bubble up contentProtection, this assumes all DRM content
|
||||
// has the same contentProtection
|
||||
|
||||
|
||||
if (playlist.attributes.contentProtection) {
|
||||
acc[name].attributes.contentProtection = playlist.attributes.contentProtection;
|
||||
}
|
||||
}
|
||||
|
||||
acc[name].attributes.timelineStarts.push({
|
||||
// Although they represent the same number, it's important to have both to make it
|
||||
// compatible with HLS potentially having a similar attribute.
|
||||
start: playlist.attributes.periodStart,
|
||||
timeline: playlist.attributes.periodStart
|
||||
});
|
||||
return acc;
|
||||
}, {}));
|
||||
allPlaylists = allPlaylists.concat(mergedPlaylists);
|
||||
});
|
||||
return allPlaylists.map(playlist => {
|
||||
playlist.discontinuityStarts = findIndexes(playlist.segments || [], 'discontinuity');
|
||||
return playlist;
|
||||
});
|
||||
|
@ -1068,7 +1082,7 @@
|
|||
uri: '',
|
||||
endList: attributes.type === 'static',
|
||||
timeline: attributes.periodStart,
|
||||
resolvedUri: '',
|
||||
resolvedUri: attributes.baseUrl || '',
|
||||
targetDuration: attributes.duration,
|
||||
discontinuitySequence,
|
||||
discontinuityStarts,
|
||||
|
@ -1081,6 +1095,10 @@
|
|||
playlist.contentProtection = attributes.contentProtection;
|
||||
}
|
||||
|
||||
if (attributes.serviceLocation) {
|
||||
playlist.attributes.serviceLocation = attributes.serviceLocation;
|
||||
}
|
||||
|
||||
if (sidx) {
|
||||
playlist.sidx = sidx;
|
||||
}
|
||||
|
@ -1122,7 +1140,7 @@
|
|||
m3u8Attributes.CODECS = attributes.codecs;
|
||||
}
|
||||
|
||||
return {
|
||||
const vttPlaylist = {
|
||||
attributes: m3u8Attributes,
|
||||
uri: '',
|
||||
endList: attributes.type === 'static',
|
||||
|
@ -1135,6 +1153,12 @@
|
|||
mediaSequence,
|
||||
segments
|
||||
};
|
||||
|
||||
if (attributes.serviceLocation) {
|
||||
vttPlaylist.attributes.serviceLocation = attributes.serviceLocation;
|
||||
}
|
||||
|
||||
return vttPlaylist;
|
||||
};
|
||||
const organizeAudioPlaylists = (playlists, sidxMapping = {}, isAudioOnly = false) => {
|
||||
let mainPlaylist;
|
||||
|
@ -1249,7 +1273,7 @@
|
|||
uri: '',
|
||||
endList: attributes.type === 'static',
|
||||
timeline: attributes.periodStart,
|
||||
resolvedUri: '',
|
||||
resolvedUri: attributes.baseUrl || '',
|
||||
targetDuration: attributes.duration,
|
||||
discontinuityStarts,
|
||||
timelineStarts: attributes.timelineStarts,
|
||||
|
@ -1264,6 +1288,10 @@
|
|||
playlist.contentProtection = attributes.contentProtection;
|
||||
}
|
||||
|
||||
if (attributes.serviceLocation) {
|
||||
playlist.attributes.serviceLocation = attributes.serviceLocation;
|
||||
}
|
||||
|
||||
if (sidx) {
|
||||
playlist.sidx = sidx;
|
||||
}
|
||||
|
@ -1356,6 +1384,7 @@
|
|||
const toM3u8 = ({
|
||||
dashPlaylists,
|
||||
locations,
|
||||
contentSteering,
|
||||
sidxMapping = {},
|
||||
previousManifest,
|
||||
eventStream
|
||||
|
@ -1399,6 +1428,10 @@
|
|||
manifest.locations = locations;
|
||||
}
|
||||
|
||||
if (contentSteering) {
|
||||
manifest.contentSteering = contentSteering;
|
||||
}
|
||||
|
||||
if (type === 'dynamic') {
|
||||
manifest.suggestedPresentationDelay = suggestedPresentationDelay;
|
||||
}
|
||||
|
@ -2248,22 +2281,33 @@
|
|||
/**
|
||||
* Builds a list of urls that is the product of the reference urls and BaseURL values
|
||||
*
|
||||
* @param {string[]} referenceUrls
|
||||
* List of reference urls to resolve to
|
||||
* @param {Object[]} references
|
||||
* List of objects containing the reference URL as well as its attributes
|
||||
* @param {Node[]} baseUrlElements
|
||||
* List of BaseURL nodes from the mpd
|
||||
* @return {string[]}
|
||||
* List of resolved urls
|
||||
* @return {Object[]}
|
||||
* List of objects with resolved urls and attributes
|
||||
*/
|
||||
|
||||
const buildBaseUrls = (referenceUrls, baseUrlElements) => {
|
||||
const buildBaseUrls = (references, baseUrlElements) => {
|
||||
if (!baseUrlElements.length) {
|
||||
return referenceUrls;
|
||||
return references;
|
||||
}
|
||||
|
||||
return flatten(referenceUrls.map(function (reference) {
|
||||
return flatten(references.map(function (reference) {
|
||||
return baseUrlElements.map(function (baseUrlElement) {
|
||||
return resolveUrl(reference, getContent(baseUrlElement));
|
||||
const initialBaseUrl = getContent(baseUrlElement);
|
||||
const resolvedBaseUrl = resolveUrl(reference.baseUrl, initialBaseUrl);
|
||||
const finalBaseUrl = merge(parseAttributes(baseUrlElement), {
|
||||
baseUrl: resolvedBaseUrl
|
||||
}); // If the URL is resolved, we want to get the serviceLocation from the reference
|
||||
// assuming there is no serviceLocation on the initialBaseUrl
|
||||
|
||||
if (resolvedBaseUrl !== initialBaseUrl && !finalBaseUrl.serviceLocation && reference.serviceLocation) {
|
||||
finalBaseUrl.serviceLocation = reference.serviceLocation;
|
||||
}
|
||||
|
||||
return finalBaseUrl;
|
||||
});
|
||||
}));
|
||||
};
|
||||
|
@ -2365,8 +2409,9 @@
|
|||
*
|
||||
* @param {Object} adaptationSetAttributes
|
||||
* Contains attributes inherited by the AdaptationSet
|
||||
* @param {string[]} adaptationSetBaseUrls
|
||||
* Contains list of resolved base urls inherited by the AdaptationSet
|
||||
* @param {Object[]} adaptationSetBaseUrls
|
||||
* List of objects containing resolved base URLs and attributes
|
||||
* inherited by the AdaptationSet
|
||||
* @param {SegmentInformation} adaptationSetSegmentInfo
|
||||
* Contains Segment information for the AdaptationSet
|
||||
* @return {inheritBaseUrlsCallback}
|
||||
|
@ -2381,9 +2426,7 @@
|
|||
return repBaseUrls.map(baseUrl => {
|
||||
return {
|
||||
segmentInfo: merge(adaptationSetSegmentInfo, representationSegmentInfo),
|
||||
attributes: merge(attributes, {
|
||||
baseUrl
|
||||
})
|
||||
attributes: merge(attributes, baseUrl)
|
||||
};
|
||||
});
|
||||
};
|
||||
|
@ -2550,8 +2593,9 @@
|
|||
*
|
||||
* @param {Object} periodAttributes
|
||||
* Contains attributes inherited by the Period
|
||||
* @param {string[]} periodBaseUrls
|
||||
* Contains list of resolved base urls inherited by the Period
|
||||
* @param {Object[]} periodBaseUrls
|
||||
* Contains list of objects with resolved base urls and attributes
|
||||
* inherited by the Period
|
||||
* @param {string[]} periodSegmentInfo
|
||||
* Contains Segment Information at the period level
|
||||
* @return {toRepresentationsCallback}
|
||||
|
@ -2627,8 +2671,9 @@
|
|||
*
|
||||
* @param {Object} mpdAttributes
|
||||
* Contains attributes inherited by the mpd
|
||||
* @param {string[]} mpdBaseUrls
|
||||
* Contains list of resolved base urls inherited by the mpd
|
||||
* @param {Object[]} mpdBaseUrls
|
||||
* Contains list of objects with resolved base urls and attributes
|
||||
* inherited by the mpd
|
||||
* @return {toAdaptationSetsCallback}
|
||||
* Callback map function
|
||||
*/
|
||||
|
@ -2647,6 +2692,43 @@
|
|||
const periodSegmentInfo = getSegmentInformation(period.node);
|
||||
return flatten(adaptationSets.map(toRepresentations(periodAttributes, periodBaseUrls, periodSegmentInfo)));
|
||||
};
|
||||
/**
|
||||
* Tranforms an array of content steering nodes into an object
|
||||
* containing CDN content steering information from the MPD manifest.
|
||||
*
|
||||
* For more information on the DASH spec for Content Steering parsing, see:
|
||||
* https://dashif.org/docs/DASH-IF-CTS-00XX-Content-Steering-Community-Review.pdf
|
||||
*
|
||||
* @param {Node[]} contentSteeringNodes
|
||||
* Content steering nodes
|
||||
* @param {Function} eventHandler
|
||||
* The event handler passed into the parser options to handle warnings
|
||||
* @return {Object}
|
||||
* Object containing content steering data
|
||||
*/
|
||||
|
||||
const generateContentSteeringInformation = (contentSteeringNodes, eventHandler) => {
|
||||
// If there are more than one ContentSteering tags, throw an error
|
||||
if (contentSteeringNodes.length > 1) {
|
||||
eventHandler({
|
||||
type: 'warn',
|
||||
message: 'The MPD manifest should contain no more than one ContentSteering tag'
|
||||
});
|
||||
} // Return a null value if there are no ContentSteering tags
|
||||
|
||||
|
||||
if (!contentSteeringNodes.length) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const infoFromContentSteeringTag = merge({
|
||||
serverURL: getContent(contentSteeringNodes[0])
|
||||
}, parseAttributes(contentSteeringNodes[0])); // Converts `queryBeforeStart` to a boolean, as well as setting the default value
|
||||
// to `false` if it doesn't exist
|
||||
|
||||
infoFromContentSteeringTag.queryBeforeStart = infoFromContentSteeringTag.queryBeforeStart === 'true';
|
||||
return infoFromContentSteeringTag;
|
||||
};
|
||||
/**
|
||||
* Gets Period@start property for a given period.
|
||||
*
|
||||
|
@ -2726,7 +2808,14 @@
|
|||
const {
|
||||
manifestUri = '',
|
||||
NOW = Date.now(),
|
||||
clientOffset = 0
|
||||
clientOffset = 0,
|
||||
// TODO: For now, we are expecting an eventHandler callback function
|
||||
// to be passed into the mpd parser as an option.
|
||||
// In the future, we should enable stream parsing by using the Stream class from vhs-utils.
|
||||
// This will support new features including a standardized event handler.
|
||||
// See the m3u8 parser for examples of how stream parsing is currently used for HLS parsing.
|
||||
// https://github.com/videojs/vhs-utils/blob/88d6e10c631e57a5af02c5a62bc7376cd456b4f5/src/stream.js#L9
|
||||
eventHandler = function () {}
|
||||
} = options;
|
||||
const periodNodes = findChildren(mpd, 'Period');
|
||||
|
||||
|
@ -2736,7 +2825,10 @@
|
|||
|
||||
const locations = findChildren(mpd, 'Location');
|
||||
const mpdAttributes = parseAttributes(mpd);
|
||||
const mpdBaseUrls = buildBaseUrls([manifestUri], findChildren(mpd, 'BaseURL')); // See DASH spec section 5.3.1.2, Semantics of MPD element. Default type to 'static'.
|
||||
const mpdBaseUrls = buildBaseUrls([{
|
||||
baseUrl: manifestUri
|
||||
}], findChildren(mpd, 'BaseURL'));
|
||||
const contentSteeringNodes = findChildren(mpd, 'ContentSteering'); // See DASH spec section 5.3.1.2, Semantics of MPD element. Default type to 'static'.
|
||||
|
||||
mpdAttributes.type = mpdAttributes.type || 'static';
|
||||
mpdAttributes.sourceDuration = mpdAttributes.mediaPresentationDuration || 0;
|
||||
|
@ -2769,6 +2861,14 @@
|
|||
});
|
||||
return {
|
||||
locations: mpdAttributes.locations,
|
||||
contentSteeringInfo: generateContentSteeringInformation(contentSteeringNodes, eventHandler),
|
||||
// TODO: There are occurences where this `representationInfo` array contains undesired
|
||||
// duplicates. This generally occurs when there are multiple BaseURL nodes that are
|
||||
// direct children of the MPD node. When we attempt to resolve URLs from a combination of the
|
||||
// parent BaseURL and a child BaseURL, and the value does not resolve,
|
||||
// we end up returning the child BaseURL multiple times.
|
||||
// We need to determine a way to remove these duplicates in a safe way.
|
||||
// See: https://github.com/videojs/mpd-parser/pull/17#discussion_r162750527
|
||||
representationInfo: flatten(periods.map(toAdaptationSets(mpdAttributes, mpdBaseUrls))),
|
||||
eventStream: flatten(periods.map(toEventStream))
|
||||
};
|
||||
|
@ -2786,7 +2886,7 @@
|
|||
try {
|
||||
xml = parser.parseFromString(manifestString, 'application/xml');
|
||||
mpd = xml && xml.documentElement.tagName === 'MPD' ? xml.documentElement : null;
|
||||
} catch (e) {// ie 11 throwsw on invalid xml
|
||||
} catch (e) {// ie 11 throws on invalid xml
|
||||
}
|
||||
|
||||
if (!mpd || mpd && mpd.getElementsByTagName('parsererror').length > 0) {
|
||||
|
@ -2864,6 +2964,7 @@
|
|||
return toM3u8({
|
||||
dashPlaylists: playlists,
|
||||
locations: parsedManifestInfo.locations,
|
||||
contentSteering: parsedManifestInfo.contentSteeringInfo,
|
||||
sidxMapping: options.sidxMapping,
|
||||
previousManifest: options.previousManifest,
|
||||
eventStream: parsedManifestInfo.eventStream
|
||||
|
|
4
node_modules/mpd-parser/dist/mpd-parser.min.js
generated
vendored
4
node_modules/mpd-parser/dist/mpd-parser.min.js
generated
vendored
File diff suppressed because one or more lines are too long
4
node_modules/mpd-parser/package.json
generated
vendored
4
node_modules/mpd-parser/package.json
generated
vendored
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "mpd-parser",
|
||||
"version": "1.1.1",
|
||||
"version": "1.2.2",
|
||||
"description": "mpd parser",
|
||||
"main": "dist/mpd-parser.cjs.js",
|
||||
"module": "dist/mpd-parser.es.js",
|
||||
|
@ -67,7 +67,7 @@
|
|||
"karma": "^5.2.3",
|
||||
"rollup": "^2.38.0",
|
||||
"rollup-plugin-string": "^3.0.0",
|
||||
"sinon": "^9.2.3",
|
||||
"sinon": "^11.1.1",
|
||||
"videojs-generate-karma-config": "^8.0.1",
|
||||
"videojs-generate-rollup-config": "~7.0.0",
|
||||
"videojs-generator-verify": "~3.0.2",
|
||||
|
|
1
node_modules/mpd-parser/src/errors.js
generated
vendored
1
node_modules/mpd-parser/src/errors.js
generated
vendored
|
@ -1,5 +1,6 @@
|
|||
export default {
|
||||
INVALID_NUMBER_OF_PERIOD: 'INVALID_NUMBER_OF_PERIOD',
|
||||
INVALID_NUMBER_OF_CONTENT_STEERING: 'INVALID_NUMBER_OF_CONTENT_STEERING',
|
||||
DASH_EMPTY_MANIFEST: 'DASH_EMPTY_MANIFEST',
|
||||
DASH_INVALID_XML: 'DASH_INVALID_XML',
|
||||
NO_BASE_URL: 'NO_BASE_URL',
|
||||
|
|
1
node_modules/mpd-parser/src/index.js
generated
vendored
1
node_modules/mpd-parser/src/index.js
generated
vendored
|
@ -28,6 +28,7 @@ const parse = (manifestString, options = {}) => {
|
|||
return toM3u8({
|
||||
dashPlaylists: playlists,
|
||||
locations: parsedManifestInfo.locations,
|
||||
contentSteering: parsedManifestInfo.contentSteeringInfo,
|
||||
sidxMapping: options.sidxMapping,
|
||||
previousManifest: options.previousManifest,
|
||||
eventStream: parsedManifestInfo.eventStream
|
||||
|
|
102
node_modules/mpd-parser/src/inheritAttributes.js
generated
vendored
102
node_modules/mpd-parser/src/inheritAttributes.js
generated
vendored
|
@ -16,21 +16,35 @@ const keySystemsMap = {
|
|||
/**
|
||||
* Builds a list of urls that is the product of the reference urls and BaseURL values
|
||||
*
|
||||
* @param {string[]} referenceUrls
|
||||
* List of reference urls to resolve to
|
||||
* @param {Object[]} references
|
||||
* List of objects containing the reference URL as well as its attributes
|
||||
* @param {Node[]} baseUrlElements
|
||||
* List of BaseURL nodes from the mpd
|
||||
* @return {string[]}
|
||||
* List of resolved urls
|
||||
* @return {Object[]}
|
||||
* List of objects with resolved urls and attributes
|
||||
*/
|
||||
export const buildBaseUrls = (referenceUrls, baseUrlElements) => {
|
||||
export const buildBaseUrls = (references, baseUrlElements) => {
|
||||
if (!baseUrlElements.length) {
|
||||
return referenceUrls;
|
||||
return references;
|
||||
}
|
||||
|
||||
return flatten(referenceUrls.map(function(reference) {
|
||||
return flatten(references.map(function(reference) {
|
||||
return baseUrlElements.map(function(baseUrlElement) {
|
||||
return resolveUrl(reference, getContent(baseUrlElement));
|
||||
const initialBaseUrl = getContent(baseUrlElement);
|
||||
const resolvedBaseUrl = resolveUrl(reference.baseUrl, initialBaseUrl);
|
||||
|
||||
const finalBaseUrl = merge(
|
||||
parseAttributes(baseUrlElement),
|
||||
{ baseUrl: resolvedBaseUrl }
|
||||
);
|
||||
|
||||
// If the URL is resolved, we want to get the serviceLocation from the reference
|
||||
// assuming there is no serviceLocation on the initialBaseUrl
|
||||
if (resolvedBaseUrl !== initialBaseUrl && !finalBaseUrl.serviceLocation && reference.serviceLocation) {
|
||||
finalBaseUrl.serviceLocation = reference.serviceLocation;
|
||||
}
|
||||
|
||||
return finalBaseUrl;
|
||||
});
|
||||
}));
|
||||
};
|
||||
|
@ -140,8 +154,9 @@ export const getSegmentInformation = (adaptationSet) => {
|
|||
*
|
||||
* @param {Object} adaptationSetAttributes
|
||||
* Contains attributes inherited by the AdaptationSet
|
||||
* @param {string[]} adaptationSetBaseUrls
|
||||
* Contains list of resolved base urls inherited by the AdaptationSet
|
||||
* @param {Object[]} adaptationSetBaseUrls
|
||||
* List of objects containing resolved base URLs and attributes
|
||||
* inherited by the AdaptationSet
|
||||
* @param {SegmentInformation} adaptationSetSegmentInfo
|
||||
* Contains Segment information for the AdaptationSet
|
||||
* @return {inheritBaseUrlsCallback}
|
||||
|
@ -158,7 +173,7 @@ export const inheritBaseUrls =
|
|||
return repBaseUrls.map(baseUrl => {
|
||||
return {
|
||||
segmentInfo: merge(adaptationSetSegmentInfo, representationSegmentInfo),
|
||||
attributes: merge(attributes, { baseUrl })
|
||||
attributes: merge(attributes, baseUrl)
|
||||
};
|
||||
});
|
||||
};
|
||||
|
@ -340,8 +355,9 @@ export const toEventStream = (period) => {
|
|||
*
|
||||
* @param {Object} periodAttributes
|
||||
* Contains attributes inherited by the Period
|
||||
* @param {string[]} periodBaseUrls
|
||||
* Contains list of resolved base urls inherited by the Period
|
||||
* @param {Object[]} periodBaseUrls
|
||||
* Contains list of objects with resolved base urls and attributes
|
||||
* inherited by the Period
|
||||
* @param {string[]} periodSegmentInfo
|
||||
* Contains Segment Information at the period level
|
||||
* @return {toRepresentationsCallback}
|
||||
|
@ -421,8 +437,9 @@ export const toRepresentations =
|
|||
*
|
||||
* @param {Object} mpdAttributes
|
||||
* Contains attributes inherited by the mpd
|
||||
* @param {string[]} mpdBaseUrls
|
||||
* Contains list of resolved base urls inherited by the mpd
|
||||
* @param {Object[]} mpdBaseUrls
|
||||
* Contains list of objects with resolved base urls and attributes
|
||||
* inherited by the mpd
|
||||
* @return {toAdaptationSetsCallback}
|
||||
* Callback map function
|
||||
*/
|
||||
|
@ -441,6 +458,41 @@ export const toAdaptationSets = (mpdAttributes, mpdBaseUrls) => (period, index)
|
|||
return flatten(adaptationSets.map(toRepresentations(periodAttributes, periodBaseUrls, periodSegmentInfo)));
|
||||
};
|
||||
|
||||
/**
|
||||
* Tranforms an array of content steering nodes into an object
|
||||
* containing CDN content steering information from the MPD manifest.
|
||||
*
|
||||
* For more information on the DASH spec for Content Steering parsing, see:
|
||||
* https://dashif.org/docs/DASH-IF-CTS-00XX-Content-Steering-Community-Review.pdf
|
||||
*
|
||||
* @param {Node[]} contentSteeringNodes
|
||||
* Content steering nodes
|
||||
* @param {Function} eventHandler
|
||||
* The event handler passed into the parser options to handle warnings
|
||||
* @return {Object}
|
||||
* Object containing content steering data
|
||||
*/
|
||||
export const generateContentSteeringInformation = (contentSteeringNodes, eventHandler) => {
|
||||
// If there are more than one ContentSteering tags, throw an error
|
||||
if (contentSteeringNodes.length > 1) {
|
||||
eventHandler({ type: 'warn', message: 'The MPD manifest should contain no more than one ContentSteering tag' });
|
||||
}
|
||||
|
||||
// Return a null value if there are no ContentSteering tags
|
||||
if (!contentSteeringNodes.length) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const infoFromContentSteeringTag =
|
||||
merge({serverURL: getContent(contentSteeringNodes[0])}, parseAttributes(contentSteeringNodes[0]));
|
||||
|
||||
// Converts `queryBeforeStart` to a boolean, as well as setting the default value
|
||||
// to `false` if it doesn't exist
|
||||
infoFromContentSteeringTag.queryBeforeStart = (infoFromContentSteeringTag.queryBeforeStart === 'true');
|
||||
|
||||
return infoFromContentSteeringTag;
|
||||
};
|
||||
|
||||
/**
|
||||
* Gets Period@start property for a given period.
|
||||
*
|
||||
|
@ -518,7 +570,14 @@ export const inheritAttributes = (mpd, options = {}) => {
|
|||
const {
|
||||
manifestUri = '',
|
||||
NOW = Date.now(),
|
||||
clientOffset = 0
|
||||
clientOffset = 0,
|
||||
// TODO: For now, we are expecting an eventHandler callback function
|
||||
// to be passed into the mpd parser as an option.
|
||||
// In the future, we should enable stream parsing by using the Stream class from vhs-utils.
|
||||
// This will support new features including a standardized event handler.
|
||||
// See the m3u8 parser for examples of how stream parsing is currently used for HLS parsing.
|
||||
// https://github.com/videojs/vhs-utils/blob/88d6e10c631e57a5af02c5a62bc7376cd456b4f5/src/stream.js#L9
|
||||
eventHandler = function() {}
|
||||
} = options;
|
||||
const periodNodes = findChildren(mpd, 'Period');
|
||||
|
||||
|
@ -529,7 +588,8 @@ export const inheritAttributes = (mpd, options = {}) => {
|
|||
const locations = findChildren(mpd, 'Location');
|
||||
|
||||
const mpdAttributes = parseAttributes(mpd);
|
||||
const mpdBaseUrls = buildBaseUrls([ manifestUri ], findChildren(mpd, 'BaseURL'));
|
||||
const mpdBaseUrls = buildBaseUrls([{ baseUrl: manifestUri }], findChildren(mpd, 'BaseURL'));
|
||||
const contentSteeringNodes = findChildren(mpd, 'ContentSteering');
|
||||
|
||||
// See DASH spec section 5.3.1.2, Semantics of MPD element. Default type to 'static'.
|
||||
mpdAttributes.type = mpdAttributes.type || 'static';
|
||||
|
@ -567,6 +627,14 @@ export const inheritAttributes = (mpd, options = {}) => {
|
|||
|
||||
return {
|
||||
locations: mpdAttributes.locations,
|
||||
contentSteeringInfo: generateContentSteeringInformation(contentSteeringNodes, eventHandler),
|
||||
// TODO: There are occurences where this `representationInfo` array contains undesired
|
||||
// duplicates. This generally occurs when there are multiple BaseURL nodes that are
|
||||
// direct children of the MPD node. When we attempt to resolve URLs from a combination of the
|
||||
// parent BaseURL and a child BaseURL, and the value does not resolve,
|
||||
// we end up returning the child BaseURL multiple times.
|
||||
// We need to determine a way to remove these duplicates in a safe way.
|
||||
// See: https://github.com/videojs/mpd-parser/pull/17#discussion_r162750527
|
||||
representationInfo: flatten(periods.map(toAdaptationSets(mpdAttributes, mpdBaseUrls))),
|
||||
eventStream: flatten(periods.map(toEventStream))
|
||||
};
|
||||
|
|
2
node_modules/mpd-parser/src/stringToMpdXml.js
generated
vendored
2
node_modules/mpd-parser/src/stringToMpdXml.js
generated
vendored
|
@ -15,7 +15,7 @@ export const stringToMpdXml = (manifestString) => {
|
|||
mpd = xml && xml.documentElement.tagName === 'MPD' ?
|
||||
xml.documentElement : null;
|
||||
} catch (e) {
|
||||
// ie 11 throwsw on invalid xml
|
||||
// ie 11 throws on invalid xml
|
||||
}
|
||||
|
||||
if (!mpd || mpd &&
|
||||
|
|
111
node_modules/mpd-parser/src/toM3u8.js
generated
vendored
111
node_modules/mpd-parser/src/toM3u8.js
generated
vendored
|
@ -11,45 +11,62 @@ export const generateSidxKey = (sidx) => sidx &&
|
|||
sidx.uri + '-' + byteRangeToString(sidx.byterange);
|
||||
|
||||
const mergeDiscontiguousPlaylists = playlists => {
|
||||
const mergedPlaylists = values(playlists.reduce((acc, playlist) => {
|
||||
// assuming playlist IDs are the same across periods
|
||||
// TODO: handle multiperiod where representation sets are not the same
|
||||
// across periods
|
||||
const name = playlist.attributes.id + (playlist.attributes.lang || '');
|
||||
|
||||
if (!acc[name]) {
|
||||
// First Period
|
||||
acc[name] = playlist;
|
||||
acc[name].attributes.timelineStarts = [];
|
||||
} else {
|
||||
// Subsequent Periods
|
||||
if (playlist.segments) {
|
||||
// first segment of subsequent periods signal a discontinuity
|
||||
if (playlist.segments[0]) {
|
||||
playlist.segments[0].discontinuity = true;
|
||||
}
|
||||
acc[name].segments.push(...playlist.segments);
|
||||
}
|
||||
|
||||
// bubble up contentProtection, this assumes all DRM content
|
||||
// has the same contentProtection
|
||||
if (playlist.attributes.contentProtection) {
|
||||
acc[name].attributes.contentProtection =
|
||||
playlist.attributes.contentProtection;
|
||||
}
|
||||
// Break out playlists into groups based on their baseUrl
|
||||
const playlistsByBaseUrl = playlists.reduce(function(acc, cur) {
|
||||
if (!acc[cur.attributes.baseUrl]) {
|
||||
acc[cur.attributes.baseUrl] = [];
|
||||
}
|
||||
|
||||
acc[name].attributes.timelineStarts.push({
|
||||
// Although they represent the same number, it's important to have both to make it
|
||||
// compatible with HLS potentially having a similar attribute.
|
||||
start: playlist.attributes.periodStart,
|
||||
timeline: playlist.attributes.periodStart
|
||||
});
|
||||
acc[cur.attributes.baseUrl].push(cur);
|
||||
|
||||
return acc;
|
||||
}, {}));
|
||||
}, {});
|
||||
|
||||
return mergedPlaylists.map(playlist => {
|
||||
let allPlaylists = [];
|
||||
|
||||
Object.values(playlistsByBaseUrl).forEach((playlistGroup) => {
|
||||
const mergedPlaylists = values(playlistGroup.reduce((acc, playlist) => {
|
||||
// assuming playlist IDs are the same across periods
|
||||
// TODO: handle multiperiod where representation sets are not the same
|
||||
// across periods
|
||||
const name = playlist.attributes.id + (playlist.attributes.lang || '');
|
||||
|
||||
if (!acc[name]) {
|
||||
// First Period
|
||||
acc[name] = playlist;
|
||||
acc[name].attributes.timelineStarts = [];
|
||||
} else {
|
||||
// Subsequent Periods
|
||||
if (playlist.segments) {
|
||||
// first segment of subsequent periods signal a discontinuity
|
||||
if (playlist.segments[0]) {
|
||||
playlist.segments[0].discontinuity = true;
|
||||
}
|
||||
acc[name].segments.push(...playlist.segments);
|
||||
}
|
||||
|
||||
// bubble up contentProtection, this assumes all DRM content
|
||||
// has the same contentProtection
|
||||
if (playlist.attributes.contentProtection) {
|
||||
acc[name].attributes.contentProtection =
|
||||
playlist.attributes.contentProtection;
|
||||
}
|
||||
}
|
||||
|
||||
acc[name].attributes.timelineStarts.push({
|
||||
// Although they represent the same number, it's important to have both to make it
|
||||
// compatible with HLS potentially having a similar attribute.
|
||||
start: playlist.attributes.periodStart,
|
||||
timeline: playlist.attributes.periodStart
|
||||
});
|
||||
|
||||
return acc;
|
||||
}, {}));
|
||||
|
||||
allPlaylists = allPlaylists.concat(mergedPlaylists);
|
||||
});
|
||||
|
||||
return allPlaylists.map(playlist => {
|
||||
playlist.discontinuityStarts =
|
||||
findIndexes(playlist.segments || [], 'discontinuity');
|
||||
|
||||
|
@ -98,7 +115,7 @@ export const formatAudioPlaylist = ({
|
|||
uri: '',
|
||||
endList: attributes.type === 'static',
|
||||
timeline: attributes.periodStart,
|
||||
resolvedUri: '',
|
||||
resolvedUri: attributes.baseUrl || '',
|
||||
targetDuration: attributes.duration,
|
||||
discontinuitySequence,
|
||||
discontinuityStarts,
|
||||
|
@ -111,6 +128,10 @@ export const formatAudioPlaylist = ({
|
|||
playlist.contentProtection = attributes.contentProtection;
|
||||
}
|
||||
|
||||
if (attributes.serviceLocation) {
|
||||
playlist.attributes.serviceLocation = attributes.serviceLocation;
|
||||
}
|
||||
|
||||
if (sidx) {
|
||||
playlist.sidx = sidx;
|
||||
}
|
||||
|
@ -139,6 +160,7 @@ export const formatVttPlaylist = ({
|
|||
duration: attributes.sourceDuration,
|
||||
number: 0
|
||||
}];
|
||||
|
||||
// targetDuration should be the same duration as the only segment
|
||||
attributes.duration = attributes.sourceDuration;
|
||||
}
|
||||
|
@ -152,7 +174,7 @@ export const formatVttPlaylist = ({
|
|||
if (attributes.codecs) {
|
||||
m3u8Attributes.CODECS = attributes.codecs;
|
||||
}
|
||||
return {
|
||||
const vttPlaylist = {
|
||||
attributes: m3u8Attributes,
|
||||
uri: '',
|
||||
endList: attributes.type === 'static',
|
||||
|
@ -165,6 +187,12 @@ export const formatVttPlaylist = ({
|
|||
mediaSequence,
|
||||
segments
|
||||
};
|
||||
|
||||
if (attributes.serviceLocation) {
|
||||
vttPlaylist.attributes.serviceLocation = attributes.serviceLocation;
|
||||
}
|
||||
|
||||
return vttPlaylist;
|
||||
};
|
||||
|
||||
export const organizeAudioPlaylists = (playlists, sidxMapping = {}, isAudioOnly = false) => {
|
||||
|
@ -289,7 +317,7 @@ export const formatVideoPlaylist = ({
|
|||
uri: '',
|
||||
endList: attributes.type === 'static',
|
||||
timeline: attributes.periodStart,
|
||||
resolvedUri: '',
|
||||
resolvedUri: attributes.baseUrl || '',
|
||||
targetDuration: attributes.duration,
|
||||
discontinuityStarts,
|
||||
timelineStarts: attributes.timelineStarts,
|
||||
|
@ -304,6 +332,10 @@ export const formatVideoPlaylist = ({
|
|||
playlist.contentProtection = attributes.contentProtection;
|
||||
}
|
||||
|
||||
if (attributes.serviceLocation) {
|
||||
playlist.attributes.serviceLocation = attributes.serviceLocation;
|
||||
}
|
||||
|
||||
if (sidx) {
|
||||
playlist.sidx = sidx;
|
||||
}
|
||||
|
@ -392,6 +424,7 @@ export const flattenMediaGroupPlaylists = (mediaGroupObject) => {
|
|||
export const toM3u8 = ({
|
||||
dashPlaylists,
|
||||
locations,
|
||||
contentSteering,
|
||||
sidxMapping = {},
|
||||
previousManifest,
|
||||
eventStream
|
||||
|
@ -437,6 +470,10 @@ export const toM3u8 = ({
|
|||
manifest.locations = locations;
|
||||
}
|
||||
|
||||
if (contentSteering) {
|
||||
manifest.contentSteering = contentSteering;
|
||||
}
|
||||
|
||||
if (type === 'dynamic') {
|
||||
manifest.suggestedPresentationDelay = suggestedPresentationDelay;
|
||||
}
|
||||
|
|
460
node_modules/mpd-parser/test/inheritAttributes.test.js
generated
vendored
460
node_modules/mpd-parser/test/inheritAttributes.test.js
generated
vendored
|
@ -9,6 +9,7 @@ import {
|
|||
import { stringToMpdXml } from '../src/stringToMpdXml';
|
||||
import errors from '../src/errors';
|
||||
import QUnit from 'qunit';
|
||||
import { stub } from 'sinon';
|
||||
import { toPlaylists } from '../src/toPlaylists';
|
||||
import decodeB64ToUint8Array from '@videojs/vhs-utils/es/decode-b64-to-uint8-array';
|
||||
import { findChildren } from '../src/utils/xml';
|
||||
|
@ -16,23 +17,23 @@ import { findChildren } from '../src/utils/xml';
|
|||
QUnit.module('buildBaseUrls');
|
||||
|
||||
QUnit.test('returns reference urls when no BaseURL nodes', function(assert) {
|
||||
const reference = ['https://example.com/', 'https://foo.com/'];
|
||||
const reference = [{ baseUrl: 'https://example.com/' }, { baseUrl: 'https://foo.com/' }];
|
||||
|
||||
assert.deepEqual(buildBaseUrls(reference, []), reference, 'returns reference urls');
|
||||
});
|
||||
|
||||
QUnit.test('single reference url with single BaseURL node', function(assert) {
|
||||
const reference = ['https://example.com'];
|
||||
const reference = [{ baseUrl: 'https://example.com' }];
|
||||
const node = [{ textContent: 'bar/' }];
|
||||
const expected = ['https://example.com/bar/'];
|
||||
const expected = [{ baseUrl: 'https://example.com/bar/' }];
|
||||
|
||||
assert.deepEqual(buildBaseUrls(reference, node), expected, 'builds base url');
|
||||
});
|
||||
|
||||
QUnit.test('multiple reference urls with single BaseURL node', function(assert) {
|
||||
const reference = ['https://example.com/', 'https://foo.com/'];
|
||||
const reference = [{ baseUrl: 'https://example.com/' }, { baseUrl: 'https://foo.com/' }];
|
||||
const node = [{ textContent: 'bar/' }];
|
||||
const expected = ['https://example.com/bar/', 'https://foo.com/bar/'];
|
||||
const expected = [{ baseUrl: 'https://example.com/bar/' }, { baseUrl: 'https://foo.com/bar/' }];
|
||||
|
||||
assert.deepEqual(
|
||||
buildBaseUrls(reference, node), expected,
|
||||
|
@ -41,36 +42,38 @@ QUnit.test('multiple reference urls with single BaseURL node', function(assert)
|
|||
});
|
||||
|
||||
QUnit.test('multiple BaseURL nodes with single reference url', function(assert) {
|
||||
const reference = ['https://example.com/'];
|
||||
const reference = [{ baseUrl: 'https://example.com/' }];
|
||||
const nodes = [{ textContent: 'bar/' }, { textContent: 'baz/' }];
|
||||
const expected = ['https://example.com/bar/', 'https://example.com/baz/'];
|
||||
const expected = [{ baseUrl: 'https://example.com/bar/' }, { baseUrl: 'https://example.com/baz/' }];
|
||||
|
||||
assert.deepEqual(buildBaseUrls(reference, nodes), expected, 'base url for each node');
|
||||
});
|
||||
|
||||
QUnit.test('multiple reference urls with multiple BaseURL nodes', function(assert) {
|
||||
const reference = ['https://example.com/', 'https://foo.com/', 'http://example.com'];
|
||||
const reference = [
|
||||
{ baseUrl: 'https://example.com/' }, { baseUrl: 'https://foo.com/' }, { baseUrl: 'http://example.com' }
|
||||
];
|
||||
const nodes =
|
||||
[{ textContent: 'bar/' }, { textContent: 'baz/' }, { textContent: 'buzz/' }];
|
||||
const expected = [
|
||||
'https://example.com/bar/',
|
||||
'https://example.com/baz/',
|
||||
'https://example.com/buzz/',
|
||||
'https://foo.com/bar/',
|
||||
'https://foo.com/baz/',
|
||||
'https://foo.com/buzz/',
|
||||
'http://example.com/bar/',
|
||||
'http://example.com/baz/',
|
||||
'http://example.com/buzz/'
|
||||
{ baseUrl: 'https://example.com/bar/' },
|
||||
{ baseUrl: 'https://example.com/baz/' },
|
||||
{ baseUrl: 'https://example.com/buzz/' },
|
||||
{ baseUrl: 'https://foo.com/bar/' },
|
||||
{ baseUrl: 'https://foo.com/baz/' },
|
||||
{ baseUrl: 'https://foo.com/buzz/' },
|
||||
{ baseUrl: 'http://example.com/bar/' },
|
||||
{ baseUrl: 'http://example.com/baz/' },
|
||||
{ baseUrl: 'http://example.com/buzz/' }
|
||||
];
|
||||
|
||||
assert.deepEqual(buildBaseUrls(reference, nodes), expected, 'creates all base urls');
|
||||
});
|
||||
|
||||
QUnit.test('absolute BaseURL overwrites reference', function(assert) {
|
||||
const reference = ['https://example.com'];
|
||||
const reference = [{ baseUrl: 'https://example.com' }];
|
||||
const node = [{ textContent: 'https://foo.com/bar/' }];
|
||||
const expected = ['https://foo.com/bar/'];
|
||||
const expected = [{ baseUrl: 'https://foo.com/bar/'}];
|
||||
|
||||
assert.deepEqual(
|
||||
buildBaseUrls(reference, node), expected,
|
||||
|
@ -78,6 +81,40 @@ QUnit.test('absolute BaseURL overwrites reference', function(assert) {
|
|||
);
|
||||
});
|
||||
|
||||
QUnit.test('reference attributes are ignored when there is a BaseURL node', function(assert) {
|
||||
const reference = [{ baseUrl: 'https://example.com', attributes: [{ name: 'test', value: 'wow' }] }];
|
||||
const node = [{ textContent: 'https://foo.com/bar/' }];
|
||||
const expected = [{ baseUrl: 'https://foo.com/bar/' }];
|
||||
|
||||
assert.deepEqual(
|
||||
buildBaseUrls(reference, node), expected,
|
||||
'baseURL attributes are not included'
|
||||
);
|
||||
});
|
||||
|
||||
QUnit.test('BasURL attributes are still added with a reference', function(assert) {
|
||||
const reference = [{ baseUrl: 'https://example.com' }];
|
||||
const node = [{ textContent: 'https://foo.com/bar/', attributes: [{ name: 'test', value: 'wow' }] }];
|
||||
|
||||
const expected = [{ baseUrl: 'https://foo.com/bar/', test: 'wow' }];
|
||||
|
||||
assert.deepEqual(
|
||||
buildBaseUrls(reference, node), expected,
|
||||
'baseURL attributes are included'
|
||||
);
|
||||
});
|
||||
|
||||
QUnit.test('attributes are replaced when both reference and BaseURL have the same attributes', function(assert) {
|
||||
const reference = [{ baseUrl: 'https://example.com', attributes: [{ name: 'test', value: 'old' }] }];
|
||||
const node = [{ textContent: 'https://foo.com/bar/', attributes: [{ name: 'test', value: 'new' }] }];
|
||||
const expected = [{ baseUrl: 'https://foo.com/bar/', test: 'new' }];
|
||||
|
||||
assert.deepEqual(
|
||||
buildBaseUrls(reference, node), expected,
|
||||
'baseURL attributes are included'
|
||||
);
|
||||
});
|
||||
|
||||
QUnit.module('getPeriodStart');
|
||||
|
||||
QUnit.test('gets period start when available', function(assert) {
|
||||
|
@ -546,6 +583,7 @@ QUnit.test('end to end - basic', function(assert) {
|
|||
`), { NOW });
|
||||
|
||||
const expected = {
|
||||
contentSteeringInfo: null,
|
||||
eventStream: [],
|
||||
locations: undefined,
|
||||
representationInfo: [{
|
||||
|
@ -593,6 +631,82 @@ QUnit.test('end to end - basic', function(assert) {
|
|||
assert.deepEqual(actual, expected);
|
||||
});
|
||||
|
||||
QUnit.test('end to end - basic using manifest uri', function(assert) {
|
||||
const NOW = Date.now();
|
||||
|
||||
const actual = inheritAttributes(stringToMpdXml(`
|
||||
<MPD mediaPresentationDuration="PT30S" >
|
||||
<BaseURL>base/</BaseURL>
|
||||
<Period>
|
||||
<AdaptationSet mimeType="video/mp4" >
|
||||
<Role value="main"></Role>
|
||||
<SegmentTemplate></SegmentTemplate>
|
||||
<Representation
|
||||
bandwidth="5000000"
|
||||
codecs="avc1.64001e"
|
||||
height="404"
|
||||
id="test"
|
||||
width="720">
|
||||
</Representation>
|
||||
</AdaptationSet>
|
||||
<AdaptationSet mimeType="text/vtt" lang="en">
|
||||
<Representation bandwidth="256" id="en">
|
||||
<BaseURL>en.vtt</BaseURL>
|
||||
</Representation>
|
||||
</AdaptationSet>
|
||||
</Period>
|
||||
</MPD>
|
||||
`), { NOW, manifestUri: 'https://www.test.com' });
|
||||
|
||||
const expected = {
|
||||
contentSteeringInfo: null,
|
||||
eventStream: [],
|
||||
locations: undefined,
|
||||
representationInfo: [{
|
||||
attributes: {
|
||||
bandwidth: 5000000,
|
||||
baseUrl: 'https://www.test.com/base/',
|
||||
codecs: 'avc1.64001e',
|
||||
height: 404,
|
||||
id: 'test',
|
||||
mediaPresentationDuration: 30,
|
||||
mimeType: 'video/mp4',
|
||||
periodStart: 0,
|
||||
role: {
|
||||
value: 'main'
|
||||
},
|
||||
sourceDuration: 30,
|
||||
type: 'static',
|
||||
width: 720,
|
||||
NOW,
|
||||
clientOffset: 0
|
||||
},
|
||||
segmentInfo: {
|
||||
template: {}
|
||||
}
|
||||
}, {
|
||||
attributes: {
|
||||
bandwidth: 256,
|
||||
baseUrl: 'https://www.test.com/base/en.vtt',
|
||||
id: 'en',
|
||||
lang: 'en',
|
||||
mediaPresentationDuration: 30,
|
||||
mimeType: 'text/vtt',
|
||||
periodStart: 0,
|
||||
role: {},
|
||||
sourceDuration: 30,
|
||||
type: 'static',
|
||||
NOW,
|
||||
clientOffset: 0
|
||||
},
|
||||
segmentInfo: {}
|
||||
}]
|
||||
};
|
||||
|
||||
assert.equal(actual.representationInfo.length, 2);
|
||||
assert.deepEqual(actual, expected);
|
||||
});
|
||||
|
||||
QUnit.test('end to end - basic dynamic', function(assert) {
|
||||
const NOW = Date.now();
|
||||
|
||||
|
@ -621,6 +735,7 @@ QUnit.test('end to end - basic dynamic', function(assert) {
|
|||
`), { NOW });
|
||||
|
||||
const expected = {
|
||||
contentSteeringInfo: null,
|
||||
eventStream: [],
|
||||
locations: undefined,
|
||||
representationInfo: [{
|
||||
|
@ -666,6 +781,303 @@ QUnit.test('end to end - basic dynamic', function(assert) {
|
|||
assert.deepEqual(actual, expected);
|
||||
});
|
||||
|
||||
QUnit.test('end to end - content steering - non resolvable base URLs', function(assert) {
|
||||
const NOW = Date.now();
|
||||
|
||||
const actual = inheritAttributes(stringToMpdXml(`
|
||||
<MPD type="dyanmic">
|
||||
<ContentSteering defaultServiceLocation="beta" queryBeforeStart="false" proxyServerURL="http://127.0.0.1:3455/steer">https://example.com/app/url</ContentSteering>
|
||||
<BaseURL serviceLocation="alpha">https://cdn1.example.com/</BaseURL>
|
||||
<BaseURL serviceLocation="beta">https://cdn2.example.com/</BaseURL>
|
||||
<Period start="PT0S">
|
||||
<AdaptationSet mimeType="video/mp4">
|
||||
<Role value="main"></Role>
|
||||
<SegmentTemplate></SegmentTemplate>
|
||||
<Representation
|
||||
bandwidth="5000000"
|
||||
codecs="avc1.64001e"
|
||||
height="404"
|
||||
id="test"
|
||||
width="720">
|
||||
</Representation>
|
||||
</AdaptationSet>
|
||||
<AdaptationSet mimeType="text/vtt" lang="en">
|
||||
<Representation bandwidth="256" id="en">
|
||||
<BaseURL>https://example.com/en.vtt</BaseURL>
|
||||
</Representation>
|
||||
</AdaptationSet>
|
||||
</Period>
|
||||
</MPD>
|
||||
`), { NOW, manifestUri: 'https://www.test.com' });
|
||||
|
||||
// Note that we expect to see the `contentSteeringInfo` object set with the
|
||||
// proper values. We also expect to see the `serviceLocation` property set to
|
||||
// the correct values inside of the correct representations.
|
||||
const expected = {
|
||||
contentSteeringInfo: {
|
||||
defaultServiceLocation: 'beta',
|
||||
proxyServerURL: 'http://127.0.0.1:3455/steer',
|
||||
queryBeforeStart: false,
|
||||
serverURL: 'https://example.com/app/url'
|
||||
},
|
||||
eventStream: [],
|
||||
locations: undefined,
|
||||
representationInfo: [
|
||||
{
|
||||
attributes: {
|
||||
NOW,
|
||||
bandwidth: 5000000,
|
||||
baseUrl: 'https://cdn1.example.com/',
|
||||
clientOffset: 0,
|
||||
codecs: 'avc1.64001e',
|
||||
height: 404,
|
||||
id: 'test',
|
||||
mimeType: 'video/mp4',
|
||||
periodStart: 0,
|
||||
role: {
|
||||
value: 'main'
|
||||
},
|
||||
serviceLocation: 'alpha',
|
||||
sourceDuration: 0,
|
||||
type: 'dyanmic',
|
||||
width: 720
|
||||
},
|
||||
segmentInfo: {
|
||||
template: {}
|
||||
}
|
||||
},
|
||||
{
|
||||
attributes: {
|
||||
NOW,
|
||||
bandwidth: 5000000,
|
||||
baseUrl: 'https://cdn2.example.com/',
|
||||
clientOffset: 0,
|
||||
codecs: 'avc1.64001e',
|
||||
height: 404,
|
||||
id: 'test',
|
||||
mimeType: 'video/mp4',
|
||||
periodStart: 0,
|
||||
role: {
|
||||
value: 'main'
|
||||
},
|
||||
serviceLocation: 'beta',
|
||||
sourceDuration: 0,
|
||||
type: 'dyanmic',
|
||||
width: 720
|
||||
},
|
||||
segmentInfo: {
|
||||
template: {}
|
||||
}
|
||||
},
|
||||
{
|
||||
attributes: {
|
||||
NOW,
|
||||
bandwidth: 256,
|
||||
baseUrl: 'https://example.com/en.vtt',
|
||||
clientOffset: 0,
|
||||
id: 'en',
|
||||
lang: 'en',
|
||||
mimeType: 'text/vtt',
|
||||
periodStart: 0,
|
||||
role: {},
|
||||
sourceDuration: 0,
|
||||
type: 'dyanmic'
|
||||
},
|
||||
segmentInfo: {}
|
||||
},
|
||||
{
|
||||
attributes: {
|
||||
NOW,
|
||||
bandwidth: 256,
|
||||
baseUrl: 'https://example.com/en.vtt',
|
||||
clientOffset: 0,
|
||||
id: 'en',
|
||||
lang: 'en',
|
||||
mimeType: 'text/vtt',
|
||||
periodStart: 0,
|
||||
role: {},
|
||||
sourceDuration: 0,
|
||||
type: 'dyanmic'
|
||||
},
|
||||
segmentInfo: {}
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
assert.equal(actual.representationInfo.length, 4);
|
||||
assert.deepEqual(actual, expected);
|
||||
});
|
||||
|
||||
QUnit.test('end to end - content steering - resolvable base URLs', function(assert) {
|
||||
const NOW = Date.now();
|
||||
|
||||
const actual = inheritAttributes(stringToMpdXml(`
|
||||
<MPD type="dyanmic">
|
||||
<ContentSteering defaultServiceLocation="beta" queryBeforeStart="false" proxyServerURL="http://127.0.0.1:3455/steer">https://example.com/app/url</ContentSteering>
|
||||
<BaseURL serviceLocation="alpha">https://cdn1.example.com/</BaseURL>
|
||||
<BaseURL serviceLocation="beta">https://cdn2.example.com/</BaseURL>
|
||||
<Period start="PT0S">
|
||||
<AdaptationSet mimeType="video/mp4">
|
||||
<Role value="main"></Role>
|
||||
<SegmentTemplate></SegmentTemplate>
|
||||
<Representation
|
||||
bandwidth="5000000"
|
||||
codecs="avc1.64001e"
|
||||
height="404"
|
||||
id="test"
|
||||
width="720">
|
||||
</Representation>
|
||||
<BaseURL>/video</BaseURL>
|
||||
</AdaptationSet>
|
||||
<AdaptationSet mimeType="text/vtt" lang="en">
|
||||
<Representation bandwidth="256" id="en">
|
||||
<BaseURL>/vtt</BaseURL>
|
||||
</Representation>
|
||||
</AdaptationSet>
|
||||
</Period>
|
||||
</MPD>
|
||||
`), { NOW, manifestUri: 'https://www.test.com' });
|
||||
|
||||
// Note that we expect to see the `contentSteeringInfo` object set with the
|
||||
// proper values. We also expect to see the `serviceLocation` property set to
|
||||
// the correct values inside of the correct representations.
|
||||
//
|
||||
// Also note that some of the representations have '/video' appended
|
||||
// to the end of the baseUrls
|
||||
const expected = {
|
||||
contentSteeringInfo: {
|
||||
defaultServiceLocation: 'beta',
|
||||
proxyServerURL: 'http://127.0.0.1:3455/steer',
|
||||
queryBeforeStart: false,
|
||||
serverURL: 'https://example.com/app/url'
|
||||
},
|
||||
eventStream: [],
|
||||
locations: undefined,
|
||||
representationInfo: [
|
||||
{
|
||||
attributes: {
|
||||
NOW,
|
||||
bandwidth: 5000000,
|
||||
baseUrl: 'https://cdn1.example.com/video',
|
||||
clientOffset: 0,
|
||||
codecs: 'avc1.64001e',
|
||||
height: 404,
|
||||
id: 'test',
|
||||
mimeType: 'video/mp4',
|
||||
periodStart: 0,
|
||||
role: {
|
||||
value: 'main'
|
||||
},
|
||||
serviceLocation: 'alpha',
|
||||
sourceDuration: 0,
|
||||
type: 'dyanmic',
|
||||
width: 720
|
||||
},
|
||||
segmentInfo: {
|
||||
template: {}
|
||||
}
|
||||
},
|
||||
{
|
||||
attributes: {
|
||||
NOW,
|
||||
bandwidth: 5000000,
|
||||
baseUrl: 'https://cdn2.example.com/video',
|
||||
clientOffset: 0,
|
||||
codecs: 'avc1.64001e',
|
||||
height: 404,
|
||||
id: 'test',
|
||||
mimeType: 'video/mp4',
|
||||
periodStart: 0,
|
||||
role: {
|
||||
value: 'main'
|
||||
},
|
||||
serviceLocation: 'beta',
|
||||
sourceDuration: 0,
|
||||
type: 'dyanmic',
|
||||
width: 720
|
||||
},
|
||||
segmentInfo: {
|
||||
template: {}
|
||||
}
|
||||
},
|
||||
{
|
||||
attributes: {
|
||||
NOW,
|
||||
bandwidth: 256,
|
||||
baseUrl: 'https://cdn1.example.com/vtt',
|
||||
clientOffset: 0,
|
||||
id: 'en',
|
||||
lang: 'en',
|
||||
mimeType: 'text/vtt',
|
||||
periodStart: 0,
|
||||
role: {},
|
||||
serviceLocation: 'alpha',
|
||||
sourceDuration: 0,
|
||||
type: 'dyanmic'
|
||||
},
|
||||
segmentInfo: {}
|
||||
},
|
||||
{
|
||||
attributes: {
|
||||
NOW,
|
||||
bandwidth: 256,
|
||||
baseUrl: 'https://cdn2.example.com/vtt',
|
||||
clientOffset: 0,
|
||||
id: 'en',
|
||||
lang: 'en',
|
||||
mimeType: 'text/vtt',
|
||||
periodStart: 0,
|
||||
role: {},
|
||||
serviceLocation: 'beta',
|
||||
sourceDuration: 0,
|
||||
type: 'dyanmic'
|
||||
},
|
||||
segmentInfo: {}
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
assert.equal(actual.representationInfo.length, 4);
|
||||
assert.deepEqual(actual, expected);
|
||||
});
|
||||
|
||||
QUnit.test('Too many content steering tags sends a warning to the eventHandler', function(assert) {
|
||||
const handlerStub = stub();
|
||||
const NOW = Date.now();
|
||||
|
||||
inheritAttributes(stringToMpdXml(`
|
||||
<MPD type="dyanmic">
|
||||
<ContentSteering defaultServiceLocation="alpha" queryBeforeStart="false" proxyServerURL="http://127.0.0.1:3455/steer">https://example.com/app/url</ContentSteering>
|
||||
<ContentSteering defaultServiceLocation="beta" queryBeforeStart="false" proxyServerURL="http://127.0.0.1:3455/steer">https://example.com/app/url</ContentSteering>
|
||||
<BaseURL serviceLocation="alpha">https://cdn1.example.com/</BaseURL>
|
||||
<BaseURL serviceLocation="beta">https://cdn2.example.com/</BaseURL>
|
||||
<Period start="PT0S">
|
||||
<AdaptationSet mimeType="video/mp4">
|
||||
<Role value="main"></Role>
|
||||
<SegmentTemplate></SegmentTemplate>
|
||||
<Representation
|
||||
bandwidth="5000000"
|
||||
codecs="avc1.64001e"
|
||||
height="404"
|
||||
id="test"
|
||||
width="720">
|
||||
</Representation>
|
||||
</AdaptationSet>
|
||||
<AdaptationSet mimeType="text/vtt" lang="en">
|
||||
<Representation bandwidth="256" id="en">
|
||||
<BaseURL>/video</BaseURL>
|
||||
</Representation>
|
||||
</AdaptationSet>
|
||||
</Period>
|
||||
</MPD>
|
||||
`), { NOW, manifestUri: 'https://www.test.com', eventHandler: handlerStub });
|
||||
|
||||
assert.ok(handlerStub.calledWith({
|
||||
type: 'warn',
|
||||
message: 'The MPD manifest should contain no more than one ContentSteering tag'
|
||||
}));
|
||||
});
|
||||
|
||||
QUnit.test('end to end - basic multiperiod', function(assert) {
|
||||
const NOW = Date.now();
|
||||
|
||||
|
@ -703,6 +1115,7 @@ QUnit.test('end to end - basic multiperiod', function(assert) {
|
|||
`), { NOW });
|
||||
|
||||
const expected = {
|
||||
contentSteeringInfo: null,
|
||||
eventStream: [],
|
||||
locations: undefined,
|
||||
representationInfo: [{
|
||||
|
@ -790,6 +1203,7 @@ QUnit.test('end to end - inherits BaseURL from all levels', function(assert) {
|
|||
`), { NOW });
|
||||
|
||||
const expected = {
|
||||
contentSteeringInfo: null,
|
||||
eventStream: [],
|
||||
locations: undefined,
|
||||
representationInfo: [{
|
||||
|
@ -867,6 +1281,7 @@ QUnit.test('end to end - alternate BaseURLs', function(assert) {
|
|||
`), { NOW });
|
||||
|
||||
const expected = {
|
||||
contentSteeringInfo: null,
|
||||
eventStream: [],
|
||||
locations: undefined,
|
||||
representationInfo: [{
|
||||
|
@ -1035,6 +1450,7 @@ QUnit.test(
|
|||
`), { NOW });
|
||||
|
||||
const expected = {
|
||||
contentSteeringInfo: null,
|
||||
eventStream: [],
|
||||
locations: undefined,
|
||||
representationInfo: [{
|
||||
|
@ -1148,6 +1564,7 @@ QUnit.test(
|
|||
`), { NOW });
|
||||
|
||||
const expected = {
|
||||
contentSteeringInfo: null,
|
||||
eventStream: [],
|
||||
locations: undefined,
|
||||
representationInfo: [{
|
||||
|
@ -1272,6 +1689,7 @@ QUnit.test(
|
|||
`), { NOW });
|
||||
|
||||
const expected = {
|
||||
contentSteeringInfo: null,
|
||||
eventStream: [],
|
||||
locations: undefined,
|
||||
representationInfo: [{
|
||||
|
@ -2116,6 +2534,7 @@ QUnit.test('keySystem info for representation - lowercase UUIDs', function(asser
|
|||
|
||||
// inconsistent quoting because of quote-props
|
||||
const expected = {
|
||||
contentSteeringInfo: null,
|
||||
eventStream: [],
|
||||
locations: undefined,
|
||||
representationInfo: [{
|
||||
|
@ -2203,6 +2622,7 @@ QUnit.test('keySystem info for representation - uppercase UUIDs', function(asser
|
|||
|
||||
// inconsistent quoting because of quote-props
|
||||
const expected = {
|
||||
contentSteeringInfo: null,
|
||||
eventStream: [],
|
||||
locations: undefined,
|
||||
representationInfo: [{
|
||||
|
@ -2372,6 +2792,7 @@ QUnit.test('gets eventStream from inheritAttributes', function(assert) {
|
|||
</Period>
|
||||
</MPD>`);
|
||||
const expected = {
|
||||
contentSteeringInfo: null,
|
||||
eventStream: [
|
||||
{
|
||||
end: 15,
|
||||
|
@ -2481,6 +2902,7 @@ QUnit.test('gets eventStream from inheritAttributes with data in Event tags', fu
|
|||
</Period>
|
||||
</MPD>`);
|
||||
const expected = {
|
||||
contentSteeringInfo: null,
|
||||
eventStream: [
|
||||
{
|
||||
end: 15,
|
||||
|
|
2
node_modules/mpd-parser/test/manifests/608-captions.js
generated
vendored
2
node_modules/mpd-parser/test/manifests/608-captions.js
generated
vendored
|
@ -46,7 +46,7 @@ export const parsedManifest = {
|
|||
'SUBTITLES': 'subs'
|
||||
},
|
||||
endList: true,
|
||||
resolvedUri: '',
|
||||
resolvedUri: 'https://www.example.com/1080p.ts',
|
||||
targetDuration: 6,
|
||||
mediaSequence: 0,
|
||||
timelineStarts: [{ start: 0, timeline: 0 }],
|
||||
|
|
2
node_modules/mpd-parser/test/manifests/708-captions.js
generated
vendored
2
node_modules/mpd-parser/test/manifests/708-captions.js
generated
vendored
|
@ -47,7 +47,7 @@ export const parsedManifest = {
|
|||
'SUBTITLES': 'subs'
|
||||
},
|
||||
endList: true,
|
||||
resolvedUri: '',
|
||||
resolvedUri: 'https://www.example.com/1080p.ts',
|
||||
targetDuration: 6,
|
||||
mediaSequence: 0,
|
||||
timelineStarts: [{ start: 0, timeline: 0 }],
|
||||
|
|
4
node_modules/mpd-parser/test/manifests/audio-only.js
generated
vendored
4
node_modules/mpd-parser/test/manifests/audio-only.js
generated
vendored
|
@ -27,7 +27,7 @@ export const parsedManifest = {
|
|||
uri: '',
|
||||
endList: true,
|
||||
timeline: 0,
|
||||
resolvedUri: '',
|
||||
resolvedUri: 'http://example.com/audio_en_2c_128k_aac.mp4',
|
||||
targetDuration: 60,
|
||||
segments: [],
|
||||
mediaSequence: 0,
|
||||
|
@ -78,7 +78,7 @@ export const parsedManifest = {
|
|||
uri: '',
|
||||
endList: true,
|
||||
timeline: 0,
|
||||
resolvedUri: '',
|
||||
resolvedUri: 'http://example.com/audio_es_2c_128k_aac.mp4',
|
||||
targetDuration: 60,
|
||||
segments: [],
|
||||
mediaSequence: 0,
|
||||
|
|
2
node_modules/mpd-parser/test/manifests/location.js
generated
vendored
2
node_modules/mpd-parser/test/manifests/location.js
generated
vendored
|
@ -32,7 +32,7 @@ export const parsedManifest = {
|
|||
'SUBTITLES': 'subs'
|
||||
},
|
||||
endList: true,
|
||||
resolvedUri: '',
|
||||
resolvedUri: 'https://www.example.com/1080p.ts',
|
||||
targetDuration: 6,
|
||||
mediaSequence: 0,
|
||||
discontinuitySequence: 0,
|
||||
|
|
2
node_modules/mpd-parser/test/manifests/locations.js
generated
vendored
2
node_modules/mpd-parser/test/manifests/locations.js
generated
vendored
|
@ -33,7 +33,7 @@ export const parsedManifest = {
|
|||
'SUBTITLES': 'subs'
|
||||
},
|
||||
endList: true,
|
||||
resolvedUri: '',
|
||||
resolvedUri: 'https://www.example.com/1080p.ts',
|
||||
targetDuration: 6,
|
||||
mediaSequence: 0,
|
||||
discontinuitySequence: 0,
|
||||
|
|
12
node_modules/mpd-parser/test/manifests/maat_vtt_segmentTemplate.js
generated
vendored
12
node_modules/mpd-parser/test/manifests/maat_vtt_segmentTemplate.js
generated
vendored
|
@ -25,7 +25,7 @@ export const parsedManifest = {
|
|||
timelineStarts: [{ start: 0, timeline: 0 }],
|
||||
discontinuitySequence: 0,
|
||||
discontinuityStarts: [],
|
||||
resolvedUri: '',
|
||||
resolvedUri: 'https://www.example.com/base',
|
||||
targetDuration: 1.984,
|
||||
segments: [
|
||||
{
|
||||
|
@ -100,7 +100,7 @@ export const parsedManifest = {
|
|||
timelineStarts: [{ start: 0, timeline: 0 }],
|
||||
discontinuitySequence: 0,
|
||||
discontinuityStarts: [],
|
||||
resolvedUri: '',
|
||||
resolvedUri: 'https://www.example.com/base',
|
||||
targetDuration: 1.984,
|
||||
segments: [
|
||||
{
|
||||
|
@ -183,7 +183,7 @@ export const parsedManifest = {
|
|||
timelineStarts: [{ start: 0, timeline: 0 }],
|
||||
discontinuitySequence: 0,
|
||||
discontinuityStarts: [],
|
||||
resolvedUri: '',
|
||||
resolvedUri: 'https://www.example.com/base',
|
||||
targetDuration: 1.984,
|
||||
segments: [
|
||||
{
|
||||
|
@ -258,7 +258,7 @@ export const parsedManifest = {
|
|||
timelineStarts: [{ start: 0, timeline: 0 }],
|
||||
discontinuitySequence: 0,
|
||||
discontinuityStarts: [],
|
||||
resolvedUri: '',
|
||||
resolvedUri: 'https://www.example.com/base',
|
||||
targetDuration: 1.984,
|
||||
segments: [
|
||||
{
|
||||
|
@ -421,7 +421,7 @@ export const parsedManifest = {
|
|||
timelineStarts: [{ start: 0, timeline: 0 }],
|
||||
discontinuitySequence: 0,
|
||||
discontinuityStarts: [],
|
||||
resolvedUri: '',
|
||||
resolvedUri: 'https://www.example.com/base',
|
||||
targetDuration: 1.9185833333333333,
|
||||
segments: [
|
||||
{
|
||||
|
@ -503,7 +503,7 @@ export const parsedManifest = {
|
|||
timelineStarts: [{ start: 0, timeline: 0 }],
|
||||
discontinuitySequence: 0,
|
||||
discontinuityStarts: [],
|
||||
resolvedUri: '',
|
||||
resolvedUri: 'https://www.example.com/base',
|
||||
targetDuration: 1.9185833333333333,
|
||||
segments: [
|
||||
{
|
||||
|
|
2
node_modules/mpd-parser/test/manifests/multiperiod-segment-list.js
generated
vendored
2
node_modules/mpd-parser/test/manifests/multiperiod-segment-list.js
generated
vendored
|
@ -36,7 +36,7 @@ export const parsedManifest = {
|
|||
timeline: 6
|
||||
}],
|
||||
targetDuration: 3,
|
||||
resolvedUri: '',
|
||||
resolvedUri: 'https://www.example.com/base',
|
||||
segments: [
|
||||
{
|
||||
duration: 3,
|
||||
|
|
4
node_modules/mpd-parser/test/manifests/multiperiod-segment-template.js
generated
vendored
4
node_modules/mpd-parser/test/manifests/multiperiod-segment-template.js
generated
vendored
|
@ -30,7 +30,7 @@ export const parsedManifest = {
|
|||
uri: '',
|
||||
endList: true,
|
||||
timeline: 0,
|
||||
resolvedUri: '',
|
||||
resolvedUri: 'https://www.example.com/base',
|
||||
targetDuration: 5,
|
||||
segments: [
|
||||
{
|
||||
|
@ -145,7 +145,7 @@ export const parsedManifest = {
|
|||
uri: '',
|
||||
endList: true,
|
||||
timeline: 0,
|
||||
resolvedUri: '',
|
||||
resolvedUri: 'https://www.example.com/base',
|
||||
targetDuration: 5,
|
||||
segments: [
|
||||
{
|
||||
|
|
38
node_modules/mpd-parser/test/manifests/multiperiod-startnumber-removed-periods.js
generated
vendored
38
node_modules/mpd-parser/test/manifests/multiperiod-startnumber-removed-periods.js
generated
vendored
|
@ -25,13 +25,13 @@ export const parsedManifest = {
|
|||
'PROGRAM-ID': 1
|
||||
},
|
||||
endList: false,
|
||||
mediaSequence: 7,
|
||||
mediaSequence: 3,
|
||||
discontinuitySequence: 2,
|
||||
discontinuityStarts: [0],
|
||||
timelineStarts: [
|
||||
{ start: 111, timeline: 111}
|
||||
],
|
||||
resolvedUri: '',
|
||||
resolvedUri: 'http://example.com/audio/v0/',
|
||||
segments: [
|
||||
{
|
||||
discontinuity: true,
|
||||
|
@ -41,7 +41,7 @@ export const parsedManifest = {
|
|||
uri: 'init.mp4'
|
||||
},
|
||||
presentationTime: 111,
|
||||
number: 7,
|
||||
number: 3,
|
||||
resolvedUri: 'http://example.com/audio/v0/862.m4f',
|
||||
timeline: 111,
|
||||
uri: '862.m4f'
|
||||
|
@ -53,7 +53,7 @@ export const parsedManifest = {
|
|||
uri: 'init.mp4'
|
||||
},
|
||||
presentationTime: 112,
|
||||
number: 8,
|
||||
number: 4,
|
||||
resolvedUri: 'http://example.com/audio/v0/863.m4f',
|
||||
timeline: 111,
|
||||
uri: '863.m4f'
|
||||
|
@ -65,7 +65,7 @@ export const parsedManifest = {
|
|||
uri: 'init.mp4'
|
||||
},
|
||||
presentationTime: 113,
|
||||
number: 9,
|
||||
number: 5,
|
||||
resolvedUri: 'http://example.com/audio/v0/864.m4f',
|
||||
timeline: 111,
|
||||
uri: '864.m4f'
|
||||
|
@ -107,7 +107,7 @@ export const parsedManifest = {
|
|||
timelineStarts: [
|
||||
{ start: 111, timeline: 111}
|
||||
],
|
||||
resolvedUri: '',
|
||||
resolvedUri: 'http://example.com/video/D/',
|
||||
segments: [
|
||||
{
|
||||
discontinuity: true,
|
||||
|
@ -172,7 +172,7 @@ export const parsedManifest = {
|
|||
{ start: 111, timeline: 111}
|
||||
],
|
||||
discontinuityStarts: [0],
|
||||
resolvedUri: '',
|
||||
resolvedUri: 'http://example.com/video/E/',
|
||||
segments: [
|
||||
{
|
||||
discontinuity: true,
|
||||
|
@ -231,13 +231,13 @@ export const parsedManifest = {
|
|||
'SUBTITLES': 'subs'
|
||||
},
|
||||
endList: false,
|
||||
mediaSequence: 7,
|
||||
mediaSequence: 3,
|
||||
discontinuitySequence: 2,
|
||||
timelineStarts: [
|
||||
{ start: 111, timeline: 111}
|
||||
],
|
||||
discontinuityStarts: [0],
|
||||
resolvedUri: '',
|
||||
resolvedUri: 'http://example.com/video/F/',
|
||||
segments: [
|
||||
{
|
||||
discontinuity: true,
|
||||
|
@ -247,7 +247,7 @@ export const parsedManifest = {
|
|||
uri: 'F_init.mp4'
|
||||
},
|
||||
presentationTime: 111,
|
||||
number: 7,
|
||||
number: 3,
|
||||
resolvedUri: 'http://example.com/video/F/F862.m4f',
|
||||
timeline: 111,
|
||||
uri: 'F862.m4f'
|
||||
|
@ -259,7 +259,7 @@ export const parsedManifest = {
|
|||
uri: 'F_init.mp4'
|
||||
},
|
||||
presentationTime: 112,
|
||||
number: 8,
|
||||
number: 4,
|
||||
resolvedUri: 'http://example.com/video/F/F863.m4f',
|
||||
timeline: 111,
|
||||
uri: 'F863.m4f'
|
||||
|
@ -271,7 +271,7 @@ export const parsedManifest = {
|
|||
uri: 'F_init.mp4'
|
||||
},
|
||||
presentationTime: 113,
|
||||
number: 9,
|
||||
number: 5,
|
||||
resolvedUri: 'http://example.com/video/F/F864.m4f',
|
||||
timeline: 111,
|
||||
uri: 'F864.m4f'
|
||||
|
@ -302,7 +302,7 @@ export const parsedManifest = {
|
|||
{ start: 111, timeline: 111}
|
||||
],
|
||||
discontinuityStarts: [0],
|
||||
resolvedUri: '',
|
||||
resolvedUri: 'http://example.com/video/A/',
|
||||
segments: [
|
||||
{
|
||||
discontinuity: true,
|
||||
|
@ -367,7 +367,7 @@ export const parsedManifest = {
|
|||
{ start: 111, timeline: 111}
|
||||
],
|
||||
discontinuityStarts: [0],
|
||||
resolvedUri: '',
|
||||
resolvedUri: 'http://example.com/video/B/',
|
||||
segments: [
|
||||
{
|
||||
discontinuity: true,
|
||||
|
@ -426,13 +426,13 @@ export const parsedManifest = {
|
|||
'SUBTITLES': 'subs'
|
||||
},
|
||||
endList: false,
|
||||
mediaSequence: 7,
|
||||
mediaSequence: 3,
|
||||
discontinuitySequence: 2,
|
||||
timelineStarts: [
|
||||
{ start: 111, timeline: 111}
|
||||
],
|
||||
discontinuityStarts: [0],
|
||||
resolvedUri: '',
|
||||
resolvedUri: 'http://example.com/video/C/',
|
||||
segments: [
|
||||
{
|
||||
discontinuity: true,
|
||||
|
@ -442,7 +442,7 @@ export const parsedManifest = {
|
|||
uri: 'C_init.mp4'
|
||||
},
|
||||
presentationTime: 111,
|
||||
number: 7,
|
||||
number: 3,
|
||||
resolvedUri: 'http://example.com/video/C/C862.m4f',
|
||||
timeline: 111,
|
||||
uri: 'C862.m4f'
|
||||
|
@ -454,7 +454,7 @@ export const parsedManifest = {
|
|||
uri: 'C_init.mp4'
|
||||
},
|
||||
presentationTime: 112,
|
||||
number: 8,
|
||||
number: 4,
|
||||
resolvedUri: 'http://example.com/video/C/C863.m4f',
|
||||
timeline: 111,
|
||||
uri: 'C863.m4f'
|
||||
|
@ -466,7 +466,7 @@ export const parsedManifest = {
|
|||
uri: 'C_init.mp4'
|
||||
},
|
||||
presentationTime: 113,
|
||||
number: 9,
|
||||
number: 5,
|
||||
resolvedUri: 'http://example.com/video/C/C864.m4f',
|
||||
timeline: 111,
|
||||
uri: 'C864.m4f'
|
||||
|
|
487
node_modules/mpd-parser/test/manifests/multiperiod-startnumber.js
generated
vendored
487
node_modules/mpd-parser/test/manifests/multiperiod-startnumber.js
generated
vendored
|
@ -27,14 +27,8 @@ export const parsedManifest = {
|
|||
endList: false,
|
||||
mediaSequence: 0,
|
||||
discontinuitySequence: 0,
|
||||
discontinuityStarts: [3, 5, 7],
|
||||
timelineStarts: [
|
||||
{ start: 100, timeline: 100},
|
||||
{ start: 103, timeline: 103},
|
||||
{ start: 107, timeline: 107},
|
||||
{ start: 111, timeline: 111}
|
||||
],
|
||||
resolvedUri: '',
|
||||
discontinuityStarts: [],
|
||||
resolvedUri: 'http://example.com/audio/1',
|
||||
segments: [
|
||||
{
|
||||
duration: 1,
|
||||
|
@ -71,16 +65,36 @@ export const parsedManifest = {
|
|||
resolvedUri: 'http://example.com/audio/502.m4f',
|
||||
timeline: 100,
|
||||
uri: '502.m4f'
|
||||
},
|
||||
}
|
||||
],
|
||||
targetDuration: 1,
|
||||
timeline: 100,
|
||||
timelineStarts: [
|
||||
{ start: 100, timeline: 100}
|
||||
],
|
||||
uri: ''
|
||||
},
|
||||
{
|
||||
attributes: {
|
||||
'BANDWIDTH': 128352,
|
||||
'CODECS': 'mp4a.40.5',
|
||||
'NAME': 'v0',
|
||||
'PROGRAM-ID': 1
|
||||
},
|
||||
discontinuitySequence: 1,
|
||||
discontinuityStarts: [2, 4],
|
||||
endList: false,
|
||||
mediaSequence: 0,
|
||||
resolvedUri: 'http://example.com/audio/v0/',
|
||||
segments: [
|
||||
{
|
||||
discontinuity: true,
|
||||
duration: 2,
|
||||
map: {
|
||||
resolvedUri: 'http://example.com/audio/v0/init.mp4',
|
||||
uri: 'init.mp4'
|
||||
},
|
||||
presentationTime: 103,
|
||||
number: 3,
|
||||
number: 0,
|
||||
resolvedUri: 'http://example.com/audio/v0/000.m4f',
|
||||
timeline: 103,
|
||||
uri: '000.m4f'
|
||||
|
@ -92,7 +106,7 @@ export const parsedManifest = {
|
|||
uri: 'init.mp4'
|
||||
},
|
||||
presentationTime: 105,
|
||||
number: 4,
|
||||
number: 1,
|
||||
resolvedUri: 'http://example.com/audio/v0/001.m4f',
|
||||
timeline: 103,
|
||||
uri: '001.m4f'
|
||||
|
@ -105,7 +119,7 @@ export const parsedManifest = {
|
|||
uri: 'init.mp4'
|
||||
},
|
||||
presentationTime: 107,
|
||||
number: 5,
|
||||
number: 2,
|
||||
resolvedUri: 'http://example.com/audio/v0/000.m4f',
|
||||
timeline: 107,
|
||||
uri: '000.m4f'
|
||||
|
@ -117,7 +131,7 @@ export const parsedManifest = {
|
|||
uri: 'init.mp4'
|
||||
},
|
||||
presentationTime: 109,
|
||||
number: 6,
|
||||
number: 3,
|
||||
resolvedUri: 'http://example.com/audio/v0/001.m4f',
|
||||
timeline: 107,
|
||||
uri: '001.m4f'
|
||||
|
@ -130,7 +144,7 @@ export const parsedManifest = {
|
|||
uri: 'init.mp4'
|
||||
},
|
||||
presentationTime: 111,
|
||||
number: 7,
|
||||
number: 4,
|
||||
resolvedUri: 'http://example.com/audio/v0/862.m4f',
|
||||
timeline: 111,
|
||||
uri: '862.m4f'
|
||||
|
@ -142,7 +156,7 @@ export const parsedManifest = {
|
|||
uri: 'init.mp4'
|
||||
},
|
||||
presentationTime: 112,
|
||||
number: 8,
|
||||
number: 5,
|
||||
resolvedUri: 'http://example.com/audio/v0/863.m4f',
|
||||
timeline: 111,
|
||||
uri: '863.m4f'
|
||||
|
@ -154,14 +168,19 @@ export const parsedManifest = {
|
|||
uri: 'init.mp4'
|
||||
},
|
||||
presentationTime: 113,
|
||||
number: 9,
|
||||
number: 6,
|
||||
resolvedUri: 'http://example.com/audio/v0/864.m4f',
|
||||
timeline: 111,
|
||||
uri: '864.m4f'
|
||||
}
|
||||
],
|
||||
targetDuration: 1,
|
||||
timeline: 100,
|
||||
targetDuration: 2,
|
||||
timeline: 103,
|
||||
timelineStarts: [
|
||||
{ start: 103, timeline: 103},
|
||||
{ start: 107, timeline: 107},
|
||||
{ start: 111, timeline: 111}
|
||||
],
|
||||
uri: ''
|
||||
}
|
||||
],
|
||||
|
@ -189,17 +208,11 @@ export const parsedManifest = {
|
|||
},
|
||||
'SUBTITLES': 'subs'
|
||||
},
|
||||
endList: false,
|
||||
mediaSequence: 0,
|
||||
discontinuitySequence: 0,
|
||||
discontinuityStarts: [3, 5, 7],
|
||||
timelineStarts: [
|
||||
{ start: 100, timeline: 100},
|
||||
{ start: 103, timeline: 103},
|
||||
{ start: 107, timeline: 107},
|
||||
{ start: 111, timeline: 111}
|
||||
],
|
||||
resolvedUri: '',
|
||||
endList: false,
|
||||
mediaSequence: 0,
|
||||
resolvedUri: 'http://example.com/video/D/',
|
||||
segments: [
|
||||
{
|
||||
duration: 1,
|
||||
|
@ -327,6 +340,12 @@ export const parsedManifest = {
|
|||
],
|
||||
targetDuration: 1,
|
||||
timeline: 100,
|
||||
timelineStarts: [
|
||||
{ start: 100, timeline: 100},
|
||||
{ start: 103, timeline: 103},
|
||||
{ start: 107, timeline: 107},
|
||||
{ start: 111, timeline: 111}
|
||||
],
|
||||
uri: ''
|
||||
},
|
||||
{
|
||||
|
@ -343,17 +362,11 @@ export const parsedManifest = {
|
|||
},
|
||||
'SUBTITLES': 'subs'
|
||||
},
|
||||
endList: false,
|
||||
mediaSequence: 0,
|
||||
discontinuitySequence: 0,
|
||||
discontinuityStarts: [3, 5, 7],
|
||||
timelineStarts: [
|
||||
{ start: 100, timeline: 100},
|
||||
{ start: 103, timeline: 103},
|
||||
{ start: 107, timeline: 107},
|
||||
{ start: 111, timeline: 111}
|
||||
],
|
||||
resolvedUri: '',
|
||||
endList: false,
|
||||
mediaSequence: 0,
|
||||
resolvedUri: 'http://example.com/video/E/',
|
||||
segments: [
|
||||
{
|
||||
duration: 1,
|
||||
|
@ -481,6 +494,12 @@ export const parsedManifest = {
|
|||
],
|
||||
targetDuration: 1,
|
||||
timeline: 100,
|
||||
timelineStarts: [
|
||||
{ start: 100, timeline: 100},
|
||||
{ start: 103, timeline: 103},
|
||||
{ start: 107, timeline: 107},
|
||||
{ start: 111, timeline: 111}
|
||||
],
|
||||
uri: ''
|
||||
},
|
||||
{
|
||||
|
@ -497,17 +516,11 @@ export const parsedManifest = {
|
|||
},
|
||||
'SUBTITLES': 'subs'
|
||||
},
|
||||
discontinuitySequence: 0,
|
||||
discontinuityStarts: [],
|
||||
endList: false,
|
||||
mediaSequence: 0,
|
||||
discontinuitySequence: 0,
|
||||
discontinuityStarts: [3, 5, 7],
|
||||
timelineStarts: [
|
||||
{ start: 100, timeline: 100},
|
||||
{ start: 103, timeline: 103},
|
||||
{ start: 107, timeline: 107},
|
||||
{ start: 111, timeline: 111}
|
||||
],
|
||||
resolvedUri: '',
|
||||
resolvedUri: 'http://example.com/video/E/',
|
||||
segments: [
|
||||
{
|
||||
duration: 1,
|
||||
|
@ -544,97 +557,77 @@ export const parsedManifest = {
|
|||
resolvedUri: 'http://example.com/video/E/F502.m4f',
|
||||
timeline: 100,
|
||||
uri: 'F502.m4f'
|
||||
},
|
||||
{
|
||||
discontinuity: true,
|
||||
duration: 2,
|
||||
map: {
|
||||
resolvedUri: 'http://example.com/video/F/F_init.mp4',
|
||||
uri: 'F_init.mp4'
|
||||
},
|
||||
presentationTime: 103,
|
||||
number: 3,
|
||||
resolvedUri: 'http://example.com/video/F/F000.m4f',
|
||||
timeline: 103,
|
||||
uri: 'F000.m4f'
|
||||
},
|
||||
{
|
||||
duration: 2,
|
||||
map: {
|
||||
resolvedUri: 'http://example.com/video/F/F_init.mp4',
|
||||
uri: 'F_init.mp4'
|
||||
},
|
||||
presentationTime: 105,
|
||||
number: 4,
|
||||
resolvedUri: 'http://example.com/video/F/F001.m4f',
|
||||
timeline: 103,
|
||||
uri: 'F001.m4f'
|
||||
},
|
||||
{
|
||||
discontinuity: true,
|
||||
duration: 1,
|
||||
map: {
|
||||
resolvedUri: 'http://example.com/video/F/F_init.mp4',
|
||||
uri: 'F_init.mp4'
|
||||
},
|
||||
presentationTime: 107,
|
||||
number: 5,
|
||||
resolvedUri: 'http://example.com/video/F/F000.m4f',
|
||||
timeline: 107,
|
||||
uri: 'F000.m4f'
|
||||
},
|
||||
{
|
||||
duration: 1,
|
||||
map: {
|
||||
resolvedUri: 'http://example.com/video/F/F_init.mp4',
|
||||
uri: 'F_init.mp4'
|
||||
},
|
||||
presentationTime: 108,
|
||||
number: 6,
|
||||
resolvedUri: 'http://example.com/video/F/F001.m4f',
|
||||
timeline: 107,
|
||||
uri: 'F001.m4f'
|
||||
},
|
||||
{
|
||||
discontinuity: true,
|
||||
duration: 1,
|
||||
map: {
|
||||
resolvedUri: 'http://example.com/video/F/F_init.mp4',
|
||||
uri: 'F_init.mp4'
|
||||
},
|
||||
presentationTime: 111,
|
||||
number: 7,
|
||||
resolvedUri: 'http://example.com/video/F/F862.m4f',
|
||||
timeline: 111,
|
||||
uri: 'F862.m4f'
|
||||
},
|
||||
{
|
||||
duration: 1,
|
||||
map: {
|
||||
resolvedUri: 'http://example.com/video/F/F_init.mp4',
|
||||
uri: 'F_init.mp4'
|
||||
},
|
||||
presentationTime: 112,
|
||||
number: 8,
|
||||
resolvedUri: 'http://example.com/video/F/F863.m4f',
|
||||
timeline: 111,
|
||||
uri: 'F863.m4f'
|
||||
},
|
||||
{
|
||||
duration: 1,
|
||||
map: {
|
||||
resolvedUri: 'http://example.com/video/F/F_init.mp4',
|
||||
uri: 'F_init.mp4'
|
||||
},
|
||||
presentationTime: 113,
|
||||
number: 9,
|
||||
resolvedUri: 'http://example.com/video/F/F864.m4f',
|
||||
timeline: 111,
|
||||
uri: 'F864.m4f'
|
||||
}
|
||||
],
|
||||
targetDuration: 1,
|
||||
timeline: 100,
|
||||
timelineStarts: [
|
||||
{ start: 100, timeline: 100}
|
||||
],
|
||||
uri: ''
|
||||
},
|
||||
{
|
||||
attributes: {
|
||||
'AUDIO': 'audio',
|
||||
'BANDWIDTH': 1277155,
|
||||
'CODECS': 'avc1.4d001e',
|
||||
'FRAME-RATE': 30,
|
||||
'NAME': 'C',
|
||||
'PROGRAM-ID': 1,
|
||||
'RESOLUTION': {
|
||||
height: 540,
|
||||
width: 960
|
||||
},
|
||||
'SUBTITLES': 'subs'
|
||||
},
|
||||
discontinuitySequence: 0,
|
||||
discontinuityStarts: [],
|
||||
endList: false,
|
||||
mediaSequence: 0,
|
||||
resolvedUri: 'http://example.com/video/E/',
|
||||
segments: [
|
||||
{
|
||||
duration: 1,
|
||||
map: {
|
||||
resolvedUri: 'http://example.com/video/E/C_init.mp4',
|
||||
uri: 'C_init.mp4'
|
||||
},
|
||||
presentationTime: 100,
|
||||
number: 0,
|
||||
resolvedUri: 'http://example.com/video/E/C500.m4f',
|
||||
timeline: 100,
|
||||
uri: 'C500.m4f'
|
||||
},
|
||||
{
|
||||
duration: 1,
|
||||
map: {
|
||||
resolvedUri: 'http://example.com/video/E/C_init.mp4',
|
||||
uri: 'C_init.mp4'
|
||||
},
|
||||
presentationTime: 101,
|
||||
number: 1,
|
||||
resolvedUri: 'http://example.com/video/E/C501.m4f',
|
||||
timeline: 100,
|
||||
uri: 'C501.m4f'
|
||||
},
|
||||
{
|
||||
duration: 1,
|
||||
map: {
|
||||
resolvedUri: 'http://example.com/video/E/C_init.mp4',
|
||||
uri: 'C_init.mp4'
|
||||
},
|
||||
presentationTime: 102,
|
||||
number: 2,
|
||||
resolvedUri: 'http://example.com/video/E/C502.m4f',
|
||||
timeline: 100,
|
||||
uri: 'C502.m4f'
|
||||
}
|
||||
],
|
||||
targetDuration: 1,
|
||||
timeline: 100,
|
||||
timelineStarts: [
|
||||
{ start: 100, timeline: 100}
|
||||
],
|
||||
uri: ''
|
||||
},
|
||||
{
|
||||
|
@ -651,17 +644,11 @@ export const parsedManifest = {
|
|||
},
|
||||
'SUBTITLES': 'subs'
|
||||
},
|
||||
endList: false,
|
||||
mediaSequence: 0,
|
||||
discontinuitySequence: 0,
|
||||
discontinuityStarts: [3, 5, 7],
|
||||
timelineStarts: [
|
||||
{ start: 100, timeline: 100},
|
||||
{ start: 103, timeline: 103},
|
||||
{ start: 107, timeline: 107},
|
||||
{ start: 111, timeline: 111}
|
||||
],
|
||||
resolvedUri: '',
|
||||
endList: false,
|
||||
mediaSequence: 0,
|
||||
resolvedUri: 'http://example.com/video/A/',
|
||||
segments: [
|
||||
{
|
||||
duration: 1,
|
||||
|
@ -789,6 +776,12 @@ export const parsedManifest = {
|
|||
],
|
||||
targetDuration: 1,
|
||||
timeline: 100,
|
||||
timelineStarts: [
|
||||
{ start: 100, timeline: 100},
|
||||
{ start: 103, timeline: 103},
|
||||
{ start: 107, timeline: 107},
|
||||
{ start: 111, timeline: 111}
|
||||
],
|
||||
uri: ''
|
||||
},
|
||||
{
|
||||
|
@ -805,17 +798,11 @@ export const parsedManifest = {
|
|||
},
|
||||
'SUBTITLES': 'subs'
|
||||
},
|
||||
endList: false,
|
||||
mediaSequence: 0,
|
||||
discontinuitySequence: 0,
|
||||
discontinuityStarts: [3, 5, 7],
|
||||
timelineStarts: [
|
||||
{ start: 100, timeline: 100},
|
||||
{ start: 103, timeline: 103},
|
||||
{ start: 107, timeline: 107},
|
||||
{ start: 111, timeline: 111}
|
||||
],
|
||||
resolvedUri: '',
|
||||
endList: false,
|
||||
mediaSequence: 0,
|
||||
resolvedUri: 'http://example.com/video/B/',
|
||||
segments: [
|
||||
{
|
||||
duration: 1,
|
||||
|
@ -943,13 +930,135 @@ export const parsedManifest = {
|
|||
],
|
||||
targetDuration: 1,
|
||||
timeline: 100,
|
||||
timelineStarts: [
|
||||
{ start: 100, timeline: 100},
|
||||
{ start: 103, timeline: 103},
|
||||
{ start: 107, timeline: 107},
|
||||
{ start: 111, timeline: 111}
|
||||
],
|
||||
uri: ''
|
||||
},
|
||||
{
|
||||
attributes: {
|
||||
'AUDIO': 'audio',
|
||||
'BANDWIDTH': 1277155,
|
||||
'CODECS': 'avc1.4d001e',
|
||||
'BANDWIDTH': 2215557,
|
||||
'CODECS': 'avc1.640020',
|
||||
'FRAME-RATE': 60,
|
||||
'NAME': 'F',
|
||||
'PROGRAM-ID': 1,
|
||||
'RESOLUTION': {
|
||||
height: 720,
|
||||
width: 1280
|
||||
},
|
||||
'SUBTITLES': 'subs'
|
||||
},
|
||||
discontinuitySequence: 1,
|
||||
discontinuityStarts: [2, 4],
|
||||
endList: false,
|
||||
mediaSequence: 0,
|
||||
resolvedUri: 'http://example.com/video/F/',
|
||||
segments: [
|
||||
{
|
||||
duration: 2,
|
||||
map: {
|
||||
resolvedUri: 'http://example.com/video/F/F_init.mp4',
|
||||
uri: 'F_init.mp4'
|
||||
},
|
||||
presentationTime: 103,
|
||||
number: 0,
|
||||
resolvedUri: 'http://example.com/video/F/F000.m4f',
|
||||
timeline: 103,
|
||||
uri: 'F000.m4f'
|
||||
},
|
||||
{
|
||||
duration: 2,
|
||||
map: {
|
||||
resolvedUri: 'http://example.com/video/F/F_init.mp4',
|
||||
uri: 'F_init.mp4'
|
||||
},
|
||||
presentationTime: 105,
|
||||
number: 1,
|
||||
resolvedUri: 'http://example.com/video/F/F001.m4f',
|
||||
timeline: 103,
|
||||
uri: 'F001.m4f'
|
||||
},
|
||||
{
|
||||
discontinuity: true,
|
||||
duration: 1,
|
||||
map: {
|
||||
resolvedUri: 'http://example.com/video/F/F_init.mp4',
|
||||
uri: 'F_init.mp4'
|
||||
},
|
||||
presentationTime: 107,
|
||||
number: 2,
|
||||
resolvedUri: 'http://example.com/video/F/F000.m4f',
|
||||
timeline: 107,
|
||||
uri: 'F000.m4f'
|
||||
},
|
||||
{
|
||||
duration: 1,
|
||||
map: {
|
||||
resolvedUri: 'http://example.com/video/F/F_init.mp4',
|
||||
uri: 'F_init.mp4'
|
||||
},
|
||||
presentationTime: 108,
|
||||
number: 3,
|
||||
resolvedUri: 'http://example.com/video/F/F001.m4f',
|
||||
timeline: 107,
|
||||
uri: 'F001.m4f'
|
||||
},
|
||||
{
|
||||
discontinuity: true,
|
||||
duration: 1,
|
||||
map: {
|
||||
resolvedUri: 'http://example.com/video/F/F_init.mp4',
|
||||
uri: 'F_init.mp4'
|
||||
},
|
||||
presentationTime: 111,
|
||||
number: 4,
|
||||
resolvedUri: 'http://example.com/video/F/F862.m4f',
|
||||
timeline: 111,
|
||||
uri: 'F862.m4f'
|
||||
},
|
||||
{
|
||||
duration: 1,
|
||||
map: {
|
||||
resolvedUri: 'http://example.com/video/F/F_init.mp4',
|
||||
uri: 'F_init.mp4'
|
||||
},
|
||||
presentationTime: 112,
|
||||
number: 5,
|
||||
resolvedUri: 'http://example.com/video/F/F863.m4f',
|
||||
timeline: 111,
|
||||
uri: 'F863.m4f'
|
||||
},
|
||||
{
|
||||
duration: 1,
|
||||
map: {
|
||||
resolvedUri: 'http://example.com/video/F/F_init.mp4',
|
||||
uri: 'F_init.mp4'
|
||||
},
|
||||
presentationTime: 113,
|
||||
number: 6,
|
||||
resolvedUri: 'http://example.com/video/F/F864.m4f',
|
||||
timeline: 111,
|
||||
uri: 'F864.m4f'
|
||||
}
|
||||
],
|
||||
targetDuration: 2,
|
||||
timeline: 103,
|
||||
timelineStarts: [
|
||||
{ start: 103, timeline: 103},
|
||||
{ start: 107, timeline: 107},
|
||||
{ start: 111, timeline: 111}
|
||||
],
|
||||
uri: ''
|
||||
},
|
||||
{
|
||||
attributes: {
|
||||
'AUDIO': 'audio',
|
||||
'BANDWIDTH': 1048480,
|
||||
'CODECS': 'avc1.4d001f',
|
||||
'FRAME-RATE': 30,
|
||||
'NAME': 'C',
|
||||
'PROGRAM-ID': 1,
|
||||
|
@ -959,63 +1068,20 @@ export const parsedManifest = {
|
|||
},
|
||||
'SUBTITLES': 'subs'
|
||||
},
|
||||
discontinuitySequence: 1,
|
||||
discontinuityStarts: [2, 4],
|
||||
endList: false,
|
||||
mediaSequence: 0,
|
||||
discontinuitySequence: 0,
|
||||
discontinuityStarts: [3, 5, 7],
|
||||
timelineStarts: [
|
||||
{ start: 100, timeline: 100},
|
||||
{ start: 103, timeline: 103},
|
||||
{ start: 107, timeline: 107},
|
||||
{ start: 111, timeline: 111}
|
||||
],
|
||||
resolvedUri: '',
|
||||
resolvedUri: 'http://example.com/video/C/',
|
||||
segments: [
|
||||
{
|
||||
duration: 1,
|
||||
map: {
|
||||
resolvedUri: 'http://example.com/video/E/C_init.mp4',
|
||||
uri: 'C_init.mp4'
|
||||
},
|
||||
presentationTime: 100,
|
||||
number: 0,
|
||||
resolvedUri: 'http://example.com/video/E/C500.m4f',
|
||||
timeline: 100,
|
||||
uri: 'C500.m4f'
|
||||
},
|
||||
{
|
||||
duration: 1,
|
||||
map: {
|
||||
resolvedUri: 'http://example.com/video/E/C_init.mp4',
|
||||
uri: 'C_init.mp4'
|
||||
},
|
||||
presentationTime: 101,
|
||||
number: 1,
|
||||
resolvedUri: 'http://example.com/video/E/C501.m4f',
|
||||
timeline: 100,
|
||||
uri: 'C501.m4f'
|
||||
},
|
||||
{
|
||||
duration: 1,
|
||||
map: {
|
||||
resolvedUri: 'http://example.com/video/E/C_init.mp4',
|
||||
uri: 'C_init.mp4'
|
||||
},
|
||||
presentationTime: 102,
|
||||
number: 2,
|
||||
resolvedUri: 'http://example.com/video/E/C502.m4f',
|
||||
timeline: 100,
|
||||
uri: 'C502.m4f'
|
||||
},
|
||||
{
|
||||
discontinuity: true,
|
||||
duration: 2,
|
||||
map: {
|
||||
resolvedUri: 'http://example.com/video/C/C_init.mp4',
|
||||
uri: 'C_init.mp4'
|
||||
},
|
||||
presentationTime: 103,
|
||||
number: 3,
|
||||
number: 0,
|
||||
resolvedUri: 'http://example.com/video/C/C000.m4f',
|
||||
timeline: 103,
|
||||
uri: 'C000.m4f'
|
||||
|
@ -1027,7 +1093,7 @@ export const parsedManifest = {
|
|||
uri: 'C_init.mp4'
|
||||
},
|
||||
presentationTime: 105,
|
||||
number: 4,
|
||||
number: 1,
|
||||
resolvedUri: 'http://example.com/video/C/C001.m4f',
|
||||
timeline: 103,
|
||||
uri: 'C001.m4f'
|
||||
|
@ -1040,7 +1106,7 @@ export const parsedManifest = {
|
|||
uri: 'C_init.mp4'
|
||||
},
|
||||
presentationTime: 107,
|
||||
number: 5,
|
||||
number: 2,
|
||||
resolvedUri: 'http://example.com/video/C/C000.m4f',
|
||||
timeline: 107,
|
||||
uri: 'C000.m4f'
|
||||
|
@ -1052,7 +1118,7 @@ export const parsedManifest = {
|
|||
uri: 'C_init.mp4'
|
||||
},
|
||||
presentationTime: 109,
|
||||
number: 6,
|
||||
number: 3,
|
||||
resolvedUri: 'http://example.com/video/C/C001.m4f',
|
||||
timeline: 107,
|
||||
uri: 'C001.m4f'
|
||||
|
@ -1065,7 +1131,7 @@ export const parsedManifest = {
|
|||
uri: 'C_init.mp4'
|
||||
},
|
||||
presentationTime: 111,
|
||||
number: 7,
|
||||
number: 4,
|
||||
resolvedUri: 'http://example.com/video/C/C862.m4f',
|
||||
timeline: 111,
|
||||
uri: 'C862.m4f'
|
||||
|
@ -1077,7 +1143,7 @@ export const parsedManifest = {
|
|||
uri: 'C_init.mp4'
|
||||
},
|
||||
presentationTime: 112,
|
||||
number: 8,
|
||||
number: 5,
|
||||
resolvedUri: 'http://example.com/video/C/C863.m4f',
|
||||
timeline: 111,
|
||||
uri: 'C863.m4f'
|
||||
|
@ -1089,14 +1155,19 @@ export const parsedManifest = {
|
|||
uri: 'C_init.mp4'
|
||||
},
|
||||
presentationTime: 113,
|
||||
number: 9,
|
||||
number: 6,
|
||||
resolvedUri: 'http://example.com/video/C/C864.m4f',
|
||||
timeline: 111,
|
||||
uri: 'C864.m4f'
|
||||
}
|
||||
],
|
||||
targetDuration: 1,
|
||||
timeline: 100,
|
||||
targetDuration: 2,
|
||||
timeline: 103,
|
||||
timelineStarts: [
|
||||
{ start: 103, timeline: 103},
|
||||
{ start: 107, timeline: 107},
|
||||
{ start: 111, timeline: 111}
|
||||
],
|
||||
uri: ''
|
||||
}
|
||||
],
|
||||
|
|
2
node_modules/mpd-parser/test/manifests/segmentBase.js
generated
vendored
2
node_modules/mpd-parser/test/manifests/segmentBase.js
generated
vendored
|
@ -25,7 +25,7 @@ export const parsedManifest = {
|
|||
'SUBTITLES': 'subs'
|
||||
},
|
||||
endList: true,
|
||||
resolvedUri: '',
|
||||
resolvedUri: 'https://www.example.com/1080p.ts',
|
||||
targetDuration: 6,
|
||||
mediaSequence: 0,
|
||||
segments: [
|
||||
|
|
4
node_modules/mpd-parser/test/manifests/segmentList.js
generated
vendored
4
node_modules/mpd-parser/test/manifests/segmentList.js
generated
vendored
|
@ -27,7 +27,7 @@ export const parsedManifest = {
|
|||
endList: true,
|
||||
mediaSequence: 0,
|
||||
targetDuration: 1,
|
||||
resolvedUri: '',
|
||||
resolvedUri: 'https://www.example.com/base',
|
||||
segments: [
|
||||
{
|
||||
duration: 1,
|
||||
|
@ -123,7 +123,7 @@ export const parsedManifest = {
|
|||
'SUBTITLES': 'subs'
|
||||
},
|
||||
endList: true,
|
||||
resolvedUri: '',
|
||||
resolvedUri: 'https://www.example.com/base',
|
||||
mediaSequence: 0,
|
||||
targetDuration: 60,
|
||||
segments: [
|
||||
|
|
12
node_modules/mpd-parser/test/manifests/vtt_codecs.js
generated
vendored
12
node_modules/mpd-parser/test/manifests/vtt_codecs.js
generated
vendored
|
@ -25,7 +25,7 @@ export const parsedManifest = {
|
|||
uri: '',
|
||||
endList: true,
|
||||
timeline: 0,
|
||||
resolvedUri: '',
|
||||
resolvedUri: 'https://www.example.com/base',
|
||||
targetDuration: 1.984,
|
||||
segments: [
|
||||
{
|
||||
|
@ -103,7 +103,7 @@ export const parsedManifest = {
|
|||
uri: '',
|
||||
endList: true,
|
||||
timeline: 0,
|
||||
resolvedUri: '',
|
||||
resolvedUri: 'https://www.example.com/base',
|
||||
targetDuration: 1.984,
|
||||
segments: [
|
||||
{
|
||||
|
@ -189,7 +189,7 @@ export const parsedManifest = {
|
|||
uri: '',
|
||||
endList: true,
|
||||
timeline: 0,
|
||||
resolvedUri: '',
|
||||
resolvedUri: 'https://www.example.com/base',
|
||||
targetDuration: 1.984,
|
||||
segments: [
|
||||
{
|
||||
|
@ -267,7 +267,7 @@ export const parsedManifest = {
|
|||
uri: '',
|
||||
endList: true,
|
||||
timeline: 0,
|
||||
resolvedUri: '',
|
||||
resolvedUri: 'https://www.example.com/base',
|
||||
targetDuration: 1.984,
|
||||
segments: [
|
||||
{
|
||||
|
@ -440,7 +440,7 @@ export const parsedManifest = {
|
|||
uri: '',
|
||||
endList: true,
|
||||
timeline: 0,
|
||||
resolvedUri: '',
|
||||
resolvedUri: 'https://www.example.com/base',
|
||||
targetDuration: 1.9185833333333333,
|
||||
segments: [
|
||||
{
|
||||
|
@ -525,7 +525,7 @@ export const parsedManifest = {
|
|||
uri: '',
|
||||
endList: true,
|
||||
timeline: 0,
|
||||
resolvedUri: '',
|
||||
resolvedUri: 'https://www.example.com/base',
|
||||
targetDuration: 1.9185833333333333,
|
||||
segments: [
|
||||
{
|
||||
|
|
4
node_modules/mpd-parser/test/manifests/webmsegments.js
generated
vendored
4
node_modules/mpd-parser/test/manifests/webmsegments.js
generated
vendored
|
@ -22,7 +22,7 @@ export const parsedManifest = {
|
|||
uri: '',
|
||||
endList: true,
|
||||
timeline: 0,
|
||||
resolvedUri: '',
|
||||
resolvedUri: 'https://www.example.com/base',
|
||||
targetDuration: 4,
|
||||
segments: [
|
||||
{
|
||||
|
@ -108,7 +108,7 @@ export const parsedManifest = {
|
|||
uri: '',
|
||||
endList: true,
|
||||
timeline: 0,
|
||||
resolvedUri: '',
|
||||
resolvedUri: 'https://www.example.com/base',
|
||||
targetDuration: 4,
|
||||
segments: [
|
||||
{
|
||||
|
|
585
node_modules/mpd-parser/test/toM3u8.test.js
generated
vendored
585
node_modules/mpd-parser/test/toM3u8.test.js
generated
vendored
|
@ -213,6 +213,591 @@ QUnit.test('playlists', function(assert) {
|
|||
assert.deepEqual(toM3u8({ dashPlaylists }), expected);
|
||||
});
|
||||
|
||||
QUnit.test('playlists with content steering and resolvable URLs', function(assert) {
|
||||
const contentSteering = {
|
||||
defaultServiceLocation: 'beta',
|
||||
proxyServerURL: 'http://127.0.0.1:3455/steer',
|
||||
queryBeforeStart: false,
|
||||
serverURL: 'https://example.com/app/url'
|
||||
};
|
||||
|
||||
const dashPlaylists = [
|
||||
{
|
||||
attributes: {
|
||||
bandwidth: 5000000,
|
||||
baseUrl: 'https://cdn1.example.com/video',
|
||||
clientOffset: 0,
|
||||
codecs: 'avc1.64001e',
|
||||
duration: 0,
|
||||
height: 404,
|
||||
id: 'test',
|
||||
mimeType: 'video/mp4',
|
||||
periodStart: 0,
|
||||
role: {
|
||||
value: 'main'
|
||||
},
|
||||
serviceLocation: 'alpha',
|
||||
sourceDuration: 0,
|
||||
type: 'dyanmic',
|
||||
width: 720
|
||||
},
|
||||
segments: [
|
||||
{
|
||||
duration: 0,
|
||||
map: {
|
||||
resolvedUri: 'https://cdn1.example.com/video',
|
||||
uri: ''
|
||||
},
|
||||
number: 1,
|
||||
presentationTime: 0,
|
||||
resolvedUri: 'https://cdn1.example.com/video',
|
||||
timeline: 0,
|
||||
uri: ''
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
attributes: {
|
||||
bandwidth: 5000000,
|
||||
baseUrl: 'https://cdn2.example.com/video',
|
||||
clientOffset: 0,
|
||||
codecs: 'avc1.64001e',
|
||||
duration: 0,
|
||||
height: 404,
|
||||
id: 'test',
|
||||
mimeType: 'video/mp4',
|
||||
periodStart: 0,
|
||||
role: {
|
||||
value: 'main'
|
||||
},
|
||||
serviceLocation: 'beta',
|
||||
sourceDuration: 0,
|
||||
type: 'dyanmic',
|
||||
width: 720
|
||||
},
|
||||
segments: [
|
||||
{
|
||||
duration: 0,
|
||||
map: {
|
||||
resolvedUri: 'https://cdn2.example.com/video',
|
||||
uri: ''
|
||||
},
|
||||
number: 1,
|
||||
presentationTime: 0,
|
||||
resolvedUri: 'https://cdn2.example.com/video',
|
||||
timeline: 0,
|
||||
uri: ''
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
attributes: {
|
||||
bandwidth: 256,
|
||||
baseUrl: 'https://cdn1.example.com/vtt',
|
||||
clientOffset: 0,
|
||||
duration: 0,
|
||||
id: 'en',
|
||||
lang: 'en',
|
||||
mimeType: 'text/vtt',
|
||||
periodStart: 0,
|
||||
role: {},
|
||||
serviceLocation: 'alpha',
|
||||
sourceDuration: 0,
|
||||
type: 'dyanmic'
|
||||
},
|
||||
segments: [
|
||||
{
|
||||
duration: 0,
|
||||
map: {
|
||||
resolvedUri: 'https://cdn1.example.com/vtt',
|
||||
uri: ''
|
||||
},
|
||||
number: 1,
|
||||
presentationTime: 0,
|
||||
resolvedUri: 'https://cdn1.example.com/vtt',
|
||||
timeline: 0,
|
||||
uri: ''
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
attributes: {
|
||||
bandwidth: 256,
|
||||
baseUrl: 'https://cdn2.example.com/vtt',
|
||||
clientOffset: 0,
|
||||
id: 'en',
|
||||
lang: 'en',
|
||||
mimeType: 'text/vtt',
|
||||
periodStart: 0,
|
||||
role: {},
|
||||
serviceLocation: 'beta',
|
||||
sourceDuration: 0,
|
||||
type: 'dyanmic'
|
||||
}
|
||||
}
|
||||
];
|
||||
|
||||
const expected = {
|
||||
allowCache: true,
|
||||
contentSteering: {
|
||||
defaultServiceLocation: 'beta',
|
||||
proxyServerURL: 'http://127.0.0.1:3455/steer',
|
||||
queryBeforeStart: false,
|
||||
serverURL: 'https://example.com/app/url'
|
||||
},
|
||||
discontinuityStarts: [],
|
||||
duration: 0,
|
||||
endList: true,
|
||||
mediaGroups: {
|
||||
AUDIO: {},
|
||||
['CLOSED-CAPTIONS']: {},
|
||||
SUBTITLES: {
|
||||
subs: {
|
||||
en: {
|
||||
autoselect: false,
|
||||
default: false,
|
||||
language: 'en',
|
||||
playlists: [
|
||||
{
|
||||
attributes: {
|
||||
BANDWIDTH: 256,
|
||||
NAME: 'en',
|
||||
['PROGRAM-ID']: 1,
|
||||
serviceLocation: 'alpha'
|
||||
},
|
||||
discontinuitySequence: 0,
|
||||
discontinuityStarts: [],
|
||||
endList: false,
|
||||
mediaSequence: 0,
|
||||
resolvedUri: 'https://cdn1.example.com/vtt',
|
||||
segments: [
|
||||
{
|
||||
duration: 0,
|
||||
map: {
|
||||
resolvedUri: 'https://cdn1.example.com/vtt',
|
||||
uri: ''
|
||||
},
|
||||
number: 0,
|
||||
presentationTime: 0,
|
||||
resolvedUri: 'https://cdn1.example.com/vtt',
|
||||
timeline: 0,
|
||||
uri: ''
|
||||
}
|
||||
],
|
||||
targetDuration: 0,
|
||||
timeline: 0,
|
||||
timelineStarts: [
|
||||
{
|
||||
start: 0,
|
||||
timeline: 0
|
||||
}
|
||||
],
|
||||
uri: ''
|
||||
},
|
||||
{
|
||||
attributes: {
|
||||
BANDWIDTH: 256,
|
||||
NAME: 'en',
|
||||
['PROGRAM-ID']: 1,
|
||||
serviceLocation: 'beta'
|
||||
},
|
||||
discontinuitySequence: 0,
|
||||
discontinuityStarts: [],
|
||||
endList: false,
|
||||
mediaSequence: 0,
|
||||
resolvedUri: 'https://cdn2.example.com/vtt',
|
||||
segments: [
|
||||
{
|
||||
duration: 0,
|
||||
number: 0,
|
||||
resolvedUri: 'https://cdn2.example.com/vtt',
|
||||
timeline: 0,
|
||||
uri: 'https://cdn2.example.com/vtt'
|
||||
}
|
||||
],
|
||||
targetDuration: 0,
|
||||
timeline: 0,
|
||||
timelineStarts: [
|
||||
{
|
||||
start: 0,
|
||||
timeline: 0
|
||||
}
|
||||
],
|
||||
uri: ''
|
||||
}
|
||||
],
|
||||
uri: ''
|
||||
}
|
||||
}
|
||||
},
|
||||
VIDEO: {}
|
||||
},
|
||||
playlists: [
|
||||
{
|
||||
attributes: {
|
||||
AUDIO: 'audio',
|
||||
BANDWIDTH: 5000000,
|
||||
CODECS: 'avc1.64001e',
|
||||
NAME: 'test',
|
||||
['PROGRAM-ID']: 1,
|
||||
RESOLUTION: {
|
||||
height: 404,
|
||||
width: 720
|
||||
},
|
||||
SUBTITLES: 'subs',
|
||||
serviceLocation: 'alpha'
|
||||
},
|
||||
discontinuitySequence: 0,
|
||||
discontinuityStarts: [],
|
||||
endList: false,
|
||||
mediaSequence: 0,
|
||||
resolvedUri: 'https://cdn1.example.com/video',
|
||||
segments: [
|
||||
{
|
||||
duration: 0,
|
||||
map: {
|
||||
resolvedUri: 'https://cdn1.example.com/video',
|
||||
uri: ''
|
||||
},
|
||||
number: 0,
|
||||
presentationTime: 0,
|
||||
resolvedUri: 'https://cdn1.example.com/video',
|
||||
timeline: 0,
|
||||
uri: ''
|
||||
}
|
||||
],
|
||||
targetDuration: 0,
|
||||
timeline: 0,
|
||||
timelineStarts: [
|
||||
{
|
||||
start: 0,
|
||||
timeline: 0
|
||||
}
|
||||
],
|
||||
uri: ''
|
||||
},
|
||||
{
|
||||
attributes: {
|
||||
AUDIO: 'audio',
|
||||
BANDWIDTH: 5000000,
|
||||
CODECS: 'avc1.64001e',
|
||||
NAME: 'test',
|
||||
['PROGRAM-ID']: 1,
|
||||
RESOLUTION: {
|
||||
height: 404,
|
||||
width: 720
|
||||
},
|
||||
SUBTITLES: 'subs',
|
||||
serviceLocation: 'beta'
|
||||
},
|
||||
discontinuitySequence: 0,
|
||||
discontinuityStarts: [],
|
||||
endList: false,
|
||||
mediaSequence: 0,
|
||||
resolvedUri: 'https://cdn2.example.com/video',
|
||||
segments: [
|
||||
{
|
||||
duration: 0,
|
||||
map: {
|
||||
resolvedUri: 'https://cdn2.example.com/video',
|
||||
uri: ''
|
||||
},
|
||||
number: 0,
|
||||
presentationTime: 0,
|
||||
resolvedUri: 'https://cdn2.example.com/video',
|
||||
timeline: 0,
|
||||
uri: ''
|
||||
}
|
||||
],
|
||||
targetDuration: 0,
|
||||
timeline: 0,
|
||||
timelineStarts: [
|
||||
{
|
||||
start: 0,
|
||||
timeline: 0
|
||||
}
|
||||
],
|
||||
uri: ''
|
||||
}
|
||||
],
|
||||
segments: [],
|
||||
timelineStarts: [
|
||||
{
|
||||
start: 0,
|
||||
timeline: 0
|
||||
}
|
||||
],
|
||||
uri: ''
|
||||
};
|
||||
|
||||
assert.deepEqual(toM3u8({ dashPlaylists, contentSteering }), expected);
|
||||
});
|
||||
|
||||
QUnit.test('playlists with content steering', function(assert) {
|
||||
const contentSteering = {
|
||||
defaultServiceLocation: 'beta',
|
||||
proxyServerURL: 'http://127.0.0.1:3455/steer',
|
||||
queryBeforeStart: false,
|
||||
serverURL: 'https://example.com/app/url'
|
||||
};
|
||||
|
||||
const dashPlaylists = [{
|
||||
attributes: {
|
||||
bandwidth: 5000000,
|
||||
baseUrl: 'https://cdn1.example.com/',
|
||||
clientOffset: 0,
|
||||
codecs: 'avc1.64001e',
|
||||
duration: 0,
|
||||
height: 404,
|
||||
id: 'test',
|
||||
mimeType: 'video/mp4',
|
||||
periodStart: 0,
|
||||
role: {
|
||||
value: 'main'
|
||||
},
|
||||
serviceLocation: 'alpha',
|
||||
sourceDuration: 0,
|
||||
type: 'dyanmic',
|
||||
width: 720
|
||||
},
|
||||
segments: [
|
||||
{
|
||||
duration: 0,
|
||||
map: {
|
||||
resolvedUri: 'https://cdn1.example.com/',
|
||||
uri: ''
|
||||
},
|
||||
number: 1,
|
||||
presentationTime: 0,
|
||||
resolvedUri: 'https://cdn1.example.com/',
|
||||
timeline: 0,
|
||||
uri: ''
|
||||
}
|
||||
]
|
||||
}, {
|
||||
attributes: {
|
||||
bandwidth: 5000000,
|
||||
baseUrl: 'https://cdn2.example.com/',
|
||||
clientOffset: 0,
|
||||
codecs: 'avc1.64001e',
|
||||
duration: 0,
|
||||
height: 404,
|
||||
id: 'test',
|
||||
mimeType: 'video/mp4',
|
||||
periodStart: 0,
|
||||
role: {
|
||||
value: 'main'
|
||||
},
|
||||
serviceLocation: 'beta',
|
||||
sourceDuration: 0,
|
||||
type: 'dyanmic',
|
||||
width: 720
|
||||
},
|
||||
segments: [
|
||||
{
|
||||
duration: 0,
|
||||
map: {
|
||||
resolvedUri: 'https://cdn2.example.com/',
|
||||
uri: ''
|
||||
},
|
||||
number: 1,
|
||||
presentationTime: 0,
|
||||
resolvedUri: 'https://cdn2.example.com/',
|
||||
timeline: 0,
|
||||
uri: ''
|
||||
}
|
||||
]
|
||||
}, {
|
||||
attributes: {
|
||||
bandwidth: 256,
|
||||
baseUrl: 'https://example.com/en.vtt',
|
||||
clientOffset: 0,
|
||||
id: 'en',
|
||||
lang: 'en',
|
||||
mimeType: 'text/vtt',
|
||||
periodStart: 0,
|
||||
role: {},
|
||||
sourceDuration: 0,
|
||||
type: 'dyanmic'
|
||||
}
|
||||
}, {
|
||||
attributes: {
|
||||
bandwidth: 256,
|
||||
baseUrl: 'https://example.com/en.vtt',
|
||||
clientOffset: 0,
|
||||
id: 'en',
|
||||
lang: 'en',
|
||||
mimeType: 'text/vtt',
|
||||
periodStart: 0,
|
||||
role: {},
|
||||
sourceDuration: 0,
|
||||
type: 'dyanmic'
|
||||
}
|
||||
}];
|
||||
|
||||
const expected = {
|
||||
allowCache: true,
|
||||
contentSteering: {
|
||||
defaultServiceLocation: 'beta',
|
||||
proxyServerURL: 'http://127.0.0.1:3455/steer',
|
||||
queryBeforeStart: false,
|
||||
serverURL: 'https://example.com/app/url'
|
||||
},
|
||||
discontinuityStarts: [],
|
||||
duration: 0,
|
||||
endList: true,
|
||||
mediaGroups: {
|
||||
AUDIO: {},
|
||||
['CLOSED-CAPTIONS']: {},
|
||||
SUBTITLES: {
|
||||
subs: {
|
||||
en: {
|
||||
autoselect: false,
|
||||
default: false,
|
||||
language: 'en',
|
||||
playlists: [
|
||||
{
|
||||
attributes: {
|
||||
BANDWIDTH: 256,
|
||||
NAME: 'en',
|
||||
['PROGRAM-ID']: 1
|
||||
},
|
||||
discontinuitySequence: 0,
|
||||
discontinuityStarts: [],
|
||||
endList: false,
|
||||
mediaSequence: 0,
|
||||
resolvedUri: 'https://example.com/en.vtt',
|
||||
segments: [
|
||||
{
|
||||
duration: 0,
|
||||
number: 0,
|
||||
resolvedUri: 'https://example.com/en.vtt',
|
||||
timeline: 0,
|
||||
uri: 'https://example.com/en.vtt'
|
||||
}
|
||||
],
|
||||
targetDuration: 0,
|
||||
timeline: 0,
|
||||
timelineStarts: [
|
||||
{
|
||||
start: 0,
|
||||
timeline: 0
|
||||
},
|
||||
{
|
||||
start: 0,
|
||||
timeline: 0
|
||||
}
|
||||
],
|
||||
uri: ''
|
||||
}
|
||||
],
|
||||
uri: ''
|
||||
}
|
||||
}
|
||||
},
|
||||
VIDEO: {}
|
||||
},
|
||||
playlists: [
|
||||
{
|
||||
attributes: {
|
||||
AUDIO: 'audio',
|
||||
BANDWIDTH: 5000000,
|
||||
CODECS: 'avc1.64001e',
|
||||
NAME: 'test',
|
||||
['PROGRAM-ID']: 1,
|
||||
RESOLUTION: {
|
||||
height: 404,
|
||||
width: 720
|
||||
},
|
||||
SUBTITLES: 'subs',
|
||||
serviceLocation: 'alpha'
|
||||
},
|
||||
discontinuitySequence: 0,
|
||||
discontinuityStarts: [],
|
||||
endList: false,
|
||||
mediaSequence: 0,
|
||||
resolvedUri: 'https://cdn1.example.com/',
|
||||
segments: [
|
||||
{
|
||||
duration: 0,
|
||||
map: {
|
||||
resolvedUri: 'https://cdn1.example.com/',
|
||||
uri: ''
|
||||
},
|
||||
number: 0,
|
||||
presentationTime: 0,
|
||||
resolvedUri: 'https://cdn1.example.com/',
|
||||
timeline: 0,
|
||||
uri: ''
|
||||
}
|
||||
],
|
||||
targetDuration: 0,
|
||||
timeline: 0,
|
||||
timelineStarts: [
|
||||
{
|
||||
start: 0,
|
||||
timeline: 0
|
||||
}
|
||||
],
|
||||
uri: ''
|
||||
},
|
||||
{
|
||||
attributes: {
|
||||
AUDIO: 'audio',
|
||||
BANDWIDTH: 5000000,
|
||||
CODECS: 'avc1.64001e',
|
||||
NAME: 'test',
|
||||
['PROGRAM-ID']: 1,
|
||||
RESOLUTION: {
|
||||
height: 404,
|
||||
width: 720
|
||||
},
|
||||
SUBTITLES: 'subs',
|
||||
serviceLocation: 'beta'
|
||||
},
|
||||
discontinuitySequence: 0,
|
||||
discontinuityStarts: [],
|
||||
endList: false,
|
||||
mediaSequence: 0,
|
||||
resolvedUri: 'https://cdn2.example.com/',
|
||||
segments: [
|
||||
{
|
||||
duration: 0,
|
||||
map: {
|
||||
resolvedUri: 'https://cdn2.example.com/',
|
||||
uri: ''
|
||||
},
|
||||
number: 0,
|
||||
presentationTime: 0,
|
||||
resolvedUri: 'https://cdn2.example.com/',
|
||||
timeline: 0,
|
||||
uri: ''
|
||||
}
|
||||
],
|
||||
targetDuration: 0,
|
||||
timeline: 0,
|
||||
timelineStarts: [
|
||||
{
|
||||
start: 0,
|
||||
timeline: 0
|
||||
}
|
||||
],
|
||||
uri: ''
|
||||
}
|
||||
],
|
||||
segments: [],
|
||||
timelineStarts: [
|
||||
{
|
||||
start: 0,
|
||||
timeline: 0
|
||||
}
|
||||
],
|
||||
uri: ''
|
||||
};
|
||||
|
||||
assert.deepEqual(toM3u8({ dashPlaylists, contentSteering }), expected);
|
||||
});
|
||||
|
||||
QUnit.test('playlists with segments', function(assert) {
|
||||
const dashPlaylists = [{
|
||||
attributes: {
|
||||
|
|
376
node_modules/mpd-parser/test/toPlaylists.test.js
generated
vendored
376
node_modules/mpd-parser/test/toPlaylists.test.js
generated
vendored
|
@ -85,6 +85,181 @@ QUnit.test('segment base', function(assert) {
|
|||
assert.deepEqual(toPlaylists(representations), playlists);
|
||||
});
|
||||
|
||||
QUnit.test('playlist with content steering BaseURLs', function(assert) {
|
||||
const representations = [
|
||||
{
|
||||
attributes: {
|
||||
bandwidth: 5000000,
|
||||
baseUrl: 'https://cdn1.example.com/',
|
||||
clientOffset: 0,
|
||||
codecs: 'avc1.64001e',
|
||||
height: 404,
|
||||
id: 'test',
|
||||
mimeType: 'video/mp4',
|
||||
periodStart: 0,
|
||||
role: {
|
||||
value: 'main'
|
||||
},
|
||||
serviceLocation: 'alpha',
|
||||
sourceDuration: 0,
|
||||
type: 'dyanmic',
|
||||
width: 720
|
||||
},
|
||||
segmentInfo: {
|
||||
template: {}
|
||||
}
|
||||
},
|
||||
{
|
||||
attributes: {
|
||||
bandwidth: 5000000,
|
||||
baseUrl: 'https://cdn2.example.com/',
|
||||
clientOffset: 0,
|
||||
codecs: 'avc1.64001e',
|
||||
height: 404,
|
||||
id: 'test',
|
||||
mimeType: 'video/mp4',
|
||||
periodStart: 0,
|
||||
role: {
|
||||
value: 'main'
|
||||
},
|
||||
serviceLocation: 'beta',
|
||||
sourceDuration: 0,
|
||||
type: 'dyanmic',
|
||||
width: 720
|
||||
},
|
||||
segmentInfo: {
|
||||
template: {}
|
||||
}
|
||||
},
|
||||
{
|
||||
attributes: {
|
||||
bandwidth: 256,
|
||||
baseUrl: 'https://example.com/en.vtt',
|
||||
clientOffset: 0,
|
||||
id: 'en',
|
||||
lang: 'en',
|
||||
mimeType: 'text/vtt',
|
||||
periodStart: 0,
|
||||
role: {},
|
||||
sourceDuration: 0,
|
||||
type: 'dyanmic'
|
||||
},
|
||||
segmentInfo: {}
|
||||
},
|
||||
{
|
||||
attributes: {
|
||||
bandwidth: 256,
|
||||
baseUrl: 'https://example.com/en.vtt',
|
||||
clientOffset: 0,
|
||||
id: 'en',
|
||||
lang: 'en',
|
||||
mimeType: 'text/vtt',
|
||||
periodStart: 0,
|
||||
role: {},
|
||||
sourceDuration: 0,
|
||||
type: 'dyanmic'
|
||||
},
|
||||
segmentInfo: {}
|
||||
}
|
||||
];
|
||||
|
||||
const playlists = [{
|
||||
attributes: {
|
||||
bandwidth: 5000000,
|
||||
baseUrl: 'https://cdn1.example.com/',
|
||||
clientOffset: 0,
|
||||
codecs: 'avc1.64001e',
|
||||
duration: 0,
|
||||
height: 404,
|
||||
id: 'test',
|
||||
mimeType: 'video/mp4',
|
||||
periodStart: 0,
|
||||
role: {
|
||||
value: 'main'
|
||||
},
|
||||
serviceLocation: 'alpha',
|
||||
sourceDuration: 0,
|
||||
type: 'dyanmic',
|
||||
width: 720
|
||||
},
|
||||
segments: [
|
||||
{
|
||||
duration: 0,
|
||||
map: {
|
||||
resolvedUri: 'https://cdn1.example.com/',
|
||||
uri: ''
|
||||
},
|
||||
number: 1,
|
||||
presentationTime: 0,
|
||||
resolvedUri: 'https://cdn1.example.com/',
|
||||
timeline: 0,
|
||||
uri: ''
|
||||
}
|
||||
]
|
||||
}, {
|
||||
attributes: {
|
||||
bandwidth: 5000000,
|
||||
baseUrl: 'https://cdn2.example.com/',
|
||||
clientOffset: 0,
|
||||
codecs: 'avc1.64001e',
|
||||
duration: 0,
|
||||
height: 404,
|
||||
id: 'test',
|
||||
mimeType: 'video/mp4',
|
||||
periodStart: 0,
|
||||
role: {
|
||||
value: 'main'
|
||||
},
|
||||
serviceLocation: 'beta',
|
||||
sourceDuration: 0,
|
||||
type: 'dyanmic',
|
||||
width: 720
|
||||
},
|
||||
segments: [
|
||||
{
|
||||
duration: 0,
|
||||
map: {
|
||||
resolvedUri: 'https://cdn2.example.com/',
|
||||
uri: ''
|
||||
},
|
||||
number: 1,
|
||||
presentationTime: 0,
|
||||
resolvedUri: 'https://cdn2.example.com/',
|
||||
timeline: 0,
|
||||
uri: ''
|
||||
}
|
||||
]
|
||||
}, {
|
||||
attributes: {
|
||||
bandwidth: 256,
|
||||
baseUrl: 'https://example.com/en.vtt',
|
||||
clientOffset: 0,
|
||||
id: 'en',
|
||||
lang: 'en',
|
||||
mimeType: 'text/vtt',
|
||||
periodStart: 0,
|
||||
role: {},
|
||||
sourceDuration: 0,
|
||||
type: 'dyanmic'
|
||||
}
|
||||
}, {
|
||||
attributes: {
|
||||
bandwidth: 256,
|
||||
baseUrl: 'https://example.com/en.vtt',
|
||||
clientOffset: 0,
|
||||
id: 'en',
|
||||
lang: 'en',
|
||||
mimeType: 'text/vtt',
|
||||
periodStart: 0,
|
||||
role: {},
|
||||
sourceDuration: 0,
|
||||
type: 'dyanmic'
|
||||
}
|
||||
}];
|
||||
|
||||
assert.deepEqual(toPlaylists(representations), playlists);
|
||||
});
|
||||
|
||||
QUnit.test('segment base with sidx', function(assert) {
|
||||
const representations = [{
|
||||
attributes: {
|
||||
|
@ -235,3 +410,204 @@ QUnit.test('presentationTime accounts for presentationTimeOffset', function(asse
|
|||
|
||||
assert.deepEqual(toPlaylists(representations), playlists);
|
||||
});
|
||||
|
||||
QUnit.test('playlist with content steering and resolvable BaseURLs', function(assert) {
|
||||
const representations = [
|
||||
{
|
||||
attributes: {
|
||||
bandwidth: 5000000,
|
||||
baseUrl: 'https://cdn1.example.com/video',
|
||||
clientOffset: 0,
|
||||
codecs: 'avc1.64001e',
|
||||
height: 404,
|
||||
id: 'test',
|
||||
mimeType: 'video/mp4',
|
||||
periodStart: 0,
|
||||
role: {
|
||||
value: 'main'
|
||||
},
|
||||
serviceLocation: 'alpha',
|
||||
sourceDuration: 0,
|
||||
type: 'dyanmic',
|
||||
width: 720
|
||||
},
|
||||
segmentInfo: {
|
||||
template: {}
|
||||
}
|
||||
},
|
||||
{
|
||||
attributes: {
|
||||
bandwidth: 5000000,
|
||||
baseUrl: 'https://cdn2.example.com/video',
|
||||
clientOffset: 0,
|
||||
codecs: 'avc1.64001e',
|
||||
height: 404,
|
||||
id: 'test',
|
||||
mimeType: 'video/mp4',
|
||||
periodStart: 0,
|
||||
role: {
|
||||
value: 'main'
|
||||
},
|
||||
serviceLocation: 'beta',
|
||||
sourceDuration: 0,
|
||||
type: 'dyanmic',
|
||||
width: 720
|
||||
},
|
||||
segmentInfo: {
|
||||
template: {}
|
||||
}
|
||||
},
|
||||
{
|
||||
attributes: {
|
||||
bandwidth: 256,
|
||||
baseUrl: 'https://cdn1.example.com/vtt',
|
||||
clientOffset: 0,
|
||||
id: 'en',
|
||||
lang: 'en',
|
||||
mimeType: 'text/vtt',
|
||||
periodStart: 0,
|
||||
role: {},
|
||||
serviceLocation: 'alpha',
|
||||
sourceDuration: 0,
|
||||
type: 'dyanmic'
|
||||
},
|
||||
segmentInfo: {
|
||||
template: {}
|
||||
}
|
||||
},
|
||||
{
|
||||
attributes: {
|
||||
bandwidth: 256,
|
||||
baseUrl: 'https://cdn2.example.com/vtt',
|
||||
clientOffset: 0,
|
||||
id: 'en',
|
||||
lang: 'en',
|
||||
mimeType: 'text/vtt',
|
||||
periodStart: 0,
|
||||
role: {},
|
||||
serviceLocation: 'beta',
|
||||
sourceDuration: 0,
|
||||
type: 'dyanmic'
|
||||
},
|
||||
segmentInfo: {}
|
||||
}
|
||||
];
|
||||
|
||||
const playlists = [
|
||||
{
|
||||
attributes: {
|
||||
bandwidth: 5000000,
|
||||
baseUrl: 'https://cdn1.example.com/video',
|
||||
clientOffset: 0,
|
||||
codecs: 'avc1.64001e',
|
||||
duration: 0,
|
||||
height: 404,
|
||||
id: 'test',
|
||||
mimeType: 'video/mp4',
|
||||
periodStart: 0,
|
||||
role: {
|
||||
value: 'main'
|
||||
},
|
||||
serviceLocation: 'alpha',
|
||||
sourceDuration: 0,
|
||||
type: 'dyanmic',
|
||||
width: 720
|
||||
},
|
||||
segments: [
|
||||
{
|
||||
duration: 0,
|
||||
map: {
|
||||
resolvedUri: 'https://cdn1.example.com/video',
|
||||
uri: ''
|
||||
},
|
||||
number: 1,
|
||||
presentationTime: 0,
|
||||
resolvedUri: 'https://cdn1.example.com/video',
|
||||
timeline: 0,
|
||||
uri: ''
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
attributes: {
|
||||
bandwidth: 5000000,
|
||||
baseUrl: 'https://cdn2.example.com/video',
|
||||
clientOffset: 0,
|
||||
codecs: 'avc1.64001e',
|
||||
duration: 0,
|
||||
height: 404,
|
||||
id: 'test',
|
||||
mimeType: 'video/mp4',
|
||||
periodStart: 0,
|
||||
role: {
|
||||
value: 'main'
|
||||
},
|
||||
serviceLocation: 'beta',
|
||||
sourceDuration: 0,
|
||||
type: 'dyanmic',
|
||||
width: 720
|
||||
},
|
||||
segments: [
|
||||
{
|
||||
duration: 0,
|
||||
map: {
|
||||
resolvedUri: 'https://cdn2.example.com/video',
|
||||
uri: ''
|
||||
},
|
||||
number: 1,
|
||||
presentationTime: 0,
|
||||
resolvedUri: 'https://cdn2.example.com/video',
|
||||
timeline: 0,
|
||||
uri: ''
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
attributes: {
|
||||
bandwidth: 256,
|
||||
baseUrl: 'https://cdn1.example.com/vtt',
|
||||
clientOffset: 0,
|
||||
duration: 0,
|
||||
id: 'en',
|
||||
lang: 'en',
|
||||
mimeType: 'text/vtt',
|
||||
periodStart: 0,
|
||||
role: {},
|
||||
serviceLocation: 'alpha',
|
||||
sourceDuration: 0,
|
||||
type: 'dyanmic'
|
||||
},
|
||||
segments: [
|
||||
{
|
||||
duration: 0,
|
||||
map: {
|
||||
resolvedUri: 'https://cdn1.example.com/vtt',
|
||||
uri: ''
|
||||
},
|
||||
number: 1,
|
||||
presentationTime: 0,
|
||||
resolvedUri: 'https://cdn1.example.com/vtt',
|
||||
timeline: 0,
|
||||
uri: ''
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
attributes: {
|
||||
bandwidth: 256,
|
||||
baseUrl: 'https://cdn2.example.com/vtt',
|
||||
clientOffset: 0,
|
||||
id: 'en',
|
||||
lang: 'en',
|
||||
mimeType: 'text/vtt',
|
||||
periodStart: 0,
|
||||
role: {},
|
||||
serviceLocation: 'beta',
|
||||
sourceDuration: 0,
|
||||
type: 'dyanmic'
|
||||
}
|
||||
}
|
||||
];
|
||||
|
||||
assert.deepEqual(toPlaylists(representations), playlists);
|
||||
});
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue