Javascript 无法使用带有socket.io的媒体源从媒体记录器跳入流
以下代码在先加载视频观察客户端,然后再加载网络摄像头客户端时工作正常。但是,如果切换顺序或以任何方式中断流(例如刷新任一客户端),流将失败,媒体源将将其就绪状态更改为关闭 我的假设是,在启动时接收的视频需要初始化头来启动,因为流是在中间读取的,所以它永远不会得到初始化头。我甚至不知道如何将这样的头添加到webm文件中 我试图改变源缓冲区上的序列模式,但没有任何效果。我已经尝试重新启动录像机,这是可行的,但我的最终计划是有多个观察客户端,录像机在每次重新连接时重新启动不是最佳的 摄像机客户端Javascript 无法使用带有socket.io的媒体源从媒体记录器跳入流,javascript,socket.io,video-streaming,media-source,web-mediarecorder,Javascript,Socket.io,Video Streaming,Media Source,Web Mediarecorder,以下代码在先加载视频观察客户端,然后再加载网络摄像头客户端时工作正常。但是,如果切换顺序或以任何方式中断流(例如刷新任一客户端),流将失败,媒体源将将其就绪状态更改为关闭 我的假设是,在启动时接收的视频需要初始化头来启动,因为流是在中间读取的,所以它永远不会得到初始化头。我甚至不知道如何将这样的头添加到webm文件中 我试图改变源缓冲区上的序列模式,但没有任何效果。我已经尝试重新启动录像机,这是可行的,但我的最终计划是有多个观察客户端,录像机在每次重新连接时重新启动不是最佳的 摄像机客户端 ma
main();
function main() {
if (hasGetUserMedia()) {
const constraints = {
video: {
facingMode: 'environment',
frameRate: {
ideal: 10,
max: 15
}
},
audio: true
};
navigator.mediaDevices.getUserMedia(constraints).
then(stream => {
setupRecorder(stream);
});
}
}
function setupRecorder(stream) {
let mediaRecorder = new MediaRecorder(stream, {
mimeType: 'video/webm; codecs="opus, vp9"'
});
mediaRecorder.ondataavailable = e => {
var blob = e.data;
socket.emit('video', blob);
}
mediaRecorder.start(500);
}
服务器只是广播接收到的任何内容
观察客户
var sourceBuffer;
var queue = [];
var mediaSource = new MediaSource();
mediaSource.addEventListener('sourceopen', sourceOpen, false);
main();
socket.on('stream', data => {
if (mediaSource.readyState == "open") {
if (sourceBuffer.updating || queue.length > 0) {
queue.push(data.video);
} else {
sourceBuffer.appendBuffer(data.video);
}
}
});
function main() {
videoElement = document.querySelector('#video');
videoElement.src = URL.createObjectURL(mediaSource);
}
function sourceOpen(e) {
console.log('open');
sourceBuffer = mediaSource.addSourceBuffer('video/webm; codecs="opus, vp9"');
sourceBuffer.addEventListener('updateend', () => {
console.log(sourceBuffer.updating, mediaSource.readyState);
if (queue.length > 0 && !sourceBuffer.updating) {
sourceBuffer.appendBuffer(queue.shift());
}
});
}
因此,实际上,代码的工作方式是不正确的,因此套接字发送服务器没有问题。它或者与MediaRecorder或者MediaSource有关
我的假设是,在启动时接收的视频需要初始化头来启动,因为流是在中间读取的,所以它永远不会得到初始化头
对
要解决这个问题,您需要了解一点WebM格式。WebM只是Matroska(MKV)的一个子集。是在EBML中存储媒体的模式规范。是一种二进制文件格式,可包含任意块。将其视为二进制XML
这意味着您可以使用诸如检查WebM文件之类的工具,并参考Matroska规范来了解发生了什么。例如:
这是对预先录制的WebM文件的检查。它将在浏览器中运行良好。您会注意到有些元素是嵌套的
在每个WebM文件中都有两个顶级元素EBML
,它定义了这个二进制文件,以及Segment
,它包含了后面的所有内容
在段中
有几个元素对您很重要。其中之一是曲目
。您会注意到该文件有两个曲目,一个用于Opus中的音频,另一个用于VP9中的视频。另一个重要的块是Info
,它包含有关时间刻度的信息和有关muxer的一些元数据
在所有这些元数据之后,您可以找到Cluster
、Cluster
、Cluster
等这些位置可以剪切WebM流,前提是每个簇
都以关键帧开始
换句话说,您的代码应该执行以下操作:
- 将第一个
群集之前的所有数据保存为“初始化数据”
- 然后在
群集上拆分
- 使用之前保存的“初始化数据”作为加载的第一件事
- 在
s中开始加载,然后在流中的任意位置开始加载Cluster
我要指出的是,你甚至不需要MediaSource。您肯定不需要Socket.IO。它可以像通过普通HTTP流输出数据一样简单。这可以直接在
元素中加载。(如果您想获得更多控制权,请务必使用MediaSource,但这不是必须的。)感谢您的回复,Brad,这非常激动人心。我确实尝试过将我的视频加载到视频元素中,但这并不是无缝的导致我使用MediaSource,但我将尝试您推荐的内容。还有一个问题是,我该如何提取元数据来解析集群?@JustinFernald您必须在代码中解析WebM。这在JavaScript中是完全可行的。WebM很容易解码。我想上一次我这么做的时候,我最终写了我自己的demuxer,但看起来NPM上至少有一个软件包可能是你的起点:真的,不过,花一个下午的时间和Matroska规范和十六进制编辑器在一起。一旦你得到它,它就相当简单了!老实说,我一直在研究FFMPEG、Dash和Matrosta,但我离我的目标还远着呢。我对如何将视频发送到FFMPEG,然后接收输出并发送给观察者感到非常困惑。我最初认为,如果我只发送初始化段,然后发送数据,它就会工作,但是,在一些测试之后,流中的任何中断都会破坏流。我知道这有点奇怪,但是布拉德,我很想有机会和你谈谈,好好想想你的想法,因为我对这一切都很陌生。文档帮助还不够。@JustinFernald当然,给我发封电子邮件到brad@audiopump.co当你有空的时候。我可以聘请顾问,但也很乐意免费简短地聊天,看看我是否能为您指出正确的方向。@AdamMarsh我不知道任何现成的demuxer。EBML查看器会向您发送该错误消息,因为您的文件是用于流式传输的,这意味着它的长度不确定。上面的示例只是为了显示这些类型的文件中包含的内容。您仍然需要编写解复用器并使用十六进制编辑器进行调试。你真的只需要在第一个集群上进行拆分。。。所以0x1f43b675
。。。当您看到这一点时,就有了集群的开始。在此之前的一切本质上都是初始化数据。