Hosting encrypted video content using HTML5

Not so long ago, a new initiative from W3C was discussed at the hub - Encrypted Media Extensions or just EME. Let us try to understand in practice what is new and interesting to us.

So, the task: suppose there is a bunch of interesting video material that you want to put on the Internet, but not for everyone, but only for good people(for friends, acquaintances, ... or just for the money). So, the Encrypted Media Extensions initiative just suggests adding all the necessary APIs for the client part to the HTML standard. In addition, the W3C proposal also addresses other issues, such as the ill-fated DRM, but we will postpone them for now, focusing solely on the client side of our hypothetical service organization. A prerequisite is that the browser supports the EME specification, which means that the basic Clear Key system is also supported .

So, there is a tag videoand file foo.webm, the video and audio in which is encrypted using the AES algorithm (this feature is supported in the WebM container , and EME authors took advantage of this):



In this situation, the EME browser, when trying to play, foo.webmdetects that the content is encrypted, stops and will wait until it is provided with a decryption key. In general, the main use case (use case) is that information about how the encrypted content, is described directly in the container; we will use this option. In addition to stopping the browser, it will also raise the needkey event on the HTMLMediaElement itself, and the application should process it and, ultimately, provide the key:



The function handleKeyNeeded()will not be very complicated - it receives the key identifier that the browser needs, then makes an XHR request to the server and gives the answer = the key to the browser back (or does not give it back if the situation requires it - this is already business logic). We will simplify it a bit - let us already have the key; for the Clear Key + AES scheme that we use, it will be an array Uint8Arrayof 16 bytes; then the function looks like this:

function handleKeyNeeded(event) {
  var video = event.target;
  var initData = event.initData;
  //var message = initData;
  //var xmlhttp = new XMLHttpRequest();
  //xmlhttp.open("POST", "http://.../getkey", false);
  //xmlhttp.send(message);
  //var key = new Uint8Array(xmlhttp.response);
  var key = new Uint8Array([0xeb, 0xdd, 0x62, 0xf1, 0x68, 0x14, 0xd2, 0x7b,
                            0x68, 0xef, 0x12, 0x2a, 0xfc, 0xe4, 0xae, 0x3c]);
  if (!video.keys)
    video.keys = video.MediaKeys("org.w3.clearkey");
  if (!video.keys)
    throw "Could not create MediaKeys";
  var keySession = video.keys.createSession(mimeType, initData);
  if (!keySession)
    throw "Could not create key session";
  keySession.update(key);
}


The main characters here:

  • event.initData: the identifier corresponding to the key the browser needs;
  • key: the decryption key itself;
  • keySession.update(key): by this call the browser receives the same key; more from the page and is not required.


Those who are interested in the rest of the handleKeyNeeded () function code (MediaKeys, MediaKeySession classes, etc.) can use the fresh translation of the future EME specification made using the Catbo service .

Thus, if we had a browser with EME support (current version 2), then on the encrypted_media_player_v2.html page we would find a black bear walking around the cage in the video (a sample of the encrypted video was bear-320x240-av-enc_av.webmtaken from the Chromium code). At the time of this writing, such a browser, with a high degree of probability, is not. However, if you modify the page code a little, you can still see the bear using the Google Chrome or Chromium browsers version 26 or higher (while support is found only in them):encrypted_media_player.html .

Next, we will consider what improvements had to be done to make our example work. First, Chrome / Chromium now supports version 1 of the future EME specification, which EME authors have already abandoned in favor of version 2, making the latter object-oriented. However, only the first version of EME is included in Chrome / Chromium; besides, in many places it is necessary to hang a prefix webkit; because of this, the function handleKeyNeeded()will change as follows:

function handleKeyNeeded(event) {
  ...
  var keySystem = "webkit-org.w3.clearkey";
  video.webkitGenerateKeyRequest(keySystem, event.initData);
  // добавление ключа
  video.webkitAddKey(keySystem, key, event.initData, null);
}


Secondly, if we now directly set the srctag attribute video, then needkeywe will not wait for the event . The fact is that in this case the video is played by the ffmpeg stock function, which is used in the browser, and it does not pay attention to any encryption flags in the container (here are some technical details about the implementation of the media stack in Chrome / Chromium). But if we use another way of providing content to the player, then we will get the result we need (triggering an event needkey). This other way is called Media Source Extensions.; its essence is that JavaScript code is allowed to generate a media stream for later playback. In fact, this means that an alternative implementation of demuxers , such as WebM and mp4, has appeared in Google’s browsers ; it was they who were taught to excite the needkey event we needed. Here is the appropriate code to download the video with the bear in our tag video:

function load() {
  var video = document.getElementById("video");
  var mediaFile = "bear-320x240-av-enc_av.webm";
  //video.src = mediaFile;
  var sourceOpened = false;
  function onSourceOpen(e) {
    if (sourceOpened)
      return;
    sourceOpened = true;
    var mediaType = 'video/webm; codecs="vorbis, vp8"';
    var srcBuffer = mediaSource.addSourceBuffer(mediaType);
    var xhr = new XMLHttpRequest();
    xhr.open('GET', mediaFile);
    xhr.responseType = 'arraybuffer';
    xhr.addEventListener('load', function(e) {
      srcBuffer.append(new Uint8Array(e.target.response));
      mediaSource.endOfStream();
    });
    xhr.send();
  }
  var mediaSource = new WebKitMediaSource();
  mediaSource.addEventListener('webkitsourceopen', onSourceOpen);
  video.src = window.URL.createObjectURL(mediaSource);
  ...
}


Of course, this code is less versatile than simple assignment.

video.src = "bear-320x240-av-enc_av.webm";


; Moreover, the future EME specification does not require such tricks (but so far it is only possible to achieve operability). We look forward to further developments.




Outside of this article, the question remains of how to encrypt the video. This task is container-specific; WebM, for example, has this capability, but this is not true for some other containers, etc ..

EME related links:
Google, Microsoft and Netflix want to add DRM to HTML5 Translation of the Encrypted Media Extensions specification / HTML5
Encrypted Media Content

Also popular now: