如何覆盖视频的i帧?

时间:2010-04-12 13:21:35

标签: c# c++ video video-processing

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

2 个答案:

答案 0 :(得分:5)

使用libavformat(来自ffmpeg的库),您可以将视频解复用为代表单个帧的数据包。然后,您可以加密标记为关键帧的数据包中的数据。最后,您可以将视频重新多路复用为新文件。有一个很好的libavformat / libavcodec教程here。您不必对帧进行实际解码/编码,因为我假设您只想加密压缩数据。在这种情况下,一旦您阅读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(视频对象平面)的开始。您可以通过测试紧跟在起始码之后的两个位是否等于00来确定它是否是I帧。如果是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删除视频容器,否则可能会破坏某些容器信息。

答案 1 :(得分:0)

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