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. 性能统计:计算并输出编码过程的总耗时。

 

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/953756.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

65.在 Vue 3 中使用 OpenLayers 绘制带有箭头的线条

前言 在现代的前端开发中&#xff0c;地图已经成为许多项目的核心功能之一。OpenLayers 是一个强大的开源地图库&#xff0c;它提供了丰富的功能和高度的定制化支持。在本篇文章中&#xff0c;我将向大家展示如何在 Vue 3 中使用 OpenLayers 绘制带有箭头的线条。 我们将实现…

C++内存泄露排查

内存泄漏是指程序动态分配的内存未能及时释放&#xff0c;导致系统内存逐渐耗尽&#xff0c;最终可能造成程序崩溃或性能下降。在C中&#xff0c;内存泄漏通常发生在使用new或malloc等分配内存的操作时&#xff0c;但没有正确地使用delete或free来释放这块内存。 在日常开发过程…

Ubuntu上,ffmpeg如何使用cuda硬件解码、编码、转码加速

本文使用 Ubuntu 环境。Ubuntu 直接使用 APT 安装的就支持 CUDA 加速。本文使用这样下载的版本进行演示&#xff0c;你自己编译或者其他源的版本可能会不同。 ffmpeg 的一些介绍&#xff0c;以及 macOS 版本的 ffmpeg 硬件加速请见《macOS上如何安装&#xff08;不需要编译安装…

linux: 文本编辑器vim

文本编辑器 vi的工作模式 (vim和vi一致) 进入vim的方法 方法一:输入 vim 文件名 此时左下角有 "文件名" 文件行数,字符数量 方法一: 输入 vim 新文件名 此时新建了一个文件并进入vim,左下角有 "文件名"[New File] 灰色的长方形就是光标,输入文字,左下…

调用企业微信新建日程 API 报 api forbidden 的解决方案

报错详细信息&#xff1a; {"errcode":48002,"errmsg":"api forbidden, hint: [1266719663513970651415782], from ip: xxx.xxx.xxx.xxx, more info at https://open.work.weixin.qq.com/devtool/query?e48002" } 解决方案&#xff1a; 1. 登…

rtthread学习笔记系列(4/5/6/7/15/16)

文章目录 4. 杂项4.1 检查是否否是2的幂 5. 预编译命令void类型和rt_noreturn类型的区别 6.map文件分析7.汇编.s文件7.1 汇编指令7.1.1 BX7.1.2 LR链接寄存器7.1.4 []的作用7.1.4 简单的指令 7.2 MSR7.3 PRIMASK寄存器7.4.中断启用禁用7.3 HardFault_Handler 15 ARM指针寄存器1…

微软与腾讯技术交锋,TRELLIS引领3D生成领域多格式支持新方向

去年 11 月&#xff0c;腾讯推出 Hunyuan3D 生成模型&#xff0c;是业界首个同时支持文字和图像生成 3D 的开源大模型。紧接着不到一个月&#xff0c;微软便发布了全新框架 TRELLIS&#xff0c;加入 3D 资产生成领域的竞争中。TRELLIS 支持多格式输出&#xff0c;包括辐射场、3…

【C++】类与对象(中上)(难点部分)

目录 &#x1f495;1.类的默认成员函数 &#x1f495;2.构造函数 &#x1f495;3.析构函数 &#x1f495;4.缺省值 &#x1f495;5.拷贝构造函数 &#xff08;最新更新时间——2025.1.14&#xff09; 这世间没有绝境 只有对处境绝望的人 &#x1f495;1.类的默认成员函数 默…

Apache Hop从入门到精通 第三课 Apache Hop下载安装

1、下载 官方下载地址&#xff1a;https://hop.apache.org/download/&#xff0c;本教程是基于apache-hop-client-2.11.0.zip进行解压&#xff0c;需要jdk17&#xff0c;小伙伴们可以根据自己的需求下载相应的版本。如下图所示 2、下载jdk17&#xff08;https://www.microsoft…

springboot房屋租赁管理系统

Spring Boot房屋租赁管理系统是一种基于Spring Boot框架构建的&#xff0c;旨在解决传统租房市场中房源信息更新不及时、虚假信息泛滥、交易流程繁琐等问题的信息化解决方案。 一、系统背景与目的 随着城市化进程的加快和人口流动性的增强&#xff0c;租房市场需求急剧增长。…

