playSound : function() {
var audio = new Audio(audio.mp3);
audio.play();
}
I am using the above code to simply play audio. But I am facing two issues below:
The sound never plays till I click on the tab (for this I go to another tab and then click the current tab). Seems it needs an event before playing sound.
Sometimes I get exception and audio never plays "Uncaught (in promise) DOMException: play() failed because the user didn't interact with the document first."
I don't want to mess with HTML element.
You're likely seeing the policy change for autoplaying media on websites issued by Chrome: https://developers.google.com/web/updates/2017/09/autoplay-policy-changes
In essence, the policy says that unless the user interacts on your media and your handler* does not start the media synchronously, it will not play.
Notice the word synchronously - you can't even use setTimeout or some such. This is to protect the user from spammy ads and alike. The other browsers have this as well, as a setting, but they might and likely will enable this by default, so you better get ready for it.
[*] - (e.g. click handler)
Related
I have blazor WASM app with "correct" .mp3 file that is played via javascript code. On the Desktop lets say its pretty instant after pressing button but on the phone its skips few ms and then it seems there is delay and its not good user experience. I tried to look up some solutions but couldnt find anything. Is there any way to solve it or its just because of phone (Iphone 7)?
sounds.js:
window.PlayAudio = (elementName) => {
document.getElementById(elementName).play();
}
*.razor:
<audio id="sound" src="#navManager.BaseUri/sound/correct.mp3" />
<button id="soundButton" #onclick="PlaySound">Click me to play "correct" sound</button>
...
public async Task PlaySound()
{
await JSRuntime.InvokeAsync<string>("PlayAudio", "sound");
}
Please see the repo and webapp:
https://github.com/Laftek/BlazorWASMPWAStaticPublish
https://laftek.github.io/BlazorWASMPWAStaticPublish/Lottie/7
Thank you any help would be much apprieciated.
This is not a Blazor issue-- it's a client browser behavior. It was once the standard across all devices, but I think newer devices have dropped some of the restrictions as audio files are no longer considered "large files."
Getting consistent media playback across all platforms has always been EXTREMELY difficult-- especially apple products, and most especially older apple products. I suspect that the sound will not start loading at all until a user action (like a button click). Then, unless you have fast data transfer, it will take some time to buffer. This was a common security feature for mobile devices to prevent sites from wasting users' data with media they didn't want.
The solution is to catch a click early on in the site's progress-- usually with a loading screen and "click here to enter." Then IN THAT CLICK HANDLER (important) you pre-start all your audio files by playing them and immediately pausing them again. Now, they will (probably) load, and when you really want to play the file, it will be loaded and ready.
Note that a simulated click will not work, so you can't just do element.onclick() after the page renders. It has to be an actual user interaction.
Javascript audio elements have various events, like canplay which let you know when enough of an audio source is loaded that you can start playing it.
https://www.w3schools.com/tags/ref_av_dom.asp
If you're lucky, Blazor has exposed the oncanplay event. Then you can use that even to enable your plaback button. I don't know if they have got around to audio events yet or not.
When playing a live audio stream, like web radio, through <audio> or Audio(), the pause event can fire in (at least) three ways:
user clicks on the pause button (with <audio controls>)
user clicks the browsers global audio controls
iOS: Control Center
Android: browser's notification drawer (at least Chrome, Opera, Firefox)
Desktop: Media Session API controls, but uninitialized, without explicit setActionHandler (might be hidden behind a flag as of now)
a buffer underrun caused by various network conditions
Is it possible to distinguish between 1/2 and 3?
Ideally, there would be an event property like isTrusted, which I am missing
I have tried to guess, looking esp. at readyState and networkState, but both are very inconclusive, especially across browsers (e.g. the interpretation/semantics of HAVE_FUTURE_DATA vs HAVE_ENOUGH_DATA)
I have shied away from making a "decaying state machine", juggling other events. A buffer underrun is often preceded by stalled events, and sometimes followed by ended events. A cross-browser implementation seems crazy complex and the danger of false positives very high.
Am I out of luck until Media Session lands everywhere?
Note: this question looks like a solution, but unfortunately isn't -- browsers handle live streams' "ends" differently and inconsistently.
The waiting event should fit your needs.
You can try this demo while you simulate bad network with the dropdown in Chrome's Network tab (e.g: Slow 3G)
const video = document.getElementById('mwe_player_0');
video.onwaiting = function() { console.log('onwaiting'); };
<video id="mwe_player_0" controls="" preload="none" style="width:800px;height:450px"><source src="https://upload.wikimedia.org/wikipedia/commons/2/22/Volcano_Lava_Sample.webm" type="video/webm; codecs="vp8, vorbis""></video>
Note that this demo works with HTMLAudioElement as well (because it inherits HTMLMediaElement). The video demo is just easier to test.
If you want to start an event when the user pauses the audio then this snippet will do the job. I didn't test it on mobile in the notification drawer but I think it'll work.
const video = document.querySelector('video');
video.addEventListener('pause', (event) => {
console.log('The Boolean paused property is now true. Either the ' +
'pause() method was called or the autoplay attribute was toggled.');
});
resource: audio element events
resource: pause event
I also found a helpful answer to what you are trying to do 2 (at least from what I understand) and why it's a bad technique. Link to question
3 Events: stalled / waiting check the events resource
With the release of OSX High-Sierra*, one of the new features in Safari is that videos on websites will not auto play anymore and scripts can't start it either, just like on iOS. As a user, I like the feature, but as a developer it puts a problem before me: I have an in-browser HTML5 game that contains video. The videos do not get automatically played anymore unless the user changes their settings. This messes up the game flow.
My question is, can I somehow use the players' interaction with the game as a trigger for the video to start playing automatically, even if said activity is not directly linked to the video element?
I cannot use jQuery or other frameworks, because of a restraint that my employer has put on our development. The one exception is pixi.js which - among all other animations - we are also using to play our videos inside a pixi container.
*The same restriction also applies on Mobile Chrome.
Yes, you can bind on event that are not directly ones triggered on the video element:
btn.onclick = e => vid.play();
<button id="btn">play</button><br>
<video id="vid" src="https://dl.dropboxusercontent.com/s/bch2j17v6ny4ako/movie720p.mp4"></video>
So you can replace this button with any other splash screen requesting an user click, and you'll be granted access to play the video.
But to keep this ability, you must call at least once the video's play method inside the event handler itself.
Not working:
btn.onclick = e => {
// won't work, we're not in the event handler anymore
setTimeout(()=> vid.play().catch(console.error), 5000);
}
<button id="btn">play</button><br>
<video id="vid" src="https://dl.dropboxusercontent.com/s/bch2j17v6ny4ako/movie720p.mp4"></video>
Proper fix:
btn.onclick = e => {
vid.play().then(()=>vid.pause()); // grants full access to the video
setTimeout(()=> vid.play().catch(console.error), 5000);
}
<button id="btn">play</button><br>
<video id="vid" src="https://dl.dropboxusercontent.com/s/bch2j17v6ny4ako/movie720p.mp4"></video>
Ps: here is the list of trusted events as defined by the specs, I'm not sure if Safari limits itself to these, nor if it includes all of these.
Important note regarding Chrome and preparing multiple MediaElements
Chrome has a long-standing bug caused by the maximum simultaneous requests per host which does affect MediaElement playing in the page, limiting their number to 6.
This means that you can not use the method above to prepare more than 6 different MediaElements in your page.
At least two workarounds exist though:
It seems that once a MediaElement has been marked as user-approved, it will keep this state, even though you change its src. So you could prepare a maximum of MediaElements and then change their src when needed.
The Web Audio API, while also concerned by this user-gesture requirement can play any number of audio sources once allowed. So, thanks to the decodeAudioData() method, one could load all their audio resources as AudioBuffers, and even audio resources from videos medias, which images stream could just be displayed in a muted <video> element in parallel of the AudioBuffer.
In my case i was combining transparent video (with audio) with GSAP animation. The solution from Kaiido works perfectly!
First, on user interaction, start and pause the video:
videoPlayer.play().then(() => videoPlayer.pause());
After that you can play it whenever you want. Like this:
const tl = gsap.timeline();
tl.from('.element', {scale: 0, duration: 5);
tl.add(() => videoPlayer.play());
Video will play after the scale animation :).
Tested in Chrome, Safari on iPhone
We use JWPlayer for video playback, with only a single player embedded in any page, and suddenly it has stopped executing callbacks, though it does not throw errors when the listeners are registered (for onPause, onPlay, and onComplete).
Embedding continues to work fine. The player responds to user interactions to pause, play etc. However, when these occur no callbacks are triggered.
As part of troubleshooting, I have replaced the callbacks with very simple troubleshooting functions, e.g.
jwplayer().onPause( function(event) { alert('pause!'); return true; } );
In the JavaScript console for both IE and Chrome, no JavaScript errors are shown. Troubleshooting statements (alerts) around every JWPlayer JavaScript call show that no errors are thrown. Wrapping all calls to JWPlayer in try/catch blocks and alerting any created error messages does nothing.
In the JavaScript console after page load, executing control methods such as jwplayer().play() exhibits the following behavior: does NOT change the player's actual playback state, but DOES trigger callbacks.
The JWPlayer version is "5.9.2156 (Licensed version)".
EDIT: Another clue: In the JavaScript console, even when the player is paused, alerting jwplayer().getState() shows "PLAYING".
EDIT: In our testing, disabling HTML5 mode prevents this bug from appearing, but is not optimal.
In the end there was no fix for this bug, but we worked around it by having the player default to flash mode first, then fall back to HTML 5 mode second.
The implication of the bug is that for those users who do not have flash support enabled in their browser, our pre-existing integration with Google Analytics will not work, but this should be a minority of users.
We considered upgrading to JWPlayer 6 but that is not an option at this time, due to a known issue with extra play events being reported.
I have an embedded Windows Media Player object in my website and I'd like to make it skip ahead to a certain point when the page loads. Most sites suggest using:
mediaplayerID.controls.currentPosition=xxx
That works great in Chrome but IE throws up a JavaScript error saying that mediaplayerID is undefined. I assume the issue is that the script is running before the media player object is fully loaded -- after the video starts playing, I can run set the currentPosition in the Developer Tools console and it skips ahead like it should. Is there a way in JavaScript to detect when (or wait until) media player is loaded and then change position?