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.rhkb.cn/news/108601.html

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

相关文章

自动设置服务器全教程

亲爱的爬虫探险家&#xff01;在网络爬虫的世界里&#xff0c;自动设置代理服务器是一个非常有用的技巧。今天&#xff0c;作为一家代理服务器供应商&#xff0c;我将为你呈上一份轻松实用的教程&#xff0c;帮助你轻松搞定爬虫自动设置代理服务器。 一、为什么需要自动设置代…

C语言实现状态机

关于状态机&#xff0c;基础的知识点可以自行理解&#xff0c;讲解的很多&#xff0c;这里主要是想写一个有限状态机FSM通用的写法&#xff0c;目的在于更好理解&#xff0c;移植&#xff0c;节省代码阅读与调试时间&#xff0c;体现出编程之美。 传统的实现方案 if...else : …

Unittest 笔记:unittest拓展生成HTM报告发送邮件

HTMLTestRunner 是一个unitest拓展可以生成HTML 报告 下载地址&#xff1a;GitHub: https://github.com/defnnig/HTMLTestRunner HTMLTestRunner是一个独立的py文件&#xff0c;可以放在Lib 作为第三方模块使用或者作为项目的一部分。 方式1&#xff1a; 验证是否安装成功&…

基于Android的课程教学互动系统 微信小程序uniapp

教学互动是学校针对学生必不可少的一个部分。在学校发展的整个过程中&#xff0c;教学互动担负着最重要的角色。为满足如今日益复杂的管理需求&#xff0c;各类教学互动程序也在不断改进。本课题所设计的springboot基于Android的教学互动系统&#xff0c;使用SpringBoot框架&am…

Android TV开发之VerticalGridView

Android TV应用开发和手机应用开发是一样的&#xff0c;只是多了焦点控制&#xff0c;即选中变色。 androidx.leanback.widget.VerticalGridView 继承 BaseGridView &#xff0c; BaseGridView 继承 RecyclerView 。 所以 VerticalGridView 就是 RecyclerView &#xff0c;使…

mysql下载

网址 MySQL :: Download MySQL Community Serverhttps://dev.mysql.com/downloads/mysql/ 2、选择MSI进行安装 3、这里我选择离线安装 4、这里我选择直接下载 5、等待下载安装即可

【无法联网】电脑wifi列表为空的解决方案

打开电脑, 发现wifi列表为空, 点击设置显示未连接 首先检查是不是网卡驱动有问题, cmd, devmgmt.msc 找到网络适配器, 看看网卡前面是否有感叹号, 如果没有则说明网卡没问题, 有问题则重装驱动 看看网络协议是否设置正确 找到"控制面板\所有控制面板项\网络和共享中心&…

通讯录(C语言)

通讯录 一、基本思路及功能介绍二、功能实现1.基础菜单的实现2.添加联系人信息功能实现3.显示联系人信息功能实现4.删除联系人信息功能实现5.查找联系人信息功能实现6.修改联系人信息功能实现7.排序联系人信息功能实现8.加载和保存联系人信息功能实现 三、源文件展示1.test.c2.…

MATLAB图论合集(三)Dijkstra算法计算最短路径

本贴介绍最短路径的计算&#xff0c;实现方式为迪杰斯特拉算法&#xff1b;对于弗洛伊德算法&#xff0c;区别在于计算了所有结点之间的最短路径&#xff0c;考虑到MATLAB计算的便捷性&#xff0c;计算时只需要反复使用迪杰斯特拉即可&#xff0c;暂不介绍弗洛伊德的实现&#…

ChatGPT 与前端技术实现制作大屏可视化

像这样的综合案例实分析,我们可以提供案例,维度与指标数据,让ChatGPT与AIGC 帮写出完整代码,并进行一个2行2列的布局设置。 数据与指令如下: 商品名称 销量 目标 完成率 可乐 479 600 79.83% 雪碧 324 600 54.00% 红茶 379 600 63.…

Unity报错DllNotFoundException:sqlite3

