<commit_before>/**
 * Copyright 2015 CANAL+ Group
 *
 * 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.
 */

import objectAssign = require("object-assign");
import { Observable } from "rxjs/Observable";
import {
  hasEMEAPIs,
  IMediaKeySystemAccess,
  IMockMediaKeys,
  shouldUnsetMediaKeys,
} from "../../compat/";
import { onEncrypted$ } from "../../compat/events";
import { EncryptedMediaError } from "../../errors";
import assert from "../../utils/assert";
import castToObservable from "../../utils/castToObservable";
import log from "../../utils/log";
import noop from "../../utils/noop";
import {
  $loadedSessions,
  $storedSessions,
} from "./globals";
import findCompatibleKeySystem, {
  getKeySystem,
  IInstanceInfo,
  IKeySystemAccessInfos,
  IKeySystemOption,
 } from "./key_system";
import { trySettingServerCertificate } from "./server_certificate";
import manageSessionCreation, {
  ErrorStream,
  ISessionEvent,
 } from "./session";
import setMediaKeysObs, { disposeMediaKeys } from "./set_media_keys";

interface IMediaKeysInfos extends IKeySystemAccessInfos {
  mediaKeys : MediaKeys|IMockMediaKeys;
}

// Persisted singleton instance of MediaKeys. We do not allow multiple
// CDM instances.
const instanceInfos : IInstanceInfo = {
  $mediaKeys: null,  // MediaKeys instance
  $mediaKeySystemConfiguration: null, // active MediaKeySystemConfiguration
  $keySystem: null,
  $videoElement: null,
};

/**
 * Call the createMediaKeys API and cast it to an observable.
 * @param {MediaKeySystemAccess} keySystemAccess
 * @returns {Observable}
 */
function createMediaKeysObs(
  keySystemAccess : IMediaKeySystemAccess
) : Observable<IMockMediaKeys|MediaKeys> {
  return Observable.defer(() => {
  // MediaKeySystemAccess.prototype.createMediaKeys returns a promise
    return castToObservable(keySystemAccess.createMediaKeys());
  });
}

/**
 * Set the session storage given in options, if one.
 * @param {Object} keySystem
 */
function setSessionStorage(keySystem: IKeySystemOption) : void {
  if (keySystem.persistentLicense) {
    if (keySystem.licenseStorage) {
      log.info("set the given license storage");
      $storedSessions.setStorage(keySystem.licenseStorage);
    } else {
      const error = new Error("no license storage found for persistent license.");
      throw new EncryptedMediaError("INVALID_KEY_SYSTEM", error, true);
    }
  }
}

/**
 * React to each "encrypted" events.
 * @param {MediaEncryptedEvent} encryptedEvent
 * @param {Object} mediaKeysInfos
 * @param {HTMLMediaElement} video
 * @param {Subject} ErrorStream
 * @returns {Observable}
 */
function handleEncryptedEvent(
  encryptedEvent : MediaEncryptedEvent,
  mediaKeysInfos : IMediaKeysInfos,
  video : HTMLMediaElement,
  errorStream : ErrorStream
): Observable<IMockMediaKeys|MediaKeys|ISessionEvent|Event> {
  return Observable.defer(() => {
    log.info("eme: encrypted event", encryptedEvent);

    if (encryptedEvent.initData == null) {
      const error = new Error("no init data found on media encrypted event.");
      throw new EncryptedMediaError("INVALID_ENCRYPTED_EVENT", error, true);
    }

    const {
      mediaKeys,
      keySystem,
      keySystemAccess,
    } = mediaKeysInfos;
    const mksConfig = keySystemAccess.getConfiguration();
    const initData = new Uint8Array(encryptedEvent.initData);

    // The mediaKeys is lazily attached to the media element
    const setMediaKeys$ =
      setMediaKeysObs(mediaKeys, mksConfig, video, keySystem, instanceInfos);
    const manageSessionCreation$ = manageSessionCreation(
      mediaKeys, mksConfig, keySystem, encryptedEvent.initDataType,
      initData, errorStream);
    return Observable.merge(setMediaKeys$, manageSessionCreation$);
 });
}

