FFmpeg 4.3 音视频-多路H265监控录放C++开发十三.3:将AVFrame转换成AVPacket.封装。代码改动

ops/2024/11/20 1:48:58/

请封装,保留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();}}


http://www.ppmy.cn/ops/135122.html

相关文章

常见的网络协议汇总(涵盖了不同的网络层次)

网络层协议 IP协议&#xff1a;IP指网际互连协议&#xff08;Internet Protocol&#xff09;&#xff0c;是TCP/IP体系中的网络层协议。IP协议包括IPv4和IPv6&#xff0c;用于为数据包提供源地址和目标地址&#xff0c;从而实现网络通信。ICMP协议&#xff1a;ICMP&#xff08…

Python读取prophesee相机输出的raw文件

import cv2 import json import numpy as np from pathlib import Path import matplotlib.pyplot as plt from metavision_core.event_io import EventsIteratordef visualization_event_streams(p_list, t_list, x_list, y_list, save_pathNone):# 事件流的3D表示fig plt.fi…

Brave127编译指南 Linux篇-环境初始化(六)

引言 完成环境配置后&#xff0c;下一个关键阶段是初始化Brave浏览器的构建环境。这个过程对于确保所有依赖项和必要资源就绪至关重要&#xff0c;为后续的编译和开发工作奠定基础。初始化构建环境涉及几个核心步骤&#xff1a;进入正确的工作目录、安装必需依赖、同步最新Chr…

蓝桥杯每日真题 - 第13天

题目&#xff1a;&#xff08;删边问题&#xff09; 题目描述&#xff08;14届 C&C B组F题&#xff09; 解题思路&#xff1a; 图的构建&#xff1a;使用邻接链表表示图&#xff0c;边的起点和终点分别存储在数组中&#xff0c;以支持高效的遍历。 Tarjan算法&#xff1a…

---usb 摄像头的Linux 下查询的命令

0) 先 列一下机子上所插的摄像头: ~$ v4l2-ctl --list-devices iContact Camera Pro: iContact C (usb-0000:00:14.0-2): /dev/video3 /dev/video4 /dev/media1 USB 2.0 PC Camera: PC Camera (usb-0000:00:14.0-8): /dev/video1 /dev/video2 /dev…

「二」体验HarmonyOS端云一体化开发模板——创建端云一体化工程

关于作者 白晓明 宁夏图尔科技有限公司董事长兼CEO、坚果派联合创始人 华为HDE、润和软件HiHope社区专家、鸿蒙KOL、仓颉KOL 华为开发者学堂/51CTO学堂/CSDN学堂认证讲师 开放原子开源基金会2023开源贡献之星 「目录」 「一」HarmonyOS端云一体化概要 「二」体验HarmonyOS端云一…

抽象工厂方法模式

工厂方法模式&#xff08;Factory Method Pattern&#xff09; 工厂方法模式是一种 创建型设计模式&#xff0c;它定义了一个创建对象的接口&#xff0c;但让子类决定实例化哪一个具体类。通过这种方式&#xff0c;工厂方法将对象的创建延迟到子类&#xff0c;避免了直接依赖具…

【青牛科技】带 ALC 双通道前置放大器电路D3308

概述&#xff1a; D3308 是一块带有 ALC 的双通道前置放大器。它适用于立体声收录机 和盒式录音机。 采用 SIP9、SOP14 的封装形式封装。 主要特点&#xff1a; ● 带内置 ALC 回路的双通道均衡放大器。 ● 低噪声&#xff1a; VNI1.0V&#xff08;典型值&#xff09;。 …