Source: player/dash_video_source.js

/**
 * @license
 * Copyright 2015 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.player.DashVideoSource');

goog.require('shaka.dash.MpdProcessor');
goog.require('shaka.dash.MpdRequest');
goog.require('shaka.dash.mpd');
goog.require('shaka.features');
goog.require('shaka.media.IAbrManager');
goog.require('shaka.media.SimpleAbrManager');
goog.require('shaka.player.DrmInfo');
goog.require('shaka.player.StreamVideoSource');
goog.require('shaka.util.EWMA');
goog.require('shaka.util.EWMABandwidthEstimator');
goog.require('shaka.util.FailoverUri');
goog.require('shaka.util.IBandwidthEstimator');
goog.require('shaka.util.TypedBind');



/**
 * Creates a DashVideoSource.
 * @param {string} mpdUrl The MPD URL.
 * @param {?shaka.player.DashVideoSource.ContentProtectionCallback}
 *     interpretContentProtection A callback to interpret the ContentProtection
 *     elements in the MPD.
 * @param {shaka.util.IBandwidthEstimator} estimator
 * @param {shaka.media.IAbrManager} abrManager
 *
 * @constructor
 * @struct
 * @extends {shaka.player.StreamVideoSource}
 * @exportDoc
 */
shaka.player.DashVideoSource =
    function(mpdUrl, interpretContentProtection, estimator, abrManager) {
  if (!estimator) {
    // For backward compatibility, provide an instance of the default
    // implementation if none is provided.
    estimator = new shaka.util.EWMABandwidthEstimator();
  }
  if (!abrManager) {
    abrManager = new shaka.media.SimpleAbrManager();
  }

  shaka.player.StreamVideoSource.call(this, null, estimator, abrManager);

  /** @private {string} */
  this.mpdUrl_ = mpdUrl;

  /** @private {shaka.dash.mpd.Mpd} */
  this.oldMpd_ = null;

  /** @private {?shaka.player.DashVideoSource.ContentProtectionCallback} */
  this.interpretContentProtection_ = interpretContentProtection;

  /** @private {!Array.<string>} */
  this.captionsUrl_ = [];

  /** @private {!Array.<string>} */
  this.captionsLang_ = [];

  /** @private {!Array.<string>} */
  this.captionsMime_ = [];

  /** @private {shaka.util.FailoverUri.NetworkCallback} */
  this.networkCallback_ = null;
};
goog.inherits(shaka.player.DashVideoSource, shaka.player.StreamVideoSource);
if (shaka.features.Dash) {
  goog.exportSymbol('shaka.player.DashVideoSource',
                    shaka.player.DashVideoSource);
}


/**
 * A callback to the application to interpret DASH ContentProtection elements.
 *
 * The first parameter is the scheme ID URI.
 * The second parameter is the ContentProtection XML element.
 *
 * The callback should return an array of {@link shaka.player.DrmInfo.Config}
 * objects. A return value of null or an empty array indicates that the
 * ContentProtection XML element could not be understood by the application. An
 * empty {@link shaka.player.DrmInfo.Config} object or one with an empty
 * key system indicates that the content is unencrypted.
 *
 * Note: the 'cenc:pssh' element is automatically parsed by the library.
 * So, if the MPD specifies an explicit PSSH then the application does not have
 * to manually convert it into an initData value
 * (see {@link shaka.player.DrmInfo.Config}).
 *
 * @typedef {function(string, !Node): Array.<shaka.player.DrmInfo.Config>}
 * @exportDoc
 */
shaka.player.DashVideoSource.ContentProtectionCallback;


/**
 * Adds the given URL as a source for text tracks in addition to those
 * specified in the MPD.  This has no effect after load().
 *
 * @param {string} url The |url| of the file to load from.
 * @param {string=} opt_lang Optional language of the text track,
 *         defaults to 'en'.
 * @param {string=} opt_mime Optional MIME type of the file, defaults
 *          to 'text/vtt'.
 * @export
 */
shaka.player.DashVideoSource.prototype.addExternalCaptions =
    function(url, opt_lang, opt_mime) {
  this.captionsUrl_.push(url);
  this.captionsLang_.push(opt_lang || '');
  this.captionsMime_.push(opt_mime || '');
};


/**
 * Sets the callback used to modify each segment request's URL and headers.
 *
 * @param {!shaka.util.FailoverUri.NetworkCallback} callback
 * @export
 */
shaka.player.DashVideoSource.prototype.setNetworkCallback = function(callback) {
  this.networkCallback_ = callback;
};


/** @override */
shaka.player.DashVideoSource.prototype.destroy = function() {
  this.interpretContentProtection_ = null;
  this.networkCallback_ = null;
  this.oldMpd_ = null;
  shaka.player.StreamVideoSource.prototype.destroy.call(this);
};


/** @override */
shaka.player.DashVideoSource.prototype.load = function() {
  var url = new shaka.util.FailoverUri(this.networkCallback_,
                                       [new goog.Uri(this.mpdUrl_)]);
  var mpdRequest = new shaka.dash.MpdRequest(url, this.mpdRequestTimeout);
  return mpdRequest.send().then(shaka.util.TypedBind(this,
      /** @param {!shaka.dash.mpd.Mpd} mpd */
      function(mpd) {
        this.oldMpd_ = mpd;
        for (var i = 0; i < this.captionsUrl_.length; i++) {
          mpd.addExternalCaptions(this.captionsUrl_[i],
                                  this.captionsLang_[i], this.captionsMime_[i]);
        }

        if (!shaka.features.Live && mpd.type == 'dynamic') {
          var error = new Error('Live manifest support not enabled.');
          error.type = 'app';
          return Promise.reject(error);
        }

        var mpdProcessor =
            new shaka.dash.MpdProcessor(this.interpretContentProtection_);
        this.manifestInfo = mpdProcessor.process(mpd, this.networkCallback_);

        var baseClassLoad = shaka.player.StreamVideoSource.prototype.load;
        var p = baseClassLoad.call(this);

        return p;
      })
  );
};


if (shaka.features.Live) {
  /** @override */
  shaka.player.DashVideoSource.prototype.onUpdateManifest = function(url) {
    var mpdRequest = new shaka.dash.MpdRequest(url, this.mpdRequestTimeout);
    return mpdRequest.send().then(shaka.util.TypedBind(this,
        /** @param {!shaka.dash.mpd.Mpd} mpd */
        function(mpd) {
          this.oldMpd_ = mpd;
          var mpdProcessor =
              new shaka.dash.MpdProcessor(this.interpretContentProtection_);
          var newManifestInfo =
              mpdProcessor.process(mpd, this.networkCallback_);
          return Promise.resolve(newManifestInfo);
        })
    );
  };

  /** @override */
  shaka.player.DashVideoSource.prototype.onUpdateLocalManifest = function() {
    shaka.asserts.assert(this.oldMpd_);
    var mpdProcessor =
        new shaka.dash.MpdProcessor(this.interpretContentProtection_);
    var newManifestInfo =
        mpdProcessor.process(/** @type {!shaka.dash.mpd.Mpd} */ (this.oldMpd_),
                             this.networkCallback_);
    return Promise.resolve(newManifestInfo);
  };
}