c++ - FFMPEG:解码视频时,是否可以将结果生成到用户提供的缓冲区?

标签 c++ video ffmpeg directx

在ffmpeg解码视频的场景下,比如H264,一般我们分配一个AVFrame,解码压缩后的数据,然后从成员data中得到结果AVFramelinesize。如以下代码:

// 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 Textures),并从解码的结果中复制。

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 Texture)。

我的问题是:是否可以将流程改进为仅1 份?另一方面,我们是否可以向ffmpeg提供缓冲区(pBits,DirectX9纹理的缓冲区),并将函数结果解码到DirectX9纹理的缓冲区,而不是AVFrame的缓冲区?

最佳答案

我找到了出路。

AVCodecContext有一个public成员get_buffer2,是一个回调函数。在调用avcodec_decode_video2时,会调用这个回调函数,这个回调函数负责将buffer和一些信息委托(delegate)给AVFrame,然后生成avcodec_decode_video2结果到 AVFrame 的缓冲区。

回调函数 get_buffer2 默认设置为 avcodec_default_get_buffer2。但是,我们可以将其覆盖为我们的私有(private)函数。例如:

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.
    // ...
}

关于c++ - FFMPEG:解码视频时,是否可以将结果生成到用户提供的缓冲区?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/28339980/

相关文章:

jquery - 更改动画 Sprite 表时 HTML5 Canvas 挂起(逐帧视频播放)

css - 覆盖顶部的视频仍然不透明

javascript - Vimeo 视频不会在 iframe 中的 chrome 上启动。不调用 LoadProgress

bash - bash脚本的循环

c++ - 反序列化构造函数无法正确读取数据

c++ - grpc 服务中连接的简单线程安全 vector

c - ffmpeg错误 "Could not allocate picture: Invalid argument Found Video Stream Found Audio Stream"

ffmpeg - 使用 ffmpeg 在给定时间从 flv 视频生成图像

c++ - 编译C++ mac,查找头文件未找到

c++ - 如何对齐指针