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

时间:2024-07-20 12:04:30
#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,有问题欢迎指正。