如何在 HTML 中完美同步媒体?

Posted

技术标签:

【中文标题】如何在 HTML 中完美同步媒体?【英文标题】:How to sync media perfectly in HTML? 【发布时间】:2017-11-27 09:09:10 【问题描述】:

假设我想同步两个视频并避免回声。我的意思是这两个轨道应该保持同步,就像播放带有音频的普通视频文件一样。

<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>

【问题讨论】:

我的问题是不是太简单或太不清楚以至于没有人回答?我确实研究了很多,但一无所获。 实际上你的问题在去年会更难回答,因为异步 javascript 很难完成。就在几个月前asyncawait 被除IE 之外的所有浏览器发布(IE 并不是真正的浏览器,这是一个大错误)。看我的回答。顺便说一句,我同步了 2 个相同的视频以证明它们是真正同步的。通过在播放过程中收听,您将听不到回声。视频和音频可以同步,但如果它们完全不同,这样做是没有意义的。 请注意,视频的加载时间以毫秒为单位,它们之间的差异非常小,平均为 @0.4 毫秒。这就像 400 秒。 【参考方案1】:

您可以为此使用纯 JavaScript。一个好的起点和参考是W3C Schools。带有oncuechange 函数的currentTime 属性应该可以帮助您开始检查时间线的变化并进行查找。

不过,我不明白你的限制(如果有的话),所以不知道你为什么不直接使用已经同步音频和视频的视频文件。

【讨论】:

【参考方案2】:

更新

经过一些实验,我发现仅仅几微秒的偏移量非常好,对于同步视频来说并不是必需的。在演示 2 中,我们有视频标签 A 和 B。A 有 4 个 eventListener() 和视频 B 作为回调。 play()pause()seek 在 A 和 B 之间同步,其中 A 为“主”,B 为“从”。

演示 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 是同步的,这意味着它一次处理一件事。使用Promises 可以同步媒体,它允许异步操作,但它的语法不直观且非常困难。幸运的是 asyncawait 仅在几个月前才在所有主要浏览器中发布*。 Promises 的所有行为和流程都在 await 中。以下是异步使用 2 个函数所涉及的基本语法:
async function functionName() 
  var A = await functionA();
  var B = await functionB();
  var C = A + B;
  return C;


详情在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>

参考文献

asyncfunction() ...await

HTMLFormControlsCollection

Event.target

Event.target.addEventListener('event',function,false)

【讨论】:

非常感谢您的努力。我从你那里学到了很多东西。尽管您的解决方案更可能是“如何在 HTML 中完美同步媒体?”的答案。我已经找到了我最初寻找的东西,我将改变这个主题的标题。还是谢谢。 不客气,先生。您是否需要在一个稍微不同的问题上得到帮助,或者它是否足够不同以至于它是一个新问题?如果是前者,您可以将其添加到此帖子中。如果是后者,您最好将其作为一个新问题发布。无论哪种方式,如果可以的话,我会尽力提供帮助,祝您编码愉快。 刚刚看到您的编辑,SO 警察不会喜欢它。您的原始代码帖子很好,我会为您回滚。 我没有看到任何编辑历史记录,所以我试图记住 OP(原始帖子)。事实证明,通过尝试重新创建您的代码,我找到了另一种同步媒体的方法。只需将D.click() 添加到A 回调函数中,当单击A 按钮时,该函数基本上会单击B 按钮。它可能比我的答案有更大的偏移量,但在我的耳朵里没有回声。 好吧,看来我必须对 CO 规则更加谨慎。感谢 bruv 为我编辑。我想我最好自己编辑它并使用我的粗略代码。

以上是关于如何在 HTML 中完美同步媒体?的主要内容,如果未能解决你的问题,请参考以下文章

IOS同步音乐流媒体

如何在 Android 中将时钟应用小部件与系统时钟完美同步?

如何在 bootstrap4 导航栏中对齐社交媒体图标及其工具提示?

如何在python中注册媒体密钥

[书目20160620]自媒体时代,我们该如何做营销

如何从隔离存储中打开媒体播放器 WebVTT 字幕