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::

最低0.47元/天 解锁文章
2134

被折叠的 条评论
为什么被折叠?



