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

news2024/11/27 22:40:02

上一章介绍使用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();

    // 初始化 GStreamer
    gst_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);
    // 创建 caps
    caps = 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 element
    if(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" 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);

    // 链接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.coloradmin.cn/o/2034954.html

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

相关文章

vue-cli(二)

箭头函数 一般的函数&#xff1a; 这里window是用来调用函数的 function fun(){console.log(this) } window.fun(); 箭头函数&#xff1a; 1、如果只有一个参数&#xff0c;形参的小括号可以省略 2、如果只有一条语句&#xff0c;{}可以省略 完整的写法 let fun2 a>…

前缀和优化DP

LeetCode3251 单调数组对数目 本题的子问题是下标 0 到 i 中的单调数组对的个数&#xff0c;且 arr1​[i]j&#xff0c;将其记作 f[i][j]。 class Solution { public:int countOfPairs(vector<int>& nums) {const int mod1e97;int nnums.size();int mnums[0];for(in…

C++(STL)的List解读

目录 list简介 list的几个特性 接口函数 1.默认成员函数 2.迭代器相关函数 3.容量相关的函数 4.成员访问相关的函数 5.modify系列 6.operation系列 7.重载在全局的函数 list简介 Lists are sequence containers that allow constant time insert and erase operation…

【Linux】阻塞信号|信号原理|深入理解捕获信号|内核态|用户态|sigaction|可重入函数|volatile|SIGCHILD|万字详解

目录 ​编辑 一&#xff0c;常见的信号术语 二&#xff0c;信号在内核中的表示 信号标志位 Pending表 Block表 handler表 POSIX.1标准 三&#xff0c;sigset_t 信号集操作函数 sigemptyset sigfillset sigaddset sigdelset sigismember sigprocmask sig…

ISP 代理与住宅代理 – 终极指南

模拟自然的、类似人类的流量可能很麻烦&#xff0c;但对于某些任务&#xff08;如帐户管理或网络自动化&#xff09;&#xff0c;没有它就无法完成。ISP 和住宅代理都可以提供帮助&#xff0c;但您不能盲目购买和部署它们。您需要了解它们的优势&#xff0c;理解它们的弱点&…

买的谷歌游戏账号被删了?如果是企业账号找回来的可能性很小。一个识别谷歌企业号的简单方法

这段时间有几个朋友找到我说&#xff0c;自己买的谷歌账号登录不了&#xff0c;有的是直接提示被删除&#xff0c;问能否恢复。 我了解了一下&#xff0c;发现他们的账号都是购买的企业账号。这种企业账号一旦被管理员删除了是无法恢复的。 特此记录下来&#xff0c;提醒各位…

8月13日笔记

msf补充 使用方法&#xff1a; 进入框架&#xff1a;msfconsole 使用search命令查找相关漏洞&#xff1a;search ms14-058 使用info查看模块信息&#xff1a;info 我们也可以将攻击代码写configure.rc&#xff08;只要是以 .rc 结尾的文件&#xff09;配置文件中&#xff0c;然…

【数学建模】介绍论文书写格式

&#x1f308;个人主页&#xff1a;Yui_ &#x1f308;Linux专栏&#xff1a;Linux &#x1f308;C语言笔记专栏&#xff1a;C语言笔记 &#x1f308;数据结构专栏&#xff1a;数据结构 文章目录 1.论文整体排版2. 标题书写3.摘要书写4.参考文献5.公式编辑5.1 常用公式编辑方法…

JavaScript 逆向技巧总结

本节属于知识总结&#xff0c;只是对思路的梳理&#xff0c;不对具体内容进行展开 JS 逆向可以分为三大部分&#xff1a; 寻找入口&#xff0c; 调试分析&#xff0c; 模拟执行 寻找入口&#xff1a; 这是非常关键的一步&#xff0c;逆向在大部分情况下就是找一些加密参数到底…

Linux磁盘管理与文件系统(二):实用工具和命令、fdisk分区示例

文章目录 4、查看或管理磁盘分区-fdisk格式选项示例 4、示例&#xff1a;使用 fdisk 命令创建分区需求操作步骤 5、创建文件系统-mkfs格式常用选项示例创建其他类型的文件系统 6、创建文件系统-mkswap格式常用选项示例拓展&#xff1a;关闭和启用交换分区拓展&#xff1a;swap分…

搬瓦工澳大利亚AS9929 VPS测评

搬瓦工澳大利亚vps怎么样&#xff1f;搬瓦工澳大利亚悉尼数据中心在运作CUII/AS9929线路的VPS&#xff0c;底层为KVM虚拟&#xff0c;纯SSD阵列&#xff0c;1Gbps带宽... 目前看到的是CPU主频是2.4GHz&#xff0c;接入XTOM网络&#xff0c;IP归属澳大利亚&#xff0c;当前大致I…

window好用的批量远程桌面连接工具

下载 安装 Remote Desktop Connection Manager 添加server

AlexNet模型搭建(三部曲_2)

文章目录 1模型介绍2 模型搭建3 模型训练4 模型预测 猫狗二分类&#xff0c;模型简单&#xff0c;训练精度并不高。数据集下载&#xff1a;<https://aistudio.baidu.com/datasetdetail/26884> 百度飞浆上找的大小只有60多M 1模型介绍 AlexNet是一个卷积神经网络的名字&a…

Linux命令(基础面试可用,都是自己觉得平时使用多的)

1.cat 参数&#xff1a;-n&#xff1a;显示行号-s&#xff1a;压缩连续的空行&#xff0c;只显示一个空行2.chattr 改变文件属性 语法&#xff1a;chattr [-RV] [/-/<属性>][文件或目录] 属性&#xff1a;a&#xff1a;让文件或目录仅供附加用途i&#xff1a;不得任意更…

MediaPipe人体姿态、手指关键点检测

MediaPipe人体姿态、手指关键点检测 文章目录 MediaPipe人体姿态、手指关键点检测前言一、手指关键点检测二、姿态检测三、3D物体案例检测案例 前言 Mediapipe是google的一个开源项目&#xff0c;用于构建机器学习管道。   提供了16个预训练模型的案例&#xff1a;人脸检测、…

基于级联深度学习算法的前列腺病灶检测在双参数MRI中的评估| 文献速递-基于深度学习的乳房、前列腺疾病诊断系统

Title 题目 Evaluation of a Cascaded Deep Learning–based Algorithm for Prostate Lesion Detection at Biparametric MRI 基于级联深度学习算法的前列腺病灶检测在双参数MRI中的评估 Background 背景 Multiparametric MRI (mpMRI) improves prostate cancer (PCa) de…

从西安出发,走向世界——西安国际数字影像产业园跻身全国十大产业园区行列

西安&#xff0c;作为中国历史文化名城&#xff0c;不仅以其丰富的历史遗产闻名于世&#xff0c;还逐渐成为现代科技和产业发展的新高地。产业园区成为推动经济增长和创新发展的重要引擎&#xff0c;西安国际数字影像产业园&#xff0c;正怀揣着雄心壮志&#xff0c;向着全国十…

idea git拉取代码can‘t update

idea有时候创建的新分支&#xff0c;提交以后却无法拉去代码&#xff0c;提示如下错误信息&#xff1a; cant update No tracked branch configured for branch 在idea的“Terminal”窗口中输入如下命令即可 git branch --set-upstream-to origin 找不到Terminal的可以参考下图…

ESP32S3 IDF 对 16路输入输出芯片MCP23017做了个简单的测试

这次还是使用了idf老版本4.4.7&#xff0c;上次用了5.3&#xff0c;感觉不好用&#xff0c;官方的MCP23017芯片是英文版&#xff0c;真的很难读明白&#xff0c;可能是我英语水平不够吧。先看看每个寄存器的功能&#xff1a; IODIRA 和 IODIRB: 输入/输出方向寄存器 IPOLA 和 I…

B端界面升级就是升级颜值,错了,这样想就片面啦。

在B端应用的发展中&#xff0c;界面升级是非常重要的一环。然而&#xff0c;界面级不仅仅是为了提升外观颜值&#xff0c;还需要关注用户体验、功能增强和效率提升等方面。 虽然美观的界面可以吸引用户的眼球&#xff0c;但如果功能不完善&#xff0c;用户可能会选择其他产品。…