For our multimedia security lesson Matthias Schmidt and I wrote a program that reads an MPEG-4 Simple Profile Byte stream and overwrites all I-Frames. The purpose of this program is to evaluate whether an encryption of I-Frames would be sufficient in order to make a video unwatchable. This technique could be used for example for Pay-TV.
Results
The first video shows an original and an encrypted video in comparison.
The second video compares the same video using different GOP sizes (10 and 50)
Error Concealment
We found out that some codecs (e.g. DirectShow) do a very good error concealment.
The picture on the left shows two different videos played in two different players. As one can see, the second player reconstructs a slightly good image.
How does it work?
We are parsing the byte stream for VideoObjectPlanes which have the start code 0×000001b6. If this code is followed by two bytes that are equal to 00 the we found an I-Frame. From here on we start overwriting thy byte stream until we reach the next start code 0×000001.
Code
#include <iostream>
#include <fstream>
using namespace std;
int main() {
ifstream ifs("in.mp4", ios::in | ios::binary);
ofstream ofs("output.mp4", ios::out | ios::binary);
//initialize buffer to 0xffffffff
unsigned char buffer[4] = {0xff, 0xff, 0xff, 0xff};
bool write = false;
int counter=0;
unsigned long overallcount=0;
unsigned long writecount=0;
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) // 0x00 00 01 b6
{
write = false;
if(buffer[3]==0xb6) {
//cout << "VOP begin" <<endl;
//vop start code found
//Test for I-frame
unsigned char ch = ifs.get();
ofs << buffer[3];
buffer[0] = buffer[1];
buffer[1] = buffer[2];
buffer[2] = buffer[3];
//read next byte from file
buffer[3] = ch;
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 (hell yeah (: )
write = true;
cout << ++counter << ". Iframe =)" << endl;
}
}
}
if(write) {
ofs << (char)0x0d;
writecount++;
}
else {
ofs << buffer[3];
overallcount++;
}
}
cout << "Read : " << overallcount << endl;
cout << "Write: " << writecount << endl;
cout << "Percent overwritten: " << (float)writecount/(float)overallcount*100.0f << endl;
return 0;
}
Excellent Article!