C++ 什么是AVHWAccel,我如何使用它?

C++ 什么是AVHWAccel,我如何使用它?,c++,ffmpeg,C++,Ffmpeg,我想利用硬件加速来解码h264编码的MP4文件 我的计算环境: Hardware: MacPro (2015 model) Software: FFmpeg (installed by brew) 以下是FFmpeg命令的输出: $ffmpeg -hwaccels Hardware acceleration methods: vda videotoolbox 根据,我的环境有两个选项,即,VDA和VideoToolBox。我用C++尝试了VDA: Codec = avcodec_find_

我想利用硬件加速来解码h264编码的MP4文件

我的计算环境:

Hardware: MacPro (2015 model)
Software: FFmpeg (installed by brew)
以下是
FFmpeg
命令的输出:

$ffmpeg -hwaccels
Hardware acceleration methods:
vda 
videotoolbox
根据,我的环境有两个选项,即,
VDA
VideoToolBox
。我用C++尝试了
VDA

Codec = avcodec_find_decoder_by_name("h264_vda");
它有点工作,但是像素格式的输出是
UYVY422
,我很难处理(关于如何在
C++
中渲染
UYVY422
有什么建议吗?理想的格式是
yuv420p

因此,我想尝试一下
VideotoolBox
,但没有像这样简单的事情(不过在编码的情况下它可能会起作用)

似乎我应该使用
AVHWAccel
,但是什么是
AVHWAccel
以及如何使用它

部分C++代码:

for( unsigned int i = 0; i < pFormatCtx->nb_streams; i++ ){
        if(pFormatCtx->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO){
            pCodecCtx = pFormatCtx->streams[i]->codec;
            video_stream = pFormatCtx->streams[i];
            if( pCodecCtx->codec_id == AV_CODEC_ID_H264 ){
                //pCodec = avcodec_find_decoder(pCodecCtx->codec_id);
                pCodec = avcodec_find_decoder_by_name("h264_vda");
                break;
            }
        }
    }
    // open codec
    if( pCodec ){
        if((ret=avcodec_open2(pCodecCtx, pCodec, NULL)) < 0) {
        ....
for(无符号整数i=0;inb\u流;i++){
如果(pFormatCtx->streams[i]->codec->codec\u type==AVMEDIA\u type\u VIDEO){
pCodecCtx=pFormatCtx->streams[i]->codec;
视频流=pFormatCtx->streams[i];
如果(pCodecCtx->codec\u id==AV\u codec\u id\u H264){
//pCodec=avcodec\u find\u解码器(pCodecCtx->codec\u id);
pCodec=avcodec_find_decoder_by_name(“h264_vda”);
打破
}
}
}
//开放式编解码器
if(pCodec){
if((ret=avcodec_open2(pCodecCtx,pCodec,NULL))<0){
....

解码器不会选择输出的像素格式,它由视频本身决定。
swscale
lib用于将一种像素格式转换为另一种像素格式

auto sws_ctx = sws_getContext(src_width, src_height, AV_PIX_FMT_UYUV422, dst_width, dst_height, AV_PIX_FMT_YUV420P, 0,0,0,0);
av_image_alloc(new_data, new_linesize, dst_width, dst_height, AV_PIX_FMT_BGR24, FRAME_ALIGN);
sws_scale(sws_ctx, frame->data, frame->linesize, 0, src_height, new_data, new_linesize);
没有
h264\u视频工具箱
解码器,只有编码器。要列出可用的解码器/编码器:

ffmpeg -encoders
ffmpeg -decoders

解码器/编码器名称写在源代码中,例如,在
libavcodec/vda_h264_dec.c
libavcodec/videotoolboxenc.c
的末尾,解码器不会选择输出的像素格式,它由视频本身决定。
swscale
lib用于将一种像素格式转换为另一种像素格式

auto sws_ctx = sws_getContext(src_width, src_height, AV_PIX_FMT_UYUV422, dst_width, dst_height, AV_PIX_FMT_YUV420P, 0,0,0,0);
av_image_alloc(new_data, new_linesize, dst_width, dst_height, AV_PIX_FMT_BGR24, FRAME_ALIGN);
sws_scale(sws_ctx, frame->data, frame->linesize, 0, src_height, new_data, new_linesize);
没有
h264\u视频工具箱
解码器,只有编码器。要列出可用的解码器/编码器:

ffmpeg -encoders
ffmpeg -decoders

解码器/编码器名称写在源代码中,例如,在
libavcodec/vda_h264_dec.c
libavcodec/videotoolboxenc.c

的末尾,与选择哪种pix格式的解码器无关

您的视频pix格式是
UYVY422
,因此您在解码帧后获得了此格式

就像前面提到的答案@halfelf一样,您可以在解码帧后执行swscale,将pix格式转换为理想格式
yuv420p
,然后进行渲染

同时,如果您确定它是
UYVY422
格式,
SDL2
可以直接为您处理渲染

在下面的示例中,我的格式是
yuv420p
,我使用swscale转换为
UYVY422
以渲染为
SDL2

// prepare swscale context, AV_PIX_FMT_UYVY422 is my destination pix format
SwsContext *swsCtx = sws_getContext(codecCtx->width, codecCtx->height, codecCtx->pix_fmt,
                                    codecCtx->width, codecCtx->height, AV_PIX_FMT_UYVY422,
                                    SWS_FAST_BILINEAR, NULL, NULL, NULL);

SDL_Init(SDL_INIT_EVERYTHING);

SDL_Window *window;
SDL_Renderer *render;
SDL_Texture *texture;

SDL_CreateWindowAndRenderer(codecCtx->width,
                            codecCtx->height, SDL_WINDOW_OPENGL, &window, &render);

texture = SDL_CreateTexture(render, SDL_PIXELFORMAT_UYVY, SDL_TEXTUREACCESS_STREAMING,
                            codecCtx->width, codecCtx->height);

// ......
// decode the frame 
// ......
AVFrame *frameUYVY = av_frame_alloc();
av_image_alloc(frameUYVY->data, frameUYVY->linesize, codecCtx->width, codecCtx->height, AV_PIX_FMT_UYVY422, 32);

SDL_LockTexture(texture, NULL, (void **)frameUYVY->data, frameUYVY->linesize);

// convert the decoded frame to destination frameUYVY (yuv420p -> uyvy422)
sws_scale(swsCtx, frame->data, frame->linesize, 0, frame->height,
                      frameUYVY->data, frameUYVY->linesize);

SDL_UnlockTexture(texture);

// performa render
SDL_RenderClear(render);
SDL_RenderCopy(render, texture, NULL, NULL);
SDL_RenderPresent(render);

在您的示例中,如果您的pix格式是
uyvy422
,则可以跳过swscale部分,并在从
ffmpeg
解码后直接执行渲染。这与要选择哪种pix格式的解码器无关

您的视频pix格式是
UYVY422
,因此您在解码帧后获得了此格式

就像前面提到的答案@halfelf一样,您可以在解码帧后执行swscale,将pix格式转换为理想格式
yuv420p
,然后进行渲染

同时,如果您确定它是
UYVY422
格式,
SDL2
可以直接为您处理渲染

在下面的示例中,我的格式是
yuv420p
,我使用swscale转换为
UYVY422
以渲染为
SDL2

// prepare swscale context, AV_PIX_FMT_UYVY422 is my destination pix format
SwsContext *swsCtx = sws_getContext(codecCtx->width, codecCtx->height, codecCtx->pix_fmt,
                                    codecCtx->width, codecCtx->height, AV_PIX_FMT_UYVY422,
                                    SWS_FAST_BILINEAR, NULL, NULL, NULL);

SDL_Init(SDL_INIT_EVERYTHING);

SDL_Window *window;
SDL_Renderer *render;
SDL_Texture *texture;

SDL_CreateWindowAndRenderer(codecCtx->width,
                            codecCtx->height, SDL_WINDOW_OPENGL, &window, &render);

texture = SDL_CreateTexture(render, SDL_PIXELFORMAT_UYVY, SDL_TEXTUREACCESS_STREAMING,
                            codecCtx->width, codecCtx->height);

// ......
// decode the frame 
// ......
AVFrame *frameUYVY = av_frame_alloc();
av_image_alloc(frameUYVY->data, frameUYVY->linesize, codecCtx->width, codecCtx->height, AV_PIX_FMT_UYVY422, 32);

SDL_LockTexture(texture, NULL, (void **)frameUYVY->data, frameUYVY->linesize);

// convert the decoded frame to destination frameUYVY (yuv420p -> uyvy422)
sws_scale(swsCtx, frame->data, frame->linesize, 0, frame->height,
                      frameUYVY->data, frameUYVY->linesize);

SDL_UnlockTexture(texture);

// performa render
SDL_RenderClear(render);
SDL_RenderCopy(render, texture, NULL, NULL);
SDL_RenderPresent(render);

在您的示例中,如果您的pix格式为
uyvy422
,则可以跳过swscale部分,并在从
ffmpeg
解码后直接执行渲染。对于同一视频文件,我使用“h264”解码器获得yuv420p,但uyvy422使用“h264\u vda”解码器。如果像素格式由视频文件本身确定,是否意味着ffmpeg正在更改像素格式?@shintaroid您指的是哪种pix_格式?
AVCodec.pix_-fmts
AVCodecContext.pix_-fmt
?您应该在
AVCodecContext
中使用
pix_-fmt
,打开输入时会检测到它,ffmpeg通常不会为您更改它。
AVCodec.pix_-fmts
对于
h264\vda
有许多功能支持像素格式,包括
uyvy422
。如果像你说的那样,那就很奇怪了。你能展示一些你是如何做到这一点的代码吗?谢谢!我在问题的底部添加了一些示例代码。在Xcode调试模式下,当在示例代码的最后一行运行时(即
if((ret=avcodec\u open2(pCodecCtx,pCodec,NULL))<0){
pCodecCtx
pCodec
都显示uyvy422 pix_fmt。但是,当使用
h264
解码器时,两者都显示yuv420p pix_fmt。@shintaroid确实找出了ffmpeg库中的错误和发生了什么。在我的示例中,无论使用什么解码器,
pCodecCtx.pix_fmt
都将保持像素格式不变om输入文件。不过,回到您的问题,如果直接渲染到SDL2,它是否有效?对不起,我使用GLFW进行渲染,所以我不确定您的代码是否有效。您认为解码器是否可以输出不同的像素格式?我使用“h264”解码器获得yuv420p,但使用“h264\U vda”解码器获得uyvy422对于相同的视频文件。如果像素格式由视频文件本身决定,是否意味着ffmpeg正在更改像素格式?@shintaroid您指的是哪种pix_格式?
AVCodec.pix_fmts
AVCodecContext.pix_fmt
?您应该在
AVCodecContext
中使用
pix_fmt
,打开输入时会检测到它,ffmpeg通常不会改变这一点