/**
 * EME abstraction and event handler used to communicate with the Content-
 * Description-Module (CDM).
 *
 * The EME handler can be given one or multiple systems and will choose the
 * appropriate one supported by the user's browser.
 * @param {HTMLMediaElement} video
 * @param {Object} keySystems
 * @param {Subject} errorStream
 * @returns {Observable}
 */
function createEME(
  video : HTMLMediaElement,
  keySystems: IKeySystemOption[],
  errorStream: ErrorStream
) : Observable<IMockMediaKeys|MediaKeys|ISessionEvent|Event> {
  if (__DEV__) {
    keySystems.forEach((ks) => assert.iface(ks, "keySystem", {
      getLicense: "function",
      type: "string",
    }));
  }

  // get the MediaKeys element the associated options
  // This is done as the "encrypted" event is not yet received for performance
  const mediaKeysInfos$ = findCompatibleKeySystem(keySystems, instanceInfos)
    .mergeMap((keySystemAccessInfos) => {
      return createMediaKeysObs(keySystemAccessInfos.keySystemAccess)
        .mergeMap(function prepareMediaKeysConfiguration(mediaKeys) {
          const { keySystem } = keySystemAccessInfos;
          const { serverCertificate } = keySystem;
          setSessionStorage(keySystem);

          const _mediaKeysInfos$ = Observable
            .of(objectAssign({ mediaKeys }, keySystemAccessInfos));

          if (
            serverCertificate != null &&
            typeof mediaKeys.setServerCertificate === "function"
          ) {
            return trySettingServerCertificate(mediaKeys, serverCertificate, errorStream)
              .concat(_mediaKeysInfos$);
          }
          return _mediaKeysInfos$;
        });
    });

  return Observable.combineLatest(onEncrypted$(video), mediaKeysInfos$)
    .mergeMap(([encryptedEvent, mediaKeysInfos]) =>
      handleEncryptedEvent(encryptedEvent, mediaKeysInfos, video, errorStream)
    );
}

/**
 * Free up all ressources taken by the EME management.
 */<commit_msg>eme: deactivate multi-DRM feature, as it can cause race conditions<commit_after>/**
 * Copyright 2015 CANAL+ Group
 *
 * 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.
 */

import objectAssign = require("object-assign");
import { Observable } from "rxjs/Observable";
import {
  hasEMEAPIs,
  IMediaKeySystemAccess,
  IMockMediaKeys,
  shouldUnsetMediaKeys,
} from "../../compat/";
import { onEncrypted$ } from "../../compat/events";
import { EncryptedMediaError } from "../../errors";
import assert from "../../utils/assert";
import castToObservable from "../../utils/castToObservable";
import log from "../../utils/log";
import noop from "../../utils/noop";
import {
  $loadedSessions,
  $storedSessions,
} from "./globals";
import findCompatibleKeySystem, {
  getKeySystem,
  IInstanceInfo,
  IKeySystemAccessInfos,
  IKeySystemOption,
 } from "./key_system";
import { trySettingServerCertificate } from "./server_certificate";
import manageSessionCreation, {
  ErrorStream,
  ISessionEvent,
 } from "./session";
import setMediaKeysObs, { disposeMediaKeys } from "./set_media_keys";

interface IMediaKeysInfos extends IKeySystemAccessInfos {
  mediaKeys : MediaKeys|IMockMediaKeys;
}

// Persisted singleton instance of MediaKeys. We do not allow multiple
// CDM instances.
const instanceInfos : IInstanceInfo = {
  $mediaKeys: null,  // MediaKeys instance
  $mediaKeySystemConfiguration: null, // active MediaKeySystemConfiguration
  $keySystem: null,
  $videoElement: null,
};

/**
 * Call the createMediaKeys API and cast it to an observable.
 * @param {MediaKeySystemAccess} keySystemAccess
 * @returns {Observable}
 */
function createMediaKeysObs(
  keySystemAccess : IMediaKeySystemAccess
) : Observable<IMockMediaKeys|MediaKeys> {
  return Observable.defer(() => {
  // MediaKeySystemAccess.prototype.createMediaKeys returns a promise
    return castToObservable(keySystemAccess.createMediaKeys());
  });
}

/**
 * Set the session storage given in options, if one.
 * @param {Object} keySystem
 */
