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);
}
}
FFmpeg4.0笔记:封装ffmpeg的解码功能类CDecode的更多相关文章
- FFmpeg4.0笔记:封装ffmpeg的解封装功能类CDemux
Github https://github.com/gongluck/FFmpeg4.0-study/tree/master/Cff CDemux.h /*********************** ...
- FFmpeg4.0笔记:封装ffmpeg的视频帧转换功能类CSws
Github https://github.com/gongluck/FFmpeg4.0-study/tree/master/Cff CSws.h /************************* ...
- FFmpeg4.0笔记:封装ffmpeg的音频重采样功能类CSwr
Github https://github.com/gongluck/FFmpeg4.0-study/tree/master/Cff CSwr.h /************************* ...
- FFmpeg4.0笔记:本地媒体文件解码、帧格式转换、重采样、编码、封装、转封装、avio、硬解码等例子
Github https://github.com/gongluck/FFmpeg4.0-study/blob/master/official%20example/my_example.cpp #in ...
- FFmpeg4.0笔记:rtsp2rtmp
Github https://github.com/gongluck/FFmpeg4.0-study.git #include <iostream> using namespace std ...
- FFmpeg4.0笔记:file2rtmp
Github: https://github.com/gongluck/FFmpeg4.0-study.git #include <iostream> using namespace st ...
- UWP笔记-使用FFmpeg编解码
在开发UWP媒体应用的时候,使用的MediaElement可以支持主流的格式,不过还是有些格式本地编解码器是不支持的,如.flv..rmvb等,这里讲到的是第三方开源库FFmpeg,可以直接播放更多的 ...
- FFmpeg4.0笔记:VS2019编译FFmpeg4.0源码
0.下载TDM.msys和yasm 1.安装TDM-GCC-64 2.安装msys到TDM-GCC的安装目录中 3.将call "C:\Program Files (x86)\Microso ...
- FFmpeg4.0笔记:采集系统声音
Github https://github.com/gongluck/FFmpeg4.0-study/tree/master/Cff // 采集系统声音 void test_systemsound() ...
随机推荐
- vim8.1安装
win下直接就有gvim8.1.exe安装.但linux下直接从apt-get里面下载的vim都是远古版本,需要手动编译安装. 首先,下载vim源代码 git clone https://github ...
- linux 分区管理
1. 查看系统中硬盘的设备 [root@centos6 ~]# ls /dev/sd* /dev/sda /dev/sda1 /dev/sda2 /dev/sda3 /dev/sdb 可以看出,系统有 ...
- django CBV模式源码执行过程
在说CBV模式之前,先看下FBV的url配置方式: urlpatterns = [ url(r'^admin/', admin.site.urls), url(r'^xxx/', login), ur ...
- linux 之 pthread_create 实现类的成员函数做参数
在C++的类中,普通成员函数不能作为pthread_create的线程函数,如果要作为pthread_create中的线程函数,必须是static ! 在C语言中,我们使用pthread_create ...
- a标签的伪类
a 超链接 伪类:给元素添加特殊的效果 :link 未访问过的链接初始颜色 :visited 访问过后的链接颜色 :hover 鼠标移入(悬停)时的颜色 :active 鼠标按下时链接的颜色 书写时的 ...
- T89353 【BIO】RGB三角形
T89353 [BIO]RGB三角形 题解 对于这个题目有一个规律: 如果一个数列的长度为 3k+1(0<=k) 那么,这个数列最终缩放成的一个字母只和这个数列的首项,尾项有关 所以我们可以先 ...
- go get命令在go mod目录下与正常目录执行的区别
转载自https://www.jianshu.com/p/0a2ebb07da54 非$GOPATH目录下的go mod项目 $ go mod init test $ cat go.mod modul ...
- C代码输出日志
模板代码,在实际开发中可以使用: Android.mk文件增加(放到 include $(CLEAR_VARS)下面) LOCAL_LDLIBS += -llog C代码中增加 #include &l ...
- java回调函数详解
声明:博客参考于https://www.cnblogs.com/yangmin86/p/7090882.html,谢谢哥们 回调函数:是指在A类执行代码时,调用了B类中的方法,但B类中的方法执行了A类 ...
- 阶段3 3.SpringMVC·_06.异常处理及拦截器_1 SpringMVC异常处理之分析和搭建环境
异常一级一级的抛出 前端控制器,调用异常处理器组件 搭建环境 注意下面两个的结尾的名称要个 Module Name对应起来. 导入开发的坐标 复制upload这个项目里面的 编程和生成 改成1.8 配 ...