ffprobe - 从文件块获取视频信息

3

在我的网站上,用户可以上传视频,这些视频会在上传时进行加密并存储到另一台服务器上。我想要存储视频的比特率、帧率等信息,但是我无法直接访问它们,也不能仅仅使用以下命令:

ffprobe -show_streams -i file.mp4

我在服务器上尝试保存包含moov原子的最后一个块,但是ffprobe输出如下:
Format mov,mp4,m4a,3gp,3g2,mj2 detected only with low score of 1, misdetection possible!
moov atom not found
C:\file.mp4: Invalid data found when processing input

我检查过了,至少截断一个字节会导致这种情况发生,尽管 moov atom 保持完好无损。

正确的方法是从包含 moov atom 的文件片段中获取视频元数据。那么从 mdata atom 片段中获取信息呢?

1个回答

8
我认为ffmpeg解析器不知道如何找到分块文件的moov原子。它会逐个分块(读取或跳过)mp4文件,直到找到moov原子,如果你截取了开头的一部分,分块结构就会被破坏,因此它将无法找到moov原子。
你可以通过使用带有-movflags +faststart(或在c/c++代码中使用类似的AVOptions)的ffmpeg重新混合来检测末尾带有moov原子的文件,并将moov原子移到开头。然后,你可以在moov原子之后截断文件,并且仍然可以解析头文件。
对于写一个针对截断片段感知修改mov解复用器的情况(请参见评论),以下是你应该采取的步骤。首先,尽量不要修改mov_read_default(),它是中心递归引擎,任何在这里进行的更改都可能破坏大多数常规功能。相反,对mov_read_header()进行更改(因为你只关心头部解析,而不是帧的解复用)。你会找到以下代码:
if (mov->moov_retry)
    avio_seek(pb, 0, SEEK_SET);
if ((err = mov_read_default(mov, pb, atom)) < 0) {
    av_log(s, AV_LOG_ERROR, "error reading header\n");
    mov_read_close(s);
    return err;
}
} while (pb->seekable && !mov->found_moov && !mov->moov_retry++);
if (!mov->found_moov) {

这是尝试解码头部树结构,其中moov是上层原子。在文件中,它寻找以下类似序列:

$ hexdump -n 32 -s 41934133 -C somefile.mov 
027fdd35  00 00 3e b4 6d 6f 6f 76  00 00 00 6c 6d 76 68 64  |..>.moov...lmvhd|
027fdd45  00 00 00 00 c9 6b 7b f5  c9 6b 7c 02 00 00 02 58  |.....k{..k|....X|

0x00003eb4是'moov'原子的字节大小,其中有一个名为'mvhd'的子原子,大小为0x0000006c字节(树形结构在此之后继续)。如果在解复用文件时将文件指针设置为该确切偏移量,则可以正确解码:

$ tail -c +41934134 somefile.mov > /tmp/hdr.mov
$ ffprobe  /tmp/hdr.mov
[..]
[mov,mp4,m4a,3gp,3g2,mj2 @ 0x7f871b002a00] stream 0, offset 0x3f3e: partial file
[mov,mp4,m4a,3gp,3g2,mj2 @ 0x7f871b002a00] Could not find codec parameters for stream 0 (Video: h264 (avc1 / 0x31637661), none(bt709), 1280x720, 10695 kb/s): unspecified pixel format
[..]
    Stream #0:0(und): Video: h264 (avc1 / 0x31637661), none(bt709), 1280x720, 10695 kb/s, 29.97 fps, 29.97 tbr, 600 tbn, 1200 tbc (default)
[..]
    Stream #0:1(und): Audio: aac (LC) (mp4a / 0x6134706D), 44100 Hz, mono, fltp, 63 kb/s (default)

如何获取文件偏移量取决于您:

  • 您可以在 mov_read_header() 函数中添加一些代码来扫描文件以查找“moov”(0x6d6f6f76)并将文件指针设置为该位置前面的 4 个字节。
  • 在创建此片段的代码中,您可以扫描 moov 并且在将该片段保存到文件之前,去掉 moov 原子之前的无用数据。

如果您要更改 ffmpeg 并将其放入用于其他功能的 ffmpeg 版本中,我建议您将其放在某种选项下,以便它不会对默认文件读取产生影响。否则,您将有可能导致常规的 mov/mp4 文件解析无法正常工作。


如果我可以在整个文件上使用ffmpeg,我就不会问问题了。 - andrzej1_1
如果您的磁盘上没有完整的文件,我认为ffmpeg无法帮助您。您可以尝试在最后一个片段上使用mplayer,但我怀疑它是否能做得更好。或者,您可以使用ffmpeg解复用器中的相关代码编写自己的头部解析器。(我以为服务器可以访问未加密的文件,因为您可以访问最后一个片段。) - Ronald S. Bultje
其实,仔细想想,你甚至可以添加一个额外的“moov片段解复用器”,它忽略块并只扫描moov原子,并使用从moov片段解复用器调用的现有moov解析代码在ffmpeg中注册该解复用器。这样,您只需编写最少量的代码。如果您对这种方法感兴趣,我可以给您一些提示。 - Ronald S. Bultje
好的,我想这么做。我下载了FFmpeg源代码,并将编辑本机moov分离器(libavformat/mov.c),而不是注册自己的分离器。我应该修改mov_read_default(),是吗? - andrzej1_1
谢谢,它有效。在Linux上,当我编辑一行时,编译整个FFmpeg需要15分钟。您知道如何快速编译和轻松调试代码吗? - andrzej1_1
在构建时,您可能会重新运行配置;您可以仅迭代调用“make”,重新构建应该几乎瞬间完成。否则我不知道会导致这种问题,可能值得另一个stackoverflow主题(还要搜索一下,可能已经被问过了)。 - Ronald S. Bultje

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接