如何使用 MediaRecorder 作为 MediaSource

2024-03-17

作为学习 WebRTC 的练习,我试图并排显示本地网络摄像头和网络摄像头的延迟播放。为了实现这一目标,我尝试将记录的 blob 传递到 BufferSource 并使用相应的 MediaSource 作为视频元素的源。

// the ondataavailable callback for the MediaRecorder
async function handleDataAvailable(event) {
  // console.log("handleDataAvailable", event);
  if (event.data && event.data.size > 0) {
    recordedBlobs.push(event.data);
  }

  if (recordedBlobs.length > 5) {
    if (recordedBlobs.length === 5)
      console.log("buffered enough for delayed playback");
    if (!updatingBuffer) {
      updatingBuffer = true;
      const bufferedBlob = recordedBlobs.shift();
      const bufferedAsArrayBuffer = await bufferedBlob.arrayBuffer();
      if (!sourceBuffer.updating) {
        console.log("appending to buffer");
        sourceBuffer.appendBuffer(bufferedAsArrayBuffer);
      } else {
        console.warn("Buffer still updating... ");
        recordedBlobs.unshift(bufferedBlob);
      }
    }
  }
}
// connecting the media source to the video element
recordedVideo.src = null;
recordedVideo.srcObject = null;
recordedVideo.src = window.URL.createObjectURL(mediaSource);
recordedVideo.controls = true;
try {
  await recordedVideo.play();
} catch (e) {
  console.error(`Play failed: ${e}`);
}

所有代码:https://jsfiddle.net/43rm7258/1/ https://jsfiddle.net/43rm7258/1/

当我在 Chromium 78 中运行它时,我得到一个NotSupportedError: Failed to load because no supported source was found.来自play视频元素的元素。

我不知道我做错了什么或此时如何继续。

这是关于类似的事情,但对我没有帮助:MediaSource 随机停止视频 https://stackoverflow.com/questions/37665469/mediasource-randomly-stops-video?noredirect=1#comment62899304_37665469

这个例子是我的出发点:https://webrtc.github.io/samples/src/content/getusermedia/record/ https://webrtc.github.io/samples/src/content/getusermedia/record/


总之

在 Firefox 和 Chrome 中使用它很简单:您只需将音频编解码器添加到编解码器列表即可!video/webm;codecs=opus,vp8

让其在 Safari 中工作要复杂得多。 MediaRecorder 是一项“实验性”功能,必须在开发人员选项下手动启用。一旦启用,Safari 就缺少isTypeSupported方法,所以你需要处理它。最后,无论您向 MediaRecorder 请求什么,Safari 都会always给您一个 MP4 文件 - 该文件无法像 WEBM 那样进行流式传输。这意味着您需要在 JavaScript 中执行转复用以动态转换视频容器格式

如果 Chrome 可以运行,Android 也应该可以运行

iOS 不支持媒体源扩展,所以SourceBufferiOS 上没有定义,整个解决方案将不起作用

原帖

查看您发布的 JSFiddle,我们开始之前有一个快速修复:

  • 您引用一个变量errorMsgElement这是从未定义的。你应该添加一个<div>到具有适当 ID 的页面,然后创建一个const errorMsgElement = document.querySelector(...)线来捕捉它

现在,在使用媒体源扩展和 MediaRecorder 时需要注意的是,每个浏览器的支持会非常不同。尽管这是 HTML5 规范的“标准化”部分,但它在各个平台上并不是非常一致。根据我的经验,让 MediaRecorder 在 Firefox 中工作并不需要太多的努力,让它在 Chrome 中工作有点困难,让它在 Safari 中工作几乎是不可能的,而让它在 iOS 上工作则几乎不可能你可以做的事情。

我在每个浏览器的基础上进行了调试并记录了我的步骤,以便您可以了解在调试媒体问题时可以使用的一些工具

Firefox

当我在 Firefox 中检查 JSFiddle 时,我在控制台中看到以下错误:

NotSupportedError: 无法录制音轨:video/webm;codecs=vp8 表示编解码器不受支持

我记得 VP8 / VP9 是 Google 大力推动的,因此可能无法在 Firefox 中工作,所以我尝试对您的代码进行一个小调整。我删除了, options)您调用的参数new MediaRecorder()。这告诉浏览器使用它想要的任何编解码器,因此您可能会在每个浏览器中得到不同的输出(但它至少应该work在每个浏览器中)

