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

 一、前言

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

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

二、近期的其他发现:

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

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;
}

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 
#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界面中除了显示gstreamer的画面,还能添加控件

原理:

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

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

使用QT实现播放gstreamer的命令(二)_第2张图片

解决方案:

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

        方案其实很多:

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

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

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

                4. 。。。

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

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

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

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

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

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

使用QT实现播放gstreamer的命令(二)_第3张图片

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

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

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

使用QT实现播放gstreamer的命令(二)_第4张图片

  • 先放没有改动的时候的测试代码:
#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;
}

使用QT实现播放gstreamer的命令(二)_第5张图片

  • 改动以后的测试代码:

改动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. 成功的关键几个点,大家理解以后,方便以后自行封装:

  • 要创建(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 
#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;
}

使用测试1:简单测试

#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;
}

效果:

使用QT实现播放gstreamer的命令(二)_第6张图片

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

  • 新建一个QMainWidget 的UI类,取名为MainWindowTest
  • 编写UI: 加入一个空Widget,然后右键提升,然后指定刚刚的自定义类,再加入几个控件
  • 使用QT实现播放gstreamer的命令(二)_第7张图片
  • 在MainWindowTest 类中,加入一个函数QWidget * getPlayWidget();  获取界面UI的界面指针
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是我测试其他功能添加的,大家没装也可以删掉

你可能感兴趣的:(qt,数据库,开发语言)