summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPavel Bludov <pbludov@gmail.com>2015-10-23 14:56:57 +0800
committerGeorge Kiagiadakis <george.kiagiadakis@collabora.com>2018-05-04 17:06:11 +0300
commit7f298aa319703826ebf78b4b8aaa6caf8e36c292 (patch)
treefe2dab5b1980aeb940f117665aaeb296849f52a1
parent437a3ae0e0548f5db7515341728a77a4e852ff80 (diff)
DeviceMonitor example
-rw-r--r--examples/CMakeLists.txt3
-rw-r--r--examples/devmon/CMakeLists.txt23
-rw-r--r--examples/devmon/devmon.pro23
-rw-r--r--examples/devmon/main.cpp34
-rw-r--r--examples/devmon/mainwindow.cpp270
-rw-r--r--examples/devmon/mainwindow.h55
6 files changed, 408 insertions, 0 deletions
diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt
index 548d84f..01ae289 100644
--- a/examples/CMakeLists.txt
+++ b/examples/CMakeLists.txt
@@ -39,3 +39,6 @@ if (Qt4or5_Quick2_FOUND)
add_subdirectory(qmlplayer2)
example_distcheck(qmlplayer2)
endif()
+
+add_subdirectory(devmon)
+example_distcheck(devmon)
diff --git a/examples/devmon/CMakeLists.txt b/examples/devmon/CMakeLists.txt
new file mode 100644
index 0000000..5f0674c
--- /dev/null
+++ b/examples/devmon/CMakeLists.txt
@@ -0,0 +1,23 @@
+project(qtgst-example-devmon)
+
+if (NOT BUILDING_QTGSTREAMER)
+ set(CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/../../cmake/modules)
+ find_package(Qt4or5 COMPONENTS Core Gui Widgets REQUIRED)
+ if (${QT_VERSION} STREQUAL "5")
+ find_package(Qt5GStreamer REQUIRED)
+ else()
+ find_package(QtGStreamer REQUIRED)
+ endif()
+ set(CMAKE_AUTOMOC ON)
+ set(CMAKE_INCLUDE_CURRENT_DIR ON)
+endif()
+
+include_directories(${QTGSTREAMER_INCLUDES})
+add_definitions(${QTGSTREAMER_DEFINITIONS})
+set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${QTGSTREAMER_FLAGS}")
+
+set(devmon_SOURCES main.cpp mainwindow.cpp)
+
+add_executable(devmon ${devmon_SOURCES})
+target_link_libraries(devmon ${QTGSTREAMER_UI_LIBRARIES})
+qt4or5_use_modules(devmon Core Gui Widgets)
diff --git a/examples/devmon/devmon.pro b/examples/devmon/devmon.pro
new file mode 100644
index 0000000..f553067
--- /dev/null
+++ b/examples/devmon/devmon.pro
@@ -0,0 +1,23 @@
+#
+# Copyright (C) 2015 Pavel Bludov <pbludov@gmail.com>
+#
+# This library is free software; you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as published
+# by the Free Software Foundation; either version 2.1 of the License, or
+# (at your option) any later version.
+#
+# This program 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 Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+QT += core gui widgets
+CONFIG += link_pkgconfig
+PKGCONFIG += Qt5GLib-2.0 Qt5GStreamer-1.0
+TARGET = devmon
+TEMPLATE = app
+SOURCES += main.cpp mainwindow.cpp
+HEADERS += mainwindow.h
+
diff --git a/examples/devmon/main.cpp b/examples/devmon/main.cpp
new file mode 100644
index 0000000..43b6126
--- /dev/null
+++ b/examples/devmon/main.cpp
@@ -0,0 +1,34 @@
+/*
+ Copyright (C) 2015 Pavel Bludov <pbludov@gmail.com>
+
+ This library is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as published
+ by the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ This program 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 Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+#include "mainwindow.h"
+#include <QApplication>
+
+#include <QGst/Init>
+
+int main(int argc, char *argv[])
+{
+ QGst::init(&argc, &argv);
+ QApplication a(argc, argv);
+
+ MainWindow w;
+ w.show();
+
+ int ret = a.exec();
+
+ QGst::cleanup();
+ return ret;
+}
diff --git a/examples/devmon/mainwindow.cpp b/examples/devmon/mainwindow.cpp
new file mode 100644
index 0000000..eef3022
--- /dev/null
+++ b/examples/devmon/mainwindow.cpp
@@ -0,0 +1,270 @@
+/*
+ Copyright (C) 2015 Pavel Bludov <pbludov@gmail.com>
+
+ This library is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as published
+ by the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ This program 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 Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+#include "mainwindow.h"
+
+#include <QApplication>
+#include <QBoxLayout>
+#include <QDebug>
+#include <QHeaderView>
+#include <QMessageBox>
+#include <QMenuBar>
+#include <QTextEdit>
+#include <QTreeWidget>
+
+#include <QGlib/Connect>
+#include <QGlib/ParamSpec>
+#include <QGst/ElementFactory>
+#include <QGst/Pipeline>
+
+MainWindow::MainWindow(QWidget *parent) :
+ QWidget(parent)
+{
+ //
+ // UI
+ //
+
+ deviceTree = new QTreeWidget;
+ propEdit = new QTextEdit;
+ capsEdit = new QTextEdit;
+
+ QLayout *layout = new QVBoxLayout();
+ layout->addWidget(deviceTree);
+ layout->addWidget(propEdit);
+ layout->addWidget(capsEdit);
+ setLayout(layout);
+
+ deviceTree->setColumnCount(2);
+ deviceTree->setColumnWidth(0, 250);
+ deviceTree->setHeaderLabels(QStringList(tr("Name")) << tr("Display name"));
+ deviceTree->addTopLevelItem(new QTreeWidgetItem(QStringList(tr("Devices"))));
+ deviceTree->setMinimumHeight(300);
+ connect(deviceTree, SIGNAL(currentItemChanged(QTreeWidgetItem*,QTreeWidgetItem*)),
+ this, SLOT(onCurrentItemChanged(QTreeWidgetItem*,QTreeWidgetItem*)));
+ connect(deviceTree, SIGNAL(itemDoubleClicked(QTreeWidgetItem*,int)), this, SLOT(onItemDoubleClicked(QTreeWidgetItem*,int)));
+
+ propEdit->setMinimumHeight(60);
+ propEdit->setReadOnly(true);
+
+ capsEdit->setMinimumHeight(200);
+ capsEdit->setReadOnly(true);
+ resize(800, 600);
+
+ //
+ // Menu
+ //
+
+ QMenuBar *bar = new QMenuBar();
+ QMenu *menu = new QMenu(tr("&Device Monitor"));
+ bar->addMenu(menu);
+ layout->setMenuBar(bar);
+
+ createElementAction = menu->addAction(tr("&Create"), this, SLOT(onCreateElement()), QKeySequence::New);
+ menu->addSeparator();
+ menu->addAction(tr("&Quit"), qApp, SLOT(quit()), QKeySequence::Quit);
+
+ //
+ // DeviceMonitor
+ //
+
+ monitor = QGst::DeviceMonitor::create();
+
+ if (!monitor) {
+ QMessageBox::warning(this, this->windowTitle(), "Failed to create the device monitor");
+ return;
+ }
+
+ if (!monitor->start()) {
+ QMessageBox::warning(this, this->windowTitle(), "Failed to start the device monitor");
+ return;
+ }
+
+ QGst::BusPtr bus = monitor->bus();
+ bus->addSignalWatch();
+ QGlib::connect(bus, "message", this, &MainWindow::onBusMessage);
+
+ // Add all existing devices to the list
+ Q_FOREACH (QGst::DevicePtr device, monitor->devices()) {
+ onDeviceAdded(device);
+ }
+}
+
+MainWindow::~MainWindow()
+{
+ QGst::BusPtr bus = monitor->bus();
+ bus->removeSignalWatch();
+ QGlib::disconnect(bus, "message", this, &MainWindow::onBusMessage);
+ monitor->stop();
+}
+
+void MainWindow::onBusMessage(const QGst::MessagePtr& msg)
+{
+ switch (msg->type()) {
+ case QGst::MessageDeviceAdded:
+ onDeviceAdded(msg.staticCast<QGst::DeviceAddedMessage>()->device());
+ break;
+ case QGst::MessageDeviceRemoved:
+ onDeviceRemoved(msg.staticCast<QGst::DeviceRemovedMessage>()->device());
+ break;
+ default:
+ qDebug() << msg->typeName() << " " << msg->source()->property("name").toString();
+ break;
+ }
+}
+
+void MainWindow::onDeviceAdded(const QGst::DevicePtr & device)
+{
+ QTreeWidgetItem *parent = deviceTree->topLevelItem(0);
+
+ // Create the class hierarchy tree
+ Q_FOREACH (QString cls, device->deviceClass().split('/')) {
+ QTreeWidgetItem* next = NULL;
+ for (int idx = 0; idx < parent->childCount(); ++idx) {
+ QTreeWidgetItem* item = parent->child(idx);
+ if (item->text(0) == cls) {
+ next = item;
+ break;
+ }
+ }
+ if (!next) {
+ next = new QTreeWidgetItem(QStringList(cls));
+ parent->addChild(next);
+ }
+ parent = next;
+ }
+
+ // Add newly created item for the device
+ QTreeWidgetItem* newItem = new QTreeWidgetItem(QStringList(device->name()) << device->displayName());
+ parent->addChild(newItem);
+ deviceTree->expandAll();
+
+ // Display all device properties. All of them are internal, undocumented, and should be never used
+ QString info;
+ Q_FOREACH (const QGlib::ParamSpecPtr & prop, device->listProperties()) {
+ if (QGlib::GetType<QGst::Device>().isA(prop->ownerType())) {
+ // Filter out all base class properties
+ continue;
+ }
+
+ info.append(prop->name())
+ .append('=')
+ .append(device->property(prop->name().toUtf8()).toString())
+ .append("\r\n");
+ }
+ newItem->setData(0, Qt::UserRole, info);
+ propEdit->setText(info);
+
+ // Dsiplay the device caps
+ QString caps = device->caps()->toString().replace("; ", ";\r\n");
+ newItem->setData(1, Qt::UserRole, caps);
+ capsEdit->setText(caps);
+}
+
+void MainWindow::onDeviceRemoved(const QGst::DevicePtr & device)
+{
+ Q_FOREACH (QTreeWidgetItem *item, deviceTree->findItems(device->name(), Qt::MatchRecursive)) {
+ if (item->isSelected()) {
+ // Clear prop & caps
+ onCurrentItemChanged(NULL, NULL);
+ }
+ delete item;
+ }
+}
+
+// Probe the device. Only Audio|Video/Source|Sink are implemented.
+void MainWindow::createElement(const QGst::DevicePtr & device)
+{
+ QGst::PipelinePtr pipeline = QGst::Pipeline::create();
+ QGst::ElementPtr src;
+ QGst::ElementPtr sink;
+
+ if (device->hasClasses("Source"))
+ {
+ src = device->createElement("src");
+ if (!src) {
+ QMessageBox::critical(this, this->windowTitle(), tr("Failed to create the source element"));
+ return;
+ }
+ sink = QGst::ElementFactory::make(device->hasClasses("Video")? "autovideosink": "autoaudiosink");
+ if (!sink) {
+ QMessageBox::critical(this, this->windowTitle(), tr("Failed to create the autosink element"));
+ return;
+ }
+ }
+ else if (device->hasClasses("Sink")) {
+ sink = device->createElement("sink");
+ if (!sink) {
+ QMessageBox::critical(this, this->windowTitle(), tr("Failed to create the sink element"));
+ return;
+ }
+ src = QGst::ElementFactory::make(device->hasClasses("Video")? "videotestsrc": "audiotestsrc");
+ if (!src) {
+ QMessageBox::critical(this, this->windowTitle(), tr("Failed to create the test source element"));
+ return;
+ }
+ }
+ else {
+ QMessageBox::warning(this, this->windowTitle(), tr("Unsupported class '%1'").arg(device->deviceClass()));
+ return;
+ }
+
+ // Build sample pipeline for testing
+
+ pipeline->add(src, sink);
+ src->link(sink);
+
+ pipeline->setState(QGst::StatePlaying);
+ if (QGst::StateChangeSuccess != pipeline->getState(NULL, NULL, QGst::ClockTime::fromSeconds(10))) {
+ QMessageBox::critical(this, this->windowTitle(), tr("Failed to set the pipeline to playing state"));
+ } else {
+ QMessageBox::information(this, device->displayName(), tr("It works! Press 'OK' to continue"));
+ }
+ pipeline->setState(QGst::StateNull);
+ pipeline->getState(NULL, NULL, QGst::ClockTime::fromSeconds(10));
+}
+
+void MainWindow::onCurrentItemChanged(QTreeWidgetItem *current, QTreeWidgetItem *)
+{
+ if (current) {
+ propEdit->setText(current->data(0, Qt::UserRole).toString());
+ capsEdit->setText(current->data(1, Qt::UserRole).toString());
+ createElementAction->setEnabled(!current->data(0, Qt::UserRole).isNull());
+ }
+ else {
+ propEdit->clear();
+ capsEdit->clear();
+ createElementAction->setEnabled(false);
+ }
+}
+
+void MainWindow::onCreateElement()
+{
+ QTreeWidgetItem *item = deviceTree->currentItem();
+ if (item) {
+ onItemDoubleClicked(item, 0);
+ }
+ }
+
+void MainWindow::onItemDoubleClicked(QTreeWidgetItem *item, int)
+{
+ QString name = item->text(0);
+ Q_FOREACH (QGst::DevicePtr device, monitor->devices()) {
+ if (device->name() == name) {
+ createElement(device);
+ break;
+ }
+ }
+}
diff --git a/examples/devmon/mainwindow.h b/examples/devmon/mainwindow.h
new file mode 100644
index 0000000..71e4c35
--- /dev/null
+++ b/examples/devmon/mainwindow.h
@@ -0,0 +1,55 @@
+/*
+ Copyright (C) 2015 Pavel Bludov <pbludov@gmail.com>
+
+ This library is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as published
+ by the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ This program 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 Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+#ifndef MAINWINDOW_H
+#define MAINWINDOW_H
+
+#include <QWidget>
+
+#include <QGst/DeviceMonitor>
+#include <QGst/Message>
+
+class QTextEdit;
+class QTreeWidget;
+class QTreeWidgetItem;
+
+class MainWindow : public QWidget
+{
+ Q_OBJECT
+
+public:
+ explicit MainWindow(QWidget *parent = 0);
+ ~MainWindow();
+
+private Q_SLOTS:
+ void onCreateElement();
+ void onCurrentItemChanged(QTreeWidgetItem *current, QTreeWidgetItem*);
+ void onItemDoubleClicked(QTreeWidgetItem *item, int);
+
+private:
+ QAction *createElementAction;
+ QTreeWidget *deviceTree;
+ QTextEdit *propEdit;
+ QTextEdit *capsEdit;
+ QGst::DeviceMonitorPtr monitor;
+
+ void onBusMessage(const QGst::MessagePtr & msg);
+ void onDeviceAdded(const QGst::DevicePtr & device);
+ void onDeviceRemoved(const QGst::DevicePtr & device);
+ void createElement(const QGst::DevicePtr & device);
+};
+
+#endif // MAINWINDOW_H