一、前言
上一篇文章写到了,如何快速使用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 playing
gst_element_set_state(pipeline, GST_STATE_PLAYING);
// Run the QT application loop
int ret = app.exec();
// Clean up
gst_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();
// 初始化 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");
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");//glimagesink
if (!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);
// 创建 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, "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 playing
gst_element_set_state(pipeline, GST_STATE_PLAYING);
// Run the QT application loop
int ret = app.exec();
// Clean up
gst_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 playing
gst_element_set_state(pipeline, GST_STATE_PLAYING);
// Run the QT application loop
int ret = app.exec();
// Clean up
gst_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_OBJECT
public:
CGstreamPlayWidget(QWidget *parent = nullptr);
~CGstreamPlayWidget();
WId getVideoWId() const ; // 获得播放的WID
void 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 0
GstBus *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 0
GstBus *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.0
LIBS += -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.0
INCLUDEPATH += \
/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.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 +=-lgstautodetect -lgstapp-1.0
INCLUDEPATH +=
/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.0
arm的Linux:
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/aarch64-linux-gnu/gstreamer-1.0
LIBS +=-lgstautodetect -lgstapp-1.0
INCLUDEPATH +=
/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.0
RK3588:
#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 那个库,大家没有安装的话,可以删掉,可以不用加,删了不影响。