[作为学习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/
[当我在Chromium 78中运行时,我从视频元素的NotSupportedError: Failed to load because no supported source was found.
元素中获得了play
。
我现在不知道我在做什么错或怎么做。
这是类似的内容,但对我没有帮助:MediaSource randomly stops video
此示例是我的起点: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不支持媒体源扩展,因此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;编解码器= vp8表示不支持的编解码器
我记得VP8 / VP9是Google大力推动的,因此可能无法在Firefox中使用,因此我尝试对您的代码进行一些小的调整。我从您对, options)
的调用中删除了new MediaRecorder()
参数。这告诉浏览器使用它想要的任何编解码器,因此您可能会在每个浏览器中获得不同的输出(但在每个浏览器中它至少应<<< [work)
这次我遇到新的错误:
(index):409未捕获(承诺)DOMException:无法在'SourceBuffer'上执行'appendBuffer':此SourceBuffer已从父媒体源中删除。在MediaRecorder.handleDataAvailable(https://fiddle.jshell.net/43rm7258/1/show/:409:22)
所以我转到浏览器中的chrome:// media-internals /并看到了:[音频流编解码器作品与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
参数的原因]Safari
这次我们遇到一个错误,可能无法解决:
未处理的承诺拒绝:ReferenceError:找不到变量:MediaRecorder
[我做的第一件事是Google“ Safari MediaRecorder”,它显示为this article。我以为可以尝试一下,所以看了一下。果然:我单击此按钮以启用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的问题更新1
我继续在Safari上工作。不幸的是,Safari缺少Chrome和Firefox来深入研究媒体内部的工具,因此涉及很多猜测工作。
我以前发现,尝试调用
addSourceBuffer
时出现错误“不支持该操作”。因此,我创建了一个一次性页面,尝试在不同情况下仅调用此方法:也许在视频上调用
play
之前添加源缓冲区也许在将媒体源附加到视频元素之前添加源缓冲区
- 也许添加带有不同编解码器的源缓冲区
- 等
我发现问题仍然是编解码器,并且关于“操作”未被允许的错误消息略有误导性。不允许使用 [我尝试做的第一件事就是前往MDN sample page,并复制他们在那里使用的编解码器: parameters >>] >>。只需提供“ h264”即可用于MediaRecorder,但是SourceBuffer需要我传递codec parameters。
'video/mp4; codecs="avc1.42E01E, mp4a.40.2"'
。这给出了相同的“不允许操作”错误。深入研究这些编解码器参数的含义(例如42E01E
甚至mean
到底会做什么)。虽然我希望有一个更好的答案,但在进行谷歌搜索时,我偶然发现了this StackOverflow post,其中提到在Safari中使用'video/mp4; codecs="avc1.64000d,mp4a.40.2"'
。我尝试了一下,控制台错误消失了!尽管现在控制台错误已消失,但我仍然看不到任何视频。因此,仍有工作要做。更新2[在Safari中的调试器中进行的进一步调查(在过程的每个步骤中放置多个断点并检查变量)发现,从未在Safari中调用
handleDataAvailable
。看起来在Firefox和Chrome中,mediaRecorder.start(100)
会正确遵循规范并每100毫秒调用一次ondatavailable
,但Safari会忽略该参数,并将所有内容缓冲到一个庞大的Blob中。手动调用mediaRecorder.stop()
会导致调用ondataavailable
并记录到那时为止的所有内容我尝试使用
setInterval
每100毫秒调用一次mediaRecorder.requestData()
,但是requestData
在Safari中没有定义(很像isTypeSupported
的定义)。这使我陷入了困境。[下一步,我尝试清理整个MediaRecorder对象,并每100毫秒创建一个新对象,但这在
await bufferedBlob.arrayBuffer()
行上引发了错误。我仍在调查为什么那个失败了更新3
我记得有关MP4格式的一件事是,要播放任何内容,都需要the "moov" atom。这就是为什么您无法下载MP4文件的中半部分并播放它的原因。您需要下载整个文件。所以我想知道我选择MP4的事实是否是我没有定期更新的原因。
我尝试将
video/mp4
更改为几个不同的值,并得到了不同的结果:[
video/webm
-不支持操作[
,我都选择了视频格式,Safari始终为我提供MP4!突然,我想起了为什么Safari是一场噩梦,为什么我在精神上将其归类为“该死的,几乎不可能”。为了将多个MP4缝合在一起,需要使用JavaScript Transmuxer那是我想起的,that's exactly what I had done before。一年多以前,我曾与MediaRecorder和SourceBuffer一起尝试创建JavaScript RTMP播放器。播放器完成后,我想添加对DVR的支持(回溯到已经流传输的视频部分),这是通过使用MediaRecorder并将环形缓冲区保留在1秒的视频Blob内存中来实现的。在Safari上,我通过编码的多路复用器运行了这些视频blob,将它们从MP4转换为ISO-BMFF,以便可以将它们连接在一起。video/x-m4v
-行为类似于MP4,只有在调用.stop()
时才得到数据video/3gpp
-表现得像MP4- [
video/flv
-不支持操作video/mpeg
-表现得像MP4- [像MP4一样,一切使我检查了实际上传递给
handleDataAvailable
的数据。那是我注意到的时候:无论
什么
我希望我可以与您共享代码,但是这些代码都归我的旧雇主所有-因此,现在解决方案对我来说已经丢失了。我知道有人在使用emscripten将FFMPEG编译为JavaScript时遇到了麻烦,因此您可以利用它。