Я хочу уничтожить все i-кадры видео. Делая это, я хочу проверить, достаточно ли шифрования только i-кадров видео, чтобы сделать его недоступным для просмотра. Как я могу это сделать? Только удаление их и повторное сжатие видео не будет таким же, как реальная перезапись i-frame в потоке без пересчета b-кадров и т.д.
Как перезаписать i-кадры видео?
Ответы (2)
С помощью libavformat (библиотека от ffmpeg) можно демультиплексировать видео в пакеты, представляющие один кадр. Затем вы можете зашифровать данные в пакетах, помеченных как ключевые кадры. Наконец, вы можете повторно мультиплексировать видео в новый файл. Существует хорошее руководство по libavformat/libavcodec здесь. Вам не нужно будет фактически декодировать/кодировать кадры, потому что я предполагаю, что вы просто хотите зашифровать сжатые данные. В этом случае, как только вы прочитаете AVPacket
, просто зашифруйте его данные, если это ключевой кадр (packet->flags & PKT_FLAG_KEY
). Затем вам придется записывать пакеты в новый файл.
Следует отметить, что вам, возможно, придется быть осторожным, когда вы просто шифруете пакеты I-frame, возвращенные из libavformat или другого программного обеспечения демультиплексирования, поскольку они могут включать данные из других заголовков, которые хранятся в битовом потоке. Например, я часто видел возвращаемую libavformat последовательность или группу заголовков изображений как часть пакета видеокадра. Уничтожение этой информации может сделать ваш тест недействительным.
Возможно, более простым способом решения этой проблемы было бы исследование синтаксиса битового потока кодека, используемого для кодирования видео, и использование начальных кодов для определения того, где начинаются кадры и являются ли они I-кадрами. Одна из проблем заключается в том, что большинство видеофайлов имеют контейнер (AVI, MP4, MPEG-PS/TS) вокруг фактически сжатых данных, и вы не захотите ничего шифровать в этой области. Скорее всего, вы обнаружите, что информация заголовка, относящаяся к формату контейнера, вкраплена в сжатые данные одного кадра. Таким образом, вы можете использовать ffmpeg
из командной строки для вывода только необработанных сжатых видеоданных:
ffmpeg -i filename -an -vcodec copy -f rawvideo output_filename
Это создаст файл только с видеоданными (без звука) без контейнера. Отсюда вы можете использовать начальные коды определенного видеоформата, чтобы найти диапазоны байтов в файле, которые соответствуют I-кадрам.
Например, в MPEG-4 вы будете искать 32-битный начальный код 0x000001b6
, чтобы указать начало VOP (плоскость видеообъекта). Вы можете определить, является ли это I-кадром или нет, проверив, равны ли два бита, следующие сразу за начальным кодом, 00
. Если это I-кадр, шифруйте данные, пока не достигнете следующего стартового кода (24-битное 0x000001
). Вы, вероятно, захотите оставить начальный код и код типа кадра нетронутыми, чтобы позже вы могли сказать, с чего начать расшифровку.
Относительно результатов вашего теста относительно того, сделает ли шифрование I-кадров видео недоступным для просмотра; это зависит от вашего значения unwatchable. Я ожидаю, что вы сможете разобрать основную форму, которая существовала в исходном видео, если оно находится в движении, поскольку его информация должна быть закодирована в кадрах B или P, но цвет и детали все равно будут мусором. Я видел, как из-за одной битовой ошибки в I-кадре вся группа изображений (I-кадр и все кадры, которые от него зависят) выглядят как мусор. Целью сжатия является уменьшение избыточности до такой степени, что каждый бит становится жизненно важным. Уничтожение всего I-кадра почти наверняка сделает его недоступным для просмотра.
Изменить: ответ на комментарий
Стартовые коды гарантированно выровнены по байтам, поэтому вы можете читать файл побайтно в 4-байтовый буфер и проверять, равен ли он стартовому коду. В С++ это можно сделать следующим образом:
#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, иначе вы можете уничтожить часть информации о контейнере.
0x000001b600
, но это должно быть просто 0x000001b6
. Не уверен, опечатка это или нет.
- person Jason B; 28.04.2010
0x000001b6
находится в шестнадцатеричном формате, что означает, что каждая цифра представляет 4 бита, поэтому вам действительно нужно искать 0x000001b6
(3 байта), а затем проверять первые два бита следующего байта. Посмотрите мой код выше, как я ищу 0x000001b6, а затем использую маскирование и сдвиг битов, чтобы проверить только первые 2 бита следующего байта. 0x000001b600
будет искать, чтобы весь следующий байт был равен нулю.
- person Jason B; 29.04.2010
В Windows вы можете скопировать файл без повторного сжатия, используя VFW и пропускать I-кадры. Чтобы найти I-кадры, вы можете использовать функцию FindSample
с флагом FIND_KEY
.