Javascript HTML5 video event canplay not firing on Safari - javascript

I have a HTML page including Javascript which is intended to allow a video (actually just audio content in this case) to play using HTTP Live Streaming on any browser. In most cases it uses hls.js but, in the case of Apple products, I need to do things differently as Safari has native HLS support.
The full page is reproduced below but the important lines are these:
else if (video.canPlayType('application/vnd.apple.mpegurl')) {
video.src = 'music.m3u8';
video.addEventListener('canplay', startPlaying);
//document.addEventListener('DOMContentLoaded', startPlaying);
}
What should happen is that when the canplay event fires the startPlaying() function is called and this makes visible a button that the user can press to begin playing the video. However, on my friend's iPhone 8plus (iOS 11.3.1), this doesn't work: no button is ever visible. If, instead, I comment out the video.addEventListener() line and replace it with the document.addEventListener() line then it all works fine: the button is made visible and he can play the stream.
Can anyone spot what I'm doing wrong? Could be a rookey mistake as I'm not very experienced with this web/script stuff, gives me nose bleeds... I could, of course, leave it with the DOM load approach but it's not right and I'd rather be right.
<!DOCTYPE html PUBLIC "-//Netscape Comm. Corp.//DTD HTML//EN">
<html>
<script src="hls.js/dist/hls.js"></script>
<head><meta http-equiv="content-type" content="text/html; charset=UTF-8"></head>
<body>
<video id="video"></video>
<button id="play" hidden>Loading</button>
<script>
'use strict';
var video = document.getElementById('video');
var playButton = document.getElementById('play');
function startPlaying() {
// For mobile browsers the start of playing has to
// be performed by a user action otherwise it will
// be ignored
playButton.addEventListener('click', function() {
video.play();
video.muted = false;
video.volume = 1;
playButton.innerHTML = "Playing";
});
playButton.hidden = false;
playButton.innerHTML = "Ready to play";
}
if (Hls.isSupported()) {
var hls = new Hls();
hls.loadSource('music.m3u8');
hls.attachMedia(video);
hls.on(Hls.Events.MANIFEST_PARSED, startPlaying);
}
// hls.js is not supported on platforms that do not have Media Source Extensions (MSE) enabled.
// When the browser has built-in HLS support (check using `canPlayType`), we can provide an HLS manifest (i.e. .m3u8 URL) directly to the video element through the `src` property.
// This is using the built-in support of the plain video element, without using hls.js.
else if (video.canPlayType('application/vnd.apple.mpegurl')) {
video.src = 'music.m3u8';
video.addEventListener('canplay', startPlaying);
//document.addEventListener('DOMContentLoaded', startPlaying);
}
</script>
</body>
</html>

From the investigations in the other question, the workaround is to wait on the event loadedmetadata, so in my case video.addEventListener('loadedmetadata', startPlaying), as this is the last event you're going to get from the HTML5 video element on Safari unless you're in the user-controlled white list. Confirmed that this works on iOS 11.3.1.

Related

Displaying the local machine camera feed in a Blazor Web Assembly app

