And*_*dré 13 html javascript webrtc
作为学习 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);
}
}
}
}
Run Code Online (Sandbox Code Playgroud)
// 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}`);
}
Run Code Online (Sandbox Code Playgroud)
所有代码:https : //jsfiddle.net/43rm7258/1/
当我在 Chromium 78 中运行它时,我NotSupportedError: Failed to load because no supported source was found.从play视频元素的元素中得到一个。
我不知道我做错了什么或此时如何继续。
这是关于类似的事情,但对我没有帮助:MediaSource随机停止视频
这个例子是我的起点:https : //webrtc.github.io/samples/src/content/getusermedia/record/
ste*_*esu 27
让它在 Firefox 和 Chrome 中工作很容易:你只需要在你的编解码器列表中添加一个音频编解码器! video/webm;codecs=opus,vp8
让它在 Safari 中工作要复杂得多。MediaRecorder 是一项“实验性”功能,必须在开发人员选项下手动启用。启用后,Safari 缺少isTypeSupported方法,因此您需要处理它。最后,无论您从 MediaRecorder 请求什么,Safari 都会始终为您提供 MP4 文件 - 无法像 WEBM 那样流式传输。这意味着您需要在 JavaScript 中执行转换以动态转换视频容器格式
如果 Chrome 可以工作,Android 应该可以工作
iOS 不支持媒体源扩展,因此SourceBuffer在 iOS 上没有定义,整个解决方案将不起作用
查看您发布的 JSFiddle,在我们开始之前的一个快速修复:
errorMsgElement从未定义的变量。您应该<div>使用适当的 ID 向页面添加一个,然后创建const errorMsgElement = document.querySelector(...)一行来捕获它现在在使用 Media Source Extensions 和 MediaRecorder 时需要注意的是,每个浏览器的支持将大不相同。尽管这是 HTML5 规范的“标准化”部分,但它在跨平台上并不是很一致。根据我的经验,让 MediaRecorder 在 Firefox 中工作并不需要太多努力,让它在 Chrome 中工作有点困难,让它在 Safari 中工作几乎是不可能的,而让它在 iOS 上工作实际上不是你可以做的事情。
我在每个浏览器的基础上进行了调试并记录了我的步骤,以便您在调试媒体问题时可以了解一些可用的工具
当我在 Firefox 中检查您的 JSFiddle 时,我在控制台中看到以下错误:
NotSupportedError:无法录制音轨:video/webm;codecs=vp8 表示不支持的编解码器
我记得 VP8 / VP9 是 Google 大力推动的,因此可能无法在 Firefox 中运行,所以我尝试对您的代码进行一些小调整。我, options)从您对new MediaRecorder(). 这告诉浏览器使用它想要的任何编解码器,所以你可能会在每个浏览器中得到不同的输出(但它至少应该在每个浏览器中工作)
这在 Firefox 中有效,所以我检查了 Chrome。
这次我遇到了一个新错误:
(index):409 Uncaught (in promise) DOMException: Failed to execute 'appendBuffer' on 'SourceBuffer': 此 SourceBuffer 已从父媒体源中删除。在 MediaRecorder.handleDataAvailable ( 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 上测试了新代码(使用音频编解码器)。这有效!所以我们只是通过将音频编解码器添加到options列表中(并将其保留在用于声明 MediaRecorder 的参数中)来实现 2 对 2
看起来 VP8 和 opus 在 Firefox 中工作,但不是默认值(尽管与 Chrome 不同,MediaRecorder 和 SourceBuffer 的默认值是相同的,这就是删除options参数完全有效的原因)
这次我们遇到了一个我们可能无法解决的错误:
未处理的承诺拒绝:ReferenceError:找不到变量:MediaRecorder
我做的第一件事是谷歌“Safari MediaRecorder”,它打开了这篇文章。我想我会试一试,所以我看了一下。果然:
我点击它以启用 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: "" };
}
Run Code Online (Sandbox Code Playgroud)
现在我只需要找到 Safari 支持的 mimeType。一些轻微的谷歌搜索表明支持 H.264,所以我试过:
options = { mimeType: "video/webm;codecs=h264" };
Run Code Online (Sandbox Code Playgroud)
这成功地给了我MediaRecorder started,但在addSourceBuffer出现新错误的行中失败了:
NotSupportedError:不支持该操作。
我将继续尝试并诊断如何在 Safari 中使用它,但现在我至少已经解决了 Firefox 和 Chrome
我继续在 Safari 上工作。不幸的是,Safari 缺乏 Chrome 和 Firefox 的工具来深入挖掘媒体内部结构,因此涉及到很多猜测。
我之前发现我们在尝试调用addSourceBuffer. 所以我创建了一个一次性页面来尝试在不同情况下调用这个方法:
play在视频上调用之前添加一个源缓冲区我发现问题仍然是编解码器,并且关于不允许“操作”的错误消息有点误导。这是不允许的参数。简单地提供“h264”适用于 MediaRecorder,但 SourceBuffer 需要我传递编解码器参数。
我尝试的第一件事是前往MDN 示例页面并复制他们在那里使用的编解码器:'video/mp4; codecs="avc1.42E01E, mp4a.40.2"'. 这给出了相同的“不允许操作”错误。挖掘到这些编解码器参数的含义(像这到底是什么42E01E,即使意思?)。虽然我希望我有一个更好的答案,但在谷歌搜索时,我偶然发现了这篇 StackOverflow 帖子,其中提到了'video/mp4; codecs="avc1.64000d,mp4a.40.2"'在 Safari 上使用。我试了一下,控制台错误消失了!
尽管控制台错误现在消失了,但我仍然没有看到任何视频。所以还有工作要做。
在 Safari 中的调试器中进行进一步调查(在流程的每个步骤放置多个断点并检查变量)发现handleDataAvailableSafari 中从未调用过。看起来在 Firefox 和 Chrome 中mediaRecorder.start(100)会正确地遵循规范并ondatavailable每 100 毫秒调用一次,但 Safari 会忽略该参数并将所有内容缓冲到一个巨大的 Blob 中。mediaRecorder.stop()手动调用导致调用ondataavailable之前记录的所有内容
我尝试使用每 100 毫秒setInterval调用mediaRecorder.requestData()一次,但未requestData在 Safari 中定义(很像如何isTypeSupported未定义)。这让我有些束手无策。
接下来,我尝试清理整个 MediaRecorder 对象并每 100 毫秒创建一个新对象,但这在await bufferedBlob.arrayBuffer(). 我仍在调查为什么那个失败了
我记得关于 MP4 格式的一件事是,播放任何内容都需要“moov”原子。这就是为什么您不能下载 MP4 文件的中间部分并播放它的原因。您需要下载整个文件。所以我想知道我选择 MP4 的事实是否是我没有定期更新的原因。
我尝试更改video/mp4为几个不同的值并得到不同的结果:
video/webm -- 不支持操作video/x-m4v-- 表现得像 MP4,我只在.stop()被调用时获取数据video/3gpp -- 表现得像 MP4video/flv -- 不支持操作video/mpeg -- 表现得像 MP4一切都像 MP4 一样,让我检查了实际传递给handleDataAvailable. 那时我注意到了这一点:
无论我选择哪种视频格式,Safari 总是给我一个 MP4!
突然我想起了为什么 Safari 是一场噩梦,以及为什么我在精神上将它归类为“该死的几乎不可能”。为了将多个 MP4 拼接在一起,需要一个 JavaScript 转换器
那时我才想起来,这正是我之前所做的。一年多前,我与 MediaRecorder 和 SourceBuffer 合作,尝试创建一个 JavaScript RTMP 播放器。播放器完成后,我想添加对 DVR 的支持(寻找已经流式传输的视频部分),我通过使用 MediaRecorder 并在 1 秒视频 blob 的内存中保留一个环形缓冲区来做到这一点。在 Safari 上,我通过我编码的多路复用器运行这些视频 blob,将它们从 MP4 转换为 ISO-BMFF,这样我就可以将它们连接在一起。
我希望我能与你分享代码,但它全部归我的老雇主所有——所以此时我已经失去了解决方案。我知道有人遇到了使用 emscripten 将 FFMPEG 编译为 JavaScript 的麻烦,因此您可以利用这一点。