Skip to content

Commit

Permalink
fix(hls): Support playing media playlists directly
Browse files Browse the repository at this point in the history
Closes #3536
  • Loading branch information
theodab committed Mar 29, 2022
1 parent 4005754 commit 7ee8248
Show file tree
Hide file tree
Showing 13 changed files with 271 additions and 65 deletions.
18 changes: 17 additions & 1 deletion demo/common/asset.js
Expand Up @@ -77,6 +77,8 @@ const ShakaDemoAssetInfo = class {
this.imaContentSrcId = null;
/** @type {?string} */
this.mimeType = null;
/** @type {?string} */
this.mediaPlaylistFullMimeType = null;


// Offline storage values.
Expand Down Expand Up @@ -161,6 +163,15 @@ const ShakaDemoAssetInfo = class {
return this.drm.length == 1 && this.drm[0] == shakaAssets.KeySystem.CLEAR;
}

/**
* @param {string} mediaPlaylistFullMimeType
* @return {!ShakaDemoAssetInfo}
*/
setMediaPlaylistFullMimeType(mediaPlaylistFullMimeType) {
this.mediaPlaylistFullMimeType = mediaPlaylistFullMimeType;
return this;
}

/**
* @param {!Object} extraConfig
* @return {!ShakaDemoAssetInfo}
Expand Down Expand Up @@ -368,14 +379,19 @@ const ShakaDemoAssetInfo = class {
*/
getConfiguration() {
const config = /** @type {shaka.extern.PlayerConfiguration} */(
{drm: {advanced: {}}, manifest: {dash: {}}});
{drm: {advanced: {}}, manifest: {dash: {}, hls: {}}});

if (this.extraConfig) {
for (const key in this.extraConfig) {
config[key] = this.extraConfig[key];
}
}

if (this.mediaPlaylistFullMimeType) {
config.manifest.hls.mediaPlaylistFullMimeType =
this.mediaPlaylistFullMimeType;
}

if (this.licenseServers.size) {
config.drm.servers = config.drm.servers || {};
this.licenseServers.forEach((value, key) => {
Expand Down
9 changes: 9 additions & 0 deletions demo/common/assets.js
Expand Up @@ -294,6 +294,15 @@ shakaAssets.testAssets = [
.addFeature(shakaAssets.Feature.SUBTITLES)
.addFeature(shakaAssets.Feature.SURROUND)
.addFeature(shakaAssets.Feature.OFFLINE),
new ShakaDemoAssetInfo(
/* name= */ 'Angel One (HLS, MP4, video media playlist only)',
/* iconUri= */ 'https://storage.googleapis.com/shaka-asset-icons/angel_one.png',
/* manifestUri= */ 'https://storage.googleapis.com/shaka-demo-assets/angel-one-hls/playlist_v-0480p-1000k-libx264.mp4.m3u8',
/* source= */ shakaAssets.Source.SHAKA)
.addFeature(shakaAssets.Feature.HLS)
.addFeature(shakaAssets.Feature.MP4)
.addFeature(shakaAssets.Feature.OFFLINE)
.setMediaPlaylistFullMimeType('video/mp4; codecs="avc1.4d401f"'),
new ShakaDemoAssetInfo(
/* name= */ 'Angel One (HLS, MP4, multilingual, Widevine)',
/* iconUri= */ 'https://storage.googleapis.com/shaka-asset-icons/angel_one.png',
Expand Down
2 changes: 2 additions & 0 deletions demo/common/message_ids.js
Expand Up @@ -107,6 +107,7 @@ shakaDemo.MessageIds = {
DRM_SYSTEM: 'DEMO_DRM_SYSTEM',
DRM_TAB: 'DEMO_DRM_TAB',
EDIT_CUSTOM: 'DEMO_EDIT_CUSTOM',
FULL_MIME_TYPE: 'DEMO_FULL_MIME_TYPE',
HEADERS_TAB: 'DEMO_HEADERS_TAB',
ICON_URL: 'DEMO_ICON_URL',
LICENSE_CERTIFICATE_URL: 'DEMO_LICENSE_CERTIFICATE_URL',
Expand All @@ -117,6 +118,7 @@ shakaDemo.MessageIds = {
MAIN_TAB: 'DEMO_MAIN_TAB',
MANIFEST_URL: 'DEMO_MANIFEST_URL',
MANIFEST_URL_ERROR: 'DEMO_MANIFEST_URL_ERROR',
MEDIA_PLAYLIST_TAB: 'DEMO_MEDIA_PLAYLIST_TAB',
NAME: 'DEMO_NAME',
NAME_ERROR: 'DEMO_NAME_ERROR',
EXTRA_SHAKA_PLAYER_CONFIG: 'DEMO_EXTRA_SHAKA_PLAYER_CONFIG',
Expand Down
38 changes: 38 additions & 0 deletions demo/custom.js
Expand Up @@ -245,6 +245,38 @@ shakaDemo.Custom = class {
}


/**
* @param {!ShakaDemoAssetInfo} assetInProgress
* @param {!Array.<!HTMLInputElement>} inputsToCheck
* @return {!Element} div
* @private
*/
makeAssetDialogContentsMediaPlaylist_(assetInProgress, inputsToCheck) {
const mediaPlaylistDiv = document.createElement('div');
const containerStyle = shakaDemo.InputContainer.Style.FLEX;
const container = new shakaDemo.InputContainer(
mediaPlaylistDiv, /* headerText= */ null, containerStyle,
/* docLink= */ null);
container.getClassList().add('wide-input');
container.setDefaultRowClass('wide-input');

const fullMimeTypeSetup = (input, container) => {
if (assetInProgress.mediaPlaylistFullMimeType) {
input.value = assetInProgress.mediaPlaylistFullMimeType;
}
};
const fullMimeTypeOnChange = (input) => {
assetInProgress.setMediaPlaylistFullMimeType(input.value);
};
const fullMimeTypeName = shakaDemoMain.getLocalizedString(
shakaDemo.MessageIds.FULL_MIME_TYPE);
this.makeField_(
container, fullMimeTypeName, fullMimeTypeSetup, fullMimeTypeOnChange);

return mediaPlaylistDiv;
}


/**
* @param {!ShakaDemoAssetInfo} assetInProgress
* @param {!Array.<!HTMLInputElement>} inputsToCheck
Expand Down Expand Up @@ -674,6 +706,8 @@ shakaDemo.Custom = class {
assetInProgress, inputsToCheck);
const adsDiv = this.makeAssetDialogContentsAds_(
assetInProgress, inputsToCheck);
const mediaPlaylistDiv = this.makeAssetDialogContentsMediaPlaylist_(
assetInProgress, inputsToCheck);
const extraConfigDiv = this.makeAssetDialogContentsExtra_(
assetInProgress, inputsToCheck);
const finishDiv = this.makeAssetDialogContentsFinish_(
Expand Down Expand Up @@ -713,6 +747,9 @@ shakaDemo.Custom = class {
shakaDemo.MessageIds.HEADERS_TAB, headersDiv, /* startOn= */ false);
addTabButton(
shakaDemo.MessageIds.ADS_TAB, adsDiv, /* startOn= */ false);
addTabButton(
shakaDemo.MessageIds.MEDIA_PLAYLIST_TAB, mediaPlaylistDiv,
/* startOn= */ false);
addTabButton(
shakaDemo.MessageIds.EXTRA_TAB, extraConfigDiv, /* startOn= */ false);

Expand All @@ -722,6 +759,7 @@ shakaDemo.Custom = class {
this.dialog_.appendChild(drmDiv);
this.dialog_.appendChild(headersDiv);
this.dialog_.appendChild(adsDiv);
this.dialog_.appendChild(mediaPlaylistDiv);
this.dialog_.appendChild(extraConfigDiv);
this.dialog_.appendChild(finishDiv);
this.dialog_.appendChild(iconDiv);
Expand Down
2 changes: 2 additions & 0 deletions demo/locales/en.json
Expand Up @@ -138,6 +138,7 @@
"DEMO_MAX_PIXELS": "Max Pixels",
"DEMO_MAX_SMALL_GAP_SIZE": "Maximum Small Gap Size",
"DEMO_MAX_WIDTH": "Max Width",
"DEMO_MEDIA_PLAYLIST_TAB": "HLS Media Playlist",
"DEMO_METACDN": "MetaCDN",
"DEMO_MICROSOFT": "Microsoft",
"DEMO_MIME_TYPE": "MIME Type",
Expand Down Expand Up @@ -166,6 +167,7 @@
"DEMO_OFFLINE_SECTION_HEADER": "Offline",
"DEMO_FAILURE_MISC": "Shaka Player failed to load! If you are using an ad blocker, try switching to compiled mode at the bottom of the page.",
"DEMO_FAILURE_NO_BROWSER_SUPPORT": "Your browser is not supported!",
"DEMO_FULL_MIME_TYPE": "Full Mime Type",
"DEMO_PLAY": "Play",
"DEMO_PLAYREADY": "PlayReady DRM",
"DEMO_PREFER_FORCED_SUBS": "Prefer Forced Subs",
Expand Down
8 changes: 8 additions & 0 deletions demo/locales/source.json
Expand Up @@ -555,6 +555,10 @@
"description": "The name of a configuration value.",
"message": "Max Width"
},
"DEMO_MEDIA_PLAYLIST_TAB": {
"description": "The header for a tab within the custom asset creation dialog.",
"message": "HLS Media Playlist"
},
"DEMO_METACDN": {
"description": "Text that describes an asset that comes from the MetaCDN asset library.",
"message": "[PROPER_NAME:MetaCDN]"
Expand Down Expand Up @@ -667,6 +671,10 @@
"description": "An error displayed if Shaka Player is unable to load due to lack of browser support.",
"message": "Your browser is not supported!"
},
"DEMO_FULL_MIME_TYPE": {
"description": "The label on a field that allows users to provide a full mime type for a custom asset.",
"message": "Full Mime Type"
},
"DEMO_PLAY": {
"description": "A button to play the attached asset.",
"message": "Play"
Expand Down
10 changes: 9 additions & 1 deletion externs/shaka/player.js
Expand Up @@ -748,7 +748,8 @@ shaka.extern.DashManifestConfiguration;
* ignoreImageStreamFailures: boolean,
* defaultAudioCodec: string,
* defaultVideoCodec: string,
* ignoreManifestProgramDateTime: boolean
* ignoreManifestProgramDateTime: boolean,
* mediaPlaylistFullMimeType: string
* }}
*
* @property {boolean} ignoreTextStreamFailures
Expand All @@ -768,6 +769,13 @@ shaka.extern.DashManifestConfiguration;
* <code>EXT-X-PROGRAM-DATE-TIME</code> tags in the manifest.
* Meant for tags that are incorrect or malformed.
* <i>Defaults to <code>false</code>.</i>
* @property {string} mediaPlaylistFullMimeType
* A string containing a full mime type, including both the basic mime type
* and also the codecs. Used when the HLS parser parses a media playlist
* directly.
* You can use the <code>shaka.util.MimeUtils.getFullType()</code> utility to
* format this value.
* <i>Defaults to <code>'video/mp4; codecs="avc1.42E01E"'</code>.</i>
* @exportDoc
*/
shaka.extern.HlsManifestConfiguration;
Expand Down

0 comments on commit 7ee8248

Please sign in to comment.