I've distilled my issue down to a boilerplate Blazor Web Assembly app.
The project is straight out of the wizard, with the below code added.
I've changed the Index.razor page to this:
#page "/"
#inject IJSRuntime JSRuntime;
#using System.Drawing;
#using System.IO;
<div>
<h1>Video Test</h1>
</div>
<video id="video" width="640" height="480" autoplay></video>
<div>
<button type="button" class="btn btn-primary" #onclick="StartVideo">Click Me</button>
</div>
#code {
async Task StartVideo()
{
await JSRuntime.InvokeVoidAsync("startVideo");
}
}
I have a JavaScript page attached like this:
function startVideo() {
alert("Test Alert!");
var video = document.getElementById('video');
if (navigator.mediaDevices && navigator.mediaDevices.getUserMedia) {
navigator.mediaDevices.getUserMedia({ video: true }).then(function (stream) {
video.src = window.URL.createObjectURL(stream);
video.srcObject = stream;
video.play();
});
}
}
The app compiles without issue. When I run it and click the button I get the alert. I added to alert to confirm the Java Script was actually being run.
The Chrome browser asks for permission to use my webcam, which I grant.
My webcam activates, (my computer has an indicator light to display when the cam is active).
However, nothing shows up on the page. I'm guessing its something straightforward with binding my camera stream to the tag. In my next iteration, I will be taking snapshots of the video feed. For the moment, I only want the feed displayed on the page.
Do I have to route the binding through the C# code block, or can I, as I have done here? Bind the Javascript directly to the HTML element?
I believe you are having problems in chromium based browsers because createObjectURL was deprecated. All you need for Chrome and the new MS Edge browser (that uses chromium) are the lines containing the srcObject and play. I tested the below code in Chrome, Firefox, and the new Edge.
function startVideo() {
alert("Test Alert!");
var video = document.getElementById('video');
if (navigator.mediaDevices && navigator.mediaDevices.getUserMedia) {
navigator.mediaDevices.getUserMedia({ video: true }).then(function (stream) {
try {
video.srcObject = stream;
} catch (error) {
video.src = window.URL.createObjectURL(stream);
}
video.play();
});
}
}
The problem was using Chrome.
It worked without issue in MS Edge.
I tried the same code on a plain HTML page, with no Blazor elements included and got the same result. It is being caused by a setting in my Chrome browser, or on my laptop system settings.
To answer the question. No, the binding doesn't have to go through the c# code. The javascript can directly connect to the HTML.

safari browser doesn’t support HTML5 audio tag in ipad/iphone,Android

