From fe4fbb5775f3c663248c1341667e57529c37a114 Mon Sep 17 00:00:00 2001 From: wm4 Date: Fri, 4 Jul 2014 02:21:14 +0200 Subject: DOCS/client_api_examples: add a Qt example This is pretty dumb and extremely basic. The main purpose is demonstrating how to integrate mpv into the Qt GUI thread. --- DOCS/client_api_examples/qtexample.cpp | 135 +++++++++++++++++++++++++++++++++ DOCS/client_api_examples/qtexample.h | 30 ++++++++ DOCS/client_api_examples/qtexample.pro | 12 +++ 3 files changed, 177 insertions(+) create mode 100644 DOCS/client_api_examples/qtexample.cpp create mode 100644 DOCS/client_api_examples/qtexample.h create mode 100644 DOCS/client_api_examples/qtexample.pro (limited to 'DOCS/client_api_examples') diff --git a/DOCS/client_api_examples/qtexample.cpp b/DOCS/client_api_examples/qtexample.cpp new file mode 100644 index 0000000000..b23d2fa891 --- /dev/null +++ b/DOCS/client_api_examples/qtexample.cpp @@ -0,0 +1,135 @@ +// License: pick one of: public domain, WTFPL, ISC, Ms-PL, AGPLv3 + +// This example can be built with: qmake && make + +#include + +#include +#include +#include +#include +#include +#include + +#include "qtexample.h" + +static void wakeup(void *ctx) +{ + // This callback is invoked from any mpv thread (but possibly also + // recursively from a thread that is calling the mpv API). Just notify + // the Qt GUI thread to wake up (so that it can process events with + // mpv_wait_event()), and return as quickly as possible. + MainWindow *mainwindow = (MainWindow *)ctx; + QCoreApplication::postEvent(mainwindow, new QEvent(QEvent::User)); +} + +MainWindow::MainWindow(QWidget *parent) : + QMainWindow(parent) +{ + QMenu *menu = menuBar()->addMenu(tr("&File")); + QAction *on_open = new QAction(tr("&Open"), this); + on_open->setShortcuts(QKeySequence::Open); + on_open->setStatusTip(tr("Open a file")); + connect(on_open, SIGNAL(triggered()), this, SLOT(on_file_open())); + menu->addAction(on_open); + + statusBar(); + + mpv = mpv_create(); + if (!mpv) + throw "can't create mpv instance"; + + // Create a video child window. Force Qt to create a native window, and + // pass the window ID to the mpv wid option. This doesn't work on OSX, + // because Cocoa doesn't support this form of embedding. + mpv_container = new QWidget(this); + setCentralWidget(mpv_container); + mpv_container->setAttribute(Qt::WA_NativeWindow); + int64_t wid = mpv_container->winId(); + mpv_set_option(mpv, "wid", MPV_FORMAT_INT64, &wid); + + // Enable default bindings, because we're lazy. Normally, a player using + // mpv as backend would implement its own key bindings. + mpv_set_option_string(mpv, "input-default-bindings", "yes"); + + // Let us receive property change events with MPV_EVENT_PROPERTY_CHANGE if + // this property changes. + mpv_observe_property(mpv, 0, "time-pos", MPV_FORMAT_DOUBLE); + + // From this point on, the wakeup function will be called. The callback + // can come from any thread, so we use the Qt QEvent mechanism to relay + // the wakeup in a thread-safe way. + mpv_set_wakeup_callback(mpv, wakeup, this); + + if (mpv_initialize(mpv) < 0) + throw "mpv failed to initialize"; +} + +void MainWindow::handle_mpv_event(mpv_event *event) +{ + switch (event->event_id) { + case MPV_EVENT_PROPERTY_CHANGE: { + mpv_event_property *prop = (mpv_event_property *)event->data; + if (strcmp(prop->name, "time-pos") == 0) { + if (prop->format == MPV_FORMAT_DOUBLE) { + double time = *(double *)prop->data; + std::stringstream ss; + ss << "At: " << time; + statusBar()->showMessage(QString::fromStdString(ss.str())); + } else if (prop->format == MPV_FORMAT_NONE) { + // The property is unavailable, which probably means playback + // was stopped. + statusBar()->showMessage(""); + } + } + break; + } + case MPV_EVENT_SHUTDOWN: { + mpv_terminate_destroy(mpv); + mpv = NULL; + break; + } + default: ; + // Ignore uninteresting or unknown events. + } +} + +bool MainWindow::event(QEvent *event) +{ + // QEvent::User is sent by wakeup(). + if (event->type() == QEvent::User) { + // Process all events, until the event queue is empty. + while (mpv) { + mpv_event *event = mpv_wait_event(mpv, 0); + if (event->event_id == MPV_EVENT_NONE) + break; + handle_mpv_event(event); + } + return true; + } + return QMainWindow::event(event); +} + +void MainWindow::on_file_open() +{ + QString filename = QFileDialog::getOpenFileName(this, "Open file"); + if (mpv) { + const char *args[] = {"loadfile", filename.toUtf8().data(), NULL}; + mpv_command_async(mpv, 0, args); + } +} + +MainWindow::~MainWindow() +{ + if (mpv) + mpv_terminate_destroy(mpv); +} + +int main(int argc, char *argv[]) +{ + QApplication a(argc, argv); + MainWindow w; + w.show(); + + return a.exec(); +} diff --git a/DOCS/client_api_examples/qtexample.h b/DOCS/client_api_examples/qtexample.h new file mode 100644 index 0000000000..b5b6fe7fb2 --- /dev/null +++ b/DOCS/client_api_examples/qtexample.h @@ -0,0 +1,30 @@ +#ifndef QTEXAMPLE_H +#define QTEXAMPLE_H + +#include + +#include + +class MainWindow : public QMainWindow +{ + Q_OBJECT + +public: + explicit MainWindow(QWidget *parent = 0); + ~MainWindow(); + +protected: + virtual bool event(QEvent *event); + +private slots: + void on_file_open(); + +private: + QWidget *mpv_container; + mpv_handle *mpv; + + void create_player(); + void handle_mpv_event(mpv_event *event); +}; + +#endif // QTEXAMPLE_H diff --git a/DOCS/client_api_examples/qtexample.pro b/DOCS/client_api_examples/qtexample.pro new file mode 100644 index 0000000000..03174b8778 --- /dev/null +++ b/DOCS/client_api_examples/qtexample.pro @@ -0,0 +1,12 @@ +QT += core gui + +greaterThan(QT_MAJOR_VERSION, 4): QT += widgets + +TARGET = qtexample +TEMPLATE = app + +CONFIG += link_pkgconfig +PKGCONFIG = mpv + +SOURCES += qtexample.cpp +HEADERS += qtexample.h -- cgit v1.2.3