首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >视频文件的硬解

视频文件的硬解

作者头像
程序员的园
发布于 2024-12-19 15:47:16
发布于 2024-12-19 15:47:16
2.1K0
举报

之前的章节中使用软解(CPU)进行音视频解码,软解通用性强,无需考虑设备硬件;但对于高分辨率(4k、8k)视频,使用软解会导致解码时间过长,CPU资源消耗过大,影响用户体验。因此,硬件解码应运而生,硬件解码可以提高解码效率,提升用户体验。

1.硬件解码

硬件解码(Hardware Decoding)是指使用硬件加速设备(如GPU、专用的视频解码芯片等)来解码文件的过程,这些硬件通常经过优化,能够以更高效的方式进行计算密集型的解码任务,不仅可以提高解码速度,而且可以减少系统资源消耗。因此,硬件解码广泛应用于高清视频播放、视频流媒体处理、实时视频会议以及视频编辑等场景。

硬件解码具有明显的优势:

  • 性能优势:硬件解码使用的是专门为解码任务设计的硬件单元,如GPU、VPU(视频处理单元)等,它们能够提供远高于CPU的解码性能,尤其在高清视频和高压缩率视频的解码中表现突出。
  • 节省CPU资源:硬件解码能够将解码过程中的计算工作交给硬件处理,从而释放CPU资源,使得系统可以专注于其他任务,如图形渲染或多任务处理。
  • 降低功耗:与CPU进行软件解码相比,硬件解码通常更加节能。这在移动设备(如手机、平板等)或需要长时间运行的视频应用中尤为重要。
  • 支持更高分辨率和比特率:硬件解码可以处理高分辨率(如4K、8K)和高比特率的视频流,而这些任务通过软件解码时会显得非常耗时,且可能导致系统性能下降。

2.硬解解码的应用

2.1解码器选择

如前文所述,硬件解码需要专门的硬件加速设备,并且不同的机型上的硬件加速设备不一定相同,如Intel的QuickSync、AMD的VCE、NVIDIA的CUDA等。为此,使用硬件解码时,需要先确定当前设备是否支持硬件解码,如果支持,则选择合适的硬件解码器进行解码。

在硬解的设备选择过程中,FFmpeg提供了如下接口:

  • av_hwdevice_iterate_types函数枚举当前可用的硬件设备类型;
  • av_hwdevice_ctx_create函数来创建硬件设备上下文,当且仅当该函数返回成功,则当前设备支持对应类型的硬件解码;
  • avcodec_get_hw_config函数用于获取硬件配置信息,配置信息中含有硬件解码的文件的格式

函数的原型如下:

代码语言:javascript
复制
//枚举设备类型
//参数:prev:上一个设备类型,通常为AV_HWDEVICE_TYPE_NONE。
//返回值:返回下一个可用的设备类型,如果没有更多设备类型,则返回AV_HWDEVICE_TYPE_NONE。
enum AVHWDeviceType av_hwdevice_iterate_types(enum AVHWDeviceType prev);
//创建硬件设备上下文
//参数:device_ctx:指向AVBufferRef结构体的指针,用于存储创建的硬件设备上下文。
// type:硬件设备类型,例如AV_HWDEVICE_TYPE_CUDA、AV_HWDEVICE_TYPE_VAAPI等。
// device:特定于硬件设备的字符串,例如CUDA设备名称、VAAPI设备名称等,通常为nullptr
// opts:用于设置硬件设备上下文的选项,可以为NULL。
// flags:用于设置硬件设备上下文的标志,可以为0。
//返回值:成功返回0,失败返回负值。
int av_hwdevice_ctx_create(AVBufferRef device_ctx, const AVHWDeviceType type,
const char *device, AVDictionary *opts, int flags);
//获取硬件配置信息
//参数:codec:指向AVCodec结构体的指针,用于指定要获取硬件配置信息的解码器。
// index:指定要获取的硬件配置信息的索引,通常为0。
//返回值:成功返回指向AVCodecHWConfig结构体的指针,失败返回NULL。
const AVCodecHWConfig *avcodec_get_hw_config(const AVCodec *codec, int index);

所以设备类型的选择可以通过如下代码实现:

