上一篇文章写到了,如何快速使用C++来执行gstreamer的命令,如何在QT中显示gstreamer的画面,原文如下:
https://blog.csdn.net/Alon1787/article/details/135107958
Demo1:使用QT界面显示gstreamer的命令:
#include
#include
#include
#include
#include
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;
}
经过测试,很多sink插件都无法正常的使用QT界面来显示,所以常用的是ximagesink、xvimagesink、glimagesink。
以下是我的一些其他输出插件的总结:
glimagesink
、基于OpenGL或者OpenGL ES
autovideosink
自动选择显示
xvimagesink
测试用的显示图片的插件,可直接显示rgb,不支持YUV. 需要VX支持,支持Xoverlay和fps
ximagesink
、元素使用X系统来显示视频,支持Xoverlay 。支持Xoverlay,但是overlay不支持fpsdisplay
alsasink
非常简单的音频输出接口
pulsesink
高级一点的音频输出接口,但是旧Linux上不稳定
osxvideosink
唯一提供的视频输出
osxaudiosink
唯一提供的音频输出
d3d11videosink
基于Direct3D11,是最好用性能最高的输出,支持overlay,但有些版本没有
d3dvideosink
基于Direct3D9,,支持overlay,不建议在win8以上系统
openslessink
唯一可用的视频输出
openslessrc
唯一可用的音频输出
androidmedia
安卓自带的解码
ahcsrc
安卓的摄像头采集
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字符显示视频画面,彩色
qmlglsink
使用qml
qtvideosink
painting on any surface with QPainter
qtglvideosink
painting on any surface with QPainter and OpenGL
qtquick2videosink
貌似没用,仅限QtQuick2
qwidgetvideosink
painting on QWidgets
命令行:
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
#include
#include
#include
#include
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的UI文件中,增加了QWidget和各种其他控件,通过之前的绑定overlay的方式,都会发现,其他所有的控件都被显示控件给占满了,原因就是因为获取的是窗口ID,相当于会铺满整个窗口,包括覆盖里面的控件。
结果:会发现所有控件都没有了。
解决方案:
经过很多次测试,忽然灵机一闪,发现,既然获得的必须是窗口ID,一定要铺满整个窗口的话,那为什么我不直接将显示窗口作为子窗口嵌入到UI界面中呢?
方案其实很多:
1. 获取每一帧gstreamer的画面图像,手动绘制到指定界面(太复杂)
2. 使用setSurface 等方法,手动绘制(太复杂)
3. 使用Qst,很多时候用不了,难安装(不建议)
4. 。。。
5. 我的方案一,直接将显示的窗口当做子窗口嵌入UI界面(简单)
6. 我的方案二,封装一个QWidget类,里面还有一个播放视频的QWidget,使用子QWidget的ID来绑定显示。
我的解决方法就是:使用 QWidget::createWindowContainer 函数,将显示窗口作为子窗口,嵌入到其他有控件的窗口即可。(当然,需要自行调节显示窗口的大小和动态缩放,时间有限,本案例没有写,以后有时间补上)
参考文章:Qt嵌入外部程序界面初探_qt findmainwindow-CSDN博客
结果:可以发现,遮挡了显示窗口后面的控件,但是在窗口外面的控件正常显示和使用。
经过不断测试,发现显示窗口上面是无法显示控件的,但是旁边可以,于是可以想到,只要控制好显示窗口的大小和位置,就可以实现很好的显示界面和控件的配合。
以下是简单的demo,时间有限,以后再封装:
#include
#include
#include
#include
#include
#include
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
#include
#include
#include
#include
#include
#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. 成功的关键几个点,大家理解以后,方便以后自行封装:
3. 大家自行实验几次以后,就可以发现一些更加高级的用法,比如我自己封装了一个简单的界面用于显示gstreamer的管道画面,通过函数来指定嵌入到哪里去。
(其实也能使用方案一,然后自己手动加入各种控件,感觉我感觉有点繁琐,后来发现其他方案)
后来发现,干脆直接封装一个自定义的继承自QWidget的类,这个类中,还有一个QWidget,使用内部的QWidget来绑定即可,就无需那么繁琐。
封装的额外功能:
自定义封装的类头文件:cGstreamPlayWidget.h
#ifndef CGSTREAMPLAYWIDGET_H
#define CGSTREAMPLAYWIDGET_H
#include
#include
#include
#include
#include
#include
#include
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(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;
}
#include
#include
#include
#include
#include
#include
#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 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;
}
效果:
QWidget *MainWindowTest::getPlayWidget()
{
return ui->widget;
}
改动的地方:
1.导入新头文件,使用那个函数获取指针:
CGstreamPlayWidget *gstPlayWidget = (CGstreamPlayWidget*)(w.getPlayWidget());
2.将gstPlayWidget的show去掉,将gstPlayWidget. 改为 gstPlayWidget->
#include
#include
#include
#include
#include
#include
#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 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 #
LIBS +=-lgstvideo-1.0 #
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 #
LIBS +=-lgstvideo-1.0 #
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 #
LIBS +=-lgstvideo-1.0 #
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 那个库,大家没有安装的话,可以删掉,可以不用加,删了不影响。
4. pro文件中,gtk+-3.0 lgstautodetect是我测试其他功能添加的,大家没装也可以删掉