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
相关文章:
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/…...
一文搞懂深度信念网络!DBN概念介绍与Pytorch实战
目录 一、概述1.1 深度信念网络的概述1.2 深度信念网络与其他深度学习模型的比较结构层次学习方式训练和优化应用领域 1.3 应用领域图像识别与处理自然语言处理推荐系统语音识别无监督学习与异常检测药物发现与生物信息学 二、结构2.1 受限玻尔兹曼机(RBM࿰…...
MyBatis:使用注解让数据库操作更简单
目录 一、简介 二、配置 三、基于注解的基本使用 四、测试 总结 一、简介 在Java开发中,数据库操作是一个常见而重要的任务。为了方便地执行SQL语句,获取结果集,处理异常等,我们通常需要使用JDBC(Java Database …...
基于PyTorch深度学习遥感影像地物分类与目标检测、分割及遥感影像问题深度学习优化
我国高分辨率对地观测系统重大专项已全面启动,高空间、高光谱、高时间分辨率和宽地面覆盖于一体的全球天空地一体化立体对地观测网逐步形成,将成为保障国家安全的基础性和战略性资源。未来10年全球每天获取的观测数据将超过10PB,遥感大数据时…...
4.网络设计与redis、memcached、nginx组件(一)
网络组件系列文章目录 第四章 网络设计与redis、memcached、nginx组件 文章目录 网络组件系列文章目录文章的思维导图前言一、网络相关的问题,网络开发中要处理那些问题?网络操作IO连接建立连接断开消息到达消息发送网络操作IO特性 二、网络中IO检测IO函…...
leetcode分类刷题:矩阵顺时针模拟
1、这种题目是对代码熟练度考察,模拟顺时针建立或访问矩阵,需要注意矩阵是否为方阵 2、具体思路:以圈数为循环条件,每一圈都坚持左闭右开的区间规则;当小的行列值为奇数,最后一圈为一行或一列或一个数字的不…...
Java8新特性整理记录
1、list实体集合根据某个属性分组后求和 方法一: list.stream().collect(Collectors.groupingBy(e -> e.getId())).values().stream().map(d -> {DemoEntity sampleData d.get(0);sampleData.setPremium(d.stream().map(s -> new BigDecimal(s.getPrem…...
43.227.196.1 RAID技术有什么意义?
RAID(Redundant Array of Inexpensive Disks)技术是一种数据存储技术,它通过将多个硬盘组合在一起,来提高数据存储的性能、可靠性和容错性。 RAID技术的主要意义包括: 提高数据读写性能:RAID技术可以将多…...
c++ qt--信号与槽(一) (第三部分)
c qt–信号与槽(一) (第三部分) 一.用qt自带的方法添加信号槽 1.第一种 1.如何添加 2.在何处进行绑定 2.第二种 1.如何添加 2.在何处进行绑定 而且会在mainwindow.h中添加槽函数的声明,在mainwindow.cpp中添加槽函数的定义 在mainwindow…...
LLM学习《Prompt Engineering for Developer》
Prompt 如何构造好的Prompt 分割符:分隔符就像是 Prompt 中的墙,将不同的指令、上下文、输入隔开,避免意外的混淆。你可以选择用 ,“”",< >, ,: 等做分隔符,只要能明确…...
nginx-获取客户端IP地址
上有服务器与客户端中间是有nginx代理服务器的,上游服务器如何获取客户端真实ip地址? nginx代理服务器设置X-Forwarded-For的header参数,代理服务器通过remote_addr获取客户端ip地址,将ip地址写入nginx代理服务器的X-Forwarded-Fo…...
Redis 高可用之集群搭建和数据分片
Redis 高可用之集群搭建和数据分片 一、简介1. Redis 集群:2. 集群搭建: 二、Redis 集群搭建1. 单机 Redis 升级为 Redis Clustera. 搭建方法b. 搭建方式说明 2. 环境准备3. 配置修改4. 启动集群 三、Redis数据分片的实现Redis数据分片概念说明数据分片的…...
兄弟,王者荣耀的段位排行榜是通过Redis实现的?
目录 一、排行榜设计方案1、数据库直接排序2、王者荣耀好友排行 二、Redis实现计数器1、什么是计数器功能?2、Redis实现计数器的原理(1)使用INCR命令实现计数器(2)使用INCRBY命令实现计数器 三、通过Redis实现“王者荣…...
Linux系统编程--文件编程--打开创建文件
创建文件需要包含以下3个头文件 #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> 打开、创建文件有以下3个API open的返回值——文件描述符(索引作用),是一个小的非负整数 int open(const char*pathn…...
http协议与apache
http概念: 互联网:是网络的网络,是所有类型网络的母集 因特网:世界上最大的互联网网络。即因特网概念从属于互联网概念 万维网:万维网并非某种特殊的计算机网络,是一个大规模的、联机式的信息贮藏库&…...
搜索二叉树的算法解析与实例演示
目录 一.搜索二叉树的特性与实现1.特点2.实现二.搜索二叉树的性能 一.搜索二叉树的特性与实现 1.特点 二叉搜索树是特殊的二叉树,它有着更严格的数据结构特点: (1)非空左子树的所有键值小于其根结点的键值。 (2&…...
研磨设计模式day13组合模式
目录 场景 不用模式实现 代码实现 有何问题 解决方案 代码改造 组合模式优缺点 思考 何时选用 场景 不用模式实现 代码实现 叶子对象 package day14组合模式;/*** 叶子对象*/ public class Leaf {/*** 叶子对象的名字*/private String name "";/**…...
Linux命令(73)之zip
linux命令之zip 1.zip介绍 linux命令zip是用来压缩文件及解压缩文件名称后缀为".zip"的文件 2.zip用法 zip [参数] filename[.zip] zip常用参数 参数说明-r压缩递归处理-d从压缩文件内删除指定的文件-T检查备份文件是否正确无误-u更换较新的文件到压缩文件内-q不…...
深入理解Reactor模型的原理与应用
1、什么是Reactor模型 Reactor意思是“反应堆”,是一种事件驱动机制。 和普通函数调用的不同之处在于:应用程序不是主动的调用某个 API 完成处理,而是恰恰相反,Reactor逆置了事件处理流程,应用程序需要提供相应的接口并…...
微信小程序开发的投票评选系统设计与实现
摘要 越来越多信息化融入到我们生活当中的同时,也在改变着我们的生活和学习方式,当然,变化最明显的除了我们普通民众之外,要数高校学生的生活方式以及校园信息化的变革。智慧是改变生活和生产的一种来源,那么智慧的体…...
【校招VIP】算法考点之堆排
考点介绍: 排序算法属于数据结构和算法的基础内容,并且也是大厂笔试中的高频考点。 堆排序是使用一棵树存储序列这个课树只保证跟节点是这棵树中的最小值,但并不保证其他节点是按顺序的。因此他的排序是每次从堆中取得堆顶,取得 n…...
关于yarn安装时报“node“ is incompatible with this module的解决办法
前提: 在用vue写一个h5页面时,当在用yarn安装时,提示如下错误: The engine “node” is incompatible with this module. Expected version "^14.18.0 || ^16.14.0 || >18. 解决办法 我是使用命令忽略错误:…...
开源利器推荐:美团动态线程池框架的接入分享及效果展示
前言 蛮早前有些过关于线程池的使用及参数的一些参考配置,有兴趣的可以翻看以前的博文,但终究无法解决线程池的动态监控和实时修改。 以前读过美团早期发布的动态线程池框架的思路相关文章,但想要独自实现不是一件容易的事。 去年,…...
Linux目录结构与文件管理 (02)(四)
提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 目录 前言 一、查看文件内容 二、创建文件 三、删除文件 四、 移动文件 五、复制文件 六、编辑文件内容 总结 前言 今天是在昨天的基础上继续学习,主要…...
对1GHz脉冲多普勒雷达进行快速和慢速处理生成5个移动目标的距离多普勒图研究(Matlab代码实现)
💥💥💞💞欢迎来到本博客❤️❤️💥💥 🏆博主优势:🌞🌞🌞博客内容尽量做到思维缜密,逻辑清晰,为了方便读者。 ⛳️座右铭&a…...
uni.uploadFile上传 PHP接收不到
开始这样,后端$file $request->file(file);接收不到 数据跑到param中去了 去掉Content-Type,就能接收到了 param只剩下...
2023年高教社杯 国赛数学建模思路 - 复盘:光照强度计算的优化模型
文章目录 0 赛题思路1 问题要求2 假设约定3 符号约定4 建立模型5 模型求解6 实现代码 建模资料 0 赛题思路 (赛题出来以后第一时间在CSDN分享) https://blog.csdn.net/dc_sinor?typeblog 1 问题要求 现在已知一个教室长为15米,宽为12米&…...
Netty简易聊天室
文章目录 本文目的参考说明环境说明maven依赖日志配置单元测试 功能介绍开发步骤 本文目的 通过一个简易的聊天室案例,讲述Netty的基本使用。同时分享案例代码。项目中用到了log4j2,junit5,同时分享这些基础组件的使用。项目中用到了awt&…...
Flutter Cannot run with sound null safety, because the following dependencies
flutter sdk 版本升级到2.0或者更高的版本后,运行之前的代码会报错 Error: Cannot run with sound null safety, because the following dependencies dont support null safety:- package:flutter_swiper- package:flutter_page_indicator- package:transformer_p…...
利用改进的遗传算法(种群隔离与个体迁移)mpi并行解决tsp问题
序 关于tsp问题的概述以及如何使用遗传算法进行求解已经在上一篇文章中说明了:遗传算法解决TSP问题. 但是,作为一种演化算法,遗传算法还存在着许多问题,比如早熟的情况,很容易在算法前期就已经收敛了,大量…...
建站用Wordpress还是/电商平台有哪些
虚拟机的网卡配置指的是在虚拟机中配置网络接口卡的过程。这一过程可以通过虚拟机管理软件来完成,如 VMware、VirtualBox 等。 通常情况下,虚拟机网卡的配置过程包括以下几个步骤: 在虚拟机管理软件中打开虚拟机设置界面。 在虚拟机设置界面中࿰…...
自己可以给公司做网站吗/千锋教育的官网
2019独角兽企业重金招聘Python工程师标准>>> 场景:生产环境下,多个普通用户登录,登录后自动记录history操作到某个统一目录保存。 具体要求: 1) 每个用户登录后自动创建子目录及history记录文件ÿ…...
福田做网站多少钱/百度竞价托管
前言 本博文主要讲解的知识点如下: 校验器统一处理异常RESTful拦截器Validation 在我们的Struts2中,我们是继承ActionSupport来实现校验的...它有两种方式来实现校验的功能 手写代码 XML配置 这两种方式也是可以特定处理方法或者整个Action的而SpringMVC…...
企业网站策划/网站推广与优化平台
https://www.lydsy.com/JudgeOnline/problem.php?id2901 挺好玩的一道线性代数题 让我这个没学过线性代数的人 更加理解了矩阵乘法 我感觉是测评机今天晚上有点问题 代码应该不能再优化了 已经相当完美了 代码: #include<bits/stdc.h> using namespace std; …...
华为官方网站专卖店/查网站
矩阵的Hadamard积与符号模式【摘要】:我们主要讨论了非负矩阵、M-阵的Hadamard积与Fan积问题,以及矩阵Hadamard积的一些范数不等式.同时也讨论了逆M-矩阵、零模式不变矩阵、符号模式矩阵、k-幂等阵和符号k-幂等阵等特殊矩阵的相关问题&#x…...
产品外观工业设计公司/小吴seo博客
读书笔记--第5篇--《公司绝不会告诉你的50大秘密》 0.法律解救不了您。 1.聪明过头并非明智之举。 2.年龄和性别歧视是活生生的现实。 3.公司并非畅所欲言的好地方。 4.如果你与老板作对,必然会被逐出公司大门。 5.与人力部门的闲谈,会使你面临失业的危险…...