Playing DRM Content

webOS TV provides the DRM Service to support DRM content playback. The DRM service manages the DRM Client which holds a certain type of information required to get a license key. When playing DRM content, Media Pipeline (Player) needs the license key for decryption. Media Pipeline acquires the license key either directly from a License Server (Widevine) or a DRM Client (PlayReady).

webOS TV supports the following DRM types:

Widevine

On Widevine DRM playback, the DRM Client only holds credential information. Media Pipeline gets the credential information from the DRM Client and requests a key from the DRM License Server with the credential information. Media Pipeline uses the returned key for decryption. The following diagram shows the operation flow of Widevine DRM content playback.

Widevine_Structure.png

The following introduces each component of the above diagram:

  • DRM Service
    The DRM Service manages the DRM Client instance and provides APIs to manage the DRM Client.

  • Web App
    The web app requests Media Pipeline to play DRM content and sends the DRM message (credential information) to the DRM Client.

  • DRM Client
    The DRM Client only holds credential information.

  • Media Pipeline
    Media Pipeline gets the credential information from the DRM Client and requests the key from the DRM License Server. After getting the license key, Media Pipeline decrypts and plays DRM Content.

  • Content Server
    The Content Server stores and publishes DRM content.

  • License Server
    The License Server manages rights and returns the key for decryption.

The following sequence diagram shows the Widevine DRM playback process among the components.

Widevine_Sequence_Diagram.png

The Widevine DRM playback sequence is described as below:

  1. The web app requests the DRM Service to create a DRM Client with the load() method. The load() method requires the DRM type and App ID.

  2. The DRM Service creates the DRM Client instance and returns its ID to the web app with a callback function.

  3. The web app uses the sendDrmMessage() method to give the DRM message to the DRM client. The DRM message holds credential information in DRM message format.

  4. The DRM Client verifies the DRM message and returns the result to the web app with a callback function.

  5. The web app gives a content target, DRM Client ID, and playback options to Media Pipeline with the video element and mediaOption Parameter.

  6. Media Pipeline gets credential information from the DRM Client.

  7. Media Pipeline gets the license key from the License Server. Media Pipeline requests it with credential information.

  8. Media Pipeline decrypts the DRM content stream with the license key and plays the decrypted content stream.

  9. The DRM Client must be removed with the unload() method before unloading the web app.

You must unload the DRM Client before:

  • Terminating the your app

  • Preparing to play other types of DRM content (for re-initialization).

