gpt4 book ai didi

javascript - 如何将 h264 编码的 MediaRecorder 流传递给 Chrome 中的 MediaSource?

转载 作者:行者123 更新时间:2023-12-04 04:09:12 29 4
gpt4 key购买 nike

我们的屏幕录制 chrome 扩展允许用户使用 getDisplayMedia 录制他们的屏幕API,它返回一个输入 MediaRecorder 的流API。
通常,我们会使用带有较新 vp9 编解码器的 webm 视频容器来记录此流,如下所示:

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"
});
这很好用有两个原因:
  • 由于我们的录音应用是 chrome 扩展,我们不介意它只能在 Chrome 中录音
  • 由于视频数据被编码为 h264,我们现在几乎可以立即将视频数据移动到 .mp4 容器中,允许 Safari 浏览器观看这些录制的视频,而无需等待昂贵的转码过程(请注意,您无需chrome 扩展,在常规网络应用程序中)

  • 但是,由于媒体记录器 API 没有方法来获取到目前为止录制的视频流的持续时间,并使用 performance.now 手动测量它。被证明是不精确的(有 25 毫秒到 150 毫秒的错误),我们不得不更改为将记录器数据输入 MediaSource 以便我们可以使用 mediaSourceBuffer.buffered.end(sourceBuffer.buffered.length - 1) * 1000 API 以 100% 准确读取迄今为止记录的视频流持续时间(以毫秒为单位)。
    问题是,由于某种原因,当我们使用“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示例
  • vp9 example (两者都有效)
  • h264 example (媒体记录器有效,媒体源无效)
  • 最佳答案

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

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

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

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

    设置currentTime视频的数量很大,等待seeked事件,你会得到正确的duration .

    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
    };
    }

    关于javascript - 如何将 h264 编码的 MediaRecorder 流传递给 Chrome 中的 MediaSource?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/62032820/

    29 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com