avformat 代码产生的输出与参数相同的 ffmpeg 略有不同 - 为什么?
avformat code produce slightly different output than ffmpeg with same parameters - why?
我想获得与代码中的此 ffmpeg 命令行调用完全相同的结果:
ffmpeg -i CAMERARTSPLINK -c:v copy -an -movflags +frag_keyframe+empty_moov -f mp4
当我运行上面的命令时,它给出了这个二进制结果:
got 36 bytes: 0, 0, 0, 36, 102, 116, 121, 112, ..., 111, 54, 109, 112, 52, 49,
got 512 bytes: 0, 0, 3, 76, 109, 111, 111, 118, 0, 0, 0, ..., 132, 0, 0, 3, 0, 4, 0, 0, 3, 0, 202,
代码可以利用 ffmpeg 库和包含,但我不想将 ffmpeg 用作程序调用(即不首选 exec* 函数)。
我用 avformat
为 RTSP H264 到 MP4 remux 创建了一个小的演示代码示例。
该代码高度重用了 horgh 不错的 videostreamer 库。
我将 sample code 发布到 pastebin.com (400 loc)。它构建成功,但您需要 link 它反对 avformat
、avdevice
、avcodec
和 avutil
.
我尽力达到相同的结果,但是当我运行这段代码时,字节#38之后的前几个字节是不同的(也许不仅仅是那些,我没有比较之后的任何东西字节#548):
writeOutput: writing 36 bytes: 0, 0, 0, 36, 102, 116, 121, 112, ..., 111, 54, 109, 112, 52, 49,
writeOutput: writing 512 bytes: 0, 0, 0, 0, 109, 111, 111, 118, 0, 0, 0, ..., 132, 0, 0, 3, 0, 4, 0, 0, 3, 0, 202,
你可以看到我的代码输出的第二行以 0
0
0
0
109
,
而 ffmpeg 给出了 0
0
3
76
109
.
其余所有(甚至字节未粘贴在这里)数据完全相同(至少前 548 个字节)。
我的代码有什么问题?这 2 个字节对于解码此流似乎非常重要。
102, 116, 121, 112
in ascii is ftyp
这是mp4格式类型框。 0, 0, 0, 36
是框的大小
109, 111, 111, 118
在 ascii 中是 mdat
这是数据框。 0, 0, 0, 0
是框的大小。
在这种情况下,mdat
框的大小是未知的,因为我们还不知道所有视频和音频帧的大小。所以使用零占位符。文件完成后,大小值应该被正确的大小覆盖
这是 ffmpeg 有限的日志记录功能加上我在视频编解码器方面的有限知识的愚蠢错误。
问题是 ffmpeg(带有 h264 输入)在将原子写入输出缓冲区时:
- 首先将 0 作为原子的大小 (movenc.c#L3981)
- 然后填充缓冲区的其余部分
- 然后在最后它在缓冲区中寻找原子的大小并更新大小 (movenc.c#L4049)
这一切都很好,但是在填充剩余缓冲区期间,它使用avio_w8和avio_w8 将刷新缓冲区,如果达到到此结束。
如果您将自定义 IO 与 avio_alloc_context
一起使用并且您没有定义足够大的缓冲区并且您也没有定义查找操作并且正在写入的原子大于您的缓冲区大小,它将被冲出并且 ffmpeg 将无法返回以更新原子的大小。
这可能会导致视频输出文件或流损坏(且无法播放)。
因此解决方案是将缓冲区大小从 512 增加到 4096,在这种情况下,moov atom 即使不寻求操作也可以适应。
如果您知道 moov 原子的长度大于 512 字节并查看我的示例代码,这将非常简单。
我想获得与代码中的此 ffmpeg 命令行调用完全相同的结果:
ffmpeg -i CAMERARTSPLINK -c:v copy -an -movflags +frag_keyframe+empty_moov -f mp4
当我运行上面的命令时,它给出了这个二进制结果:
got 36 bytes: 0, 0, 0, 36, 102, 116, 121, 112, ..., 111, 54, 109, 112, 52, 49,
got 512 bytes: 0, 0, 3, 76, 109, 111, 111, 118, 0, 0, 0, ..., 132, 0, 0, 3, 0, 4, 0, 0, 3, 0, 202,
代码可以利用 ffmpeg 库和包含,但我不想将 ffmpeg 用作程序调用(即不首选 exec* 函数)。
我用 avformat
为 RTSP H264 到 MP4 remux 创建了一个小的演示代码示例。
该代码高度重用了 horgh 不错的 videostreamer 库。
我将 sample code 发布到 pastebin.com (400 loc)。它构建成功,但您需要 link 它反对 avformat
、avdevice
、avcodec
和 avutil
.
我尽力达到相同的结果,但是当我运行这段代码时,字节#38之后的前几个字节是不同的(也许不仅仅是那些,我没有比较之后的任何东西字节#548):
writeOutput: writing 36 bytes: 0, 0, 0, 36, 102, 116, 121, 112, ..., 111, 54, 109, 112, 52, 49,
writeOutput: writing 512 bytes: 0, 0, 0, 0, 109, 111, 111, 118, 0, 0, 0, ..., 132, 0, 0, 3, 0, 4, 0, 0, 3, 0, 202,
你可以看到我的代码输出的第二行以 0
0
0
0
109
,
而 ffmpeg 给出了 0
0
3
76
109
.
其余所有(甚至字节未粘贴在这里)数据完全相同(至少前 548 个字节)。
我的代码有什么问题?这 2 个字节对于解码此流似乎非常重要。
102, 116, 121, 112
in ascii is ftyp
这是mp4格式类型框。 0, 0, 0, 36
是框的大小
109, 111, 111, 118
在 ascii 中是 mdat
这是数据框。 0, 0, 0, 0
是框的大小。
在这种情况下,mdat
框的大小是未知的,因为我们还不知道所有视频和音频帧的大小。所以使用零占位符。文件完成后,大小值应该被正确的大小覆盖
这是 ffmpeg 有限的日志记录功能加上我在视频编解码器方面的有限知识的愚蠢错误。
问题是 ffmpeg(带有 h264 输入)在将原子写入输出缓冲区时:
- 首先将 0 作为原子的大小 (movenc.c#L3981)
- 然后填充缓冲区的其余部分
- 然后在最后它在缓冲区中寻找原子的大小并更新大小 (movenc.c#L4049)
这一切都很好,但是在填充剩余缓冲区期间,它使用avio_w8和avio_w8 将刷新缓冲区,如果达到到此结束。
如果您将自定义 IO 与 avio_alloc_context
一起使用并且您没有定义足够大的缓冲区并且您也没有定义查找操作并且正在写入的原子大于您的缓冲区大小,它将被冲出并且 ffmpeg 将无法返回以更新原子的大小。
这可能会导致视频输出文件或流损坏(且无法播放)。
因此解决方案是将缓冲区大小从 512 增加到 4096,在这种情况下,moov atom 即使不寻求操作也可以适应。
如果您知道 moov 原子的长度大于 512 字节并查看我的示例代码,这将非常简单。