FFmpeg 4.3 音视频-多路H265监控录放C++开发十三.3:将AVFrame转换成AVPacket.封装。代码改动
请封装,保留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_free
AVFrame* 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;
///说明有error
printErr(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;
///说明有error
printErr(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;
//在失败后要记得 释放avpacket
av_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;
//在失败后要记得 释放avpacket
av_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();
}
}