欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 房产 > 家装 > ffmpeg硬件编码

ffmpeg硬件编码

2025/1/18 23:31:53 来源:https://blog.csdn.net/qq_42805085/article/details/145120849  浏览:    关键词:ffmpeg硬件编码

使用FFmpeg进行硬件编码可以显著提高视频编码的性能,尤其是在处理高分辨率视频时。硬件编码利用GPU或其他专用硬件(如Intel QSV、NVIDIA NVENC、AMD AMF等)来加速编码过程。以下是使用FFmpeg进行硬件编码的详细说明和示例代码。

 

1. 硬件编码支持的检查

在开始之前,确保你的系统支持硬件编码。可以通过以下命令检查FFmpeg支持的硬件编码器:

ffmpeg -hwaccels

 然后检查可用的硬件编码器:

ffmpeg -encoders | grep h264

2. 硬件编码的基本流程

硬件编码的基本流程与软件编码类似,但需要额外设置硬件设备上下文和硬件帧上下文。以下是主要步骤:

  1. 初始化硬件设备上下文:指定硬件加速类型(如QSV、CUDA等)。

  2. 创建硬件帧上下文:配置硬件帧的格式、分辨率等。

  3. 查找硬件编码器:如h264_qsvh264_nvenc等。

  4. 配置编码器上下文:绑定硬件帧上下文,设置编码参数。

  5. 编码帧:将帧数据发送到编码器,接收编码后的数据包。

  6. 写入输出文件:将编码后的数据包写入文件。

 3. 示例代码(Intel QSV 硬件编码)

