一、前言
上一篇文章写到了,如何快速使用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 那个库,大家没有安装的话,可以删掉,可以不用加,删了不影响。