2010-04-12 88 views
3

我想销毁视频的所有i帧。这样做我想检查一下,只加密视频的i帧是否足以使其无法观看。我怎样才能做到这一点?只删除它们并重新压缩视频将不会像重写流中的i帧一样重新计算b帧等。如何覆盖视频的i-帧?

回答

5

使用了libavformat(从FFMPEG库),则可以解复用视频成代表单个帧的数据包。然后您可以加密标记为关键帧的数据包中的数据。最后,您可以将视频重新复用为新文件。有一个很好的libavformat/libavcodec教程here。您不必实际解码/编码帧,因为我认为您只是想对加密的数据进行加密。在这种情况下,一旦你读AVPacket,只是如果它是一个关键帧(packet->flags & PKT_FLAG_KEY)加密其数据。然后您将不得不将数据包写入新文件。

有一点需要注意的是,当你只是加密从libavformat或其他一些demuxing软件返回的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删除视频容器,否则您可能会销毁一些容器信息。

+0

感谢您的评论。我已经想过读取字节流并找到标题标记。但不幸的是,我不知道如何在这样的流中找到0x000001b6。我用fopen以二进制模式打开文件(C++)吗?或者这种做法是错误的?如果它是正确的,那么我如何找到一个32位的代码? – 2010-04-12 16:40:44

+0

看到我上面的编辑。 – 2010-04-12 17:22:40

+0

非常感谢! =) – 2010-04-13 08:14:04

0

在Windows上,您可以复制文件而不用重新使用VFW并跳过I帧。要查找I帧,您可以使用FindSample功能和FIND_KEY标志。

+0

会更喜欢黑色的图片?加密会产生噪音,这可能会在应用pframe时显示出不同的视觉效果。 - 但无论那种工具看起来很酷。 – Potatoswatter 2010-04-12 13:59:19

+0

通过使用与整个文件相同的编码器压缩空白(白色或黑色)帧可以替代I帧。但是你不应该重新压缩B帧来达到想要的效果。 – 2010-04-12 14:13:38

+0

谢谢。代替iframe听起来不错。我会看看功能。 – 2010-04-12 16:43:36