To play Widevine DRM content, you need to use LS API of the DRM Service and mediaOption parameter. You can play Widevine DRM content as follows:

  1. Add the webOS library in your app to call DRM Service API.

    // HTML Code
    ...
    <script type="text/javascript" src="lib/webOS.js"></script>
    ...
    
  2. Call the load() method when your app is loaded.

    var appId = "com.yourdomain.yourapp";
    
    // Define DRM Type
    var drmType = "widevine";
    var clientId;
    var isDrmClientLoaded;
    
    function loadDrmClient() {
        var request = webOS.service.request("luna://com.webos.service.drm", { 
            method:"load", 
            parameters: {
                "drmType": drmType,
                "appId": appId
            }, 
            onSuccess: function (result) {
                clientId = result.clientId;
                isDrmClientLoaded= true;
                console.log("DRM Client is loaded successfully.");
            },
            onFailure: function (result) {
                console.log("[" + result.errorCode + "] " + result.errorText);
                // Do something for error handling
            }
        });
    }
    ...
    
  3. Send the DRM message (credential information) with the sendDrmMessage() method.

    var msgId;
    
    // Message format for widevine
    var msg = '<?xml version="1.0" encoding="utf-8"?>
    <WidevineCredentialsInfo xmlns="http://www.smarttv-alliance.org/DRM/widevine/2012/protocols/">
        <ContentURL>http://example.org/examplePath/example.vob</ContentURL>
        <DeviceID>ab-cd-ef-01-02-03</DeviceID>
        <StreamID>123abc-ab12-1234-abcd-abc123</StreamID>
        <ClientIP>Sample</ClientIP>
        <DRMServerURL>https://drmserver.example.org/drmserver.cgi</DRMServerURL>
        <DRMAckServerURL>https://ackserver.example.org/ack.cgi</DRMAckServerURL>
        <DRMHeartBeatURL>https://heartbeat.example.org/heartbeat.cgi</DRMHeartBeatURL>
        <DRMHeartBeatPeriod>120</DRMHeartBeatPeriod>
        <UserData>SampleUserData</UserData>
        <Portal>Sample (e.g. company name)</Portal>
        <StoreFront>Sample (e.g. company name)</StoreFront>
        <BandwidthCheckURL>none</BandwidthCheckURL>
        <BandwidthCheckInterval>none</BandwidthCheckInterval>
    </WidevineCredentialsInfo >'
    
    // Message type for widevine
    var msgType = "application/widevine+xml";
    
    // Unique ID of DRM system
    var drmSystemId = "urn:dvb:casystemid:19156";
    
    function sendRightInformation() {
        var request = webOS.service.request("luna://com.webos.service.drm", { 
            method:"sendDrmMessage", 
            parameters: {
                "clientId": clientId,
                "msgType": msgType,
                "msg": msg,
                "drmSystemId": drmSystemId
            }, 
            onSuccess: function (result) {
                // DRM API does not return the msgId, resultCode, resultMsg for Widevine type.
                console.log("sendDrmMessage succeeded");
            },
            onFailure: function (result) {
                console.log("[" + result.errorCode + "] " + result.errorText);
            }
        });
    }
    
    

    You should do:

    • Even if you do not use the optional elements, you must specify the elements with an empty value.
    • You should keep the order of XML elements in the Widevine message as above example.

    For more detailed information, see DRM Message.

  4. Create an object for mediaOption of video element and set the playback options.

    // HTML code
    <video id="myVideo" ...> </video>
    
    // JavaScript code
    var options = {};
    options.mediaTransportType = "WIDEVINE";
    options.option = {};
    options.option.drm = {};
    options.option.drm.type = drmType;
    options.option.drm.clientId = clientId;
    
  5. Convert the created object to JSON string and encode it.

    var mediaOption = encodeURIComponent(JSON.stringify(options));
    
  6. Create the source element.

    var source = document.createElement("source");
    
  7. Add attributes to the created source element for media content. Add the encoded string to type attribute as below:

    var dataURL = "http://example.com/test.mp4";
    source.setAttribute('src', dataURL);
    source.setAttribute('type', 'video/mp4;mediaOption=' + mediaOption);
    
    
    For more information about media types, see the Supported Internet Media Type.
  8. Add the created source element to the video element or audio element.

    video = document.getElementById('myVideo');
    video.appendChild(source);
    
  9. Call the unload() method before terminating your app or preparing to play other types of DRM content (for re-initialization).

    // HTML Code
    ...
    <body onunload="unloadDrmClient()">
    ...
    
    // JavaScript Code
    ...
    function unloadDrmClient() {
        if (isDrmClientLoaded) {
            var request = webOS.service.request("luna://com.webos.service.drm", { 
                method:"unload", 
                parameters: { "clientId": clientId },
                onSuccess: function (result) {
                    isDrmClientLoaded = false;
                    console.log("DRM Client is unloaded successfully.");
                },
                onFailure: function (result) {
                    console.log("[" + result.errorCode + "] " + result.errorText);
                    // Do something for error handling
                }  
            });
        }
    }

PlayReady

On PlayReady DRM, the DRM Client requests a key from the DRM License Server with the initiator information directly. Media Pipeline gets the license key from the DRM Client. The following diagram shows the operation flow of PlayReady DRM content playback.

PlayReady_Structure.png

