使用QT实现播放gstreamer的命令(二)

 一、前言

      上一篇文章写到了,如何快速使用C++来执行gstreamer的命令,如何在QT中显示gstreamer的画面,原文如下:

        https://blog.csdn.net/Alon1787/article/details/135107958

二、近期的其他发现:

1.gstreamer的画面显示在QT界面,使用的是绑定overlay

Demo1:使用QT界面显示gstreamer的命令:

#include <QApplication>
#include <QTimer>
#include <QWidget>
#include <gst/gst.h>
#include <gst/video/videooverlay.h>int main(int argc, char *argv[])
{QApplication app(argc, argv);QWidget window;WId window_handle;gst_init(&argc, &argv);// 创建管道 pipeline//GstElement *pipeline = gst_parse_launch("filesrc name=mysrc ! qtdemux name=demux demux.video_0 ! h264parse ! avdec_h264 ! videoconvert ! videoscale ! video/x-raw, width=640,height=480 ! ximagesink name=vsink", NULL);GstElement *pipeline = gst_parse_launch("videotestsrc ! ximagesink name=vsink",NULL);// 设置管道中的属性(创建管道的时候,使用第一条才有效)GstElement *mysrc = gst_bin_get_by_name (GST_BIN (pipeline), "mysrc");g_object_set (mysrc, "location", "/home/enpht/Videos/1081.mp4", NULL);g_object_unref (mysrc);// 创建界面window.show();window_handle = window.winId();// 链接到QT:GstElement *vsink = gst_bin_get_by_name (GST_BIN (pipeline), "vsink");gst_video_overlay_set_window_handle (GST_VIDEO_OVERLAY (vsink), window_handle);// Start playinggst_element_set_state(pipeline, GST_STATE_PLAYING);// Run the QT application loopint ret = app.exec();// Clean upgst_element_set_state(pipeline, GST_STATE_NULL);gst_object_unref(GST_OBJECT(pipeline));return ret;
}

2. 并不是所有的sink插件,都支持overlay:

        经过测试,很多sink插件都无法正常的使用QT界面来显示,所以常用的是ximagesink、xvimagesink、glimagesink。

        以下是我的一些其他输出插件的总结:

常用输出插件:(一般在结尾处)
  • glimagesink、基于OpenGL或者OpenGL ES

  • autovideosink 自动选择显示

Linux:
  • xvimagesink 测试用的显示图片的插件,可直接显示rgb,不支持YUV. 需要VX支持,支持Xoverlay和fps

  • ximagesink、元素使用X系统来显示视频,支持Xoverlay 。支持Xoverlay,但是overlay不支持fpsdisplay

  • alsasink 非常简单的音频输出接口

  • pulsesink 高级一点的音频输出接口,但是旧Linux上不稳定

Mac OS X:
  • osxvideosink 唯一提供的视频输出

  • osxaudiosink 唯一提供的音频输出

windows:
  • d3d11videosink 基于Direct3D11,是最好用性能最高的输出,支持overlay,但有些版本没有

  • d3dvideosink 基于Direct3D9,,支持overlay,不建议在win8以上系统

Android:
  • openslessink 唯一可用的视频输出

  • openslessrc 唯一可用的音频输出

  • androidmedia 安卓自带的解码

  • ahcsrc 安卓的摄像头采集

IOS:
  • osxaudiosink 唯一可用音频接收

  • iosassetsrc 读取IOS内容

  • iosavassetsrc 读取IOS内容

其他:
  • waylandsink 元素使用Wayland显示协议来显示视频,不建议使用

  • filesink 文件输出 如filesink location=/home/enpht/Pictures/YUV_test

  • fpsdisplaysink 可以打印帧率等信息 默认autovideosink,可以设置video-sink来设置选用哪个实际输出

  • fakevideosink 调试用

  • gtksink 自带的GUI显示

  • gtkglsink 自带的OpenGL的GUI显示

  • clutterautovideosink 使用clutter库实现播放,一般不用

  • aasink 用ascii字符显示视频画面

  • cacasink 用ascii字符显示视频画面,彩色

QT中可以使用,但是命令行用不了:
  • qmlglsink 使用qml

  • qtvideosink painting on any surface with QPainter

  • qtglvideosink painting on any surface with QPainter and OpenGL

  • qtquick2videosink 貌似没用,仅限QtQuick2

  • qwidgetvideosink painting on QWidgets

3. 使用fpsdisplaysink可以通过设置video-sink的属性来显示画面和帧率

命令行:

gst-launch-1.0 -v udpsrc port=10010 ! capsfilter caps="application/x-rtp, media=(string)video, clock-rate=(int)90000, encoding-name=(string)RAW, sampling=(string)YCbCr-4:2:0, depth=(string)8, width=(string)1920, height=(string)1080, colorimetry=(string)SMPTE240M, payload=(int)96, a-framerate=(string)30" ! queue ! rtpvrawdepay ! videoconvert ! fpsdisplaysink video-sink=xvimagesink

改为QT中的C++代码,QT显示RTP视频,同时显示帧率

