if(!!window.IntersectionObserver){
let target = document.querySelector('#testme');
let audio = document.querySelector('audio');
let isPaused = false; /* flag for auto-pausing of the audio */
let observer = new IntersectionObserver((entries, observer) => {
entries.forEach(entry => {
if(entry.intersectionRatio!=.5 && !audio.paused){
audio.pause(); isPaused = true;
}
else if(isPaused) {audio.play(); isPaused=false}
});
}, {threshold: .5});
observer.observe(target) ;
}
else document.querySelector('#warning').style.display = 'block';
<!-- some warnings, for demo purposes -->
<strong id="warning" hidden>IntersectionObserver is not supported by your browser, so you can't see the effect. Check its browser support here.</strong>
<noscript>⚠️ JAVASCRIPT IS DISABLED IN YOUR BROWSER!</noscript>
<div style="height:600px">
<h1>Click video just to trigger the ability to auto play audio on site, then as you scroll through the page, the respective audio should pause/play depending if it's in the view</h1>
<video src="http://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4" controls="" height="200px"></video>
</div>
<br>
<br>
<br>
<hr>
<!-- audio files -->
<div id="testme" style="height:600px; background-color:#CCC">
<audio controls loop src="https://truefa-znfmrrlhm9.live-website.com/wp-content/uploads/2022/12/random_music.mp3" height="10"></audio>
</div>
<hr>
<audio id="testme" controls loop src="https://truefa-znfmrrlhm9.live-website.com/wp-content/uploads/2022/12/random_muisc2.mp3"></audio>
I'm trying to play different audio depending where the user is on the page by leveraging intersection observer
However, the respective audio is not pausing and playing when the various divs are in view. Not sure what I'm missing here for the code to function accordingly...
if(!!window.IntersectionObserver){
let target = document.querySelector('#testme');
let audio = document.querySelector('audio');
let isPaused = false; /* flag for auto-pausing of the audio */
let observer = new IntersectionObserver((entries, observer) => {
entries.forEach(entry => {
if(entry.intersectionRatio!=.5 && !audio.paused){
audio.pause(); isPaused = true;
}
else if(isPaused) {audio.play(); isPaused=false}
});
}, {threshold: .5});
observer.observe(target) ;
}
else document.querySelector('#warning').style.display = 'block';
https://codepen.io/truefa/pen/PoBWZLV
Related
I am facing a problem with video playback when mousemove function is applied. the function is working but when the video is played it has jerks. I want it to be smooth with an easing function maybe. Please guide me on how to play video without hanging up in between.
const startDrawing = () => {
const video = document.querySelector("video");
let currentFrame = 0
let mediaTime;
let paintCount = 0;
let startTime = 0.0;
let fps = 60
video.pause();
window.addEventListener("mousemove", () => {video.currentTime = video.currentTime + 1/fps});
video.addEventListener("play", () => {
if (!("requestVideoFrameCallback" in HTMLVideoElement.prototype)) {
return alert("Your browser does not support the `Video.requestVideoFrameCallback()` API.");
}
});
};
window.addEventListener("load", startDrawing);
Courtesy of Big Buck Bunny: </br><video width="320" height="240" autoplay="" muted="" loop="" src="https://www.w3schools.com/html/mov_bbb.mp4"></video>
Dont play with frames, Play video in mousemove event else pause the video.
I am adding multiple HTML5 videos onto a webpage.
The code I am replicating is from this recommended accessible approach. http://jspro.brothercake.com/audio-descriptions/ The video plays fine, and audio captions work, but when I add a new video to the same page the second video does not play the audio captions at all. Does anyone have suggestions on how I can fix this issue?
<video id="video" preload="auto" controls="controls"
width="640" height="360" poster="./media/HorribleHistories.jpg">
<source src="./media/HorribleHistories.mp4" type="video/mp4" />
<source src="./media/HorribleHistories.webm" type="video/webm" />
</video>
<audio id="audio" preload="auto">
<source src="./media/HorribleHistories.mp3" type="audio/mp3" />
<source src="./media/HorribleHistories.ogg" type="audio/ogg" />
</audio>
<script type="text/javascript">
(function()
{
//get references to the video and audio elements
var video = document.getElementById('video');
var audio = document.getElementById('audio');
//if media controllers are supported,
//create a controller instance for the video and audio
if(typeof(window.MediaController) === 'function')
{
var controller = new MediaController();
audio.controller = controller;
video.controller = controller;
}
//else create a null controller reference for comparison
else
{
controller = null;
}
//reduce the video volume slightly to emphasise the audio
audio.volume = 1;
video.volume = 0.8;
//when the video plays
video.addEventListener('play', function()
{
//if we have audio but no controller
//and the audio is paused, play that too
if(!controller && audio.paused)
{
audio.play();
}
}, false);
//when the video pauses
video.addEventListener('pause', function()
{
//if we have audio but no controller
//and the audio isn't paused, pause that too
if(!controller && !audio.paused)
{
audio.pause();
}
}, false);
//when the video ends
video.addEventListener('ended', function()
{
//if we have a controller, pause that
if(controller)
{
controller.pause();
}
//otherwise pause the video and audio separately
else
{
video.pause();
audio.pause();
}
}, false);
//when the video time is updated
video.addEventListener('timeupdate', function()
{
//if we have audio but no controller,
//and the audio has sufficiently loaded
if(!controller && audio.readyState >= 4)
{
//if the audio and video times are different,
//update the audio time to keep it in sync
if(Math.ceil(audio.currentTime) != Math.ceil(video.currentTime))
{
audio.currentTime = video.currentTime;
}
}
}, false);
})();
</script>
So your problem is to do with how you are grabbing the elements in the first place.
var video = document.getElementById('video');
var audio = document.getElementById('audio');
What you are doing is grabbing a single item on the page with the ID of "video" (same for "audio").
IDs have to be unique, so what you want to do is use classes instead.
<video class="video" preload="auto" controls="controls"
width="640" height="360" poster="./media/HorribleHistories.jpg">
See I changed the ID to a class.
Now any element with the class "video" can be used in our code.
However we do need to modify our code a bit as now we have multiple items to bind to.
please note the below is to give you an idea of how you loop items etc. You would need to rewrite your code to move each of the steps into functions etc. as your original code is not designed to work with multiple items
(function()
{
//get references to every single video and audio element
var videos = document.querySelectorAll('.video');
var audios = document.querySelectorAll('.audio');
// loop through all videos adding logic etc.
for(x = 0; x < videos.length; x++){
// grab a single video from our list to make our code neater
var video = videos[x];
if(typeof(window.MediaController) === 'function')
{
var controller = new MediaController();
video.controller = controller;
} else {
controller = null;
}
video.volume = 0.8;
//...etc.
}
})();
Quick Tip:
I would wrap your <video> and <audio> elements that are related in a <div> with a class (e.g. class="video-audio-wrapper").
This way you can change your CSS selector to something like:
var videoContainers = document.querySelectorAll('.video-audio-wrapper');
Then loop through them instead and check if they have a video and / or audio element
for(x = 0; x < videoContainers.length; x++){
var thisVideoContainer = videoContainers[x];
//query this container only - we can use `querySelector` as there should only be one video per container and that returns a single item / the first item it finds.
var video = thisVideoContainer.querySelector('video');
var audio = thisVideoContainer.querySelector('audio');
//now we can check if an element exists
if(video.length == 1){
//apply video logic
}
if(audio.length == 1){
//apply audio logic
}
// alternatively we can check both exist if we have to have both
if(video.length != 1 || audio.length != 1){
// we either have one or both missing.
// apply any logic for when a video / audio element is missing
//using "return" we can exit the function early, meaning all code after this point is not run.
return false;
}
///The beauty of this approach is you could then just use your original code!
}
Doing it this way you could recycle most of your code.
Thank you for your suggestions in changing the ID's into classes and adding the video wrapper <div> to the video container. That all makes sense in grouping each video on 1 page. I updated the the following code, but the audio captions won't play at all. The video plays and pauses fine, and the volume works. I am also not getting any syntax errors in the browser console. Here's what I got for my HTML and JS. I appreciate your help/feedback.
<div class="video-container-wrapper">
<div class="video-container">
<video class="video" preload="auto" controls="controls" width="640" height="360" poster="img/red-zone-thumb.png">
<source src="https://player.vimeo.com/external/395077086.hd.mp4?s=1514637c1ac308a950fafc00ad46c0a113c6e8be&profile_id=175" type="video/mp4">
<track kind="captions" label="English captions" src="captions/redzone-script.vtt" srclang="en" default="">
</video>
<audio class="audio" preload="auto">
<source src="captions/redzone-message.mp3" type="audio/mp3">
</audio>
</div>
</div>
Javascript:
var videoContainers = document.querySelectorAll('.video-container-wrapper');
for (x = 0; x < videoContainers.length; x++) {
var thisVideoContainer = videoContainers[x];
//query this container only - we can use `querySelector` as there should only be one video per container and that returns a single item / the first item it finds.
var video = thisVideoContainer.querySelector('video');
var audio = thisVideoContainer.querySelector('audio');
//now we can check if an element exists
if (video.length == 1) {
//apply video logic
//reduce the video volume slightly to emphasise the audio
video.volume = 0.8;
//when the video ends
video.addEventListener('ended', function () {
video.pause();
}, false);
}
if (audio.length == 1) {
//apply audio logic
audio.volume = 1;
//when the video plays
video.addEventListener('play', function () {
if (audio.paused) {
audio.play();
}
}, false);
// when the video ends
video.addEventListener('ended', function () {
audio.pause();
}, false);
//when the video time is updated
video.addEventListener('timeupdate', function () {
if (audio.readyState >= 4) {
//if the audio and video times are different,
//update the audio time to keep it in sync
if (Math.ceil(audio.currentTime) != Math.ceil(video.currentTime)) {
audio.currentTime = video.currentTime;
}
}
}, false);
}
}
I'm trying to create a loop of a video and when I click some button break that loop to finish the video
function stopIt() {
document.getElementById('stopIt').classList.add('stop');
}
function loopy() {
var vid = document.getElementById('front-video');
if (vid.currentTime > 1) {
vid.currentTime = 0;
if (document.getElementById('stopIt').classList.contains('stop')) {
break;
}
}
}
<video class="d-none d-lg-block" id="front-video" autoplay onTimeUpdate="loopy()">
<source src="video/front-video.mp4" type="video/mp4">
</video>
<div class="col-12 front-box py-3 pl-4" id="stopIt" onClick="stopIt()"></div>
I have a box with the id stopIt, but actually the video doesn't return to the start, It does it when I erase the second if.
Any ideas, thanks!
I'm not sure if I understand you correctly, but I think you want to do this:
function loopy() {
if (!document.getElementById('stopIt').classList.contains('stop')) {
var vid = document.getElementById('front-video');
if (vid.currentTime > 1) vid.currentTime = 0;
}
}
If the requirement is to loop from a specific begin time in media playback to a specific end time in media playback you can set the initial src of the HTMLMediaElement to the URL followed by a media fragment identifier.
The media element will pause at the end time of the media fragment identifier. You can use pause event, which is dispatched when a media element reaches the end time of a media fragment identifier, where .load() then .play() can be called to loop the media playback.
At click on button element, remove pause event handler from button element, store the .currentTime in a variable, remove the media fragment identifier from the src of the media element, call .load() on media element, set .currentTime to the stored .currentTime and then call .play().
const video = document.querySelector('video');
const button = document.querySelector('button');
let [from, to] = [0, 1];
let loop = true;
let currentTime = 0;
const src = "http://mirrors.creativecommons.org/movingimages/webm/ScienceCommonsJesseDylan_240p.webm";
const handlePause = e => {
if (loop) {
video.load();
video.play();
}
}
const stopLoop = e => {
if (loop) {
loop = !loop;
video.removeEventListener('pause', handlePause);
currentTime = video.currentTime;
video.src = video.src.replace(/#.+$/, '');
video.load();
video.currentTime = currentTime;
video.play();
}
}
button.addEventListener('click', stopLoop)
video.addEventListener('pause', handlePause);
video.src = `${src}#t=${from},${to}`
<button>stop loop</button>
<video src="" muted="false" controls autoplay>
I would like to record the video tag so that I can stream the blob data to a websocket server, however, when I attempt to start the mediaRecorder I get the following error:
The MediaRecorder failed to start because there are no audio or video
tracks available.
Is it possible to add the audio/video tracks from the html5 video tag to the media stream?
<script>
var video = document.getElementById('video');
var mediaStream = video.captureStream(30);
var mediaRecorder = new MediaRecorder(
mediaStream,
{
mimeType: 'video/webm;codecs=h264',
videoBitsPerSecond: 3000000
}
);
fileElem.onchange = function () {
var file = fileElem.files[0],
canplay = !!video.canPlayType(file.type).replace("no", ""),
isaudio = file.type.indexOf("audio/") === 0 && file.type !== "audio/mpegurl";
video.src = URL.createObjectURL(file);
video.play();
mediaRecorder.start(1000); // Start recording, and dump data every second
};
</script>
<p id="choice" class="info"><input type="file" id="file"> File type: <span id="ftype"></span></p>
<video width="640" height="360" id="video" src="" controls="false"></video>
Is it possible to use the MediaRecorder API with html5 video?
Yes, however you need to initialize MediaRecorder after the HTMLVideoElement.readyState has metadata.
Here is a sample, but it only works if the video source is from the same origin (since captureStream cannot capture from element with cross-origin data)
Note: In this sample, I use onloadedmetadata to initialize MediaRecorder after the video got metadata.
var mainVideo = document.getElementById("mainVideo"),
displayVideo = document.getElementById("displayVideo"),
videoData = [],
mediaRecorder;
var btnStart = document.getElementById("btnStart"),
btnStop = document.getElementById("btnStop"),
btnResult = document.getElementById("btnResult");
var initMediaRecorder = function() {
// Record with 25 fps
mediaRecorder = new MediaRecorder(mainVideo.captureStream(25));
mediaRecorder.ondataavailable = function(e) {
videoData.push(e.data);
};
}
function startCapture() {
videoData = [];
mediaRecorder.start();
// Buttons 'disabled' state
btnStart.setAttribute('disabled', 'disabled');
btnStop.removeAttribute('disabled');
btnResult.setAttribute('disabled', 'disabled');
};
function endCapture() {
mediaRecorder.stop();
// Buttons 'disabled' state
btnStart.removeAttribute('disabled');
btnStop.setAttribute('disabled', 'disabled');
btnResult.removeAttribute('disabled');
};
function showCapture() {
return new Promise(resolve => {
setTimeout(() => {
// Wrapping this in setTimeout, so its processed in the next RAF
var blob = new Blob(videoData, {
'type': 'video/mp4'
}),
videoUrl = window.URL.createObjectURL(blob);
displayVideo.src = videoUrl;
resolve();
}, 0);
});
};
video {
max-width: 300px;
max-height: 300px;
display: block;
margin: 10px 0;
}
<video id="mainVideo" playsinline="" webkit-playsinline="1" controls="1" onloadedmetadata="initMediaRecorder()">
<source src="sampleVideo.mp4" type="video/mp4">
</video>
<button id="btnStart" onclick="startCapture()"> Start </button>
<button id="btnStop" disabled='disabled' onclick="endCapture()"> Stop </button>
<button id="btnResult" disabled='disabled' onclick="showCapture()"> Show Result </button>
<video id="displayVideo" playsinline="" webkit-playsinline="1" controls="1"></video>
I have custom controls for a video.
See codepen.
Great. It works relatively well. However, I miss a functionality. When the video is paused and I drag slider on the seekbar, the video frames are not updating real time, only after you "put" the slider down (mousedown).
As you can see here, with the native html5 video functionality it's done like that: while you drag the bar, the video updates to the current frame your cursor is on. For me this would be quite important.
So, how could I make this happen? The problem lies in the nature of .addEventListener("change"), doesn't it?
<div class="row">
<div class="col-sm-4" id="video-container">
<!-- Video -->
<video id="video" muted>
<source src="https://www.w3schools.com/html/mov_bbb.mp4" type="video/mp4">
<p>
Your browser doesn't support HTML5 video.
</p>
</video>
<!-- Video Controls -->
<div id="video-controls">
<button type="button" id="play-pause" class="play"><img src="https://storage.googleapis.com/material-icons/external-assets/v4/icons/svg/ic_play_arrow_white_24px.svg"></button>
<input type="range" id="seek-bar" value="0">
<button type="button" id="full-screen"><img src=https://storage.googleapis.com/material-icons/external-assets/v4/icons/svg/ic_fullscreen_white_24px.svg></button>
</div>
</div>
</div>
<script type="text/javascript">
window.onload = function() {
// Video
var video = document.getElementById("video");
// Buttons
var playButton = document.getElementById("play-pause");
var fullScreenButton = document.getElementById("full-screen");
// Sliders
var seekBar = document.getElementById("seek-bar");
// Event listener for the play/pause button
playButton.addEventListener("click", function() {
if (video.paused == true) {
// Play the video
video.play();
// Update the button text to 'Pause'
$('img', playButton).attr("src","https://storage.googleapis.com/material-icons/external-assets/v4/icons/svg/ic_pause_white_24px.svg");
} else {
// Pause the video
video.pause();
// Update the button text to 'Play'
$('img', playButton).attr("src","https://storage.googleapis.com/material-icons/external-assets/v4/icons/svg/ic_play_arrow_white_24px.svg");
}
});
// Event listener for the full-screen button
fullScreenButton.addEventListener("click", function() {
if (video.requestFullscreen) {
video.requestFullscreen();
} else if (video.mozRequestFullScreen) {
video.mozRequestFullScreen(); // Firefox
} else if (video.webkitRequestFullscreen) {
video.webkitRequestFullscreen(); // Chrome and Safari
}
});
// Event listener for the seek bar
seekBar.addEventListener("change", function() {
// Calculate the new time
var time = video.duration * (seekBar.value / 100);
// Update the video time
video.currentTime = time;
});
// Update the seek bar as the video plays
video.addEventListener("timeupdate", function() {
// Calculate the slider value
var value = (100 / video.duration) * video.currentTime;
// Update the slider value
seekBar.value = value;
});
// Pause the video when the seek handle is being dragged
seekBar.addEventListener("mousedown", function() {
video.pause();
});
$('#video-controls').width($('video').width());
$('#seek-bar').width($('video').width() -105);
}
</script>
I got it done by changing the .addEventListener("change") to .addEventListener("input"), but maybe this question could be helpful for someone so I didn't delete it.