计算机网络 (35)TCP报文段的首部格式

前言 计算机网络中的TCP&#xff08;传输控制协议&#xff09;报文段的首部格式是TCP协议的核心组成部分&#xff0c;它包含了控制TCP连接的各种信息和参数。 一、TCP报文段的结构 TCP报文段由首部和数据两部分组成。其中&#xff0c;首部包含了控制TCP连接的各种字段&#xff…

鸿蒙-页面和自定义组件生命周期

页面生命周期&#xff0c;即被Entry装饰的组件生命周期&#xff0c;提供以下生命周期接口&#xff1a; onPageShow&#xff1a;页面每次显示时触发一次&#xff0c;包括路由过程、应用进入前台等场景。onPageHide&#xff1a;页面每次隐藏时触发一次&#xff0c;包括路由过程、…

道旅科技借助云消息队列 Kafka 版加速旅游大数据创新发展

作者&#xff1a;寒空、横槊、娜米、公仪 道旅科技&#xff1a;科技驱动&#xff0c;引领全球旅游分销服务 道旅科技 &#xff08;https://www.didatravel.com/home&#xff09; 成立于 2012 年&#xff0c;总部位于中国深圳&#xff0c;是一家以科技驱动的全球酒店资源批发商…

【HarmonyOS NEXT】鸿蒙跳转华为应用市场目标APP下载页

【HarmonyOS NEXT】鸿蒙跳转华为应用市场目标APP下载页 一、问题背景&#xff1a; 如今&#xff0c;大家都离不开各种手机应用。随着鸿蒙系统用户越来越多&#xff0c;大家都希望能在鸿蒙设备上快速找到想用的 APP。华为应用市场里有海量的 APP&#xff0c;但之前从鸿蒙设备进…

JavaScript动态渲染页面爬取之Splash

Splash是一个 JavaScript渲染服务,是一个含有 HTTP API的轻量级浏览器,它还对接了 Python 中的 Twisted 库和 OT库。利用它&#xff0c;同样可以爬取动态渲染的页面。 功能介绍 利用 Splash&#xff0c;可以实现如下功能&#xff1a; 异步处理多个网页的渲染过程:获取渲染后…

Thrustmaster Hotas Warthog飞行操作杆开发

目录 0 摘 要 &#xff1a;简单说一下这篇文章在搞啥 1 背 景 &#xff1a;什么需求以及对开发的背景调查 2 环境配置 &#xff1a;具体需要什么环境&#xff0c;对软件层面的需求 3 硬件测试 &#xff1a;测试遥感器…

算法-查找数组对角线上最大的质数

力扣题目&#xff1a;2614. 对角线上的质数 - 力扣&#xff08;LeetCode&#xff09; 给你一个下标从 0 开始的二维整数数组 nums 。 返回位于 nums 至少一条 对角线 上的最大 质数 。如果任一对角线上均不存在质数&#xff0c;返回 0 。 注意&#xff1a; 如果某个整数大于…

电梯系统的UML文档02

现在我们来回答用UML 设计电梯系统的实践中遇到的问题&#xff1a;“UML 是一种适合于实时系统的建模语言吗?”我们发现基于上段提到的特征&#xff0c;UML 是适合的但有不足。用UML 设计实时系统有以下问题&#xff1a; •特定硬件及它们特征的定义。 •在对象、任务和硬件层…

mysql set age=‘0‘ 和 set age=0的区别?

select case when(t1.business_transfer‘source’)then 0 else t1.settlement_tyy_cash_amount end as tyy from t_settlement_waybill t1 where waybill_sn in (‘2025010700001’); select case when(t1.business_transfer‘source’)then (t1.settlement_tyy_cash_amount‘…

利用Java爬虫按图搜索1688商品(拍立淘)的实践指南

在当今数字化时代&#xff0c;网购已成为人们生活中不可或缺的一部分。而1688作为国内领先的B2B电商平台&#xff0c;汇聚了海量的商品资源。然而&#xff0c;在面对众多商品时&#xff0c;传统的文字搜索方式有时难以满足我们的需求。比如&#xff0c;当我们看到一件心仪的商品…