We are trying to play video content using native WebOS player and com.webos.service.drm service. The goal is to play at least some DRM content (dash+widevine or hls+widevine). Clear content works fine, but DRM content stucks completely, and no diagnostic messages appear.
The same result happens on WebOS 3.4, 4.4, and 6.0. Is there any working example of DRM content playback?
Our code is given below.
var appId = "com.zodiac.app";
// Define DRM Type
var drmType = "widevine";
function webos_request(service, params)
{
var caller = arguments.callee.caller.name;
console.log("REQ: %s: %s %s", caller, service, JSON.stringify(params))
return new Promise( function(resolve, reject) {
webOS.service.request(service, Object.assign({}, params, {
onSuccess: function (result) { console.log("REQ SUCCESS: %s: %s %s: %s", caller, service, params.method, JSON.stringify(result)); resolve(result) },
// onFailure: function (result) {
// console.log("[" + result.errorCode + "] " + result.errorText);
// reject()
// }
onFailure: function (result) { console.error("REQ ERROR: %s: %s %s: %s", caller, service, params.method, JSON.stringify(result)); reject(result) }
}))
});
}
function webos_subscribe(service, params, method)
{
var caller = arguments.callee.caller.name;
var completed = false
return new Promise( function(resolve, reject) {
params.parameters = params.parameters || {}
params.parameters.subscribe = true;
console.log("SUB: %s: %s %s", caller, service, JSON.stringify(params))
webOS.service.request("luna://com.webos.service.drm", Object.assign({}, params, {
onSuccess: function (result) { // Subscription Callback
if (!completed) {
completed = true;
if (result.subscribed) {
console.log("SUB: %s: %s: SUCCESS", caller, service)
resolve(result)
}
else {
console.error("SUB: %s: %s: FAILED", caller, service)
reject();
}
}
else
method(result)
},
onFailure: function (result) {
console.error("SUB: %s: %s: onFailure", caller, service, result)
// console.log('Player.subscribeLicensingError onFailure: ' + '[' + result.errorCode + '] ' + result.errorText);
completed = true;
reject()
}
}));
});
}
function unloadDrmClient(clientId)
{
webos_request("luna://com.webos.service.drm",
{
method:"unload",
parameters: {
"clientId": result.clientId
}
});
}
function loadDrmClient()
{
return webos_request( "luna://com.webos.service.drm", {
method:"load",
parameters: {
"drmType": drmType,
"appId": appId
}})
.then(function (result) {
console.log("DRM Client is loaded successfully. %s", JSON.stringify(result));
document.addEventListener('visibilitychange', function() {
if (document.visibilityState === 'hidden') {
unloadDrmClient(result.clientId)
}
})
return result.clientId
})
}
function sendRightInformation(clientId, url, la_url) {
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>' + url + '</ContentURL>',
'<DeviceID></DeviceID>',
'<StreamID></StreamID>',
'<ClientIP></ClientIP>',
'<DRMServerURL>' + la_url + '</DRMServerURL>',
'<DRMAckServerURL></DRMAckServerURL>',
'<DRMHeartBeatURL></DRMHeartBeatURL>',
'<DRMHeartBeatPeriod>0</DRMHeartBeatPeriod>',
'<UserData></UserData>',
'<Portal></Portal>',
'<StoreFront></StoreFront>',
'<BandwidthCheckURL></BandwidthCheckURL>',
'<BandwidthCheckInterval></BandwidthCheckInterval>',
'</WidevineCredentialsInfo>',
].join("")
// Message type for widevine
var msgType = "application/widevine+xml";
// Unique ID of DRM system
var drmSystemId = "urn:dvb:casystemid:19156";
return webos_request( "luna://com.webos.service.drm", {
method:"sendDrmMessage",
parameters: {
"clientId": clientId,
"msgType": msgType,
"msg": msg,
"drmSystemId": drmSystemId
}})
.then( function (result) {
// DRM API does not return the msgId, resultCode, resultMsg for Widevine type.
console.log("sendDrmMessage succeeded. %s", JSON.stringify(result));
return
});
}
function subscribeLicensingError(clientId, msgId)
{
return webos_subscribe("luna://com.webos.service.drm", {
method:"getRightsError",
parameters: {
"clientId": clientId
}},
function (result) { // Subscription Callback
var 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
}
else {
console.log("Unknown errorState: %s", JSON.stringify(result));
}
}
else {
console.log("skip notification %s", JSON.stringify(result));
}
});
}
var video = document.getElementById('myVideo');
function playback()
{
var config = {
'dash+wv': {
type: "application/dash+xml",
mediaTransportType: "WIDEVINE",
url: 'https://bitmovin-a.akamaihd.net/content/art-of-motion_drm/mpds/11331.mpd',
la_url: 'https://widevine-proxy.appspot.com/proxy'
},
'hls+wv': {
type: "application/x-mpegURL",
mediaTransportType: "HLS",
url: 'https://storage.googleapis.com/shaka-demo-assets/angel-one-widevine-hls/hls.m3u8',
la_url: 'https://cwip-shaka-proxy.appspot.com/no_auth'
},
'mp4': {
type: "video/mp4",
url: "https://jsoncompare.org/LearningContainer/SampleFiles/Video/MP4/Sample-MP4-Video-File-Download.mp4"
},
'hls': {
type: "application/vnd.apple.mpegurl",
url: "https://bitdash-a.akamaihd.net/content/MI201109210084_1/m3u8s/f08e80da-bf1d-4e3d-8899-f0f6155f6efa.m3u8",
},
'dash': {
type: "application/dash+xml",
url: 'https://storage.googleapis.com/shaka-demo-assets/bbb-dark-truths/dash.mpd'
}
}
var stream = config['hls+wv']
var prepare
if (stream.la_url) {
prepare = loadDrmClient()
.then( function(id) {
subscribeLicensingError(id, undefined);
document.body.addEventListener("unload", function() {
webOS.service.request("luna://com.webos.service.drm", {
method:"unload",
parameters: { "clientId": id },
onSuccess: function (result) {
console.log("DRM Client is unloaded successfully.");
},
onFailure: function (result) {
console.log("[" + result.errorCode + "] " + result.errorText);
// Do something for error handling
}
});
})
return sendRightInformation(id, stream.url, stream.la_url).then( function () { return id; })
})
}
else {
prepare = Promise.resolve()
}
return prepare.then( function (id) {
var type = stream.type
if (stream.la_url) {
var options = {
mediaTransportType: stream.mediaTransportType,
option: {
drm: {
type: drmType,
clientId: id,
}
}
};
console.log("Options: %s", JSON.stringify(options));
var mediaOption = encodeURIComponent(JSON.stringify(options));
type += ';mediaOption=' + mediaOption;
}
console.log("open url: %s", stream.url)
console.log("type : %s", type)
var source = document.createElement("source");
source.setAttribute('src', stream.url);
source.setAttribute('type', type);
video.addEventListener('loadedmetadata', function(event) {
console.log("loadedmetadata %O", event)
});
video.addEventListener('error', function(e) {
console.error('error', e);
});
video.addEventListener('stalled', function(e) {
console.log('stalled', e);
});
video.addEventListener('loadeddata', function() {
console.log('[Device_Webos_Player] loadeddata');
});
video.addEventListener('loadedmetadata', function() {
console.log('[Device_Webos_Player] loadedmetadata');
});
video.addEventListener('canplay', function () {
console.log('[Device_Webos_Player] canplay');
});
video.addEventListener('durationchange', function() {
console.log('[Device_Webos_Player] durationchange: ' + video.duration);
});
video.addEventListener('timeupdate', function() {
console.log('[Device_Webos_Player] timeupdate: ' + video.currentTime);
}, { once: true});
video.appendChild(source);
// video.load()
video.play()
})
}
playback()
Finally the idea is [see here]
See "Streaming Protocol & DRM Combination" here