首页 > 解决方案 > ffmpeg 创建一个 mp4 流,导致 Firefox 出错

问题描述

我即将在 HTML5 视频元素中播放 fmp4。

我成功创建了一个 websocket 来将 ffmpeg 的输出传递给 MSE。

但是,当我尝试在 Firefox(72.0.1,64 位,在 Ubuntu 18.04LTS 下)中打开页面时,总是会导致错误:

Media resource blob:http://localhost/XXXX could not be decoded, error: Error Code: NS_ERROR_FAILURE (0x80004005)
Details: virtual mozilla::MediaResult mozilla::MP4ContainerParser::IsInitSegmentPresent(const mozilla::MediaSpan &): Invalid Top-Level Box:f"

这是我的 FFMPEG 行:

ffmpeg -r 5 -i rtsp://IPCAMERA -c:v copy -an -movflags +frag_keyframe+empty_moov+default_base_moof -f mp4 pipe:1

这就是服务器端 Java(使用 Tomcat 引擎)解析此命令的输出的方式(这可能效率低,但现在还可以):

ProcessBuilder b = new ProcessBuilder(FFMPEGCOMMAND.split("\\s+"));
try {
    p = b.start();
} catch (IOException e) {
    e.printStackTrace();
}
InputStream input = p.getInputStream();
int bytes_read = 0;
byte buffer[] = new byte[512];
try {
    while (0 < (bytes_read = input.read(buffer, 0, 512))) {
        System.out.println("Bytes read:" + bytes_read);
        this.session.getBasicRemote().sendBinary(ByteBuffer.wrap(buffer));
    }
} catch (IOException e) {
    e.printStackTrace();
}

然后客户端是一个 websocket-MSE,就像这个repo一样。

结果:

  1. 当我调试服务器端时,在sendBinary调用之前有一个断点,我在让服务器端运行之前等待几秒钟,然后在浏览器中显示第一张图片,然后立即进入上面的错误。

  2. 如果我运行服务器端(没有任何断点),浏览器不显示任何图片,它立即进入错误。

Invalid Top-Level box错误消息后面总是跟着 (a) 随机垃圾字符。

这应该工作的证据在第 1 点。如果我在让数据滚动到客户端之前等待一段时间,它可以在达到该错误之前解码 1(或者可能更多帧)。

这可能是我的 ffmpeg 命令行错误。

但是我真的找不到关于这个主题的任何好的资源(只找到那些与旧版本的 Firefox 相关的资源)。

更新1

这是同一命令创建 mp4 文件而不是管道时的 FFMPEG 日志:https ://pastebin.com/Gjq2vxeT

这是带有错误框的详细 Firefox 日志:

Details: virtual mozilla::MediaResult mozilla::MP4ContainerParser::IsInitSegmentPresent(const mozilla::MediaSpan &): Invalid Top-Level Box:f

请注意,当我执行第 2 点中标记的场景(无断点运行)时,顶级框始终为“f”。

更新2:

以下是 ffmpeg 的当前输出(前 128 项使用数字和字母数字表示):https ://pastebin.com/DeJMfNYs

有趣的是前 4 个字节对我来说似乎无效。但是,从 byte4(第 5 个字节)开始,似乎还可以(“ftyp”)。

你能确认一下吗?

标签: ffmpegmp4fmp4

解决方案


问题出在服务器端。

有了这条线:

this.session.getBasicRemote().sendBinary(ByteBuffer.wrap(buffer));

这不好,因为当缓冲区中有 0 时,ByteBuffer 的 wrap 方法会停止。

好的是这样的:

this.session.getBasicRemote().sendBinary(ByteBuffer.wrap(buffer, 0, bytes_read));

多么愚蠢的错误 - 现在工作正常:)


推荐阅读