代码语言:javascript
复制
enum AVHWDeviceType type = AV_HWDEVICE_TYPE_NONE;//设备类型
AVBufferRef *hw_device_ctx = nullptr;//硬件设备上下文
enum AVPixelFormat hw_pix_fmt{ AV_PIX_FMT_NONE };//硬件纹理格式
while ((type = av_hwdevice_iterate_types(type)) != AV_HWDEVICE_TYPE_NONE) {
int ret = av_hwdevice_ctx_create(&hw_device_ctx, type, nullptr, nullptr, 0);
if (ret >= 0)
{
break;//找到硬件解码的支持类型,退出循环
}
}
if(type == AV_HWDEVICE_TYPE_NONE)
{
std::cerr << "No suitable hardware device found" << std::endl;
if(hw_device_ctx)
{
av_buffer_unref(&hw_device_ctx);
}
return -1;
}
for (int i = 0;; i++)
{
const AVCodecHWConfig* config = avcodec_get_hw_config(decoder, i);
if (!config)
{
fprintf(stderr, "Decoder %s does not support device type %s.\n",
decoder->name, av_hwdevice_get_type_name(hw_device_type));
return;
}
if (config->methods & AV_CODEC_HW_CONFIG_METHOD_HW_DEVICE_CTX &&
config->device_type == hw_device_type)
{
hw_pix_fmt = config->pix_fmt;
break;
}
}
//now can use device type and hw_device_ctx to decode

2.2解码器上下文

在真正解码过程中无论是发送数据包还是获取解码完的数据帧,都需要的是解码器上下文,因此,无论是硬件解码还是软件解码均需要创建解码器上下文并进行参数的复制。只是区别于软解,硬解需要多做如下工作:

  • 需要在打开解码器前将上文创建的硬件设备上下文传递给解码器上下文中的hw_device_ctx变量
  • 需要在打开解码器前为解码器上下文设置磋商纹理格式的回调函数,用于协商硬件解码器与解码器之间的纹理格式,该回调函数赋值给变量get_format

具体代码如下:

代码语言:javascript
复制
static enum AVPixelFormat get_hw_format(AVCodecContext* ctx,
const enum AVPixelFormat* pix_fmts)
{
const enum AVPixelFormat* p;
for (p = pix_fmts; *p != -1; p++) {
if (*p == hw_pix_fmt)//hw_pix_fmt为上文获取的硬件的配置信息中的纹理格式
return *p;
}
fprintf(stderr, "Failed to get HW surface format.\n");
return AV_PIX_FMT_NONE;
}
if (hw_device_ctx)
{
dec_ctx->hw_device_ctx = av_buffer_ref(hw_device_ctx);
dec_ctx->get_format = get_hw_format;
}

至此已完成硬解相关的解码器上下文的设置,可以直接参照软解的流程进行解码。读取文件数据包,发送数据包,获取帧数据的流程与软解的流程是一样的。

2.3数据转换

只是此时获取的数据帧是硬件帧(存储在硬件相关的缓冲区),数据格式和特性是由硬件设备本身决定的,与软件帧(存储在系统内存的常规缓冲区)格式不同,不符合后续处理的要求。所以需要数据转换的环节——将硬件帧转换为软件帧。

FFmmpeg提供了一个函数av_hwframe_transfer_data用于将硬件帧转换为软件帧。该函数的原型如下:

代码语言:javascript
复制
//数据转换
//参数:
// dst:指向目标AVFrame结构体的指针,用于存储转换后的软件帧。
// src:指向源AVFrame结构体的指针,用于存储硬件帧。
// flags:用于设置转换的标志,可以为0。
//返回值:成功返回0,失败返回负值。
int av_hwframe_transfer_data(AVFrame *dst, const AVFrame *src, int flags);

借助如上函数获得的软件帧,即可进行后续的处理。

如上已列出硬解区别于软解的关键接口,可自行书写硬件解码的代码

我本将全部的代码写到了本文中,奈何阅读体验不佳,如果需要如上代码的话,可加好友获取。

3.总结

本文主要介绍了FFmpeg中硬件解码的基本流程,包括硬件设备的选择、解码器上下文的创建、数据转换等关键步骤。通过硬件解码,可以大大提升视频解码的效率,减少系统负担,提高视频播放的流畅性。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2024-12-12,如有侵权请联系 cloudcommunity@tencent.com 删除
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档