总之
在 Firefox 和 Chrome 中使用它很简单:您只需将音频编解码器添加到编解码器列表即可!video/webm;codecs=opus,vp8
让其在 Safari 中工作要复杂得多。 MediaRecorder 是一项“实验性”功能,必须在开发人员选项下手动启用。一旦启用,Safari 就缺少isTypeSupported
方法,所以你需要处理它。最后,无论您向 MediaRecorder 请求什么,Safari 都会always给您一个 MP4 文件 - 该文件无法像 WEBM 那样进行流式传输。这意味着您需要在 JavaScript 中执行转复用以动态转换视频容器格式
如果 Chrome 可以运行,Android 也应该可以运行
iOS 不支持媒体源扩展,所以SourceBuffer
iOS 上没有定义,整个解决方案将不起作用
原帖
查看您发布的 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)
所以我在浏览器中访问 chrome://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 中的调试器中进行进一步调查(放置多个断点并在过程的每个步骤检查变量)发现handleDataAvailable
Safari 中从未被调用过。它看起来像在 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 的麻烦,所以您也许可以利用它。