?一、硬件編碼核心流程?
-
?硬件設備初始化
// 創建CUDA硬件設備上下文? AVBufferRef *hw_device_ctx = NULL; av_hwdevice_ctx_create(&hw_device_ctx, AV_HWDEVICE_TYPE_CUDA, NULL, NULL, 0);// 綁定硬件設備到編碼器上下文? codec_ctx->hw_device_ctx = av_buffer_ref(hw_device_ctx);
-
?編碼器選擇與參數配置
// 查找NVIDIA H.264硬件編碼器? const AVCodec *encoder = avcodec_find_encoder_by_name("h264_nvenc");// 設置編碼參數(分辨率、碼率、GOP等)? codec_ctx->width = 1920; codec_ctx->height = 1080; codec_ctx->bit_rate = 5000000; codec_ctx->time_base = (AVRational){1, 30}; codec_ctx->pix_fmt = AV_PIX_FMT_CUDA; // 指定硬件像素格式?// 設置編碼預設參數(NVIDIA專用)? av_opt_set(codec_ctx->priv_data, "preset", "llhp", 0); // 低延遲高性能
-
硬件幀內存分配
// 創建硬件幀并綁定GPU內存? AVFrame *hw_frame = av_frame_alloc(); hw_frame->format = codec_ctx->pix_fmt; hw_frame->width = codec_ctx->width; hw_frame->height = codec_ctx->height; av_hwframe_get_buffer(codec_ctx->hw_frames_ctx, hw_frame, 0);
-
編碼數據流處理
// 送入硬件編碼器? avcodec_send_frame(codec_ctx, hw_frame);// 接收編碼后的數據包 AVPacket *pkt = av_packet_alloc(); while (avcodec_receive_packet(codec_ctx, pkt) >= 0) {av_interleaved_write_frame(output_ctx, pkt); // 寫入輸出文件?av_packet_unref(pkt); }
?二、跨平臺適配示例?
?硬件平臺? | ?編碼器名稱? | ?像素格式? | ?初始化函數? |
---|---|---|---|
NVIDIA GPU | h264_nvenc | AV_PIX_FMT_CUDA | av_hwdevice_ctx_create(..., AV_HWDEVICE_TYPE_CUDA) ? |
Intel QSV | h264_qsv | AV_PIX_FMT_QSV | av_hwdevice_ctx_create(..., AV_HWDEVICE_TYPE_QSV) ? |
AMD AMF | h264_amf | AV_PIX_FMT_D3D11 | av_hwdevice_ctx_create(..., AV_HWDEVICE_TYPE_D3D11VA) ? |
?三、關鍵問題解決?
-
?編碼器初始化失敗?
- 檢查FFmpeg編譯時是否啟用對應硬件加速選項(如
--enable-nvenc
、--enable-libmfx
)? - 確認硬件驅動版本與FFmpeg兼容性?
- 檢查FFmpeg編譯時是否啟用對應硬件加速選項(如
-
?CPU-GPU內存拷貝開銷優化
// 使用hwupload濾鏡直接上傳數據至GPU? AVFilterContext *upload_filter; const AVFilter *hwupload = avfilter_get_by_name("hwupload"); avfilter_graph_create_filter(&upload_filter, hwupload, "upload", NULL, NULL, filter_graph);
??四、資源釋放
av_buffer_unref(&hw_device_ctx); // 釋放硬件設備上下文?
avcodec_free_context(&codec_ctx); // 釋放編碼器上下文
av_frame_free(&hw_frame); // 釋放硬件幀
av_packet_free(&pkt); // 釋放數據包
?五、編譯依賴?
- ?NVIDIA平臺?:需安裝CUDA Toolkit,編譯時添加
--enable-cuda --enable-nvenc
? - ?Intel平臺?:需安裝Intel Media SDK,編譯時添加
--enable-libmfx
?
六、硬件編碼示例代碼
支持從本地YUV文件讀取數據、GPU加速編碼并輸出H.264視頻流到MP4文件。
#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
#include <libavutil/imgutils.h>
#include <libavutil/hwcontext.h>
#include <libswscale/swscale.h>#define INPUT_FILE "input.yuv"
#define OUTPUT_FILE "output.mp4"
#define WIDTH 1280
#define HEIGHT 720
#define FRAME_RATE 30int main() {AVFormatContext *fmt_ctx = NULL;AVCodecContext *enc_ctx = NULL;AVBufferRef *hw_device_ctx = NULL;SwsContext *sws_ctx = NULL;int ret = 0;// 1. 初始化硬件設備上下文ret = av_hwdevice_ctx_create(&hw_device_ctx, AV_HWDEVICE_TYPE_CUDA, NULL, NULL, 0); // ?:ml-citation{ref="1,3" data="citationList"}if (ret < 0) {fprintf(stderr, "Failed to create CUDA device\n");goto cleanup;}// 2. 打開輸出文件并配置封裝格式avformat_alloc_output_context2(&fmt_ctx, NULL, NULL, OUTPUT_FILE); // ?:ml-citation{ref="8" data="citationList"}if (!fmt_ctx) {fprintf(stderr, "Failed to create output context\n");ret = -1;goto cleanup;}// 3. 查找并配置硬件編碼器const AVCodec *encoder = avcodec_find_encoder_by_name("h264_nvenc"); // ?:ml-citation{ref="1,4" data="citationList"}if (!encoder) {fprintf(stderr, "NVENC encoder not found\n");ret = -1;goto cleanup;}enc_ctx = avcodec_alloc_context3(encoder);enc_ctx->width = WIDTH;enc_ctx->height = HEIGHT;enc_ctx->time_base = (AVRational){1, FRAME_RATE};enc_ctx->pix_fmt = AV_PIX_FMT_CUDA; // 硬件像素格式 ?:ml-citation{ref="3,5" data="citationList"}enc_ctx->bit_rate = 4000000; // 4Mbps碼率 ?:ml-citation{ref="5,8" data="citationList"}enc_ctx->hw_device_ctx = av_buffer_ref(hw_device_ctx); // 綁定設備 ?:ml-citation{ref="3,6" data="citationList"}// 4. 打開編碼器并添加視頻流if ((ret = avcodec_open2(enc_ctx, encoder, NULL)) < 0) { // ?:ml-citation{ref="4" data="citationList"}fprintf(stderr, "Failed to open encoder\n");goto cleanup;}AVStream *stream = avformat_new_stream(fmt_ctx, NULL);avcodec_parameters_from_context(stream->codecpar, enc_ctx);// 5. 打開輸出文件并寫入頭信息if (!(fmt_ctx->oformat->flags & AVFMT_NOFILE)) {ret = avio_open(&fmt_ctx->pb, OUTPUT_FILE, AVIO_FLAG_WRITE); // ?:ml-citation{ref="8" data="citationList"}if (ret < 0) {fprintf(stderr, "Failed to open output file\n");goto cleanup;}}avformat_write_header(fmt_ctx, NULL); // ?:ml-citation{ref="8" data="citationList"}// 6. 初始化YUV到CUDA格式轉換器sws_ctx = sws_getContext(WIDTH, HEIGHT, AV_PIX_FMT_YUV420P,WIDTH, HEIGHT, AV_PIX_FMT_CUDA,SWS_BILINEAR, NULL, NULL, NULL); // ?:ml-citation{ref="4,7" data="citationList"}// 7. 準備輸入幀和硬件幀AVFrame *yuv_frame = av_frame_alloc();yuv_frame->width = WIDTH;yuv_frame->height = HEIGHT;yuv_frame->format = AV_PIX_FMT_YUV420P;av_frame_get_buffer(yuv_frame, 0);AVFrame *hw_frame = av_frame_alloc();hw_frame->format = enc_ctx->pix_fmt;hw_frame->width = WIDTH;hw_frame->height = HEIGHT;av_hwframe_get_buffer(enc_ctx->hw_frames_ctx, hw_frame, 0); // ?:ml-citation{ref="3,6" data="citationList"}// 8. 處理每一幀數據FILE *yuv_file = fopen(INPUT_FILE, "rb");AVPacket *pkt = av_packet_alloc();for (int i = 0; i < 100; i++) { // 編碼100幀測試// 從YUV文件讀取數據fread(yuv_frame->data, 1, WIDTH*HEIGHT, yuv_file); // Y分量fread(yuv_frame->data?:ml-citation{ref="1" data="citationList"}, 1, (WIDTH/2)*(HEIGHT/2), yuv_file); // U分量fread(yuv_frame->data?:ml-citation{ref="2" data="citationList"}, 1, (WIDTH/2)*(HEIGHT/2), yuv_file); // V分量// 轉換到硬件幀sws_scale(sws_ctx, (const uint8_t**)yuv_frame->data, yuv_frame->linesize,0, HEIGHT, hw_frame->data, hw_frame->linesize); // ?:ml-citation{ref="4,7" data="citationList"}// 編碼并寫入文件avcodec_send_frame(enc_ctx, hw_frame);while (avcodec_receive_packet(enc_ctx, pkt) >= 0) {av_packet_rescale_ts(pkt, enc_ctx->time_base, stream->time_base);av_interleaved_write_frame(fmt_ctx, pkt); // ?:ml-citation{ref="8" data="citationList"}av_packet_unref(pkt);}}cleanup:// 9. 釋放所有資源if (yuv_file) fclose(yuv_file);if (sws_ctx) sws_freeContext(sws_ctx);av_packet_free(&pkt);av_frame_free(&yuv_frame);av_frame_free(&hw_frame);if (fmt_ctx) avformat_free_context(fmt_ctx);avcodec_free_context(&enc_ctx);av_buffer_unref(&hw_device_ctx);return ret;
}
關鍵代碼說明:?
?組件? | ?功能說明? | ?依賴項? |
---|---|---|
hw_device_ctx | 管理CUDA設備上下文,用于GPU內存分配和硬件加速操作 | CUDA驅動和NVENC支持 ? |
sws_ctx | 將CPU端的YUV420P數據轉換為GPU端的CUDA格式(如NV12) | libswscale庫? |
av_hwframe_get_buffer | 直接從GPU顯存分配幀內存,避免CPU-GPU內存拷貝 | FFmpeg硬件幀支持 |
avcodec_send_frame | 將硬件幀送入編碼器隊列,觸發異步編碼操作 | 編碼器線程模型 |