Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/core/fetchers/manifest/manifest_fetcher.ts
Original file line number Diff line number Diff line change
Expand Up @@ -625,7 +625,7 @@ export default class ManifestFetcher extends EventEmitter<IManifestFetcherEvent>
// updates (e.g. redirects or manifest-provided alternatives) to stay the
// source of truth after the refresh completed.
fullRefresh = !enablePartialRefresh || manifestUpdateUrl === undefined;
refreshURL = fullRefresh ? manifest.getUrls()[0] : manifestUpdateUrl;
refreshURL = fullRefresh ? manifest.getRefreshUrls()[0] : manifestUpdateUrl;
}
const externalClockOffset = manifest.clockOffset;

Expand Down
2 changes: 1 addition & 1 deletion src/main_thread/api/public_api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1749,7 +1749,7 @@ class Player extends EventEmitter<IPublicAPIEvent> {
return originalUrl === undefined ? undefined : [originalUrl];
}
if (manifest !== null) {
return manifest.uris;
return manifest.refreshUrls;
}
return undefined;
}
Expand Down
14 changes: 7 additions & 7 deletions src/manifest/classes/manifest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -172,7 +172,7 @@ export default class Manifest
* They can be used for refreshing the Manifest.
* Listed from the most important to the least important.
*/
public uris: string[];
public refreshUrls: string[];

/** Optional URL that points to a shorter version of the Manifest used
* for updates only. */
Expand Down Expand Up @@ -352,7 +352,7 @@ export default class Manifest
this.isDynamic = parsedManifest.isDynamic;
this.isLive = parsedManifest.isLive;
this.isLastPeriodKnown = parsedManifest.isLastPeriodKnown;
this.uris = parsedManifest.uris === undefined ? [] : parsedManifest.uris;
this.refreshUrls = parsedManifest.refreshUrls.map((r) => r.baseUrl);

