FFmpeg 4.3 H265十七,XCodec封装重构,编码和解码有很多重复的地方,我们来封装一下

1.提取编解码公共部分代码 XCodec

xcodec.h

#pragma once
#include <mutex>
#include <vector>
#include "myutils.h"
struct AVCodecContext;
struct AVPacket;
struct AVFrame;

//这里重构,将编码器 和 解码器相同的部分 提取出来到 XCode
class XCode
{

public:


	/// <summary>
	/// 我们已经有了 AVFrame* XCode::CreateFrame(int avframewidth, int avframeheight, int pixformat, int align)
	/// 方法,
	/// 这里再次添加一个 createFrame方法,此方法没有参数,
	/// 我们知道在alloc frame( av_frame_alloc();) 后,要设置相关参数
	///     avframe->width = avframewidth;
	///     avframe->height = avframeheight;
	///     avframe->format = pixformat;
	/// 最后通过 av_frame_get_buffer(avframe, align); 给avframe 填充空间
	/// 那么创建avframe的 分辨率,format,align 从哪里来呢?
	/// 分辨率,format 是从 this->avcodecContext_ 来的.
	/// align 使用的默认值 0
	/// 
	/// 也就是 新建的这个没有参数的 CreateFrame()方法。使用前,
	/// 一定是调用过Create方法,并且设置了Create方法的返回值AVCodecContext的 width,height,pixformat
	/// 且通过set_c(AVCodecContext* avcodecContext) 给 this->avcodecContext_赋值完成
	/// </summary>
	/// <returns></returns>

	AVFrame* CreateFrame();


	///////////////////////////////////////////////////////////////
// 创建AVFrame,线程安全
// 为什么将这个函数放在第一个呢?因为在createAVCodecContex方法中,要设定 分辨率,pixformat,linesize[0]等必要的参数
// 我们当前的设想是在创建AVFrame的时候,就将这些参数设置进去。
// 然后再逻辑层,得到AVCodecContext后,再将 avframe 的参数 赋值给 AVCodecContext的必要参数
// @param avframewidth   要读文件的分辨率 宽
// @param avframeheight  要读文件的分辨率 高
// @param pixformat      要读取文件的 pixformat
// @param align          要读取文件的 以多少位对齐。必须是4的倍数,或者是0,或者是1,0表示使用ffmepg默认的对齐方式
// 由于 align在windows 默认值是32,该值不能大于64
// 当align的值 =1 的时候,通过 av_frame_get_buffer 得到的linesize[0] = width,linesize[1] = width/2,linesize[0] = width/2,
//根据AVCodecContext 创建一个AVFrame,需要调用者释放av_frame_free
	AVFrame* CreateFrame(int avframewidth, int avframeheight, int pixformat, int align);





	/// 创建编解码器上下文
	/// @para codec_id 编码器ID号,对应ffmpeg
	/// @para isencoder true代表编码器,false代表解码器
	/// @return 编码上下文 ,失败返回nullptr
	/// 这里将这个函数变成static,原因是不想让 业务层 去维护AVCodeContext的 空间
	/// 那么不让 业务层 管理avcodecContex,XEncoder 就要自己管理,因此有了下面的 set_c 函数,
	/// 而xencoder内部会维护一个 私有成员变量 avcodeContext_ ,
	/// 在需要销毁私有变量 avcodeContext_的时候,调用set_c(nullptr),就可以将 私有成员 avcodeContext_ 清空
	/// 请结合 set_c()函数的实现,进一步理解。

	static AVCodecContext* Create(int codec_id, bool isencoder);


	//////////////////////////////////////////
	/// 设置对象的编码器上下文.
	/// 这里将 编码器上下文传递到对象中,目的是让 XEncode维护AVCodecContext的销毁
	/// 加锁 线程安全
	/// @para avcodecContext 编码器上下文 如果avcodeContext_ 不为nullptr,则先清理资源
	void set_c(AVCodecContext* avcodecContext);


	/////////////////////////////////////////////
	/// 设置编码参数,线程安全
	/// 这里对应在 AVCodecContext 在生成后,调用 avcodec_open2 函数之前,
	/// 有可能需要通过 av_opt_set(this->avcodecContext_->priv_data, key, val, 0); 设置avcodecContex的参数。
	bool SetOpt(const char* key, const char* val);

