当前位置: 首页 > news >正文

【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;
}

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • 【C++ 秘籍】解锁 stack、queue 和 priority_queue 及容器适配器的神奇世界
  • haproxy七层代理
  • C++构造和析构
  • `torch.device(“mps“)`、pip华为镜像源dockerfile以及后缀安装
  • 华为OD笔试
  • C#开启和关闭UAC功能
  • MySQL —— 约束
  • 周报 | 24.8.5-24.8.11文章汇总
  • Ubuntu安装MySQL5.7 + Apache + PHP + 禅道 保姆及教程
  • 糟糕界面集锦-控件篇05
  • XML动态sql查询当前时间之前的信息报错
  • 【数据结构进阶】哈希的应用
  • Matlab-use-yalmip-and-cplex12-10/
  • Flink开发语言选择:Java vs Scala,哪种更适合你的项目?
  • RAG与LLM原理及实践(11)--- Milvus hybrid search 源码分析及思想
  • [译]如何构建服务器端web组件,为何要构建?
  • angular学习第一篇-----环境搭建
  • C++类中的特殊成员函数
  • CentOS学习笔记 - 12. Nginx搭建Centos7.5远程repo
  • docker-consul
  • JavaWeb(学习笔记二)
  • Java应用性能调优
  • LeetCode刷题——29. Divide Two Integers(Part 1靠自己)
  • mysql中InnoDB引擎中页的概念
  • nodejs:开发并发布一个nodejs包
  • PHP 的 SAPI 是个什么东西
  • PHP变量
  • php中curl和soap方式请求服务超时问题
  • vue的全局变量和全局拦截请求器
  • 服务器从安装到部署全过程(二)
  • 服务器之间,相同帐号,实现免密钥登录
  • 聊聊directory traversal attack
  • 面试总结JavaScript篇
  • 前端
  • 数据库写操作弃用“SELECT ... FOR UPDATE”解决方案
  • 新手搭建网站的主要流程
  • 一文看透浏览器架构
  • 与 ConTeXt MkIV 官方文档的接驳
  • 正则与JS中的正则
  • MiKTeX could not find the script engine ‘perl.exe‘ which is required to execute ‘latexmk‘.
  • mysql面试题分组并合并列
  • ​2020 年大前端技术趋势解读
  • # Redis 入门到精通(八)-- 服务器配置-redis.conf配置与高级数据类型
  • ###51单片机学习(2)-----如何通过C语言运用延时函数设计LED流水灯
  • #Datawhale AI夏令营第4期#多模态大模型复盘
  • (09)Hive——CTE 公共表达式
  • (Redis使用系列) Springboot 使用Redis+Session实现Session共享 ,简单的单点登录 五
  • (ros//EnvironmentVariables)ros环境变量
  • (八)五种元启发算法(DBO、LO、SWO、COA、LSO、KOA、GRO)求解无人机路径规划MATLAB
  • (第一天)包装对象、作用域、创建对象
  • (二) Windows 下 Sublime Text 3 安装离线插件 Anaconda
  • (附源码)springboot猪场管理系统 毕业设计 160901
  • (附源码)ssm高校实验室 毕业设计 800008
  • (附源码)ssm失物招领系统 毕业设计 182317
  • (六)软件测试分工