this.updateUrl = manifestUpdateUrl;
this.lifetime = parsedManifest.lifetime;
Expand Down Expand Up @@ -447,8 +447,8 @@ export default class Manifest
* `undefined` if no URL is found.
* @returns {Array.<string>}
*/
public getUrls(): string[] {
return this.uris;
public getRefreshUrls(): string[] {
return this.refreshUrls;
}

/**
Expand Down Expand Up @@ -650,7 +650,7 @@ export default class Manifest
isLastPeriodKnown: this.isLastPeriodKnown,
suggestedPresentationDelay: this.suggestedPresentationDelay,
clockOffset: this.clockOffset,
uris: this.uris,
refreshUrls: this.refreshUrls,
availabilityStartTime: this.availabilityStartTime,
timeBounds: this.timeBounds,
};
Expand Down Expand Up @@ -689,11 +689,11 @@ export default class Manifest
let updatedPeriodsResult;
if (updateType === MANIFEST_UPDATE_TYPE.Full) {
this.timeBounds = newManifest.timeBounds;
this.uris = newManifest.uris;
this.refreshUrls = newManifest.refreshUrls;
updatedPeriodsResult = replacePeriods(this.periods, newManifest.periods);
} else {
this.timeBounds.maximumTimeData = newManifest.timeBounds.maximumTimeData;
this.updateUrl = newManifest.uris[0];
this.updateUrl = newManifest.refreshUrls[0];
updatedPeriodsResult = updatePeriods(this.periods, newManifest.periods);

// Partial updates do not remove old Periods.
Expand Down
8 changes: 2 additions & 6 deletions src/manifest/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -91,12 +91,8 @@ export interface IManifestMetadata {
*/
isLastPeriodKnown: boolean;

/*
* Every URI linking to that Manifest.
* They can be used for refreshing the Manifest.
* Listed from the most important to the least important.
*/
uris: string[];
/** URLs through which the Manifest can be refreshed, by order of importance. */
refreshUrls: string[];

/**
* Minimum time, in seconds, at which a segment defined in the Manifest
Expand Down
33 changes: 29 additions & 4 deletions src/parsers/manifest/dash/common/parse_mpd.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,11 @@ import type { IManifest } from "../../../../manifest/index.ts";
import arrayFind from "../../../../utils/array_find.ts";
import isNullOrUndefined from "../../../../utils/is_null_or_undefined.ts";
import getMonotonicTimeStamp from "../../../../utils/monotonic_timestamp.ts";
import { getFilenameIndexInUrl } from "../../../../utils/url-utils.ts";
import {
getFilenameIndexInUrl,
isAbsoluteURL,
resolveURL,
} from "../../../../utils/url-utils.ts";
import type { IParsedManifest } from "../../types.ts";
import type {
IMPDIntermediateRepresentation,
Expand Down Expand Up @@ -418,9 +422,33 @@ function parseCompleteIntermediateRepresentation(
(parsedPeriods[parsedPeriods.length - 1]?.end !== undefined ||
mpdIR.attributes.mediaPresentationDuration !== undefined));

const refreshUrls = [];
const manifestUrl =
args.url !== undefined && args.url.length > 0 ? args.url : undefined;
for (const location of rootChildren.Location) {
if (manifestUrl === undefined && !isAbsoluteURL(location.value)) {
warnings.push(
new Error(
`DASH Parser: Cannot resolve relative <Location> URL without a manifest URL: "${location.value}"`,
),
);
continue;
}
refreshUrls.push({
baseUrl:
manifestUrl === undefined
? location.value
: resolveURL(manifestUrl, location.value),
id: location.attributes.serviceLocation,
});
}
if (refreshUrls.length === 0 && manifestUrl !== undefined) {
refreshUrls.push({ baseUrl: manifestUrl, id: undefined });
}
const parsedMPD: IParsedManifest = {
availabilityStartTime,
clockOffset: args.externalClockOffset,
refreshUrls,
isDynamic,
isLive: isDynamic,
isLastPeriodKnown,
Expand All @@ -434,9 +462,6 @@ function parseCompleteIntermediateRepresentation(
maximumTimeData,
},
lifetime,
uris: isNullOrUndefined(args.url)
? rootChildren.Location.map((l) => l.value)
: [args.url, ...rootChildren.Location.map((l) => l.value)],
};

return { type: "done", value: { parsed: parsedMPD, warnings } };
Expand Down
2 changes: 1 addition & 1 deletion src/parsers/manifest/dash/common/resolve_base_urls.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ export default function resolveBaseURLs(
}

const newBaseUrls: IResolvedBaseUrl[] = newBaseUrlsIR.map((ir) => {
return { url: ir.value };
return { url: ir.value, serviceLocation: ir.attributes.serviceLocation };
});
if (currentBaseURLs.length === 0) {
return newBaseUrls;
Expand Down
17 changes: 13 additions & 4 deletions src/parsers/manifest/dash/js-parser/node_parsers/BaseURL.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
* limitations under the License.
*/

import isNullOrUndefined from "../../../../../utils/is_null_or_undefined.ts";
import type { ITNode } from "../../../../../utils/xml-parser.ts";
import type { IBaseUrlIntermediateRepresentation } from "../../node_parser_types.ts";
import { textContent } from "./utils.ts";
Expand All @@ -25,12 +26,20 @@ import { textContent } from "./utils.ts";
* @returns {Array.<Object|undefined>}
*/
export default function parseBaseURL(
root: ITNode | string,
root: ITNode,
): [IBaseUrlIntermediateRepresentation | undefined, Error[]] {
const attributes: { serviceLocation?: string } = {};
const value = typeof root === "string" ? root : textContent(root.children);
const warnings: Error[] = [];
if (value === null || value.length === 0) {
return [undefined, warnings];

for (const attributeName of Object.keys(root.attributes)) {
const attributeVal = root.attributes[attributeName];
if (isNullOrUndefined(attributeVal)) {
continue;
}
if (attributeName === "serviceLocation") {
attributes.serviceLocation = attributeVal;
}
}
return [{ value }, warnings];
return [{ value, attributes }, warnings];
}
18 changes: 16 additions & 2 deletions src/parsers/manifest/dash/js-parser/node_parsers/MPD.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import type {
IMPDAttributes,
IMPDChildren,
IMPDIntermediateRepresentation,
ILocationIntermediateRepresentation,
} from "../../node_parser_types.ts";
import parseBaseURL from "./BaseURL.ts";
import parseContentProtection from "./ContentProtection.ts";
Expand Down Expand Up @@ -66,9 +67,10 @@ function parseMPDChildren(
break;
}

case "Location":
ret.Location.push({ value: textContent(currentNode.children) });
case "Location": {
ret.Location.push(parseLocation(currentNode));
break;
}

case "Period": {
const [period, periodWarnings] = createPeriodIntermediateRepresentation(
Expand Down Expand Up @@ -204,6 +206,18 @@ function parseMPDAttributes(root: ITNode): [IMPDAttributes, Error[]] {
return [res, warnings];
}

/** Parse a root-level `<Location>` element. */
export default function parseLocation(root: ITNode): ILocationIntermediateRepresentation {
const location: ILocationIntermediateRepresentation = {
value: textContent(root.children),
attributes: {},
};
if (typeof root.attributes.serviceLocation === "string") {
location.attributes.serviceLocation = root.attributes.serviceLocation;
}
return location;
}

/**
* @param {Object} root
* @param {string} fullMpd
Expand Down
23 changes: 22 additions & 1 deletion src/parsers/manifest/dash/node_parser_types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ export interface IMPDChildren {
* node,
* from the first encountered to the last encountered.
*/
Location: Array<{ value: string }>;
Location: ILocationIntermediateRepresentation[];
/**
* Temporal subdivisions in that Manifest.
*
Expand Down Expand Up @@ -410,6 +410,27 @@ export interface IBaseUrlIntermediateRepresentation {
* This is the inner content of a BaseURL node.
*/
value: string;

/** Attributes assiociated to the BaseURL node. */
attributes: {
/**
* Potential value for a `serviceLocation` attribute, used in content
* steering mechanisms.
*/
serviceLocation?: string;
};
}

/** Intermediate representation for a Location node. */
export interface ILocationIntermediateRepresentation {
/** The URL contained in the Location node. */
value: string;

/** Attributes associated to the Location node. */
attributes: {
/** Value of the `serviceLocation` attribute. */
serviceLocation?: string | undefined;
};
}

/** Intermediate representation for a Node following a "scheme" format. */
Expand Down
5 changes: 3 additions & 2 deletions src/parsers/manifest/dash/wasm-parser/rs/events.rs
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,9 @@ pub enum TagName {

/// Indicate an <Initialization> node
Initialization = 22,

/// Indicate a `<Location>` node.
Location = 23,
}

#[derive(PartialEq, Clone, Copy)]
Expand Down Expand Up @@ -232,8 +235,6 @@ pub enum AttributeName {

Text = 64,
QualityRanking = 65,
Location = 66,

InitializationMedia = 67,

/// Describes an encountered "mediaPresentationDuration" attribute, as found
Expand Down
12 changes: 12 additions & 0 deletions src/parsers/manifest/dash/wasm-parser/rs/processor/attributes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -284,6 +284,18 @@ pub fn report_segment_url_attrs(tag_bs: &quick_xml::events::BytesStart) {
}
}

pub fn report_location_attrs(tag_bs: &quick_xml::events::BytesStart) {
for res_attr in tag_bs.attributes() {
match res_attr {
Ok(attr) => match attr.key.as_ref() {
b"serviceLocation" => ServiceLocation.try_report_as_string(&attr),
_ => {}
},
Err(err) => ParsingError::from(err).report_err(),
};
}
}

pub fn report_event_stream_attrs(tag_bs: &quick_xml::events::BytesStart) {
for res_attr in tag_bs.attributes() {
match res_attr {
Expand Down
9 changes: 7 additions & 2 deletions src/parsers/manifest/dash/wasm-parser/rs/processor/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,11 @@ impl MPDProcessor {
self.process_base_url_element();
}
b"cenc:pssh" => self.process_cenc_element(),
b"Location" => self.process_location_element(),
b"Location" => {
TagName::Location.report_tag_open();
attributes::report_location_attrs(&tag);
self.process_location_element();
}
b"Label" => {
TagName::Label.report_tag_open();
self.process_label_element();
Expand Down Expand Up @@ -243,7 +247,7 @@ impl MPDProcessor {
Ok(Event::Text(t)) => {
if t.len() > 0 {
match t.unescape() {
Ok(unescaped) => AttributeName::Location.report(unescaped),
Ok(unescaped) => AttributeName::Text.report(unescaped),
Err(err) => ParsingError::from(err).report_err(),
}
}
Expand All @@ -253,6 +257,7 @@ impl MPDProcessor {
if inner_tag > 0 {
inner_tag -= 1;
} else {
TagName::Location.report_tag_close();
break;
}
}
Expand Down
16 changes: 14 additions & 2 deletions src/parsers/manifest/dash/wasm-parser/ts/generators/BaseURL.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,20 @@ export function generateBaseUrlAttrParser(
): IAttributeParser {
const textDecoder = new TextDecoder();
return function onMPDAttribute(attr: AttributeName, ptr: number, len: number) {
if (attr === AttributeName.Text) {
baseUrlAttrs.value = parseString(textDecoder, linearMemory.buffer, ptr, len);
switch (attr) {
case AttributeName.Text:
baseUrlAttrs.value = parseString(textDecoder, linearMemory.buffer, ptr, len);
break;

case AttributeName.ServiceLocation: {
baseUrlAttrs.attributes.serviceLocation = parseString(
textDecoder,
linearMemory.buffer,
ptr,
len,
);
break;
}
}
};
}
27 changes: 27 additions & 0 deletions src/parsers/manifest/dash/wasm-parser/ts/generators/Location.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import type { ILocationIntermediateRepresentation } from "../../../node_parser_types.ts";
import type { IAttributeParser } from "../parsers_stack.ts";
import { AttributeName } from "../types.ts";
import { parseString } from "../utils.ts";

/** Generate the attribute parser for a root-level `<Location>` element. */
export function generateLocationAttrParser(
location: ILocationIntermediateRepresentation,
linearMemory: WebAssembly.Memory,
): IAttributeParser {
const textDecoder = new TextDecoder();
return (attribute, ptr, len) => {
switch (attribute) {
case AttributeName.Text:
location.value = parseString(textDecoder, linearMemory.buffer, ptr, len);
break;
case AttributeName.ServiceLocation:
location.attributes.serviceLocation = parseString(
textDecoder,
linearMemory.buffer,
ptr,
len,
);
break;
}
};
}
Loading
Loading