diff --git a/subprojects/gst-plugins-bad/tests/examples/meson.build b/subprojects/gst-plugins-bad/tests/examples/meson.build index 7a2057e7b8c50965fc708268893b80266696f1d3..1c725300eefc5ad16aab3f61e414031cb9c255d7 100644 --- a/subprojects/gst-plugins-bad/tests/examples/meson.build +++ b/subprojects/gst-plugins-bad/tests/examples/meson.build @@ -12,7 +12,7 @@ subdir('nvcodec') subdir('opencv', if_found: opencv_dep) subdir('uvch264') subdir('va') -subdir('waylandsink') +subdir('waylandsink/gtk') subdir('webrtc') subdir('wpe') diff --git a/subprojects/gst-plugins-bad/tests/examples/waylandsink/main.c b/subprojects/gst-plugins-bad/tests/examples/waylandsink/gtk/main.c similarity index 100% rename from subprojects/gst-plugins-bad/tests/examples/waylandsink/main.c rename to subprojects/gst-plugins-bad/tests/examples/waylandsink/gtk/main.c diff --git a/subprojects/gst-plugins-bad/tests/examples/waylandsink/meson.build b/subprojects/gst-plugins-bad/tests/examples/waylandsink/gtk/meson.build similarity index 100% rename from subprojects/gst-plugins-bad/tests/examples/waylandsink/meson.build rename to subprojects/gst-plugins-bad/tests/examples/waylandsink/gtk/meson.build diff --git a/subprojects/gst-plugins-bad/tests/examples/waylandsink/wayland-threads.c b/subprojects/gst-plugins-bad/tests/examples/waylandsink/gtk/wayland-threads.c similarity index 100% rename from subprojects/gst-plugins-bad/tests/examples/waylandsink/wayland-threads.c rename to subprojects/gst-plugins-bad/tests/examples/waylandsink/gtk/wayland-threads.c diff --git a/subprojects/gst-plugins-bad/tests/examples/waylandsink/window.ui b/subprojects/gst-plugins-bad/tests/examples/waylandsink/gtk/window.ui similarity index 100% rename from subprojects/gst-plugins-bad/tests/examples/waylandsink/window.ui rename to subprojects/gst-plugins-bad/tests/examples/waylandsink/gtk/window.ui diff --git a/subprojects/gst-plugins-bad/tests/examples/waylandsink/qt/CMakeLists.txt b/subprojects/gst-plugins-bad/tests/examples/waylandsink/qt/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..ae1fb94fc65d91c3408b4840545cddee4a809534 --- /dev/null +++ b/subprojects/gst-plugins-bad/tests/examples/waylandsink/qt/CMakeLists.txt @@ -0,0 +1,49 @@ +cmake_minimum_required(VERSION 3.14) + +project(waylandsink LANGUAGES CXX) + +set(CMAKE_INCLUDE_CURRENT_DIR ON) + +set(CMAKE_AUTOUIC ON) +set(CMAKE_AUTOMOC ON) +set(CMAKE_AUTORCC ON) + +set(CMAKE_CXX_STANDARD 11) +set(CMAKE_CXX_STANDARD_REQUIRED ON) + +set(PROJECT_SOURCES + main.cpp + videowidget.cpp videowidget.h + ) + +add_executable(waylandsink ${PROJECT_SOURCES}) + +find_package(PkgConfig REQUIRED) + +# GStreamer dependencies +pkg_search_module(GSTREAMER1.0 gstreamer-1.0) +pkg_search_module(GSTREAMER1.0_VIDEO gstreamer-video-1.0) +pkg_search_module(GSTREAMER1.0_WAYLAND gstreamer-wayland-1.0) + +include_directories( + ${GSTREAMER1.0_INCLUDE_DIRS} +) + +target_link_libraries(waylandsink + ${GSTREAMER1.0_LIBRARIES} + ${GSTREAMER1.0_VIDEO_LIBRARIES} + ${GSTREAMER1.0_WAYLAND_LIBRARIES} + ) + +# Qt dependencies +find_package(QT NAMES Qt5 COMPONENTS Core Widgets REQUIRED) +find_package(Qt5 COMPONENTS Core Widgets REQUIRED) +find_package(Qt5Gui REQUIRED) + +include_directories( + ${Qt5Core_INCLUDE_DIRS} + ${Qt5Gui_PRIVATE_INCLUDE_DIRS} + ${Qt5Widgets_INCLUDE_DIRS} +) + +target_link_libraries(waylandsink Qt5::Core Qt5::Widgets) diff --git a/subprojects/gst-plugins-bad/tests/examples/waylandsink/qt/README.md b/subprojects/gst-plugins-bad/tests/examples/waylandsink/qt/README.md new file mode 100644 index 0000000000000000000000000000000000000000..31bdd0188aff2d934a9b2477b805f76a69b9012a --- /dev/null +++ b/subprojects/gst-plugins-bad/tests/examples/waylandsink/qt/README.md @@ -0,0 +1,23 @@ +# Qt Waylandsink Example + +Example of using `waylandsink` in Qt + +## How to build + +```shell +$ mkdir build && cd build +$ cmake path/to/this/example +$ make +``` + +## How to run + +Play `videotestsrc`: +```shell +$ QT_QPA_PLATFORM=wayland ./waylandsink +``` + +Play a video: +```shell +$ QT_QPA_PLATFORM=wayland ./waylandsink file:///home/user/Videos/video.mp4 +``` diff --git a/subprojects/gst-plugins-bad/tests/examples/waylandsink/qt/main.cpp b/subprojects/gst-plugins-bad/tests/examples/waylandsink/qt/main.cpp new file mode 100644 index 0000000000000000000000000000000000000000..3d0772df723c6f9f488c71b72a39b724759d85a9 --- /dev/null +++ b/subprojects/gst-plugins-bad/tests/examples/waylandsink/qt/main.cpp @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2022 Toshiba Corporation. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include "videowidget.h" + +#include +#include + +int main(int argc, char *argv[]) { + QApplication app(argc, argv); + gst_init(&argc, &argv); + + VideoWidget v; + v.resize(500, 500); + + QCommandLineParser parser; + parser.setApplicationDescription("Qt Waylandsink Example"); + parser.addHelpOption(); + parser.addPositionalArgument("video", "Video path, eg. file:///home/user/Videos/video.mp4"); + parser.process(app); + + /* Default pipeline to run if no video path is passed. */ + QString pipelineStr = QString("videotestsrc ! video/x-raw,width=%1,height=%2 ! waylandsink") + .arg(v.width()).arg(v.height()); + + if (!parser.positionalArguments().isEmpty()) + pipelineStr = "playbin video-sink=waylandsink uri=" + parser.positionalArguments().constFirst(); + + v.setPipeline(pipelineStr); + v.show(); + + return app.exec(); +} diff --git a/subprojects/gst-plugins-bad/tests/examples/waylandsink/qt/videowidget.cpp b/subprojects/gst-plugins-bad/tests/examples/waylandsink/qt/videowidget.cpp new file mode 100644 index 0000000000000000000000000000000000000000..1f5f7d8f5c97d6e89b6f74de6142594e5121baa2 --- /dev/null +++ b/subprojects/gst-plugins-bad/tests/examples/waylandsink/qt/videowidget.cpp @@ -0,0 +1,149 @@ +/* + * Copyright (C) 2022 Toshiba Corporation. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include "videowidget.h" + +#include +#include + +#include + +static gboolean busWatchCb(GstBus *bus, GstMessage *message, VideoWidget *data) { + auto messageType = GST_MESSAGE_TYPE(message); + if (messageType == GST_MESSAGE_ERROR) { + GError *err = nullptr; + gchar *dbg = nullptr; + gst_message_parse_error(message, &err, &dbg); + qDebug(" %s: %s", GST_OBJECT_NAME (message->src), err->message); + qDebug("Debugging info: %s", (dbg) ? dbg : "none"); + g_error_free(err); + g_free(dbg); + } + + return TRUE; +} + +GstBusSyncReply busSyncHandler(GstBus *bus, GstMessage *message, gpointer user_data) { + if (gst_is_wayland_display_handle_need_context_message(message)) { + GstContext *context; + QPlatformNativeInterface *pni; + struct wl_display *display_handle; + + pni = QGuiApplication::platformNativeInterface(); + display_handle = (struct wl_display *) pni->nativeResourceForWindow("display", nullptr); + context = gst_wayland_display_handle_context_new(display_handle); + gst_element_set_context(GST_ELEMENT (GST_MESSAGE_SRC(message)), context); + gst_context_unref(context); + + goto drop; + } else if (gst_is_video_overlay_prepare_window_handle_message(message)) { + /* Qt application window needs to be ready at this time + * or waylandsink will create a new window by itself. */ + auto *v = static_cast(user_data); + QPlatformNativeInterface *pni; + struct wl_surface *window_handle; + GstVideoOverlay *videoOverlay; + + videoOverlay = GST_VIDEO_OVERLAY (GST_MESSAGE_SRC(message)); + pni = QGuiApplication::platformNativeInterface(); + + /* Get window handle from widget's window + * https://doc.qt.io/qt-5/qwidget.html#window */ + window_handle = (struct wl_surface *) pni->nativeResourceForWindow("surface", + v->window()->windowHandle()); + + gst_video_overlay_set_window_handle(videoOverlay, (guintptr) window_handle); + gst_video_overlay_set_render_rectangle(videoOverlay, v->x(), v->y(), v->width(), v->height()); + + v->setVideoOverlay(videoOverlay); + goto drop; + } + + return GST_BUS_PASS; + + drop: + gst_message_unref(message); + return GST_BUS_DROP; +} + +VideoWidget::VideoWidget(QWidget *parent) : QWidget(parent) { + /* Make this widget expand, so it can fill empty space in a Qt layout. */ + setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); +} + +VideoWidget::~VideoWidget() { + if (_pipeline) { + stop(); + g_object_unref(_pipeline); + } + _pipeline = nullptr; + _videoOverlay = nullptr; +} + +void VideoWidget::setVideoOverlay(GstVideoOverlay *videoOverlay) { + _videoOverlay = videoOverlay; +} + +void VideoWidget::setPipeline(const QString &pipelineStr) { + qDebug("Pipeline: %s", pipelineStr.toStdString().c_str()); + _pipeline = gst_parse_launch(pipelineStr.toStdString().c_str(), nullptr); + if (!_pipeline) + qFatal("Failed to create pipeline"); + + GstBus *bus = gst_pipeline_get_bus(GST_PIPELINE (_pipeline)); + gst_bus_set_sync_handler(bus, busSyncHandler, this, nullptr); + gst_bus_add_watch(bus, reinterpret_cast(&busWatchCb), this); + gst_object_unref(bus); +} + +void VideoWidget::play() { + _setState(GST_STATE_PLAYING); +} + +void VideoWidget::pause() { + _setState(GST_STATE_PAUSED); +} + +void VideoWidget::stop() { + _setState(GST_STATE_NULL); +} + +bool VideoWidget::event(QEvent *event) { + /* Play video automatically at the first time window is active. */ + if (event->type() == QEvent::WindowActivate) { + if (_firstActive) { + play(); + _firstActive = false; + } + } + return QWidget::event(event); +} + +void VideoWidget::resizeEvent(QResizeEvent *event) { + QWidget::resizeEvent(event); + + /* Change render rectangle as widget size. */ + if (_videoOverlay) + gst_video_overlay_set_render_rectangle(_videoOverlay, x(), y(), width(), height()); +} + +void VideoWidget::_setState(GstState state) { + if (_pipeline) + gst_element_set_state(_pipeline, state); +} \ No newline at end of file diff --git a/subprojects/gst-plugins-bad/tests/examples/waylandsink/qt/videowidget.h b/subprojects/gst-plugins-bad/tests/examples/waylandsink/qt/videowidget.h new file mode 100644 index 0000000000000000000000000000000000000000..3c918d6d70b5e889587ba0d740d6895f24ed751b --- /dev/null +++ b/subprojects/gst-plugins-bad/tests/examples/waylandsink/qt/videowidget.h @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2022 Toshiba Corporation. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef WAYLANDSINK_VIDEOWIDGET_H +#define WAYLANDSINK_VIDEOWIDGET_H + +#include + +#include + +class VideoWidget : public QWidget { +Q_OBJECT +public: + explicit VideoWidget(QWidget *parent = nullptr); + ~VideoWidget() override; + + void setPipeline(const QString &pipelineStr); + void setVideoOverlay(GstVideoOverlay *videoOverlay); + +public slots: + void play(); + void pause(); + void stop(); + +protected: + bool event(QEvent *event) override; + void resizeEvent(QResizeEvent *event) override; + +private: + bool _firstActive = true; + GstElement *_pipeline = nullptr; + GstVideoOverlay *_videoOverlay = nullptr; + + void _setState(GstState state); +}; + + +#endif //WAYLANDSINK_VIDEOWIDGET_H