The following introduces each component of the above diagram:

  • DRM Service
    The DRM Service manages the DRM Client instance and provides APIs to manage the DRM Client.

  • Web App
    The web app requests Media Pipeline to play DRM content and sends the DRM message (initiator information) to the DRM client.

  • DRM Client
    The DRM Client holds initiator information and gets a license key from the License Server. Licensing error might occur during content playback via the DRM Client.

  • Media Pipeline
    Media Pipeline gets the license key from the DRM Client. Media Pipeline decrypts DRM content with the license key and plays it.

  • Content Server
    The Content Server stores and publishes DRM content.

  • License Server
    The License Server manages rights and returns the key for decryption.

The following sequence diagram shows the PlayReady DRM playback process among the components.

PlayReady_Sequence_Diagram.png

PlayReady DRM playback sequence is described as below:

  1. The web app requests the DRM Service to create a DRM Client with the load() method. The load() method requires the DRM type and App ID.

  2. The DRM Service creates the DRM Client instance and returns its ID to the web app with a callback function.

  3. The web app uses the sendDrmMessage() method to give the DRM message to the DRM client. The DRM message holds initiator information in DRM message format.

  4. The DRM client verifies the DRM message and returns the result with msgId. The msgId is used to identify the subscription message of the getRightsError() method.

  5. As licensing error might occur during content playback via the DRM Client, the web app needs to subscribe licensing error message with the getRightsError() method.

  6. The DRM client gets a license key from the License Server.

  7. The web app gives a content target, DRM Client ID, and playback options to Media Pipeline with the video element and mediaOption Parameter.

  8. Media Pipeline gets the license key from the DRM Client.

  9. Media Pipeline decrypts the DRM content stream with the license key and plays the decrypted content stream.

  10. Callback function, registered by the getRightsError() method for the licensing error message, verifies the message and handles errors during content playback.

  11. The DRM Client must be removed with the unload() method before unloading the web app.

Licensing error message is broadcasted to every subscribed app. The web app needs to check the valid message with msgId which is given by the sendDrmMessage() method.

You must unload the DRM Client before:

  • Terminating your app

  • Preparing to play other types of DRM content (for re-initialization).

