繁体   English   中英

如何将 h264 编码的 MediaRecorder stream 传递给 Chrome 中的 MediaSource?

[英]How to pass a h264 encoded MediaRecorder stream to a MediaSource in Chrome?

我们的屏幕录制 chrome 扩展允许用户使用getDisplayMedia API 记录他们的屏幕,它返回一个 stream,该 stream 被输入MediaRecorder API。

通常,我们会使用带有较新 vp9 编解码器的 webm 视频容器来录制 stream,如下所示:

const mediaRecorder = new MediaRecorder(mediaStream, {
    mimeType: "video/webm; codecs=vp9"
  });

但是Safari不支持webm容器,也不支持解码vp9编解码器。 由于 Chrome 中的 MediaRecorder API 仅支持在 webm 容器中录制,但确实支持 h264 编码(Safari 可以解码),因此我们在 webm 容器中使用 h264 编解码器进行录制:

const mediaRecorder = new MediaRecorder(mediaStream, {
    mimeType: "video/webm; codecs=h264"
  });

这很好用有两个原因:

  1. 由于我们的录音应用是 chrome 扩展,我们不介意它只能在 Chrome 中录音

  2. 由于视频数据被编码为 h264,我们现在几乎可以立即将视频数据移动到 a.mp4 容器中,从而允许 Safari 观众观看这些录制的视频,而无需等待昂贵的转码过程(请注意,您无需chrome 扩展,在常规 web 应用程序中)

但是,由于媒体记录器 API 无法获取到目前为止录制的视频 stream 的持续时间,并且使用performance.now手动测量它。现在证明是不精确的(有 25ms 到 150ms 的误差),我们不得不更改为馈送记录器数据到 MediaSource 中,这样我们就可以使用mediaSourceBuffer.buffered.end(sourceBuffer.buffered.length - 1) * 1000 API 来 100% 准确地读取到目前为止记录的视频 stream 持续时间(以毫秒为单位)。

问题是,由于某种原因,当我们使用“video/webm;codecs=h264”mime 类型时,MediaSource 无法实例化。

这样做:

mediaSourceBuffer = mediaSource.addSourceBuffer("video/webm; codecs=h264");

结果是:

Failed to execute 'addSourceBuffer' on 'MediaSource': The type provided ('video/webm; codecs=h264') is unsupported.

为什么 MediaRecorder 支持 mime 类型,但 MediaSource 不支持? 由于它们属于同一个 API 系列,它们不应该支持相同的 mime 类型吗? 在使用 addSourceBuffer 将数据传递到 MediaSource 时,我们如何使用 h264 编解码器进行录制?

到目前为止,我们能想到的唯一解决方案是创建 2 个媒体记录器,一个在 vp9 中录制,以便我们使用buffered.end API 读取迄今为止录制的视频的准确持续时间,在 h264 中录制一个以便我们能够立即将视频数据移动到 mp4 容器,而无需为 Safari 用户将编解码器从 vp9 转码为 h264。 但是,这将是非常低效的,因为它将有效地在 RAM 中保存两倍的数据。

复制案例/codeandbox示例

  1. vp9 示例(均有效)
  2. h264 示例(媒体记录器有效,媒体源无效)

解码器和编码器完全是不同的野兽。 例如,Webkit (Safari) 可以解码几种格式,但它不能编码任何东西。

此外,MediaSource API 要求传递给它的媒体可以是分段的,因此无法读取浏览器可以解码的所有媒体,例如,如果有一天某个浏览器支持生成标准(非分段)mp4 文件,那么它们仍然无法将其传递给 MediaSource API。

我不能确定他们是否可以支持这个特定的编解码器(我猜是的),但你甚至可能根本不需要所有的解决方法。

如果您的扩展程序能够生成 DOM 元素,那么您可以简单地使用<video>元素来告诉您录制视频的持续时间,使用此答案中描述的技巧:

将视频的currentTime设置为一个非常大的数字,等待搜索的事件,您将获得正确duration seeked

 const canvas_stream = getCanvasStream(); const rec = new MediaRecorder( canvas_stream.stream ); const chunks = []; rec.ondataavailable = (evt) => chunks.push( evt.data ); rec.onstop = async (evt) => { canvas_stream.stop(); console.log( "duration:", await measureDuration( chunks ) ); }; rec.start(); setTimeout( () => rec.stop(), 5000 ); console.log( 'Recording 5s' ); function measureDuration( chunks ) { const blob = new Blob( chunks, { type: "video/webm" } ); const vid = document.createElement( 'video' ); return new Promise( (res, rej) => { vid.onerror = rej; vid.onseeked = (evt) => res( vid.duration ); vid.onloadedmetadata = (evt) => { URL.revokeObjectURL( vid.src ); // for demo only, to show it's Infinity in Chrome console.log( 'before seek', vid.duration ); }; vid.src = URL.createObjectURL( blob ); vid.currentTime = 1e10; } ); } // just so we can have a MediaStream in StackSnippet function getCanvasStream() { const canvas = document.createElement( 'canvas' ); const ctx = canvas.getContext( '2d' ); let stopped = false; function draw() { ctx.fillRect( 0,0,1,1 ); if(;stopped ) { requestAnimationFrame( draw ); } } draw(): return { stream. canvas,captureStream(): stop; () => stopped = true }; }

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM