【QGroundControl二次开发】十. QT添加GStreamer视频播放同时保存

上一章介绍使用QT播放GStreamer视频流 【QGroundControl二次开发】八. QT实现播放gstreamer视频。
这章介绍如何在原有基础上保存为视频,同时保存为一个个规定大小的小视频。

一. 思想

之前的文章展示了如何在QT中播放GST视频流,这章在原有的基础上增加了一部分代码。原理就是使用Gstreamer的tee为管道开一条支流。大致图示如下(增加一条保存分路):
在这里插入图片描述

二. 增加代码

2.1 创建文件分路所需元素

record_queue = gst_element_factory_make("queue", "record_queue");
video_rate = gst_element_factory_make("videorate", "video_rate");
jpeg_enc = gst_element_factory_make("jpegenc", "jpeg_enc");
avi_mux = gst_element_factory_make("avimux", "avi_mux");
file_sink = gst_element_factory_make ("filesink", "file_sink");

2.2 把这些保存视频要使用的元素放进管道。

gst_bin_add_many(GST_BIN (pipeline), record_queue,video_rate,jpeg_enc,avi_mux,file_sink, NULL);
gst_element_sync_state_with_parent(record_queue);// link element
if(gst_element_link_many(record_queue,video_rate,jpeg_enc,avi_mux,file_sink, NULL) != TRUE){g_printerr ("Elements could not be linked.\n");gst_object_unref (pipeline);return -1;
}
g_object_set (G_OBJECT(file_sink),"location",videofilename,NULL);

2.3 把支路和主路连接起来。

//Manually link the Tee, which has "Request" pads
tee_record_pad = gst_element_get_request_pad (tee, "src_%u");
g_print ("Obtained request pad %s for record branch.\n", gst_pad_get_name (tee_record_pad));
queue_record_pad = gst_element_get_static_pad (record_queue, "sink");
if (!queue_record_pad) g_printerr ("queue_record_pad not be get.\n");
// 链接tee的MP4分支到mp4mux
if (gst_pad_link(tee_record_pad, queue_record_pad) != GST_PAD_LINK_OK) {g_printerr("Tee MP4 branch could not be linked.\n");gst_object_unref(pipeline);return -1;
}//设置队列参数
g_object_set (G_OBJECT(record_queue),"max-size-buffers",0,NULL);
g_object_set (G_OBJECT(record_queue),"max-size-time",0,NULL);
g_object_set (G_OBJECT(record_queue),"max-size-bytes",512000000,NULL);
//释放pad
gst_object_unref (queue_record_pad);

最后保存的为MP4文件,尝试x264enc,mp4mux 无发连接管道,但是使用jpeg_enc,avi_mux可以保存和播放MP4视频。。。

三. 改进

由于一直保存视频会导致文件特别大,需要将保存的视频按需自动分成一个一个的规定大小或时间的视频,就要用到splitmuxsink 元素。

splitmuxsink 是 GStreamer 中一个非常有用的元素,它可以在达到最大文件大小或时间阈值时,自动分割视频文件,且分割操作在视频关键帧边界进行,保证了视频片段的完整性。通俗讲splitmuxsink = muxer + sink。

默认情况下,splitmuxsink 使用 mp4mux 作为muxer和 filesink 作为sink,但也可以通过设置相应的属性来使用其他的muxer和sink。

//文件分路所需元素
record_queue = gst_element_factory_make("queue", "record_queue");
video_rate = gst_element_factory_make("videorate", "video_rate");
jpeg_enc = gst_element_factory_make("jpegenc", "jpeg_enc");
avi_mux = gst_element_factory_make("avimux", "avi_mux");
splitmuxsink= gst_element_factory_make("splitmuxsink", "splitmuxsink");
file_sink = gst_element_factory_make ("filesink", "file_sink");g_object_set(G_OBJECT(splitmuxsink),"muxer", avi_mux,"sink", file_sink,"max-size-time", (guint64)90*GST_SECOND,"max-files", 20,"location",filename , NULL);

这段代码设置splitmuxsink参数,每段视频最长90秒,每次最多保存20个视频文件超出覆盖最旧的文件。max-size-time 和 max-size-bytes 属性分别用来设置文件的最大时间和大小限制。max-files 属性允许你指定要保留在磁盘上的最大文件数。当达到此最大值时,最旧的文件将开始被删除以为新文件腾出空间。

四. 效果

在这里插入图片描述

五. 完整代码:

