FFmpeg使用GPU编解码,及在C++代码中实现FFmpeg使用GPU编解码
一.使用 GPU 进行编解码的常见方法
FFmpeg 是一个强大的多媒体处理工具,支持使用 GPU 进行编解码以加速视频处理。以下是使用 GPU 进行编解码的常见方法:
1. 使用 NVIDIA GPU(通过 NVENC/NVDEC)
NVIDIA 提供了 NVENC(编码)和 NVDEC(解码)硬件加速。
安装支持 GPU 的 FFmpeg
确保安装了支持 NVIDIA 的 FFmpeg 版本,可以通过编译 FFmpeg 时启用 --enable-nvenc
和 --enable-cuda
选项。
常用命令
-
编码(使用 NVENC):
ffmpeg -i input.mp4 -c:v h264_nvenc -b:v 5M output.mp4
其中
h264_nvenc
是 H.264 编码器,-b:v
指定视频比特率。 -
解码(使用 NVDEC):
ffmpeg -hwaccel cuda -i input.mp4 -c:v h264_nvenc output.mp4
-hwaccel cuda
启用 CUDA 硬件加速解码。
2. 使用 AMD GPU(通过 AMF)
AMD 提供了 AMF(Advanced Media Framework)用于硬件加速。
安装支持 AMF 的 FFmpeg
编译 FFmpeg 时启用 --enable-amf
选项。
常用命令
- 编码(使用 AMF):
ffmpeg -i input.mp4 -c:v h264_amf -b:v 5M output.mp4
h264_amf
是 H.264 编码器。
3. 使用 Intel GPU(通过 Quick Sync Video)
Intel 的 Quick Sync Video(QSV)提供硬件加速。
安装支持 QSV 的 FFmpeg
编译 FFmpeg 时启用 --enable-libmfx
选项。
常用命令
- 编码(使用 QSV):
ffmpeg -i input.mp4 -c:v h264_qsv -b:v 5M output.mp4
h264_qsv
是 H.264 编码器。
4. 使用 VAAPI(适用于 Intel 和 AMD GPU)
VAAPI 是一种通用的硬件加速 API,支持 Intel 和 AMD GPU。
安装支持 VAAPI 的 FFmpeg
编译 FFmpeg 时启用 --enable-vaapi
选项。
常用命令
- 编码(使用 VAAPI):
ffmpeg -vaapi_device /dev/dri/renderD128 -i input.mp4 -vf 'format=nv12,hwupload' -c:v h264_vaapi -b:v 5M output.mp4
-vaapi_device
指定 VAAPI 设备,-vf
指定视频滤镜。
5. 使用 OpenCL
FFmpeg 也支持通过 OpenCL 进行 GPU 加速。
安装支持 OpenCL 的 FFmpeg
编译 FFmpeg 时启用 --enable-opencl
选项。
常用命令
- 使用 OpenCL 进行滤镜处理:
ffmpeg -i input.mp4 -vf "hwupload, hwmap, overlay_opencl=10:10, hwmap" output.mp4
总结
- NVIDIA GPU:使用 NVENC/NVDEC,命令如
h264_nvenc
。 - AMD GPU:使用 AMF,命令如
h264_amf
。 - Intel GPU:使用 QSV 或 VAAPI,命令如
h264_qsv
或h264_vaapi
。 - OpenCL:适用于通用 GPU 加速。
确保硬件和驱动支持相应的编解码器,并根据需要编译 FFmpeg。
二.在C++代码中实现FFmpeg使用GPU编解码
在 C++ 中使用 FFmpeg 进行 GPU 编解码需要结合 FFmpeg 的 API 和 GPU 相关的硬件加速 API(如 NVIDIA 的 NVENC/NVDEC、AMD 的 AMF、Intel 的 QSV 等)。以下是一个简单的示例,展示如何使用 FFmpeg 的 API 结合 NVIDIA 的 NVENC/NVDEC 进行 GPU 编解码。
环境准备
- 安装 FFmpeg:确保安装了支持 GPU 加速的 FFmpeg 版本。
- 安装 NVIDIA 驱动和 CUDA:确保安装了最新的 NVIDIA 驱动和 CUDA 工具包。
- 编译 FFmpeg:编译 FFmpeg 时启用
--enable-nvenc
和--enable-cuda
选项。
示例代码
以下是一个简单的 C++ 示例,使用 FFmpeg 和 NVENC/NVDEC 进行 GPU 编解码。
extern "C" {
#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
#include <libavutil/opt.h>
#include <libavutil/hwcontext.h>
#include <libavutil/hwcontext_cuda.h>
}
#include <iostream>
#include <stdexcept>
void encode(AVCodecContext* enc_ctx, AVFrame* frame, AVPacket* pkt, FILE* outfile) {
int ret = avcodec_send_frame(enc_ctx, frame);
if (ret < 0) {
throw std::runtime_error("Error sending frame to encoder");
}
while (ret >= 0) {
ret = avcodec_receive_packet(enc_ctx, pkt);
if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) {
return;
} else if (ret < 0) {
throw std::runtime_error("Error encoding frame");
}
fwrite(pkt->data, 1, pkt->size, outfile);
av_packet_unref(pkt);
}
}
int main() {
av_register_all();
avcodec_register_all();
const char* filename = "output.h264";
FILE* outfile = fopen(filename, "wb");
if (!outfile) {
std::cerr << "Could not open " << filename << std::endl;
return 1;
}
const AVCodec* codec = avcodec_find_encoder_by_name("h264_nvenc");
if (!codec) {
std::cerr << "Codec h264_nvenc not found" << std::endl;
return 1;
}
AVCodecContext* c = avcodec_alloc_context3(codec);
if (!c) {
std::cerr << "Could not allocate video codec context" << std::endl;
return 1;
}
c->bit_rate = 400000;
c->width = 640;
c->height = 480;
c->time_base = (AVRational){1, 25};
c->framerate = (AVRational){25, 1};
c->gop_size = 10;
c->max_b_frames = 1;
c->pix_fmt = AV_PIX_FMT_CUDA;
if (avcodec_open2(c, codec, NULL) < 0) {
std::cerr << "Could not open codec" << std::endl;
return 1;
}
AVFrame* frame = av_frame_alloc();
if (!frame) {
std::cerr << "Could not allocate video frame" << std::endl;
return 1;
}
frame->format = c->pix_fmt;
frame->width = c->width;
frame->height = c->height;
if (av_frame_get_buffer(frame, 0) < 0) {
std::cerr << "Could not allocate the video frame data" << std::endl;
return 1;
}
AVPacket* pkt = av_packet_alloc();
if (!pkt) {
std::cerr << "Could not allocate AVPacket" << std::endl;
return 1;
}
for (int i = 0; i < 100; i++) {
av_frame_make_writable(frame);
// Fill frame with test pattern
for (int y = 0; y < c->height; y++) {
for (int x = 0; x < c->width; x++) {
frame->data[0][y * frame->linesize[0] + x] = x + y + i * 3;
}
}
frame->pts = i;
encode(c, frame, pkt, outfile);
}
encode(c, NULL, pkt, outfile);
fclose(outfile);
avcodec_free_context(&c);
av_frame_free(&frame);
av_packet_free(&pkt);
return 0;
}
代码说明
- 初始化 FFmpeg:调用
av_register_all()
和avcodec_register_all()
初始化 FFmpeg。 - 查找编码器:使用
avcodec_find_encoder_by_name("h264_nvenc")
查找 NVIDIA 的 H.264 编码器。 - 设置编码器参数:设置编码器的宽度、高度、比特率、帧率等参数。
- 分配帧和包:使用
av_frame_alloc()
和av_packet_alloc()
分配帧和包。 - 编码帧:使用
avcodec_send_frame()
和avcodec_receive_packet()
进行编码。 - 释放资源:编码完成后,释放编码器上下文、帧和包。
编译和运行
编译时需要链接 FFmpeg 的相关库。例如:
g++ -o gpu_encode gpu_encode.cpp -lavcodec -lavformat -lavutil -lswscale
运行生成的可执行文件,将会生成一个 H.264 编码的视频文件 output.h264
。
注意事项
- 确保系统支持 NVIDIA 的 NVENC/NVDEC。
- 根据实际需求调整编码器参数。
- 如果需要解码,可以使用
h264_cuvid
解码器,并设置AVCodecContext
的hw_device_ctx
为 CUDA 设备上下文。
通过这种方式,你可以在 C++ 中使用 FFmpeg 和 GPU 进行高效的视频编解码。