Search code examples
html5-videoplayready

HTML5 player not working on chrome


I'm new to Stackoverflow and this will be my first question. My HTML5 player works fine on Internet Explorer but doesn't work on google chrome. I'm using a PlayReady stream which is encrypted with CENC. How can I let this work on chrome? I don't have access to the servers, they're run by third parties.

Thanks


Solution

  • Technically it is possible to support Widevine while you're stream is PlayReady. This is possible since you use CENC. Since you don't have access to the servers like you mentioned you can use a technique called PSSH Forging. It basically replaces the pieces to make chrome think it's Widevine, since it's CENC the CDM will decrypt the video and the stream will play.

    For the sake of ease i'm going to assume you use DASH.

    We have here a PSSH Box:

    const widevinePSSH = '0000005c7073736800000000edef8ba979d64acea3c827dcd51d21ed0000003c080112101c773709e5ab359cbed9512bc27755fa1a087573702d63656e63221848486333436557724e5a792b32564572776e64562b673d3d2a003200';
    

    You need to replace 1c773709e5ab359cbed9512bc27755fa with your KID.

    And then at the part where you insert you'r segment in the SourceBuffer (before appendSegment) you can do the following:

    let segment = args[0];
    
        segment = new Uint8Array(segment);
        const newPssh = widevinePSSH.replace('1c773709e5ab359cbed9512bc27755fa', psshKid);
        const subArray = new Uint8Array(DRMUtils.stringToArrayBuffer('70737368'));
    
        let index = 0;
        const found = subArray.every((item) => {
            const masterIndex = segment.indexOf(item, index);
    
            if (~masterIndex) {
                index = masterIndex;
                return true;
            }
        });
    
        if (found) {
            return originalSourceBufferAppendBuffer.apply(this, [].slice.call(args));
        }
    
        segment = DRMUtils.uInt8ArrayToHex(segment);
    
        // Inject the forged signal
        // 70737368 = pssh
        segment = segment.substr(0, segment.lastIndexOf('70737368') - 8) + newPssh + segment.substr(segment.lastIndexOf('70737368') - 8);
    
        // Fix the MOOV atom length
        // 6d6f6f76 = moov
        const header = segment.substr(0, segment.indexOf('6d6f6f76') - 8);
        const payload = segment.substr(segment.indexOf('6d6f6f76') - 8);
        const newLength = Math.floor(payload.length / 2);
    
        segment = header + DRMUtils.intToHex(newLength, 8) + payload.substr(8);
    
        segment = decode(segment).b;
    

    Sadly i can only share bits and pieces but this is roughly what you should do to get it working.