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

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,有问题欢迎指正。

相关推荐

  1. FFmpeg--音频解码流程:aac解码pcm

    2024-07-21 01:50:05       44 阅读
  2. 读书之深入理解ffmpeg_简单笔记3(初步)

    2024-07-21 01:50:05       44 阅读
  3. 读书之深入理解ffmpeg_简单笔记2(初步)

    2024-07-21 01:50:05       49 阅读
  4. Python与FFmpeg深入理解input参数的使用

    2024-07-21 01:50:05       24 阅读

最近更新

  1. docker php8.1+nginx base 镜像 dockerfile 配置

    2024-07-21 01:50:05       52 阅读
  2. Could not load dynamic library ‘cudart64_100.dll‘

    2024-07-21 01:50:05       54 阅读
  3. 在Django里面运行非项目文件

    2024-07-21 01:50:05       45 阅读
  4. Python语言-面向对象

    2024-07-21 01:50:05       55 阅读

热门阅读

  1. Go知识点记录

    2024-07-21 01:50:05       19 阅读
  2. DAY05 CSS

    DAY05 CSS

    2024-07-21 01:50:05      17 阅读
  3. MacOS命令行运行fortran程序|编程私教解答

    2024-07-21 01:50:05       18 阅读
  4. 类与对象-多态-案例3-电脑组装具体实现

    2024-07-21 01:50:05       18 阅读
  5. OpenPyXL 写入 Excel 文件

    2024-07-21 01:50:05       15 阅读
  6. 量化机器人如何实现无缝交易?

    2024-07-21 01:50:05       17 阅读
  7. Redis 深度历险:核心原理与应用实践 - 读书笔记

    2024-07-21 01:50:05       15 阅读
  8. Head size 160 is not supported by PagedAttention.

    2024-07-21 01:50:05       15 阅读