深入理解FFmpeg--软/硬件解码流程

news2024/12/23 6:10:30

FFmpeg是一款强大的多媒体处理工具,支持软件和硬件解码。软件解码利用CPU执行解码过程,适用于各种平台,但可能对性能要求较高。硬件解码则利用GPU或其他专用硬件加速解码,能显著降低CPU负载,提升解码效率和能效。FFmpeg结合这两种解码方式,提供了灵活的多媒体解决方案,适合于视频处理、流媒体和多媒体应用开发。

1、FFmpeg支持多种硬件加速类型,用于编解码视频,以提升性能和效率。以下是FFmpeg支持的主要硬件加速类型:

  1. NVIDIA NVENC/NVDEC:利用NVIDIA显卡进行视频编码(NVENC)和解码(NVDEC)。
  2. Intel Quick Sync Video (QSV):利用Intel处理器中的集成图形进行视频加速。
  3. AMD Video Coding Engine (VCE)和Video Decoding Acceleration (VDA):利用AMD显卡进行视频编码和解码。
  4. VAAPI (Video Acceleration API):适用于Intel和AMD硬件,通过通用的API接口进行硬件加速。
  5. VDPAU (Video Decode and Presentation API for Unix):主要用于NVIDIA显卡的硬件解码加速。
  6. DXVA2 (DirectX Video Acceleration):适用于Windows平台,利用DirectX进行视频加速。
  7. OpenMAX IL (Open Media Acceleration Integration Layer):用于移动设备和嵌入式系统的视频加速。
  8. Vulkan:一种跨平台的图形和计算API,也可以用于视频加速。

这些硬件加速类型使FFmpeg在处理高分辨率视频时更加高效,减少了CPU负载,提高了多媒体处理的整体性能。

2、硬件解码流程图(软解流程比起硬解少一些步骤,就不单独画了):

 3、代码示例:

Decode.h

#ifndef WINDOWS_FFMPEG_DECODE_H
#define WINDOWS_FFMPEG_DECODE_H
extern "C" {
#include "libavcodec/avcodec.h"
#include "libavformat/avformat.h"
};
#include <memory>

class Decode {
public:

    Decode();

    ~Decode();

    //初始化软解码
    //IN:AVCodecID, AVPixelFormat
    int InitSoftDecode(int VideoType, int PixFmt);

    //初始化硬解码
    //IN:AVFormatContext输入上下文, 硬解类型名称
    int InitHardDecode(AVFormatContext* input_ctx, const std::string& HWType);

    //解码视频数据
    int DecodePacket(AVPacket* packet, AVFrame* frame);

private:

    //解码器上下文的get_format函数
    static enum AVPixelFormat get_hw_format(AVCodecContext *ctx, const enum AVPixelFormat *pix_fmts);

    //初始化AVBufferRef
    static int hw_decoder_init(AVCodecContext *ctx, const enum AVHWDeviceType type);

private:
    AVCodecContext* pDecodeCtx_;
    AVCodecParserContext* pParseCtx_;
    AVStream* pVStream_;
    const AVCodec* pCodec_;
    static enum AVPixelFormat ePixFmt_;
    static AVBufferRef* pDeviceCtx;
    bool bHWDecode_;
};

#endif //WINDOWS_FFMPEG_DECODE_H

Decode.cpp

#include "Decode.h"
#include <iostream>
using namespace std;

enum AVPixelFormat Decode::ePixFmt_;
AVBufferRef* Decode::pDeviceCtx;
Decode::Decode() {

}

Decode::~Decode() {

}

enum AVPixelFormat Decode::get_hw_format(AVCodecContext *ctx, const enum AVPixelFormat *pix_fmts) {
    const enum AVPixelFormat *p;

    for (p = pix_fmts; *p != -1; p++) {
        if (*p == ePixFmt_)
            return *p;
    }

    fprintf(stderr, "Failed to get HW surface format.\n");
    return AV_PIX_FMT_NONE;
}

int Decode::hw_decoder_init(AVCodecContext *ctx, const enum AVHWDeviceType type) {
    int err = 0;

    if ((err = av_hwdevice_ctx_create(&pDeviceCtx, type,
                                      nullptr, nullptr, 0)) < 0) {
        fprintf(stderr, "Failed to create specified HW device.\n");
        return err;
    }
    ctx->hw_device_ctx = av_buffer_ref(pDeviceCtx);

    return err;
}