#include <QApplication>
#include <QWidget>
#include <QtConcurrent/QtConcurrent>
#include <gst/gst.h>
#include <glib.h>
#include <gst/video/videooverlay.h>
#include "ui_mainwindow.h"int main(int argc, char *argv[]) {// 获取当前时间QString filename = "video%02d.mp4";//初始化程序QApplication a(argc, argv);GstElement *pipeline, *udpsrc, *capsfilter, *disp_queue, *rtph264depay, *h264parse, *avdec_h264, *videoconvert, *vsink;GstCaps *caps;// 创建新元素GstElement *tee, *record_queue,*video_rate, *avi_mux, *jpeg_enc, *file_sink,*splitmuxsink;GstPad *tee_record_pad, *queue_record_pad;GstStateChangeReturn ret;QWidget *window = new QWidget();window->resize(1920, 1080);window->show();WId xwinid = window->winId();// 初始化 GStreamergst_init(NULL, NULL);// 创建元素pipeline = gst_pipeline_new("my-pipeline");udpsrc = gst_element_factory_make("udpsrc", "udpsrc");capsfilter = gst_element_factory_make("capsfilter", "capsfilter");disp_queue = gst_element_factory_make("queue", "disp_queue");tee = gst_element_factory_make ("tee", "tee");h264parse = gst_element_factory_make("h264parse", "h264parse");avdec_h264 = gst_element_factory_make("avdec_h264", "avdec_h264");rtph264depay = gst_element_factory_make("rtph264depay", "rtph264depay");videoconvert = gst_element_factory_make("videoconvert", "videoconvert");vsink = gst_element_factory_make("xvimagesink", "vsink");//glimagesink//文件分路所需元素record_queue = gst_element_factory_make("queue", "record_queue");video_rate = gst_element_factory_make("videorate", "video_rate");jpeg_enc = gst_element_factory_make("jpegenc", "jpeg_enc");avi_mux = gst_element_factory_make("avimux", "avi_mux");splitmuxsink= gst_element_factory_make("splitmuxsink", "splitmuxsink");file_sink = gst_element_factory_make ("filesink", "file_sink");//设置splitmuxsink参数,每段视频最长90秒,每次最多保存20个视频文件超出覆盖最旧的文件g_object_set(G_OBJECT(splitmuxsink),"muxer", avi_mux,"sink", file_sink,"max-size-time", (guint64)90*GST_SECOND,"max-files", 20,"location",filename , NULL);// 设置 udpsrc 元素的参数g_object_set(udpsrc, "port", 25600, NULL);// 创建 capscaps = gst_caps_new_simple("application/x-rtp","media", G_TYPE_STRING, "video","clock-rate", G_TYPE_INT, 90000,"encoding-name", G_TYPE_STRING, "H264",/*"depth", G_TYPE_STRING, "8",* "width", G_TYPE_STRING, "1920",* "height", G_TYPE_STRING, "1080",*/NULL);g_object_set(capsfilter, "caps", caps, NULL);gst_caps_unref(caps);//检查元素if (!pipeline || !udpsrc || !capsfilter || !disp_queue || !rtph264depay || !h264parse || !avdec_h264 || !videoconvert || !file_sink || !vsink){g_printerr("Failed to create elements. Exiting.\n0000000");return -1;}// 将元素添加到管道中gst_bin_add_many(GST_BIN(pipeline), udpsrc, capsfilter, disp_queue, rtph264depay, h264parse, avdec_h264, videoconvert, vsink,tee,/*queue2, x264enc,mp4mux,file_sink,*/ NULL);if (gst_element_link_many (udpsrc,capsfilter,rtph264depay,h264parse, avdec_h264,videoconvert,tee,disp_queue, vsink, NULL) != TRUE /*||gst_element_link_many (queue2, x264enc,mp4mux,file_sink, NULL) != TRUE */){g_printerr ("main Elements could not be linked.\n1111111111111111");gst_object_unref (pipeline);return -1;}//录像支路gst_bin_add_many(GST_BIN (pipeline), record_queue,video_rate,jpeg_enc,splitmuxsink, NULL);gst_element_sync_state_with_parent(record_queue);// link elementif(gst_element_link_many(record_queue,video_rate,jpeg_enc,splitmuxsink, NULL) != TRUE){g_printerr ("Elements could not be linked.\n");gst_object_unref (pipeline);return -1;}//    g_object_set (G_OBJECT(file_sink),"location",videofilename,NULL);//Manually link the Tee, which has "Request" padstee_record_pad = gst_element_get_request_pad (tee, "src_%u");g_print ("Obtained request pad %s for record branch.\n", gst_pad_get_name (tee_record_pad));queue_record_pad = gst_element_get_static_pad (record_queue, "sink");if (!queue_record_pad) g_printerr ("queue_record_pad not be get.\n");// 链接tee的MP4分支到mp4muxif (gst_pad_link(tee_record_pad, queue_record_pad) != GST_PAD_LINK_OK) {g_printerr("Tee MP4 branch could not be linked.\n");gst_object_unref(pipeline);return -1;}//设置队列参数g_object_set (G_OBJECT(record_queue),"max-size-buffers",0,NULL);g_object_set (G_OBJECT(record_queue),"max-size-time",0,NULL);g_object_set (G_OBJECT(record_queue),"max-size-bytes",512000000,NULL);//释放padgst_object_unref (queue_record_pad);// 链接QT界面gst_video_overlay_set_window_handle (GST_VIDEO_OVERLAY (vsink), xwinid);// 设置管道状态为播放ret = gst_element_set_state(pipeline, GST_STATE_PLAYING);if (ret == GST_STATE_CHANGE_FAILURE) {g_printerr("Failed to set pipeline state to PLAYING. Exiting.\n");gst_object_unref(pipeline);return -1;}auto res = a.exec();// 释放资源g_free(videofilename);gst_element_set_state(pipeline, GST_STATE_NULL);gst_object_unref(pipeline);return res;
}

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

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

