MFCreateFMPEG4MediaSink 不生成 MSE 兼容的 MP4

MFCreateFMPEG4MediaSink does not generate MSE-compatible MP4

我正在尝试将 H.264 视频源流式传输到网络浏览器。 Media Foundation 用于编码分段的 MPEG4 流(MFCreateFMPEG4MediaSink 启用 MFTranscodeContainerType_FMPEG4MF_LOW_LATENCYMF_READWRITE_ENABLE_HARDWARE_TRANSFORMS)。然后流通过 IMFByteStream.

连接到 Web 服务器

H.264 视频流在被 <video src=".."/> 标签使用时工作正常。但是,由此产生的延迟约为 2 秒,这对于相关应用程序来说太长了。我怀疑客户端缓冲会导致大部分延迟。因此,我正在尝试使用媒体源扩展 (MSE) 对浏览器内流进行编程控制。然而,Chrome 在通过 MSE 使用相同的 MPEG4 流时确实失败并出现以下错误:

Failure parsing MP4: TFHD base-data-offset not allowed by MSE. See https://www.w3.org/TR/mse-byte-stream-format-isobmff/#movie-fragment-relative-addressing

MPEG4 流中 moof/mdat 片段的 mp4dump。这清楚地表明 TFHD 包含一个“非法”base data offset 参数:

[moof] size=8+200
  [mfhd] size=12+4
    sequence number = 3
  [traf] size=8+176
    [tfhd] size=12+16, flags=1
      track ID = 1
      base data offset = 36690
    [trun] size=12+136, version=1, flags=f01
      sample count = 8
      data offset = 0
[mdat] size=8+1624

我在 Win10 版本 1709 (16299.309) 上使用 Chrome 65.0.3325.181(官方构建)(32 位),运行。

是否有任何方法可以使用 Media Foundation 生成与 MSE 兼容的 H.264/MPEG4 视频流?

状态更新:

根据roman-r的建议,我设法通过拦截生成的 MPEG4 流并执行以下修改自行解决了问题:

  • Modify Track Fragment Header Box (tfhd):
    • remove base_data_offset parameter (reduces stream size by 8bytes)
    • set default-base-is-moof flag
  • Add missing Track Fragment Decode Time (tfdt) (increases stream size by 20bytes)
    • set baseMediaDecodeTime parameter
  • Modify Track fragment Run box (trun):
    • adjust data_offset parameter

字段说明记录在 https://www.iso.org/standard/68960.html(免费下载)中。

切换到基于 MSE 的视频流将延迟从约 2.0 秒减少到 0.7 秒。通过在每次 IMFSinkWriter::WriteSample 调用后调用 IMFSinkWriter::NotifyEndOfSegment,延迟进一步减少到 0-1 帧。

https://github.com/forderud/AppWebStream

上有一个示例实现

按照 roman-r 的建议并修改生成的 MPEG4 流,问题已解决。请参阅上面的答案。

另一种方法是再次使用@Fredrik 提到的相同代码,但我编写了自己的 IMFByteStream 并检查了写入 IMFByteStream 的块。 FFMpeg 几乎一次写入原子。所以你可以检查原子名称并进行修改。这是一回事。我希望有一个符合 MSE 标准的 windows 坠子。

有没有可以为 HLS 生成 .ts 文件的软件?

提到的 0.7 秒延迟(在您的 状态更新 中)是由媒体基金会的 MFTranscodeContainerType_FMPEG4 containterizer 造成的,它大约每 1/3 秒收集并输出一次(来自未知原因)在一个 MP4 moof/mdat 框对中的帧数。这意味着您需要等待 19 帧才能以 60 FPS 从 MFTranscodeContainerType_FMPEG4 获得任何输出。

要每帧输出单个 MP4 moof/mdat,只需说 MF_MT_FRAME_RATE 是 1 FPS(或高于 1/3 秒的任何值)。要以正确的速度播放视频,请使用 Media Source Extensions 的 <video>.playbackRate 或更新 MP4 中 mvhdmdhd 框的 timescale(即乘以实际 FPS)流拦截器以获取正确定时的 MP4 流。

这样做,延迟可以压缩到 20 毫秒以下。当您在 Unity(研究)-> NvEnc -> MFTranscodeContainerType_FMPEG4 -> WebSocket -> Chrome 媒体源扩展显示等链中并排看到 localhost 上的输出时,这几乎无法识别.

请注意,MFTranscodeContainerType_FMPEG4 仍会引入 1 帧延迟(第 1 帧输入,无输出,第 2 帧输入,第 1 帧输出,...),因此在 60 FPS 时有 20 毫秒的延迟。唯一的解决方案似乎是编写自己的 FMPEG4 容器化程序。但这比拦截 Media Foundation 的 MP4 流要复杂得多。

尝试通过 MSE 播放 fmp4 时,我遇到了同样的错误(解析 MP4 失败:MSE 不允许 TFHD 基础数据偏移量)。 fmp4 是使用以下 ffmpeg 命令从 mp4 创建的:

ffmpeg -i myvideo.mp4 -g 52 -vcodec copy -f mp4 -movflags frag_keyframe+empty_moov myfmp4video.mp4

基于这个问题,我发现要让 fmp4 在 Chrome 中工作,我必须添加“default_base_moof”标志.因此,在使用以下命令创建 fmp4 之后:

ffmpeg -i myvideo.mp4 -g 52 -vcodec copy -f mp4 -movflags frag_keyframe+empty_moov+default_base_moof myfmp4video.mp4

我能够使用媒体源扩展成功播放视频。

这篇 Mozilla 文章有助于找出丢失的标志: https://developer.mozilla.org/en-US/docs/Web/API/Media_Source_Extensions_API/Transcoding_assets_for_MSE