	/////////////////////////////////////////////
	/// 设置编码参数,线程安全
	/// 这里对应在 AVCodecContext 在生成后,调用 avcodec_open2 函数之前,
	/// 有可能需要通过 av_opt_set_int(this->avcodecContext_->priv_data, key, val, 0); 设置avcodecContex的参数。
	bool SetOpt(const char* key, int val);

	//////////////////////////////////////////////////////////////
	/// 打开编码器 ,线程安全
	/// 对应 ffmepg 中代码为 int avcodec_open2(AVCodecContext *avctx, const AVCodec *codec, AVDictionary **options);
	bool OpenAVCodecContext();



protected:
	AVCodecContext* avcodeContext_ = nullptr;  //编码器上下文
	std::mutex mux_;               //编码器上下文锁
};

xcode.cpp

#include "xcode.h"
#include <iostream>
using namespace std;
extern "C"
{
#include <libavcodec/avcodec.h>
#include <libavutil/opt.h>
}
using namespace std;

AVFrame* XCode::CreateFrame() {
	unique_lock<mutex>lock(mux_);
	if (this->avcodeContext_ == nullptr) {
		return nullptr;
	}
	auto frame = av_frame_alloc();
	frame->width = this->avcodeContext_->width;
	frame->height = this->avcodeContext_->height;
	frame->format = this->avcodeContext_->pix_fmt;
	auto re = av_frame_get_buffer(frame, 0);
	if (re != 0)
	{
		av_frame_free(&frame);
		PrintErr(re);
		return nullptr;
	}
	return frame;
}


AVFrame* XCode::CreateFrame(int avframewidth, int avframeheight, int pixformat, int align)
{
	if (avframewidth <= 0 || avframeheight <= 0 || pixformat > AV_PIX_FMT_NB || pixformat < 0 || align > 64) {
		cerr << "CreateFrame func error avframewidth = " << avframewidth
			<< "  avframeheight = " << avframeheight
			<< "  pixformat = " << pixformat
			<< "  align = " << align
			<< endl;
		return nullptr;
	}
	unique_lock<mutex>lock(mux_);
	AVFrame* avframe = av_frame_alloc();
	if (avframe == nullptr) {
		cout << "CreateFrame func error because av_frame_alloc() error " << endl;
		return nullptr;
	}
	avframe->width = avframewidth;
	avframe->height = avframeheight;
	avframe->format = pixformat;
	auto re = av_frame_get_buffer(avframe, align);
	if (re != 0)
	{
		av_frame_free(&avframe);
		PrintErr(re);
		return nullptr;
	}
	return avframe;
}

AVCodecContext* XCode::Create(int codec_id, bool isencoder)
{
	AVCodec* avcodec = nullptr;
	AVCodecContext* avcodecContext = nullptr;
	if (isencoder) {
		avcodec = avcodec_find_encoder((AVCodecID)codec_id);
		if (avcodec == nullptr) {
			cerr << "avcodec_find_encoder failed!" << codec_id << endl;
			return nullptr;
		}
	}
	else {
		avcodec = avcodec_find_decoder((AVCodecID)codec_id);
		if (avcodec == nullptr) {
			cerr << "avcodec_find_decoder failed!" << codec_id << endl;
			return nullptr;
		}
	}
	avcodecContext = avcodec_alloc_context3(avcodec);
	if (avcodecContext == nullptr)
	{
		cerr << "avcodec_alloc_context3 failed!" << codec_id << endl;
		return nullptr;
	}

	//设置参数默认值
	avcodecContext->time_base = { 1,25 };
	avcodecContext->pix_fmt = AV_PIX_FMT_YUV420P;
	avcodecContext->thread_count = 16;
	return avcodecContext;
}

void XCode::set_c(AVCodecContext* avcodecContext)
{
	unique_lock<mutex>lock(mux_);
	if (this->avcodeContext_)
	{
		avcodec_free_context(&avcodeContext_);
	}
	this->avcodeContext_ = avcodecContext;
}

bool XCode::SetOpt(const char* key, const char* val)
{
	unique_lock<mutex>lock(mux_);
	if (!this->avcodeContext_)return false;
	auto re = av_opt_set(this->avcodeContext_->priv_data, key, val, 0);
	if (re != 0)
	{
		cerr << "av_opt_set failed!";
		PrintErr(re);
	}
	return true;
}