int Decode::InitSoftDecode(int VideoType, int PixFmt) {
    pCodec_ = avcodec_find_decoder((AVCodecID)VideoType);
    if (!pCodec_) {
        std::cout<<"avcodec_find_decoder Failed"<<std::endl;
        return -1;
    }

    pParseCtx_ = av_parser_init(pCodec_->id);
    if (!pParseCtx_) {
        std::cout<<"av_parser_init Failed"<<std::endl;
        return -1;
    }

    pDecodeCtx_ = avcodec_alloc_context3(pCodec_);
    if (!pDecodeCtx_) {
        std::cout<<"avcodec_alloc_context3 Failed"<<std::endl;
        return -1;
    }

    pDecodeCtx_->pix_fmt = (AVPixelFormat)PixFmt;
    if (avcodec_open2(pDecodeCtx_, pCodec_, nullptr) < 0) {
        std::cout<<"avcodec_open2 Failed"<<std::endl;
        return -1;
    }

    bHWDecode_ = false;
    return 0;
}

int Decode::InitHardDecode(AVFormatContext* input_ctx, const std::string& HWType) {
    enum AVHWDeviceType type;
    type = av_hwdevice_find_type_by_name(HWType.c_str());
    if (type == AV_HWDEVICE_TYPE_NONE) {
        std::cout<<"UnKnown HW Device Type"<<std::endl;
        while((type = av_hwdevice_iterate_types(type)) != AV_HWDEVICE_TYPE_NONE) {
            std::cout<< type <<std::endl;
        }
        return -1;
    }

    int video_index = av_find_best_stream(input_ctx, AVMEDIA_TYPE_VIDEO, -1 , -1, &pCodec_, 0);
    if (video_index < 0) {
        cout<<"Cannot find a video stream in the input file"<<endl;
        return -1;
    }

    for (int i = 0; ; i++) {
        const AVCodecHWConfig *config = avcodec_get_hw_config(pCodec_, i);
        if (!config) {
            cout<<"avcodec_get_hw_config Failed"<<i<<endl;
            return -1;
        }
        if (config->methods & AV_CODEC_HW_CONFIG_METHOD_HW_DEVICE_CTX &&
            config->device_type == type) {
            ePixFmt_ = config->pix_fmt;
            break;
        }
    }

    pDecodeCtx_ = avcodec_alloc_context3(pCodec_);
    if (!pDecodeCtx_) {
        cout<<"avcodec_alloc_context3 Failed"<<endl;
        return -1;
    }

    pVStream_ = input_ctx->streams[video_index];
    if (avcodec_parameters_to_context(pDecodeCtx_, pVStream_->codecpar) < 0) {
        cout<<"avcodec_parameters_to_context Failed"<<endl;
        return -1;
    }

    pDecodeCtx_->get_format = get_hw_format;
    if (hw_decoder_init(pDecodeCtx_, type) < 0) {
        return -1;
    }

    if (avcodec_open2(pDecodeCtx_, pCodec_, nullptr) < 0) {
        cout<<"avcodec_open2 Failed"<<endl;
        return -1;
    }

    bHWDecode_ = true;
    return 0;
}

int Decode::DecodePacket(AVPacket* packet, AVFrame* frame) {
    //软解码
    if (!bHWDecode_) {
        int nRet = avcodec_send_packet(pDecodeCtx_, packet);    //将AVPacket发送至解码器中
        if (nRet < 0) {
            cout<<"Error sending a packet for decoding"<<endl;
            return -1;
        }

        nRet = avcodec_receive_frame(pDecodeCtx_, frame);    //从解码器中获取被解码后的帧数据AVFrame
        if (nRet == AVERROR(EAGAIN) || nRet == AVERROR_EOF)
            return 0;
        else if (nRet < 0) {
            cout<<"Error during decoding"<<endl;
            return -1;
        }

        return 0;
    } else {    //硬解码
        AVFrame* tmpFrame = nullptr, *swFrame = nullptr;
        int nRet = avcodec_send_packet(pDecodeCtx_, packet);    //将AVPacket发送至解码器中
        if (nRet < 0) {
            cout<<"Error sending a packet for decoding"<<endl;
            av_frame_free(&tmpFrame);
            av_frame_free(&swFrame);
            return -1;
        }

        if (!(tmpFrame = av_frame_alloc()) || !(swFrame = av_frame_alloc())) {
            cout<<"Can not alloc frame"<<endl;
            av_frame_free(&tmpFrame);
            av_frame_free(&swFrame);
            nRet = AVERROR(ENOMEM);
            return -1;
        }

        nRet = avcodec_receive_frame(pDecodeCtx_, tmpFrame);    //从解码器中获取被解码后的帧数据AVFrame
        if (nRet == AVERROR(EAGAIN) || nRet == AVERROR_EOF) {
            av_frame_free(&tmpFrame);
            av_frame_free(&swFrame);
            return 0;
        } else if (nRet < 0) {
            cout<<"Error while decoding"<<endl;
            av_frame_free(&tmpFrame);
            av_frame_free(&swFrame);
            return -1;
        }

        if (frame->format == ePixFmt_) {
            /* 将GPU中的数据 移交到CPU中*/
            if (av_hwframe_transfer_data(swFrame, tmpFrame, 0) < 0) {
                cout<<"Error transferring the data to system memory"<<endl;
                av_frame_free(&tmpFrame);
                av_frame_free(&swFrame);
                return -1;
            }
            frame = swFrame;
        } else {
            frame = tmpFrame;
        }

        av_frame_free(&tmpFrame);
        av_frame_free(&swFrame);
        return 0;
    }
}

 代码仅供参考,因为电脑太旧,硬解没识别出来支持的硬件,简单跟着深入理解FFmpeg写的Demo,有问题欢迎指正。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Leetcode双指针法应用

