Open asgvard opened 4 years ago
drmChallengeRequest.send(decodedMessage);
will UTF-8 escape your "string" and will basically destroy it. Convert it to byte array first:
var buf = new Uint8Array(decodedMessage.length);
for (var i = 0; i < decodedMessage.length; ++i)
buf[i] = decodedMessage.charCodeAt(i);
drmChallengeRequest.send(buf);
Thanks, that actually worked :) At least we are not getting 500 on license request now, however still getting an error in prepareAsync
, but this is the next step to figure out.
Try this:
function drmEventCallback(event, data) {
if (data.name == "Challenge") {
// request license data from license server (via HTTP POST)
var message = atob(data.challenge); // The challenge data is base64 encoded type.
var buf = new Uint8Array(message.length);
for (var i = 0; i < message.length; ++i) {
buf[i] = message.charCodeAt(i);
}
var xmlhttp = new XMLHttpRequest();
xmlhttp.responseType = "arraybuffer";
xmlhttp.open("POST", "http://widevine-proxy.appspot.com/proxy", true);
xmlhttp.onload = function (e) {
if (this.status == 200) {
if (this.response) {
var buf = new Uint8Array(/*ArrayBuffer*/this.response);
var list = [];
for (var i = 0; i < buf.length; ++i)
list.push(String.fromCharCode(buf[i]));
var re = list.join('');
console.log("re.len", re.length);
var licenseParam = data.session_id + "PARAM_START_POSITION" + btoa(re) + "PARAM_START_POSITION";
webapis.avplay.setDrm("WIDEVINE_CDM", "widevine_license_data", licenseParam);
}
}
};
xmlhttp.send(buf);
//<-- server interface guide -->
} else if (data.name == "DrmError") {
// error handling
}
};
But we couldn't make it play for more than 20 seconds.
Edit: Regarding 500
-- it is likely bug in error handling in Widevine proxy. Try adding from google.protobuf import message
on the top of proxy.py
(run it in a console to be able to see errors).
Hi, thanks for reply. I actually just tried to do similar thing, decoded the license response from ArrayBuffer to a string with:
const base64Response = btoa(new Uint8Array(this.response)
.reduce((data, byte) => data + String.fromCharCode(byte), ''));
const licenseParam =
`${drmData.session_id}PARAM_START_POSITION${base64Response}PARAM_START_POSITION`;
webapis.avplay.setDrm(DRM_WIDEVINE, 'widevine_license_data', licenseParam);
Now the streaming works 👍 Thanks again for tips
@asgvard Thanks for the information. That's a lot of help.
Could you please provide a full source code if possible?
I'm also trying to play the same sample you've mentioned but I can't get the prepareCallback
. (License challenge is fine.)
Here's my partial source code. (With Tizen 5.0)
function drmEventCallback(event, data) {
console.log(event, data);
log(data.name);
// Challenge case
if(data.name == "Challenge") {
console.log(data.session_id);
// request license data from license server (via HTTP POST)
var message = atob(data.challenge); // The challenge data is base64 encoded type.
console.log(message, data.challenge);
var buf = new Uint8Array(message.length);
for (var i = 0; i < message.length; ++i)
buf[i] = message.charCodeAt(i);
// <--The application should follow the server interface guide when acquiring license data -->
var xmlhttp = new XMLHttpRequest();
xmlhttp.responseType = "arraybuffer";
// xmlhttp.open("POST", "http://widevine-proxy.appspot.com/proxy", true);
xmlhttp.open("POST", "https://cwip-shaka-proxy.appspot.com/no_auth", true);
xmlhttp.onload = function(e) {
if (this.status == 200) {
if (this.response) {
const base64Response = btoa(new Uint8Array(this.response)
.reduce(function (data, byte) { return data + String.fromCharCode(byte) }, ''));
const licenseParam =
data.session_id + 'PARAM_START_POSITION' + base64Response + 'PARAM_START_POSITION';
console.log('param = ' ,licenseParam);
webapis.avplay.setDrm("WIDEVINE_CDM", 'widevine_license_data', licenseParam);
}
}
};
xmlhttp.send(buf);
//<-- server interface guide -->
} else if(data.name == "DrmError") {
// error handling
}
}
function prepareCallback() {
console.log('prep callback!');
webapis.avplay.play();
}
var json = {
"AppSession" : "25351",
"DataType" : "MPEG-DASH" // The application should set DataType when the value is 'matroska_webm'.
};
var properties = JSON.stringify(json);
webapis.avplay.open(url);
webapis.avplay.setListener({
ondrmevent:drmEventCallback,
onbufferingstart: function () {
log("Buffering start.");
},
onbufferingprogress: function (percent) {
log("Buffering progress data : " + percent);
},
onbufferingcomplete: function () {
log("Buffering complete.");
},
oncurrentplaytime: function (currentTime) {
log("Current playtime: " + currentTime);
},
onevent: function (eventType, eventData) {
log("event type: " + eventType + ", data: " + eventData);
},
onstreamcompleted: function () {
log("Stream Completed");
this.stop();
}.bind(this),
onerror: function (eventType) {
log("event type error : " + eventType);
}
});
webapis.avplay.setDrm("WIDEVINE_CDM", "SetProperties", properties);
webapis.avplay.setDisplayRect(0, 0, 1920, 1080);
webapis.avplay.prepareAsync(prepareCallback);
Thanks.
In our case the culprit for 20-seconds playback turned out to be Widevine licensing proxy server. Nevertheless, same code works on some years/models and does not work on other years/models. Which year/model you have issues with? Verify if you've properly whitelisted all referenced domains in config.xml
.
@606u Thanks for your sharing! I just tried with your code and it WORKED!
But there're some problem. It would be very appreciated if you have ever encountered with those problems and share your experience.
(Updated)
There's a long stall on calling prepareAsync
. The TV app calls .mpd file over the network immediately. but after that, it takes 10 seconds before it calls License server.
~~When it first calls prepareAsync
, Buffering start event fires and it starts buffering. Then Buffering end events is called. But it never play after that. I tried to call avplay.play()
on onbufferingcomplete
event but InvalidStateError
occurs.
On the second prepareAsync
call, it starts playing normally sometimes or it repeats the same error sometimes.~~
NVM. I was calling prepare()
and prepareAsync()
at the same time. :P
And as you mentioned that your code worked on some years/models and does not work on other years/models. How does that end up with? It seems that it's a total nightmare. Like, if it's true, it seems that making a useful app with using AVPlay is completely impossible.
BTW, my testing device model is QN65Q90RAFXKR , which came out on 2019.
Thanks.
Well, our issue with some models playing Widevine-protected content, while other models not playing same content, is still open.
import * as b64ab from 'base64-arraybuffer';
...
...
// WIDEVINE_CDM
async handleDrmEvent(event, data) {
if (data.name === 'Challenge') {
const response = await fetch(
licenseServer,
{
method: 'post',
body: b64ab.decode(data.challenge)
}
);
if (response.ok) {
const buffer = await response.arrayBuffer();
const encode = b64ab.encode(buffer);
const licenseParam = `${data.session_id}PARAM_START_POSITION${encode}PARAM_START_POSITION`;
this.webapis.avplay.setDrm('WIDEVINE_CDM', 'widevine_license_data', licenseParam);
} else {
console.error('handleWidevineEvent', response.status);
}
}
Can anyone provide me the working example of widevine modular in Tizen 2.4
i put working example of widevine player for tizen 6.5 in this repo: https://github.com/zamroni111/samsung_avplay_widevine
The HTTP response's content-type header must be application/dash+xml. MPD such as "https://storage.googleapis.com/shaka-demo-assets/angel-one-widevine/dash.mpd" is unaplayable due to incorrect content-type.
Additional features:
Hi!
We are having some issues with the integration of Widevine Modular DRM using avplay. Followed the example from this page: DRM Contents Playback Sequence, but even with the testing streams from Shaka demo samples we are getting "Invalid license challenge" 500 errors from the license servers. Same content works with Shaka player. There is some difference in how EME in the browser constructs the Challenge body and how avplay does it in "ondrmevent" callback, but it's hard to understand since it's base64 encoded (even after decoding it's still not readable).
Please provide working updated example of Widevine Modular integration.
Sample Dash Manifest URL: https://storage.googleapis.com/shaka-demo-assets/angel-one-widevine/dash.mpd Sample Widevine License URL: https://cwip-shaka-proxy.appspot.com/no_auth No custom headers needed, everything works with Shaka on this combination.
Brief example of how we do it in the code:
Here is the Diff of how Shaka player constructs license challenge request body, and how Tizen native player does that. First of all Tizen player doesn't detect TV model properly, and then there is a difference at the bottom of the request, but since it's unique at each request and encoded, it's impossible to understand which part of it is incorrect: https://www.diffchecker.com/h8CZ5Y3X