bool XCode::SetOpt(const char* key, int val)
{
	unique_lock<mutex>lock(mux_);
	if (!this->avcodeContext_)return false;
	auto re = av_opt_set_int(this->avcodeContext_->priv_data, key, val, 0);
	if (re != 0)
	{
		cerr << "av_opt_set failed!";
		PrintErr(re);
	}
	return true;
}


//////////////////////////////////////////////////////////////
/// 打开编码器 线程安全
///对应 ffmepg 中代码为 int avcodec_open2(AVCodecContext* avctx, const AVCodec* codec, AVDictionary** options);
bool XCode::OpenAVCodecContext()
{
	unique_lock<mutex>lock(mux_);
	if (!this->avcodeContext_)return false;
	auto re = avcodec_open2(this->avcodeContext_, NULL, NULL);
	if (re != 0)
	{
		PrintErr(re);
		return false;
	}
	return true;
}

2.重构XEncode

xencode.h

#pragma once
#include <mutex>
#include <vector>
#include "xcode.h"
struct AVCodecContext;
struct AVPacket;
struct AVFrame;


class XEncode : public XCode
{
public:



    //////////////////////////////////////////////////////////////
/// 编码数据 线程安全 内部实现每次新创建AVPacket
/// @para frame 空间由用户维护
/// @return 失败范围nullptr 
/// 返回的AVPacket用户需要通过av_packet_free 清理
    AVPacket* Encode(const AVFrame* frame);

    //////////////////////////////////////////////////////////////
    /// 
    /// 编码数据 线程安全 内部实现每次新创建AVPacket,
    /// 这里的写法可能是为了兼容 音频。在音频阶段的时候再测试
    /// @para frame 空间由用户维护
    /// @return 失败范围nullptr 
    /// 返回的AVPacket用户需要通过av_packet_free 清理
    std::vector<AVPacket*> Encode2(const AVFrame* frame);

    //////////////////////////////////////////////////////////////
    //返回所有编码缓存中AVPacket
    std::vector<AVPacket*> End();


};

xencode.cpp

#include "xencode.h"
#include <iostream>
using namespace std;
extern "C"
{
#include <libavcodec/avcodec.h>
#include <libavutil/opt.h>
}
using namespace std;


AVPacket* XEncode::Encode(const AVFrame* avframe)
{
	if (!avframe)return nullptr;
	unique_lock<mutex>lock(mux_);
	if (!this->avcodeContext_)return nullptr;
	int ret = avcodec_send_frame(this->avcodeContext_, avframe);
	if (ret !=0) {
		cout << "avcodec_send_frame error ret = " << ret << endl;
		PrintErr(ret);
		return nullptr;
	}
	AVPacket * avpacket = av_packet_alloc();
	if (avpacket == nullptr) {

		cerr<<("func Encode error because av_packet_alloc() error") << endl;
		return nullptr;
	}

	ret = avcodec_receive_packet(this->avcodeContext_, avpacket);

	if (ret == 0 ) {
		return avpacket;
	}
	av_packet_free(&avpacket);

	if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF)
	{
		return nullptr;
	}
	if (ret < 0)
	{
		PrintErr(ret);
	}
	return nullptr;
}

vector<AVPacket*> XEncode::Encode2(const AVFrame* avframe)
{
	vector<AVPacket*> vecavpacket;
	if (!avframe) {
		return vecavpacket;
	}
	unique_lock<mutex>lock(mux_);
	if (!this->avcodeContext_) {
		return vecavpacket;
	}
	int ret = avcodec_send_frame(this->avcodeContext_, avframe);
	if (ret != 0) {
		return vecavpacket;
	}
	else {
		while (true) {
			AVPacket* avpacket = av_packet_alloc();
			if (avpacket == nullptr) {

				cerr << ("func Encode2 error because av_packet_alloc() error") << endl;
				return vecavpacket;
			}
			ret = avcodec_receive_packet(this->avcodeContext_, avpacket);
			if (ret == 0) {
				vecavpacket.push_back(avpacket);
			}
			else if (ret < 0) {
				av_packet_free(&avpacket);
				PrintErr(ret);
				return vecavpacket;
			}
		}
	}
	return vecavpacket;

}

std::
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值