Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

play video in loop using setTimeout

I want to play videos in a loop. For some reason I don't want change video src on ended event. So I created video elements for each video in a loop. Also I have video src and durations in array.

here is my idea: Only current playing video tag can be visible. Others will be hided. Instead of using ended event, I want to use setTimeout function. Video's duration will be delay parameter.

But they all play together. I couldn't make them play in order.

Here is what I done so far:

videoArray = [
    {"video":video1.mp4, "duration": 5},
    {"video":video2.mp4, "duration": 7},
    {"video":video3.mp4, "duration": 9},
    {"video":video4.mp4, "duration": 10},
]

for (var j = 0; j < videoArray.length; j++){
    var video = document.createElement("video");
    video.src=videoArray[j];
    video.id="video-"+j;
    video.preload = "metadata";
    video.type="video/mp4";
    video.autoplay = true; 
    video.style.display="none";
    document.body.appendChild(video); 
}

for (var count = 0; count < videoArray.length; count++) {
    (function(num){
        setTimeout(function() {
            videoArray[num].video.style.display="block";
            videoArray[num].video.play();
        }, 1000 * videoArray[num].duration);
        videoArray[num].video.style.display="none";
    })(count);
}
like image 282
linepisode Avatar asked Dec 22 '22 18:12

linepisode


2 Answers

Disclaimer

I know that the question was asked without the ended event, but I do not think that set time out is the way to go.
Think of the scenario where you have video buffering, or slowing down for any reason, your setTimeout will be out of sync.

At the bottom I've added another solution that answers the requirement of not using the ended event, but again, I do not recommend using it.

Solution

The idea is to have an event listener to the end of the video, in that case, even if you run the video on a different speed you are still going to run the next video regardless of the duration.

Another benefit is that you do not need to know the duration of the videos in the first place.

PS. the event listener that you need to listen to is video.addEventListener("ended", callback);

You are more than welcome to run the code or to have a look at a working example I've created for you

Working Example

    const videoUrls = [
        'https://videos-play-loop.netlify.com/video1.mp4',
        'https://videos-play-loop.netlify.com//video2.mp4',
        'https://videos-play-loop.netlify.com//video3.mp4',
    ];

    const createVideo = ({id, src, width, cls = 'video', display = 'block', playbackRate = 1, muted = true, type = 'video/mp4', autoplay = false, controls = true}) => {
        const videoElement = document.createElement("video");
        videoElement.id = id;
        videoElement.src = src;
        videoElement.classList.add(src);
        videoElement.type = type;
        videoElement.autoplay = autoplay;
        videoElement.controls = controls;
        videoElement.style.display = display;
        videoElement.muted = muted;
        videoElement.playbackRate = playbackRate;
        return videoElement;
    };


    const addVideos = (container, videoUrls) => {
        const videos = videoUrls.map((url, index) => {
            const first = index === 0;
            const display = first ? 'block' : 'none';
            return createVideo({id: `video-${index}`, src: url,display, width: 640, autoplay: first, playbackRate: 3});
        });
        videos.forEach((video, index) => {
            const last = index === videos.length - 1;
            const playNext = (element) => {
                element.target.style.display = "none";
                const nextElementIndex = last ? 0 : index + 1;
                const nextElement = videos[nextElementIndex];
                nextElement.autoplay = true;
                nextElement.style.display="block";
                nextElement.load();
            };
            video.addEventListener("ended", playNext);
            container.appendChild(video)
        });
    };
    const videoWrapper = document.getElementById('video-wrapper');
    addVideos(videoWrapper, videoUrls);
#video-wrapper video {
    max-width: 600px;
}
<div id="video-wrapper"></div>

Working solution with setTimeout (please use the solution above)

const videoUrls = [{
    url: `https://videos-play-loop.netlify.com/video3.mp4`,
    duration: 3,
  },
  {
    url: `https://videos-play-loop.netlify.com/video2.mp4`,
    duration: 4
  },
  {
    url: `https://videos-play-loop.netlify.com/video1.mp4`,
    duration: 5
  }
];


