I'm working on an audio player using the HTML5 <audio> element and am trying to iron out a few issues cross-browser.
This player is configured to switch between audio tracks when a link for a different track is clicked. Internally, it pauses the <audio> element, sets its currentTime to 0 (as explained in HTML5 audio not playing multiple times in Android 4.0.4 device Native Browser) and removes it from the DOM before a new <audio> element is created with the new source track specified. The play() method is called on the element to load the track and start playing.
On desktop browsers, an iPhone and iPad, this works fine. It also works on a colleague's Samsung Galaxy S3 Mini. However on a different test phone, a full-size Samsung Galaxy S3, the playback of subsequent tracks fails - the track won't even play and it shows in the playback bar as having completed. This is also the case on the stock Android 4.1.2 browser on my Motorola Xoom tablet.
The first track specified on page load always works fine.
After adding a few debugging statements among the JavaScript code, particularly the timeupdate callback function, I have found that in the cases where playback fails, the <audio> element's duration has a value of 1 in the two instances when timeupdate is raised. Normally, the duration is reported as anything from 350 to just over 1000 for a 3-4 min track. This explains why the playback bar immediately showed the newly-loaded track as having completed playback.
I'm thinking that the subsequent track isn't being loaded properly; even if it has been previously loaded and cached on the phone, surely it must be able to be loaded correctly. All tracks are MP3s and are able to be played in the respective browsers.
What am I missing here? Any idea what the problem is and how to get this to work?
Many thanks.
More Info:
I've hooked up other events to the <audio> element to trace what may be happening:
progress (with a report on the buffered ranges)
loadedmetadata
canplay
error
On desktop (where this works correctly), the events are as follows:
timeupdate (with NaN duration), progress x3 (0 buffered ranges), loadedmetadata, canplay, timeupdate with duration of 121.172368, progress with 1 buffered range [0, 11.507715], followed by a sequence of timeupdate and progress events as the track continues to play. The iPhone has a similar sequence of events.
On the Samsung S3, I get the following events: timeupdate with a duration of 1, followed by progress (0 buffered ranges), loadedmetadata, canplay, and finally a timeupdate with duration still at 1.
This page has been very useful in explaining the events and data structures: http://www.sitepoint.com/essential-audio-and-video-events-for-html5/
Well I finally solved it:
Firstly, I updated the audio implementation to reuse the same <audio> element when switching tracks, making sure that I paused the audio before changing the src, etc. This may or may not have had any positive effect, though it was a way to guarantee that we didn't have too many <audio> elements potentially floating around in memory, especially if some unknown phone can only handle one instance at a time.
Before switching tracks, I added a guard clause to stop any currently-playing audio and ensure that neither pause() nor setting currentTime caused an error if no audio had been loaded:
if (audioPlayerElement.readyState > 0)
{
audioPlayerElement.pause();
audioPlayerElement.currentTime = 0;
}
Since subsequent tracks are auto-played, the ordering of statements needed fixing.
The misreporting of the track duration was a symptom of a larger issue in the way the <audio> element was being used. Previously, I had initialised the audio player as follows:
Create the <audio> element / set the src to the new URL of the MP3 track
Call load() on the <audio> element
Bind the timeupdate and ended events
Since this was an auto-play, added the autoplay attribute to the <audio> element
Since this was an auto-play, called play() on the <audio> element
(The binding of loadedmetadata, canplay, progress events for debug tracing was performed between steps 1 and 2 above.)
The problems that I found with this sequence were:
Calling play() is redundant if the autoplay attribute is set; and
Adding the autoplay attribute should happen before load() is called, since load() automatically starts playing the track once enough data has been buffered.
Thus, the final steps that work are:
Set the src attribute of the <audio> element to the new URL of the MP3 track
Bind the timeupdate and ended events
Since this is an auto-play, add the autoplay attribute to the <audio> element
Call load() on the <audio> element
The play() function only applies when clicking the play/pause button or starting playback from a particular position on the track bar.
Finally, it's worth noting that on iOS devices and some Android devices (depending on browser), auto-play is ignored if the player is initialised to start playing on page load. On such devices, auto-play will only work if the execution can be traced back to a click event (or similar). Bear this in mind when updating visuals to indicate 'playing' state - the canplay event handler seems a good place to do this.
I'm going to fully answer your question because it is useful for me as well - if you have a better solution please let me know :)
I'm going to be keeping a hash of all the metadata I'll need for each sound. This hash is probably going to be just a json response from the server - eg:
app.sounds.metadata = {
"enter the sandman": {
"artist": "Metaliica",
"album": "Metallica.",
"description": "Lorem ipsum dolor sit amet."
"genre": "metal",
"duration": 19920,
"playcount": 312
},
"piano man": {
"artist": "Billy Joel",
"album": "You're my home",
"description": "Lorem ipsum dolor sit amet."
"genre": "Soft rock",
"duration": 16200,
"playcount": 135,
}
}
And my audio elements will be declared such that:
<audio data-sound="piano man">
<source src="piano_man.ogg" type="audio/ogg">
<source src="piano_man.mp3" type="audio/mpeg">
</audio>
Now when I'm using my sound I can just use:
var html5offset = 1000
var meta = app.sounds.metadata[sound.getAttribute("data-sound")];
var soundduration = meta.duration || sound.duration*html5offset;
Related
I have 2 vides hosted on a server.
In Chromium browser (Version 65.0.3325.181) https://server/index.html is loaded.
Using Javascript, 2 video elements are inserted after window.onload and the 1st one is started.
Both inserted like:
let video = document.createElement('video');
video.setAttribute('width', '100%');
video.setAttribute('height', '100%');
video.setAttribute('preload', 'auto');
let source = document.createElement('source');
source.setAttribute('src', <http://server/video/path>);
source.setAttribute('type', 'video/mp4');
video.appendChild(source);
After finish video.on('ended') video element becomes hidden. And next video element becomes visible and started.
Everything works fine:
2 video elements created and hidden.
Immediately 1st video element started and playing ..
On 1st video ended - it becomes hidden
Second video becomes visible and started ... successfully reach its end and becomes hidden again...
then start again from 1st video...
I have following observation.
First video is always playing smootly after initially cached.
Second video is always playing with glitches and freezes of few milliseconds.
I am using Chromium on Raspberry Pi 3.
When first video starts: CPU is like 40-50-60%
Everytime the second video starts: CPU is like 120-220% and with glitches and freezes.
The issue is not video or encoding related, because when i switch places: the new 2th video (which was 1st before) becomes the problematic one. Always the second video!
Why this can be?
Can you give me some clue or direction what it might be?
One possible reason is that the first video is using the Raspberry PI's built in Broadcom HW video decoding, but the second one is having to fall back to SW decoding.
The same affect can be seen on many computers if you play multiple videos and exceed what decoding the platform can handle in HW - a typical approach is to fall back on some form of SW decoding, or perhaps to simply give priority to the first video(s) playing.
There are also restrictions to the codec and frame rates supported on most video cards, although this does not sound like the issue you are seeing as the video sounds like it plays fine when it is the only or the first video.
I have got an issue using alert() in JavaScript. When message appears, tag stops playing .mp3 file. Is it JavaScript bug or I'm doing something wrong?
alert has nothing to do with your audio stopping. The audio are not running on the same JS thread with your code. Take a look at this demo and the HTML5 specification:
When an audio element is potentially playing, it must have its audio data played synchronised with the current playback position, at the element's effective media volume.
By saying "potentially playing", it means the following:
A media element is said to be potentially playing when its paused attribute is false, the element has not ended playback, playback has not stopped due to errors, the element either has no current media controller or has a current media controller but is not blocked on its media controller, and the element is not a blocked media element.
alert doesn't fit into anywhere in the specification so it should not affect your audio.
So your audio might be stopped by many reasons. It might be that your audio file is broken, or perhaps some of your code isn't functioning as you expected (did you check to see if there is any .pause() method calling from your code?). Without seeing any of your code it is hard to diagnose the problem, but it is sure that alert isn't the one causing the issue.
So I have this audio tag:
<audio src=".....mp3" autoplay id="aud"></audio>
And 20 seconds later I fire this code:
var obj=$('#aud')
obj[0].volume=0;
obj[0].pause();
obj.prop('volume',0);
obj.trigger('pause');
obj.attr('src','');
obj.remove();
console.log('REMOVED!!');
But after all of this, the audio is still playing??
The audio tag has been successfully removed by obj.remove(), but the audio goes on.
The console.log() logs correctly. I get no errors. But despite using several methods to mute, pause and remove the audio tag, the audio goes on.
Can anyone explain why?
I need to purge this audio with salt and flame. Any help will be most appreciated, this is slowly sending me insane...
So I fiddled with this when writing this as an answer and I conclude the following:
Manually creating the audio tag with autoplay property causes it to play even before being added to the DOM tree 2 times(on older jquery implementations). Even after adding that element to the DOM tree, it will be usually impossible to stop both audio streams - prolly one of the streams gets disconnected from the element when second stream is played. Did not dig into that too deeply. I reproduced the issue here:
http://jsfiddle.net/2gaBs/3/
When you click Create w/o autoplay button you can pause it properly, but when you click Create autoplay it will start immedietally (even before adding to the DOM tree) 2 times! You probably didn't notice the 2nd playback(neither did I) because when mp3 is on the same machine 2 audio streams are almost perfectly in sync. Pressing Try stop will stop one of the audio streams (you need to wait 5 seconds, note the setTimeout).
Also note that if you switch to jQuery ver to 1.8.3 or above this issue no longer occurs, so it seems that both of us were working on old jQuery libs that day^^
So 2 solutions are: update jquery version to 1.8.3 or above, or create the element without autoplay property and start it later.
This is how the sound is "stored":
<audio id = "hammer" src="res/sounds/Building/hammer.wav"></audio>
In the actual game (which I use sounds for), I use this to play the sound:
function playSound(soundid)
{
document.getElementById(soundid).currentTime = 0;
document.getElementById(soundid).play();
}
But the sound plays only the first time, and never again!
I tried resetting the "currentTime" in the console to 0, but I literally get this:
>document.getElementById("hammer").currentTime = 0
0
>document.getElementById("hammer").currentTime
0.340...
Why is this happening, how do I fix it?
See this slide and the preceding three slides of Evan Wallace and Justin Ardini's presentation on html5 game dev.
For all the resources to make some awesome games, part of their talk: http://madebyevan.com/gamedevclass/
Audio still doesn't work consistently across all browsers, as of right now:
An element must be reloaded in Chrome or it will only play once
An element must not be reloaded in Firefox or there will be a delay
function playSoundEvent() {
if (window.chrome) elems[index].load()
elems[index].play()
index = (index + 1) % elems.length
}
I had this issue recently with an html5. Worked everywhere except safari.
Using load() before calling play() solved this problem. It also helps to make sure that sound effects do not overlap with heavy clickers when event-handlers trigger sounds.
Here what I used
<audio id="sound-one" preload="auto">
<source src="http://myurl/foo.mp3"></source>
<source src="http://myurl/foo.ogg"></source>
</audio>
click here
Jquery
$("#navigation-id") //this attached is to an element on the page
.mouseover(function() {
sound-one.load();
sound-one.play();
});
A few months before I faced the same issue while developing a Piano in HTML5. When a key was pressed again and again I had to stop, rewind and play the audio on each key press. I used the same code written in this question which worked in Firefox and Safari, but not in Chrome. In Chrome it didn't play again. Finally I had to modify the code to make it work. Added one listener for the event onseeked, then set currentTime = 0 and in the event handler invoked play. This worked for me. Similarly I had to wait for the event of one action before giving next action in many places. It was an old version of Chrome. Later I found out that even some versions of Browsers support Audio, the way each one supports is slightly different. This difference won't be visible if we simply put an audio tag and play the audio, but will experience when we try to control the audio using Javascript. Anyways its all about older versions of Browsers, its much much better in all latest versions. So please check in the latest version of Chrome ( Even my piano worked in Chrome 10 without the code modification ) and regarding the audio format, I would suggest you to keep mp3 and ogg files of your audio instead of single wav file.
For anyone stumbling across this post in the future, the audio tag is really not a great way to do game audio.
I'd highly recommend taking the time to learn the WebAudio API instead.
I am implementing sound effects in HTML5 audio but after a while, it just stops playing any audio. The file type is correct because it starts fine but then seems to give up.
var sound = new Audio(url);
function play() {
sound.play();
}
Is there a better way to do this so it consistently plays sound?
Here is a link to my implementation. Easy to reproduce by pressing spacebar a lot until it eventually gives up (also shoot the lights for added sounds). http://craftyjs.com/elevatoraction/
This occurs for me in the latest version of Chrome (8.0)
Edit: I did as Gaurav suggested and only played the same instance of each sound file, but the same sort of problems are present. It will arbitrarily stop playing.
Edit 2: I just noticed that whenever I try to play the sound, the networkState is always 1 which according to this means it hasn't fully loaded. That is odd seeing as it still plays sometimes and even when it plays the networkState is always 1
Don't create a new audio object each time you want to play a sound, reuse the same resource.
var sound1 = new Audio(url);
function playSound1() {
sound1.play();
}
I think this is related to the bug http://code.google.com/p/chromium/issues/detail?id=57070