#include <QApplication>
#include <QWidget>
#include <QtConcurrent/QtConcurrent>
#include <gst/gst.h>
#include <gst/video/videooverlay.h>int main(int argc, char *argv[]) {QApplication a(argc, argv);GstElement *pipeline, *udpsrc, *capsfilter, *queue, *rtpvrawdepay, *jitterbuffer, *videoconvert, *vsink, *fpssink;GstCaps *caps;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");queue = gst_element_factory_make("queue", "queue");jitterbuffer = gst_element_factory_make ("rtpjitterbuffer", "jitterbuffer");rtpvrawdepay = gst_element_factory_make("rtpvrawdepay", "rtpvrawdepay");videoconvert = gst_element_factory_make("videoconvert", "videoconvert");fpssink = gst_element_factory_make("fpsdisplaysink", "fpssink");vsink = gst_element_factory_make("xvimagesink", "vsink");//glimagesinkif (!pipeline || !udpsrc || !capsfilter || !queue || !rtpvrawdepay || !videoconvert || !fpssink || !vsink) {g_printerr("Failed to create elements. Exiting.\n");return -1;}// 设置 udpsrc 元素的参数g_object_set(udpsrc, "port", 10010, 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, "RAW","sampling", G_TYPE_STRING, "YCbCr-4:2:0","depth", G_TYPE_STRING, "8","width", G_TYPE_STRING, "1920","height", G_TYPE_STRING, "1080","colorimetry", G_TYPE_STRING, "SMPTE240M","payload", G_TYPE_INT, 96,"a-framerate", G_TYPE_STRING, "30",NULL);g_object_set(capsfilter, "caps", caps, NULL);gst_caps_unref(caps);g_object_set (jitterbuffer, "latency", 20, "do-lost", TRUE, "do-retransmission", TRUE, NULL);g_object_set (fpssink, "video-sink", vsink, NULL);// 将元素添加到管道中gst_bin_add_many(GST_BIN(pipeline), udpsrc, capsfilter, queue, jitterbuffer, rtpvrawdepay, videoconvert, fpssink, NULL);// 连接元素if (!gst_element_link_many(udpsrc, capsfilter, queue, jitterbuffer, rtpvrawdepay, videoconvert, fpssink, NULL)) {g_printerr("Failed to link elements. Exiting.\n");gst_object_unref(pipeline);return -1;}// 链接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;}//    QtConcurrent::run([=](){
//        GstBus *bus;
//        GstMessage *msg;
//        // 获取管道的总线
//        bus = gst_element_get_bus(pipeline);
//        // 等待消息
//        msg = gst_bus_timed_pop_filtered(bus, GST_CLOCK_TIME_NONE, GstMessageType(GST_MESSAGE_ERROR | GST_MESSAGE_EOS));//        // 处理消息
//        if (msg != NULL) {
//            GError *err = NULL;
//            gchar *debug_info = NULL;//            switch (GST_MESSAGE_TYPE(msg)) {
//                case GST_MESSAGE_ERROR:
//                    gst_message_parse_error(msg, &err, &debug_info);
//                    g_printerr("Error received from element %s: %s\n", GST_OBJECT_NAME(msg->src), err->message);
//                    g_printerr("Debugging information: %s\n", debug_info ? debug_info : "none");
//                    g_clear_error(&err);
//                    g_free(debug_info);
//                    break;
//                case GST_MESSAGE_EOS:
//                    g_print("End-Of-Stream reached.\n");
//                    break;
//                default:
//                    // 不处理其他消息
//                    break;
//            }
//            gst_message_unref(msg);
//        }
//        gst_object_unref(bus);
//    });auto res = a.exec();// 释放资源gst_element_set_state(pipeline, GST_STATE_NULL);gst_object_unref(pipeline);return res;
}

三、QT界面中除了显示gstreamer的画面,还能添加控件

原理:

        大家可以自行测试,比如在一个QT的UI文件中,增加了QWidget和各种其他控件,通过之前的绑定overlay的方式,都会发现,其他所有的控件都被显示控件给占满了,原因就是因为获取的是窗口ID,相当于会铺满整个窗口,包括覆盖里面的控件。

结果:会发现所有控件都没有了。

解决方案:

        经过很多次测试,忽然灵机一闪,发现,既然获得的必须是窗口ID,一定要铺满整个窗口的话,那为什么我不直接将显示窗口作为子窗口嵌入到UI界面中呢?

        方案其实很多:

                1. 获取每一帧gstreamer的画面图像,手动绘制到指定界面(太复杂)

                2. 使用setSurface 等方法,手动绘制(太复杂)

                3. 使用Qst,很多时候用不了,难安装(不建议)

                4. 。。。

                5. 我的方案一,直接将显示的窗口当做子窗口嵌入UI界面(简单)

                6. 我的方案二,封装一个QWidget类,里面还有一个播放视频的QWidget,使用子QWidget的ID来绑定显示。

1. 方案一:有UI界面文件的时候:

        我的解决方法就是:使用 QWidget::createWindowContainer 函数,将显示窗口作为子窗口,嵌入到其他有控件的窗口即可。(当然,需要自行调节显示窗口的大小和动态缩放,时间有限,本案例没有写,以后有时间补上)

        参考文章:Qt嵌入外部程序界面初探_qt findmainwindow-CSDN博客

结果:可以发现,遮挡了显示窗口后面的控件,但是在窗口外面的控件正常显示和使用。

        经过不断测试,发现显示窗口上面是无法显示控件的,但是旁边可以,于是可以想到,只要控制好显示窗口的大小和位置,就可以实现很好的显示界面和控件的配合。

