MediaRecorder 的 Webm blob 生成损坏的视频

Mil*_*bit 7 base64 elixir webm web-mediarecorder

我试图通过 WS 将webm Blobs (Base64 编码并由 生成MediaRecorder)发送到我的 Phoenix 后端,在那里我对它们进行 Base64 解码并将它们附加到文件中。我最终得到的是一个有缺陷的 webm 视频,当我在播放器中打开它时,我可以看到第一帧,但它有点像素化,颜色关闭等。视频长度不存在,当我尝试诊断的错误ffmpeg,它说:

[h264 @ 0x7fcbd3804200] decode_slice_header error
[h264 @ 0x7fcbd3804200] no frame!
[h264 @ 0x7fcbd3804200] non-existing PPS 0 referenced
(repeated many times)
Run Code Online (Sandbox Code Playgroud)

我不确定我在哪里犯了错误(或者我是否犯了错误),这是我的简短客户端代码:

const mediaStream = await navigator.mediaDevices.getUserMedia({ audio:true, video: true });
const options = {
  audioBitsPerSecond: 128000,
  videoBitsPerSecond: 2500000,
  mimeType: 'video/webm'
}
const mediaRecorder = new MediaRecorder(mediaStream,options);
mediaRecorder.ondataavailable = ((e) => {
  var reader = new window.FileReader();
  reader.readAsDataURL(e.data);
  reader.onloadend = (() => {
    base64data = reader.result;
    topic.push('video_feed', {data: base64data});
  })
})
mediaRecorder.start(1500);
Run Code Online (Sandbox Code Playgroud)

我的后端 Elixir 代码负责文件 IO 和 Base64 解码:

{:ok, io} = File.open("some_file.webm", [:binary, :append]) # This part is triggered once
# Everything below is triggered upon receiving a blob (feed is the base64data object from 
# clientside code above)
"data:video/x-matroska;codecs=avc1,opus;base64," <> base64_bit = feed
decode_res = Base.decode64(base64_bit)
case decode_res do
  :error -> some_error_handling_code
  {:ok, data} -> io |> IO.binwrite(data)
end
{:noreply, state}
Run Code Online (Sandbox Code Playgroud)

我是否以某种方式弄乱了文件 IO?我怀疑我以某种方式丢失了媒体播放器所需的一些标头或文件元数据。

Mil*_*bit 6

好吧,经过2天的挖掘和调试webm,我意识到这是一个客户端问题(与代码相关)。

有一个竞争条件,我在调用后立即从客户端关闭了套接字mediaRecorder.stop()。这是一个错误,因为我忘记了在这种情况下总是会与其余数据一起发出最终事件(最后一次发射时间和停止记录器的时间之间的块)一起发出。该事件从未发送,因为我立即以编程方式关闭了我的选项卡(明智的,我知道),所以我的视频格式错误。我还有一些其他与代码相关的问题,但没有这么大。

确保您收到最终事件,类似于:

topic.push('feed', {data: base64data})
  .receive('ok', () => {
    // MR state will become inactive as soon as you call .stop() method
    if (mediaRecorder.state === 'inactive') {
      x.push('that_was_the_final_chunk', {}).receive('ok' => {
        nowWeCanSafelyTerminate();
      })
    }
  })
}
Run Code Online (Sandbox Code Playgroud)