Files
shaka-player/lib/offline/manifest_converter.js
T
Joey Parrish f539147d48 fix: Correct license headers in compiled output
This fixes all the license headers in the main library, which corrects
the appearance of the main license in the compiled output.

It seems that the `!` in the header forces the compiler to keep it in
the output.  I believe older compiler releases did this purely based
on `@license`.

Issue #2638

Change-Id: I7f0e918caad10c9af689c9d07672b7fe9be7b2f3
2020-06-09 16:05:09 -07:00

291 lines
8.2 KiB
JavaScript

/*! @license
* Shaka Player
* Copyright 2016 Google LLC
* SPDX-License-Identifier: Apache-2.0
*/
goog.provide('shaka.offline.ManifestConverter');
goog.require('goog.asserts');
goog.require('shaka.media.InitSegmentReference');
goog.require('shaka.media.PresentationTimeline');
goog.require('shaka.media.SegmentIndex');
goog.require('shaka.media.SegmentReference');
goog.require('shaka.offline.OfflineUri');
goog.require('shaka.util.ManifestParserUtils');
/**
* Utility class for converting database manifest objects back to normal
* player-ready objects. Used by the offline system to convert on-disk
* objects back to the in-memory objects.
*/
shaka.offline.ManifestConverter = class {
/**
* Create a new manifest converter. Need to know the mechanism and cell that
* the manifest is from so that all segments paths can be created.
*
* @param {string} mechanism
* @param {string} cell
*/
constructor(mechanism, cell) {
/** @private {string} */
this.mechanism_ = mechanism;
/** @private {string} */
this.cell_ = cell;
}
/**
* Convert a |shaka.extern.ManifestDB| object to a |shaka.extern.Manifest|
* object.
*
* @param {shaka.extern.ManifestDB} manifestDB
* @return {shaka.extern.Manifest}
*/
fromManifestDB(manifestDB) {
const timeline = new shaka.media.PresentationTimeline(null, 0);
timeline.setDuration(manifestDB.duration);
/** @type {!Array.<shaka.extern.StreamDB>} */
const audioStreams =
manifestDB.streams.filter((streamDB) => this.isAudio_(streamDB));
/** @type {!Array.<shaka.extern.StreamDB>} */
const videoStreams =
manifestDB.streams.filter((streamDB) => this.isVideo_(streamDB));
/** @type {!Map.<number, shaka.extern.Variant>} */
const variants = this.createVariants(audioStreams, videoStreams, timeline);
/** @type {!Array.<shaka.extern.Stream>} */
const textStreams =
manifestDB.streams.filter((streamDB) => this.isText_(streamDB))
.map((streamDB) => this.fromStreamDB_(streamDB, timeline));
const drmInfos = manifestDB.drmInfo ? [manifestDB.drmInfo] : [];
if (manifestDB.drmInfo) {
for (const variant of variants.values()) {
if (variant.audio && variant.audio.encrypted) {
variant.audio.drmInfos = drmInfos;
}
if (variant.video && variant.video.encrypted) {
variant.video.drmInfos = drmInfos;
}
}
}
return {
presentationTimeline: timeline,
minBufferTime: 2,
offlineSessionIds: manifestDB.sessionIds,
variants: Array.from(variants.values()),
textStreams: textStreams,
};
}
/**
* Recreates Variants from audio and video StreamDB collections.
*
* @param {!Array.<!shaka.extern.StreamDB>} audios
* @param {!Array.<!shaka.extern.StreamDB>} videos
* @param {shaka.media.PresentationTimeline} timeline
* @return {!Map.<number, !shaka.extern.Variant>}
*/
createVariants(audios, videos, timeline) {
// Get all the variant ids from all audio and video streams.
/** @type {!Set.<number>} */
const variantIds = new Set();
for (const streamDB of audios) {
for (const id of streamDB.variantIds) {
variantIds.add(id);
}
}
for (const streamDB of videos) {
for (const id of streamDB.variantIds) {
variantIds.add(id);
}
}
/** @type {!Map.<number, shaka.extern.Variant>} */
const variantMap = new Map();
for (const id of variantIds) {
variantMap.set(id, this.createEmptyVariant_(id));
}
// Assign each audio stream to its variants.
for (const audio of audios) {
/** @type {shaka.extern.Stream} */
const stream = this.fromStreamDB_(audio, timeline);
for (const variantId of audio.variantIds) {
const variant = variantMap.get(variantId);
goog.asserts.assert(
!variant.audio, 'A variant should only have one audio stream');
variant.language = stream.language;
variant.primary = variant.primary || stream.primary;
variant.audio = stream;
}
}
// Assign each video stream to its variants.
for (const video of videos) {
/** @type {shaka.extern.Stream} */
const stream = this.fromStreamDB_(video, timeline);
for (const variantId of video.variantIds) {
const variant = variantMap.get(variantId);
goog.asserts.assert(
!variant.video, 'A variant should only have one video stream');
variant.primary = variant.primary || stream.primary;
variant.video = stream;
}
}
return variantMap;
}
/**
* @param {shaka.extern.StreamDB} streamDB
* @param {shaka.media.PresentationTimeline} timeline
* @return {shaka.extern.Stream}
* @private
*/
fromStreamDB_(streamDB, timeline) {
/** @type {!Array.<!shaka.media.SegmentReference>} */
const segments = streamDB.segments.map(
(segment, index) => this.fromSegmentDB_(index, segment));
timeline.notifySegments(segments);
/** @type {!shaka.media.SegmentIndex} */
const segmentIndex = new shaka.media.SegmentIndex(segments);
/** @type {shaka.extern.Stream} */
const stream = {
id: streamDB.id,
originalId: streamDB.originalId,
createSegmentIndex: () => Promise.resolve(),
segmentIndex,
mimeType: streamDB.mimeType,
codecs: streamDB.codecs,
width: streamDB.width || undefined,
height: streamDB.height || undefined,
frameRate: streamDB.frameRate,
pixelAspectRatio: streamDB.pixelAspectRatio,
kind: streamDB.kind,
encrypted: streamDB.encrypted,
drmInfos: [],
keyIds: streamDB.keyIds,
language: streamDB.language,
label: streamDB.label,
type: streamDB.type,
primary: streamDB.primary,
trickModeVideo: null,
emsgSchemeIdUris: null,
roles: streamDB.roles,
channelsCount: streamDB.channelsCount,
audioSamplingRate: streamDB.audioSamplingRate,
closedCaptions: streamDB.closedCaptions,
};
return stream;
}
/**
* @param {number} index
* @param {shaka.extern.SegmentDB} segmentDB
* @return {!shaka.media.SegmentReference}
* @private
*/
fromSegmentDB_(index, segmentDB) {
/** @type {!shaka.offline.OfflineUri} */
const uri = shaka.offline.OfflineUri.segment(
this.mechanism_, this.cell_, segmentDB.dataKey);
const initSegmentReference = segmentDB.initSegmentKey != null ?
this.fromInitSegmentDB_(segmentDB.initSegmentKey) : null;
return new shaka.media.SegmentReference(
segmentDB.startTime,
segmentDB.endTime,
() => [uri.toString()],
/* startByte= */ 0,
/* endByte= */ null,
initSegmentReference,
segmentDB.timestampOffset,
segmentDB.appendWindowStart,
segmentDB.appendWindowEnd);
}
/**
* @param {number} key
* @return {!shaka.media.InitSegmentReference}
* @private
*/
fromInitSegmentDB_(key) {
/** @type {!shaka.offline.OfflineUri} */
const uri = shaka.offline.OfflineUri.segment(
this.mechanism_, this.cell_, key);
return new shaka.media.InitSegmentReference(
() => [uri.toString()],
/* startBytes= */ 0,
/* endBytes= */ null );
}
/**
* @param {shaka.extern.StreamDB} streamDB
* @return {boolean}
* @private
*/
isAudio_(streamDB) {
const ContentType = shaka.util.ManifestParserUtils.ContentType;
return streamDB.type == ContentType.AUDIO;
}
/**
* @param {shaka.extern.StreamDB} streamDB
* @return {boolean}
* @private
*/
isVideo_(streamDB) {
const ContentType = shaka.util.ManifestParserUtils.ContentType;
return streamDB.type == ContentType.VIDEO;
}
/**
* @param {shaka.extern.StreamDB} streamDB
* @return {boolean}
* @private
*/
isText_(streamDB) {
const ContentType = shaka.util.ManifestParserUtils.ContentType;
return streamDB.type == ContentType.TEXT;
}
/**
* Creates an empty Variant.
*
* @param {number} id
* @return {!shaka.extern.Variant}
* @private
*/
createEmptyVariant_(id) {
return {
id: id,
language: '',
primary: false,
audio: null,
video: null,
bandwidth: 0,
allowedByApplication: true,
allowedByKeySystem: true,
};
}
};