I am working on a project based on jquery animation its animation works fine on desktop (Firefox,chrome,opera,IE) also support HTML 5 audio tag but in Ipad/iphone/ Android safari audio tag doesn’t support.Its works fine on Ipad/iphone/ Android firefox.i have searched it in many forum don’t get desire Result. I have used this function :
function playmusic(file1,file2)
{
document.getElementById('music11').innerHTML='<audio id="music1"><source src="'+file1+'" type="audio/ogg"><source src="'+file2+'" type="audio/mpeg"></audio>';
$("#music1").get(0).play();
}
I have called function like : playmusic(2.ogg','2.mp3');
If I give autoplay in audio tag it works but play method not working and I have to use play method as in my application needs sound in particular event see the link
http://solutions.hariomtech.com/jarmies/
I have also changed my function and give direct audio tag in div and call function the same problem I face as I mentioned above. I need sound play in background without any click.if I use auto play method so it play sound only one time but I need sound multiple time on event.
Try to add an autoplay attribute on the audio tag:
function playmusic(file1, file2) {
document.getElementById('music11').innerHTML='<audio autoplay id="music1"><source src="'+file1+'" type="audio/ogg"><source src="'+file2+'" type="audio/mpeg"></audio>';
}
I would however recommend building a proper element and insert that into the DOM - something like this:
function playmusic(file1, file2) {
var audio = document.createElement('audio');
audio.preload = 'auto';
audio.autoplay = true;
if (audio.canPlayType('audio/ogg')) {
audio.src = file1;
}
else if (audio.canPlayType('audio/mpg')) {
audio.src = file2;
}
document.getElementById('music11').appendChild(audio);
}

<audio> element not working at all in Safari

I'm trying to add music to a virtual tour application I'm writing in JS and jQuery and so far my code, shown below, works great in Chrome, FF, IE9, and Opera. But in Safari 5.1.7, which is the newest you can get for a Windows machine, it just doesn't work... Through my research of the problem, I know that Safari doesn't autoplay music and it has to be started manually, but when I click the play button, nothing happens. And, btw, the code inspector in Safari shows that my audio tag is there with the music sources so I don't think it has anything to do with the DOM. Any ideas on my problem?
HTML:
<div id="musicBtns">
<p>Music:</p>
<p id="musicPlayBtn">Play</p>
<p>/</p>
<p id="musicPauseBtn">Pause</p>
</div>
My music object in JS:
var Music =
{
musicBtns: $('#musicBtns'),
playBtn: $('#musicPlayBtn'),
pauseBtn: $('#musicPauseBtn'),
isPlaying: false,
init: function()
{
if(!music.includeMusic) // If the client didn't want any music in the tour app, remove the music btns from the DOM
{
this.musicBtns.remove();
}
else // Else, setup the audio element
{
var parent = this;
var audio = $('<audio loop="true">');
if(music.autoplay)
{
audio.attr('autoplay', 'autoplay');
this.isPlaying = true;
this.togglePlayPause();
}
// Add a source elements and appends them to the audio element
this.addSource(audio, music.ogg); // 'music.ogg' is a reference to the path in a JSON file
this.addSource(audio, music.mp3);
this.musicBtns.append(audio);
// Add event listeners for the play/pause btns
this.playBtn.click(function()
{
audio.trigger('play');
parent.isPlaying = true;
parent.togglePlayPause();
});
this.pauseBtn.click(function()
{
audio.trigger('pause');
parent.isPlaying = false;
parent.togglePlayPause();
});
}
},
addSource: function(el, path)
{
el.append($('<source>').attr('src', path));
},
// Add or remove classes depending on the state of play/pause
togglePlayPause: function()
{
if(this.isPlaying)
{
this.playBtn.addClass('underline');
this.pauseBtn.removeClass('underline');
}
else
{
this.playBtn.removeClass('underline');
this.pauseBtn.addClass('underline');
}
}
}
Edit: Could this be a bug in Safari?
So the problem turned out to be with QuickTime. I guess I must of deleted it off of my machine awhile back because I didn't think I needed it. After I re-installed QuickTime, Safari plays music using the audio tag with no problem. Autoplay even works too.
Kinda funny how the champion of native HTML5 audio/video support, doesn't support HTML5 audio/video without a plugin...
In HTML5 video on Safari I had an issue with this where I had to "load" (which, I think, either starts the buffer or loads the whole thing) before I set it to play. This seemed to actually make things work in a few webkit browsers. So something like:
var a = new Audio("file.mp3");
a.load();
a.play();
Worth a try

How to play a mp3 using Javascript? [duplicate]

This question already has answers here:
Play mp3 file using javascript
(3 answers)
Closed 10 years ago.
I have a directory on my website with several mp3's.
I dynamically create a list of them in the website using php.
I also have a drag and drop function associated to them and I can select a list of those mp3 to play.
Now, giving that list, how can I click on a button (Play) and make the website play the first mp3 of the list? (I also know where the music is on the website)
new Audio('<url>').play()
If you want a version that works for old browsers, I have made this library:
// source: https://stackoverflow.com/a/11331200/4298200
function Sound(source, volume, loop)
{
this.source = source;
this.volume = volume;
this.loop = loop;
var son;
this.son = son;
this.finish = false;
this.stop = function()
{
document.body.removeChild(this.son);
}
this.start = function()
{
if (this.finish) return false;
this.son = document.createElement("embed");
this.son.setAttribute("src", this.source);
this.son.setAttribute("hidden", "true");
this.son.setAttribute("volume", this.volume);
this.son.setAttribute("autostart", "true");
this.son.setAttribute("loop", this.loop);
document.body.appendChild(this.son);
}
this.remove = function()
{
document.body.removeChild(this.son);
this.finish = true;
}
this.init = function(volume, loop)
{
this.finish = false;
this.volume = volume;
this.loop = loop;
}
}
Documentation:
Sound takes three arguments. The source url of the sound, the volume (from 0 to 100), and the loop (true to loop, false not to loop).
stop allow to start after (contrary to remove).
init re-set the argument volume and loop.
Example:
var foo = new Sound("url", 100, true);
foo.start();
foo.stop();
foo.start();
foo.init(100, false);
foo.remove();
//Here you you cannot start foo any more
You will probably want to use the new HTML5 audio element to create an Audio object, load the mp3, and play it.
Due to browser inconsistencies, this sample code is a bit lengthly, but it should suit your needs with a bit of tweaking.
//Create the audio tag
var soundFile = document.createElement("audio");
soundFile.preload = "auto";
//Load the sound file (using a source element for expandability)
var src = document.createElement("source");
src.src = fileName + ".mp3";
soundFile.appendChild(src);
//Load the audio tag
//It auto plays as a fallback
soundFile.load();
soundFile.volume = 0.000000;
soundFile.play();
//Plays the sound
function play() {
//Set the current time for the audio file to the beginning
soundFile.currentTime = 0.01;
soundFile.volume = volume;
//Due to a bug in Firefox, the audio needs to be played after a delay
setTimeout(function(){soundFile.play();},1);
}
Edit:
To add Flash support, you would append an object element inside the audio tag.
You can use <audio> HTML5 tag to play audio using JavaScript.
But this is not cross-browser solution. It supported only in modern browsers. For cross-browser compatibility you probably need to use Flash for that (for example jPlayer).
Browsers compatibility table is provided at link I mentioned above.
You could try SoundManager 2: it will transparently handle the <audio> tag wherever it's supported, and use Flash wherever it isn't.
Assuming that the browser supports MP3 playback and is fairly new to support newer JavaScript features, I would suggest taking a look at jPlayer.
You can see a short demo tutorial on how to implement it.
Jquery plugin for audio mp3 player http://www.jplayer.org/0.2.1/demos/
Enjoy it ;)
<html>
<head>
<title>Play my music....</title>
</head>
<body>
<ul>
<li>
<a id="PlayLink" href="http://www.moderntalking.ru/real/music/Modern_Talking-You_Can_Win(DEMO).mp3" onclick="pplayMusic(this, 'music_select');">U Can Win</a>
</li>
<li>
<a id="A1" href="http://www.moderntalking.ru/real/music/Modern_Talking-Brother_Louie(DEMO).mp3" onclick="pplayMusic(this, 'music_select');">Brother Louie</a>
</li>
</ul>
<script type="text/javascript" src="http://mediaplayer.yahoo.com/js"></script>
</body>
</html>

