FFmpeg4.0笔记:封装ffmpeg的解码功能类CDecode

Stella981
• 阅读 559

##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);
    }
}
点赞
收藏
评论区
推荐文章
blmius blmius
3年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
皕杰报表之UUID
​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为
待兔 待兔
5个月前
手写Java HashMap源码
HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22
Jacquelyn38 Jacquelyn38
3年前
2020年前端实用代码段,为你的工作保驾护航
有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )
Wesley13 Wesley13
3年前
mysql设置时区
mysql设置时区mysql\_query("SETtime\_zone'8:00'")ordie('时区设置失败,请联系管理员!');中国在东8区所以加8方法二:selectcount(user\_id)asdevice,CONVERT\_TZ(FROM\_UNIXTIME(reg\_time),'08:00','0
Wesley13 Wesley13
3年前
00:Java简单了解
浅谈Java之概述Java是SUN(StanfordUniversityNetwork),斯坦福大学网络公司)1995年推出的一门高级编程语言。Java是一种面向Internet的编程语言。随着Java技术在web方面的不断成熟,已经成为Web应用程序的首选开发语言。Java是简单易学,完全面向对象,安全可靠,与平台无关的编程语言。
Stella981 Stella981
3年前
Django中Admin中的一些参数配置
设置在列表中显示的字段,id为django模型默认的主键list_display('id','name','sex','profession','email','qq','phone','status','create_time')设置在列表可编辑字段list_editable
Stella981 Stella981
3年前
FFmpeg4.0笔记:封装ffmpeg的音频重采样功能类CSwr
Githubhttps://github.com/gongluck/FFmpeg4.0study/tree/master/Cff(https://www.oschina.net/action/GoToLink?urlhttps%3A%2F%2Fgithub.com%2Fgongluck%2FFFmpeg4.0study%2Ftree%2Fma
Wesley13 Wesley13
3年前
MySQL部分从库上面因为大量的临时表tmp_table造成慢查询
背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_
Python进阶者 Python进阶者
11个月前
Excel中这日期老是出来00:00:00,怎么用Pandas把这个去除
大家好,我是皮皮。一、前言前几天在Python白银交流群【上海新年人】问了一个Pandas数据筛选的问题。问题如下:这日期老是出来00:00:00,怎么把这个去除。二、实现过程后来【论草莓如何成为冻干莓】给了一个思路和代码如下:pd.toexcel之前把这