I'm working on a custom player built around a JWPlayer. When clicking the "Play" button, I first fetch the content asynchronously, and once the content is fetched, i call jwplayer.setup(config), followed by jwplayer.play().
Listening to the "playAttemptFailed" event, i get the following error: "303210 - The play attempt was interrupted for unknown reasons".
If i set 'mute' as 'true' in the jwplayer.setup(), then everything works, which makes me believe it is related to the autoplay policies.
Is there any way to remedy this? It should not be treated as autoplay, since the user interaction (play button click) did indeed happen, right?
This only happens on iOS.
I had the same issue on IOS device, so I fixed calling the jwplayer when the site is loaded and call play function on click event:
var player = null;
var playerButton = document.getElementById('buttonPlayer');
function initializePlayer() {
player = jwplayer.setup({ file: '<STREAMING_URL>'});
}
function playButtonFunction() {
player.play(true);
}
playerButton.addEventListener('click', function() {
playButtonFunction();
});
window.addEventListener('load' , function() {
initializePlayer();
});
Related
Problem: Safari will throw exceptions when certain actions are performed to audio elements inside of a callback.
This includes:
Playback
Setting the src property
Playing a URL object that was recorded/generated by the user
I need to record audio and visualize the data in one step. When a button is clicked, the recorded audio should be played back.
My application works like this: The user presses a record button, and the application begins recording audio once the user gives permission for the app to record. When the user presses the stop button, the recording is stopped and then two asynchronous operations happen:
Recording Phase
I. The user stops the recording by pressing a stop button in the UI.
This invokes the stop method on the recording controller class.
II.
Two asynchronous methods fire here: 1. The audio is decoded into an
object URL and set to the src of an HTMLAudioElement that is not
loaded into the DOM. 2. The audio is decoded into a buffer then
visualized on the screen.
//RecordingController
public stopRecording(): Promise
{
return new Promise( (resovle,reject)=>
{
if (this._isRecording)
{
this._isRecording=false;
this._mediaRecorder.stop();
this._mediaRecorder.onstop=()=>{
const blob = new Blob(this._recordBuffer, { 'type' : 'audio/ogg; codecs=opus' });
this._recordBuffer=[];
this.emitter.emit("recording-stopped",blob);
//another view subscribes to recording-stopped and visualizes the data
resovle(blob);
};
}
else {
reject();
}
});
}
//Vue UI - invoked when pressing stop button
protected stopRecording()
{
this.recording=false;
const stopRecording=()=>{
this.controller.stopRecording().then(blob=>{
console.log("RECORDING STOPPED!")
const url = URL.createObjectURL(blob);
if (this._data.item)
{
this._data.item.src=url;
}
this._data.hidePlay=false;
this.emitter.emit("recording-stopped");
});
};
if (!this.isIos)
{
setTimeout(stopRecording,500);
}
else {
stopRecording();
}
}
Everything works perfectly in Firefox and Chrome on Android and Windows. Also works perfectly on Chromium edge. I've devised an unacceptable workaround on Safari iOS that works, partially. When the user presses the play button, it decodes the object URL into a data URL, performs the visualization and then sets the SRC to the data url instead of the object url. Play is also invoked when the data URL is returned, but it fails because it's done in an asynch method. When play is pressed a 2nd time, it plays but is not audible.
Giving an onscreen in-DOM HTMLAudioElement the data URL will play back the audio data and you can hear it. The unacceptable workaround is to use the default HTMLAudioElement.
I know that there is a limitation in iOS Safari where the audio is not playing until user triggers an interaction. So I have placed the code inside a touchstart event. But unfortunately, I have tried almost every combination, and I couldn't get it to play on iOS Safari.
Here are the things I have tried:
putting the audio load outside the touchstart callback
try adding a gain node
use 0.01 as the start time
and none of the above works in iOS Safari, but they can all play in desktop Chrome and Safari. Here is the link to the gist, you can see the versions where I made the changes (P.S. the click event is used for testing on desktop)
https://gist.github.com/angelathewebdev/32e0fbd817410db5dea1
Sounds play only when currentTime starts to run, but scheduling sounds exactly at currentTime doesn't seem to work. They need to be a little bit into the future (ex: 10ms). You can use the following createAudioContext function to wait until the context is ready to make noise. User action doesn't seem to be required on iPhone, but no such success on iPad just yet.
function createAudioContext(callback, errback) {
var ac = new webkitAudioContext();
ac.createGainNode(); // .. and discard it. This gets
// the clock running at some point.
var count = 0;
function wait() {
if (ac.currentTime === 0) {
// Not ready yet.
++count;
if (count > 600) {
errback('timeout');
} else {
setTimeout(wait, 100);
}
} else {
// Ready. Pass on the valid audio context.
callback(ac);
}
}
wait();
}
Subsequently, when playing a note, don't call .noteOn(ac.currentTime), but do .noteOn(ac.currentTime + 0.01) instead.
I've scoured the web, upgraded the player, rewritten it 5 times, and now completing my 5th day of failing, and still cannot accomplish what the folks at Longtail tell me will work. (Don't get me wrong, I love 'em there, but this has me ready to jump off a bridge).
I'm simply trying to load a video that will play with Flash or iOS, and upon loading it, immediately go to a specific point in the video useing the .seek() method. Longtail tells me to use the onBeforePlay() function because iOS apparently doesn't respect the start value of the playlist. This code works like smoke with Flash, but ignores the seek in iOS.
Can ANYone assist me with this - it has become the most expensive script I've ever worked on and I have made zero progress at all. :( :( :( Also, I removed all the console functions and tried that, but with the same result.
Full code/player can be seen at http://www.tempurl.us/jw6e.html. You can see that with Flash, the video starts at 60 seconds, but on iOS, it starts at 0.
jwp = jwplayer('jwp').setup({
title: 'Single File Player', width: '720', height:'240', autostart: 'false', listbar: {position: "right",size: 400},
sources:[
{ file: 'http://media3.scctv.net/insight/mp4:nursing_4_clips_400.mp4/playlist.m3u8'},
{ file: 'rtmp://fms.scctv.net/insight/nursing_4_clips_400.mp4'}
]
}
);
jwp.onReady(function() {
// Create a playlist item of the video to play
var newItem = [
{ title: 'Title4 ACUTE_ABDO_PAIN_400',
image: 'playlistitem.png',
sources:[
{ file: 'http://media3.scctv.net/insight/mp4:ACUTE_ABDO_PAIN_400.mp4/playlist.m3u8'},
{ file: 'rtmp://fms.scctv.net/insight/ACUTE_ABDO_PAIN_400.mp4'}
]
}
];
jwp.load(newItem);
});
jwp.onBeforePlay(function() {
// This Works on PC/Mac with Flash, but does nothing on iPad/iPhone
jwp.seek(60);
});
Simply to close the question, the bottom line on this problem was that iOS will not allow autostart - period. Knowing that, all the expected events that were not behaving as expected made sense. Once the user initiates the stream with Play, everything works as expected. In our case, this is still a problem because we want to start later in the stream, but knowing that made dealing with it more manageable.
If the problem is iOS will not allow autostart - period. Knowing that,
all the expected events that were not behaving as expected made sense.
Once the user initiates the stream with Play, everything works as
expected
then you can have a play button only for tablet and ios device and on Clicking the play button,
call jwplayer().play(), this could be a work around for your problem, and after you have invoked jwplayer.play, which is only possible with the touch event, after play is triggeredother events will work.
otherwise even if you try jwplayer().play() onReady(), or autostart nothing will work because of iOs will not allow autostart as you said
I've solved this problem on iOS using onBeforePlay with seek() and play(). This work on desktop flash and IOS. Doesn't work on Android using the parameter androidhls:true
jwplayer().onBeforePlay(function() { jwplayer().seek(60); });
jwplayer().play();
As Ethan JWPlayer mentioned in comment use onPlay event. To prevent "loop buffering" as you said just use flag variable:
var isFirstStart = true,
seekValue = 60;
jwplayer().onPlay(function(){
//exit if it's no first playback start
if( !isFirstStart ) {
return;
}
jwplayer().seek(seekValue);
isFirstStart = false;
});
I'm trying to to write a javascript app that use the [SoundManager 2][1] api and aim to run in
all desktop and mobile browsers. On the iPad platform, Soundmanager is using the HTML5 audio api since there is on flash support. Now, when I'm trying to play two audio files back to back, both loaded in response to a click event, a [HTML5::stalled][2] event is occasionally raised. How do I set an event handler to catch the stalled event?
Since sound objects in my app are created on the fly and I don't know how to access directly to tags that are created by SoundManager, I tried to use a delegate to handle the stalled event:
document.delegate('audio', 'stalled', function (event) {...});
It doesn't work. the event did not raised in respond to stalled. (I had an alert in my handler).
Also tried to use [Sound::onsuspend()][3] to listen for stalled, but onsuspend pops out
on the end of sound::play(). How can we distinguish between stalled and other events that may raise the audio::suspend? Is there any other way to access the tags that SoundManager must create in order to play HTML audio?
I solved it with the following solution. This is not documented and found by reverse engineering.
It is all about accessing the html audio object, which is availalbe under _a.
currentSound = soundManager.createSound({..});
currentSound._a.addEventListener('stalled', function() {
if (!self.currentSound) return;
var audio = this;
audio.load();
audio.play();
});
The body of the method is based on this post about html5 stalled callback in safari
I can suggest a different "fix" I use with an html5 using platform (samsung smart TV):
var mySound = soundManager.createSound({..});
mySound.load();
setTimeout(function() {
if (mySound.readyState == 1) {
// this object is probably stalled
}
}, 1500);
This works since in html5, unlike flash, the 'readystate' property jumps from '0' to '3' almost instantanously, skipping '1'. ('cause if the track started buffering it's playable...).
Hope this works for you as well.
I want to detect when audio has loaded on iPad. My code loads, and plays the mp3, but the event listener never fires.
$mp3.load()
$mp3.addEventListener("load", function() {
alert('Happy days') // <~~ this never fires
}, true)
$mp3.play()
I am using iOS 4.2. I am aware that all of this might not work on the latest iOS, and I don't mind that.
You need to add an event listener for canplaythrough, e.g.
addEventListener("canplaythrough", this.onLoad.bind(this), false);
Then once it triggers, remove it so you wont get it again:
onLoad:function ()
{
arguments.callee.removeEventListener("canplaythrough", this.onLoad.bind(this), false);
// do something
}