以下是简单的demo,时间有限,以后再封装:

  • 先自定义UI,这里为了测试,我设计了4个按钮放在四周:

  • 先放没有改动的时候的测试代码:
#include <QApplication>
#include <QTimer>
#include <QWidget>
#include <QWindow>
#include <gst/gst.h>
#include <gst/video/videooverlay.h>int main(int argc, char *argv[])
{QApplication app(argc, argv);QWidget window;WId window_handle;gst_init(&argc, &argv);// 创建管道 pipeline// GstElement *pipeline = gst_parse_launch("filesrc name=mysrc ! qtdemux name=demux demux.video_0 ! h264parse ! avdec_h264 ! videoconvert ! videoscale ! video/x-raw, width=640,height=480 ! ximagesink name=vsink", NULL);// 设置管道中的属性(创建管道的时候,使用第一条才有效)// GstElement *mysrc = gst_bin_get_by_name (GST_BIN (pipeline), "mysrc");// g_object_set (mysrc, "location", "/home/enpht/Videos/1081.mp4", NULL);// g_object_unref (mysrc);// 测试视频:GstElement *pipeline = gst_parse_launch("videotestsrc ! ximagesink name=vsink",NULL);// 创建界面window.show();window_handle = window.winId();// 链接到QT:GstElement *vsink = gst_bin_get_by_name (GST_BIN (pipeline), "vsink");gst_video_overlay_set_window_handle (GST_VIDEO_OVERLAY (vsink), window_handle);// Start playinggst_element_set_state(pipeline, GST_STATE_PLAYING);// Run the QT application loopint ret = app.exec();// Clean upgst_element_set_state(pipeline, GST_STATE_NULL);gst_object_unref(GST_OBJECT(pipeline));return ret;
}

  • 改动以后的测试代码:

改动1:先创建一个CGstreamPlayWidget UI类,自动生成头文件、cpp文件和UI文件

改动2:导入头文件,然后显示出来

改动3:将之前的Widget设置为不要显示

改动4:创建一个可以嵌入窗口的容器,然后把这个容器加入到UI界面中显示出来。

#include <QApplication>
#include <QTimer>
#include <QWidget>
#include <QWindow>
#include <gst/gst.h>
#include <gst/video/videooverlay.h>#include "cGstreamPlayWidget.h"int main(int argc, char *argv[])
{QApplication app(argc, argv);QWidget window;WId window_handle;// 此处改动:增加UI界面的显示CGstreamPlayWidget gstPlayWidget;gstPlayWidget.show();gst_init(&argc, &argv);// 创建管道 pipeline// GstElement *pipeline = gst_parse_launch("filesrc name=mysrc ! qtdemux name=demux demux.video_0 ! h264parse ! avdec_h264 ! videoconvert ! videoscale ! video/x-raw, width=640,height=480 ! ximagesink name=vsink", NULL);// 设置管道中的属性(创建管道的时候,使用第一条才有效)// GstElement *mysrc = gst_bin_get_by_name (GST_BIN (pipeline), "mysrc");// g_object_set (mysrc, "location", "/home/enpht/Videos/1081.mp4", NULL);// g_object_unref (mysrc);// 测试视频:GstElement *pipeline = gst_parse_launch("videotestsrc ! ximagesink name=vsink",NULL);// 创建界面// 此处改动:先不要显示// window.show();window_handle = window.winId();// 链接到QT:GstElement *vsink = gst_bin_get_by_name (GST_BIN (pipeline), "vsink");gst_video_overlay_set_window_handle (GST_VIDEO_OVERLAY (vsink), window_handle);// 此处改动:获取到窗口的ID,然后创建独立的窗口容器,嵌入到UI界面中**************QWindow* m_window;QWidget *m_widget;m_window= QWindow::fromWinId((WId)(window_handle));m_widget = QWidget::createWindowContainer(m_window, &gstPlayWidget);m_widget->resize(640,480);m_widget->show();//**********************************************************************// Start playinggst_element_set_state(pipeline, GST_STATE_PLAYING);// Run the QT application loopint ret = app.exec();// Clean upgst_element_set_state(pipeline, GST_STATE_NULL);gst_object_unref(GST_OBJECT(pipeline));return ret;
}
  • 实现效果:发现只覆盖了一个按钮:

  • 这里大家可以自行测试,不影响这三个按钮的点击事件。

总结:

1.  关键是以下这段话:

    // 此处改动:获取到窗口的ID,然后创建独立的窗口容器,嵌入到UI界面中**************QWindow* m_window;QWidget *m_widget;m_window= QWindow::fromWinId((WId)(window_handle));m_widget = QWidget::createWindowContainer(m_window, &gstPlayWidget);m_widget->resize(640,480);m_widget->show();//**********************************************************************

        通过QWindow::fromWinId 获取到创建好的创建的窗口ID,然后使用QWidget::createWindowContainer 函数,嵌入到UI界面中,这个函数有三个参数,第一个参数表示刚刚获取的窗口ID(必须经过fromWinId 转换为QWindow才行),第二个参数表示想嵌入到的界面指针,第三个参数这里没有填,自行百度,建议不要填。

