Как перезаписать i-кадры видео?

Я хочу уничтожить все i-кадры видео. Делая это, я хочу проверить, достаточно ли шифрования только i-кадров видео, чтобы сделать его недоступным для просмотра. Как я могу это сделать? Только удаление их и повторное сжатие видео не будет таким же, как реальная перезапись i-frame в потоке без пересчета b-кадров и т.д.


person Dominik Goltermann    schedule 12.04.2010    source источник


Ответы (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, иначе вы можете уничтожить часть информации о контейнере.

person Jason B    schedule 12.04.2010
comment
Спасибо за ваш комментарий. я уже думал о чтении потока байтов и поиске маркеров заголовков. но, к сожалению, я понятия не имею, как найти 0x000001b6 в таком потоке. Могу ли я открыть файл с помощью fopen в двоичном режиме (С++)? Или такой подход неправильный? Если это правильно, то как мне найти 32-битный код? - person Dominik Goltermann; 12.04.2010
comment
Привет, Джейсон, я пытался преобразовать видео в необработанное (это было видео в формате h264), но все попытки найти коды 0x000001b600 с помощью шестнадцатеричного редактора оказались тщетными :( Я нашел некоторые из этих кодов в исходном видеофайле, но там все ближе к концу файла и за ними не последовало не так много данных.Теперь мне интересно,что я сделал не так.Возможно вы могли бы мне подсказать=)с уважением доминик - person Dominik Goltermann; 28.04.2010
comment
Это Н.264? Если это так, см. мой ответ здесь: stackoverflow.com/questions/2732028/. Есть 2 вида видео в формате mpeg-4; часть 2 и часть 10. Мой ответ относится к части 2, но если это H.264, то это часть 10. - person Jason B; 28.04.2010
comment
Также в вашем комментарии говорится, что вы искали 0x000001b600, но это должно быть просто 0x000001b6. Не уверен, опечатка это или нет. - person Jason B; 28.04.2010
comment
Вы написали: Например, в MPEG-4 вам нужно искать 32-битный начальный код 0x000001b6, чтобы указать начало VOP (плоскость видеообъекта). Вы можете определить, является ли это I-кадром или нет, проверив, равны ли два бита, следующие сразу за начальным кодом, 00. Итак, я предполагал, что 0x000001b600 будет началом iframe? Или я вас неправильно понял? - person Dominik Goltermann; 29.04.2010
comment
0x000001b6 находится в шестнадцатеричном формате, что означает, что каждая цифра представляет 4 бита, поэтому вам действительно нужно искать 0x000001b6 (3 байта), а затем проверять первые два бита следующего байта. Посмотрите мой код выше, как я ищу 0x000001b6, а затем использую маскирование и сдвиг битов, чтобы проверить только первые 2 бита следующего байта. 0x000001b600 будет искать, чтобы весь следующий байт был равен нулю. - person Jason B; 29.04.2010

В Windows вы можете скопировать файл без повторного сжатия, используя VFW и пропускать I-кадры. Чтобы найти I-кадры, вы можете использовать функцию FindSample с флагом FIND_KEY.

person Kirill V. Lyadvinsky    schedule 12.04.2010
comment
Будет ли это иметь эффект, больше похожий на затемнение изображения? Вместо этого шифрование будет генерировать шум, который может показать другой визуальный эффект при применении pframes. — но в любом случае этот инструмент выглядит круто. - person Potatoswatter; 12.04.2010
comment
Можно заменить I-кадры, сжав пустой (белый или черный) кадр тем же кодировщиком, что и весь файл. Но вы не должны повторно сжимать B-кадры для достижения желаемого эффекта. - person Kirill V. Lyadvinsky; 12.04.2010
comment
Благодарю. замена фреймов звучит хорошо. посмотрю функции. - person Dominik Goltermann; 12.04.2010