【问题标题】:Is there a way to play audio and video separate from each other in the same video有没有办法在同一个视频中分别播放音频和视频
【发布时间】:2019-04-28 09:09:37
【问题描述】:

<video id="video1" width="320" height="176" controls="controls">
  <source src="mov_bbb.mp4" type="video/mp4">
  
  <source src="mov_bbb.m4a" type="video/m4a">
  
</video>

我有一段无声视频,想在视频播放中添加他的音频文件

【问题讨论】:

  • 不在单个元素中,您需要将音频和视频结合起来。您也许可以拥有 &lt;video&gt;&lt;audio&gt; 元素并尝试保持同步,但可能不会是一次很棒的体验
  • 您应该能够使用 MediaSource Extensions 来获得所需的内容。

标签: javascript html css audio video


【解决方案1】:

MediaSource API 允许我们这样做。

请注意,要使其工作,您需要以与该 API 兼容的方式准备媒体资产。仔细阅读this MDN article,它很好地解释了如何做到这一点。

一旦你有了你的资产,剩下的就很简单了:

(async() => {

  const fetching = Promise.all( [
    // the video "only" file
    fetchData( "https://dl.dropboxusercontent.com/s/u9ycdfwy8fig4dl/bbb_video.mp4" ),
    // the audio "only" file
    fetchData( "https://dl.dropboxusercontent.com/s/rj4dh32vxwi1iv5/bbb_audio.mp4" )
  ] );

  const video_mime = "video/mp4; codecs=avc1.64000c";
  const audio_mime = "audio/mp4; codecs=mp4a.40.2";
  if(
    !MediaSource.isTypeSupported( video_mime ) ||
    !MediaSource.isTypeSupported( audio_mime )
  ) {
    throw "unsupported codecs";
  }
  
  const source = new MediaSource();
  document.querySelector( "video" ).src = URL.createObjectURL( source );
  await waitForEvent( source, "sourceopen" );

  const video_buffer = source.addSourceBuffer( video_mime );
  const audio_buffer = source.addSourceBuffer( audio_mime );
  video_buffer.mode = audio_buffer.mode = "sequence";

  const [ video_data, audio_data ] = await fetching;
  // There is a 'variable' limit as to how much
  // data we can append in on go, 10MB seems quite safe
  const chunk_size = 10 * 1024 * 1024;
  let i = 0;
  while (
    i < video_data.length &&
    i < audio_data.length
  ) {
    const next_i = i + chunk_size;
    const events = Promise.all( [
      waitForEvent( video_buffer, "updateend" ),
      waitForEvent( audio_buffer, "updateend" )
    ] );
    video_buffer.appendBuffer( video_data.subarray( i, next_i ) );
    audio_buffer.appendBuffer( audio_data.subarray( i, next_i ) );
    await events;
    i = next_i;
  }
  
  source.endOfStream();

})().catch( console.error );

function fetchData( url ) {
  return fetch( url )
    .then( (resp) => resp.ok && resp.arrayBuffer() )
    // we return an Uint8 view to be able to do a zero-cost subarray()
    .then( (buf) => new Uint8Array( buf ) );
}
function waitForEvent( target, event_name ) {
  return new Promise( (res) => {
    target.addEventListener( event_name, res, { once: true } );
  } );
}
&lt;video controls&gt;&lt;/video&gt;

【讨论】:

  • 谢谢。我试过这个,但它失败了,因为我的文件是本地文件,而且似乎 fetch 不适用于本地文件。你知道有什么解决方法吗?
  • @StevenPenny 最好是运行本地 Web 服务器,如果您的文件与文档位于同一目录中,您也可以尝试使用 XMLHttpRequest 而不是 fetch,否则您可以使用 @ 启动 Chrome 浏览器987654326@.
【解决方案2】:

这对我有用:

<meta charset="utf-8">
<video controls id="v" height="480" src="file.webm"></video>
<audio id="a" src="file.weba"></audio>

<script>
let v = document.getElementById('v');
let a = document.getElementById('a');
v.onpause = () => a.pause();
v.onplay = () => a.play();
v.onseeked = () => a.currentTime = v.currentTime;
</script>

控件在视频元素上,音频元素会响应视频播放, 暂停并寻找。音量控制在此示例中不起作用,因此您可以 要么添加该代码,要么只使用系统音量控制。

【讨论】:

  • 有趣的是,网络似乎在循环运行。这是十多年前SMIL 解决的问题。
猜你喜欢
  • 1970-01-01
  • 2023-03-16
  • 2012-03-20
  • 1970-01-01
  • 2015-09-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多