当前位置: 首页 > article >正文

FFmpeg硬件解码

使用FFmpeg进行硬件解码时,通常需要结合FFmpeg的API和硬件加速API(如CUDA、VAAPI、DXVA2等)。以下是一个简单的C++代码示例,展示如何使用FFmpeg进行硬件解码。这个示例使用了CUDA作为硬件加速的后端。

1. 安装FFmpeg和CUDA

确保你已经安装了FFmpeg和CUDA,并且在编译时链接了相关的库。

2. 示例代码

extern "C" {
#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
#include <libavutil/hwcontext.h>
#include <libavutil/hwcontext_cuda.h>
}

#include <iostream>
#include <stdexcept>

void decode(AVCodecContext* codec_ctx, AVPacket* pkt, AVFrame* frame) {
    int ret = avcodec_send_packet(codec_ctx, pkt);
    if (ret < 0) {
        throw std::runtime_error("Error sending a packet for decoding");
    }

    while (ret >= 0) {
        ret = avcodec_receive_frame(codec_ctx, frame);
        if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) {
            return;
        } else if (ret < 0) {
            throw std::runtime_error("Error during decoding");
        }

        // 处理解码后的帧
        std::cout << "Frame decoded, width: " << frame->width << ", height: " << frame->height << std::endl;
    }
}

int main(int argc, char* argv[]) {
    if (argc < 2) {
        std::cerr << "Usage: " << argv[0] << " <input file>" << std::endl;
        return 1;
    }

    const char* filename = argv[1];

    av_register_all();
    avformat_network_init();

    AVFormatContext* format_ctx = nullptr;
    if (avformat_open_input(&format_ctx, filename, nullptr, nullptr) != 0) {
        std::cerr << "Could not open file " << filename << std::endl;
        return 1;
    }

    if (avformat_find_stream_info(format_ctx, nullptr) < 0) {
        std::cerr << "Could not find stream information" << std::endl;
        return 1;
    }

    int video_stream_index = -1;
    for (unsigned int i = 0; i < format_ctx->nb_streams; i++) {
        if (format_ctx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {
            video_stream_index = i;
            break;
        }
    }

    if (video_stream_index == -1) {
        std::cerr << "Could not find a video stream" << std::endl;
        return 1;
    }

    AVCodecParameters* codecpar = format_ctx->streams[video_stream_index]->codecpar;
    AVCodec* codec = avcodec_find_decoder(codecpar->codec_id);
    if (!codec) {
        std::cerr << "Unsupported codec" << std::endl;
        return 1;
    }

    AVCodecContext* codec_ctx = avcodec_alloc_context3(codec);
    if (!codec_ctx) {
        std::cerr << "Could not allocate video codec context" << std::endl;
        return 1;
    }

    if (avcodec_parameters_to_context(codec_ctx, codecpar) < 0) {
        std::cerr << "Could not copy codec parameters to codec context" << std::endl;
        return 1;
    }

    // 设置硬件加速
    AVBufferRef* hw_device_ctx = nullptr;
    if (av_hwdevice_ctx_create(&hw_device_ctx, AV_HWDEVICE_TYPE_CUDA, nullptr, nullptr, 0) < 0) {
        std::cerr << "Failed to create CUDA hardware device context" << std::endl;
        return 1;
    }
    codec_ctx->hw_device_ctx = av_buffer_ref(hw_device_ctx);

    if (avcodec_open2(codec_ctx, codec, nullptr) < 0) {
        std::cerr << "Could not open codec" << std::endl;
        return 1;
    }

    AVPacket* pkt = av_packet_alloc();
    AVFrame* frame = av_frame_alloc();
    if (!pkt || !frame) {
        std::cerr << "Could not allocate packet or frame" << std::endl;
        return 1;
    }

    while (av_read_frame(format_ctx, pkt) >= 0) {
        if (pkt->stream_index == video_stream_index) {
            decode(codec_ctx, pkt, frame);
        }
        av_packet_unref(pkt);
    }

    // 刷新解码器
    decode(codec_ctx, nullptr, frame);

    av_frame_free(&frame);
    av_packet_free(&pkt);
    avcodec_free_context(&codec_ctx);
    avformat_close_input(&format_ctx);
    av_buffer_unref(&hw_device_ctx);

    return 0;
}

3. 编译命令

假设你已经安装了FFmpeg和CUDA,并且它们的库和头文件在标准路径中,你可以使用以下命令编译代码:

g++ -o hw_decode hw_decode.cpp -lavcodec -lavformat -lavutil -lavdevice -lswscale -lavfilter -lavresample -lcuda

4. 运行

编译成功后,你可以运行生成的可执行文件,并传入一个视频文件作为参数:

./hw_decode input.mp4

5. 注意事项

  • 这个示例使用了CUDA作为硬件加速的后端。如果你使用其他硬件加速API(如VAAPI、DXVA2等),你需要相应地修改代码。

  • 确保你的系统支持所选的硬件加速API,并且已经正确安装了相关的驱动和库。

  • 这个示例仅展示了基本的硬件解码流程,实际应用中可能需要处理更多的细节,例如帧格式转换、错误处理等。

最后展示是一个qt的demo

https://download.csdn.net/download/qq_42805085/90262623 


http://www.kler.cn/a/502396.html

相关文章:

  • EasyExcel - 行合并策略(二级列表)
  • 中等难度——python实现电子宠物和截图工具
  • Elasticsearch快速入门
  • 【解决】okhttp的java.lang.IllegalStateException: closed错误
  • window.print()预览时表格显示不全
  • JuiceFS 2024:开源与商业并进,迈向 AI 原生时代
  • Redis HyperLogLog
  • linux 安装docker-compose
  • 黑马linux笔记(03)在Linux上部署各类软件 MySQL5.7/8.0 Tomcat(JDK) Nginx RabbitMQ
  • 基于单片机的数字气压计设计
  • 《零基础Go语言算法实战》【题目 2-25】goroutine 的执行权问题
  • 全网首发:嵌入式交叉编译libssh,正确编译脚本
  • LLM进化下的Agent演变及软件重构下的一点思考
  • Golang学习笔记_22——Reader示例
  • java项目之在线文档管理系统源码(springboot+mysql+vue+文档)
  • 从光子到图像——相机如何捕获世界?
  • 53_多级缓存基础环境搭建
  • doris:数据类型
  • win10电脑 定时关机
  • 5、波分复用 WDM
  • java项目之房屋租赁系统源码(springboot+mysql+vue)
  • 如何在后端使用redis进行缓存,任意一种语言都可以
  • nginx-lua模块安装
  • 【算法】判断一个链表是否为回文结构
  • gcc编译过程中-L和-rpath的作用
  • 农业电商|基于SprinBoot+vue的农业电商服务系统(源码+数据库+文档)