这在 Firefox 中有效,所以我检查了 Chrome。

Chrome

这次我遇到了新的错误:

(索引):409 未捕获(承诺中)DOMException:无法在“SourceBuffer”上执行“appendBuffer”:此 SourceBuffer 已从父媒体源中删除。 在 MediaRecorder.handleDataAvailable (https://fiddle.jshell.net/43rm7258/1/show/:409:22 https://fiddle.jshell.net/43rm7258/1/show/:409:22)

所以我在浏览器中访问 c​​hrome://media-internals/ 并看到了以下内容:

音频流编解码器 opus 与 SourceBuffer 编解码器不匹配。

在代码中,您指定了视频编解码器(VP9 或 VP8),但没有指定音频编解码器,因此 MediaRecorder 让浏览器选择它想要的任何音频编解码器。看起来 Chrome 的 MediaRecorder 默认选择“opus”作为音频编解码器,但 Chrome 的 SourceBuffer 默认选择其他内容。这已经被简单地解决了。我更新了你设置的两行options.mimeType像这样:

  • options = { mimeType: "video/webm;codecs=opus, vp9" };
  • options = { mimeType: "video/webm;codecs=opus, vp8" };

既然你用的是同一个options用于声明 MediaRecorder 和 SourceBuffer 的对象,将音频编解码器添加到列表中意味着 SourceBuffer 现在已使用有效的音频编解码器声明并且视频可以播放

为了更好地衡量,我在 Firefox 上测试了新代码(带有音频编解码器)。这有效!所以我们只需将音频编解码器添加到即可实现 2 for 2options列表(并将其保留在用于声明 MediaRecorder 的参数中)

看起来 VP8 和 opus 在 Firefox 中工作,但不是默认值(尽管与 Chrome 不同,MediaRecorder 和 SourceBuffer 的默认值是相同的,这就是为什么删除options参数完全有效)

Safari

这次我们遇到了一个可能无法解决的错误:

未处理的承诺拒绝:ReferenceError:找不到变量:MediaRecorder

我做的第一件事是谷歌“Safari MediaRecorder”,结果出现了本文 https://blog.addpipe.com/safari-technology-preview-73-adds-limited-mediastream-recorder-api-support/。我想我应该尝试一下,所以我就看了一下。果然:

我单击此按钮来启用 MediaRecorder,并在控制台中遇到以下内容:

未处理的承诺拒绝:TypeError:MediaRecorder.isTypeSupported 不是函数。 (在“MediaRecorder.isTypeSupported(options.mimeType)”中,“MediaRecorder.isTypeSupported”未定义)

所以 Safari 没有isTypeSupported方法。不用担心,我们只会说“如果这个方法不存在,假设它是 Safari 并相应地设置类型”

  if (MediaRecorder.isTypeSupported) {
    options = { mimeType: "video/webm;codecs=vp9" };
    if (!MediaRecorder.isTypeSupported(options.mimeType)) {
      console.error(`${options.mimeType} is not Supported`);
      errorMsgElement.innerHTML = `${options.mimeType} is not Supported`;
      options = { mimeType: "video/webm;codecs=vp8" };
      if (!MediaRecorder.isTypeSupported(options.mimeType)) {
        console.error(`${options.mimeType} is not Supported`);
        errorMsgElement.innerHTML = `${options.mimeType} is not Supported`;
        options = { mimeType: "video/webm" };
        if (!MediaRecorder.isTypeSupported(options.mimeType)) {
          console.error(`${options.mimeType} is not Supported`);
          errorMsgElement.innerHTML = `${options.mimeType} is not Supported`;
          options = { mimeType: "" };
        }
      }
    }
  } else {
    options = { mimeType: "" };
  }

现在我只需要找到 Safari 支持的 mimeType 即可。一些简单的谷歌搜索表明支持 H.264,所以我尝试了:

options = { mimeType: "video/webm;codecs=h264" };

这成功地给了我MediaRecorder started,但失败了addSourceBuffer出现新错误:

NotSupportedError:不支持该操作。

我将继续尝试和诊断如何在 Safari 中使其正常工作,但目前我至少已经解决了 Firefox 和 Chrome 的问题

Update 1

我继续致力于 Safari 的开发。不幸的是,Safari 缺乏 Chrome 和 Firefox 的工具来深入挖掘媒体内部结构,因此涉及很多猜测。

我之前发现我们在尝试调用时收到错误“不支持该操作”addSourceBuffer。因此,我创建了一个一次性页面来尝试在不同情况下调用此方法:

  • 也许之前添加一个源缓冲区play视频中被称为
  • 也许在媒体源附加到视频元素之前添加源缓冲区
  • 也许添加具有不同编解码器的源缓冲区
  • etc

我发现问题仍然是编解码器,并且有关不允许“操作”的错误消息有点误导。这是参数这是不允许的。简单地提供“h264”适用于 MediaRecorder,但 SourceBuffer 需要我传递编解码器参数 https://blog.pearce.org.nz/2013/11/what-does-h264avc1-codecs-parameters.html.

我尝试的第一件事就是前往MDN 示例页面 https://developer.mozilla.org/en-US/docs/Web/API/MediaSource并复制他们在那里使用的编解码器:'video/mp4; codecs="avc1.42E01E, mp4a.40.2"'。这给出了相同的“不允许操作”错误。深入研究这些编解码器参数的含义(就像到底是做什么的)42E01E even mean?)。虽然我希望我有一个更好的答案,但在谷歌搜索时我偶然发现这个 StackOverflow 帖子 https://stackoverflow.com/questions/30388198/stream-hls-on-safari-using-mediasource-api其中提到使用'video/mp4; codecs="avc1.64000d,mp4a.40.2"'在 Safari 上。我尝试了一下,控制台错误消失了!

尽管控制台错误现在已经消失,但我仍然看不到任何视频。所以还有工作要做。

Update 2

在 Safari 中的调试器中进行进一步调查(放置多个断点并在过程的每个步骤检查变量)发现handleDataAvailableSafari 中从未被调用过。它看起来像在 Firefox 和 Chrome 中mediaRecorder.start(100)将正确遵循规范并致电ondatavailable每 100 毫秒一次,但 Safari 会忽略该参数并将所有内容缓冲到一个巨大的 Blob 中。呼唤mediaRecorder.stop()人为造成的ondataavailable调用之前记录的所有内容

我尝试使用setInterval打电话mediaRecorder.requestData()每 100 毫秒一次,但是requestData没有在 Safari 中定义(很像isTypeSupported未定义)。这让我有点陷入困境。

接下来,我尝试清理整个 MediaRecorder 对象并每 100 毫秒创建一个新对象,但这会引发错误await bufferedBlob.arrayBuffer()。我仍在调查为什么失败

Update 3

关于 MP4 格式,我记得的一件事是“moov”原子 https://www.adobe.com/devnet/video/articles/mp4_movie_atom.html需要播放任何内容。这就是为什么您无法下载 MP4 文件的中间部分并播放它。您需要下载整个文件。所以我想知道我选择 MP4 是否是我没有定期更新的原因。

我尝试改变video/mp4一些不同的值并得到不同的结果:

  • video/webm-- 不支持操作
  • video/x-m4v-- 表现得像 MP4,我只有在以下情况下才获取数据.stop()被称为
  • video/3gpp-- 表现得像 MP4
  • video/flv-- 不支持操作
  • video/mpeg-- 表现得像 MP4

所有表现得像 MP4 的东西都让我检查了实际传递到的数据handleDataAvailable。就在那时我注意到了这一点:

不管what我选择了视频格式,Safari 总是给我一个 MP4!

突然我想起了为什么 Safari 是一场噩梦,以及为什么我在心里把它归类为“该死的——几乎不可能”。为了将多个 MP4 拼接在一起,需要 JavaScript 转换器

这时我才想起来,这正是我之前所做的 https://stackoverflow.com/questions/53745450/where-is-pixel-format-stored-in-h-264-mp4-file。一年多前,我使用 MediaRecorder 和 SourceBuffer 尝试创建一个 JavaScript RTMP 播放器。播放器完成后,我想添加对 DVR 的支持(返回已流式传输的视频部分),我通过使用 MediaRecorder 并在 1 秒视频 blob 的内存中保留一个环形缓冲区来实现。在 Safari 上,我通过 Transmuxer 运行这些视频 blob,我已编码将它们从 MP4 转换为 ISO-BMFF,以便我可以将它们连接在一起。

我希望我可以与您分享代码,但它全部属于我的旧雇主 - 所以此时我已经失去了解决方案。我知道有人遇到了使用 emscripten 将 FFMPEG 编译为 JavaScript 的麻烦,所以您也许可以利用它。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

如何使用 MediaRecorder 作为 MediaSource 的相关文章