I want to destroy all i-frames of a video. Doing this I want to check if encrypting only the i-frames of a video would be sufficient for making it unwatchable. How can I do this? Only removing them and recompressing the video would not be the same as really overwriting the i-frame in the stream without recalculating b-frames etc.
On Windows you could copy file without recompress using VFW and skip I-frames. To find I-frames you could use FindSample
function with FIND_KEY
flag.
Using libavformat (library from ffmpeg), you can demultiplex the video into packets that represent a single frame. You can then encrypt data in the packets that are marked as key frames. Finally you can remultiplex the video into a new file. There is a good libavformat/libavcodec tutorial here. You will not have to actually decode/encode the frames because I assume you just want to encrypt the compressed data. In this case, once you read the AVPacket
, just encrypt its data if it's a key frame (packet->flags & PKT_FLAG_KEY
). You would then have to write the packets to a new file.
One thing to note is that you might have to be careful when you just encrypt the I-frame packets returned from libavformat or some other demuxing software since they may include data from other headers that are stored in the bitstream. For instance, I have often seen libavformat return sequence or group of picture headers as part of a video frame packet. Destroying this information may invalidate your test.
A possibly easier way to approach the problem would be to research the bitstream syntax of the codec used to encode the video and use the start codes to determine where frames start and whether or not they are I-frames. One problem is that most video files have a container (AVI, MP4, MPEG-PS/TS) around the actual compressed data and you would not want to encrypt anything in that area. You will most likely find header information belonging to the container format interspersed within the compressed data of a single frame. So you could use ffmpeg
from the command line to output just the raw compressed video data:
ffmpeg -i filename -an -vcodec copy -f rawvideo output_filename
This will create a file with only the video data(no audio) with no container. From here you can use the start codes of the specific video format to find the ranges of bytes in the file that correspond to I-frames.
For instance, in MPEG-4, you would be looking for the 32-bit start code 0x000001b6
to indicate the start of a VOP (video object plane). You could determine whether it is an I-frame or not by testing whether two bits immediately following the start code are equal to 00
. If it is an I frame, encrypt the data until you reach the next start code (24-bit 0x000001
). You'll probably want to leave the start code and frame type code untouched so you can tell later where to start decrypting.
Concerning outcome of your test as to whether or not encrypting I-frames will make a video unwatchable; it depends on your meaning of unwatchable. I would expect that you may be able to make out a major shape that existed in the original video if it is in motion since its information would have to be encoded in the B or P frames, but the color and detail would still be garbage. I have seen a single bit error in an I-frame make the entire group of pictures (the I-frame and all frames that depend on it) look like garbage. The purpose of the compression is to reduce redundancy to the point that each bit is vital. Destroying the entire I-frame will almost definitely make it unwatchable.
Edit: Response to comment
Start codes are guaranteed to be byte-aligned, so you can read the file a byte at a time into a 4 byte buffer and test whether it is equal to the start code. In C++, you can do this by the following:
#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
//...
}
}
}
Finding a 24-bit start code is similar, just use a 3 byte buffer. Remember that you must remove the video container with ffmpeg before doing this or you may destroy some of the container information.
Hi,Thanks a lot, now How can I reverse the effect of :
ffmpeg -i filename -an -vcodec copy -f rawvideo output_filename
so that I could play the output file again?