Does encrypting all I-Frames of a video make it unwatchable?

Does encrypting all I-Frames of a video make it unwatchable?

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.

YouTube Preview Image

The second video compares the same video using different GOP sizes (10 and 50)

YouTube Preview Image

Error Concealment

We found out that some codecs (e.g. DirectShow) do a very good error concealment.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;
}

About the Author

Studying MultimediaTechnology in Salzburg, Austria