2. 成功的关键几个点,大家理解以后,方便以后自行封装:

  • 要创建(new也行)一个单独的Widget,就像我这里的QWidget window; ,不要以为没他可以,其实他的作用是将gstreamer绑定到自己身上

3. 大家自行实验几次以后,就可以发现一些更加高级的用法,比如我自己封装了一个简单的界面用于显示gstreamer的管道画面,通过函数来指定嵌入到哪里去。

2.方案二:没有UI界面文件的时候:

(其实也能使用方案一,然后自己手动加入各种控件,感觉我感觉有点繁琐,后来发现其他方案)

  后来发现,干脆直接封装一个自定义的继承自QWidget的类,这个类中,还有一个QWidget,使用内部的QWidget来绑定即可,就无需那么繁琐。

  封装的额外功能:

  • 提前放置了3个布局,1个垂直布局,2个水平布局,以及3个函数来往布局里面加控件
  • 提前留出了信号,方便以后使用QT关联
  • 预留出gstremer的信号回调函数

自定义封装的类头文件:cGstreamPlayWidget.h

#ifndef CGSTREAMPLAYWIDGET_H
#define CGSTREAMPLAYWIDGET_H#include <QMainWindow>
#include <QPushButton>
#include <QHBoxLayout>
#include <QVBoxLayout>
#include <QSlider>
#include <QTimer>
#include <gst/gst.h>class CGstreamPlayWidget : public QWidget
{Q_OBJECTpublic:CGstreamPlayWidget(QWidget *parent = nullptr);~CGstreamPlayWidget();WId  getVideoWId() const ;               // 获得播放的WIDvoid setGstPipline(GstElement* pipline); // 设置管道:将外部管道指针传递进来void addWidget_h1Layout(QWidget *w);     // 增加界面到横向布局1里面void addWidget_h2Layout(QWidget *w);     // 增加界面到横向布局2里面void addWidget_vLayout(QWidget *w);      // 增加界面到纵向布局里面// 处理bus信息: 外部使用案例GstBus *bus = gst_element_get_bus(pipeline); gst_bus_add_watch(bus, &CGstreamPlayWidget::postGstMessage, window); window是创建的此类对象static gboolean postGstMessage(GstBus * bus, GstMessage * message, gpointer user_data);private:GstElement *pipeline = nullptr;         // 管道QWidget *videoWindow = nullptr;         // 播放窗口QHBoxLayout *h1Layout = nullptr;         // 横向布局QHBoxLayout *h2Layout = nullptr;         // 横向布局QVBoxLayout *vLayout = nullptr;         // 纵向布局signals:void sigAlbum(const QString &album);      // 发送视频专辑名字信息void sigState(GstState st);               // 发送播放状态信息void sigEos();                            // 发送文件结束信号};#endif // CGSTREAMPLAYWIDGET_H

自定义类的cpp文件:

#include "cGstreamPlayWidget.h"CGstreamPlayWidget::CGstreamPlayWidget(QWidget *parent): QWidget(parent)
{videoWindow = new QWidget();h1Layout = new QHBoxLayout();h2Layout = new QHBoxLayout();vLayout = new QVBoxLayout();vLayout->addLayout(h1Layout);vLayout->addWidget(videoWindow);vLayout->addLayout(h2Layout);this->setLayout(vLayout);
}CGstreamPlayWidget::~CGstreamPlayWidget()
{if(pipeline){/* 停止管道 */gst_element_set_state(pipeline, GST_STATE_NULL);/* 释放资源 */gst_object_unref(pipeline);}}WId CGstreamPlayWidget::getVideoWId() const
{return videoWindow->winId();
}void CGstreamPlayWidget::addWidget_h1Layout(QWidget *w)
{h1Layout->addWidget(w);
}void CGstreamPlayWidget::addWidget_h2Layout(QWidget *w)
{h2Layout->addWidget(w);
}void CGstreamPlayWidget::addWidget_vLayout(QWidget *w)
{vLayout->addWidget(w);
}gboolean CGstreamPlayWidget::postGstMessage(GstBus *bus, GstMessage *message, gpointer user_data)
{CGstreamPlayWidget *pw = NULL;if (user_data) {pw = reinterpret_cast<CGstreamPlayWidget*>(user_data);}switch (GST_MESSAGE_TYPE(message)) {case GST_MESSAGE_STATE_CHANGED: {GstState old_state, new_state, pending_state;gst_message_parse_state_changed (message, &old_state, &new_state, &pending_state);pw->sigState(new_state);break;}case GST_MESSAGE_TAG: {GstTagList *tags = NULL;gst_message_parse_tag(message, &tags);gchar *album= NULL;if (gst_tag_list_get_string(tags, GST_TAG_ALBUM, &album)) {pw->sigAlbum(album);g_free(album);}gst_tag_list_unref(tags);break;}case GST_MESSAGE_EOS: {pw->sigEos();break;}default:break;}return TRUE;
}

使用测试1:简单测试

#include <QApplication>
#include <QTimer>
#include <QWidget>
#include <QWindow>
#include <gst/gst.h>
#include <gst/video/videooverlay.h>#include "cGstreamPlayWidget.h"int main(int argc, char *argv[])
{QApplication app(argc, argv);WId window_handle;// 创建自定义界面CGstreamPlayWidget gstPlayWidget;gstPlayWidget.resize(800,600);gstPlayWidget.show();gst_init(&argc, &argv);window_handle = gstPlayWidget.getVideoWId();// 测试增加控件
#if 1// 增加控件:#include <QPushButton>QPushButton pbt1("test1");QPushButton pbt2("test2");QPushButton pbt3("test3");gstPlayWidget.addWidget_h1Layout(&pbt1);gstPlayWidget.addWidget_h1Layout(&pbt3);gstPlayWidget.addWidget_vLayout(&pbt2);#endif// 测试创建管道
#if 1// 创建管道方法1:// 测试视频:GstElement *pipeline = gst_parse_launch("videotestsrc ! glimagesink name=vsink",NULL);  //ximagesink  xvimagesink   glimagesink// 链接到QT:GstElement *vsink = gst_bin_get_by_name (GST_BIN (pipeline), "vsink");gst_video_overlay_set_window_handle (GST_VIDEO_OVERLAY (vsink), window_handle);#elif// 创建管道方法2:// 测试视频:播放本地文件GstElement *pipeline = gst_parse_launch ("playbin uri=file:home/enpht/Videos/1080.mp4", NULL);// 链接到QT:GstElement *vsink = gst_element_factory_make ("ximagesink", "vsink");gst_video_overlay_set_window_handle (GST_VIDEO_OVERLAY (vsink), window_handle);g_object_set(GST_OBJECT(pipeline), "video-sink", vsink, NULL);// 创建管道方法3:// 创建管道 pipeline// GstElement *pipeline = gst_parse_launch("filesrc name=mysrc ! qtdemux name=demux demux.video_0 ! h264parse ! avdec_h264 ! videoconvert ! videoscale ! video/x-raw, width=640,height=480 ! ximagesink name=vsink", NULL);// 设置管道中的属性(创建管道的时候,使用第一条才有效)// GstElement *mysrc = gst_bin_get_by_name (GST_BIN (pipeline), "mysrc");// g_object_set (mysrc, "location", "/home/enpht/Videos/1081.mp4", NULL);// g_object_unref (mysrc);// 链接到QT:// GstElement *vsink = gst_element_factory_make ("ximagesink", "vsink");// gst_video_overlay_set_window_handle (GST_VIDEO_OVERLAY (vsink), window_handle);// g_object_set(GST_OBJECT(pipeline), "video-sink", vsink, NULL);#endif// 测试呼出事件:
#if 0GstBus *bus = gst_element_get_bus(pipeline);gst_bus_add_watch(bus, &CGstreamPlayWidget::postGstMessage, &gstPlayWidget);gst_object_unref(bus);#endif// 开始播放gst_element_set_state(pipeline, GST_STATE_PLAYING);// QT的事件循环int ret = app.exec();// 释放内存gst_element_set_state(pipeline, GST_STATE_NULL);gst_object_unref(GST_OBJECT(pipeline));return ret;
}

效果:

使用测试2:使用提升的方式

  • 新建一个QMainWidget 的UI类,取名为MainWindowTest
  • 编写UI: 加入一个空Widget,然后右键提升,然后指定刚刚的自定义类,再加入几个控件
  • 在MainWindowTest 类中,加入一个函数QWidget * getPlayWidget();  获取界面UI的界面指针
QWidget *MainWindowTest::getPlayWidget()
{return ui->widget;
}
  • 重写编写测试代码:

        改动的地方:

        1.导入新头文件,使用那个函数获取指针:

        CGstreamPlayWidget *gstPlayWidget = (CGstreamPlayWidget*)(w.getPlayWidget());

        2.将gstPlayWidget的show去掉,将gstPlayWidget. 改为 gstPlayWidget->

#include <QApplication>
#include <QTimer>
#include <QWidget>
#include <QWindow>
#include <gst/gst.h>
#include <gst/video/videooverlay.h>#include "cGstreamPlayWidget.h"
#include "mainWindowTest.h"int main(int argc, char *argv[])
{QApplication app(argc, argv);WId window_handle;MainWindowTest w;w.show();// 创建自定义界面CGstreamPlayWidget *gstPlayWidget = (CGstreamPlayWidget*)(w.getPlayWidget());gstPlayWidget->resize(800,600);gstPlayWidget->show();gst_init(&argc, &argv);window_handle = gstPlayWidget->getVideoWId();// 测试增加控件
#if 1// 增加控件:#include <QPushButton>QPushButton pbt1("test1");QPushButton pbt2("test2");QPushButton pbt3("test3");gstPlayWidget->addWidget_h1Layout(&pbt1);gstPlayWidget->addWidget_h1Layout(&pbt3);gstPlayWidget->addWidget_vLayout(&pbt2);#endif// 测试创建管道
#if 1// 创建管道方法1:// 测试视频:GstElement *pipeline = gst_parse_launch("videotestsrc ! glimagesink name=vsink",NULL);  //ximagesink  xvimagesink   glimagesink// 链接到QT:GstElement *vsink = gst_bin_get_by_name (GST_BIN (pipeline), "vsink");gst_video_overlay_set_window_handle (GST_VIDEO_OVERLAY (vsink), window_handle);#elif// 创建管道方法2:// 测试视频:播放本地文件GstElement *pipeline = gst_parse_launch ("playbin uri=file:home/enpht/Videos/1080.mp4", NULL);// 链接到QT:GstElement *vsink = gst_element_factory_make ("ximagesink", "vsink");gst_video_overlay_set_window_handle (GST_VIDEO_OVERLAY (vsink), window_handle);g_object_set(GST_OBJECT(pipeline), "video-sink", vsink, NULL);// 创建管道方法3:// 创建管道 pipeline// GstElement *pipeline = gst_parse_launch("filesrc name=mysrc ! qtdemux name=demux demux.video_0 ! h264parse ! avdec_h264 ! videoconvert ! videoscale ! video/x-raw, width=640,height=480 ! ximagesink name=vsink", NULL);// 设置管道中的属性(创建管道的时候,使用第一条才有效)// GstElement *mysrc = gst_bin_get_by_name (GST_BIN (pipeline), "mysrc");// g_object_set (mysrc, "location", "/home/enpht/Videos/1081.mp4", NULL);// g_object_unref (mysrc);// 链接到QT:// GstElement *vsink = gst_element_factory_make ("ximagesink", "vsink");// gst_video_overlay_set_window_handle (GST_VIDEO_OVERLAY (vsink), window_handle);// g_object_set(GST_OBJECT(pipeline), "video-sink", vsink, NULL);#endif// 测试呼出事件:
#if 0GstBus *bus = gst_element_get_bus(pipeline);gst_bus_add_watch(bus, &CGstreamPlayWidget::postGstMessage, &gstPlayWidget);gst_object_unref(bus);#endif// 开始播放gst_element_set_state(pipeline, GST_STATE_PLAYING);// QT的事件循环int ret = app.exec();// 释放内存gst_element_set_state(pipeline, GST_STATE_NULL);gst_object_unref(GST_OBJECT(pipeline));return ret;
}

效果:会神奇的发现,控件竟然可以在画面上显示了!!!

方案对比:

        毫无疑问,这两种方案,都比自己写绘制的代码要方便的多,但是这两种方案各有优劣,当然优点肯定是第二个更多一些。各自有各自的适应环境,比如,如果已经确定了UI界面,那么干脆就使用第一种,将整个播放界面嵌入进更大的界面去。其他情况,都更加适合第二种,因为可以使用提升的方式,很好的去布局,内部有加入控件的接口,也方便接入,同时我也预留了接口。

最后:因为写这篇文章也耗费了一些精力,希望可以点赞收藏

项目源码:

(QT播放gstreamer管道命令的示例资源-CSDN文库)

源码使用注意事项:

1. 请百度,或者看我前面的文章,自行安装gstreamer和QT环境

2. 各个虚拟机和板子的环境配置不一样,需要自己在pro文件中微调:

    例如下面的/usr/lib/x86_64-linux-gnu/glib-2.0/include   这个需要调

CONFIG += link_pkgconfig
PKGCONFIG += gstreamer-1.0 gstreamer-plugins-base-1.0 gtk+-3.0LIBS += -lX11
LIBS    +=-lglib-2.0
LIBS    +=-lgobject-2.0
LIBS    +=-lgstreamer-1.0          # <gst/gst.h>
LIBS    +=-lgstvideo-1.0             # <gst/video/videooverlay.h>
LIBS    +=-L/usr/lib/x86_64-linux-gnu/gstreamer-1.0
LIBS    +=-lgstrtspserver-1.0
LIBS    +=-lgstautodetect
LIBS    +=-lgstaudio-1.0
LIBS    +=-lgstapp-1.0INCLUDEPATH += \/usr/include/glib-2.0 \/usr/lib/x86_64-linux-gnu/glib-2.0/include \/usr/include/gstreamer-1.0 \/usr/lib/x86_64-linux-gnu/gstreamer-1.0/include/

    以下是我自己用的环境配置:

虚拟机Linux:**LIBS    +=-lglib-2.0LIBS    +=-lgobject-2.0LIBS    +=-lgstreamer-1.0          # <gst/gst.h>LIBS    +=-lgstvideo-1.0             # <gst/video/videooverlay.h>LIBS    +=-L/usr/lib/x86_64-linux-gnu/gstreamer-1.0LIBS    +=-lgstautodetect -lgstapp-1.0INCLUDEPATH += /usr/include/glib-2.0 /usr/lib/x86_64-linux-gnu/glib-2.0/include /usr/include/gstreamer-1.0 /usr/lib/x86_64-linux-gnu/gstreamer-1.0/include/也可以使用:
CONFIG += link_pkgconfig
PKGCONFIG += gstreamer-1.0 gstreamer-plugins-base-1.0arm的Linux:LIBS    +=-lglib-2.0LIBS    +=-lgobject-2.0LIBS    +=-lgstreamer-1.0          # <gst/gst.h>LIBS    +=-lgstvideo-1.0             # <gst/video/videooverlay.h>LIBS    +=-L/usr/lib/aarch64-linux-gnu/gstreamer-1.0LIBS    +=-lgstautodetect -lgstapp-1.0INCLUDEPATH += /usr/include/glib-2.0 /usr/lib/aarch64-linux-gnu/glib-2.0/include /usr/include/gstreamer-1.0 /usr/lib/aarch64-linux-gnu/gstreamer-1.0/include/window:
INCLUDEPATH += $$PWD/gstreamer/include/gstreamer-1.0/gst
INCLUDEPATH += $$PWD/gstreamer/include
INCLUDEPATH += $$PWD/gstreamer/include/gstreamer-1.0
INCLUDEPATH += $$PWD/gstreamer/include/glib-2.0
INCLUDEPATH += $$PWD/gstreamer/lib/glib-2.0/include
LIBS += -L$$PWD/gstreamer/lib/ -lgstreamer-1.0 -lgstvideo-1.0 -lgobject-2.0 -lglib-2.0RK3588:
#INCLUDEPATH += $$PWD/gstreamer/include/gstreamer-1.0/gst
INCLUDEPATH += /opt/enpht/rk3588/sysroot/usr/include/gstreamer-1.0/gst#INCLUDEPATH += $$PWD/gstreamer/include
INCLUDEPATH += /opt/enpht/rk3588/sysroot/usr/include#INCLUDEPATH += $$PWD/gstreamer/include/gstreamer-1.0
INCLUDEPATH += /opt/enpht/rk3588/sysroot/usr/include/gstreamer-1.0#INCLUDEPATH += $$PWD/gstreamer/include/glib-2.0
INCLUDEPATH += /opt/enpht/rk3588/sysroot/usr/include/glib-2.0#INCLUDEPATH += $$PWD/gstreamer/lib/glib-2.0/include
INCLUDEPATH += /opt/enpht/rk3588/sysroot/usr/lib/aarch64-linux-gnu/glib-2.0/include#LIBS += -L$$PWD/gstreamer/lib/ -lgstreamer-1.0 -lgstvideo-1.0 -lgobject-2.0 -lglib-2.0
LIBS += -L/opt/enpht/rk3588/sysroot/usr/lib/aarch64-linux-gnu/ -lgstreamer-1.0 -lgstvideo-1.0 -lgobject-2.0 -lglib-2.0 -lgstbase-1.0
#LIBS += -L/usr/lib/x86_64-linux-gnu/ -lgstreamer-1.0 -lgstvideo-1.0 -lgobject-2.0 -lglib-2.0#QT_CONFIG -= no-pkg-config
#CONFIG += link_pkgconfig debug
#PKGCONFIG = \
#    gstreamer-1.0 \
#    gstreamer-video-1.0

3. 最后的opencv 那个库,大家没有安装的话,可以删掉,可以不用加,删了不影响。

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

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

相关文章

蓝桥杯AT24C02问题记录

问题1&#xff1a;从这个图片上可以看出这两个在IIC的.c文件里延时时间不一样&#xff0c;第一张图使用了15个_nop_(); 12M晶振机器周期是 1/12M*121uS&#xff1b;nop()要延时1个指令周期。延时时间不对会对时序产生影响&#xff0c;时序不对&#xff0c;则AT24C02有没被使用…

关于Spring Boot和MyBatis常见的十道面试题

拦截器和过滤器有什么区别&#xff1f; 拦截器&#xff08;Interceptor&#xff09;和过滤器&#xff08;Filter&#xff09;都是用于在请求道道目标资源的之前或之后进行处理的组件。主要区别有以下几点&#xff1a; 依赖对象不同&#xff1a;过滤器是来时Servlet&#xff0…

IDEA安装MyBatisX插件

IDEA工具在开发人员中经常使用&#xff0c;从dao层到xml文件对应的查看很费劲&#xff0c;这时候就有相应的插件工具出现了MyBatisX。他的好处如下&#xff1a; mapper and xml can jump back and forth mybatis.xml,mapper.xml prompt mapper and xml support auto prompt lik…

Netty核心——Reactor下篇(十)

任务队列中的Task有3种典型使用场景 用户程序自定义的普通任务 比如有一个非常耗时长的业务 异步执行提交该Channel对应的NioEventLoop的TaskQueue中 用户自定义定时任务 该任务提交到scheduleTaskQueue中 非当前Reactor线程调用Channel的各种方法 例如在推送系统的业务线程…

大数据StarRocks(八):资源隔离实战

前言 自 2.2 版本起&#xff0c;StarRocks 支持资源组管理&#xff0c;集群可以通过设置资源组&#xff08;Resource Group&#xff09;的方式限制查询对资源的消耗&#xff0c;实现多租户之间的资源隔离与合理利用。在 2.3 版本中&#xff0c;StarRocks 支持限制大查询&#…

酒鬼酒2024年展望:稳发展动能,迈入恢复性增长轨道

文 | 琥珀酒研社 作者 | 渡过 最近几个月来&#xff0c;白酒估值回落到近十年来低位&#xff0c;反映出了整个白酒行业的市场低迷和虚弱现状。不管是头部企业五粮液、泸州老窖&#xff0c;还是区域酒企口子窖、金种子酒等&#xff0c;最近都通过“回购”或“增持”&#xff0…

常用直线检测算法

概述 在计算机视觉领域&#xff0c;我们经常需要做一些特殊的任务&#xff0c;而这些任务中经常会用到直线检测算法&#xff0c;比如车道线检测、长度测量等。– 资料 直线检测算法汇总_技术挖掘者的博客-CSDN博客_直线检测算法 直线检测算法博文中缺失的几个源码(Hough_lin…

联想乐商店更新安卓APK错误处理

当你点击“重新提交”&#xff0c;联想开放平台会卡死 其实他们的网页是有BUG的。HTTP GET appDetail请求会有个服务器内部错误 联系了联想客服&#xff0c;他们的绕过去方案是&#xff0c;你要选择“已上架” 然后再更新版本就可以了

【Python机器学习系列】建立XGBoost模型预测心脏疾病(完整实现过程)

一、引言 前文回顾&#xff1a; 一文彻底搞懂机器学习中的归一化与反归一化问题 【Python机器学习系列】一文彻底搞懂机器学习中表格数据的输入形式&#xff08;理论源码&#xff09; 【Python机器学习系列】一文带你了解机器学习中的Pipeline管道机制&#xff08;理论源码…

【Python】03快速上手爬虫案例三:搞定药师帮

文章目录 前言1、破解验证码2、获取数据 前言 提示&#xff1a;通过用户名、密码、搞定验证码&#xff0c;登录进药师帮网站&#xff0c;然后抓取想要的数据。 爬取数据&#xff0c;最终效果图&#xff1a; 1、破解验证码 使用药师帮测试系统&#xff1a;https://dianrc.ysb…

2024 高级前端面试题之 HTML 「精选篇」

该内容主要整理关于 HTML 的相关面试题&#xff0c;其他内容面试题请移步至 「最新最全的前端面试题集锦」 查看。 HTML模块精选篇 1. 如何理解HTML语义化2. H5的新特性有哪些3. 说一下 HTML5 Drag API4. iframe有那些缺点5. 如何实现浏览器内多个标签页之间的通信6. 简述一下s…

Elasticsearch:如何为 Elastic Stack 配置 AI Assistant

了解并安装 Elastic AI Assistant Elastic 推出了 Observability AI Assistant&#xff0c;这是一款利用生成式 AI 来增强你的 Observability 体验的强大工具。 该 AI 助手由 OpenAI 或 Azure OpenAI 服务的连接器提供支持&#xff0c;可带来上下文洞察和聊天功能&#xff0c;…

104.乐理基础-五线谱-中音谱号、次中音谱号

内容参考于&#xff1a;三分钟音乐社 上一个内容&#xff1a;103.乐理基础-五线谱-低音谱号-CSDN博客 上一个内容练习答案&#xff1a; 常用的谱号就是下图所示的四个&#xff0c;其中高音谱号与低音谱号已经在上一个内容和上上一个内容中写过了&#xff0c;音乐中百分之九十…

【C++历练之路】探秘C++三大利器之一——多态

W...Y的主页 &#x1f60a; 代码仓库分享&#x1f495; 前言&#x1f354;: 在计算机科学的广袤领域中&#xff0c;C多态性是一门令人着迷的技术艺术&#xff0c;它赋予我们的代码更强大的灵活性和可维护性。想象一下&#xff0c;你正在构建一个程序&#xff0c;需要适应不断…

python3.8 安装缺少ssl、_ctypes模块解决办法

问题 安装pyhton3.8安装默认不依赖ssl 运行Flask项目时报错&#xff1a; Traceback (most recent call last):File "/usr/local/python3/bin/flask", line 8, in <module>sys.exit(main())File "/usr/local/python3/lib/python3.8/site-packages/flask…

电脑文件丢失怎么恢复数据?数据恢复,3个方法!

“我有一份很重要的资料文件保存在电脑上了&#xff0c;但是刚刚发现这些文件莫名其妙丢失了。电脑文件丢失应该怎么恢复数据呀&#xff1f;大家有什么比较好用的方法可以推荐吗&#xff1f;” 在日常生活中&#xff0c;我们经常都需要使用电脑&#xff0c;当然&#xff0c;也会…

虹科干货 | 如何使用nProbe Cento构建100 Gbit NetFlow 传感器

本文是一份全面的指南&#xff0c;解释了如何使用nProbe Cento构建一个高效的100 Gbit NetFlow传感器。旨在帮助大家充分利用NetFlow技术&#xff0c;以监控和分析高速网络流量。 当需要监控分布式网络&#xff0c;了解流经上行链路或关键网段的网络流量时&#xff0c;NetFlow…

Linux系列之查看cpu、内存、磁盘使用情况

查看磁盘空间 df命令用于显示磁盘分区上的可使用的磁盘空间。默认显示单位为KB。可以利用该命令来获取硬盘被占用了多少空间&#xff0c;目前还剩下多少空间等信息。使用df -h命令&#xff0c;加个-h参数是为了显示GB MB KB单位&#xff0c;这样更容易查看 Filesystem …

如何使用手机安装JuiceSSH远程连接本地Linux服务器

⛳️ 推荐 前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。点击跳转到网站。 文章目录 ⛳️ 推荐1. Linux安装cpolar2. 创建公网SSH连接地址3. JuiceSSH公网远程连接4. 固定连接SSH公网地址5. SSH固定地址…

毕设(一)——概述

文章目录 一、前言二、设计构思2.1 软件构思2.2 硬件构思 三、计划安排四、计划 一、前言 emmmm,我查了查我做的是&#xff0c;井盖侧翻&#xff0c;嗯&#xff0c;外加一些其他传感器&#xff0c;GPS等等吧。用的话&#xff0c;我这边依旧是准备是pico作为主控 二、设计构思…