To play PlayReady DRM content, you need to use LS API of the DRM Service and mediaOption parameter. You can play PlayReady DRM content as follows:

  1. Add the webOS library in your app to call DRM Service API.

    // HTML Code
    ...
    <script type="text/javascript" src="lib/webOS.js"></script>
    ...
    
  2. Call the load() method when your app is loaded.

    var appId = "com.yourdomain.yourapp";
    
    // Define DRM Type
    var drmType = "playready";
    var clientId;
    var isDrmClientLoaded;
    
    function loadDrmClient() {
        var request = webOS.service.request("luna://com.webos.service.drm", { 
            method:"load", 
            parameters: {
                "drmType": drmType,
                "appId": appId
            }, 
            onSuccess: function (result) {
                clientId = result.clientId;
                isDrmClientLoaded= true;
                console.log("DRM Client is loaded successfully.");
            },
            onFailure: function (result) {
                console.log("[" + result.errorCode + "] " + result.errorText);
                // Do something for error handling
            }
        });
    }
    
  3. Send the DRM message (initiator information) with the sendDrmMessage() method.

    // Message example for playready
    var msg = '<?xml version="1.0" encoding="utf-8"?>
    <PlayReadyInitiator xmlns= "http://schemas.microsoft.com/DRM/2007/03/protocols/">
      <LicenseAcquisition>
        <Header>
          <WRMHEADER xmlns= "http://schemas.microsoft.com/DRM/2007/03/PlayReadyHeader" version="4.0.0.0">
            <DATA>
              <PROTECTINFO>
                <KEYLEN>16</KEYLEN>
                <ALGID>AESCTR</ALGID>
              </PROTECTINFO>
              <LA_URL>http://playready.directtaps.net/pr/svc/rightsmanager.asmx</LA_URL>
              <KID>lFmb2gxg0Cr5bfEnJXgJeA==</KID>
              <CHECKSUM>P7ORpD2IpA==</CHECKSUM>
            </DATA>
          </WRMHEADER>
        </Header>
        <CustomData>AuthZToken XYZ</CustomData>
      </LicenseAcquisition>
    </PlayReadyInitiator>'
    
    var msgId;
    
    // Message type for PlayReady
    var msgType = "application/vnd.ms-playready.initiator+xml"; 
    
    // Unique ID of DRM system
    var drmSystemId = "urn:dvb:casystemid:19219";
    
    function sendRightInformation() {
        request = webOS.service.request("luna://com.webos.service.drm", { 
            method:"sendDrmMessage", 
            parameters: {
                "clientId": clientId,
                "msgType": msgType,
                "msg": msg,
                "drmSystemId": drmSystemId
            }, 
            onSuccess: function (result) {
                msgId = result.msgId;
                var resultCode = result.resultCode;
                var resultMsg = result.resultMsg;
                console.log("Message ID: " + msgId);
                console.log("[" + resultCode + "] " + resultMsg);
                
                if (resultCode != 0){
                    // Do Handling DRM message error
                }
            },
            onFailure: function (result) {
                console.log("[" + result.errorCode + "] " + result.errorText);
            }
        });
    }
    
    

    For more information about DRM messages, see DRM Message.

  4. Subscribe the licensing error message with the getRightsError() method.

    // JavaScript code
    var subscriptionHandler;
    ...
    function subscribeLicensingError() {
        var request = webOS.service.request("luna://com.webos.service.drm", { 
            method:"getRightsError", 
            parameters: {
                "clientId": clientId,
                "subscribe": true
            }, 
            onSuccess: function (result) { // Subscription Callback
                contentId = result.contentId;
                if (contentId == msgId) {
                    if ( 0 == result.errorState) {
                        console.log("No license");
                        // Do something for error handling
                    }
                    else if ( 1 == result.errorState) {
                        console.log("Invalid license");
                        // Do something for error handling
                    }
                    console.log("DRM System ID: " + result.drmSystemId);
                    console.log("License Server URL: " + result.rightIssueUrl);
                }
            },
            onFailure: function (result) {
                console.log("[" + result.errorCode + "] " + result.errorText);
            }
        });
        //Register subscription handler
        subscriptionHandler = request;
    }
    ...
    
  5. Create an object for mediaOption of the video element and set the playback options.

    // HTML code
    <video id="myVideo" ...> </video>
    
    // JavaScript code
    var options = {};
    options.option = {};
    options.option.drm = {};
    options.option.drm.type = drmType;
    options.option.drm.clientId = clientId;
    
  6. Convert the created object to JSON string and encode it.

    var mediaOption = encodeURIComponent(JSON.stringify(options));
    
  7. Create the source element.

    var source = document.createElement("source");
    
  8. Add attributes to the created source element for media content. Add the encoded string to type attribute as below:

    var dataURL = "http://example.com/test.mp4";
    source.setAttribute('src', dataURL);
    source.setAttribute('type', 'video/mp4;mediaOption=' + mediaOption);
    
    

    For more information about media types, see the Supported Internet Media Type.

  9. Add the created source element to the video element or audio element.

    video = document.getElementById('myVideo');
    video.appendChild(source);
    
  10. Call the unload() method and cancel the subscription before terminating your app or preparing to play other types of DRM content (for re-initialization).

    // HTML Code
    ...
    <body onunload="unloadDrmClient()">
    ...
    
    // JavaScript Code
    ...
    function unloadDrmClient() {
        if (isDrmClientLoaded) {
            var request = webOS.service.request("luna://com.webos.service.drm", { 
                method:"unload", 
                parameters: { "clientId": clientId },
                onSuccess: function (result) {
                    isDrmClientLoaded = false;
                    console.log("DRM Client is unloaded successfully.");
                },
                onFailure: function (result) {
                    console.log("[" + result.errorCode + "] " + result.errorText);
                    // Do something for error handling
                }
            });
    
            // Cancel the subscription
            subscriptionHandler.cancel();
        }
    }
Navigation