与视频同步播放多个音轨之一

2023-12-02

我正在尝试在网络浏览器中播放视频,原始视频带有两个或多个音频流,每个音频流都采用不同的语言。我想让用户可以选择切换他们正在收听的音轨。

我尝试使用audioTracks在视频元素上,但尽管说它在大多数浏览器中都支持在标志后面,至少在 Firefox 和 Chrome 中我不会说它根本起作用(在 Firefox 中它只显示第一个轨道,元数据是错误的,而在 Chrome 中)一旦您将主音轨静音,视频就会暂停,并且您必须寻找视频才能使其真正继续播放)。

我尝试使用ffmpeg单独保存各个音轨并尝试与视频同步播放它们(设置audio.currentTime = video.currentTime回应视频中的几个事件,例如play, playing, pause, seeked, stalled),播放两个音轨<audio>连接到的元素GainNode使用网络音频API(切换音轨将增益设置为1对于您想要的曲目以及0对于其余的)。这似乎在 Chrome 中完美地工作,但 Firefox 到处都是,甚至在同步之后currentTime属性实际音频关闭一秒或更长时间。

我看到其他人抱怨 MP3 的时序问题,但我使用的是 AAC。在这些情况下的解决方案是不对音频使用可变比特率,但这似乎并没有改善它(ffmpeg -i video.mkv -map 0:a:0 -acodec aac -b:a 128k track-0.aac)

有什么好的策略可以做到这一点吗?如果可以避免的话,我宁愿不必为每个音轨都有重复的视频文件。


在你的情况下最好的可能是使用媒体源扩展 (MSE) API.
这将允许您在继续播放原始视频的同时仅切换音频源。
由于我们将用其他音频源替换整个音频 SourceBuffer 的内容,因此不会出现同步问题,对于播放器来说,就像只有一个音频源一样。

(async() => {
  const vid = document.querySelector( "video" );
  const check = document.querySelector( "input" );
  // video track as ArrayBuffer
  const bufvid = await getFileBuffer( "525d5ltprednwh1/test.webm" );
  // audio track one
  const buf300 = await getFileBuffer( "p56kvhwku7pdzd9/beep300hz.webm" );
  // audio track two
  const buf800 = await getFileBuffer( "me3y69ekxyxabhi/beep800hz.webm" );
  
  const source = new MediaSource();
  // load our MediaSource into the video
  vid.src = URL.createObjectURL( source );
  // when the MediaSource becomes open
  await waitForEvent( source, "sourceopen" );

  // append video track
  const vid_buffer = source.addSourceBuffer( "video/webm;codecs=vp8" );
  vid_buffer.appendBuffer( bufvid );

  // append one of the audio tracks
  const aud_buffer =  source.addSourceBuffer( "audio/webm;codecs=opus" );
  aud_buffer.appendBuffer( check.checked ? buf300 : buf800 );
  // wait for both SourceBuffers to be ready
  await Promise.all( [
    waitForEvent( aud_buffer, "updateend" ),
    waitForEvent( vid_buffer, "updateend" )
  ] );
  // Tell the UI the stream is ended (so that 'ended' can fire)
  source.endOfStream();
  
  check.onchange = async (evt) => {
    // remove all the data we had in the Audio track's buffer
    aud_buffer.remove( 0, source.duration );
    // it is async, so we need to wait it's done
    await waitForEvent( aud_buffer, "updateend" );
    // no we append the data of the other track
    aud_buffer.appendBuffer( check.checked ? buf300 : buf800 );
    // also async
    await waitForEvent( aud_buffer, "updateend" );
    // for ended to fire
    source.endOfStream();
  };

})();

// helpers
function getFileBuffer( filename ) {
  return fetch( "https://dl.dropboxusercontent.com/s/" + filename )
    .then( (resp) => resp.arrayBuffer() );
}
function waitForEvent( target, event ) {
  return new Promise( res => {
    target.addEventListener( event, res, { once: true } );
  } );
}
video { max-width: 100%; max-height: 100% }
<label>Use 300Hz audio track instead of 800Hz <input type="checkbox"></label><br>
<video controls></video>
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

与视频同步播放多个音轨之一 的相关文章

