diff options
author | Pavel Bludov <pbludov@gmail.com> | 2015-10-23 14:56:57 +0800 |
---|---|---|
committer | George Kiagiadakis <george.kiagiadakis@collabora.com> | 2018-05-04 17:06:11 +0300 |
commit | 7f298aa319703826ebf78b4b8aaa6caf8e36c292 (patch) | |
tree | fe2dab5b1980aeb940f117665aaeb296849f52a1 | |
parent | 437a3ae0e0548f5db7515341728a77a4e852ff80 (diff) |
DeviceMonitor example
-rw-r--r-- | examples/CMakeLists.txt | 3 | ||||
-rw-r--r-- | examples/devmon/CMakeLists.txt | 23 | ||||
-rw-r--r-- | examples/devmon/devmon.pro | 23 | ||||
-rw-r--r-- | examples/devmon/main.cpp | 34 | ||||
-rw-r--r-- | examples/devmon/mainwindow.cpp | 270 | ||||
-rw-r--r-- | examples/devmon/mainwindow.h | 55 |
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 |