相关文章

金九银十,软件测试面试题合集(含答案)

前言 前面看到了一些面试题&#xff0c;总感觉会用得到&#xff0c;但是看一遍又记不住&#xff0c;所以我把面试题都整合在一起&#xff0c;都是来自各路大佬的分享&#xff0c;为了方便以后自己需要的时候刷一刷&#xff0c;不用再到处找题&#xff0c;今天把自己整理的这些…

常见的几种用例测试方法

等价类划分法 适用场景&#xff1a;需要有大量的测试数据输入&#xff0c;但是我们实际测试中不可能一一列举进行测试&#xff0c;所以讲数据进行分类&#xff0c;选出具有代表性的数据代表一类数据进行测试。 分类&#xff1a; 有效等价类&#xff1a;满足需求的数据无效等…

普元EOS-新项目不停提示登录信息已过期

1 问题 新创建的EOS精简应用&#xff0c; 项目端口为 28015 启动后&#xff0c;在浏览器输入地址 http://127.0.0.1:28015 。 页面不停提示 “登录信息已过期” 2 解决办法 EOS的项目对Login-Filter的配置错误&#xff0c; EOS的项目在Http安全过滤管理的时候&#xff0c;会…

【原创】java+swing+mysql商品信息管理系统设计与实现

个人主页&#xff1a;程序员杨工 个人简介&#xff1a;从事软件开发多年&#xff0c;前后端均有涉猎&#xff0c;具有丰富的开发经验 博客内容&#xff1a;全栈开发&#xff0c;分享Java、Python、Php、小程序、前后端、数据库经验和实战 开发背景&#xff1a; 使用javaswing技…

使用mybatis注解和xml映射执行javaWeb中增删改查等操作

Mapper接口 使用注解执行SQL语句操作和相应的Java抽象类&#xff08;对于简单的增删改查使用注解&#xff09; Mapper public interface EmpMapper {// 根据id删除员工信息Delete("delete from mybatis.emp where id#{id}")public int EmpDelete(Integer id);// 查…

【mysql 第一篇章】系统和数据库的交互方法

一、宏观的查看系统怎么和数据库交互 在我们刚刚接触系统和数据库的时候不明白其中的原理&#xff0c;只知道系统和数据库是需要交互的。所以我们会理解成上图的形式。 二、MYSQL 驱动 随着我们的学习时间的加长以及对程序的了解&#xff0c;发现链接数据库是需要有别的工具辅…

可乐机的设计验证

前言 状态机&#xff08;State Machine&#xff09;是一种数学模型&#xff0c;用于表示具有有限状态集合的系统。它通过定义状态、转移规则和事件&#xff0c;描述系统在不同条件下的行为。状态机的核心概念包括状态、事件、转移和动作。状态是系统的具体条件或配置&#xff0…

【Python】函数入门(下)

3&#xff09;&#xff09;* ** ​​​​​​注意&#xff1a;也遵循位置传参在前面&#xff0c;按关键字传参在后面。 代码示例&#xff1a; def func(*args,**kwargs):print(args,kwargs) 该函数中的参数会自动根据传参的方式不同&#xff08;即&#xff1a;按位置…

k8s 四种Service类型(ClusterIP、NodePort、LoadBalancer、ExternalName)详解

&#x1f407;明明跟你说过&#xff1a;个人主页 &#x1f3c5;个人专栏&#xff1a;《Kubernetes航线图&#xff1a;从船长到K8s掌舵者》 &#x1f3c5; &#x1f516;行路有良友&#xff0c;便是天堂&#x1f516; 目录 一、引言 1、k8s概述 2、Service在Kubernetes中的…

【Redis】事务

