From 52de5ea6addb62a89a79689397b055ba8fab1795 Mon Sep 17 00:00:00 2001 From: wang-bin Date: Sun, 11 Oct 2015 17:19:36 +0800 Subject: DOCS/client_api_examples: add qt widget + opengl-cb example --- DOCS/client_api_examples/qt_opengl/main.cpp | 13 +++ DOCS/client_api_examples/qt_opengl/mainwindow.cpp | 53 +++++++++ DOCS/client_api_examples/qt_opengl/mainwindow.h | 27 +++++ DOCS/client_api_examples/qt_opengl/mpvwidget.cpp | 128 ++++++++++++++++++++++ DOCS/client_api_examples/qt_opengl/mpvwidget.h | 38 +++++++ DOCS/client_api_examples/qt_opengl/qt_opengl.pro | 13 +++ 6 files changed, 272 insertions(+) create mode 100644 DOCS/client_api_examples/qt_opengl/main.cpp create mode 100644 DOCS/client_api_examples/qt_opengl/mainwindow.cpp create mode 100644 DOCS/client_api_examples/qt_opengl/mainwindow.h create mode 100644 DOCS/client_api_examples/qt_opengl/mpvwidget.cpp create mode 100644 DOCS/client_api_examples/qt_opengl/mpvwidget.h create mode 100644 DOCS/client_api_examples/qt_opengl/qt_opengl.pro (limited to 'DOCS/client_api_examples') diff --git a/DOCS/client_api_examples/qt_opengl/main.cpp b/DOCS/client_api_examples/qt_opengl/main.cpp new file mode 100644 index 0000000000..086a4b4617 --- /dev/null +++ b/DOCS/client_api_examples/qt_opengl/main.cpp @@ -0,0 +1,13 @@ +#include +#include "mainwindow.h" + +int main(int argc, char *argv[]) +{ + QApplication a(argc, argv); + // Qt sets the locale in the QApplication constructor, but libmpv requires + // the LC_NUMERIC category to be set to "C", so change it back. + setlocale(LC_NUMERIC, "C"); + MainWindow w; + w.show(); + return a.exec(); +} diff --git a/DOCS/client_api_examples/qt_opengl/mainwindow.cpp b/DOCS/client_api_examples/qt_opengl/mainwindow.cpp new file mode 100644 index 0000000000..2e3cf210ee --- /dev/null +++ b/DOCS/client_api_examples/qt_opengl/mainwindow.cpp @@ -0,0 +1,53 @@ +#include "mainwindow.h" +#include "mpvwidget.h" +#include +#include +#include +#include + +MainWindow::MainWindow(QWidget *parent) : QWidget(parent) +{ + m_mpv = new MpvWidget(this); + m_slider = new QSlider(); + m_slider->setOrientation(Qt::Horizontal); + m_openBtn = new QPushButton("Open"); + m_playBtn = new QPushButton("Pause"); + QHBoxLayout *hb = new QHBoxLayout(); + hb->addWidget(m_openBtn); + hb->addWidget(m_playBtn); + QVBoxLayout *vl = new QVBoxLayout(); + vl->addWidget(m_mpv); + vl->addWidget(m_slider); + vl->addLayout(hb); + setLayout(vl); + connect(m_slider, SIGNAL(sliderMoved(int)), SLOT(seek(int))); + connect(m_openBtn, SIGNAL(clicked()), SLOT(openMedia())); + connect(m_playBtn, SIGNAL(clicked()), SLOT(pauseResume())); + connect(m_mpv, SIGNAL(positionChanged(int)), m_slider, SLOT(setValue(int))); + connect(m_mpv, SIGNAL(durationChanged(int)), this, SLOT(setSliderRange(int))); +} + +void MainWindow::openMedia() +{ + QString file = QFileDialog::getOpenFileName(0, "Open a video"); + if (file.isEmpty()) + return; + m_mpv->command(QStringList() << "loadfile" << file); +} + +void MainWindow::seek(int pos) +{ + m_mpv->command(QVariantList() << "seek" << pos << "absolute"); +} + +void MainWindow::pauseResume() +{ + const bool paused = m_mpv->getProperty("pause").toBool(); + m_mpv->setProperty("pause", !paused); +} + +void MainWindow::setSliderRange(int duration) +{ + const int time0 = m_mpv->getProperty("time-start").toInt(); + m_slider->setRange(time0, time0+duration); +} diff --git a/DOCS/client_api_examples/qt_opengl/mainwindow.h b/DOCS/client_api_examples/qt_opengl/mainwindow.h new file mode 100644 index 0000000000..523b385bb4 --- /dev/null +++ b/DOCS/client_api_examples/qt_opengl/mainwindow.h @@ -0,0 +1,27 @@ +#ifndef MainWindow_H +#define MainWindow_H + +#include + +class MpvWidget; +class QSlider; +class QPushButton; +class MainWindow : public QWidget +{ + Q_OBJECT +public: + explicit MainWindow(QWidget *parent = 0); +public Q_SLOTS: + void openMedia(); + void seek(int pos); + void pauseResume(); +private Q_SLOTS: + void setSliderRange(int duration); +private: + MpvWidget *m_mpv; + QSlider *m_slider; + QPushButton *m_openBtn; + QPushButton *m_playBtn; +}; + +#endif // MainWindow_H diff --git a/DOCS/client_api_examples/qt_opengl/mpvwidget.cpp b/DOCS/client_api_examples/qt_opengl/mpvwidget.cpp new file mode 100644 index 0000000000..2f3bf7e200 --- /dev/null +++ b/DOCS/client_api_examples/qt_opengl/mpvwidget.cpp @@ -0,0 +1,128 @@ +#include "mpvwidget.h" +#include +#include +#include + +static void wakeup(void *ctx) +{ + QMetaObject::invokeMethod((MpvWidget*)ctx, "on_mpv_events", Qt::QueuedConnection); +} + +static void *get_proc_address(void *ctx, const char *name) { + Q_UNUSED(ctx); + QOpenGLContext *glctx = QOpenGLContext::currentContext(); + if (!glctx) + return NULL; + return (void *)glctx->getProcAddress(QByteArray(name)); +} + +MpvWidget::MpvWidget(QWidget *parent, Qt::WindowFlags f) + : QOpenGLWidget(parent, f) +{ + mpv = mpv::qt::Handle::FromRawHandle(mpv_create()); + if (!mpv) + throw std::runtime_error("could not create mpv context"); + + mpv_set_option_string(mpv, "terminal", "yes"); + mpv_set_option_string(mpv, "msg-level", "all=v"); + if (mpv_initialize(mpv) < 0) + throw std::runtime_error("could not initialize mpv context"); + + // Make use of the MPV_SUB_API_OPENGL_CB API. + mpv::qt::set_option_variant(mpv, "vo", "opengl-cb"); + + // Request hw decoding, just for testing. + mpv::qt::set_option_variant(mpv, "hwdec", "auto"); + + mpv_gl = (mpv_opengl_cb_context *)mpv_get_sub_api(mpv, MPV_SUB_API_OPENGL_CB); + if (!mpv_gl) + throw std::runtime_error("OpenGL not compiled in"); + mpv_opengl_cb_set_update_callback(mpv_gl, MpvWidget::on_update, (void *)this); + connect(this, SIGNAL(frameSwapped()), SLOT(swapped())); + + mpv_observe_property(mpv, 0, "duration", MPV_FORMAT_DOUBLE); + mpv_observe_property(mpv, 0, "time-pos", MPV_FORMAT_DOUBLE); + mpv_set_wakeup_callback(mpv, wakeup, this); +} + +MpvWidget::~MpvWidget() +{ + if (mpv_gl) + mpv_opengl_cb_set_update_callback(mpv_gl, NULL, NULL); + // Until this call is done, we need to make sure the player remains + // alive. This is done implicitly with the mpv::qt::Handle instance + // in this class. + mpv_opengl_cb_uninit_gl(mpv_gl); +} + +void MpvWidget::command(const QVariant& params) +{ + mpv::qt::command_variant(mpv, params); +} + +void MpvWidget::setProperty(const QString& name, const QVariant& value) +{ + mpv::qt::set_property_variant(mpv, name, value); +} + +QVariant MpvWidget::getProperty(const QString &name) const +{ + return mpv::qt::get_property_variant(mpv, name); +} + +void MpvWidget::initializeGL() +{ + int r = mpv_opengl_cb_init_gl(mpv_gl, NULL, get_proc_address, NULL); + if (r < 0) + throw std::runtime_error("could not initialize OpenGL"); +} + +void MpvWidget::paintGL() +{ + mpv_opengl_cb_draw(mpv_gl, QOpenGLContext::currentContext()->defaultFramebufferObject(), width(), -height()); +} + +void MpvWidget::swapped() +{ + mpv_opengl_cb_report_flip(mpv_gl, 0); +} + +void MpvWidget::on_mpv_events() +{ + // 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); + } +} + +void MpvWidget::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; + Q_EMIT positionChanged(time); + } + } else if (strcmp(prop->name, "duration") == 0) { + if (prop->format == MPV_FORMAT_DOUBLE) { + double time = *(double *)prop->data; + Q_EMIT durationChanged(time); + } + } + break; + } + default: ; + // Ignore uninteresting or unknown events. + } +} + +void MpvWidget::on_update(void *ctx) +{ + QMetaObject::invokeMethod((MpvWidget*)ctx, "update"); +} diff --git a/DOCS/client_api_examples/qt_opengl/mpvwidget.h b/DOCS/client_api_examples/qt_opengl/mpvwidget.h new file mode 100644 index 0000000000..a219681393 --- /dev/null +++ b/DOCS/client_api_examples/qt_opengl/mpvwidget.h @@ -0,0 +1,38 @@ +#ifndef PLAYERWINDOW_H +#define PLAYERWINDOW_H + +#include +#include +#include +#include + +class MpvWidget Q_DECL_FINAL: public QOpenGLWidget +{ + Q_OBJECT +public: + MpvWidget(QWidget *parent = 0, Qt::WindowFlags f = 0); + ~MpvWidget(); + void command(const QVariant& params); + void setProperty(const QString& name, const QVariant& value); + QVariant getProperty(const QString& name) const; + QSize sizeHint() const { return QSize(480, 270);} +Q_SIGNALS: + void durationChanged(int value); + void positionChanged(int value); +protected: + void initializeGL() Q_DECL_OVERRIDE; + void paintGL() Q_DECL_OVERRIDE; +private Q_SLOTS: + void swapped(); + void on_mpv_events(); +private: + void handle_mpv_event(mpv_event *event); + static void on_update(void *ctx); + + mpv::qt::Handle mpv; + mpv_opengl_cb_context *mpv_gl; +}; + + + +#endif // PLAYERWINDOW_H diff --git a/DOCS/client_api_examples/qt_opengl/qt_opengl.pro b/DOCS/client_api_examples/qt_opengl/qt_opengl.pro new file mode 100644 index 0000000000..8a2bcc665d --- /dev/null +++ b/DOCS/client_api_examples/qt_opengl/qt_opengl.pro @@ -0,0 +1,13 @@ +CONFIG -= app_bundle +QT += widgets + +QT_CONFIG -= no-pkg-config +CONFIG += link_pkgconfig debug +PKGCONFIG += mpv + +HEADERS = \ + mpvwidget.h \ + mainwindow.h +SOURCES = main.cpp \ + mpvwidget.cpp \ + mainwindow.cpp -- cgit v1.2.3