3

我想销毁视频的所有 i 帧。这样做我想检查仅加密视频的 i 帧是否足以使其无法观看。我怎样才能做到这一点?仅删除它们并重新压缩视频与真正覆盖流中的 i 帧而不重新计算 b 帧等不同。

4

2 回答 2

6

使用 libavformat(来自 ffmpeg 的库),您可以将视频解复用为代表单个帧的数据包。然后,您可以对标记为关键帧的数据包中的数据进行加密。最后,您可以将视频重新复用到一个新文件中。这里有一个很好的 libavformat/libavcodec 教程。您不必实际解码/编码帧,因为我假设您只想加密压缩数据。在这种情况下,一旦您读取了AVPacket,只要它是关键帧 ( packet->flags & PKT_FLAG_KEY) 就加密它的数据。然后,您必须将数据包写入新文件。

需要注意的一点是,当您只加密从 libavformat 或其他解复用软件返回的 I 帧数据包时,您可能必须小心,因为它们可能包含来自存储在比特流中的其他标头的数据。例如,我经常看到 libavformat 返回序列或图片头组作为视频帧数据包的一部分。销毁此信息可能会使您的测试无效。

解决该问题的一种可能更简单的方法是研究用于对视频进行编码的编解码器的比特流语法,并使用起始码来确定帧从哪里开始以及它们是否是 I 帧。一个问题是大多数视频文件在实际压缩数据周围都有一个容器(AVI、MP4、MPEG-PS/TS),您不想加密该区域中的任何内容。您很可能会发现属于容器格式的标题信息散布在单个帧的压缩数据中。因此,您可以ffmpeg从命令行使用仅输出原始压缩视频数据:

ffmpeg -i filename -an -vcodec copy -f rawvideo output_filename

这将创建一个仅包含视频数据(无音频)且没有容器的文件。从这里您可以使用特定视频格式的起始代码来查找文件中对应于 I 帧的字节范围。

例如,在 MPEG-4 中,您将寻找 32 位起始码0x000001b6来指示 VOP(视频对象平面)的开始。您可以通过测试紧跟在起始码后面的两位是否等于 来确定它是否是 I 帧00。如果是 I 帧,则对数据进行加密,直到到达下一个起始码(24 位0x000001)。您可能希望保留起始代码和帧类型代码不变,以便稍后知道从哪里开始解密。

关于加密 I 帧是否会使视频无法观看的测试结果;这取决于您无法观看的含义。我希望您能够辨认出原始视频中存在的主要形状(如果它处于运动状态),因为它的信息必须在 B 或 P 帧中编码,但颜色和细节仍然是垃圾。我已经看到 I 帧中的一个位错误使整个图片组(I 帧和依赖它的所有帧)看起来像垃圾。压缩的目的是将冗余减少到每个比特都至关重要的程度。销毁整个 I 帧几乎肯定会使其无法观看。

编辑:对评论的回应

起始码保证是字节对齐的,因此您可以一次将文件读取一个字节到一个 4 字节的缓冲区中并测试它是否等于起始码。在 C++ 中,您可以通过以下方式执行此操作:

#include <iostream>
using namespace std;
//...

//...
ifstream ifs("filename", ios::in | ios::binary);
//initialize buffer to 0xffffffff
unsigned char buffer[4] = {0xff, 0xff, 0xff, 0xff};
while(!ifs.eof())
{
    //Shift to make space for new read.
    buffer[0] = buffer[1];
    buffer[1] = buffer[2];
    buffer[2] = buffer[3];

    //read next byte from file
    buffer[3] = ifs.get();

    //see if the current buffer contains the start code.
    if(buffer[0]==0x00 && buffer[1]==0x00 && buffer[2]==0x01 && buffer[3]==0xb6)
    {
        //vop start code found
        //Test for I-frame
        unsigned char ch = ifs.get();
        int vop_coding_type = (ch & 0xc0) >> 6;   //masks out the first 2 bits and shifts them to the least significant bits of the uchar
        if(vop_coding_type == 0)
        {
            //It is an I-frame
            //...
        }
    }
}

查找 24 位起始码类似,只是使用 3 字节缓冲区。请记住,您必须在执行此操作之前使用 ffmpeg 删除视频容器,否则您可能会破坏一些容器信息。

于 2010-04-12T15:04:08.483 回答
0

在 Windows 上,您可以复制文件而无需使用VFW重新压缩并跳过 I 帧。要查找 I 帧,您可以使用FindSample带有FIND_KEY标志的函数。

于 2010-04-12T13:31:31.730 回答