请封装,保留ffmpeg结构体
现成安全处理
获取编码缓冲区数据
xencoder.h
#pragma once#include <mutex>
#include <vector>struct AVCodecContext;
struct AVFrame;
struct AVPacket;
class XEncoder
{public:AVCodecContext * Create(int code_id);///// 设置对象的编码器上下文 上下文传递到对象中,资源由XEncode维护/// 加锁 线程安全/// @para avcodecContext 编码器上下文 如果 _avcodecContext 不为nullptr,则先清理资源/// 问题是:为什么要这么设计呢?/// 我们要把通过Create方法创建的avcodecContext 添加到xencoder对象中去。void SetAVCodecContext(AVCodecContext* avcodecContext);/
/// 设置编码参数,线程安全bool SetAVCodecContextOpt(const char* key, const char* val);bool SetAVCodecContextOptInt(const char* key, int val);//
/// 打开编码器 线程安全bool Open();///
//根据AVCodecContext 创建一个AVFrame,需要调用者释放av_frame_freeAVFrame* CreateFrame();//
/// 编码数据 线程安全 每次新创建AVPacket
/// @para frame 空间由用户维护
/// @return 失败范围nullptr 返回的AVPacket用户需要通过av_packet_free 清理std::vector<AVPacket*> Encode(const AVFrame* frame);/// 编码数据 线程安全 每次新创建AVPacket
/// @para frame 空间由用户维护
/// @return 失败范围nullptr 返回的AVPacket用户需要通过av_packet_free 清理std::vector<AVPacket*> FlushEncode();private:std::mutex _mutex;AVCodecContext* _avcodecContext = nullptr;
};
xencoder.cpp
#include "xencoder.h"
#include <iostream>
using namespace std;extern "C" {
#include "libavcodec/avcodec.h"
#include "libavutil/opt.h"
}void printErr(int errorcode) {char buf[1024] = { 0 };av_strerror(errorcode, buf, sizeof(buf) - 1);cout << buf << endl;
}
AVCodecContext* XEncoder::Create(int code_id)
{AVCodec* avcodec = avcodec_find_encoder((AVCodecID)code_id);if (avcodec == nullptr) {cout << "AVCodecContext* XEncoder::Create error avcodec_find_encoder == nullptr code_id = " << code_id << endl;return nullptr;}AVCodecContext * avcodecContext = avcodec_alloc_context3(avcodec);if (avcodecContext == nullptr) {cout << "AVCodecContext* XEncoder::Create error avcodecContext == nullptr" << endl;return nullptr;}//如果存在,则设定一些默认参数avcodecContext->time_base = {1,25};avcodecContext->pix_fmt = AV_PIX_FMT_YUV420P;avcodecContext->thread_count = 8;// 这个一般要从 CPU的数量获得return avcodecContext;
}void XEncoder::SetAVCodecContext(AVCodecContext* avcodecContext)
{unique_lock<mutex> lock(_mutex);//如果在这之前有存储 avcodecContext到 this,那么要保证之前存储的avcodecContext被释放了,不然会有问题if (this->_avcodecContext != nullptr) {avcodec_free_context(&this->_avcodecContext);}//保存现在传递进来的this->_avcodecContext = avcodecContext;
}bool XEncoder::SetAVCodecContextOpt(const char* key, const char* val)
{int ret = 0;unique_lock<mutex> lock(_mutex);if (this->_avcodecContext == nullptr) {cout << "SetAVCodecContextOpt return false because this->_avcodecContext == nullptr key = " << key <<" val = " << val << endl;return false;}ret = av_opt_set(this->_avcodecContext->priv_data, key, val, 0);if (ret != 0) {cout << "av_opt_set error key = " << key << "val = " << val << endl;///说明有errorprintErr(ret);return false;}return true;
}bool XEncoder::SetAVCodecContextOptInt(const char* key, int val)
{int ret = 0;unique_lock<mutex> lock(_mutex);if (this->_avcodecContext == nullptr) {cout << "SetAVCodecContextOptInt return false because this->_avcodecContext == nullptr key = " << key << " val = " << val << endl;return false;}ret = av_opt_set_int(this->_avcodecContext->priv_data, key, val, 0);if (ret != 0) {cout << "av_opt_set_int error key = " << key << "val = " << val << endl;///说明有errorprintErr(ret);return false;}return true;
}bool XEncoder::Open()
{int ret = 0;unique_lock<mutex> lock(_mutex);if (this->_avcodecContext == nullptr) {cout << "Open return false because this->_avcodecContext == nullptr " << endl;return false;}auto re = avcodec_open2(this->_avcodecContext, NULL, NULL);if (re != 0){printErr(re);return false;}return true;
}AVFrame* XEncoder::CreateFrame()
{unique_lock<mutex>lock(_mutex);if (this->_avcodecContext == nullptr) {cout << "CreateFrame return false because this->_avcodecContext == nullptr " << endl;return nullptr;}auto frame = av_frame_alloc();frame->width = this->_avcodecContext->width;frame->height = this->_avcodecContext->height;frame->format = this->_avcodecContext->pix_fmt;auto re = av_frame_get_buffer(frame, 0);if (re != 0){av_frame_free(&frame);printErr(re);return nullptr;}return frame;
}vector<AVPacket*> XEncoder::Encode(const AVFrame* frame)
{vector<AVPacket*> vecpacket;int ret = 0;if (frame == nullptr) {cout << "Encode return false because frame == nullptr " << endl;return vecpacket;}unique_lock<mutex>lock(_mutex);if (this->_avcodecContext == nullptr) {cout << "Encode return false because this->_avcodecContext == nullptr " << endl;return vecpacket;}ret = avcodec_send_frame(this->_avcodecContext, frame);if (ret != 0) {//说明发送到 编码器的时候就有问题.那么就让继续读取下一帧,也可以直接退出。return vecpacket;}while (true) {AVPacket* avpacket = av_packet_alloc();ret = avcodec_receive_packet(this->_avcodecContext, avpacket);if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) {//在失败后要记得 释放avpacket//cout << "avcodec_receive_packet ret == AVERROR(EAGAIN) || ret == AVERROR_EOF RET = " << ret << endl;av_packet_free(&avpacket);return vecpacket;}if (ret < 0) {cout << "avcodec_receive_packet error" << endl;//在失败后要记得 释放avpacketav_packet_free(&avpacket);printErr(ret);return vecpacket;}if (ret == 0) {vecpacket.push_back(avpacket);//cout << "debug point11" << endl;}}return vecpacket;
}vector<AVPacket*> XEncoder::FlushEncode()
{int ret = 0;vector<AVPacket*> vecpacket;unique_lock<mutex>lock(_mutex);if (this->_avcodecContext == nullptr) {cout << "FlushEncode return false because this->_avcodecContext == nullptr " << endl;return vecpacket;}ret = avcodec_send_frame(this->_avcodecContext, NULL);if (ret != 0) {//说明发送到 编码器的时候就有问题.那么就让继续读取下一帧,也可以直接退出。return vecpacket;}while (true) {AVPacket* avpacket = av_packet_alloc();ret = avcodec_receive_packet(this->_avcodecContext, avpacket);if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) {//在失败后要记得 释放avpacket//cout << "avcodec_receive_packet ret == AVERROR(EAGAIN) || ret == AVERROR_EOF RET = " << ret << endl;av_packet_free(&avpacket);return vecpacket;}if (ret < 0) {cout << "avcodec_receive_packet error" << endl;//在失败后要记得 释放avpacketav_packet_free(&avpacket);printErr(ret);return vecpacket;}if (ret == 0) {vecpacket.push_back(avpacket);cout << "debug point 2" << endl;}}return vecpacket;
}
mian.cpp
#include <iostream>
#include <fstream>
#include "xencoder.h"
extern "C" {#include "libavcodec/avcodec.h"#include "libavcodec/codec.h"#include "libavutil/opt.h"#include "libavutil/imgutils.h"
}
using namespace std;char* yuvfilename = (char *)"400_300_25.yuv";
//char* h264filename = (char*)"400_300_25preset_ultrafast.h264";
char* h264filename = (char*)"400_300_25xencoder.h264";
ifstream fin;
ofstream fout;AVCodecID codec_id_h264 = AV_CODEC_ID_H264;
AVCodecID codec_id_h265 = AV_CODEC_ID_H265;
AVCodecID codec_id_h265_1 = AV_CODEC_ID_HEVC;
AVCodec* avcodec = nullptr;
AVCodecContext* avcodecContext = nullptr;
AVFrame* avframe = nullptr;
uint8_t* frame_bytes_buf = nullptr;
void freeAndClose();
/***
*
* 目的是从一个YUV文件中,读取YUV数据到AVFrame
* 然后通过 H264编码器将AVFrame 转换成AVPacket
* 将每一帧的AVPacket 直接写入 xxx.h264文件,编码后的数据有带startcode
* h264文件可以通过 VLC 播放器播放,
***/
int main(int argc, char * argv[]) {cout << "014ChangeAVFrameToAVPakcet start" << endl;int ret = 0;//打开要读取的YUV 文件fin.open(yuvfilename, ios_base::binary);if (!fin) {ret = -1;cout << "open yuv file error yuvfilename = " << yuvfilename << endl;freeAndClose();return ret;}//打开要写入的h264文件fout.open(h264filename, ios_base::binary);if (!fout) {ret = -2;cout << "open h264 file error h264filename = " << h264filename << endl;freeAndClose();return ret;}XEncoder en;avcodecContext = en.Create(codec_id_h264);avcodecContext->width = 400;avcodecContext->height = 300;en.SetAVCodecContext(avcodecContext);en.SetAVCodecContextOptInt("crf", 18);en.Open();avframe = en.CreateFrame();//计算一张图片的大小//先计算出一张图片的大小int frame_bytes_bufsize = av_image_get_buffer_size((enum AVPixelFormat)avframe->format,avframe->width,avframe->height,1);//我们在分配对应大小的空间frame_bytes_buf = (uint8_t*)malloc(frame_bytes_bufsize);//开始读取文件int posnum = 1;while (!fin.eof()) {memset(frame_bytes_buf, 0, frame_bytes_bufsize);fin.read((char *)frame_bytes_buf, frame_bytes_bufsize);//将 frame_bytes_buf中的数据填充到avframe中,返回值为实际填充的大小int need_size = av_image_fill_arrays(avframe->data,avframe->linesize,frame_bytes_buf,(enum AVPixelFormat)avframe->format,avframe->width,avframe->height,1);//如果实际填充的大小 和 需要填充的大小不相等,说明有问题if (need_size != frame_bytes_bufsize) {continue;}else {avframe->pts = posnum++;//说明正确的读取到的数据vector<AVPacket*> vectorAVPacket = en.Encode(avframe);for (size_t i = 0; i < vectorAVPacket.size(); i++){fout.write((char*)vectorAVPacket[i]->data, vectorAVPacket[i]->size);av_packet_free(&vectorAVPacket[i]);//cout << "debug point 123" << endl;}}}vector<AVPacket*> vectorFlushAVPacket = en.FlushEncode();for (size_t i = 0; i < vectorFlushAVPacket.size(); i++){fout.write((char*)vectorFlushAVPacket[i]->data, vectorFlushAVPacket[i]->size);av_packet_free(&vectorFlushAVPacket[i]);//cout << "debug point 123" << endl;}freeAndClose();return ret;
}void freeAndClose() {if (frame_bytes_buf!=nullptr) {free(frame_bytes_buf);frame_bytes_buf = nullptr;}if (avframe != nullptr) {av_frame_free(&avframe);}if (avcodecContext != nullptr) {avcodec_free_context(&avcodecContext);}if (!fout) {fout.close();}if (!fin) {fin.close();}}