With some codecs and containers, it's possible for a video to change resolution mid-stream. This is particularly common with RTC-style video streams where resolution can scale up/down based on available bandwidth. In other cases, the recording device might be rotated and the video may flip from portrait to landscape or vice versa.
When playing these videos on a web page (simple <video> tag), how can I detect when this change in size occurs with JavaScript?
The best I can think of is verifying the size of the video every frame, but there is quite a bit of overhead to this method. If there were a way to have a callback fired when the video changed sizes, or an event triggered, that'd be best.
Example video that resizes, severely: https://bugzilla.mozilla.org/attachment.cgi?id=8722238
There is now a resize event which fires when the video resolution changes.
HTML
<p data-content="resolution"></p>
<video src="https://bug1250345.bmoattachments.org/attachment.cgi?id=8722238"></video>
JavaScript
document.querySelector('video').addEventListener('resize', (e) => {
document.querySelector('[data-content="resolution"]').textContent = [
e.target.videoWidth,
e.target.videoHeight
].join('x');
});
(JSFiddle: http://jsfiddle.net/qz61o2xt/)
References:
HTML Living Standard Media Events - resize
Chromium Bug: Add a 'resize' event to elements for when the video data changes dimensions
Chromium Bug: Fire 'resize' event on initial metadata load, too.
look if this helps I'm not sure.
Link - https://www.w3.org/2010/05/video/mediaevents.html
You can use loadedmetadata event to define global variables or utilize Element.dataset to reflect initial .videoWidth, .videoHeight properties of <video> element; at timeupdate event of <video> initially stored and current event .videoWidth, .videoHeight values, if one of the properties changed call function
window.onload = function() {
function handleResolutionChange(event) {
// do stuff if `.videoWidth` or `.videoHeight` changed from initial value
}
var video = document.querySelector("video");
video.addEventListener("loadedmetadata", function(event) {
event.target.dataset.width = event.target.videoWidth;
event.target.dataset.height = event.target.videoHeight;
})
video.addEventListener("timeupdate", function(event) {
if (+event.target.dataset.width !== event.target.videoWidth
&& +event.target.dataset.height !== event.target.videoHeight) {
// call `handleResolutionChange` one or more times
// if `event.target` `.videoWidth` or `.videoHeight` changed
handleResolutionChange.call(event.target, event)
}
})
}
I am playing a small audio clip on click of each link in my navigation
HTML Code:
<audio tabindex="0" id="beep-one" controls preload="auto" >
<source src="audio/Output 1-2.mp3">
<source src="audio/Output 1-2.ogg">
</audio>
JS code:
$('#links a').click(function(e) {
e.preventDefault();
var beepOne = $("#beep-one")[0];
beepOne.play();
});
It's working fine so far.
Issue is when a sound clip is already running and i click on any link nothing happens.
I tried to stop the already playing sound on click of link, but there is no direct event for that in HTML5's Audio API
I tried following code but it's not working
$.each($('audio'), function () {
$(this).stop();
});
Any suggestions please?
Instead of stop() you could try with:
sound.pause();
sound.currentTime = 0;
This should have the desired effect.
first you have to set an id for your audio element
in your js :
var ply = document.getElementById('player');
var oldSrc = ply.src;// just to remember the old source
ply.src = "";// to stop the player you have to replace the source with nothing
I was having same issue. A stop should stop the stream and onplay go to live if it is a radio. All solutions I saw had a disadvantage:
player.currentTime = 0 keeps downloading the stream.
player.src = '' raise error event
My solution:
var player = document.getElementById('radio');
player.pause();
player.src = player.src;
And the HTML
<audio src="http://radio-stream" id="radio" class="hidden" preload="none"></audio>
Here is my way of doing stop() method:
Somewhere in code:
audioCh1: document.createElement("audio");
and then in stop():
this.audioCh1.pause()
this.audioCh1.src = 'data:audio/wav;base64,UklGRiQAAABXQVZFZm10IBAAAAABAAEAVFYAAFRWAAABAAgAZGF0YQAAAAA=';
In this way we don`t produce additional request, the old one is cancelled and our audio element is in clean state (tested in Chrome and FF) :>
This method works:
audio.pause();
audio.currentTime = 0;
But if you don't want to have to write these two lines of code every time you stop an audio you could do one of two things. The second I think is the more appropriate one and I'm not sure why the "gods of javascript standards" have not made this standard.
First method: create a function and pass the audio
function stopAudio(audio) {
audio.pause();
audio.currentTime = 0;
}
//then using it:
stopAudio(audio);
Second method (favoured): extend the Audio class:
Audio.prototype.stop = function() {
this.pause();
this.currentTime = 0;
};
I have this in a javascript file I called "AudioPlus.js" which I include in my html before any script that will be dealing with audio.
Then you can call the stop function on audio objects:
audio.stop();
FINALLY CHROME ISSUE WITH "canplaythrough":
I have not tested this in all browsers but this is a problem I came across in Chrome. If you try to set currentTime on an audio that has a "canplaythrough" event listener attached to it then you will trigger that event again which can lead to undesirable results.
So the solution, similar to all cases when you have attached an event listener that you really want to make sure it is not triggered again, is to remove the event listener after the first call. Something like this:
//note using jquery to attach the event. You can use plain javascript as well of course.
$(audio).on("canplaythrough", function() {
$(this).off("canplaythrough");
// rest of the code ...
});
BONUS:
Note that you can add even more custom methods to the Audio class (or any native javascript class for that matter).
For example if you wanted a "restart" method that restarted the audio it could look something like:
Audio.prototype.restart= function() {
this.pause();
this.currentTime = 0;
this.play();
};
It doesn't work sometimes in chrome,
sound.pause();
sound.currentTime = 0;
just change like that,
sound.currentTime = 0;
sound.pause();
From my own javascript function to toggle Play/Pause - since I'm handling a radio stream, I wanted it to clear the buffer so that the listener does not end up coming out of sync with the radio station.
function playStream() {
var player = document.getElementById('player');
(player.paused == true) ? toggle(0) : toggle(1);
}
function toggle(state) {
var player = document.getElementById('player');
var link = document.getElementById('radio-link');
var src = "http://192.81.248.91:8159/;";
switch(state) {
case 0:
player.src = src;
player.load();
player.play();
link.innerHTML = 'Pause';
player_state = 1;
break;
case 1:
player.pause();
player.currentTime = 0;
player.src = '';
link.innerHTML = 'Play';
player_state = 0;
break;
}
}
Turns out, just clearing the currentTime doesn't cut it under Chrome, needed to clear the source too and load it back in. Hope this helps.
As a side note and because I was recently using the stop method provided in the accepted answer, according to this link:
https://developer.mozilla.org/en-US/docs/Web/Guide/Events/Media_events
by setting currentTime manually one may fire the 'canplaythrough' event on the audio element. In the link it mentions Firefox, but I encountered this event firing after setting currentTime manually on Chrome. So if you have behavior attached to this event you might end up in an audio loop.
shamangeorge wrote:
by setting currentTime manually one may fire the 'canplaythrough' event on the audio element.
This is indeed what will happen, and pausing will also trigger the pause event, both of which make this technique unsuitable for use as a "stop" method. Moreover, setting the src as suggested by zaki will make the player try to load the current page's URL as a media file (and fail) if autoplay is enabled - setting src to null is not allowed; it will always be treated as a URL. Short of destroying the player object there seems to be no good way of providing a "stop" method, so I would suggest just dropping the dedicated stop button and providing pause and skip back buttons instead - a stop button wouldn't really add any functionality.
This approach is "brute force", but it works assuming using jQuery is "allowed". Surround your "player" <audio></audio> tags with a div (here with an id of "plHolder").
<div id="plHolder">
<audio controls id="player">
...
</audio>
<div>
Then this javascript should work:
function stopAudio() {
var savePlayer = $('#plHolder').html(); // Save player code
$('#player').remove(); // Remove player from DOM
$('#FlHolder').html(savePlayer); // Restore it
}
I was looking for something similar due to making an application that could be used to layer sounds with each other for focus. What I ended up doing was - when selecting a sound, create the audio element with Javascript:
const audio = document.createElement('audio') as HTMLAudioElement;
audio.src = getSoundURL(clickedTrackId);
audio.id = `${clickedTrackId}-audio`;
console.log(audio.id);
audio.volume = 20/100;
audio.load();
audio.play();
Then, append child to document to actually surface the audio element
document.body.appendChild(audio);
Finally, when unselecting audio, you can stop and remove the audio element altogether - this will also stop streaming.
const audio = document.getElementById(`${clickedTrackId}-audio`) as HTMLAudioElement;
audio.pause();
audio.remove();
If you have several audio players on your site and you like to pause all of them:
$('audio').each( function() {
$(this)[0].pause();
});
I believe it would be good to check if the audio is playing state and reset the currentTime property.
if (sound.currentTime !== 0 && (sound.currentTime > 0 && sound.currentTime < sound.duration) {
sound.currentTime = 0;
}
sound.play();
for me that code working fine. (IE10+)
var Wmp = document.getElementById("MediaPlayer");
Wmp.controls.stop();
<object classid="clsid:6BF52A52-394A-11D3-B153-00C04F79FAA6"
standby="Loading áudio..." style="width: 100%; height: 170px" id="MediaPlayer">...
Hope this help.
What I like to do is completely remove the control using Angular2 then it's reloaded when the next song has an audio path:
<audio id="audioplayer" *ngIf="song?.audio_path">
Then when I want to unload it in code I do this:
this.song = Object.assign({},this.song,{audio_path: null});
When the next song is assigned, the control gets completely recreated from scratch:
this.song = this.songOnDeck;
The simple way to get around this error is to catch the error.
audioElement.play() returns a promise, so the following code with a .catch() should suffice manage this issue:
function playSound(sound) {
sfx.pause();
sfx.currentTime = 0;
sfx.src = sound;
sfx.play().catch(e => e);
}
Note: You may want to replace the arrow function with an anonymous function for backward compatibility.
In IE 11 I used combined variant:
player.currentTime = 0;
player.pause();
player.currentTime = 0;
Only 2 times repeat prevents IE from continuing loading media stream after pause() and flooding a disk by that.
What's wrong with simply this?
audio.load()
As stated by the spec and on MDN, respectively:
Playback of any previously playing media resource for this element stops.
Calling load() aborts all ongoing operations involving this media element
I'm trying to use the souncloud javascript api to trigger an onplay and onpause call back of an oembeded widget. So basically I'm using SC.oembed to create/display the widget in a div, and then i thought to use SC.stream to control the soundmanager 2 options, but this doesn't seem possible.
All I want to do is call a function when someone presses play on the widget, and when someone presses pause.
What is the best way to achieve this result?
UPDATE
Here's a code snippet
//set up soundcloud gallery
SC.initialize({
client_id: 'MY_CLIENT_ID'
});
SC.get('/users/MY_USER/tracks', function(tracks) {
$('#tracks_holder').addClass('scrollY').empty()
for (var i = 0; i < tracks.length; i++) {
console.log(tracks[i].title);
var newTrack = $('<div class="scTrack"></div>')[0];
SC.oEmbed(tracks[i].permalink_url, {maxheight:"200px"}, newTrack);
$('#tracks_holder').append(newTrack);
}
});
Thanks!
There is a great example HERE. SC allows you to attach handlers to many events, as is in the docs HERE
(function(){
var widgetIframe = document.getElementById('sc-widget'),
widget = SC.Widget(widgetIframe);
widget.bind(SC.Widget.Events.READY, function() {
widget.bind(SC.Widget.Events.PLAY, function() {
// get information about currently playing sound
widget.getCurrentSound(function(currentSound) {
console.log('sound ' + currentSound.get('') + 'began to play');
});
});
// get current level of volume
widget.getVolume(function(volume) {
console.log('current volume value is ' + volume);
});
// set new volume level
widget.setVolume(50);
// get the value of the current position
});
}());
Audio related events:
SC.Widget.Events.LOAD_PROGRESS — fired periodically while the sound is loading.
SC.Widget.Events.PLAY_PROGRESS — fired periodically while the sound is playing.
SC.Widget.Events.PLAY — fired when the sound begins to play.
SC.Widget.Events.PAUSE — fired when the sound pauses.
SC.Widget.Events.FINISH — fired when the sound finishes.
SC.Widget.Events.SEEK — fired when the user seeks.
I can't seem to listen for onended on a video on iPad(Safari)...I want to remove a class that I was able to add when the play button was pressed, but I can't seem to track down when the video ends (works well every where else including iphone, just need it for iPad/Safari)
link here: http://www.artandseek.net/meyerson/tour/
code snippet here
$(".playBtn").click(function(){
var thisVideo = $(this).prevAll(".img-wrap").children(".togglePlay").get(0);
thisVideo.onended = function(e) {
$(this).fadeOut().parent(".img-wrap").removeClass("playing");
$(this).parent().next("h2").fadeIn();
classie.remove( thisVideo, 'tabletActive');
}; ...
I was able to find the answer here:
Bind Play/Pause/Ended functions to HTML5 video using jQuery
using this:$(thisVideo).bind('ended', function () {
console.log('working');
});
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);
}