1、背景
上篇文章我们详细阐述了windows环境下使用ffmpeg进行软解码的步骤,并给出了完整代码。 FFmpeg 4.x 从入门到精通(一)—— QT 中如何用 FFmpeg 实现软件解码
接下来这篇文章继续进行深入,带大家学习使用FFmpeg实现windows下的硬解码。
FFmpeg下载地址(粉丝免费下载): download.csdn.net/download/u0…
本文的语言环境基于C++,界面部分是 QT。
2、流程分析
老规矩,在开始看代码之前,我们必须先了解下ffmpeg硬解的常规流程:

红色的部分是和软解的区别。
虽然有些函数在第一篇文章已经解释了,但是为了使文章显得完整,因为本文也会再讲一遍。
1.1 avformat_open_input
为 AVFormatContext 分配空间,打开输入的视频数据并且探测视频的格式,这个函数里面包含了复杂的格式解析与探测算法,可解析的内容包括:视频流、音频流、视频流参数、音频流参数、视频帧索引等。用雷神的话说就是 可以算作FFmpeg的“灵魂”。
1.2 avformat_find_stream_info
获取多媒体流的信息,包括码流、帧率、时长等信息。但是有些早期格式或者裸流数据它的索引并没有放到头当中,因此需要在后面进行探测。注意一个视频文件中可能会同时包括视频文件、音频文件、字幕文件等多个媒体流。
1.3 av_find_best_stream
当视频被解封装出来后,需要分开处理音频和视频,需要找到对应的音频流和视频流,获取音视频对应的stream_index。
1.4 enum AVHWDeviceType av_hwdevice_find_type_by_name(const char *name)
对于硬解码来说就不能使用avcodec_find_decoder方法来查找解码器了。而是使用av_hwdevice_find_type_by_name方法。要知道avcodec_find_decoder根据 AVCodecID 查找,比如我们要解码H264,那入参就是AV_CODEC_ID_H264,找到先注册的decoder就退出了,可能得到的这个解码器不能去解码AV_CODEC_ID_VP6文件。最主要的是:同一个 AVCodecID 可能对应多个不同的解码器 (AVCodec),他们有不同的 AVCodec.name。
但是比如说你想在windows下使用dxva解码,不管视频源是h264、H265,使用av_hwdevice_find_type_by_name函数,入参是dxva就行,得到的解码器就可以解H264和H265(当然前提是你的解码器支持解码这两种编码)。
所以说这是两个完全不同的方法,可能看起来类似,但千万不要被它迷惑。
【学习地址】: FFmpeg/WebRTC/RTMP/NDK/Android音视频流媒体高级开发
【文章福利】:免费领取更多音视频学习资料包、大厂面试题、技术视频和学习路线图,资料包括(C/C++,Linux,FFmpeg webRTC rtmp hls rtsp ffplay srs 等等)有需要的可以点击 1079654574 加群领取哦~

1.5 const AVCodecHWConfig *avcodec_get_hw_config(const AVCodec *codec, int index);
获取到该解码器codec的硬件属性,比如可以支持的目标像素格式等。
1.6 avcodec_alloc_context3
创建AVCodecContext并分配空间。
1.7 avcodec_parameters_to_context
该函数用于将流里面的参数,也就是AVStream里面的参数直接复制到AVCodecContext的上下文当中,执行真正的内容拷贝。avcodec_parameters_to_context()是新的API,替换了旧版本的avcodec_copy_context()。
1.8 av_hwdevice_ctx_create
初始化硬件,打开硬件,创建硬件设备相关的上下文信息AVHWDeviceContext,包括分配内存资源、对硬件设备进行初始化。 初始化硬件设备完成后,还有个必须的操作是将获取到的硬件信息绑定到AVCodecContext的hw_device_ctx指针上。
如果使用软解码则默认有一个软解码的缓冲区(获取AVFrame的),而硬解码则需要额外创建硬件解码的缓冲区。这个缓冲区变量为hw_frames_ctx,不手动创建,则在调用avcodec_send_packet()函数内部自动创建一个,但是必须手动赋值硬件解码缓冲区引用hw_device_ctx(它是一个AVBufferRef变量),不然的话调用avcodec_send_packet的时候会返回错误。
1.9 avcodec_open2
用给定的 AVCodec 去初始化 AVCodecContext。
到这儿,解码器的初始化工作已经完成。下面就可以开始真正的解码操作了。
1.10 av_read_frame
读取码流中的音频若干帧或者视频一帧,av_read_frame()函数是新型ffmpeg的用法,对 av_read_packet 进行了封装,旧用法之所以被抛弃,就是因为以前获取的数据可能不是完整的,而av_read_frame()保证了视频数据一帧的完整性,使读出的数据总是完整的一帧。
1.11 avcodec_send_packet
发送数据到后台解码队列。
It can be NULL (or an AVPacket with data set to NULL and
size set to 0); in this case, it is considered a flush
packet, which signals the end of the stream. Sending the
first flush packet will return success. Subsequent ones are
unnecessary and will

本文介绍了在QT环境中使用FFmpeg 4.2进行硬解码的流程,包括avformat_open_input、avformat_find_stream_info、avcodec_open2等关键步骤,并探讨了av_hwframe_transfer_data数据转移的效率问题,提供了在QT中使用QImage显示解码后的视频帧的方法。
—— QT 中用 FFmpeg 实现硬解码并使用QImage显示&spm=1001.2101.3001.5002&articleId=129954703&d=1&t=3&u=69bece4514fc4231a06697622429616b)
1985

被折叠的 条评论
为什么被折叠?



