FFMPEG: При декодировании видео можно генерировать результат в предоставленный пользователем буфер?

В сценарии декодирования видео ffmpeg, например, H264, мы обычно выделяем AVFrame и декодировать сжатые данные, тогда мы получим результат от члена data а также linesize из AVFrame, Как следующий код:

// input setting: data and size are a H264 data.
AVPacket avpkt;
av_init_packet(&avpkt);
avpkt.data = const_cast<uint8_t*>(data);
avpkt.size = size;

// decode video: H264 ---> YUV420
AVFrame *picture = avcodec_alloc_frame();
int len = avcodec_decode_video2(context, picture, &got_picture, &avpkt);

Мы можем использовать результат для выполнения других задач, например, для визуализации с помощью DirectX9. То есть подготовить буферы (текстуры DirectX9) и скопировать из результата декодирования.

D3DLOCKED_RECT lrY;
D3DLOCKED_RECT lrU;
D3DLOCKED_RECT lrV;
textureY->LockRect(0, &lrY, NULL, 0);
textureU->LockRect(0, &lrU, NULL, 0);
textureV->LockRect(0, &lrV, NULL, 0);

// copy YUV420: picture->data ---> lr.pBits.
my_copy_image_function(picture->data[0], picture->linesize[0], lrY.pBits, lrY.Pitch, width, height);
my_copy_image_function(picture->data[1], picture->linesize[1], lrU.pBits, lrU.Pitch, width / 2, height / 2);
my_copy_image_function(picture->data[2], picture->linesize[2], lrV.pBits, lrV.Pitch, width / 2, height / 2);

Считается, что в этом процессе происходит 2 копирования (ffmpeg копирует результат в изображение-> данные, а затем копирует изображение-> данные в текстуру DirectX9).

Мой вопрос: возможно ли улучшить процесс только до 1 экземпляра? С другой стороны, можем ли мы предоставить буферы (pBitsбуфера текстур DirectX9) к ffmpeg, и функция декодирования приводит к буферу текстуры DirectX9, а не к буферам AVFrame?

1 ответ

Я нашел выход.

Есть публичный член AVCodecContext, get_buffer2, который является функцией обратного вызова. Во время звонка avcodec_decode_video2эта функция обратного вызова будет вызываться, и эта функция обратного вызова отвечает за делегирование буферов и некоторую информацию AVFrame, затем avcodec_decode_video2 генерировать результат в буферах AVFrame,

Функция обратного вызова get_buffer2 установлена avcodec_default_get_buffer2 по умолчанию. Тем не менее, мы можем переопределить это как нашу конфиденциальную функцию. Например:

void our_buffer_default_free(void *opaque, uint8_t *data)
{
    // empty
}
int our_get_buffer(struct AVCodecContext *c, AVFrame *pic, int flags)
{
    assert(c->codec_type == AVMEDIA_TYPE_VIDEO);
    pic->data[0] = lrY.pBits;
    pic->data[1] = lrU.pBits;
    pic->data[2] = lrV.pBits;
    picture->linesize[0] = lrY.Pitch;
    picture->linesize[1] = lrU.Pitch;
    picture->linesize[2] = lrV.Pitch;
    pic->buf[0] = av_buffer_create(pic->data[0], pic->linesize[0] * pic->height, our_buffer_default_free, NULL, 0);
    pic->buf[1] = av_buffer_create(pic->data[1], pic->linesize[1] * pic->height / 2, our_buffer_default_free, NULL, 0);
    pic->buf[2] = av_buffer_create(pic->data[2], pic->linesize[2] * pic->height / 2, our_buffer_default_free, NULL, 0);
    return 0;
}

Перед декодированием мы переопределяем функцию обратного вызова:

context->get_buffer2 = our_get_buffer;

затем avcodec_decode_video2 сгенерирует результат для наших предоставленных буферов.

Кстати, для программ на C++, которые часто реализуют эти процессы в классах, мы можем сначала записать этот указатель:

context->opaque = this;

И определите переопределенную функцию обратного вызова как статический член:

static int myclass::my_get_buffer(struct AVCodecContext *c, AVFrame *pic, int flags)
{
    auto this_pointer = static_cast<decode_context*>(c->opaque);
    return this_pointer->my_get_buffer_real(c, pic, flags);
}
int myclass::my_get_buffer_real(struct AVCodecContext *c, AVFrame *pic, int flags)
{
    // ditto with above our_get_buffer.
    // ...
}
Другие вопросы по тегам