Apple FairPlay "Обнаружена ошибка ключа дешифрования."Javascript - PullRequest
0 голосов
/ 05 июня 2019

Я пытаюсь заставить Apple FairPlay работать, используя пример safari, предоставленный в SDK, но получаю ошибку A decryption key error was encountered.

Я использую модуль сервера ключей, найденный здесь (написано в go), который работает, если я отправлю ему пример http-запроса. Я также использую ключи разработки по умолчанию, предоставленные в SDK.

У меня есть файл dev_certificate.der, размещенный на моем локальном хосте, а также пример видеофайла master.m3u8 (который я получил из тестового контента Apple FPS).

Мои вопросы: Какую ошибку я получаю? (что это значит и т. д.) Правильно ли настроен мой HTML-файл? Нужно ли размещать сертификат иначе, чем просто файл? Любая помощь очень ценится.

Я просто пытаюсь просто использовать FairPlay для запуска видео, которое нельзя отсканировать в сафари.

Я полагаю, что это как-то связано с моей настройкой на стороне сервера, поскольку он также не работает с использованием videojs FairPlay. (та же ошибка)

Это текущий код моего html-файла (я ничего не изменил из файла примера, кроме путей к файлам).

<!DOCTYPE html>
<html>
    <!--  Portions Copyright (C) 2015 Apple Inc. All Rights Reserved.
     See the Apple Developer Program License Agreement for this file's licensing information.
     All use of these materials is subject to the terms of the Apple Developer Program License Agreement.
     -->

    <!-- Usage Notes:
        At least 4 adjustments need to be made for functionality and to meet the needs of the integrator:
         a) update path to your FPS certificate
         b) update path to your Key Security Module (KSM)
         c) update for the SPC extraction from the POST according to your client protocol
         d) update for the m3u8 URL lookup and communication in the response to the client

        The sections requiring tweaking are marked with the "ADAPT" comment in the code below.
    -->

    <head>
        <title>FPS Template</title>
        <script>

        /*
            The EME specification (https://dvcs.w3.org/hg/html-media/raw-file/tip/encrypted-media/encrypted-media.html)
            is supported starting OSX 10.10 and greater.
        */
        var keySystem;
        var certificate;
        var serverCertificatePath = 'http://localhost/dev_certificate.der'; // ADAPT: This is the path to the fps certificate on your server.
        var serverProcessSPCPath  = 'http://localhost:8080/fps/rest/getLicense';     // ADAPT: This is the path/URL to the keyserver module that processes the SPC and returns a CKC

        function stringToArray(string) {
            var buffer = new ArrayBuffer(string.length*2); // 2 bytes for each char
            var array = new Uint16Array(buffer);
            for (var i=0, strLen=string.length; i<strLen; i++) {
                array[i] = string.charCodeAt(i);
            }
            return array;
        }

        function arrayToString(array) {
            var uint16array = new Uint16Array(array.buffer);
            return String.fromCharCode.apply(null, uint16array);
        }

        function base64DecodeUint8Array(input) {
            var raw = window.atob(input);
            var rawLength = raw.length;
            var array = new Uint8Array(new ArrayBuffer(rawLength));

            for(i = 0; i < rawLength; i++)
                array[i] = raw.charCodeAt(i);

            return array;
        }

        function base64EncodeUint8Array(input) {
            var keyStr = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
            var output = "";
            var chr1, chr2, chr3, enc1, enc2, enc3, enc4;
            var i = 0;

            while (i < input.length) {
                chr1 = input[i++];
                chr2 = i < input.length ? input[i++] : Number.NaN; // Not sure if the index
                chr3 = i < input.length ? input[i++] : Number.NaN; // checks are needed here

                enc1 = chr1 >> 2;
                enc2 = ((chr1 & 3) << 4) | (chr2 >> 4);
                enc3 = ((chr2 & 15) << 2) | (chr3 >> 6);
                enc4 = chr3 & 63;

                if (isNaN(chr2)) {
                    enc3 = enc4 = 64;
                } else if (isNaN(chr3)) {
                    enc4 = 64;
                }
                output += keyStr.charAt(enc1) + keyStr.charAt(enc2) +
                    keyStr.charAt(enc3) + keyStr.charAt(enc4);
            }
            return output;
        }

        function waitForEvent(name, action, target) {
            target.addEventListener(name, function() {
                    action(arguments[0]);
                    }, false);
        }

        function loadCertificate()
        {
            var request = new XMLHttpRequest();
            request.responseType = 'arraybuffer';
            request.addEventListener('load', onCertificateLoaded, false);
            request.addEventListener('error', onCertificateError, false);
            request.open('GET', serverCertificatePath, true);
            request.setRequestHeader('Pragma', 'Cache-Control: no-cache');
            request.setRequestHeader("Cache-Control", "max-age=0");
            request.send();
        }

        function onCertificateLoaded(event) {
            var request = event.target;
            certificate = new Uint8Array(request.response);
            startVideo();
        }

        function onCertificateError(event) {
            window.console.error('Failed to retrieve the server certificate.')
        }

        function extractContentId(initData) {
            contentId = arrayToString(initData);
            // contentId is passed up as a URI, from which the host must be extracted:
            var link = document.createElement('a');
            link.href = contentId;
            return link.hostname;
        }

        function concatInitDataIdAndCertificate(initData, id, cert) {
            if (typeof id == "string")
                id = stringToArray(id);
            // layout is [initData][4 byte: idLength][idLength byte: id][4 byte:certLength][certLength byte: cert]
            var offset = 0;
            var buffer = new ArrayBuffer(initData.byteLength + 4 + id.byteLength + 4 + cert.byteLength);
            var dataView = new DataView(buffer);

            var initDataArray = new Uint8Array(buffer, offset, initData.byteLength);
            initDataArray.set(initData);
            offset += initData.byteLength;

            dataView.setUint32(offset, id.byteLength, true);
            offset += 4;

            var idArray = new Uint16Array(buffer, offset, id.length);
            idArray.set(id);
            offset += idArray.byteLength;

            dataView.setUint32(offset, cert.byteLength, true);
            offset += 4;

            var certArray = new Uint8Array(buffer, offset, cert.byteLength);
            certArray.set(cert);

            return new Uint8Array(buffer, 0, buffer.byteLength);
        }

        function selectKeySystem()
        {
            if (WebKitMediaKeys.isTypeSupported("com.apple.fps.1_0", "video/mp4"))
            {
                keySystem = "com.apple.fps.1_0";
            }
            else
            {
                throw "Key System not supported";
            }
        }

        function startVideo()
        {
            var video = document.getElementsByTagName('video')[0];
            video.addEventListener('webkitneedkey', onneedkey, false);
            video.addEventListener('error', onerror, false);
            // ADAPT: there must be logic here to fetch/build the appropriate m3u8 URL
            video.src = 'http://localhost/master.m3u8';
        }

        function onerror(event) {
            window.console.error('A video playback error occurred')
        }

        function onneedkey(event)
        {
            var video = event.target;
            var initData = event.initData;
            var contentId = extractContentId(initData);
            initData = concatInitDataIdAndCertificate(initData, contentId, certificate);

            if (!video.webkitKeys)
            {
                selectKeySystem();
                video.webkitSetMediaKeys(new WebKitMediaKeys(keySystem));
            }

            if (!video.webkitKeys)
                throw "Could not create MediaKeys";

            var keySession = video.webkitKeys.createSession("video/mp4", initData);
            if (!keySession)
                throw "Could not create key session";

            keySession.contentId = contentId;
            waitForEvent('webkitkeymessage', licenseRequestReady, keySession);
            waitForEvent('webkitkeyadded', onkeyadded, keySession);
            waitForEvent('webkitkeyerror', onkeyerror, keySession);
        }

        /*
            This function assumes the Key Server Module understands the following POST format --
            spc=<base64 encoded data>&assetId=<data>
            ADAPT: Partners must tailor to their own protocol.
        */
        function licenseRequestReady(event)
        {
            var session = event.target;
            var message = event.message;
            var request = new XMLHttpRequest();
            var sessionId = event.sessionId;
            request.responseType = 'text';
            request.session = session;
            request.addEventListener('load', licenseRequestLoaded, false);
            request.addEventListener('error', licenseRequestFailed, false);
            var params = 'spc='+base64EncodeUint8Array(message)+'&assetId='+encodeURIComponent(session.contentId);
            request.open('POST', serverProcessSPCPath, true);
            request.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
            request.send(params);
        }

        function licenseRequestLoaded(event)
        {
            var request = event.target;
            var session = request.session;
            // response can be of the form: '\n<ckc>base64encoded</ckc>\n'
            // so trim the excess:
            keyText = request.responseText.trim();
            if (keyText.substr(0, 5) === '<ckc>' && keyText.substr(-6) === '</ckc>')
                keyText = keyText.slice(5,-6);
            key = base64DecodeUint8Array(keyText);
            session.update(key);
        }

        function licenseRequestFailed(event)
        {
            window.console.error('The license request failed.');
        }

        function onkeyerror(event) {
            window.console.error('A decryption key error was encountered');
        }

        function onkeyadded(event) {
            window.console.log('Decryption key was added to session.');
        }

        </script>
    </head>
    <body onload="loadCertificate()">
        <video controls autoplay preload="auto" width=960></video>
    </body>
</html>

Если вы нуждаетесь во мне, чтобы предоставить что-то еще, я более чем счастлив сделать это.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...