FFmpeg支持多线程编码并保存mp4文件示例

      之前介绍的示例:

      (1).https://blog.csdn.net/fengbingchun/article/details/132129988 中对编码后数据保存成mp4

      (2).https://blog.csdn.net/fengbingchun/article/details/132128885 中通过AVIOContext实现从内存读取数据

      (3).https://blog.csdn.net/fengbingchun/article/details/132389734 中将图像加载到视频中

      这里将三部分整合到类中,便于后面增加测试代码,下面的示例是两个线程:从内存中读取数据,并将指定的图像加载到视频,将结果保存成mp4。

      示例代码如下:

      1. 类PacketScaleQueue:用于持续的从指定内存中读取原始数据,上面的示例中已包含此代码

      2.类CodecQueue:用于将解码数据存入队列中,并通过单独的线程进行编码

class AVFrameQueue {
public:AVFrameQueue() = default;~AVFrameQueue() {}void push(AVFrame** frame) {std::unique_lock<std::mutex> lck(mtx);queue.push(*frame);cv.notify_all();}void pop(AVFrame** frame) {std::unique_lock<std::mutex> lck(mtx);while (queue.empty()) {//cv.wait(lck);if (cv.wait_for(lck, std::chrono::milliseconds(150)) == std::cv_status::timeout) {fprintf(stderr, "#### Warning: wait timeout\n");*frame = nullptr;return;}}*frame = queue.front();queue.pop();}size_t size() const {return queue.size();}private:std::queue<AVFrame*> queue;std::mutex mtx;std::condition_variable cv;
};class CodecQueue {
public:CodecQueue() = default;void init(unsigned int frame_num) {for (auto i = 0; i < frame_num; ++i) {AVFrame* frame = nullptr;pushDecode(&frame);}}~CodecQueue() { release(); }void release() {AVFrame* frame = nullptr;while (getDecodeSize() > 0) {popDecode(&frame);av_frame_free(&frame);}while (getEncodeSize() > 0) {popEncode(&frame);av_frame_free(&frame);}}void pushDecode(AVFrame** frame) { decode_queue.push(frame); }void popDecode(AVFrame** frame) { decode_queue.pop(frame); }size_t getDecodeSize() const { return decode_queue.size(); }void pushEncode(AVFrame** frame) { encode_queue.push(frame); }void popEncode(AVFrame** frame) { encode_queue.pop(frame); }size_t getEncodeSize() const { return encode_queue.size(); }private:AVFrameQueue decode_queue, encode_queue;
};

      3.类VideoCodec:供外面的接口调用,封装了视频的解码和编码过程

      声明如下:

typedef struct CodecCtx {char outfile_name[VIDEO_CODEC_MAX_STRING_SIZE];char video_size[VIDEO_CODEC_MAX_STRING_SIZE];char bitrate_str[VIDEO_CODEC_MAX_STRING_SIZE];char pixel_format[VIDEO_CODEC_MAX_STRING_SIZE];char filter_descr[VIDEO_CODEC_MAX_STRING_SIZE];AVFormatContext* ifmt_ctx;AVFormatContext* ofmt_ctx;AVCodecContext* dec_ctx;AVCodecContext* enc_ctx;AVFrame* dec_frame;AVFilterContext* buffersink_ctx;AVFilterContext* buffersrc_ctx;AVFilterGraph* filter_graph;AVPacket* enc_pkt;AVRational frame_rate;int term_status;int stream_index;int frame_count;bool encode_thread_end;
} CodecCtx;class VideoCodec {
public:VideoCodec() = default;~VideoCodec() {  }void setOutfileName(const std::string& name) { outfile_name_ = name; }void setVideoSize(const std::string& size) { video_size_ = size; }void setPixelFormat(const std::string& format) { pixel_format_ = format; }void setFilterDescr(const std::string& filter_descr) { filter_descr_ = filter_descr; }void stopEncode() {while (raw_packet_queue_.getScaleSize() != 0);codec_ctx_->term_status = 1;Buffer buffer;raw_packet_queue_.popPacket(buffer);memset(buffer.data, 0, block_size_);raw_packet_queue_.pushScale(buffer); // for av_read_frame to exit normally}PacketScaleQueue& get_raw_packet_queue(unsigned int buffer_num, size_t buffer_size) {raw_packet_queue_.init(buffer_num, buffer_size);block_size_ = buffer_size;return raw_packet_queue_;}int openEncode();int processEncode();int closeEncode();private:std::string outfile_name_ = "";std::string video_size_ = "";std::string pixel_format_ = "";std::string filter_descr_ = "";PacketScaleQueue raw_packet_queue_;int block_size_ = 0;CodecCtx* codec_ctx_ = nullptr;AVIOContext* avio_ctx_ = nullptr;CodecQueue codec_queue_;std::thread encode_thread_;int get_decode_context();int get_encode_context();int init_filters();int filter_encode_write_frame(AVFrame* frame);int get_output_format_context();int flush_encode_write_frame();int flush_decoder();int flush_encoder();void flush_codec();
};

      类VideoCodec实现部分:是之前示例的整理,参考之前示例

      4.测试代码,即调用VideoCodec接口,以下是同时两个线程进行编码写

namespace {const int total_push_count = 121;
bool flag1 = true;
const size_t block_size_1 = 640 * 480 * 3;
size_t total_push_count_1 = 0;void fill_raw_data_1(PacketScaleQueue& raw_packet)
{unsigned char value = 0;while (total_push_count_1 < total_push_count) {value += 10;if (value >= 255) value = 0;Buffer buffer;raw_packet.popPacket(buffer);memset(buffer.data, value, block_size_1);raw_packet.pushScale(buffer);std::this_thread::sleep_for(std::chrono::milliseconds(33));++total_push_count_1;}flag1 = false;
}void sleep_seconds_1(VideoCodec& video_codec)
{while (flag1) {std::this_thread::sleep_for(std::chrono::milliseconds(33));}video_codec.stopEncode();
}void encode_1()
{VideoCodec video_codec;video_codec.setOutfileName("out1.mp4");video_codec.setVideoSize("640x480");video_codec.setPixelFormat("bgr24");video_codec.setFilterDescr("movie=1.jpg[logo];[in][logo]overlay=10:20[out]");auto& raw_queue = video_codec.get_raw_packet_queue(16, block_size_1);std::thread thread_fill(fill_raw_data_1, std::ref(raw_queue));auto ret = video_codec.openEncode();if (ret != 0) {std::cout << "fail to openEncode: " << ret << std::endl;//return -1;}std::thread thread_sleep(sleep_seconds_1, std::ref(video_codec));ret = video_codec.processEncode();if (ret != 0) {std::cout << "fail to processEncode: " << ret << std::endl;//return -1;}thread_fill.join();thread_sleep.join();video_codec.closeEncode();std::cout << "1 total push count: " << total_push_count_1 << std::endl;
}bool flag2 = true;
const size_t block_size_2 = 640 * 480 * 3;
size_t total_push_count_2 = 0;void fill_raw_data_2(PacketScaleQueue& raw_packet)
{unsigned char value = 0;while (total_push_count_2 < total_push_count) {value += 10;if (value >= 255) value = 0;Buffer buffer;raw_packet.popPacket(buffer);memset(buffer.data, value, block_size_2);raw_packet.pushScale(buffer);std::this_thread::sleep_for(std::chrono::milliseconds(33));++total_push_count_2;}flag2 = false;
}void sleep_seconds_2(VideoCodec& video_codec)
{while (flag2) {std::this_thread::sleep_for(std::chrono::milliseconds(33));}video_codec.stopEncode();
}void encode_2()
{VideoCodec video_codec;video_codec.setOutfileName("out2.mp4");video_codec.setVideoSize("640x480");video_codec.setPixelFormat("bgr24");video_codec.setFilterDescr("movie=1.jpg[logo];[in][logo]overlay=10:20[out]");auto& raw_queue = video_codec.get_raw_packet_queue(16, block_size_2);std::thread thread_fill(fill_raw_data_2, std::ref(raw_queue));auto ret = video_codec.openEncode();if (ret != 0) {std::cout << "fail to openEncode: " << ret << std::endl;//return -1;}std::thread thread_sleep(sleep_seconds_2, std::ref(video_codec));ret = video_codec.processEncode();if (ret != 0) {std::cout << "fail to processEncode: " << ret << std::endl;//return -1;}thread_fill.join();thread_sleep.join();std::cout << "2 total push count: " << total_push_count_2 << std::endl;
}} // namespceint test_ffmpeg_libavfilter_movie_multi_thread()
{std::thread thread_1(encode_1);std::thread thread_2(encode_2);thread_1.join();thread_2.join();std::cout << "test finish" << std::endl;return 0;
}

      生成的mp4文件结果如下:在release下生成的两个视频文件完全一致;在debug下编码过程中有时会timeout

      GitHub:https://github.com/fengbingchun/OpenCV_Test

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/56964.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

几个nlp的小任务(机器翻译)

几个nlp的小任务(机器翻译) 安装依赖库数据集介绍与模型介绍加载数据集看一看数据集的样子评测测试数据预处理测试tokenizer处理目标特殊的token预处理函数对数据集的所有数据进行预处理微调预训练模型设置训练参数需要一个数据收集器,把处理好数据喂给模型设置评估方法参数…

美团面试拷打:ConcurrentHashMap 为何不能插入 null?HashMap 为何可以?

周末的时候,有一位小伙伴提了一些关于 ConcurrentHashMap 的问题,都是他最近面试遇到的。原提问如下(星球原贴地址:https://t.zsxq.com/11jcuezQs ): 整个提问看着非常复杂,其实归纳来说就是两个问题: ConcurrentHashMap 为什么 key 和 value 不能为 null?ConcurrentH…

【C++ 学习 ⑱】- 多态(上)

目录 一、多态的概念和虚函数 1.1 - 用基类指针指向派生类对象 1.2 - 虚函数和虚函数的重写 1.3 - 多态构成的条件 1.4 - 多态的应用场景 二、协变和如何析构派生类对象 2.1 - 协变 2.2 - 如何析构派生类对象 三、C11 的 override 和 final 关键字 一、多态的概念和虚…

webrtc的Sdp中的Plan-b和UnifiedPlan

在一些类似于视频会议场景下&#xff0c;媒体会话参与者需要接收或者发送多个流&#xff0c;例如一个源端&#xff0c;同时发送多个左右音轨的音频&#xff0c;或者多个摄像头的视频流&#xff1b;在2013年&#xff0c;提出了2个不同的SDP IETF草案Plan B和Unified Plan&#x…

android framework之Applicataion启动流程分析

Application启动流程分析 启动方式一&#xff1a;通过Launcher启动app 启动方式二&#xff1a;在某一个app里启动第二个app的Activity. 以上两种方式均可触发app进程的启动。但无论哪种方式&#xff0c;最终通过通过调用AMS的startActivity()来启动application的。 根据上图…

ABeam×Startup | 德硕管理咨询(深圳)创新研究团队拜访微漾创客空间

近日&#xff0c;德硕管理咨询&#xff08;深圳&#xff09;&#xff08;以下简称&#xff1a;“ABeam-SZ”&#xff09;创新研究团队前往微漾创客空间&#xff08;以下简称&#xff1a;微漾&#xff09;拜访参观&#xff0c;并展开合作交流。会议上&#xff0c;双方相互介绍了…

每日一题 57. 插入区间

读研了&#xff0c;开始用python刷题 今天的题目是力扣 每日一题 57. 插入区间 难度&#xff1a;中等 思路&#xff1a; 处理新区间起点&#xff0c;要么在两个老区间之间&#xff0c;要么被一个老区间包含处理新区间中点&#xff0c;同起点一样 我的代码如下 class Solut…

解锁市场进入成功:GTM 策略和即用型示例

在最初的几年里&#xff0c;创办一家初创公司可能会充满挑战。根据美国小企业管理局的数据&#xff0c;大约三分之二的新成立企业存活了两年&#xff0c;几乎一半的企业存活了五年以上。导致创业失败的因素有市场需求缺失、资金短缺、团队不合适、成本问题等。由此&#xff0c;…

合宙Air724UG LuatOS-Air LVGL API控件--按钮 (Button)

按钮 (Button) 按钮控件&#xff0c;这个就不用多说了&#xff0c;界面的基础控件之一。 示例代码 – 按键回调函数 event_handler function(obj, event) if event lvgl.EVENT_CLICKED then print(“Clicked\n”) elseif event lvgl.EVENT_VALUE_CHANGED then print(“To…

服务器数据库中了locked勒索病毒怎么办,locked勒索病毒恢复工具

最近一段时间网络上的locked勒索病毒非常嚣张&#xff0c;自从6月份以来&#xff0c;很多企业的计算机服务器数据库遭到了locked勒索病毒的攻击&#xff0c;起初locked勒索病毒攻击用友畅捷通T用户&#xff0c;后来七月份开始攻击金蝶云星空客户&#xff0c;导致企业的财务系统…

揭秘视频号创收计划:松松一个月赚1300+

我是卢松松&#xff0c;点点上面的头像&#xff0c;欢迎关注我哦&#xff01; 这是卢松松一个月视频号的收益&#xff0c;1300元。自从视频号在五月份推出创作者分成计划以来&#xff0c;许许多多的视频号创作者开始获得了一些收益&#xff0c;这绝对是一项挺不错的进展。 目前…

R-Meta分析核心技术教程

详情点击链接&#xff1a;全流程R-Meta分析核心技术教程 一&#xff0c;Meta分析的选题与检索 1、Meta分析的选题与文献检索 1)什么是Meta分析 2)Meta分析的选题策略 3)精确检索策略&#xff0c;如何检索全、检索准 4)文献的管理与清洗&#xff0c;如何制定文献纳入排除标准 …

回归预测 | MATLAB实现TSO-ELM金枪鱼群优化算法优化极限学习机多输入单输出回归预测(多指标,多图)

回归预测 | MATLAB实现TSO-ELM金枪鱼群优化算法优化极限学习机多输入单输出回归预测&#xff08;多指标&#xff0c;多图&#xff09; 目录 回归预测 | MATLAB实现TSO-ELM金枪鱼群优化算法优化极限学习机多输入单输出回归预测&#xff08;多指标&#xff0c;多图&#xff09;效…

【大数据】Linkis:打通上层应用与底层计算引擎的数据中间件

Linkis&#xff1a;打通上层应用与底层计算引擎的数据中间件 1.引言2.背景3.设计初衷4.技术架构5.业务架构6.处理流程7.如何支撑高并发8.用户级隔离度和调度时效性9.总结 Linkis 是微众银行开源的一款 数据中间件&#xff0c;用于解决前台各种工具、应用&#xff0c;和后台各种…

无锁并发:探秘CAS机制的魔力

&#x1f60a; 作者&#xff1a; 一恍过去 &#x1f496; 主页&#xff1a; https://blog.csdn.net/zhuocailing3390 &#x1f38a; 社区&#xff1a; Java技术栈交流 &#x1f389; 主题&#xff1a; 无锁并发&#xff1a;探秘CAS机制的魔力 ⏱️ 创作时间&#xff1a; 2…

4.6 TCP面向字节流

TCP 是面向字节流的协议&#xff0c;UDP 是面向报文的协议 操作系统对 TCP 和 UDP 协议的发送方的机制不同&#xff0c;也就是问题原因在发送方。 UDP面向报文协议&#xff1a; 操作系统不会对UDP协议传输的消息进行拆分&#xff0c;在组装好UDP头部后就交给网络层处理&…

Flask狼书笔记 | 03_模板

文章目录 3 模板3.1 模板基本使用3.2 模板结构组织3.3 模板进阶 3 模板 模板&#xff08;template&#xff09;&#xff1a;包含固定内容和动态部分的可重用文件。Jinja2模板引擎可用于任何纯文本文件。 3.1 模板基本使用 HTML实体&#xff1a;https://dev.w3.org/html5/htm…

kafka学习笔记

1、kafka是什么&#xff1f; kafka是一个高吞吐&#xff0c;分布式&#xff0c;基于发布/订阅的消息系统&#xff0c;最大的特性就是可以实时的处理大量的数据以满足各种需求场景&#xff1a;日志收集&#xff0c;离线和在线的消息消费&#xff0c;等等 2、kakfa的基础架构&am…

算法竞赛入门【码蹄集新手村600题】(MT1220-1240)C语言

算法竞赛入门【码蹄集新手村600题】(MT1220-1240&#xff09;C语言 目录MT1221 分数的总和MT1222 等差数列MT1223 N是什么MT1224 棋盘MT1225 复杂分数MT1226 解不等式MT1227 宝宝爬楼梯MT1228 宝宝抢糖果MT1229 搬家公司MT1230 圆周率MT1231圆周率IIMT1232 数字和MT1233 数字之…

c++的分文件编写

前言 在C中&#xff0c;你可以将代码分割成多个文件来提高可维护性和组织性。分割文件有助于将代码模块化&#xff0c;使大型项目更易于管理。以下是C中关于分文件的一些规则和概念&#xff1a; 理论知识 头文件&#xff08;Header Files&#xff09;&#xff1a; 头文件通常…