zoukankan      html  css  js  c++  java
  • FFmpeg4.0笔记:封装ffmpeg的解码功能类CDecode

    Github

    https://github.com/gongluck/FFmpeg4.0-study/tree/master/Cff

    CDecode.h

    /*******************************************************************
    *  Copyright(c) 2019
    *  All rights reserved.
    *
    *  文件名称:    CDecode.h
    *  简要描述:    解码
    *
    *  作者:  gongluck
    *  说明:
    *
    *******************************************************************/
    
    #ifndef __CDECODE_H__
    #define __CDECODE_H__
    
    #ifdef __cplusplus
    extern "C"
    {
    #endif
    
    #include <libavcodec/avcodec.h>
    
    #ifdef __cplusplus
    }
    #endif
    
    #include <string>
    #include <mutex>
    
    class CDecode
    {
    public:
        virtual ~CDecode();
        // 解码帧回调声明
        typedef void (*DecFrameCallback)(const AVFrame* frame, void* param);
    
        // 设置解码帧回调 
        bool set_dec_callback(DecFrameCallback cb, void* param, std::string& err);
    
        // 设置硬解
        bool set_hwdec_type(AVHWDeviceType hwtype, bool trans, std::string& err);
    
        // 设置解码器
        bool set_codeid(AVCodecID id, std::string& err);
        bool copy_param(const AVCodecParameters* par, std::string& err);
    
        // 打开解码器
        bool codec_open(std::string& err);
    
        // 解码
        bool decode(const AVPacket* packet, std::string& err);
        bool decode(const void* data, uint32_t size, std::string& err);
    
        // 清理资源
        bool clean_opt(std::string& err);
    
    private:
        std::recursive_mutex mutex_;
    
        DecFrameCallback decframecb_ = nullptr;
        void* decframecbparam_ = nullptr;
    
        //ffmpeg
        AVCodecContext* codectx_ = nullptr;
        AVCodec* codec_ = nullptr;
        AVCodecParserContext* par_ = nullptr;
        AVHWDeviceType hwtype_ = AV_HWDEVICE_TYPE_NONE;
        AVPixelFormat hwfmt_ = AV_PIX_FMT_NONE;
        AVPacket pkt_;
        bool trans_ = false;
    };
    
    #endif//__CDECODE_H__
    

    CDecode.cpp

    /*******************************************************************
    *  Copyright(c) 2019
    *  All rights reserved.
    *
    *  文件名称:    CDecode.cpp
    *  简要描述:    解码
    *
    *  作者:  gongluck
    *  说明:
    *
    *******************************************************************/
    
    #include "common.h"
    #include "CDecode.h"
    
    CDecode::~CDecode()
    {
        std::string err;
        clean_opt(err);
    }
    
    bool CDecode::set_dec_callback(DecFrameCallback cb, void* param, std::string& err)
    {
        LOCK();
        err = "opt succeed.";
    
        decframecb_ = cb;
        decframecbparam_ = param;
    
        return true;
    }
    
    bool CDecode::set_hwdec_type(AVHWDeviceType hwtype, bool trans, std::string& err)
    {
        LOCK();
        err = "opt succeed.";
    
        hwtype_ = hwtype;
        trans_ = trans;
    
        return true;
    }
    
    bool CDecode::set_codeid(AVCodecID id, std::string& err)
    {
        LOCK();
        err = "opt succeed.";
        int ret;
    
        if (!clean_opt(err))
        {
            return false;
        }
    
        do
        {
            codec_ = avcodec_find_decoder(id);
            if (codec_ == nullptr)
            {
                err = "avcodec_find_decoder return nullptr";
                break;
            }
            codectx_ = avcodec_alloc_context3(codec_);
            if (codectx_ == nullptr)
            {
                err = "avcodec_alloc_context3 return nullptr";
                break;
            }
            par_ = av_parser_init(codec_->id);
            if (par_ == nullptr)
            {
                err = "av_parser_init return nullptr";
                //break;
            }
    
            if (hwtype_ != AV_HWDEVICE_TYPE_NONE)
            {
                // 查询硬解码支持
                for (int i = 0;; i++)
                {
                    const AVCodecHWConfig* config = avcodec_get_hw_config(codec_, i);
                    if (config == nullptr)
                    {
                        err = codec_->name + std::string(" not support ") + av_hwdevice_get_type_name(hwtype_);
                        break;
                    }
                    if (config->methods & AV_CODEC_HW_CONFIG_METHOD_HW_DEVICE_CTX &&
                        config->device_type == hwtype_)
                    {
                        // 硬解上下文
                        AVBufferRef* hwbufref = nullptr;
                        ret = av_hwdevice_ctx_create(&hwbufref, hwtype_, nullptr, nullptr, 0);
                        if (ret < 0)
                        {
                            err = av_err2str(ret);
                            break;
                        }
                        else
                        {
                            codectx_->hw_device_ctx = av_buffer_ref(hwbufref);
                            if (codectx_->hw_device_ctx == nullptr)
                            {
                                err = "av_buffer_ref(hwbufref) return nullptr.";
                                break;
                            }
                            av_buffer_unref(&hwbufref);
                            hwfmt_ = config->pix_fmt;
                            return true;
                        }
                    }
                }
            }
            return true;
        } while (true);
    
        std::string e;
        clean_opt(e);
        return false;
    }
    
    bool CDecode::copy_param(const AVCodecParameters* par, std::string& err)
    {
        LOCK();
        err = "opt succeed.";
        int ret = 0;
    
        if (par == nullptr)
        {
            err = "par is nullptr";
            return false;
        }
        if (!set_codeid(par->codec_id, err))
        {
            return false;
        }
        
        ret = avcodec_parameters_to_context(codectx_, par);
        if (ret < 0)
        {
            clean_opt(err);
            err = av_err2str(ret);
            return false;
        }
    
        return true;
    }
    
    bool CDecode::codec_open(std::string& err)
    {
        LOCK();
        err = "opt succeed.";
        int ret = 0;
    
        if (codectx_ == nullptr || codec_ == nullptr)
        {
            err = "codectx_ is nullptr or codec_ is nullptr";
            return false;
        }
    
        ret = avcodec_open2(codectx_, codec_, nullptr);
        if (ret < 0)
        {
            err = av_err2str(ret);
            return false;
        }
    
        return true;
    }
    
    bool CDecode::decode(const AVPacket* packet, std::string& err)
    {
        LOCK();
        err = "opt succeed.";
        int ret = 0;
    
        if (packet == nullptr)
        {
            err == "packet is nullptr.";
            return false;
        }
        else if (codectx_ == nullptr)
        {
            err = "codectx_ is nullptr.";
            return false;
        }
        
        // 发送将要解码的数据
        ret = avcodec_send_packet(codectx_, packet);
        CHECKFFRET(ret);
    
        AVFrame* frame = av_frame_alloc();
        AVFrame* traframe = av_frame_alloc();
        if (frame == nullptr || traframe == nullptr)
        {
            err = "av_frame_alloc() return nullptr.";
            av_frame_free(&frame);
            av_frame_free(&traframe);
            return false;
        }
    
        while (ret >= 0)
        {
            // 接收解码数据
            ret = avcodec_receive_frame(codectx_, frame);
            if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF)
            {
                // 不完整或者EOF
                err = av_err2str(ret);
                break;
            }
            else if (ret < 0)
            {
                // 其他错误
                err = av_err2str(ret);
                break;
            }
            else
            {
                // 得到解码数据
                if (decframecb_ != nullptr)
                {
                    if (hwtype_ != AV_HWDEVICE_TYPE_NONE // 使用硬解
                        && frame->format == hwfmt_ // 硬解格式
                        && trans_ // 显卡->内存转换
                        )
                    {
                        ret = av_hwframe_transfer_data(traframe, frame, 0);
                        if (ret < 0)
                        {
                            err = av_err2str(ret);
                            break;
                        }
                        else
                        {
                            traframe->pts = frame->pts;
                            traframe->pkt_dts = frame->pkt_dts;
                            traframe->pkt_duration = frame->pkt_duration;
                            decframecb_(traframe, decframecbparam_);
                        }
                    }
                    else
                    {
                        decframecb_(frame, decframecbparam_);
                    }
                }
                // 这里没有直接break,是因为存在再次调用avcodec_receive_frame能拿到新数据的可能
            }
        }
    
        av_frame_free(&frame);
        av_frame_free(&traframe);
        return true;
    }
    
    bool CDecode::decode(const void* data, uint32_t size, std::string& err)
    {
        LOCK();
        err = "opt succeed.";
        int ret = 0;
    
        int pos = 0;
        while (size > 0) 
        {
            ret = av_parser_parse2(par_, codectx_, &pkt_.data, &pkt_.size, static_cast<const uint8_t*>(data)+pos, size, AV_NOPTS_VALUE, AV_NOPTS_VALUE, 0);
            CHECKFFRET(ret);
            pos += ret;
            size -= ret;
    
            if (pkt_.size > 0)
            {
                ret = decode(&pkt_, err);
                CHECKFFRET(ret);
            }
        }
    
        return true;
    }
    
    bool CDecode::clean_opt(std::string& err)
    {
        LOCK();
        err = "opt succeed.";
    
        codec_ = nullptr;
        av_parser_close(par_);
        avcodec_free_context(&codectx_);
    
        return true;
    }
    

    测试

    // 解码h264
    void test_decode_h264()
    {
        bool ret = false;
        std::string err;
        std::ifstream h264("in.h264", std::ios::binary);
        char buf[1024] = { 0 };
        CDecode decode;
    
        ret = decode.set_dec_callback(DecVideoFrameCB, &decode, err);
        TESTCHECKRET(ret);
        //ret = decode.set_hwdec_type(AV_HWDEVICE_TYPE_DXVA2, true, err);
        //TESTCHECKRET(ret);
        ret = decode.set_codeid(AV_CODEC_ID_H264, err);
        TESTCHECKRET(ret);
        ret = decode.codec_open(err);
        TESTCHECKRET(ret);
    
        while (!h264.eof())
        {
            h264.read(buf, sizeof(buf));
            ret = decode.decode(buf, sizeof(buf), err);
            TESTCHECKRET(ret);
        }
    }
    
    // 解码aac
    void test_decode_aac()
    {
        bool ret = false;
        std::string err;
        std::ifstream aac("in.aac", std::ios::binary);
        char buf[1024] = { 0 };
        CDecode decode;
    
        ret = decode.set_dec_callback(DecAudioFrameCB, &decode, err);
        TESTCHECKRET(ret);
        ret = decode.set_codeid(AV_CODEC_ID_AAC, err);
        TESTCHECKRET(ret);
        ret = decode.codec_open(err);
        TESTCHECKRET(ret);
    
        while (!aac.eof())
        {
            aac.read(buf, sizeof(buf));
            ret = decode.decode(buf, sizeof(buf), err);
            TESTCHECKRET(ret);
        }
    }
    
    // 解码mp3
    void test_decode_mp3()
    {
        bool ret = false;
        std::string err;
        std::ifstream mp3("in.mp3", std::ios::binary);
        char buf[1024] = { 0 };
        CDecode decode;
    
        ret = decode.set_dec_callback(DecAudioFrameCB, &decode, err);
        TESTCHECKRET(ret);
        ret = decode.set_codeid(AV_CODEC_ID_MP3, err);
        TESTCHECKRET(ret);
        ret = decode.codec_open(err);
        TESTCHECKRET(ret);
    
        while (!mp3.eof())
        {
            mp3.read(buf, sizeof(buf));
            ret = decode.decode(buf, sizeof(buf), err);
            TESTCHECKRET(ret);
        }
    }
    
  • 相关阅读:
    bzoj 1031: [JSOI2007]字符加密Cipher 後綴數組模板題
    hdu3949 XOR xor高斯消元
    The xor-longest Path
    Contest20140906 反思
    Contest20140906 ProblemC 菲波拉契数制 DP
    Contest20140906 ProblemA dp+线段树优化
    bzoj 1257: [CQOI2007]余数之和sum 数学 && 枚举
    tyvj P1716
    BZOJ 1012 [JSOI2008]最大数maxnumber【线段树】
    Bzoj1083 1083: [SCOI2005]繁忙的都市【MST】
  • 原文地址:https://www.cnblogs.com/gongluck/p/11017265.html
Copyright © 2011-2022 走看看