【问题标题】:MFCreateFMPEG4MediaSink does not generate MSE-compatible MP4MFCreateFMPEG4MediaSink 不生成与 MSE 兼容的 MP4
【发布时间】:2018-03-22 13:28:43
【问题描述】:

我正在尝试将 H.264 视频源流式传输到网络浏览器。媒体基础用于编码分段的 MPEG4 流(MFCreateFMPEG4MediaSink 启用了MFTranscodeContainerType_FMPEG4MF_LOW_LATENCYMF_READWRITE_ENABLE_HARDWARE_TRANSFORMS)。然后流通过IMFByteStream 连接到网络服务器。

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

解析 MP4 失败:MSE 不允许 TFHD 基础数据偏移。看 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

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

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

状态更新:

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

  • 修改Track Fragment Header Box (tfhd):
    • 删除base_data_offset参数(将流大小减少8字节)
    • 设置default-base-is-moof标志
  • 添加缺少的跟踪片段解码时间 (tfdt)(将流大小增加 20 字节)
    • 设置baseMediaDecodeTime参数
  • 修改Track fragment Run框(trun):
    • 调整data_offset参数

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

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

https://github.com/forderud/AppWebStream 上有一个示例实现

【问题讨论】:

  • 我不知道改变行为的方法,但是我过去曾通过对生成的字节流进行后处理和创建备用媒体接收器来解决此问题。尽管这不是您要确切询问的内容,但两条(两者中的任何一条)路径都会导致来自 Media Foundation 管道的 MSE 友好输出。
  • 遗憾的是,后处理不是一个选项,因为流式传输的是“实时”视频源。但是,另一种媒体接收器可能值得研究。您能否提供更多详细信息和/或指出一些示例代码的方向?
  • 我没有任何可以分享的代码,很抱歉。后处理(是的,我这样做是为了实时低延迟提要)正在拦截字节流,将其解析为原子并重新组合回来。一般来说,这是可行的。自定义媒体接收器是一种直接的开发,它取代了库存接收器。 MF 因样本不多而臭名昭著,但也许您可以找到一些媒体接收器样本作为起点。 Win SDK 7.x 中的 Wavsink 示例可能是一个很好的例子。

标签: mp4 h.264 ms-media-foundation mse


【解决方案1】:

提到的 0.7 秒延迟(在您的状态更新中)是由 Media Foundation 的 MFTranscodeContainerType_FMPEG4 容器生成器引起的,该容器在一个帧中收集并输出大约 1/3 秒(未知原因)的帧MP4 moof/mdat 盒对。这意味着您需要等待 19 帧才能从 MFTranscodeContainerType_FMPEG4 以 60 FPS 获得任何输出。

要每帧输出单个 MP4 moof/mdat,只需谎称 MF_MT_FRAME_RATE 为 1 FPS(或高于 1/3 秒的任何值)。要以正确的速度播放视频,请使用媒体源扩展的 <video>.playbackRate 或更新您的 MP4 流拦截器中的 mvhdmdhd 框的 timescale(即乘以实际 FPS)以获得正确计时的 MP4流。

这样做,可以将延迟压缩到 20 毫秒以下。当您在 Unity (research) -> NvEnc -> MFTranscodeContainerType_FMPEG4 -> WebSocket -> Chrome Media Source Extensions 显示等链中并排看到 localhost 上的输出时,这几乎无法识别。

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

【讨论】:

  • 非常感谢@svobodb 提供的非常有用的建议!我现在更新了我的 AppWebStream 示例项目,以假装 1 FPS 将延迟减少到仅 1 帧延迟。
  • 我最近被告知有一种比摆弄时间戳更简单的方法来减少延迟。在每个 WriteSample 之后调用 IMFSinkWriter::NotifyEndOfSegment 似乎可以将编码延迟减少到仅 0-1 帧。
【解决方案2】:

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

【讨论】:

    【解决方案3】:

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

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

    【讨论】:

      【解决方案4】:

      尝试通过 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

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2011-12-11
        • 2010-10-29
        • 1970-01-01
        • 2022-07-14
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多