function setSessionStorage(keySystem: IKeySystemOption) : void {
  if (keySystem.persistentLicense) {
    if (keySystem.licenseStorage) {
      log.info("set the given license storage");
      $storedSessions.setStorage(keySystem.licenseStorage);
    } else {
      const error = new Error("no license storage found for persistent license.");
      throw new EncryptedMediaError("INVALID_KEY_SYSTEM", error, true);
    }
  }
}

/**
 * React to each "encrypted" events.
 * @param {MediaEncryptedEvent} encryptedEvent
 * @param {Object} mediaKeysInfos
 * @param {HTMLMediaElement} video
 * @param {Subject} ErrorStream
 * @returns {Observable}
 */
function handleEncryptedEvent(
  encryptedEvent : MediaEncryptedEvent,
  mediaKeysInfos : IMediaKeysInfos,
  video : HTMLMediaElement,
  errorStream : ErrorStream
): Observable<IMockMediaKeys|MediaKeys|ISessionEvent|Event> {
  return Observable.defer(() => {
    log.info("eme: encrypted event", encryptedEvent);

    if (encryptedEvent.initData == null) {
      const error = new Error("no init data found on media encrypted event.");
      throw new EncryptedMediaError("INVALID_ENCRYPTED_EVENT", error, true);
    }

    const {
      mediaKeys,
      keySystem,
      keySystemAccess,
    } = mediaKeysInfos;
    const mksConfig = keySystemAccess.getConfiguration();
    const initData = new Uint8Array(encryptedEvent.initData);

    // The mediaKeys is lazily attached to the media element
    const setMediaKeys$ =
      setMediaKeysObs(mediaKeys, mksConfig, video, keySystem, instanceInfos);
    const manageSessionCreation$ = manageSessionCreation(
      mediaKeys, mksConfig, keySystem, encryptedEvent.initDataType,
      initData, errorStream);
    return Observable.merge(setMediaKeys$, manageSessionCreation$);
 });
}

/**
 * EME abstraction and event handler used to communicate with the Content-
 * Description-Module (CDM).
 *
 * The EME handler can be given one or multiple systems and will choose the
 * appropriate one supported by the user's browser.
 * @param {HTMLMediaElement} video
 * @param {Object} keySystems
 * @param {Subject} errorStream
 * @returns {Observable}
 */
function createEME(
  video : HTMLMediaElement,
  keySystems: IKeySystemOption[],
  errorStream: ErrorStream
) : Observable<IMockMediaKeys|MediaKeys|ISessionEvent|Event> {
  if (__DEV__) {
    keySystems.forEach((ks) => assert.iface(ks, "keySystem", {
      getLicense: "function",
      type: "string",
    }));
  }

  // get the MediaKeys element the associated options
  // This is done as the "encrypted" event is not yet received for performance
  const mediaKeysInfos$ = findCompatibleKeySystem(keySystems, instanceInfos)
    .mergeMap((keySystemAccessInfos) => {
      return createMediaKeysObs(keySystemAccessInfos.keySystemAccess)
        .mergeMap(function prepareMediaKeysConfiguration(mediaKeys) {
          const { keySystem } = keySystemAccessInfos;
          const { serverCertificate } = keySystem;
          setSessionStorage(keySystem);

          const _mediaKeysInfos$ = Observable
            .of(objectAssign({ mediaKeys }, keySystemAccessInfos));

          if (
            serverCertificate != null &&
            typeof mediaKeys.setServerCertificate === "function"
          ) {
            return trySettingServerCertificate(mediaKeys, serverCertificate, errorStream)
              .concat(_mediaKeysInfos$);
          }
          return _mediaKeysInfos$;
        });
    });

  return Observable.combineLatest(
    onEncrypted$(video).take(1),
    mediaKeysInfos$
  )
    .mergeMap(([encryptedEvent, mediaKeysInfos]) =>
      handleEncryptedEvent(encryptedEvent, mediaKeysInfos, video, errorStream)
    );
}

/**
 * Free up all ressources taken by the EME management.
 */
{'fraction_non_alphanumeric': 0.10325693606755126, 'fraction_numerical': 0.0015279453156413349, 'mean_word_length': 3.942766295707472, 'pattern_counts': {'":': 0, '<': 9, '<?xml version=': 0, '>': 19, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3788959', 'n_tokens_mistral': 3449, 'n_tokens_neox': 3410, 'n_words': 976}