Unity项目中要使用轻型数据库sqlite&#xff0c;除了导入sqlite3.dll外&#xff0c;还需要导入Mono.Data.Sqlite.dll和System.Data.dll&#xff08;工程里或者编辑器里面有System.Data.dll时就不需要&#xff09;两个文件。 如果在编辑器中运行出现 “DllNotFoundException:sql…

优化器调整策略

损失函数的作用是衡量模型输出与真实标签的差异。当我们有了这个loss之后&#xff0c;我们就可以通过反向传播机制得到参数的梯度&#xff0c;那么我们如何利用这个梯度进行更新参数使得模型的loss逐渐的降低呢&#xff1f; 优化器的作用 Pytorch的优化器&#xff1a; 管理并…

nacos总结1

5.Nacos注册中心 国内公司一般都推崇阿里巴巴的技术&#xff0c;比如注册中心&#xff0c;SpringCloudAlibaba也推出了一个名为Nacos的注册中心。 5.1.认识和安装Nacos Nacos是阿里巴巴的产品&#xff0c;现在是SpringCloud中的一个组件。相比Eureka功能更加丰富&#xff0c…

windows可视化界面管理服务器上的env文件

需求&#xff1a;在 Windows 环境中通过可视化界面编辑位于 Linux 主机上的 env 文件的情况&#xff0c;我现在环境是windows环境&#xff0c;我的env文件在linux的192.168.20.124上&#xff0c;用户是op&#xff0c;密码是op&#xff0c;文件绝对路径是/home/op/compose/env …

CTFhub-sqli注入-报错注入

用到的函数 updatexml(1&#xff0c; &#xff0c;1) concat(0x7e, ,0x7e) group_concat(目标值) right(&#xff0c;32) 1 1 1 union select updatexml(1,concat(0x7e,database(),0x7e),1) 1 union select updatexml(1,concat(0x7e,(select(group_concat(ta…

TensorBoard的使用

TensorBoard&#xff1a;对图像进行变换 1. SummaryWriter的使用 ctrl类出现注释解析&#xff1a; 将条目直接log_dir写入要成为由TensorBoard使用。 “摘要编写器”类提供了一个高级 API 来创建事件文件&#xff0c;并在给定目录中添加摘要和事件。该类更新文件内容异步。…

leetcode.105 从前序和中序遍历序列构造二叉树

题目描述&#xff1a; 给定两个整数数组 preorder 和 inorder &#xff0c;其中 preorder 是二叉树的先序遍历&#xff0c; inorder 是同一 棵树的中序遍历&#xff0c;请构造二叉树并返回其根节点。 题目要求&#xff1a; 1 < preorder.length < 3000inorder.length…

【论文阅读】自动驾驶安全的研究现状与挑战

文章目录 术语解释摘要1.引言1.1.自动驾驶安全1.2.攻击面1.3.内容和路线图 2.自动驾驶技术2.1.组成2.2.技术 3.传感器安全3.1.照相机3.2.GNSS&#xff08;全球导航系统&#xff09;/IMU&#xff08;惯性测量单元&#xff09;3.3.超声波传感器3.4.毫米波雷达3.5.激光雷达3.6.多传…

Harbour.Space Scholarship Contest 2023-2024 (Div. 1 + Div. 2) A ~ D

比赛链接 A 正常枚举就行&#xff0c;从最后一位往前枚举&#xff0c;-1、-2、-3...这样 #include<bits/stdc.h> #define IOS ios::sync_with_stdio(0);cin.tie(0);cout.tie(0); #define endl \nusing namespace std;typedef pair<int, int> PII; typedef long l…

使用windeployqt和InstallShield打包发布Qt软件的流程

前言 Qt编译之后需要打包发布&#xff0c;并且发布给用户后需要增加一个安装软件&#xff0c;通过安装软件可以实现Qt软件的安装&#xff1b;用于安装软件的软件有很多&#xff0c;这里主要介绍InstallShield使用的流程&#xff1b; 使用windeployqt打包Qt编译后的程序 Qt程序…