Have you read the FAQ and checked for duplicate open issues?: yes
What version of Shaka Player are you using?: 2.3.7
Can you reproduce the issue with our latest release version?: yes
Can you reproduce the issue with the latest code from master?: haven't tried to compile
Are you using the demo app or your own custom app?: custom
If custom app, can you reproduce the issue using our demo app?: haven't tried
What browser and OS are you using?: chrome 66 mac high sierra
What are the manifest and license server URIs?: http://yt-dash-mse-test.commondatastorage.googleapis.com/media/car-20120827-manifest.mpd
What did you do?
I'm writing a custom element that wraps shaka player. My method for loading manifests looks like this:
async loadManifest(manifestUrl, player = this.player) { // this.player is a handle on the shaka player instance
if (!player) throw new Error('Could not load player');
// If the player is already initialized, unload it's sources.
if (player.getManifest()) player.unload();
const loaded = await player.load(manifestUrl); // this promise is never resolving
this.dispatchEvent(new CustomEvent('manifest-loaded', {detail:loaded})); // breakpoints set here never break
return loaded;
}
The promise from player.load() never resolves. I never reach the breakpoint at this.dispatchEvent.
What did you expect to happen?
I expected the promise to resolve, the event to be dispatched, and the video to play
What actually happened?
Although the promise never resolves, If I run player.getManifest() in the js console, I do in fact see the parsed manifest, and I see that the manifest was loaded over the network by the browser. Nonetheless, when I try to videoElement.play() I get a DOMException: The element has no supported sources.
I'm unable to reproduce this in our demo app:
https://shaka-player-demo.appspot.com/demo/#asset=https://yt-dash-mse-test.commondatastorage.googleapis.com/media/car-20120827-manifest.mpd;lang=en-US;build=uncompiled
This leads me to expect that it's something specific to your app or your wrapper.
I notice that you are explicitly calling unload(), and also not waiting on unload() to complete. unload() is an async method that returns a Promise.
But you shouldn't have to call unload() explicitly at all, since that is implied by a subsequent load() call. You can simply call load() again without unloading it first. What happens in your app if you drop the unload() call?
After looking into this, I'm sure it has something to do with a double call to load or unload. I removed the unload call and waited on any previous load promise, and that got things working again.
Could this be an area where a console warning would help users?
Thanks.
It could be a bug. We are supposed to tolerate all manner of calling patterns between load/unload/destroy.
With the caveat that after destroy, nothing else can be called, there's no need to necessarily wait between load/unload/destroy, and they can be called multiple times.
We have a bunch of automated tests to verify this, but it's possible we have a regression that the tests don't catch.
Let me see if I can reproduce it by mimicking your code above.
I'm fascinated that you're building a polymer element for Shaka! I think that's really cool.
I can't reproduce the issue, though, in a standalone app. I started with our basic usage tutorial, and I tried adding all the same things (except requestAnimationFrame) that you had in the broken commit:
async function initPlayer() {
// Create a Player instance.
var video = document.getElementById('video');
var player = new shaka.Player(video);
// Attach player to the window to make it easy to access in the JS console.
window.player = player;
// Listen for error events.
player.addEventListener('error', onErrorEvent);
console.log('PROBE 1');
support = await shaka.Player.probeSupport();
console.log(support);
console.log('LOAD 1');
try {
player.load(manifestUri);
} catch (error) {
onError(error);
}
await delay(5);
console.log('UNLOAD');
video.pause();
video.src = '';
player.unload();
console.log('PROBE 2');
support = await shaka.Player.probeSupport();
console.log(support);
console.log('LOAD 2');
try {
await player.load(manifestUri);
} catch (error) {
onError(error);
}
}
Did I miss something?
Also, I noticed that you're doing this in the working version on each new load:
const support = await shaka.Player.probeSupport();
const manifest = support.manifest.mpd ? dashManifest : hlsManifest;
You shouldn't need to call probeSupport(). It's a method that may cause user prompts while probing DRM support, and the manifest support you're checking for is decided at compile time. So if you are using a standard build of Shaka Player, or if you've customized it in a way that has both DASH and HLS, you know ahead of time what manifest types are supported.
Even if you're using did need to use probeSupport() to check for browser support for various DRM systems and codecs, you can still cache that result across playbacks. It won't change during the life of the page.
Could it be because I'm connecting multiple players to the soon at the same time? In my case, I had these part elements as children in a virtual-list. Multiple re renders via Lit-html. Could be a contributing factor.
I was using probesupport as it seemed the only way to check at runtime if dash is supported. Did I miss something?
There is no need to use probeSupport() for DASH support if you control the build of Shaka Player that is loaded. DASH & HLS support are determined at compile time, and are both on by default.
Ahhh perhaps I wasn't clear as to what I had in mind. My idea was to load hls on iPhones as video src instead of loading a dash manifest, and, preferring feature detection to UA parsing, I thought that probeSupport was the only reliable way to do that.
Do you recommend, for example, that I should do some UA parsing in app code before passing the manifest as a property to my element?
First, we don't support iOS at all since it doesn't support MediaSource. We also do our own DASH and HLS parsing, we never pass it to the browser; this means we support them on all platforms equally. So we either support DASH and HLS or don't support anything (assuming the default build).
What you could do is use shaka.Player.isBrowserSupported to detect whether the browser is supported, then fall back to src=. Note that you should always use isBrowserSupported before creating the Player to ensure the browser supports everything we need.
if (shaka.Player.isBrowserSupported()) {
let player = new shaka.Player(video);
player.load(dashManifest);
} else {
video.src = 'foo.mp4'; // or 'foo.m3u8' on iOS/Mac.
}
Instead of UA parsing, use shaka.Player.isBrowserSupported() as @TheModMaker suggests. If that returns true, you can pass either DASH or HLS, whichever you prefer.
That sounds great. thanks for the advice, I think that should do it. I'm going to close because it seems my problem is solved, but please consider adding some more descriptive errors.