#include <iostream>
#include <chrono>
extern "C" {
#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
#include <libavutil/hwcontext.h>
#include <libswscale/swscale.h>
#include <libavutil/opt.h>
#include <libavutil/imgutils.h>
#ifdef HAVE_NVENC
#include <libnvenc/nvenc.h>
#endif
#ifdef HAVE_QSV
#include <libx264/x264.h> // 注意:QSV的实际头文件可能与此不同,这只是一个示例
#endif
}
#include <iostream>
#include <stdexcept>
#include <thread>
#include <chrono>
// 记录当前时间,用于计算编码过程的总耗时
auto nows = std::chrono::steady_clock::now();// 输出文件名
const char* output_filename = "output.h264";
// 视频分辨率
const int width = 1920;
const int height = 1080;
// 帧率(30帧/秒)
const AVRational frame_rate = { 30, 1 };
// 硬件像素格式(Intel QSV)
const AVPixelFormat hw_pix_fmt = AV_PIX_FMT_QSV;
// 软件像素格式(NV12)
const AVPixelFormat sw_pix_fmt = AV_PIX_FMT_NV12;// FFmpeg 相关上下文和结构体
AVFormatContext* fmt_ctx = nullptr;  // 输出文件上下文
AVCodecContext* codec_ctx = nullptr; // 编码器上下文
AVFrame* hw_frame = nullptr;         // 硬件帧
AVFrame* sw_frame = nullptr;         // 软件帧
AVPacket* pkt = nullptr;             // 编码后的数据包
AVBufferRef* hw_device_ctx = nullptr; // 硬件设备上下文try {// 1. 初始化硬件设备上下文int ret = av_hwdevice_ctx_create(&hw_device_ctx, AV_HWDEVICE_TYPE_QSV, nullptr, nullptr, 0);if (ret < 0) {throw std::runtime_error("Failed to create hardware device context");}// 2. 创建硬件帧上下文AVBufferRef* hw_frames_ref = av_hwframe_ctx_alloc(hw_device_ctx);if (!hw_frames_ref) {throw std::runtime_error("Failed to create hardware frames context");}// 配置硬件帧上下文参数AVHWFramesContext* hw_frames_ctx = (AVHWFramesContext*)hw_frames_ref->data;hw_frames_ctx->format = AV_PIX_FMT_QSV;       // 硬件像素格式hw_frames_ctx->sw_format = AV_PIX_FMT_NV12;   // 软件像素格式hw_frames_ctx->width = width;                 // 视频宽度hw_frames_ctx->height = height;               // 视频高度hw_frames_ctx->initial_pool_size = 20;        // 初始帧池大小// 初始化硬件帧上下文ret = av_hwframe_ctx_init(hw_frames_ref);if (ret < 0) {throw std::runtime_error("Failed to initialize hardware frames context");}// 3. 查找编码器(使用 Intel QSV 的 H.264 编码器)const AVCodec* codec = avcodec_find_encoder_by_name("h264_qsv");if (!codec) {throw std::runtime_error("Codec h264_qsv not found");}// 4. 创建编码器上下文codec_ctx = avcodec_alloc_context3(codec);if (!codec_ctx) {throw std::runtime_error("Could not allocate video codec context");}// 配置编码器参数codec_ctx->hw_frames_ctx = av_buffer_ref(hw_frames_ref); // 绑定硬件帧上下文codec_ctx->width = width;                                // 视频宽度codec_ctx->height = height;                              // 视频高度codec_ctx->time_base = av_inv_q(frame_rate);             // 时间基(帧率的倒数)codec_ctx->framerate = frame_rate;                       // 帧率codec_ctx->pix_fmt = AV_PIX_FMT_QSV;                     // 像素格式codec_ctx->bit_rate = 4000000;                           // 码率(4 Mbps)codec_ctx->gop_size = 1;                                 // GOP 大小(关键帧间隔)// 打开编码器ret = avcodec_open2(codec_ctx, codec, nullptr);if (ret < 0) {throw std::runtime_error("Could not open codec");}// 5. 创建硬件帧hw_frame = av_frame_alloc();if (!hw_frame) {throw std::runtime_error("Could not allocate video frame");}hw_frame->format = AV_PIX_FMT_QSV; // 硬件像素格式hw_frame->width = width;           // 视频宽度hw_frame->height = height;         // 视频高度// 为硬件帧分配内存ret = av_hwframe_get_buffer(av_buffer_ref(hw_frames_ref), hw_frame, 0);if (ret < 0) {throw std::runtime_error("Could not allocate hardware frame buffer");}// 6. 创建软件帧sw_frame = av_frame_alloc();if (!sw_frame) {throw std::runtime_error("Could not allocate software frame");}sw_frame->format = AV_PIX_FMT_NV12; // 软件像素格式sw_frame->width = width;            // 视频宽度sw_frame->height = height;          // 视频高度// 为软件帧分配内存ret = av_frame_get_buffer(sw_frame, 0);if (ret < 0) {throw std::runtime_error("Could not allocate software frame buffer");}// 7. 创建输出文件上下文ret = avformat_alloc_output_context2(&fmt_ctx, nullptr, nullptr, output_filename);if (ret < 0) {throw std::runtime_error("Could not create output context");}// 8. 创建视频流AVStream* stream = avformat_new_stream(fmt_ctx, nullptr);if (!stream) {throw std::runtime_error("Could not create video stream");}// 从编码器上下文复制参数到视频流avcodec_parameters_from_context(stream->codecpar, codec_ctx);stream->time_base = AVRational{ 1, 90000 }; // 时间基// 9. 打开输出文件if (!(fmt_ctx->oformat->flags & AVFMT_NOFILE)) {ret = avio_open(&fmt_ctx->pb, output_filename, AVIO_FLAG_WRITE);if (ret < 0) {throw std::runtime_error("Could not open output file");}}// 10. 写入文件头ret = avformat_write_header(fmt_ctx, nullptr);if (ret < 0) {throw std::runtime_error("Error writing header to output file");}// 11. 编码帧FILE* f = fopen("asdhfladghakl.yuv", "rb"); // 打开 YUV 文件if (!f) {throw std::runtime_error("Could not open YUV file");}for (int i = 0; i < 50; i++) { // 编码 50 帧// 从 YUV 文件读取数据到软件帧fread(sw_frame->data[0], 1, width * height, f);      // Y 分量fread(sw_frame->data[1], 1, width * height / 2, f);  // UV 分量// 将软件帧数据拷贝到硬件帧ret = av_hwframe_transfer_data(hw_frame, sw_frame, 0);if (ret < 0) {throw std::runtime_error("Error transferring data to hardware frame");}// 设置帧的显示时间戳(PTS)hw_frame->pts = i * 3000; // PTS = 帧序号 * 帧间隔hw_frame->time_base = AVRational{ 1, 90000 }; // 时间基// 发送帧到编码器ret = avcodec_send_frame(codec_ctx, hw_frame);if (ret < 0) {throw std::runtime_error("Error sending frame to encoder");}// 接收编码后的数据包pkt = av_packet_alloc();while (ret >= 0) {ret = avcodec_receive_packet(codec_ctx, pkt);if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) {av_packet_free(&pkt);break;}// 设置数据包的流索引和时间基pkt->stream_index = stream->index;pkt->time_base = AVRational{ 1, 90000 };// 写入数据包到输出文件ret = av_interleaved_write_frame(fmt_ctx, pkt);if (ret < 0) {throw std::runtime_error("Error writing packet to file");}// 释放数据包av_packet_unref(pkt);}}// 12. 刷新编码器(发送空帧以刷新缓冲区)ret = avcodec_send_frame(codec_ctx, nullptr);while (ret >= 0) {pkt = av_packet_alloc();ret = avcodec_receive_packet(codec_ctx, pkt);if (ret == AVERROR_EOF) {break;}// 写入剩余的数据包到输出文件pkt->stream_index = stream->index;pkt->time_base = AVRational{ 1, 90000 };ret = av_interleaved_write_frame(fmt_ctx, pkt);av_packet_unref(pkt);}// 13. 写入文件尾av_write_trailer(fmt_ctx);// 关闭 YUV 文件fclose(f);std::cout << "Encoding completed successfully!" << std::endl;
}
catch (const std::exception& e) {std::cerr << "Error: " << e.what() << std::endl;
}// 计算并输出编码过程的总耗时
std::cout << "Total time taken: "<< std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::steady_clock::now() - nows).count()<< " milliseconds" << std::endl;// 14. 释放资源
if (fmt_ctx && !(fmt_ctx->oformat->flags & AVFMT_NOFILE)) {avio_closep(&fmt_ctx->pb);
}
avformat_free_context(fmt_ctx);
av_frame_free(&hw_frame);
av_frame_free(&sw_frame);
av_packet_free(&pkt);
avcodec_free_context(&codec_ctx);
av_buffer_unref(&hw_device_ctx);

代码总结

  1. 硬件初始化:初始化 Intel QSV 硬件设备上下文和硬件帧上下文。

  2. 编码器设置:查找并配置 H.264 编码器,绑定硬件帧上下文。

  3. 帧处理:从 YUV 文件读取数据,拷贝到硬件帧,编码并写入输出文件。

  4. 资源释放:释放所有分配的资源,避免内存泄漏。

  5. 性能统计:计算并输出编码过程的总耗时。

 

可以看见运行编码得到明显提升 

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com