Description
Have you read the FAQ and checked for duplicate open issues?
yes
If the problem is related to FairPlay, have you read the tutorial?
yes
What version of Shaka Player are you using?
I tried latest as well master. In the demo I am using the last version on cdnjs (4.12.6)
Can you reproduce the issue with our latest release version?
yes
Can you reproduce the issue with the latest code from main
?
yes
Are you using the demo app or your own custom app?
own
If custom app, can you reproduce the issue using our demo app?
it uses custom DRM request/response filter, couldn't use demo app
What browser and OS are you using?
I tested on latest Safari on
- macos 15.1.1 - works
- ios 18.1.1 - does not work (tested on multiple phones in the office, behaved the same)
What are the manifest and license server URIs?
Its all part of the demo sample below
What configuration are you using? What is the output of player.getNonDefaultConfiguration()
?
Its all part of the demo sample below
What did you do?
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Shaka Player Example</title>
<script src="https://cdnjs.cloudflare.com/ajax/libs/shaka-player/4.12.6/shaka-player.ui.debug.min.js" integrity="sha512-dZZCdD5vzAd9E9kDye1xY/JzzflJW5CyHq3KIm+YT0KClWlD3AWfUmbJHvOSqbooxSrjNa556ng+I6GomKXjgA==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
</head>
<body>
<div id="consoleOutput" style="background-color: #f0f0f0; padding: 10px; height: 500px; overflow-y: scroll; border: 1px solid #ccc; font-size: 0.6em;"></div>
<script>
// Custom function to handle all console outputs
(function() {
// Get the consoleOutput div
var outputDiv = document.getElementById('consoleOutput');
// Function to handle logging to the div
function safeStringify(obj, space = 2) {
const seen = new WeakSet();
return JSON.stringify(obj, (key, value) => {
if (typeof value === "object" && value !== null) {
if (seen.has(value)) {
return "[Circular]";
}
seen.add(value);
}
return value;
}, space);
}
function logToDiv(type, args) {
// Convert arguments to a string representation
var msg = Array.from(args).map(arg => (typeof arg === 'string' ? arg : safeStringify(arg))).join(' ');
// Create a new div element for each log entry
var logEntry = document.createElement('div');
logEntry.textContent = msg;
// Style log entry based on the log type
if (type === 'warn') {
logEntry.style.color = 'orange';
} else if (type === 'error') {
logEntry.style.color = 'red';
} else if (type === 'info') {
logEntry.style.color = 'blue';
} else if (type === 'debug') {
logEntry.style.color = 'green';
} else {
logEntry.style.color = 'black'; // Default for log
}
// Append the log entry to the consoleOutput div
outputDiv.appendChild(logEntry);
// Scroll to the bottom of the div to see the latest log
outputDiv.scrollTop = outputDiv.scrollHeight;
}
// List of console methods to override
var methods = ['log', 'warn', 'error', 'info', 'debug'];
methods.forEach(function(method) {
var oldMethod = console[method];
console[method] = function() {
logToDiv(method, arguments);
oldMethod.apply(console, arguments); // Call the original method
};
});
})();
</script>
<video id="video" width="640" height="360" controls autoplay style="max-width: 100%; height: auto;"></video>
<script>
function uInt8ArrayToString(array) {
return String.fromCharCode.apply(null, Array.from(array));
}
function base64EncodeUint8Array(input) {
return btoa(uInt8ArrayToString(input));
}
function base64ToUint8Array(base64) {
const binaryString = atob(base64);
const len = binaryString.length;
const bytes = new Uint8Array(len);
for (let i = 0; i < len; i++) {
bytes[i] = binaryString.charCodeAt(i);
}
return bytes;
}
document.addEventListener('DOMContentLoaded', () => {
const video = document.getElementById('video');
const base64der = "MIIE2jCCA8KgAwIBAgIIKtRnQQ5QBBgwDQYJKoZIhvcNAQEFBQAwfzELMAkGA1UEBhMCVVMxEzARBgNVBAoMCkFwcGxlIEluYy4xJjAkBgNVBAsMHUFwcGxlIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MTMwMQYDVQQDDCpBcHBsZSBLZXkgU2VydmljZXMgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMjEwNjE0MDU1MjE2WhcNMjMwNjE1MDU1MjE1WjBoMQswCQYDVQQGEwJDWjEXMBUGA1UECgwOTW90di5ldSBzLnIuby4xEzARBgNVBAsMCko1WTZFQkJUUUsxKzApBgNVBAMMIkZhaXJQbGF5IFN0cmVhbWluZzogTW90di5ldSBzLnIuby4wgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAPCylmldNcZ4rG/MsXB/Lt09zz4C6MQw+oD8DRUtv8PCHbZfWejhrJqlDISeFRFboE95EyDsTR8aSFqz5qoNVHksvf+pLs87OVIeKcNaXzd0kNjdD2sy0ND9/atVsBEwGjS8hDnGLoxiPOIF+uM/yP8KfePUQZvPPVe1dczMeQ7zAgMBAAGjggHzMIIB7zAMBgNVHRMBAf8EAjAAMB8GA1UdIwQYMBaAFGPkR1TLhXFZRiyDrMxEMWRnAyy+MIHiBgNVHSAEgdowgdcwgdQGCSqGSIb3Y2QFATCBxjCBwwYIKwYBBQUHAgIwgbYMgbNSZWxpYW5jZSBvbiB0aGlzIGNlcnRpZmljYXRlIGJ5IGFueSBwYXJ0eSBhc3N1bWVzIGFjY2VwdGFuY2Ugb2YgdGhlIHRoZW4gYXBwbGljYWJsZSBzdGFuZGFyZCB0ZXJtcyBhbmQgY29uZGl0aW9ucyBvZiB1c2UsIGNlcnRpZmljYXRlIHBvbGljeSBhbmQgY2VydGlmaWNhdGlvbiBwcmFjdGljZSBzdGF0ZW1lbnRzLjA1BgNVHR8ELjAsMCqgKKAmhiRodHRwOi8vY3JsLmFwcGxlLmNvbS9rZXlzZXJ2aWNlcy5jcmwwHQYDVR0OBBYEFAIWSJaVyWs0pYaJ+U3B9Q9zg9/UMA4GA1UdDwEB/wQEAwIFIDBJBgsqhkiG92NkBg0BAwEB/wQ3AWp4aHc0bjVic3d4dG1qc3g5dWF3Z2R6Nnk4Y29semtuNWlhcWlseXYzYWJycGpwcTZob3M0MzAoBgsqhkiG92NkBg0BBAEB/wQWATZmNm56bTl3d2F6Z3Y3MDgzZ28weDANBgkqhkiG9w0BAQUFAAOCAQEAuvQFOR1to43xCjRRZssfRkJNY62qILxRN2zgG1QrfpP+Sqr93DRVEk1jym5iZOpWcH/XzK3w703+osbRWM4ADM1doInQ+1NtikfKTE1Lrcns+QNOeeFSx1datI7YSjrO/dLsilLAGWXk4J3jPA0ngSm69E+4eMQZhJj2h9ujfKJ2FNp/uSNt0Gm6XFY1KOTPaQ44xRTWcQ19Fj05FRvW8ZXD6UNl80EG6rggB58AURcsVtWsjGIJkrjtqsE/wI+ik2u8m/3zR2kCBap4+d4GGlAfz6AhR9zphGSz7fhiF4H5loHgqe0Srv62YbQGC3U+wHGKM77M2c0LWuE/cb+9mg==";
const cert = base64ToUint8Array(base64der);
if (shaka.Player.isBrowserSupported()) {
shaka.polyfill.installAll();
const player = new shaka.Player();
player.attach(video)
config = {
drm: {
servers: {
"com.apple.fps": "https://mw.motv.eu/ksm",
"com.widevine.alpha": "https://mw.motv.eu/widevine_proxy",
},
advanced: {
"com.apple.fps": {
serverCertificate: cert
}
}
}
};
const deviceHash = "sdfssdfdsf";
const version = "0.1";
const profilesId = 274483;
const devicesType = "ios";
const deviceInfo = "test";
const customerToken = "glf40bnmzmmwiw85glre6gqy8qjjvnhy99g90ul8";
player.configure(config);
player.getNetworkingEngine().registerRequestFilter((type, request) => {
if (type === shaka.net.NetworkingEngine.RequestType.LICENSE) {
request.headers.Authorization = `Bearer ${customerToken}`;
request.headers.profilesId = btoa(profilesId.toString());
request.headers.devicesType = btoa(devicesType);
request.headers.version = btoa(version);
const wrapped = {
devices_hash: deviceHash,
devices_identification: deviceInfo,
edges_id: 1,
offset: 0,
// rawLicense: shaka.util.Uint8ArrayUtils.toBase64(new Uint8Array(request.body), false), // for widevine
rawLicense: base64EncodeUint8Array(new Uint8Array(request.body)), // for fairplay
timestamp: new Date().getTime() / 1000,
};
const wrappedJson = JSON.stringify(wrapped);
request.body = shaka.util.StringUtils.toUTF8(wrappedJson);
}
});
player.getNetworkingEngine().registerResponseFilter((type, response) => {
if (type === shaka.net.NetworkingEngine.RequestType.LICENSE) {
const wrappedString = shaka.util.StringUtils.fromUTF8(response.data);
const wrapped = JSON.parse(wrappedString);
const rawLicense = wrapped.rawLicense;
response.data = shaka.util.Uint8ArrayUtils.fromBase64(rawLicense);
}
});
player.addEventListener('error', (event) => {
console.error('Shaka Player Error', event.detail);
});
player.load("https://edge1.motv.eu/s1/vods/30/neni-houba-jako-houba-3852/66c72001de232-2635-mp4-cbcs.m3u8", null).then(() => {
video.play();
}).catch((error) => {
console.error('Error loading video', error);
});
} else {
console.error("Browser not supported by Shaka Player");
}
});
</script>
</body>
</html>
What did you expect to happen?
I expected playback on iOS
What actually happened?
it fails with error 3016, Media failed to decode. You should be easily able to reproduce the same using the code sample above or its available on URL https://mw.motv.eu/static/player.html
I tested on multiple mac's and it worked everywhere, I would expect that the some code would work on iOS on Safari but it does not. However the error reported is not very descriptive and I am not sure where to look now, could you please help me find the cause?
Are you planning to send a PR to fix it?
No, I am not sure what is wrong.