Youdao
Youdao

Reputation: 77

How to sync media perfectly in HTML?

Let's say I would like to synchronize two videos and avoid echos. I meant that the two tracks should stay in sync as though playing a regular video file with audio.

<video id="A" src="http://media.w3.org/2010/05/sintel/trailer.webm" controls=""></video><br>
<p>Current Time:<span id="aTime"></span></p><br>
<video id="B" src="http://media.w3.org/2010/05/sintel/trailer.webm" controls=""></video><br>
<p>Current Time:<span id="bTime"></span></p><br>
<button onclick="playM()">Play</button>
<button onclick="pauseM()">Pause</button>
<script>
var a = document.getElementById("A");
var b = document.getElementById("B");
function playM() {
a.play();
b.play();
}
function pauseM() {
a.pause();
b.pause();
}
a.ontimeupdate = function() {
document.getElementById("aTime").innerHTML = a.currentTime;
};
b.ontimeupdate = function() {
document.getElementById("bTime").innerHTML = b.currentTime;
};
a.onseeked = function() {
b.currentTime= a.currentTime;
};
b.onseeked = function() {
a.currentTime = b.currentTime;
};
</script>

Upvotes: 0

Views: 1111

Answers (2)

zer00ne
zer00ne

Reputation: 43880

Update

After some experimentation, I've discovered that the an offset of mere microseconds is great, but not really necessary for syncing videos. In Demo 2 we have video tag A and B. A has 4 eventListener() and video B as the callback. play(), pause(), and seek are synced between A and B with A being the "master" and B the "slave".

Demo 2

<!DOCTYPE html>
<html>

<head>
  <style>
    html {
      font: 400 16px/1.5 Consolas
    }
    
    section {
      display: flex;
      justify-content: space-around;
      width: 100%;
      height: calc(100% - 160px);
    }
    
    button {
      padding: 0;
      border: 0;
    }
  </style>
</head>

<body>
  <section>
    <video id="A" src="http://media.w3.org/2010/05/sintel/trailer.webm" controls width='240' height='135'></video>
    <video id="B" src="http://media.w3.org/2010/05/sintel/trailer.webm" controls width='240' height='135'></video>
  </section>


  <script>
    var A = document.getElementById("A");
    var B = document.getElementById("B");


    A.addEventListener("play", function() {
      B.play();
    });

    A.addEventListener("pause", function() {
      B.pause();
    });

    A.addEventListener("seeking", function() {
      B.currentTime = A.currentTime;
    });

    A.addEventListener("seeked", function() {
      B.currentTime = A.currentTime;
    });
  </script>
</body>

</html>


JavaScript is synchronous, meaning that it processes one thing at a time. Syncing media is possible by using Promises which allows asynchronous operations but it's syntax is unintuitive and down right difficult. Fortunately async and await just got released in all major browsers* only a couple of months ago. All of the behavior and flow of Promises are in await. Here's the basic syntax involved using 2 functions asynchronously:

async function functionName() {
  var A = await functionA();
  var B = await functionB();
  var C = A + B;
  return C;
}


Details are commented in Demo

References are at the bottom of post

Demo

/* For details on how this demo controls the <form>, 
|| see HTMLFormControlsCollection in References
*/
var x = document.forms.ui.elements;
var playM = x.playM;
var pauseM = x.pauseM;

/* Register the buttons to the click event
|| callback is asyncCall()
|| Register a video tag to the ended event
|| callback is just an anonymous function to
|| display 'IDLE' message when the videos have ended
*/
/* Avoid the use of on event handlers, use event
|| listeners instead. For details on Event Listeners
|| see addEventListener() in References
*/
playM.addEventListener('click', asyncCall, false);

pauseM.addEventListener('click', asyncCall, false);

document.querySelector('video').addEventListener('ended', function(e) {
  x.display.value = 'IDLE';
}, false);

/* This callback is an Async Function which uses a key
|| word called "await". await waits for a function
|| to start then moves on to the next await to 
|| see when it's ready to start. The players are 
|| playing asynchronously (i.e. congruently, i.e i.e. in 
|| parallel). Normally JavaScript is synchronous because
|| the evaluation and execution of a function is the
|| only thing a browser would do, so everything else
|| had to wait their turn. That is called: "blocking".
|| While await is waiting, the browser is allowed to do
|| other processing. For details on async and await,
|| see References.
*/
async function asyncCall(e) {
  var A = document.getElementById("A");
  var B = document.getElementById("B");
  var status;
  
  /* if/if else condition to determin which button was
  || actually clicked. Use e.target to get the origin
  || of event (i.e. clicked button). For details on
  || Event.target, see References
  */
  // if the button's #id is 'playM'...
  if (e.target.id === 'playM') {
    var mediaA = await mPlay(A);
    x.outA.value = performance.now(mediaA);
    var mediaB = await mPlay(B);
    x.outB.value = performance.now(mediaB);
    status = mPlay(B);
    
  // otherwise if the button's #id is 'pauseM'... 
  } else if (e.target.id === 'pauseM') {
    var mediaA = await mIdle(A);
    x.outA.value = performance.now(mediaA);
    var mediaB = await mIdle(B);
    x.outB.value = performance.now(mediaB);
    status = mIdle(B);
  } else {
    status = 'IDLE';
  }
  x.display.value = status;
  return status;
}

// Simple function used in asyncCall() to play
function mPlay(ele) {
  var state = 'PLAYING';
  ele.play();
  return state;
}

// Simple function used in asyncCall() to pause
function mIdle(ele) {
  var state = 'IDLE';
  ele.pause();
  return state;
}
html {
  font: 400 16px/1.5 Consolas
}

section {
  display: flex;
  justify-content: space-around;
  width: 100%;
  height: calc(100% - 160px);
}

button {
  padding: 0;
  border: 0;
}

#playM:before {
  content: '▶';
  font-size: 32px
}

#pauseM::before {
  content: '⏸';
  font-size: 32px;
}
<section>
  <video id="A" src="http://media.w3.org/2010/05/sintel/trailer.webm" controls width='160' height='90'></video>
  <video id="B" src="http://media.w3.org/2010/05/sintel/trailer.webm" controls width='160' height='90'></video>
</section>

<form id='ui'>
  <fieldset>
    <legend>Asynchronous Playback</legend>

    <button id='playM' type='button'></button>

    <button id='pauseM' type='button'></button>

    <output id='display'></output><br><br>

    <label for='outA'>Source A: </label>
    <output id='outA'></output><br><br>

    <label for='outB'>Source B: </label>
    <output id='outB'></output><br><br>

  </fieldset>
</form>

References

Upvotes: 2

dom_ahdigital
dom_ahdigital

Reputation: 1681

You can use plain JavaScript for this. A good starting point and reference would be W3C Schools. The currentTime property with an oncuechange function should get you started in checking changes in the timeline and seeking.

Although, I don't understand your restrictions (if any) so don't know why you wouldn't just use a video file with the audio and video already synced.

Upvotes: 1

Related Questions