1.双指针法 文章目录 1.双指针法1.1什么是双指针法&#xff1f;1.2解题思路1.3扩展 1.1什么是双指针法&#xff1f; 双指针算法是一种在数组或序列上操作的技巧&#xff0c;实际上是对暴力枚举算法的一种优化&#xff0c;通常涉及到两个索引&#xff08;或指针&#xff09;从两…

ubuntu 安装图形化界面

前言&#xff1a; 如果在首次安装操作系统的时候是最小化安装&#xff0c;可以参照本文进行安装 安装图形化界面软件包 下载源最好提前换成国内源 sudo apt-get install ubuntu-desktop设置图形化启动 sudo systemctl set-default graphical.target重启系统 reboot验证&…

《Techporters架构搭建》-Day02 集成Mybatis-plus

集成Mybatis-plus Mybatis-plus集成Mybatis-plus步骤小结 Mybatis-plus Mybatis-plus官网 MyBatisPlus&#xff08;简称MP&#xff09;是一个MyBatis的增强工具&#xff0c;在MyBatis的基础上只做增强不做改变&#xff0c;为简化开发、提高效率而生。它引入了一些新的特性&…

免费的数字孪生平台助力产业创新,让新质生产力概念有据可依

关于新质生产力的概念&#xff0c;在如今传统企业现代化发展中被反复提及。 那到底什么是新质生产力&#xff1f;它与哪些行业存在联系&#xff0c;我们又该使用什么工具来加快新质生产力的发展呢&#xff1f;今天我将介绍一款为发展新质生产力而量身定做的数字孪生工具。 新…

java学校--Object类方法--toString

第一点解析&#xff1a; 全类名就是包名加类名 getClass&#xff08;&#xff09;.getName()是得到其包名和类名如图&#xff0c;包名是com.hspedu.object_类名是Monster。 Integer.toHexString&#xff08;hashCode&#xff08;&#xff09;&#xff09;&#xff1b;是得到其…

【2024最新版】Vue前端面试篇,看这一篇就够了

文章目录 Vue常用的指令都有哪些v-bind和v-model的区别Vue2的生命周期有哪些Vue3的生命周期有哪些vue3中创建响应式变量的方法ref和reactive原理vuex有哪些方法vue-router生命周期钩子vue框架和原生JavaScript有什么区别对于提升项目加载速度和运行效率是怎么做的webpack能做什…

栈及栈的应用(有效的括号 力扣20)

栈的概念 栈是一种特殊的线性表&#xff0c;其只允许在固定的一端进行插入和删除元素操作。进行数据插入和删除操作的一端称为栈顶&#xff0c;另一端称为栈底。栈中的数据元素遵守后进先出LIFO&#xff08;Last In First Out&#xff09;的原则。 画个图理解一下 咱们可以观…

【笔记:3D航路规划算法】一、随机搜索锚点(python实现,讲解思路)

目录 关键概念3D路径规划算法1. A*算法2. 快速随机锚点1. 初始化&#xff1a;2. 实例化搜索算法&#xff1a;3. 路径生成&#xff1a;4. 绘制图像&#xff1a; 3D路径规划是在三维空间中寻找从起点到终点的最短或最优路径的一种技术。它广泛应用于无人机导航、机器人运动规划、…

关于垂直领域大模型的探索和尝试

节前&#xff0c;我们组织了一场算法岗技术&面试讨论会&#xff0c;邀请了一些互联网大厂朋友、今年参加社招和校招面试的同学。 针对大模型技术趋势、算法项目落地经验分享、新手如何入门算法岗、该如何准备面试攻略、面试常考点等热门话题进行了深入的讨论。 总结链接如…

SpringCloud—08—高级之SpringCloud Alibaba中—Sentinel