Play (and replay) a sound on safari mobile

I need to play a sound when a new message appears on a website. It works fine on Chrome and Safari but I can't make it work on Safari mobile.
I saw that the sound has to be initialised with a user action so I tried that:
var sound = new Audio('./path/to/my/sound.mp3');
var hasPlayed = false;
$('body').bind('click touchstart', function() {
sound.load();
});
sound.addEventListener('play', function() {
hasPlayed = true;
});
var playSound = function() {
if(hasPlayed) {
sound.currentTime = 0;
}
sound.play();
}
Unfortunately, the sound still don't play. I also tried with the Buzz library, and the issue is the same.
So, the question is : how can I play a sound programmatically on mobile browsers ?
First of all: HTML5 audio support in Mobile Safari on iOS (5.01, 5.1) is rather limited. But I have managed to get some small 'event type' sounds working in my iPad 2 web apps. Since you are talking about only one sound file for your app, you don't have to fall back on audio sprites tricks (i.e. merging multiple MP3's into one MP3 file and changing the play position within the merged file depending on the sound you want to be played).
As you have noticed, you cannot play audio automatically in Mobile Safari, i.e. without the user clicking on some element. Technically speaking, the audio must be played (not loaded) in the same call stack as a click event. But you will probably experience a 0,5 second delay then, when Mobile Safari creates the audio object. Here is a solution to this 'problem':
At the start of your app (while loading/initializing), add a click handler to the HTML document that starts playing your audio file as soon as the user clicks/taps anywhere in the app. This will force Safari to start loading the audio.
Listen for the 'play' event that is triggered when the audio is ready to be played, and immediately pause.
Now start playing the audio (without delay) again when you need it.
Here is some quick JavaScript code:
function initAudio() {
var audio = new Audio('./path/to/my/sound.mp3');
audio.addEventListener('play', function () {
// When the audio is ready to play, immediately pause.
audio.pause();
audio.removeEventListener('play', arguments.callee, false);
}, false);
document.addEventListener('click', function () {
// Start playing audio when the user clicks anywhere on the page,
// to force Mobile Safari to load the audio.
document.removeEventListener('click', arguments.callee, false);
audio.play();
}, false);
}
For those that are coming across this problem and the solution by Jeroen is not working here is a solution that works and ensures the proper scoping is correctly enforced.
Make sure initAudio is called on page load. I.e. in your Init function or for jquery inside the document.ready ($(function(){});)
function initAudio(){
var audio = new Audio('./path/to/my/sound.mp3');
var self = this;
//not sure if you need this, but it's better to be safe
self.audio = audio;
var startAudio = function(){
self.audio.play();
document.removeEventListener("touchstart", self.startAudio, false);
}
self.startAudio = startAudio;
var pauseAudio = function(){
self.audio.pause();
self.audio.removeEventListener("play", self.pauseAudio, false);
}
self.pauseAudio = pauseAudio;
document.addEventListener("touchstart", self.startAudio, false);
self.audio.addEventListener("play", self.pauseAudio, false);
}

Categories