ffmpeg 创建一个 mp4 流,在 Firefox 中导致错误

ffmpeg creates an mp4 stream which results error in 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. 如果我运行服务器端(没有任何断点),浏览器不显示任何图片,马上就报错

无效的顶级框 错误消息后面始终跟有 (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 点中标记的场景时(运行没有断点),Top-Level Box 总是 'f'。

更新2:

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

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

能否确认一下?

问题出在服务器端。

这一行:

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

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

好的是这个:

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

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