UNPKG

shaka-player

Version:
303 lines (254 loc) 8.71 kB
/** * @license * Copyright 2016 Google Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ goog.provide('shaka.media.ManifestParser'); goog.require('goog.Uri'); goog.require('goog.asserts'); goog.require('shaka.log'); goog.require('shaka.net.NetworkingEngine'); goog.require('shaka.util.Error'); goog.require('shaka.util.Platform'); /** * @namespace shaka.media.ManifestParser * @summary An interface to register manifest parsers. * @exportDoc */ /** * Contains the parser factory functions indexed by MIME type. * * @type {!Object.<string, shaka.extern.ManifestParser.Factory>} */ shaka.media.ManifestParser.parsersByMime = {}; /** * Contains the parser factory functions indexed by file extension. * * @type {!Object.<string, shaka.extern.ManifestParser.Factory>} */ shaka.media.ManifestParser.parsersByExtension = {}; /** * Registers a manifest parser by file extension. * * @param {string} extension The file extension of the manifest. * @param {shaka.extern.ManifestParser.Factory} parserFactory The factory * used to create parser instances. * @export */ shaka.media.ManifestParser.registerParserByExtension = function( extension, parserFactory) { shaka.media.ManifestParser.parsersByExtension[extension] = parserFactory; }; /** * Registers a manifest parser by MIME type. * * @param {string} mimeType The MIME type of the manifest. * @param {shaka.extern.ManifestParser.Factory} parserFactory The factory * used to create parser instances. * @export */ shaka.media.ManifestParser.registerParserByMime = function( mimeType, parserFactory) { shaka.media.ManifestParser.parsersByMime[mimeType] = parserFactory; }; /** * Returns a map of manifest support for well-known types. * * @return {!Object.<string, boolean>} */ shaka.media.ManifestParser.probeSupport = function() { const ManifestParser = shaka.media.ManifestParser; let support = {}; // Make sure all registered parsers are shown, but only for MSE-enabled // platforms where our parsers matter. if (shaka.util.Platform.supportsMediaSource()) { for (let type in ManifestParser.parsersByMime) { support[type] = true; } for (let type in ManifestParser.parsersByExtension) { support[type] = true; } } // Make sure all well-known types are tested as well, just to show an explicit // false for things people might be expecting. const testMimeTypes = [ // DASH 'application/dash+xml', // HLS 'application/x-mpegurl', 'application/vnd.apple.mpegurl', // SmoothStreaming 'application/vnd.ms-sstr+xml', ]; const testExtensions = { // DASH 'mpd': 'application/dash+xml', // HLS 'm3u8': 'application/x-mpegurl', // SmoothStreaming 'ism': 'application/vnd.ms-sstr+xml', }; for (let type of testMimeTypes) { // Only query our parsers for MSE-enabled platforms. Otherwise, query a // temporary media element for native support for these types. if (shaka.util.Platform.supportsMediaSource()) { support[type] = !!ManifestParser.parsersByMime[type]; } else { support[type] = shaka.util.Platform.supportsMediaType(type); } } for (let extension in testExtensions) { // Only query our parsers for MSE-enabled platforms. Otherwise, query a // temporary media element for native support for these MIME type for the // extension. if (shaka.util.Platform.supportsMediaSource()) { support[extension] = !!ManifestParser.parsersByExtension[extension]; } else { const type = testExtensions[extension]; support[extension] = shaka.util.Platform.supportsMediaType(type); } } return support; }; /** * Create a manifest parser to parse the manifest at |uri|. * * @param {string} uri * @param {!shaka.net.NetworkingEngine} netEngine * @param {shaka.extern.RetryParameters} retryParams * @param {?string} mimeType * @return {!Promise.<!shaka.extern.ManifestParser>} */ shaka.media.ManifestParser.create = async function( uri, netEngine, retryParams, mimeType) { try { const Factory = await shaka.media.ManifestParser.getFactory_( uri, netEngine, retryParams, mimeType); return new Factory(); } catch (error) { goog.asserts.assert( error instanceof shaka.util.Error, 'Incorrect error type'); // Regardless of what the error was, we need to upgrade it to a critical // error. We can't do anything if we can't create a manifest parser. error.severity = shaka.util.Error.Severity.CRITICAL; throw error; } }; /** * Get a factory that can create a manifest parser that should be able to parse * the manifest at |uri|. * * @param {string} uri * @param {!shaka.net.NetworkingEngine} netEngine * @param {shaka.extern.RetryParameters} retryParams * @param {?string} mimeType * @return {!Promise.<shaka.extern.ManifestParser.Factory>} * @private */ shaka.media.ManifestParser.getFactory_ = async function( uri, netEngine, retryParams, mimeType) { const ManifestParser = shaka.media.ManifestParser; // Try using the MIME type we were given. if (mimeType) { const factory = ManifestParser.parsersByMime[mimeType.toLowerCase()]; if (factory) { return factory; } shaka.log.warning( 'Could not determine manifest type using MIME type ', mimeType); } const extension = ManifestParser.getExtension(uri); if (extension) { const factory = ManifestParser.parsersByExtension[extension]; if (factory) { return factory; } shaka.log.warning( 'Could not determine manifest type for extension ', extension); } else { shaka.log.warning('Could not find extension for ', uri); } if (!mimeType) { mimeType = await ManifestParser.getMimeType(uri, netEngine, retryParams); if (mimeType) { const factory = shaka.media.ManifestParser.parsersByMime[mimeType]; if (factory) { return factory; } shaka.log.warning('Could not determine manifest type using MIME type', mimeType); } } throw new shaka.util.Error( shaka.util.Error.Severity.CRITICAL, shaka.util.Error.Category.MANIFEST, shaka.util.Error.Code.UNABLE_TO_GUESS_MANIFEST_TYPE, uri); }; /** * @param {string} uri * @param {!shaka.net.NetworkingEngine} netEngine * @param {shaka.extern.RetryParameters} retryParams * @return {!Promise.<string>} */ shaka.media.ManifestParser.getMimeType = async function( uri, netEngine, retryParams) { const type = shaka.net.NetworkingEngine.RequestType.MANIFEST; let request = shaka.net.NetworkingEngine.makeRequest([uri], retryParams); request.method = 'HEAD'; let response = await netEngine.request(type, request).promise; // https://bit.ly/2K9s9kf says this header should always be available, // but just to be safe: const mimeType = response.headers['content-type']; return mimeType ? mimeType.toLowerCase().split(';').shift() : ''; }; /** * @param {string} uri * @return {string} */ shaka.media.ManifestParser.getExtension = function(uri) { const uriObj = new goog.Uri(uri); const uriPieces = uriObj.getPath().split('/'); const uriFilename = uriPieces.pop(); const filenamePieces = uriFilename.split('.'); // Only one piece means there is no extension. if (filenamePieces.length == 1) { return ''; } return filenamePieces.pop().toLowerCase(); }; /** * Determines whether or not this URI and MIME type are supported by our own * manifest parsers on this platform. This takes into account whether or not * MediaSource is available, as well as which parsers are registered to the * system. * * @param {string} uri * @param {string} mimeType * @return {boolean} */ shaka.media.ManifestParser.isSupported = function(uri, mimeType) { // Without MediaSource, our own parsers are useless. if (!shaka.util.Platform.supportsMediaSource()) { return false; } if (mimeType in shaka.media.ManifestParser.parsersByMime) { return true; } const extension = shaka.media.ManifestParser.getExtension(uri); if (extension in shaka.media.ManifestParser.parsersByExtension) { return true; } return false; };