目录 什么是事务 事务操作 MULTI EXEC DISCARD WATCH UNWATCH 什么是事务 Redis 的事务和 MySQL 的事务概念上是类似的. 都是把⼀系列操作绑定成⼀组. 让这⼀组能够批量执⾏. 但是注意体会 Redis 的事务和 MySQL 事务的区别: 弱化的原⼦性: redis 没有 "回滚机制…

Zabbix自动导出PDF报告

zabbix6提供了定时导出PDF报告功能。此功能可按照Dashboard维度&#xff0c;定时自动导出报告&#xff0c;并通过邮件发送。 1.安装 zabbix 提供了官方的rhel8版本的rpm包&#xff0c;可使用yum方式安装&#xff0c;zabbix自动导出PDF功能是基于go环境的zabbix web service程…

C语言 ——— 在杨氏矩阵中查找具体的某个数

目录 何为杨氏矩阵 题目要求 代码实现 何为杨氏矩阵 可以把杨氏矩阵理解为一个二维数组&#xff0c;这个二维数组中的每一行从左到右是递增的&#xff0c;每一列从上到下是递增的 题目要求 在杨氏矩阵中查找具体的某个数 要求&#xff1a;时间复杂度小于O(N) 代码实现…

如何定义和引用二维数组

一.二维数组 常称为矩阵&#xff0c;把二维数组写成行和列的排列形式。、 二.怎么定义二维数组 float pay[3][5]; 以上定义了一个float型的二维数组&#xff0c;第1维有3个元素&#xff0c;第2维有6个元素。每一维的长度分别用一对方括号括起来。 二维数组定义的一般形式为 …

基于STM32开发的智能家居照明控制系统

目录 引言环境准备工作 硬件准备软件安装与配置系统设计 系统架构硬件连接代码实现 初始化代码控制代码应用场景 家庭照明自动化节能照明管理常见问题及解决方案 常见问题解决方案结论 1. 引言 智能家居照明控制系统通过整合各种传感器和控制器&#xff0c;能够实现对家居照…

基于ICMP(Ping)的多线程网络通道监视程序(QT)开发

基于ICMP(Ping)的多线程网络通道监视程序(QT)开发 1、 ICMP原理简介 可参考 ICMP(Ping)功能原理及其C实现简介 。 2、 网络通道监视程序开发 设计原理&#xff1a; 通过PING 功能实现服务器、交换机、网闸等设备的网络检测&#xff0c;判断网络的否可达和TTL计算 。 具备功…

【应用层协议】自定义协议 {定义结构化数据;数据格式转换:序列化和反序列化,使用json库进行数据格式交换;分包和解包:为报文内容添加报头}

一、简单了解TCP协议&#xff08;引子&#xff09; 1.1 三次握手 三次握手就是客户端向服务端发起连接的过程 服务器初始化 调用socket&#xff0c;创建套接字文件 调用bind&#xff0c;将当前的文件描述符和ip/port绑定在一起&#xff1b;如果这个端口已经被其他进程占用了&…

外贸12年,通过6个方法,成交几千万订单

做外贸有12个年头了&#xff0c;各种各样的方法都有试过&#xff0c;我成交的这几千万订单&#xff0c;大部分都是通过这6个方法来的&#xff0c;下面我来给大家整理分享一下。 1.谷歌搜索 谷歌搜索算是做外贸入门级的基础技能了&#xff0c;要做好外贸&#xff0c;这个技能一…

86.小米相机修改拍照(尺寸,画幅,比例)的方法

目录 1.打开相机&#xff0c;拍照模式&#xff0c;上面有个箭头或三个点&#xff0c;点击 2.点击画幅 3.点击你想要的画幅即可。 想要修改手机照片的&#xff08;尺寸&#xff0c;画幅&#xff0c;比例&#xff09;时&#xff0c;总会去找分辨率&#xff0c;其实并不是&…

Leetcode - 周赛410

目录 一&#xff0c;3248. 矩阵中的蛇 二&#xff0c;3249. 统计好节点的数目 三&#xff0c;3250. 单调数组对的数目 I dfs记忆化 dfs记忆化1&#xff1a;1改递推 四&#xff0c;3251. 单调数组对的数目 II 一&#xff0c;3248. 矩阵中的蛇 本题就是一道纯模拟题&#x…

django高校毕业生就业推荐系统-计算机毕业设计源码26096

摘 要 当前就业市场竞争激烈&#xff0c;高校毕业生面临着就业难的问题&#xff0c;同时企业也面临招聘难、选人难的挑战。为了更好地对接高校毕业生和企业之间的需求&#xff0c;为毕业生提供个性化的就业求着信息&#xff0c;开发一套充分利用Django和Python技术实现的毕业生…