From da4f7225796037b814919e79912cfaa7a9ddc113 Mon Sep 17 00:00:00 2001 From: wm4 Date: Thu, 1 Jan 2015 23:07:46 +0100 Subject: DOCS/client_api_examples: move all examples into their own subdirs Also get rid of shared.h; it actually doesn't have much value. Just copy the tiny function it contained into the 2 files which used it. --- DOCS/client_api_examples/cocoa/cocoabasic.m | 191 ++++++++++++++++++++++++ DOCS/client_api_examples/cocoabasic.m | 181 ----------------------- DOCS/client_api_examples/qt/qtexample.cpp | 216 ++++++++++++++++++++++++++++ DOCS/client_api_examples/qt/qtexample.h | 36 +++++ DOCS/client_api_examples/qt/qtexample.pro | 12 ++ DOCS/client_api_examples/qtexample.cpp | 216 ---------------------------- DOCS/client_api_examples/qtexample.h | 36 ----- DOCS/client_api_examples/qtexample.pro | 12 -- DOCS/client_api_examples/shared.h | 10 -- DOCS/client_api_examples/simple.c | 45 ------ DOCS/client_api_examples/simple/simple.c | 52 +++++++ wscript_build.py | 4 +- 12 files changed, 509 insertions(+), 502 deletions(-) create mode 100644 DOCS/client_api_examples/cocoa/cocoabasic.m delete mode 100644 DOCS/client_api_examples/cocoabasic.m create mode 100644 DOCS/client_api_examples/qt/qtexample.cpp create mode 100644 DOCS/client_api_examples/qt/qtexample.h create mode 100644 DOCS/client_api_examples/qt/qtexample.pro delete mode 100644 DOCS/client_api_examples/qtexample.cpp delete mode 100644 DOCS/client_api_examples/qtexample.h delete mode 100644 DOCS/client_api_examples/qtexample.pro delete mode 100644 DOCS/client_api_examples/shared.h delete mode 100644 DOCS/client_api_examples/simple.c create mode 100644 DOCS/client_api_examples/simple/simple.c diff --git a/DOCS/client_api_examples/cocoa/cocoabasic.m b/DOCS/client_api_examples/cocoa/cocoabasic.m new file mode 100644 index 0000000000..74f8f2e864 --- /dev/null +++ b/DOCS/client_api_examples/cocoa/cocoabasic.m @@ -0,0 +1,191 @@ +// Plays a video from the command line in a window provided by mpv. +// You likely want to play the video in your own window instead, +// but that's not quite ready yet. +// You may need a basic Info.plist and MainMenu.xib to make this work. + +#include "libmpv/client.h" + +#include +#include + +static inline void check_error(int status) +{ + if (status < 0) { + printf("mpv API error: %s\n", mpv_error_string(status)); + exit(1); + } +} + +#import + +@interface CocoaWindow : NSWindow +@end + +@implementation CocoaWindow +- (BOOL)canBecomeMainWindow { return YES; } +- (BOOL)canBecomeKeyWindow { return YES; } +@end + +@interface AppDelegate : NSObject +{ + mpv_handle *mpv; + dispatch_queue_t queue; + NSWindow *w; +} +@end + +static void wakeup(void *); + +@implementation AppDelegate + +- (void)createWindow { + + int mask = NSTitledWindowMask|NSClosableWindowMask| + NSMiniaturizableWindowMask|NSResizableWindowMask; + + self->w = [[CocoaWindow alloc] + initWithContentRect:NSMakeRect(0,0, 1280, 720) + styleMask:mask + backing:NSBackingStoreBuffered + defer:NO]; + + [self->w setTitle:@"cocoabasic example"]; + [self->w makeMainWindow]; + [self->w makeKeyAndOrderFront:nil]; + + NSMenu *m = [[NSMenu alloc] initWithTitle:@"AMainMenu"]; + NSMenuItem *item = [m addItemWithTitle:@"Apple" action:nil keyEquivalent:@""]; + NSMenu *sm = [[NSMenu alloc] initWithTitle:@"Apple"]; + [m setSubmenu:sm forItem:item]; + [sm addItemWithTitle: @"Shutdown mpv" action:@selector(shutdown) keyEquivalent:@"s"]; + [NSApp setMenu:m]; + [NSApp activateIgnoringOtherApps:YES]; +} + +- (void) applicationDidFinishLaunching:(NSNotification *)notification { + [NSApp setActivationPolicy:NSApplicationActivationPolicyRegular]; + atexit_b(^{ + // Because activation policy has just been set to behave like a real + // application, that policy must be reset on exit to prevent, among + // other things, the menubar created here from remaining on screen. + [NSApp setActivationPolicy:NSApplicationActivationPolicyProhibited]; + }); + + // Read filename + NSArray *args = [NSProcessInfo processInfo].arguments; + if (args.count < 2) { + NSLog(@"Expected filename on command line"); + exit(1); + } + NSString *filename = args[1]; + + [self createWindow]; + + // Deal with MPV in the background. + queue = dispatch_queue_create("mpv", DISPATCH_QUEUE_SERIAL); + dispatch_async(queue, ^{ + + mpv = mpv_create(); + if (!mpv) { + printf("failed creating context\n"); + exit(1); + } + + int64_t wid = (intptr_t) [self->w contentView]; + check_error(mpv_set_option(mpv, "wid", MPV_FORMAT_INT64, &wid)); + + // Maybe set some options here, like default key bindings. + // NOTE: Interaction with the window seems to be broken for now. + check_error(mpv_set_option_string(mpv, "input-default-bindings", "yes")); + + // for testing! + check_error(mpv_set_option_string(mpv, "input-media-keys", "yes")); + check_error(mpv_set_option_string(mpv, "input-cursor", "no")); + check_error(mpv_set_option_string(mpv, "input-vo-keyboard", "yes")); + + // request important errors + check_error(mpv_request_log_messages(mpv, "warn")); + + check_error(mpv_initialize(mpv)); + + // Register to be woken up whenever mpv generates new events. + mpv_set_wakeup_callback(mpv, wakeup, (__bridge void *) self); + + // Load the indicated file + const char *cmd[] = {"loadfile", filename.UTF8String, NULL}; + check_error(mpv_command(mpv, cmd)); + }); +} + +- (void) handleEvent:(mpv_event *)event +{ + switch (event->event_id) { + case MPV_EVENT_SHUTDOWN: + // Clean up and shut down. + mpv_terminate_destroy(mpv); + mpv = NULL; + dispatch_async(dispatch_get_main_queue(), ^{ + [[NSApplication sharedApplication] terminate:nil]; + }); + break; + + case MPV_EVENT_LOG_MESSAGE: { + struct mpv_event_log_message *msg = (struct mpv_event_log_message *)event->data; + printf("[%s] %s: %s", msg->prefix, msg->level, msg->text); + } + + case MPV_EVENT_VIDEO_RECONFIG: + dispatch_async(dispatch_get_main_queue(), ^{ + [self->w selectNextKeyView:nil]; + }); + + default: + printf("event: %s\n", mpv_event_name(event->event_id)); + } +} + +- (void) readEvents +{ + dispatch_async(queue, ^{ + while (mpv) { + mpv_event *event = mpv_wait_event(mpv, 0); + if (event->event_id == MPV_EVENT_NONE) + break; + [self handleEvent:event]; + } + }); +} + +static void wakeup(void *context) { + AppDelegate *a = (__bridge AppDelegate *) context; + [a readEvents]; +} + +// Ostensibly, mpv's window would be hooked up to this. +- (BOOL) windowShouldClose:(id)sender +{ + [self shutdown]; + if (self->w) + [self->w release]; + return YES; +} + +- (void) shutdown +{ + if (mpv) { + const char *args[] = {"quit", NULL}; + mpv_command(mpv, args); + } +} +@end + +// Delete this if you already have a main.m. +int main(int argc, const char * argv[]) { + @autoreleasepool { + NSApplication *app = [NSApplication sharedApplication]; + AppDelegate *delegate = [AppDelegate new]; + app.delegate = delegate; + [app run]; + } + return EXIT_SUCCESS; +} diff --git a/DOCS/client_api_examples/cocoabasic.m b/DOCS/client_api_examples/cocoabasic.m deleted file mode 100644 index 9c3d23ef57..0000000000 --- a/DOCS/client_api_examples/cocoabasic.m +++ /dev/null @@ -1,181 +0,0 @@ -// Plays a video from the command line in a window provided by mpv. -// You likely want to play the video in your own window instead, -// but that's not quite ready yet. -// You may need a basic Info.plist and MainMenu.xib to make this work. - -#include "../../libmpv/client.h" -#include "shared.h" - -#import - -@interface CocoaWindow : NSWindow -@end - -@implementation CocoaWindow -- (BOOL)canBecomeMainWindow { return YES; } -- (BOOL)canBecomeKeyWindow { return YES; } -@end - -@interface AppDelegate : NSObject -{ - mpv_handle *mpv; - dispatch_queue_t queue; - NSWindow *w; -} -@end - -static void wakeup(void *); - -@implementation AppDelegate - -- (void)createWindow { - - int mask = NSTitledWindowMask|NSClosableWindowMask| - NSMiniaturizableWindowMask|NSResizableWindowMask; - - self->w = [[CocoaWindow alloc] - initWithContentRect:NSMakeRect(0,0, 1280, 720) - styleMask:mask - backing:NSBackingStoreBuffered - defer:NO]; - - [self->w setTitle:@"cocoabasic example"]; - [self->w makeMainWindow]; - [self->w makeKeyAndOrderFront:nil]; - - NSMenu *m = [[NSMenu alloc] initWithTitle:@"AMainMenu"]; - NSMenuItem *item = [m addItemWithTitle:@"Apple" action:nil keyEquivalent:@""]; - NSMenu *sm = [[NSMenu alloc] initWithTitle:@"Apple"]; - [m setSubmenu:sm forItem:item]; - [sm addItemWithTitle: @"Shutdown mpv" action:@selector(shutdown) keyEquivalent:@"s"]; - [NSApp setMenu:m]; - [NSApp activateIgnoringOtherApps:YES]; -} - -- (void) applicationDidFinishLaunching:(NSNotification *)notification { - [NSApp setActivationPolicy:NSApplicationActivationPolicyRegular]; - atexit_b(^{ - // Because activation policy has just been set to behave like a real - // application, that policy must be reset on exit to prevent, among - // other things, the menubar created here from remaining on screen. - [NSApp setActivationPolicy:NSApplicationActivationPolicyProhibited]; - }); - - // Read filename - NSArray *args = [NSProcessInfo processInfo].arguments; - if (args.count < 2) { - NSLog(@"Expected filename on command line"); - exit(1); - } - NSString *filename = args[1]; - - [self createWindow]; - - // Deal with MPV in the background. - queue = dispatch_queue_create("mpv", DISPATCH_QUEUE_SERIAL); - dispatch_async(queue, ^{ - - mpv = mpv_create(); - if (!mpv) { - printf("failed creating context\n"); - exit(1); - } - - int64_t wid = (intptr_t) [self->w contentView]; - check_error(mpv_set_option(mpv, "wid", MPV_FORMAT_INT64, &wid)); - - // Maybe set some options here, like default key bindings. - // NOTE: Interaction with the window seems to be broken for now. - check_error(mpv_set_option_string(mpv, "input-default-bindings", "yes")); - - // for testing! - check_error(mpv_set_option_string(mpv, "input-media-keys", "yes")); - check_error(mpv_set_option_string(mpv, "input-cursor", "no")); - check_error(mpv_set_option_string(mpv, "input-vo-keyboard", "yes")); - - // request important errors - check_error(mpv_request_log_messages(mpv, "warn")); - - check_error(mpv_initialize(mpv)); - - // Register to be woken up whenever mpv generates new events. - mpv_set_wakeup_callback(mpv, wakeup, (__bridge void *) self); - - // Load the indicated file - const char *cmd[] = {"loadfile", filename.UTF8String, NULL}; - check_error(mpv_command(mpv, cmd)); - }); -} - -- (void) handleEvent:(mpv_event *)event -{ - switch (event->event_id) { - case MPV_EVENT_SHUTDOWN: - // Clean up and shut down. - mpv_terminate_destroy(mpv); - mpv = NULL; - dispatch_async(dispatch_get_main_queue(), ^{ - [[NSApplication sharedApplication] terminate:nil]; - }); - break; - - case MPV_EVENT_LOG_MESSAGE: { - struct mpv_event_log_message *msg = (struct mpv_event_log_message *)event->data; - printf("[%s] %s: %s", msg->prefix, msg->level, msg->text); - } - - case MPV_EVENT_VIDEO_RECONFIG: - dispatch_async(dispatch_get_main_queue(), ^{ - [self->w selectNextKeyView:nil]; - }); - - default: - printf("event: %s\n", mpv_event_name(event->event_id)); - } -} - -- (void) readEvents -{ - dispatch_async(queue, ^{ - while (mpv) { - mpv_event *event = mpv_wait_event(mpv, 0); - if (event->event_id == MPV_EVENT_NONE) - break; - [self handleEvent:event]; - } - }); -} - -static void wakeup(void *context) { - AppDelegate *a = (__bridge AppDelegate *) context; - [a readEvents]; -} - -// Ostensibly, mpv's window would be hooked up to this. -- (BOOL) windowShouldClose:(id)sender -{ - [self shutdown]; - if (self->w) - [self->w release]; - return YES; -} - -- (void) shutdown -{ - if (mpv) { - const char *args[] = {"quit", NULL}; - mpv_command(mpv, args); - } -} -@end - -// Delete this if you already have a main.m. -int main(int argc, const char * argv[]) { - @autoreleasepool { - NSApplication *app = [NSApplication sharedApplication]; - AppDelegate *delegate = [AppDelegate new]; - app.delegate = delegate; - [app run]; - } - return EXIT_SUCCESS; -} diff --git a/DOCS/client_api_examples/qt/qtexample.cpp b/DOCS/client_api_examples/qt/qtexample.cpp new file mode 100644 index 0000000000..756c206916 --- /dev/null +++ b/DOCS/client_api_examples/qt/qtexample.cpp @@ -0,0 +1,216 @@ +// This example can be built with: qmake && make + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#if QT_VERSION >= 0x050000 +#include +#endif + +#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; + emit mainwindow->mpv_events(); +} + +MainWindow::MainWindow(QWidget *parent) : + QMainWindow(parent) +{ + setWindowTitle("Qt embedding demo"); + setMinimumSize(640, 480); + + 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, &QAction::triggered, this, &MainWindow::on_file_open); + menu->addAction(on_open); + + statusBar(); + + QMainWindow *log_window = new QMainWindow(this); + log = new QTextEdit(log_window); + log->setReadOnly(true); + log_window->setCentralWidget(log); + log_window->setWindowTitle("mpv log window"); + log_window->setMinimumSize(500, 50); + log_window->show(); + + mpv = mpv_create(); + if (!mpv) + throw std::runtime_error("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. Works on: X11, win32, Cocoa + mpv_container = new QWidget(this); + setCentralWidget(mpv_container); + mpv_container->setAttribute(Qt::WA_DontCreateNativeAncestors); + mpv_container->setAttribute(Qt::WA_NativeWindow); + // If you have a HWND, use: int64_t wid = (intptr_t)hwnd; + 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"); + + // Enable keyboard input on the X11 window. For the messy details, see + // --input-vo-keyboard on the manpage. + mpv_set_option_string(mpv, "input-vo-keyboard", "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); + + mpv_observe_property(mpv, 0, "track-list", MPV_FORMAT_NODE); + mpv_observe_property(mpv, 0, "chapter-list", MPV_FORMAT_NODE); + + // Request log messages with level "info" or higher. + // They are received as MPV_EVENT_LOG_MESSAGE. + mpv_request_log_messages(mpv, "info"); + + // From this point on, the wakeup function will be called. The callback + // can come from any thread, so we use the QueuedConnection mechanism to + // relay the wakeup in a thread-safe way. + connect(this, &MainWindow::mpv_events, this, &MainWindow::on_mpv_events, + Qt::QueuedConnection); + mpv_set_wakeup_callback(mpv, wakeup, this); + + if (mpv_initialize(mpv) < 0) + throw std::runtime_error("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(""); + } + } else if (strcmp(prop->name, "chapter-list") == 0 || + strcmp(prop->name, "track-list") == 0) + { + // Dump the properties as JSON for demo purposes. +#if QT_VERSION >= 0x050000 + if (prop->format == MPV_FORMAT_NODE) { + QVariant v = mpv::qt::node_to_variant((mpv_node *)prop->data); + // Abuse JSON support for easily printing the mpv_node contents. + QJsonDocument d = QJsonDocument::fromVariant(v); + append_log("Change property " + QString(prop->name) + ":\n"); + append_log(d.toJson().data()); + } +#endif + } + break; + } + case MPV_EVENT_VIDEO_RECONFIG: { + // Retrieve the new video size. + int64_t w, h; + if (mpv_get_property(mpv, "dwidth", MPV_FORMAT_INT64, &w) >= 0 && + mpv_get_property(mpv, "dheight", MPV_FORMAT_INT64, &h) >= 0 && + w > 0 && h > 0) + { + // Note that the MPV_EVENT_VIDEO_RECONFIG event doesn't necessarily + // imply a resize, and you should check yourself if the video + // dimensions really changed. + // mpv itself will scale/letter box the video to the container size + // if the video doesn't fit. + std::stringstream ss; + ss << "Reconfig: " << w << " " << h; + statusBar()->showMessage(QString::fromStdString(ss.str())); + } + break; + } + case MPV_EVENT_LOG_MESSAGE: { + struct mpv_event_log_message *msg = (struct mpv_event_log_message *)event->data; + std::stringstream ss; + ss << "[" << msg->prefix << "] " << msg->level << ": " << msg->text; + append_log(QString::fromStdString(ss.str())); + break; + } + case MPV_EVENT_SHUTDOWN: { + mpv_terminate_destroy(mpv); + mpv = NULL; + break; + } + default: ; + // Ignore uninteresting or unknown events. + } +} + +// This slot is invoked by wakeup() (through the mpv_events signal). +void MainWindow::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 MainWindow::on_file_open() +{ + QString filename = QFileDialog::getOpenFileName(this, "Open file"); + if (mpv) { + const QByteArray c_filename = filename.toUtf8(); + const char *args[] = {"loadfile", c_filename.data(), NULL}; + mpv_command_async(mpv, 0, args); + } +} + +void MainWindow::append_log(const QString &text) +{ + QTextCursor cursor = log->textCursor(); + cursor.movePosition(QTextCursor::End); + cursor.insertText(text); + log->setTextCursor(cursor); +} + +MainWindow::~MainWindow() +{ + if (mpv) + mpv_terminate_destroy(mpv); +} + +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. + std::setlocale(LC_NUMERIC, "C"); + + MainWindow w; + w.show(); + + return a.exec(); +} diff --git a/DOCS/client_api_examples/qt/qtexample.h b/DOCS/client_api_examples/qt/qtexample.h new file mode 100644 index 0000000000..f59738cfbd --- /dev/null +++ b/DOCS/client_api_examples/qt/qtexample.h @@ -0,0 +1,36 @@ +#ifndef QTEXAMPLE_H +#define QTEXAMPLE_H + +#include + +#include + +class QTextEdit; + +class MainWindow : public QMainWindow +{ + Q_OBJECT + +public: + explicit MainWindow(QWidget *parent = 0); + ~MainWindow(); + +private slots: + void on_file_open(); + void on_mpv_events(); + +signals: + void mpv_events(); + +private: + QWidget *mpv_container; + mpv_handle *mpv; + QTextEdit *log; + + void append_log(const QString &text); + + void create_player(); + void handle_mpv_event(mpv_event *event); +}; + +#endif // QTEXAMPLE_H diff --git a/DOCS/client_api_examples/qt/qtexample.pro b/DOCS/client_api_examples/qt/qtexample.pro new file mode 100644 index 0000000000..40a4d964e2 --- /dev/null +++ b/DOCS/client_api_examples/qt/qtexample.pro @@ -0,0 +1,12 @@ +QT += core gui + +greaterThan(QT_MAJOR_VERSION, 4): QT += widgets + +TARGET = qtexample +TEMPLATE = app + +CONFIG += link_pkgconfig debug +PKGCONFIG += mpv + +SOURCES += qtexample.cpp +HEADERS += qtexample.h diff --git a/DOCS/client_api_examples/qtexample.cpp b/DOCS/client_api_examples/qtexample.cpp deleted file mode 100644 index 756c206916..0000000000 --- a/DOCS/client_api_examples/qtexample.cpp +++ /dev/null @@ -1,216 +0,0 @@ -// This example can be built with: qmake && make - -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include - -#if QT_VERSION >= 0x050000 -#include -#endif - -#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; - emit mainwindow->mpv_events(); -} - -MainWindow::MainWindow(QWidget *parent) : - QMainWindow(parent) -{ - setWindowTitle("Qt embedding demo"); - setMinimumSize(640, 480); - - 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, &QAction::triggered, this, &MainWindow::on_file_open); - menu->addAction(on_open); - - statusBar(); - - QMainWindow *log_window = new QMainWindow(this); - log = new QTextEdit(log_window); - log->setReadOnly(true); - log_window->setCentralWidget(log); - log_window->setWindowTitle("mpv log window"); - log_window->setMinimumSize(500, 50); - log_window->show(); - - mpv = mpv_create(); - if (!mpv) - throw std::runtime_error("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. Works on: X11, win32, Cocoa - mpv_container = new QWidget(this); - setCentralWidget(mpv_container); - mpv_container->setAttribute(Qt::WA_DontCreateNativeAncestors); - mpv_container->setAttribute(Qt::WA_NativeWindow); - // If you have a HWND, use: int64_t wid = (intptr_t)hwnd; - 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"); - - // Enable keyboard input on the X11 window. For the messy details, see - // --input-vo-keyboard on the manpage. - mpv_set_option_string(mpv, "input-vo-keyboard", "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); - - mpv_observe_property(mpv, 0, "track-list", MPV_FORMAT_NODE); - mpv_observe_property(mpv, 0, "chapter-list", MPV_FORMAT_NODE); - - // Request log messages with level "info" or higher. - // They are received as MPV_EVENT_LOG_MESSAGE. - mpv_request_log_messages(mpv, "info"); - - // From this point on, the wakeup function will be called. The callback - // can come from any thread, so we use the QueuedConnection mechanism to - // relay the wakeup in a thread-safe way. - connect(this, &MainWindow::mpv_events, this, &MainWindow::on_mpv_events, - Qt::QueuedConnection); - mpv_set_wakeup_callback(mpv, wakeup, this); - - if (mpv_initialize(mpv) < 0) - throw std::runtime_error("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(""); - } - } else if (strcmp(prop->name, "chapter-list") == 0 || - strcmp(prop->name, "track-list") == 0) - { - // Dump the properties as JSON for demo purposes. -#if QT_VERSION >= 0x050000 - if (prop->format == MPV_FORMAT_NODE) { - QVariant v = mpv::qt::node_to_variant((mpv_node *)prop->data); - // Abuse JSON support for easily printing the mpv_node contents. - QJsonDocument d = QJsonDocument::fromVariant(v); - append_log("Change property " + QString(prop->name) + ":\n"); - append_log(d.toJson().data()); - } -#endif - } - break; - } - case MPV_EVENT_VIDEO_RECONFIG: { - // Retrieve the new video size. - int64_t w, h; - if (mpv_get_property(mpv, "dwidth", MPV_FORMAT_INT64, &w) >= 0 && - mpv_get_property(mpv, "dheight", MPV_FORMAT_INT64, &h) >= 0 && - w > 0 && h > 0) - { - // Note that the MPV_EVENT_VIDEO_RECONFIG event doesn't necessarily - // imply a resize, and you should check yourself if the video - // dimensions really changed. - // mpv itself will scale/letter box the video to the container size - // if the video doesn't fit. - std::stringstream ss; - ss << "Reconfig: " << w << " " << h; - statusBar()->showMessage(QString::fromStdString(ss.str())); - } - break; - } - case MPV_EVENT_LOG_MESSAGE: { - struct mpv_event_log_message *msg = (struct mpv_event_log_message *)event->data; - std::stringstream ss; - ss << "[" << msg->prefix << "] " << msg->level << ": " << msg->text; - append_log(QString::fromStdString(ss.str())); - break; - } - case MPV_EVENT_SHUTDOWN: { - mpv_terminate_destroy(mpv); - mpv = NULL; - break; - } - default: ; - // Ignore uninteresting or unknown events. - } -} - -// This slot is invoked by wakeup() (through the mpv_events signal). -void MainWindow::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 MainWindow::on_file_open() -{ - QString filename = QFileDialog::getOpenFileName(this, "Open file"); - if (mpv) { - const QByteArray c_filename = filename.toUtf8(); - const char *args[] = {"loadfile", c_filename.data(), NULL}; - mpv_command_async(mpv, 0, args); - } -} - -void MainWindow::append_log(const QString &text) -{ - QTextCursor cursor = log->textCursor(); - cursor.movePosition(QTextCursor::End); - cursor.insertText(text); - log->setTextCursor(cursor); -} - -MainWindow::~MainWindow() -{ - if (mpv) - mpv_terminate_destroy(mpv); -} - -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. - std::setlocale(LC_NUMERIC, "C"); - - MainWindow w; - w.show(); - - return a.exec(); -} diff --git a/DOCS/client_api_examples/qtexample.h b/DOCS/client_api_examples/qtexample.h deleted file mode 100644 index f59738cfbd..0000000000 --- a/DOCS/client_api_examples/qtexample.h +++ /dev/null @@ -1,36 +0,0 @@ -#ifndef QTEXAMPLE_H -#define QTEXAMPLE_H - -#include - -#include - -class QTextEdit; - -class MainWindow : public QMainWindow -{ - Q_OBJECT - -public: - explicit MainWindow(QWidget *parent = 0); - ~MainWindow(); - -private slots: - void on_file_open(); - void on_mpv_events(); - -signals: - void mpv_events(); - -private: - QWidget *mpv_container; - mpv_handle *mpv; - QTextEdit *log; - - void append_log(const QString &text); - - 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 deleted file mode 100644 index 40a4d964e2..0000000000 --- a/DOCS/client_api_examples/qtexample.pro +++ /dev/null @@ -1,12 +0,0 @@ -QT += core gui - -greaterThan(QT_MAJOR_VERSION, 4): QT += widgets - -TARGET = qtexample -TEMPLATE = app - -CONFIG += link_pkgconfig debug -PKGCONFIG += mpv - -SOURCES += qtexample.cpp -HEADERS += qtexample.h diff --git a/DOCS/client_api_examples/shared.h b/DOCS/client_api_examples/shared.h deleted file mode 100644 index 027c03f1e9..0000000000 --- a/DOCS/client_api_examples/shared.h +++ /dev/null @@ -1,10 +0,0 @@ -#include -#include - -static inline void check_error(int status) -{ - if (status < 0) { - printf("mpv API error: %s\n", mpv_error_string(status)); - exit(1); - } -} diff --git a/DOCS/client_api_examples/simple.c b/DOCS/client_api_examples/simple.c deleted file mode 100644 index 6dbc40506a..0000000000 --- a/DOCS/client_api_examples/simple.c +++ /dev/null @@ -1,45 +0,0 @@ -#include -#include -#include - -#include "libmpv/client.h" -#include "shared.h" - -int main(int argc, char *argv[]) -{ - if (argc != 2) { - printf("pass a single media file as argument\n"); - return 1; - } - - mpv_handle *ctx = mpv_create(); - if (!ctx) { - printf("failed creating context\n"); - return 1; - } - - // Enable default key bindings, so the user can actually interact with - // the player (and e.g. close the window). - check_error(mpv_set_option_string(ctx, "input-default-bindings", "yes")); - mpv_set_option_string(ctx, "input-x11-keyboard", "yes"); - int val = 1; - check_error(mpv_set_option(ctx, "osc", MPV_FORMAT_FLAG, &val)); - - // Done setting up options. - check_error(mpv_initialize(ctx)); - - // Play this file. - const char *cmd[] = {"loadfile", argv[1], NULL}; - check_error(mpv_command(ctx, cmd)); - - // Let it play, and wait until the user quits. - while (1) { - mpv_event *event = mpv_wait_event(ctx, 10000); - printf("event: %s\n", mpv_event_name(event->event_id)); - if (event->event_id == MPV_EVENT_SHUTDOWN) - break; - } - - mpv_terminate_destroy(ctx); - return 0; -} diff --git a/DOCS/client_api_examples/simple/simple.c b/DOCS/client_api_examples/simple/simple.c new file mode 100644 index 0000000000..62fc9e8061 --- /dev/null +++ b/DOCS/client_api_examples/simple/simple.c @@ -0,0 +1,52 @@ +#include +#include +#include + +#include "libmpv/client.h" + +static inline void check_error(int status) +{ + if (status < 0) { + printf("mpv API error: %s\n", mpv_error_string(status)); + exit(1); + } +} + +int main(int argc, char *argv[]) +{ + if (argc != 2) { + printf("pass a single media file as argument\n"); + return 1; + } + + mpv_handle *ctx = mpv_create(); + if (!ctx) { + printf("failed creating context\n"); + return 1; + } + + // Enable default key bindings, so the user can actually interact with + // the player (and e.g. close the window). + check_error(mpv_set_option_string(ctx, "input-default-bindings", "yes")); + mpv_set_option_string(ctx, "input-x11-keyboard", "yes"); + int val = 1; + check_error(mpv_set_option(ctx, "osc", MPV_FORMAT_FLAG, &val)); + + // Done setting up options. + check_error(mpv_initialize(ctx)); + + // Play this file. + const char *cmd[] = {"loadfile", argv[1], NULL}; + check_error(mpv_command(ctx, cmd)); + + // Let it play, and wait until the user quits. + while (1) { + mpv_event *event = mpv_wait_event(ctx, 10000); + printf("event: %s\n", mpv_event_name(event->event_id)); + if (event->event_id == MPV_EVENT_SHUTDOWN) + break; + } + + mpv_terminate_destroy(ctx); + return 0; +} diff --git a/wscript_build.py b/wscript_build.py index 1791dacbae..694a65b038 100644 --- a/wscript_build.py +++ b/wscript_build.py @@ -524,8 +524,8 @@ def build(ctx): if ctx.dependency_satisfied('client-api-examples'): # This assumes all examples are single-file (as examples should be) examples_sources = [ - ( "simple.c" ), - ( "cocoabasic.m", "cocoa" ), + ( "simple/simple.c" ), + ( "cocoa/cocoabasic.m", "cocoa" ), ] for source in ctx.filtered_sources(examples_sources): -- cgit v1.2.3