2016-05-25 3 views
1

Я создал с помощью C API ffmpeg приложение C++, которое считывает фреймы из файла и записывает их в новый файл. Все работает нормально, пока я сразу пишу кадры на выходе. Другими словами, следующая структура программы выводит правильный результат (на данный момент я помещаю только псевдокод, при необходимости я также могу опубликовать некоторые реальные фрагменты, но классы, которые я создал для обработки функциональных возможностей ffmpeg, довольно велики):ffmpeg C API - создание очереди кадров

AVFrame* frame = av_frame_alloc(); 
int got_frame; 

// readFrame returns 0 if file is ended, got frame = 1 if 
// a complete frame has been extracted 
while(readFrame(inputfile,frame, &got_frame)) { 
    if (got_frame) { 
    // I actually do some processing here 
    writeFrame(outputfile,frame); 
    } 
} 
av_frame_free(&frame); 

Следующий шаг заключается в параллелизации приложения, и, как следствие, кадры не записываются сразу после их чтения (я не хочу вдаваться в подробности распараллеливания). В этом случае возникают проблемы: на выходе происходит некоторое мерцание, как если бы некоторые кадры повторялись случайным образом. Однако количество кадров и продолжительность выходного видео остаются правильными.

Теперь я пытаюсь полностью отделить чтение от записи в последовательной реализации, чтобы понять, что происходит. Я создаю очереди указателей кадров:

std::queue<AVFrame*> queue; 
int ret = 1, got_frame; 
while (ret) { 
    AVFrame* frame = av_frame_alloc(); 
    ret = readFrame(inputfile,frame,&got_frame); 
    if (got_frame) 
    queue.push(frame); 
} 

Для записи кадров в выходной файл я делаю:

while (!queue.empty()) { 
    frame = queue.front(); 
    queue.pop(); 
    writeFrame(outputFile,frame); 
    av_frame_free(&frame); 
} 

В результате в этом случае является видеовыход с правильной продолжительности и числа фреймов, которые являются лишь повторением последних 3 (я думаю) кадров видео.

Я предполагаю, что что-то может пойти не так, потому что в первом случае я всегда использую одно и то же место памяти для чтения кадров, а во втором случае я выделяю много разных кадров.

Любые предложения о том, что может быть проблемой?

ответ

1

А, так я предполагаю, что readFrame() является оберткой вокруг av_read_frame() libavformat и libavcodec's avcodec_decode_video2(), так?

От documentation:

AVCodecContext.refcounted_frames Когда установлен в 1, то кадр является подсчет ссылок и возвращается ссылка относится к абоненту. Вызывающий абонент должен освободить фрейм, используя av_frame_unref(), когда рамка больше не требуется.

и:

Когда AVCodecContext.refcounted_frames установлен в 0, то возвращается ссылка относится к декодеру и действует только до следующего вызова этой функции или до закрытия или промывки декодера.

Очевидно, что из этого следует, что вам необходимо установить AVCodecContext.refcounted_frames 1. Значение по умолчанию 0, так что мое шестое чувство, что вам нужно, чтобы установить его в 1, и это будет исправить вашу проблему. Не забывайте использовать av_fame_unref() на изображениях после использования, чтобы предотвратить memleaks, а также не забудьте освободить свой AVFrame в этом цикле, если got_frame = 0 - еще раз, чтобы предотвратить memleaks:

while (ret) { 
    AVFrame* frame = av_frame_alloc(); 
    ret = readFrame(inputfile,frame,&got_frame); 
    if (got_frame) 
    queue.push(frame); 
    else 
    av_frame_free(frame); 
} 

(или в качестве альтернативы вы можете реализовать некоторый кеш для frame, чтобы вы только перераспределили его, если предыдущий объект был нажат в очередь.)

+0

СПАСИБО! Это была моя проблема! – lupod

0

Нет ничего явно неправильного в вашем псевдокоде. Проблема почти наверняка заключается в том, как вы блокируете очередь между потоками.

+0

Спасибо за ваш ответ. Я забыл указать, что на данный момент я отложил в сторону распараллеливание и что очередь построена последовательно (я отредактировал свой вопрос), – lupod

+0

Если вы не хотите публиковать полный код, я предлагаю регистрировать адреса нажатых и всплывающих указателей на фреймы , Убедитесь, что вы видите одну и ту же последовательность адресов в обоих направлениях. –

+1

Вы также можете зарегистрировать очки фрейма и проверить его монотонность. –

0

Ваше распределение памяти похоже на меня. Вы можете сделать что-то еще между чтением и записью кадров?

Есть ли queue в той же очереди в процедурах, которые читают и записывают кадры?

Смежные вопросы