文章目录 提前预知18、Sentinel是什么&#xff1f;18.1、sentinel是什么&#xff1f;18.2、Sentinel下载安装运行18.3、Sentinel初始化监控18.4、Sentinel流控规则1、流控规则基本介绍2、流控规则之-QPS-直接-快速失败3、流控规则之-线程数-直接失败4、流控规则之-QPS-关联-快速…

实战篇(十):使用Processing创建可爱花朵:实现随机位置、大小和颜色的花朵

使用Processing创建可爱花朵 0.效果预览1. 引言2. 设置Processing环境3. 创建花朵类4. 实现花瓣绘制5. 绘制可爱的笑脸6. 鼠标点击生成花朵7. 完整代码8. 总结与扩展0.效果预览 在本教程中,我们将使用Processing编程语言来创建一个可爱的花朵生成器。通过封装花朵为一个类,并…

git教程, 命令行版

前言 git就是代码版本管理系统&#xff0c;很简单的作用就是每一次commit之后&#xff0c;修改文件都是跟上一次commit的仓库文件做对比&#xff0c;也可以调出历史的文件查看某次commit修改了什么东西 0环境准备&#xff1a; 安装git, 百度一下&#xff0c;然后打开cmd&…

教室管理系统的开发与实现(Java+MySQL)

引言 教室管理系统是学校和培训机构日常运营中不可或缺的工具。本文将介绍如何使用Java、Swing GUI、MySQL和JDBC开发一个简单而有效的教室管理系统&#xff0c;并涵盖系统的登录认证、教室管理、查询、启用、暂停和排课管理功能。 技术栈介绍 Java&#xff1a;作为主要编程…

[数据集][目标检测]导盲犬拐杖检测数据集VOC+YOLO格式4635张2类别

数据集格式&#xff1a;Pascal VOC格式YOLO格式(不包含分割路径的txt文件&#xff0c;仅仅包含jpg图片以及对应的VOC格式xml文件和yolo格式txt文件) 图片数量(jpg文件个数)&#xff1a;4635 标注数量(xml文件个数)&#xff1a;4635 标注数量(txt文件个数)&#xff1a;4635 标注…

base SAS programming学习笔记(read raw files3)

使用LIST INPUT 来读入位置不固定的外部文件&#xff0c;如下所示&#xff1a; 1.LIST input格式 INPUT variable <$> :$符合表示字符&#xff0c;variable是读入的变量名&#xff1b;默认的数值和字符长度为8&#xff1b;可以使用length 语句为字符变量设置长度&#xf…

学习测试10-1自动化 python基础

下载python 要配置环境变量 进入Python的官方下载页面 http://www.python.org/download/安装PyCharm工具&#xff0c;网上可以下载&#xff0c;很多资源&#xff0c;也有免安装的版本&#xff0c;在网上找激活码 链接: https://pan.baidu.com/s/1Y6S_n3KbnjOdE9EDZ5nixw?pwdd…

使用Windows Linux 子系统安装 Tensorflow,并使用GPU环境

在Microsoft Store商店安装Ubuntu 20.04 使用 nvidia-smi 命令查看GPU信息&#xff0c;查看支持的CUDA版本&#xff0c;这里最高支持11.7 安装cuda工具集 进入官网&#xff1a;CUDA Toolkit Archive | NVIDIA Developer&#xff0c;现在对应版本&#xff0c;点击 配置平台&…

走进数组的奇妙之旅(1)

引言&#xff1a; 在前几篇文章中&#xff0c;我们深入探讨了函数的奥秘。在讲述函数知识的过程中&#xff0c;我们邂逅了一个新的概念&#xff0c;你或许还记得在演示 strcpy函数时&#xff0c;出现的这行代码&#xff1a;char1[20]{0};。当时&#xff0c;你是否感到好奇&…

前端组件化实践:Vue自定义加载Loading组件的设计与实现

摘要 随着前端技术的飞速发展&#xff0c;组件化开发已成为提高开发效率、降低维护成本的重要方法。本文介绍了前端Vue自定义加载Loading组件的设计思路与实现过程&#xff0c;该组件通过设置gif动画实现加载效果&#xff0c;可广泛应用于页面请求加载场景。通过该组件的实践&…

银行业务知识全篇(财务知识、金融业务知识)

第一部分 零售业务 1.1 储蓄业务 4 1.1.1 普通活期储蓄(本外币) 4 1.1.2 定期储蓄(本外币) 5 1.1.3 活期一本通 9 1.1.4 定期一本通 10 1.1.5 电话银行 11 1.1.6 个人支票 11 1.1.7 通信存款 13 1.1.8 其他业务规…