随机推荐

  • Katalon studio Java jar 文件

    我想在 Katalon studio 中使用我的 Java 代码 但我不知道哪个 jar我必须导入 Katalon 的文件 我阅读了 Katalon 提供的所有文档 并阅读了所有有关 Katalon 中 java 代码使用的论坛帖子 我没有
  • Heroku 500 内部服务器错误

    我正在开发 Ruby on Rails 应用程序并尝试使用 Heroku 部署它 但是我遇到了一些无法解决的错误 我已经查看了 Heroku 日志 可以看到详细信息 但我不知道出了什么问题 在我看来 问题来自我的 header html e
  • Android - 从另一个活动控制按钮

    我有两项活动 主页活动包含一个列表视图 其中有两个名为checkIn and 方向 当单击 checkIn 按钮时 它会执行一些操作 例如 A 单击方向按钮时 它会启动方向活动 因此 在方向活动中 如果满足某些条件 则会出现一个警报框 询问
  • static const(非整数)成员初始化语法背后的基本原理?

    我知道如何初始化不是整数的静态成员 但我想知道这种语法背后的基本原理是什么 我希望能够将值放入类中 就像使用整数成员一样 class A static const int i 3 我意识到 如果我更改该值 这可能意味着更多的重建 因为它是标
  • Rails 3 电子邮件中的 CSS 图像

    我正在尝试使用 Rails 3 和 Action Mailer 发送一封电子邮件 电子邮件发送得很好 但我希望它采用 HTML 格式 并带有一些基本样式 其中包括背景图像 我知道图像可能会被阻止 直到用户允许显示它们 但我仍然认为最好链接到
  • 宽松的原子规则有什么(轻微)差异?

    看到赫伯 萨特斯表现出色后talk about 原子武器 我有点困惑轻松原子例子 我随身携带了一个atomic in the C 内存模型 SC DRF 无数据竞争的顺序一致 在加载 读取时执行 获取 据我所知 对于负载 和存储 默认值是s
  • 从 Drupal 网站创建 Iframe

    我有一个 drupal 网站 我想生成一个包含我的 drupal 站点内容的 Iframe 其他站点可以嵌入该内容 我认为如何实现这一目标 Method 1 创建一个独立于 drupal 引擎的 php 脚本 导入配置文件 从而获得对数据库
  • 使用 http.NewRequest POST 数据失败

    我正在尝试使用以下命令将数据从一个 golang 服务传递到另一个 golang 服务http NewRequest 为此 我使用了以下代码 httpClient http Client userserviceUrl http user 7
  • 如何从 BroadcastReceiver 向 Activity 或 Fragment 发送消息

    我有一个接收器 它执行呼叫详细信息保存任务 例如存储来电 去电等 所有这些详细信息都会保存到 sqlite DB 中 如果我的活动没有运行 那就没问题了 有时 当我的活动正在运行时 我会接到一些来电 接收器运行并将数据存储到数据库 UI 不
  • 使用ant删除重复文件?

    有没有办法使用ant删除重复文件 具体来说 如果我在两个不同的输出目录中有相同的文件名 我想从第二个目录中删除它 我想我想出了一个解决方案
  • dplyr r:选择名称位于外部向量中的列[重复]

    这个问题在这里已经有答案了 Purpose 我可以使用选择列dplyr条件是列名位于外部向量中 我发现一些帖子解释了如何使用名称向量对数据框进行子集化 但当向量中的某些名称在数据框中不存在时 我找不到帖子 示例数据集 library tid
  • 如何停止张量流中张量某些条目的梯度

    我正在尝试实现一个嵌入层 将使用预先训练的手套嵌入来初始化嵌入 对于可以在手套中找到的单词 它将被修复 对于那些没有出现在手套中的单词 它会被随机初始化 并且是可训练的 我如何在张量流中做到这一点 我知道整个张量有一个 tf stop gr
  • 带有表达式不需要的结果的 C 宏

    我正在运行以下程序并得到结果为 9 7 我理解为什么输出是 9 但我不明白为什么我得到 7 作为输出 include
  • 从登录参数创建用户对象

    我试图在用户登录后创建一个包含所有用户字段的用户对象 以便我可以从用户的类中检索任何给定的属性 这是用户类 public class User private String username private String password
  • 运行时错误“9”下标超出条件格式代码范围

    我对 VBA 以及一般的任何类型的编程 非常陌生 所以我不确定如何继续 我猜我的错误与条件格式的重叠范围有关 因为当代码以不同的方式设置时 我也会遇到错误 一旦范围不再重叠 这些错误就会得到解决 这里的情况可能并非如此 但我认为了解一下会有
  • 自签名证书可以保护多个 CN / FQDN 吗?

    这是一个有点愚蠢的设置 但这就是我现在正在查看的内容 我正在学习 Kubernetes 我想将自定义代码推送到我的 Kubernetes 集群 这意味着该代码必须作为 Docker 映像提供 可从someDocker 存储库 默认为 Doc
  • 重复调用 Ng-Options 表达式

    我的设备有多个问题
  • 配置 Eclipse 以与 MSYS2 一起使用

    我使用 Eclipse Mars 和 MSYS2 Eclipse 无法识别我的 MSYS2 安装 它包含用于 32 位编译的 Mingw w64 我在互联网上找到的东西不起作用 我应该怎么办 好吧 聚会有点晚了 但看起来还有没什么特别的关于
  • ASP.NET-发送电子邮件

    我正在做一个航班预订系统 我想向用户发送一封电子邮件 其中包含他的旅行的电子机票 电子机票是使用从数据库中获取的预订 ID 以及前面页面中的其他详细信息 例如乘客姓名等 动态生成的 那么我怎样才能将动态生成的电子客票发送到他的电子邮件 ID
  • 与视频同步播放多个音轨之一

    我正在尝试在网络浏览器中播放视频 原始视频带有两个或多个音频流 每个音频流都采用不同的语言 我想让用户可以选择切换他们正在收听的音轨 我尝试使用audioTracks在视频元素上 但尽管说它在大多数浏览器中都支持在标志后面 至少在 Fire