dashjs
Version:
A reference client implementation for the playback of MPEG DASH via Javascript and compliant browsers.
213 lines (212 loc) • 29.7 kB
JavaScript
;Object.defineProperty(exports,"__esModule",{value:true});var _typeof=typeof Symbol==="function"&&typeof Symbol.iterator==="symbol"?function(obj){return typeof obj;}:function(obj){return obj&&typeof Symbol==="function"&&obj.constructor===Symbol&&obj!==Symbol.prototype?"symbol":typeof obj;};/**
* The copyright in this software is being made available under the BSD License,
* included below. This software may be subject to other third party and contributor
* rights, including patent rights, and no such rights are granted under this license.
*
* Copyright (c) 2013, Dash Industry Forum.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation and/or
* other materials provided with the distribution.
* * Neither the name of Dash Industry Forum nor the names of its
* contributors may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS AS IS AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/var _CommonEncryption=require('../CommonEncryption');var _CommonEncryption2=_interopRequireDefault(_CommonEncryption);var _MediaCapability=require('../vo/MediaCapability');var _MediaCapability2=_interopRequireDefault(_MediaCapability);var _KeySystemConfiguration=require('../vo/KeySystemConfiguration');var _KeySystemConfiguration2=_interopRequireDefault(_KeySystemConfiguration);var _ProtectionErrors=require('../errors/ProtectionErrors');var _ProtectionErrors2=_interopRequireDefault(_ProtectionErrors);var _DashJSError=require('../../vo/DashJSError');var _DashJSError2=_interopRequireDefault(_DashJSError);function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj};}var NEEDKEY_BEFORE_INITIALIZE_RETRIES=5;var NEEDKEY_BEFORE_INITIALIZE_TIMEOUT=500;var LICENSE_SERVER_REQUEST_RETRIES=3;var LICENSE_SERVER_REQUEST_RETRY_INTERVAL=1000;var LICENSE_SERVER_REQUEST_DEFAULT_TIMEOUT=8000;/**
* @module ProtectionController
* @description Provides access to media protection information and functionality. Each
* ProtectionController manages a single {@link MediaPlayer.models.ProtectionModel}
* which encapsulates a set of protection information (EME APIs, selected key system,
* key sessions). The APIs of ProtectionController mostly align with the latest EME
* APIs. Key system selection is mostly automated when combined with app-overrideable
* functionality provided in {@link ProtectionKeyController}.
* @todo ProtectionController does almost all of its tasks automatically after init() is
* called. Applications might want more control over this process and want to go through
* each step manually (key system selection, session creation, session maintenance).
* @param {Object} config
*/function ProtectionController(config){config=config||{};var protectionKeyController=config.protectionKeyController;var protectionModel=config.protectionModel;var eventBus=config.eventBus;var events=config.events;var debug=config.debug;var BASE64=config.BASE64;var constants=config.constants;var needkeyRetries=[];var instance=void 0,logger=void 0,pendingNeedKeyData=void 0,mediaInfoArr=void 0,protDataSet=void 0,sessionType=void 0,robustnessLevel=void 0,keySystem=void 0;function setup(){logger=debug.getLogger(instance);pendingNeedKeyData=[];mediaInfoArr=[];sessionType='temporary';robustnessLevel='';}function checkConfig(){if(!eventBus||!eventBus.hasOwnProperty('on')||!protectionKeyController||!protectionKeyController.hasOwnProperty('getSupportedKeySystemsFromContentProtection')){throw new Error('Missing config parameter(s)');}}/**
* Initialize this protection system with a given audio
* or video stream information.
*
* @param {StreamInfo} [mediaInfo] Media information
* @memberof module:ProtectionController
* @instance
* @todo This API will change when we have better support for allowing applications
* to select different adaptation sets for playback. Right now it is clunky for
* applications to create {@link StreamInfo} with the right information,
* @ignore
*/function initializeForMedia(mediaInfo){// Not checking here if a session for similar KS/KID combination is already created
// because still don't know which keysystem will be selected.
// Once Keysystem is selected and before creating the session, we will do that check
// so we create the strictly necessary DRM sessions
if(!mediaInfo){throw new Error('mediaInfo can not be null or undefined');}checkConfig();eventBus.on(events.INTERNAL_KEY_MESSAGE,onKeyMessage,this);eventBus.on(events.INTERNAL_KEY_STATUS_CHANGED,onKeyStatusChanged,this);mediaInfoArr.push(mediaInfo);// ContentProtection elements are specified at the AdaptationSet level, so the CP for audio
// and video will be the same. Just use one valid MediaInfo object
var supportedKS=protectionKeyController.getSupportedKeySystemsFromContentProtection(mediaInfo.contentProtection);if(supportedKS&&supportedKS.length>0){selectKeySystem(supportedKS,true);}}/**
* Returns a set of supported key systems and CENC initialization data
* from the given array of ContentProtection elements. Only
* key systems that are supported by this player will be returned.
* Key systems are returned in priority order (highest first).
*
* @param {Array.<Object>} cps - array of content protection elements parsed
* from the manifest
* @returns {Array.<Object>} array of objects indicating which supported key
* systems were found. Empty array is returned if no
* supported key systems were found
* @memberof module:ProtectionKeyController
* @instance
* @ignore
*/function getSupportedKeySystemsFromContentProtection(cps){checkConfig();return protectionKeyController.getSupportedKeySystemsFromContentProtection(cps);}/**
* Create a new key session associated with the given initialization data from
* the MPD or from the PSSH box in the media
*
* @param {ArrayBuffer} initData the initialization data
* @param {Uint8Array} cdmData the custom data to provide to licenser
* @memberof module:ProtectionController
* @instance
* @fires ProtectionController#KeySessionCreated
* @todo In older versions of the EME spec, there was a one-to-one relationship between
* initialization data and key sessions. That is no longer true in the latest APIs. This
* API will need to modified (and a new "generateRequest(keySession, initData)" API created)
* to come up to speed with the latest EME standard
* @ignore
*/function createKeySession(initData,cdmData){var initDataForKS=_CommonEncryption2.default.getPSSHForKeySystem(keySystem,initData);var protData=getProtData(keySystem);if(initDataForKS){// Check for duplicate initData
var currentInitData=protectionModel.getAllInitData();for(var i=0;i<currentInitData.length;i++){if(protectionKeyController.initDataEquals(initDataForKS,currentInitData[i])){logger.warn('DRM: Ignoring initData because we have already seen it!');return;}}try{protectionModel.createKeySession(initDataForKS,protData,getSessionType(keySystem),cdmData);}catch(error){eventBus.trigger(events.KEY_SESSION_CREATED,{data:null,error:new _DashJSError2.default(_ProtectionErrors2.default.KEY_SESSION_CREATED_ERROR_CODE,_ProtectionErrors2.default.KEY_SESSION_CREATED_ERROR_MESSAGE+error.message)});}}else if(initData){protectionModel.createKeySession(initData,protData,getSessionType(keySystem),cdmData);}else{eventBus.trigger(events.KEY_SESSION_CREATED,{data:null,error:new _DashJSError2.default(_ProtectionErrors2.default.KEY_SESSION_CREATED_ERROR_CODE,_ProtectionErrors2.default.KEY_SESSION_CREATED_ERROR_MESSAGE+'Selected key system is '+(keySystem?keySystem.systemString:null)+'. needkey/encrypted event contains no initData corresponding to that key system!')});}}/**
* Loads a key session with the given session ID from persistent storage. This
* essentially creates a new key session
*
* @param {string} sessionID
* @param {string} initData
* @memberof module:ProtectionController
* @instance
* @fires ProtectionController#KeySessionCreated
* @ignore
*/function loadKeySession(sessionID,initData){checkConfig();protectionModel.loadKeySession(sessionID,initData,getSessionType(keySystem));}/**
* Removes the given key session from persistent storage and closes the session
* as if {@link ProtectionController#closeKeySession}
* was called
*
* @param {SessionToken} sessionToken the session
* token
* @memberof module:ProtectionController
* @instance
* @fires ProtectionController#KeySessionRemoved
* @fires ProtectionController#KeySessionClosed
* @ignore
*/function removeKeySession(sessionToken){checkConfig();protectionModel.removeKeySession(sessionToken);}/**
* Closes the key session and releases all associated decryption keys. These
* keys will no longer be available for decrypting media
*
* @param {SessionToken} sessionToken the session
* token
* @memberof module:ProtectionController
* @instance
* @fires ProtectionController#KeySessionClosed
* @ignore
*/function closeKeySession(sessionToken){checkConfig();protectionModel.closeKeySession(sessionToken);}/**
* Sets a server certificate for use by the CDM when signing key messages
* intended for a particular license server. This will fire
* an error event if a key system has not yet been selected.
*
* @param {ArrayBuffer} serverCertificate a CDM-specific license server
* certificate
* @memberof module:ProtectionController
* @instance
* @fires ProtectionController#ServerCertificateUpdated
*/function setServerCertificate(serverCertificate){checkConfig();protectionModel.setServerCertificate(serverCertificate);}/**
* Associate this protection system with the given HTMLMediaElement. This
* causes the system to register for needkey/encrypted events from the given
* element and provides a destination for setting of MediaKeys
*
* @param {HTMLMediaElement} element the media element to which the protection
* system should be associated
* @memberof module:ProtectionController
* @instance
*/function setMediaElement(element){checkConfig();if(element){protectionModel.setMediaElement(element);eventBus.on(events.NEED_KEY,onNeedKey,this);}else if(element===null){protectionModel.setMediaElement(element);eventBus.off(events.NEED_KEY,onNeedKey,this);}}/**
* Sets the session type to use when creating key sessions. Either "temporary" or
* "persistent-license". Default is "temporary".
*
* @param {string} value the session type
* @memberof module:ProtectionController
* @instance
*/function setSessionType(value){sessionType=value;}/**
* Sets the robustness level for video and audio capabilities. Optional to remove Chrome warnings.
* Possible values are SW_SECURE_CRYPTO, SW_SECURE_DECODE, HW_SECURE_CRYPTO, HW_SECURE_CRYPTO, HW_SECURE_DECODE, HW_SECURE_ALL.
*
* @param {string} level the robustness level
* @memberof module:ProtectionController
* @instance
*/function setRobustnessLevel(level){robustnessLevel=level;}/**
* Attach KeySystem-specific data to use for license acquisition with EME
*
* @param {Object} data an object containing property names corresponding to
* key system name strings (e.g. "org.w3.clearkey") and associated values
* being instances of {@link ProtectionData}
* @memberof module:ProtectionController
* @instance
* @ignore
*/function setProtectionData(data){protDataSet=data;protectionKeyController.setProtectionData(data);}/**
* Stop method is called when current playback is stopped/resetted.
*
* @memberof module:ProtectionController
* @instance
*/function stop(){if(protectionModel){protectionModel.stop();}}/**
* Destroys all protection data associated with this protection set. This includes
* deleting all key sessions. In the case of persistent key sessions, the sessions
* will simply be unloaded and not deleted. Additionally, if this protection set is
* associated with a HTMLMediaElement, it will be detached from that element.
*
* @memberof module:ProtectionController
* @instance
* @ignore
*/function reset(){checkConfig();eventBus.off(events.INTERNAL_KEY_MESSAGE,onKeyMessage,this);eventBus.off(events.INTERNAL_KEY_STATUS_CHANGED,onKeyStatusChanged,this);setMediaElement(null);keySystem=undefined;//TODO-Refactor look at why undefined is needed for this. refactor
if(protectionModel){protectionModel.reset();protectionModel=null;}needkeyRetries.forEach(function(retryTimeout){return clearTimeout(retryTimeout);});needkeyRetries=[];mediaInfoArr=[];}///////////////
// Private
///////////////
function getProtData(keySystem){var protData=null;if(keySystem){var keySystemString=keySystem.systemString;if(protDataSet){protData=keySystemString in protDataSet?protDataSet[keySystemString]:null;}}return protData;}function getKeySystemConfiguration(keySystem){var protData=getProtData(keySystem);var audioCapabilities=[];var videoCapabilities=[];var audioRobustness=protData&&protData.audioRobustness&&protData.audioRobustness.length>0?protData.audioRobustness:robustnessLevel;var videoRobustness=protData&&protData.videoRobustness&&protData.videoRobustness.length>0?protData.videoRobustness:robustnessLevel;var ksSessionType=getSessionType(keySystem);var distinctiveIdentifier=protData&&protData.distinctiveIdentifier?protData.distinctiveIdentifier:'optional';var persistentState=protData&&protData.persistentState?protData.persistentState:ksSessionType==='temporary'?'optional':'required';mediaInfoArr.forEach(function(media){if(media.type===constants.AUDIO){audioCapabilities.push(new _MediaCapability2.default(media.codec,audioRobustness));}else if(media.type===constants.VIDEO){videoCapabilities.push(new _MediaCapability2.default(media.codec,videoRobustness));}});return new _KeySystemConfiguration2.default(audioCapabilities,videoCapabilities,distinctiveIdentifier,persistentState,[ksSessionType]);}function getSessionType(keySystem){var protData=getProtData(keySystem);var ksSessionType=protData&&protData.sessionType?protData.sessionType:sessionType;return ksSessionType;}function selectKeySystem(supportedKS,fromManifest){var self=this;var requestedKeySystems=[];var ksIdx=void 0;if(keySystem){// We have a key system
for(ksIdx=0;ksIdx<supportedKS.length;ksIdx++){if(keySystem===supportedKS[ksIdx].ks){var _ret=function(){requestedKeySystems.push({ks:supportedKS[ksIdx].ks,configs:[getKeySystemConfiguration(keySystem)]});// Ensure that we would be granted key system access using the key
// system and codec information
var onKeySystemAccessComplete=function onKeySystemAccessComplete(event){eventBus.off(events.KEY_SYSTEM_ACCESS_COMPLETE,onKeySystemAccessComplete,self);if(event.error){if(!fromManifest){eventBus.trigger(events.KEY_SYSTEM_SELECTED,{error:new _DashJSError2.default(_ProtectionErrors2.default.KEY_SYSTEM_ACCESS_DENIED_ERROR_CODE,_ProtectionErrors2.default.KEY_SYSTEM_ACCESS_DENIED_ERROR_MESSAGE+event.error)});}}else{logger.info('DRM: KeySystem Access Granted');eventBus.trigger(events.KEY_SYSTEM_SELECTED,{data:event.data});if(supportedKS[ksIdx].sessionId){// Load MediaKeySession with sessionId
loadKeySession(supportedKS[ksIdx].sessionId,supportedKS[ksIdx].initData);}else if(supportedKS[ksIdx].initData){// Create new MediaKeySession with initData
createKeySession(supportedKS[ksIdx].initData,supportedKS[ksIdx].cdmData);}}};eventBus.on(events.KEY_SYSTEM_ACCESS_COMPLETE,onKeySystemAccessComplete,self);protectionModel.requestKeySystemAccess(requestedKeySystems);return'break';}();if(_ret==='break')break;}}}else if(keySystem===undefined){// First time through, so we need to select a key system
keySystem=null;pendingNeedKeyData.push(supportedKS);// Add all key systems to our request list since we have yet to select a key system
for(var i=0;i<supportedKS.length;i++){requestedKeySystems.push({ks:supportedKS[i].ks,configs:[getKeySystemConfiguration(supportedKS[i].ks)]});}var keySystemAccess=void 0;var onKeySystemAccessComplete=function onKeySystemAccessComplete(event){eventBus.off(events.KEY_SYSTEM_ACCESS_COMPLETE,onKeySystemAccessComplete,self);if(event.error){keySystem=undefined;eventBus.off(events.INTERNAL_KEY_SYSTEM_SELECTED,onKeySystemSelected,self);if(!fromManifest){eventBus.trigger(events.KEY_SYSTEM_SELECTED,{data:null,error:new _DashJSError2.default(_ProtectionErrors2.default.KEY_SYSTEM_ACCESS_DENIED_ERROR_CODE,_ProtectionErrors2.default.KEY_SYSTEM_ACCESS_DENIED_ERROR_MESSAGE+event.error)});}}else{keySystemAccess=event.data;logger.info('DRM: KeySystem Access Granted ('+keySystemAccess.keySystem.systemString+')! Selecting key system...');protectionModel.selectKeySystem(keySystemAccess);}};var onKeySystemSelected=function onKeySystemSelected(event){eventBus.off(events.INTERNAL_KEY_SYSTEM_SELECTED,onKeySystemSelected,self);eventBus.off(events.KEY_SYSTEM_ACCESS_COMPLETE,onKeySystemAccessComplete,self);if(!event.error){if(!protectionModel){return;}keySystem=protectionModel.getKeySystem();eventBus.trigger(events.KEY_SYSTEM_SELECTED,{data:keySystemAccess});// Set server certificate from protData
var protData=getProtData(keySystem);if(protData&&protData.serverCertificate&&protData.serverCertificate.length>0){protectionModel.setServerCertificate(BASE64.decodeArray(protData.serverCertificate).buffer);}for(var _i=0;_i<pendingNeedKeyData.length;_i++){for(ksIdx=0;ksIdx<pendingNeedKeyData[_i].length;ksIdx++){if(keySystem===pendingNeedKeyData[_i][ksIdx].ks){// For Clearkey: if parameters for generating init data was provided by the user, use them for generating
// initData and overwrite possible initData indicated in encrypted event (EME)
if(protectionKeyController.isClearKey(keySystem)&&protData&&protData.hasOwnProperty('clearkeys')){var initData={kids:Object.keys(protData.clearkeys)};pendingNeedKeyData[_i][ksIdx].initData=new TextEncoder().encode(JSON.stringify(initData));}if(pendingNeedKeyData[_i][ksIdx].sessionId){// Load MediaKeySession with sessionId
loadKeySession(pendingNeedKeyData[_i][ksIdx].sessionId,pendingNeedKeyData[_i][ksIdx].initData);}else if(pendingNeedKeyData[_i][ksIdx].initData!==null){// Create new MediaKeySession with initData
createKeySession(pendingNeedKeyData[_i][ksIdx].initData,pendingNeedKeyData[_i][ksIdx].cdmData);}break;}}}}else{keySystem=undefined;if(!fromManifest){eventBus.trigger(events.KEY_SYSTEM_SELECTED,{data:null,error:new _DashJSError2.default(_ProtectionErrors2.default.KEY_SYSTEM_ACCESS_DENIED_ERROR_CODE,_ProtectionErrors2.default.KEY_SYSTEM_ACCESS_DENIED_ERROR_MESSAGE+'Error selecting key system! -- '+event.error)});}}};eventBus.on(events.INTERNAL_KEY_SYSTEM_SELECTED,onKeySystemSelected,self);eventBus.on(events.KEY_SYSTEM_ACCESS_COMPLETE,onKeySystemAccessComplete,self);protectionModel.requestKeySystemAccess(requestedKeySystems);}else{// We are in the process of selecting a key system, so just save the data
pendingNeedKeyData.push(supportedKS);}}function sendLicenseRequestCompleteEvent(data,error){eventBus.trigger(events.LICENSE_REQUEST_COMPLETE,{data:data,error:error});}function onKeyStatusChanged(e){if(e.error){eventBus.trigger(events.KEY_STATUSES_CHANGED,{data:null,error:e.error});}else{logger.debug('DRM: key status = '+e.status);}}function onKeyMessage(e){logger.debug('DRM: onKeyMessage');// Dispatch event to applications indicating we received a key message
var keyMessage=e.data;eventBus.trigger(events.KEY_MESSAGE,{data:keyMessage});var messageType=keyMessage.messageType?keyMessage.messageType:'license-request';var message=keyMessage.message;var sessionToken=keyMessage.sessionToken;var protData=getProtData(keySystem);var keySystemString=keySystem?keySystem.systemString:null;var licenseServerData=protectionKeyController.getLicenseServer(keySystem,protData,messageType);var eventData={sessionToken:sessionToken,messageType:messageType};// Ensure message from CDM is not empty
if(!message||message.byteLength===0){sendLicenseRequestCompleteEvent(eventData,new _DashJSError2.default(_ProtectionErrors2.default.MEDIA_KEY_MESSAGE_NO_CHALLENGE_ERROR_CODE,_ProtectionErrors2.default.MEDIA_KEY_MESSAGE_NO_CHALLENGE_ERROR_MESSAGE));return;}// Message not destined for license server
if(!licenseServerData){logger.debug('DRM: License server request not required for this message (type = '+e.data.messageType+'). Session ID = '+sessionToken.getSessionID());sendLicenseRequestCompleteEvent(eventData);return;}// Perform any special handling for ClearKey
if(protectionKeyController.isClearKey(keySystem)){var clearkeys=protectionKeyController.processClearKeyLicenseRequest(keySystem,protData,message);if(clearkeys){logger.debug('DRM: ClearKey license request handled by application!');sendLicenseRequestCompleteEvent(eventData);protectionModel.updateKeySession(sessionToken,clearkeys);return;}}// All remaining key system scenarios require a request to a remote license server
// Determine license server URL
var url=null;if(protData&&protData.serverURL){var serverURL=protData.serverURL;if(typeof serverURL==='string'&&serverURL!==''){url=serverURL;}else if((typeof serverURL==='undefined'?'undefined':_typeof(serverURL))==='object'&&serverURL.hasOwnProperty(messageType)){url=serverURL[messageType];}}else if(protData&&protData.laURL&&protData.laURL!==''){// TODO: Deprecated!
url=protData.laURL;}else{url=keySystem.getLicenseServerURLFromInitData(_CommonEncryption2.default.getPSSHData(sessionToken.initData));if(!url){url=e.data.laURL;}}// Possibly update or override the URL based on the message
url=licenseServerData.getServerURLFromMessage(url,message,messageType);// Ensure valid license server URL
if(!url){sendLicenseRequestCompleteEvent(eventData,new _DashJSError2.default(_ProtectionErrors2.default.MEDIA_KEY_MESSAGE_NO_LICENSE_SERVER_URL_ERROR_CODE,_ProtectionErrors2.default.MEDIA_KEY_MESSAGE_NO_LICENSE_SERVER_URL_ERROR_MESSAGE));return;}// Set optional XMLHttpRequest headers from protection data and message
var reqHeaders={};var withCredentials=false;var updateHeaders=function updateHeaders(headers){if(headers){for(var key in headers){if('authorization'===key.toLowerCase()){withCredentials=true;}reqHeaders[key]=headers[key];}}};if(protData){updateHeaders(protData.httpRequestHeaders);}updateHeaders(keySystem.getRequestHeadersFromMessage(message));// Overwrite withCredentials property from protData if present
if(protData&&typeof protData.withCredentials=='boolean'){withCredentials=protData.withCredentials;}var reportError=function reportError(xhr,eventData,keySystemString,messageType){var errorMsg=xhr.response?licenseServerData.getErrorResponse(xhr.response,keySystemString,messageType):'NONE';sendLicenseRequestCompleteEvent(eventData,new _DashJSError2.default(_ProtectionErrors2.default.MEDIA_KEY_MESSAGE_LICENSER_ERROR_CODE,_ProtectionErrors2.default.MEDIA_KEY_MESSAGE_LICENSER_ERROR_MESSAGE+keySystemString+' update, XHR complete. status is "'+xhr.statusText+'" ('+xhr.status+'), readyState is '+xhr.readyState+'. Response is '+errorMsg));};var onLoad=function onLoad(xhr){if(!protectionModel){return;}if(xhr.status===200){var licenseMessage=licenseServerData.getLicenseMessage(xhr.response,keySystemString,messageType);if(licenseMessage!==null){sendLicenseRequestCompleteEvent(eventData);protectionModel.updateKeySession(sessionToken,licenseMessage);}else{reportError(xhr,eventData,keySystemString,messageType);}}else{reportError(xhr,eventData,keySystemString,messageType);}};var onAbort=function onAbort(xhr){sendLicenseRequestCompleteEvent(eventData,new _DashJSError2.default(_ProtectionErrors2.default.MEDIA_KEY_MESSAGE_LICENSER_ERROR_CODE,_ProtectionErrors2.default.MEDIA_KEY_MESSAGE_LICENSER_ERROR_MESSAGE+keySystemString+' update, XHR aborted. status is "'+xhr.statusText+'" ('+xhr.status+'), readyState is '+xhr.readyState));};var onError=function onError(xhr){sendLicenseRequestCompleteEvent(eventData,new _DashJSError2.default(_ProtectionErrors2.default.MEDIA_KEY_MESSAGE_LICENSER_ERROR_CODE,_ProtectionErrors2.default.MEDIA_KEY_MESSAGE_LICENSER_ERROR_MESSAGE+keySystemString+' update, XHR error. status is "'+xhr.statusText+'" ('+xhr.status+'), readyState is '+xhr.readyState));};var reqPayload=keySystem.getLicenseRequestFromMessage(message);var reqMethod=licenseServerData.getHTTPMethod(messageType);var responseType=licenseServerData.getResponseType(keySystemString,messageType);var timeout=protData&&!isNaN(protData.httpTimeout)?protData.httpTimeout:LICENSE_SERVER_REQUEST_DEFAULT_TIMEOUT;doLicenseRequest(url,reqHeaders,reqMethod,responseType,withCredentials,reqPayload,LICENSE_SERVER_REQUEST_RETRIES,timeout,onLoad,onAbort,onError);}// Implement license requests with a retry mechanism to avoid temporary network issues to affect playback experience
function doLicenseRequest(url,headers,method,responseType,withCredentials,payload,retriesCount,timeout,onLoad,onAbort,onError){var xhr=new XMLHttpRequest();xhr.open(method,url,true);xhr.responseType=responseType;xhr.withCredentials=withCredentials;if(timeout>0){xhr.timeout=timeout;}for(var key in headers){xhr.setRequestHeader(key,headers[key]);}var retryRequest=function retryRequest(){// fail silently and retry
retriesCount--;setTimeout(function(){doLicenseRequest(url,headers,method,responseType,withCredentials,payload,retriesCount,timeout,onLoad,onAbort,onError);},LICENSE_SERVER_REQUEST_RETRY_INTERVAL);};xhr.onload=function(){if(this.status===200||retriesCount<=0){onLoad(this);}else{logger.warn('License request failed ('+this.status+'). Retrying it... Pending retries: '+retriesCount);retryRequest();}};xhr.ontimeout=xhr.onerror=function(){if(retriesCount<=0){onError(this);}else{logger.warn('License request network request failed . Retrying it... Pending retries: '+retriesCount);retryRequest();}};xhr.onabort=function(){onAbort(this);};xhr.send(payload);}function onNeedKey(event,retry){logger.debug('DRM: onNeedKey');// Ignore non-cenc initData
if(event.key.initDataType!=='cenc'){logger.warn('DRM: Only \'cenc\' initData is supported! Ignoring initData of type: '+event.key.initDataType);return;}if(mediaInfoArr.length===0){logger.warn('DRM: onNeedKey called before initializeForMedia, wait until initialized');retry=typeof retry==='undefined'?1:retry+1;if(retry<NEEDKEY_BEFORE_INITIALIZE_RETRIES){needkeyRetries.push(setTimeout(function(){onNeedKey(event,retry);},NEEDKEY_BEFORE_INITIALIZE_TIMEOUT));return;}}// Some browsers return initData as Uint8Array (IE), some as ArrayBuffer (Chrome).
// Convert to ArrayBuffer
var abInitData=event.key.initData;if(ArrayBuffer.isView(abInitData)){abInitData=abInitData.buffer;}// If key system has already been selected and initData already seen, then do nothing
if(keySystem){var initDataForKS=_CommonEncryption2.default.getPSSHForKeySystem(keySystem,abInitData);if(initDataForKS){// Check for duplicate initData
var currentInitData=protectionModel.getAllInitData();for(var i=0;i<currentInitData.length;i++){if(protectionKeyController.initDataEquals(initDataForKS,currentInitData[i])){logger.warn('DRM: Ignoring initData because we have already seen it!');return;}}}}logger.debug('DRM: initData:',String.fromCharCode.apply(null,new Uint8Array(abInitData)));var supportedKS=protectionKeyController.getSupportedKeySystems(abInitData,protDataSet);if(supportedKS.length===0){logger.debug('DRM: Received needkey event with initData, but we don\'t support any of the key systems!');return;}selectKeySystem(supportedKS,false);}function getKeySystems(){return protectionKeyController?protectionKeyController.getKeySystems():[];}function setKeySystems(keySystems){if(protectionKeyController){protectionKeyController.setKeySystems(keySystems);}}instance={initializeForMedia:initializeForMedia,createKeySession:createKeySession,loadKeySession:loadKeySession,removeKeySession:removeKeySession,closeKeySession:closeKeySession,setServerCertificate:setServerCertificate,setMediaElement:setMediaElement,setSessionType:setSessionType,setRobustnessLevel:setRobustnessLevel,setProtectionData:setProtectionData,getSupportedKeySystemsFromContentProtection:getSupportedKeySystemsFromContentProtection,getKeySystems:getKeySystems,setKeySystems:setKeySystems,stop:stop,reset:reset};setup();return instance;}ProtectionController.__dashjs_factory_name='ProtectionController';exports.default=dashjs.FactoryMaker.getClassFactory(ProtectionController);/* jshint ignore:line */
//# sourceMappingURL=ProtectionController.js.map