const createVideo = ({
  id,
  src,
  width,
  cls = 'video',
  display = 'block',
  duration,
  playbackRate = 1,
  muted = true,
  type = 'video/mp4',
  autoplay = false,
  controls = true
}) => {
  const videoElement = document.createElement("video");
  videoElement.id = id;
  videoElement.src = src;
  videoElement.classList.add(src);
  videoElement.type = type;
  videoElement.autoplay = autoplay;
  videoElement.controls = controls;
  videoElement.style.display = display;
  videoElement.muted = muted;
  videoElement.playbackRate = playbackRate;
  videoElement.setAttribute('data-duration', duration);
  return videoElement;
};

const playNext = (videos, index) => {
  const current = videos[index];
  const activeVideoDuration = parseInt(current.dataset.duration) * 1000;
  setTimeout(() => {
    const last = index === videos.length - 1;
    current.style.display = "none";
    current.pause();
    const activeVideoIndex = last ? 0 : index + 1;
    const next = videos[activeVideoIndex];
    next.autoplay = true;
    next.style.display = "block";
    next.load();
    next.play();
    playNext(videos, activeVideoIndex);
  }, activeVideoDuration);
};


const addVideos = (container, videoUrls) => {
  const videos = videoUrls.map((video, index) => {
    const {
      url,
      duration
    } = video;
    const first = index === 0;
    const display = first ? 'block' : 'none';
    return createVideo({
      id: `video-${index}`,
      src: url,
      duration,
      display,
      width: 640,
      autoplay: first,
    });
  });

  videos.forEach(video => container.appendChild(video));
  playNext(videos, 0);
};

const videoWrapper = document.getElementById('video-wrapper');
addVideos(videoWrapper, videoUrls);
#video-wrapper video {
  max-width: 600px;
}
<div id="video-wrapper"></div>
like image 78
Shahar Avatar answered Dec 28 '22 08:12

Shahar


You could hold the duration of the videos in a variable and the accumulate this variable with the previous video duration and set this a the setTimeOut duration.

Note that the time of the videos is in seconds. And for the first video to play user has to interact otherwise the video will not play.

Working Example:

function startVideos(event) {
 event.target.style.display= "none";
        (function() {
          videoArray = [
            {
              video:
                "http://techslides.com/demos/sample-videos/small.mp4",
              duration: 5
            },
            {
              video:
                "http://clips.vorwaerts-gmbh.de/big_buck_bunny.mp4",
              duration: 60
            },
            {
              video:
                "https://mobamotion.mobatek.net/samples/sample-mp4-video.mp4",
              duration: 120
            },
            {
              video:
                "http://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4",
              duration: 600
            }
          ];

          let videArrayElem = [];

          for (var j = 0; j < videoArray.length; j++) {
            var video = document.createElement("video");
            video.src = videoArray[j].video;
            video.id = "video-" + j;
            video.preload = "metadata";
            video.type = "video/mp4";
            video.autoplay = false;
            video.controls= true;
            video.style.display = "none";
            videArrayElem.push(video);
            document.body.appendChild(video);
          }

          let prviousVideoDuration = 0;
          for (var count = 0; count < videoArray.length; count++) {
            (function(num) {
              setTimeout(function() {
                videArrayElem[num].style.display = "block";
                videArrayElem[num].play();
              }, prviousVideoDuration);
              prviousVideoDuration += 1000 * videoArray[num].duration;
              videArrayElem[num].style.display = "none";
            })(count);
          }
        })();
      }
video {
        height: 100px;
        width: 100px;
        display: inline-block;
        margin: 4px;
        float: left;
      }
<button type="button" onclick="startVideos(event)">Start Video Demo</button>
like image 31
Sohail Ashraf Avatar answered Dec 28 '22 08:12

Sohail Ashraf