【CUDA】使用FFmpeg和CUDA加速实现RTMP推流

759 阅读4分钟

关键词:C++、FFMPEG、OPENCV、RTMP、 CUDA、 NVIDIA、 GPU

一. 引言

  实时推流是一个常见的需求,为了满足高效、低延迟的推流需求,硬件加速(如NVIDIA的CUDA)成为了不可或缺的技术。鉴于自己前面两篇博客简介了基础版的推流操作以及在cpu中的加速操作:
1.【DLL】基于Python与C++的FFmpeg与OpenCV实践

2.【CPU】SIMD流指令集优化FFMPEG在因特尔CPU中的加速操作提升推流效率

  在本文中将详细介绍如何使用FFmpeg结合CUDA加速,实现从OpenCV摄像头捕获视频到RTMP推流的核心流程。

准备工作

  在开始之前,确保以下环境已正确配置:

  • NVIDIA驱动:安装最新版本的NVIDIA显卡驱动;
  • CUDA Toolkit:安装与显卡驱动兼容的CUDA Toolkit;
  • FFmpeg:编译支持CUDA和NVENC的FFmpeg(需启用--enable-cuda--enable-nvenc选项);
  • OpenCV:安装支持视频捕获的OpenCV库,进行验证;

在自己的win系统中的ffmpeg库中查看得知如下:

E:\ffmpeg\bin\win64>ffmpeg -encoders
ffmpeg version 4.2.1 Copyright (c) 2000-2019 the FFmpeg developers
  built with gcc 9.1.1 (GCC) 20190807
  configuration: --disable-static --enable-shared --enable-gpl --enable-version3 --enable-sdl2 --enable-fontconfig --enable-gnutls --enable-iconv --enable-libass --enable-libdav1d --enable-libbluray --enable-libfreetype --enable-libmp3lame --enable-libopencore-amrnb --enable-libopencore-amrwb --enable-libopenjpeg --enable-libopus --enable-libshine --enable-libsnappy --enable-libsoxr --enable-libtheora --enable-libtwolame --enable-libvpx --enable-libwavpack --enable-libwebp --enable-libx264 --enable-libx265 --enable-libxml2 --enable-libzimg --enable-lzma --enable-zlib --enable-gmp --enable-libvidstab --enable-libvorbis --enable-libvo-amrwbenc --enable-libmysofa --enable-libspeex --enable-libxvid --enable-libaom --enable-libmfx --enable-amf --enable-ffnvcodec --enable-cuvid --enable-d3d11va --enable-nvenc --enable-nvdec --enable-dxva2 --enable-avisynth --enable-libopenmpt
  libavutil      56. 31.100 / 56. 31.100
  libavcodec     58. 54.100 / 58. 54.100
  libavformat    58. 29.100 / 58. 29.100
  libavdevice    58.  8.100 / 58.  8.100
  libavfilter     7. 57.100 /  7. 57.100
  libswscale      5.  5.100 /  5.  5.100
  libswresample   3.  5.100 /  3.  5.100
  libpostproc    55.  5.100 / 55.  5.100

  目前基础库都安装结束后以github中的开源项目为参考,参考链接:github.com/jkuri/openc… 。该开源项目为模板进行改造工作使其能够调用cuda进行推流操作。下文中该开源项目简称“母版”

代码实现

1. CUDA硬件加速的引入

  在改进项目中,引入了CUDA硬件加速,通过FFmpeg的硬件加速API来实现视频编码的加速。与母版相比,改进工程的主要区别在于使用了h264_nvenc编码器,并配置了CUDA硬件设备上下文。

// 初始化硬件设备上下文
int ret = av_hwdevice_ctx_create(&hw_device_ctx, AV_HWDEVICE_TYPE_CUDA, NULL, NULL, 0);
if (ret < 0) {
    printf("Failed to create CUDA device context\n");
    exit(1);
}

  这里,使用av_hwdevice_ctx_create函数创建了一个CUDA设备上下文,并将其与编码器上下文关联。这使得后续的视频编码操作可以在GPU上执行,从而大幅提升编码效率。

2. 硬件帧的分配与管理

  在改进项目中,使用了硬件帧(AVFrame)来存储视频帧数据。与母版中的软件帧不同,硬件帧直接在GPU显存中分配,避免了CPU与GPU之间的数据拷贝开销。

// 分配显存帧
int ret = av_hwframe_get_buffer(codec_ctx->hw_frames_ctx, frame, 0);
if (ret < 0) {
    printf("Failed to allocate CUDA frame buffer\n");
    exit(1);
}

  通过av_hwframe_get_buffer函数,我们为硬件帧分配了显存,并将其与编码器上下文关联。这样,后续的视频帧数据可以直接上传到GPU显存中进行处理。

3. 图像格式的转换与上传

  在改进项目中,使用了sws_scale函数将OpenCV读取的BGR图像转换为NV12格式(CUDA常用的像素格式),并通过av_hwframe_transfer_data函数将数据上传到GPU显存中。

// 将BGR24转换为NV12
sws_scale(swsctx, src_data, src_linesize, 0, height, tmp_frame->data, tmp_frame->linesize);

// 上传到GPU帧
int ret = av_hwframe_transfer_data(frame, tmp_frame, 0);
if (ret < 0) {
    fprintf(stderr, "Error transferring data to GPU frame\n");
    exit(1);
}

  与母版相比,改进项目在图像格式转换后,直接将数据上传到GPU显存中,避免了CPU与GPU之间的数据拷贝,进一步提升了处理效率。

4. 编码器配置的优化

  在改进项目中,我们对编码器的配置进行了优化,使用了h264_nvenc编码器,并设置了低延迟模式和恒定码率(CBR)等参数,以适应实时推流的需求。

// 设置编码器参数
av_dict_set(&codec_options, "profile", codec_profile, 0);
av_dict_set(&codec_options, "preset", "fast", 0);        // 使用fast预设
av_dict_set(&codec_options, "tune", "ll", 0);            // 低延迟模式
av_dict_set(&codec_options, "rc", "cbr", 0);             // 恒定码率
av_dict_set_int(&codec_options, "gpu", 0, 0);            // 使用0号GPU
av_dict_set_int(&codec_options, "delay", 0, 0);          // 禁用延迟

总结

  在这里通过结合FFmpeg和CUDA,我们成功实现了高效的视频编码和RTMP推流。这篇文章是在上篇cpu加速后赶出来的,本来打算写成一个系列文章,无奈太忙了,写的不完整。趁着项目不太忙,赶紧整理了一下,纯码字,如果有任何问题可以直接留言。