From 2a80cb53921bb7a511c7aa15ee880bebd8c030c1 Mon Sep 17 00:00:00 2001 From: feiyangqingyun Date: Thu, 27 Aug 2020 15:38:51 +0800 Subject: [PATCH] =?UTF-8?q?=E6=96=B0=E5=A2=9E=E8=A7=86=E9=A2=91=E7=9B=91?= =?UTF-8?q?=E6=8E=A7=E5=86=85=E6=A0=B8mpv?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- QWidgetDemo.pro | 2 + README.md | 1 + ffmpegdemo/ffmpeg/ffmpeg.cpp | 26 +- ffmpegdemo/ffmpeg/ffmpeg.h | 5 +- ffmpegdemo/ffmpeg/ffmpeg.pri | 6 +- ffmpegdemo/ffmpeg/ffmpeghead.h | 19 +- ffmpegdemo/readme.txt | 10 +- ffmpegdemo/widget.cpp | 11 +- ffmpegdemo/widget.ui | 2 +- mpvdemo/main.cpp | 43 + mpvdemo/mpv/include/client.h | 1979 +++++++++++++++++++++++++++++ mpvdemo/mpv/include/opengl_cb.h | 339 +++++ mpvdemo/mpv/include/render.h | 626 +++++++++ mpvdemo/mpv/include/render_gl.h | 216 ++++ mpvdemo/mpv/include/stream_cb.h | 240 ++++ mpvdemo/mpv/include64/client.h | 1979 +++++++++++++++++++++++++++++ mpvdemo/mpv/include64/opengl_cb.h | 339 +++++ mpvdemo/mpv/include64/render.h | 626 +++++++++ mpvdemo/mpv/include64/render_gl.h | 216 ++++ mpvdemo/mpv/include64/stream_cb.h | 240 ++++ mpvdemo/mpv/mpv.cpp | 209 +++ mpvdemo/mpv/mpv.h | 85 ++ mpvdemo/mpv/mpv.pri | 22 + mpvdemo/mpv/mpvhead.h | 26 + mpvdemo/mpv/mpvtool.h | 373 ++++++ mpvdemo/mpv/winlib/libmpv.lib | Bin 0 -> 761570 bytes mpvdemo/mpv/winlib64/libmpv.lib | Bin 0 -> 742344 bytes mpvdemo/mpvdemo.pro | 19 + mpvdemo/readme.txt | 9 + mpvdemo/widget.cpp | 37 + mpvdemo/widget.h | 24 + mpvdemo/widget.ui | 63 + vlcdemo/readme.txt | 10 +- vlcdemo/vlc/vlc.cpp | 26 +- vlcdemo/vlc/vlc.h | 4 +- vlcdemo/vlc/vlc.pri | 6 +- vlcdemo/vlc/vlchead.h | 49 +- vlcdemo/widget.cpp | 11 +- vlcdemo/widget.ui | 2 +- 39 files changed, 7790 insertions(+), 110 deletions(-) create mode 100644 mpvdemo/main.cpp create mode 100644 mpvdemo/mpv/include/client.h create mode 100644 mpvdemo/mpv/include/opengl_cb.h create mode 100644 mpvdemo/mpv/include/render.h create mode 100644 mpvdemo/mpv/include/render_gl.h create mode 100644 mpvdemo/mpv/include/stream_cb.h create mode 100644 mpvdemo/mpv/include64/client.h create mode 100644 mpvdemo/mpv/include64/opengl_cb.h create mode 100644 mpvdemo/mpv/include64/render.h create mode 100644 mpvdemo/mpv/include64/render_gl.h create mode 100644 mpvdemo/mpv/include64/stream_cb.h create mode 100644 mpvdemo/mpv/mpv.cpp create mode 100644 mpvdemo/mpv/mpv.h create mode 100644 mpvdemo/mpv/mpv.pri create mode 100644 mpvdemo/mpv/mpvhead.h create mode 100644 mpvdemo/mpv/mpvtool.h create mode 100644 mpvdemo/mpv/winlib/libmpv.lib create mode 100644 mpvdemo/mpv/winlib64/libmpv.lib create mode 100644 mpvdemo/mpvdemo.pro create mode 100644 mpvdemo/readme.txt create mode 100644 mpvdemo/widget.cpp create mode 100644 mpvdemo/widget.h create mode 100644 mpvdemo/widget.ui diff --git a/QWidgetDemo.pro b/QWidgetDemo.pro index 8387152..d451da7 100644 --- a/QWidgetDemo.pro +++ b/QWidgetDemo.pro @@ -40,6 +40,8 @@ SUBDIRS += imageswitch #图片开关控件 #SUBDIRS += ffmpegdemo #视频流播放ffmpeg内核 #vlcdemo默认提供的win的lib,如果是win可以自行打开 #SUBDIRS += vlcdemo #视频流播放vlc内核 +#mpvdemo默认提供的win的lib,如果是win可以自行打开 +#SUBDIRS += mpvdemo #视频流播放mpv内核 #designer项目只支持Qt4,如果是Qt4可以自行打开 #SUBDIRS += designer #QtDesigner4源码 diff --git a/README.md b/README.md index 3999b64..0cf782e 100644 --- a/README.md +++ b/README.md @@ -44,6 +44,7 @@ | 37 | live | 程序守护进程 | | 38 | designer | QtDesigner4源码 | | 39 | netserver | 网络中转服务器 | +| 40 | mpvdemo | 视频流播放mpv内核 | ### 二、学习群 1. **Qt交流大会群 853086607(雨田哥)** diff --git a/ffmpegdemo/ffmpeg/ffmpeg.cpp b/ffmpegdemo/ffmpeg/ffmpeg.cpp index fdf33a1..f87275e 100644 --- a/ffmpegdemo/ffmpeg/ffmpeg.cpp +++ b/ffmpegdemo/ffmpeg/ffmpeg.cpp @@ -1,9 +1,5 @@ #include "ffmpeg.h" -#pragma execution_character_set("utf-8") -#define TIMEMS qPrintable(QTime::currentTime().toString("HH:mm:ss zzz")) -#define STRDATETIME qPrintable(QDateTime::currentDateTime().toString("yyyy-MM-dd-HH-mm-ss")) - FFmpegThread::FFmpegThread(QObject *parent) : QThread(parent) { setObjectName("FFmpegThread"); @@ -360,8 +356,8 @@ void FFmpegThread::stop() //实时视频显示窗体类 FFmpegWidget::FFmpegWidget(QWidget * parent) : QWidget(parent) { - ffmpeg = new FFmpegThread(this); - connect(ffmpeg, SIGNAL(receiveImage(QImage)), this, SLOT(updateImage(QImage))); + thread = new FFmpegThread(this); + connect(thread, SIGNAL(receiveImage(QImage)), this, SLOT(updateImage(QImage))); image = QImage(); } @@ -390,7 +386,7 @@ void FFmpegWidget::updateImage(const QImage &image) void FFmpegWidget::setUrl(const QString &url) { - ffmpeg->setUrl(url); + thread->setUrl(url); } void FFmpegWidget::open() @@ -398,27 +394,27 @@ void FFmpegWidget::open() //qDebug() << TIMEMS << "open video" << objectName(); clear(); - ffmpeg->play(); - ffmpeg->start(); + thread->play(); + thread->start(); } void FFmpegWidget::pause() { - ffmpeg->pause(); + thread->pause(); } void FFmpegWidget::next() { - ffmpeg->next(); + thread->next(); } void FFmpegWidget::close() { //qDebug() << TIMEMS << "close video" << objectName(); - if (ffmpeg->isRunning()) { - ffmpeg->stop(); - ffmpeg->quit(); - ffmpeg->wait(500); + if (thread->isRunning()) { + thread->stop(); + thread->quit(); + thread->wait(500); } QTimer::singleShot(1, this, SLOT(clear())); diff --git a/ffmpegdemo/ffmpeg/ffmpeg.h b/ffmpegdemo/ffmpeg/ffmpeg.h index 2af2c8a..c0b192b 100644 --- a/ffmpegdemo/ffmpeg/ffmpeg.h +++ b/ffmpegdemo/ffmpeg/ffmpeg.h @@ -7,7 +7,6 @@ #endif #include "ffmpeghead.h" -class FFmpegWidget; class FFmpegThread : public QThread { @@ -81,8 +80,8 @@ protected: void paintEvent(QPaintEvent *); private: - FFmpegThread *ffmpeg; //实时视频采集对象 - QImage image; //要显示的图片 + FFmpegThread *thread; + QImage image; private slots: //接收图像并绘制 diff --git a/ffmpegdemo/ffmpeg/ffmpeg.pri b/ffmpegdemo/ffmpeg/ffmpeg.pri index 4de0580..c626b0f 100644 --- a/ffmpegdemo/ffmpeg/ffmpeg.pri +++ b/ffmpegdemo/ffmpeg/ffmpeg.pri @@ -1,6 +1,6 @@ -HEADERS += $$PWD/ffmpeghead.h -HEADERS += $$PWD/ffmpeg.h -SOURCES += $$PWD/ffmpeg.cpp +HEADERS += $$PWD/ffmpeghead.h +HEADERS += $$PWD/ffmpeg.h +SOURCES += $$PWD/ffmpeg.cpp #如果用的是ffmpeg4内核请将ffmpeg3改成ffmpeg4,两种内核不兼容,头文件也不一样 DEFINES += ffmpeg3 diff --git a/ffmpegdemo/ffmpeg/ffmpeghead.h b/ffmpegdemo/ffmpeg/ffmpeghead.h index fadfb6c..5c77ad5 100644 --- a/ffmpegdemo/ffmpeg/ffmpeghead.h +++ b/ffmpegdemo/ffmpeg/ffmpeghead.h @@ -1,4 +1,7 @@ -//必须加以下内容,否则编译不能通过,为了兼容C和C99标准 +#ifndef FFMPEGHEAD_H +#define FFMPEGHEAD_H + +//必须加以下内容,否则编译不能通过,为了兼容C和C99标准 #ifndef INT64_C #define INT64_C #define UINT64_C @@ -15,6 +18,7 @@ extern "C" { #include "libavutil/ffversion.h" #include "libavcodec/avcodec.h" #include "libswscale/swscale.h" +#include "libswresample/swresample.h" #include "libavformat/avformat.h" #include "libavfilter/avfilter.h" @@ -27,3 +31,16 @@ extern "C" { #include "libavutil/hwcontext_qsv.h" #endif } + +#include "qdatetime.h" +#pragma execution_character_set("utf-8") + +#define TIMEMS qPrintable(QTime::currentTime().toString("HH:mm:ss zzz")) +#define TIME qPrintable(QTime::currentTime().toString("HH:mm:ss")) +#define QDATE qPrintable(QDate::currentDate().toString("yyyy-MM-dd")) +#define QTIME qPrintable(QTime::currentTime().toString("HH-mm-ss")) +#define DATETIME qPrintable(QDateTime::currentDateTime().toString("yyyy-MM-dd HH:mm:ss")) +#define STRDATETIME qPrintable(QDateTime::currentDateTime().toString("yyyy-MM-dd-HH-mm-ss")) +#define STRDATETIMEMS qPrintable(QDateTime::currentDateTime().toString("yyyy-MM-dd-HH-mm-ss-zzz")) + +#endif // FFMPEGHEAD_H diff --git a/ffmpegdemo/readme.txt b/ffmpegdemo/readme.txt index 032811d..e2ebc3e 100644 --- a/ffmpegdemo/readme.txt +++ b/ffmpegdemo/readme.txt @@ -6,12 +6,4 @@ 2. վ㣺[https://gitee.com/feiyangqingyun](https://gitee.com/feiyangqingyun) 3. վ㣺[https://github.com/feiyangqingyun](https://github.com/feiyangqingyun) 4. ҳ[https://blog.csdn.net/feiyangqingyun](https://blog.csdn.net/feiyangqingyun) -5. ֪ҳ[https://www.zhihu.com/people/feiyangqingyun/](https://www.zhihu.com/people/feiyangqingyun/) - -1. ߳ʵʱ -2. ͬʱƵƵ -3. ֧Qt汾ϵͳ -4. ʹ룬չǿ -5. ѡffmpeg3ffmpeg4汾 -6. ѡ32λ64λffmpeg -7. ע;ϸ \ No newline at end of file +5. ֪ҳ[https://www.zhihu.com/people/feiyangqingyun/](https://www.zhihu.com/people/feiyangqingyun/) \ No newline at end of file diff --git a/ffmpegdemo/widget.cpp b/ffmpegdemo/widget.cpp index fae6d11..de3dd74 100644 --- a/ffmpegdemo/widget.cpp +++ b/ffmpegdemo/widget.cpp @@ -2,9 +2,7 @@ #include "widget.h" #include "ui_widget.h" -Widget::Widget(QWidget *parent) - : QWidget(parent) - , ui(new Ui::Widget) +Widget::Widget(QWidget *parent) : QWidget(parent), ui(new Ui::Widget) { ui->setupUi(this); @@ -17,6 +15,7 @@ Widget::Widget(QWidget *parent) urls << "rtsp://192.168.1.247:554/av0_0"; urls << "rtsp://192.168.1.247:554/av0_1"; ui->cboxUrl->addItems(urls); + ui->cboxUrl->setCurrentIndex(5); } Widget::~Widget() @@ -29,10 +28,10 @@ void Widget::on_btnOpen_clicked() if (ui->btnOpen->text() == "打开") { ui->btnOpen->setText("关闭"); QString url = ui->cboxUrl->currentText().trimmed(); - ui->ffmpegWidget->setUrl(url); - ui->ffmpegWidget->open(); + ui->playWidget->setUrl(url); + ui->playWidget->open(); } else { ui->btnOpen->setText("打开"); - ui->ffmpegWidget->close(); + ui->playWidget->close(); } } diff --git a/ffmpegdemo/widget.ui b/ffmpegdemo/widget.ui index 6b75261..8caf843 100644 --- a/ffmpegdemo/widget.ui +++ b/ffmpegdemo/widget.ui @@ -15,7 +15,7 @@ - + 0 diff --git a/mpvdemo/main.cpp b/mpvdemo/main.cpp new file mode 100644 index 0000000..82336ab --- /dev/null +++ b/mpvdemo/main.cpp @@ -0,0 +1,43 @@ +#pragma execution_character_set("utf-8") +#include "widget.h" + +#include +#include +#include + +int main(int argc, char *argv[]) +{ + QApplication a(argc, argv); + + QFont font; + font.setFamily("MicroSoft Yahei"); + font.setPixelSize(12); + a.setFont(font); + +#if (QT_VERSION <= QT_VERSION_CHECK(5,0,0)) +#if _MSC_VER + QTextCodec *codec = QTextCodec::codecForName("gbk"); +#else + QTextCodec *codec = QTextCodec::codecForName("utf-8"); +#endif + QTextCodec::setCodecForLocale(codec); + QTextCodec::setCodecForCStrings(codec); + QTextCodec::setCodecForTr(codec); +#else + QTextCodec *codec = QTextCodec::codecForName("utf-8"); + QTextCodec::setCodecForLocale(codec); +#endif + + Widget w; + w.setWindowTitle("视频流播放mpv内核 (QQ: 517216493)"); + w.show(); + + //居中显示窗体 + QDesktopWidget deskWidget; + int deskWidth = deskWidget.availableGeometry().width(); + int deskHeight = deskWidget.availableGeometry().height(); + QPoint movePoint(deskWidth / 2 - w.width() / 2, deskHeight / 2 - w.height() / 2); + w.move(movePoint); + + return a.exec(); +} diff --git a/mpvdemo/mpv/include/client.h b/mpvdemo/mpv/include/client.h new file mode 100644 index 0000000..ce880e1 --- /dev/null +++ b/mpvdemo/mpv/include/client.h @@ -0,0 +1,1979 @@ +/* Copyright (C) 2017 the mpv developers + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/* + * Note: the client API is licensed under ISC (see above) to enable + * other wrappers outside of mpv. But keep in mind that the + * mpv core is by default still GPLv2+ - unless built with + * --enable-lgpl, which makes it LGPLv2+. + */ + +#ifndef MPV_CLIENT_API_H_ +#define MPV_CLIENT_API_H_ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * Mechanisms provided by this API + * ------------------------------- + * + * This API provides general control over mpv playback. It does not give you + * direct access to individual components of the player, only the whole thing. + * It's somewhat equivalent to MPlayer's slave mode. You can send commands, + * retrieve or set playback status or settings with properties, and receive + * events. + * + * The API can be used in two ways: + * 1) Internally in mpv, to provide additional features to the command line + * player. Lua scripting uses this. (Currently there is no plugin API to + * get a client API handle in external user code. It has to be a fixed + * part of the player at compilation time.) + * 2) Using mpv as a library with mpv_create(). This basically allows embedding + * mpv in other applications. + * + * Documentation + * ------------- + * + * The libmpv C API is documented directly in this header. Note that most + * actual interaction with this player is done through + * options/commands/properties, which can be accessed through this API. + * Essentially everything is done with them, including loading a file, + * retrieving playback progress, and so on. + * + * These are documented elsewhere: + * * http://mpv.io/manual/master/#options + * * http://mpv.io/manual/master/#list-of-input-commands + * * http://mpv.io/manual/master/#properties + * + * You can also look at the examples here: + * * https://github.com/mpv-player/mpv-examples/tree/master/libmpv + * + * Event loop + * ---------- + * + * In general, the API user should run an event loop in order to receive events. + * This event loop should call mpv_wait_event(), which will return once a new + * mpv client API is available. It is also possible to integrate client API + * usage in other event loops (e.g. GUI toolkits) with the + * mpv_set_wakeup_callback() function, and then polling for events by calling + * mpv_wait_event() with a 0 timeout. + * + * Note that the event loop is detached from the actual player. Not calling + * mpv_wait_event() will not stop playback. It will eventually congest the + * event queue of your API handle, though. + * + * Synchronous vs. asynchronous calls + * ---------------------------------- + * + * The API allows both synchronous and asynchronous calls. Synchronous calls + * have to wait until the playback core is ready, which currently can take + * an unbounded time (e.g. if network is slow or unresponsive). Asynchronous + * calls just queue operations as requests, and return the result of the + * operation as events. + * + * Asynchronous calls + * ------------------ + * + * The client API includes asynchronous functions. These allow you to send + * requests instantly, and get replies as events at a later point. The + * requests are made with functions carrying the _async suffix, and replies + * are returned by mpv_wait_event() (interleaved with the normal event stream). + * + * A 64 bit userdata value is used to allow the user to associate requests + * with replies. The value is passed as reply_userdata parameter to the request + * function. The reply to the request will have the reply + * mpv_event->reply_userdata field set to the same value as the + * reply_userdata parameter of the corresponding request. + * + * This userdata value is arbitrary and is never interpreted by the API. Note + * that the userdata value 0 is also allowed, but then the client must be + * careful not accidentally interpret the mpv_event->reply_userdata if an + * event is not a reply. (For non-replies, this field is set to 0.) + * + * Asynchronous calls may be reordered in arbitrarily with other synchronous + * and asynchronous calls. If you want a guaranteed order, you need to wait + * until asynchronous calls report completion before doing the next call. + * + * See also the section "Asynchronous command details" in the manpage. + * + * Multithreading + * -------------- + * + * The client API is generally fully thread-safe, unless otherwise noted. + * Currently, there is no real advantage in using more than 1 thread to access + * the client API, since everything is serialized through a single lock in the + * playback core. + * + * Basic environment requirements + * ------------------------------ + * + * This documents basic requirements on the C environment. This is especially + * important if mpv is used as library with mpv_create(). + * + * - The LC_NUMERIC locale category must be set to "C". If your program calls + * setlocale(), be sure not to use LC_ALL, or if you do, reset LC_NUMERIC + * to its sane default: setlocale(LC_NUMERIC, "C"). + * - If a X11 based VO is used, mpv will set the xlib error handler. This error + * handler is process-wide, and there's no proper way to share it with other + * xlib users within the same process. This might confuse GUI toolkits. + * - mpv uses some other libraries that are not library-safe, such as Fribidi + * (used through libass), ALSA, FFmpeg, and possibly more. + * - The FPU precision must be set at least to double precision. + * - On Windows, mpv will call timeBeginPeriod(1). + * - On memory exhaustion, mpv will kill the process. + * - In certain cases, mpv may start sub processes (such as with the ytdl + * wrapper script). + * - Using UNIX IPC (off by default) will override the SIGPIPE signal handler, + * and set it to SIG_IGN. + * - mpv will reseed the legacy C random number generator by calling srand() at + * some random point once. + * - mpv may start sub processes, so overriding SIGCHLD, or waiting on all PIDs + * (such as calling wait()) by the parent process or any other library within + * the process must be avoided. libmpv itself only waits for its own PIDs. + * + * Encoding of filenames + * --------------------- + * + * mpv uses UTF-8 everywhere. + * + * On some platforms (like Linux), filenames actually do not have to be UTF-8; + * for this reason libmpv supports non-UTF-8 strings. libmpv uses what the + * kernel uses and does not recode filenames. At least on Linux, passing a + * string to libmpv is like passing a string to the fopen() function. + * + * On Windows, filenames are always UTF-8, libmpv converts between UTF-8 and + * UTF-16 when using win32 API functions. libmpv never uses or accepts + * filenames in the local 8 bit encoding. It does not use fopen() either; + * it uses _wfopen(). + * + * On OS X, filenames and other strings taken/returned by libmpv can have + * inconsistent unicode normalization. This can sometimes lead to problems. + * You have to hope for the best. + * + * Also see the remarks for MPV_FORMAT_STRING. + * + * Embedding the video window + * -------------------------- + * + * Using the render API (in render_cb.h) is recommended. This API requires + * you to create and maintain an OpenGL context, to which you can render + * video using a specific API call. This API does not include keyboard or mouse + * input directly. + * + * There is an older way to embed the native mpv window into your own. You have + * to get the raw window handle, and set it as "wid" option. This works on X11, + * win32, and OSX only. It's much easier to use than the render API, but + * also has various problems. + * + * Also see client API examples and the mpv manpage. There is an extensive + * discussion here: + * https://github.com/mpv-player/mpv-examples/tree/master/libmpv#methods-of-embedding-the-video-window + * + * Compatibility + * ------------- + * + * mpv development doesn't stand still, and changes to mpv internals as well as + * to its interface can cause compatibility issues to client API users. + * + * The API is versioned (see MPV_CLIENT_API_VERSION), and changes to it are + * documented in DOCS/client-api-changes.rst. The C API itself will probably + * remain compatible for a long time, but the functionality exposed by it + * could change more rapidly. For example, it's possible that options are + * renamed, or change the set of allowed values. + * + * Defensive programming should be used to potentially deal with the fact that + * options, commands, and properties could disappear, change their value range, + * or change the underlying datatypes. It might be a good idea to prefer + * MPV_FORMAT_STRING over other types to decouple your code from potential + * mpv changes. + * + * Also see: DOCS/compatibility.rst + * + * Future changes + * -------------- + * + * This are the planned changes that will most likely be done on the next major + * bump of the library: + * + * - remove all symbols and include files that are marked as deprecated + * - reassign enum numerical values to remove gaps + * - remove the mpv_opengl_init_params.extra_exts field + * - change the type of mpv_event_end_file.reason + * - disabling all events by default + */ + +/** + * The version is incremented on each API change. The 16 lower bits form the + * minor version number, and the 16 higher bits the major version number. If + * the API becomes incompatible to previous versions, the major version + * number is incremented. This affects only C part, and not properties and + * options. + * + * Every API bump is described in DOCS/client-api-changes.rst + * + * You can use MPV_MAKE_VERSION() and compare the result with integer + * relational operators (<, >, <=, >=). + */ +#define MPV_MAKE_VERSION(major, minor) (((major) << 16) | (minor) | 0UL) +#define MPV_CLIENT_API_VERSION MPV_MAKE_VERSION(1, 107) + +/** + * The API user is allowed to "#define MPV_ENABLE_DEPRECATED 0" before + * including any libmpv headers. Then deprecated symbols will be excluded + * from the headers. (Of course, deprecated properties and commands and + * other functionality will still work.) + */ +#ifndef MPV_ENABLE_DEPRECATED +#define MPV_ENABLE_DEPRECATED 1 +#endif + +/** + * Return the MPV_CLIENT_API_VERSION the mpv source has been compiled with. + */ +unsigned long mpv_client_api_version(void); + +/** + * Client context used by the client API. Every client has its own private + * handle. + */ +typedef struct mpv_handle mpv_handle; + +/** + * List of error codes than can be returned by API functions. 0 and positive + * return values always mean success, negative values are always errors. + */ +typedef enum mpv_error { + /** + * No error happened (used to signal successful operation). + * Keep in mind that many API functions returning error codes can also + * return positive values, which also indicate success. API users can + * hardcode the fact that ">= 0" means success. + */ + MPV_ERROR_SUCCESS = 0, + /** + * The event ringbuffer is full. This means the client is choked, and can't + * receive any events. This can happen when too many asynchronous requests + * have been made, but not answered. Probably never happens in practice, + * unless the mpv core is frozen for some reason, and the client keeps + * making asynchronous requests. (Bugs in the client API implementation + * could also trigger this, e.g. if events become "lost".) + */ + MPV_ERROR_EVENT_QUEUE_FULL = -1, + /** + * Memory allocation failed. + */ + MPV_ERROR_NOMEM = -2, + /** + * The mpv core wasn't configured and initialized yet. See the notes in + * mpv_create(). + */ + MPV_ERROR_UNINITIALIZED = -3, + /** + * Generic catch-all error if a parameter is set to an invalid or + * unsupported value. This is used if there is no better error code. + */ + MPV_ERROR_INVALID_PARAMETER = -4, + /** + * Trying to set an option that doesn't exist. + */ + MPV_ERROR_OPTION_NOT_FOUND = -5, + /** + * Trying to set an option using an unsupported MPV_FORMAT. + */ + MPV_ERROR_OPTION_FORMAT = -6, + /** + * Setting the option failed. Typically this happens if the provided option + * value could not be parsed. + */ + MPV_ERROR_OPTION_ERROR = -7, + /** + * The accessed property doesn't exist. + */ + MPV_ERROR_PROPERTY_NOT_FOUND = -8, + /** + * Trying to set or get a property using an unsupported MPV_FORMAT. + */ + MPV_ERROR_PROPERTY_FORMAT = -9, + /** + * The property exists, but is not available. This usually happens when the + * associated subsystem is not active, e.g. querying audio parameters while + * audio is disabled. + */ + MPV_ERROR_PROPERTY_UNAVAILABLE = -10, + /** + * Error setting or getting a property. + */ + MPV_ERROR_PROPERTY_ERROR = -11, + /** + * General error when running a command with mpv_command and similar. + */ + MPV_ERROR_COMMAND = -12, + /** + * Generic error on loading (usually used with mpv_event_end_file.error). + */ + MPV_ERROR_LOADING_FAILED = -13, + /** + * Initializing the audio output failed. + */ + MPV_ERROR_AO_INIT_FAILED = -14, + /** + * Initializing the video output failed. + */ + MPV_ERROR_VO_INIT_FAILED = -15, + /** + * There was no audio or video data to play. This also happens if the + * file was recognized, but did not contain any audio or video streams, + * or no streams were selected. + */ + MPV_ERROR_NOTHING_TO_PLAY = -16, + /** + * When trying to load the file, the file format could not be determined, + * or the file was too broken to open it. + */ + MPV_ERROR_UNKNOWN_FORMAT = -17, + /** + * Generic error for signaling that certain system requirements are not + * fulfilled. + */ + MPV_ERROR_UNSUPPORTED = -18, + /** + * The API function which was called is a stub only. + */ + MPV_ERROR_NOT_IMPLEMENTED = -19, + /** + * Unspecified error. + */ + MPV_ERROR_GENERIC = -20 +} mpv_error; + +/** + * Return a string describing the error. For unknown errors, the string + * "unknown error" is returned. + * + * @param error error number, see enum mpv_error + * @return A static string describing the error. The string is completely + * static, i.e. doesn't need to be deallocated, and is valid forever. + */ +const char *mpv_error_string(int error); + +/** + * General function to deallocate memory returned by some of the API functions. + * Call this only if it's explicitly documented as allowed. Calling this on + * mpv memory not owned by the caller will lead to undefined behavior. + * + * @param data A valid pointer returned by the API, or NULL. + */ +void mpv_free(void *data); + +/** + * Return the name of this client handle. Every client has its own unique + * name, which is mostly used for user interface purposes. + * + * @return The client name. The string is read-only and is valid until the + * mpv_handle is destroyed. + */ +const char *mpv_client_name(mpv_handle *ctx); + +/** + * Create a new mpv instance and an associated client API handle to control + * the mpv instance. This instance is in a pre-initialized state, + * and needs to be initialized to be actually used with most other API + * functions. + * + * Some API functions will return MPV_ERROR_UNINITIALIZED in the uninitialized + * state. You can call mpv_set_property() (or mpv_set_property_string() and + * other variants, and before mpv 0.21.0 mpv_set_option() etc.) to set initial + * options. After this, call mpv_initialize() to start the player, and then use + * e.g. mpv_command() to start playback of a file. + * + * The point of separating handle creation and actual initialization is that + * you can configure things which can't be changed during runtime. + * + * Unlike the command line player, this will have initial settings suitable + * for embedding in applications. The following settings are different: + * - stdin/stdout/stderr and the terminal will never be accessed. This is + * equivalent to setting the --no-terminal option. + * (Technically, this also suppresses C signal handling.) + * - No config files will be loaded. This is roughly equivalent to using + * --config=no. Since libmpv 1.15, you can actually re-enable this option, + * which will make libmpv load config files during mpv_initialize(). If you + * do this, you are strongly encouraged to set the "config-dir" option too. + * (Otherwise it will load the mpv command line player's config.) + * For example: + * mpv_set_option_string(mpv, "config-dir", "/my/path"); // set config root + * mpv_set_option_string(mpv, "config", "yes"); // enable config loading + * (call mpv_initialize() _after_ this) + * - Idle mode is enabled, which means the playback core will enter idle mode + * if there are no more files to play on the internal playlist, instead of + * exiting. This is equivalent to the --idle option. + * - Disable parts of input handling. + * - Most of the different settings can be viewed with the command line player + * by running "mpv --show-profile=libmpv". + * + * All this assumes that API users want a mpv instance that is strictly + * isolated from the command line player's configuration, user settings, and + * so on. You can re-enable disabled features by setting the appropriate + * options. + * + * The mpv command line parser is not available through this API, but you can + * set individual options with mpv_set_property(). Files for playback must be + * loaded with mpv_command() or others. + * + * Note that you should avoid doing concurrent accesses on the uninitialized + * client handle. (Whether concurrent access is definitely allowed or not has + * yet to be decided.) + * + * @return a new mpv client API handle. Returns NULL on error. Currently, this + * can happen in the following situations: + * - out of memory + * - LC_NUMERIC is not set to "C" (see general remarks) + */ +mpv_handle *mpv_create(void); + +/** + * Initialize an uninitialized mpv instance. If the mpv instance is already + * running, an error is returned. + * + * This function needs to be called to make full use of the client API if the + * client API handle was created with mpv_create(). + * + * Only the following options are required to be set _before_ mpv_initialize(): + * - options which are only read at initialization time: + * - config + * - config-dir + * - input-conf + * - load-scripts + * - script + * - player-operation-mode + * - input-app-events (OSX) + * - all encoding mode options + * + * @return error code + */ +int mpv_initialize(mpv_handle *ctx); + +/** + * Disconnect and destroy the mpv_handle. ctx will be deallocated with this + * API call. + * + * If the last mpv_handle is detached, the core player is destroyed. In + * addition, if there are only weak mpv_handles (such as created by + * mpv_create_weak_client() or internal scripts), these mpv_handles will + * be sent MPV_EVENT_SHUTDOWN. This function may block until these clients + * have responded to the shutdown event, and the core is finally destroyed. + */ +void mpv_destroy(mpv_handle *ctx); + +#if MPV_ENABLE_DEPRECATED +/** + * @deprecated use mpv_destroy(), which has exactly the same semantics (the + * deprecation is a mere rename) + * + * Since mpv client API version 1.29: + * If the last mpv_handle is detached, the core player is destroyed. In + * addition, if there are only weak mpv_handles (such as created by + * mpv_create_weak_client() or internal scripts), these mpv_handles will + * be sent MPV_EVENT_SHUTDOWN. This function may block until these clients + * have responded to the shutdown event, and the core is finally destroyed. + * + * Before mpv client API version 1.29: + * This left the player running. If you want to be sure that the + * player is terminated, send a "quit" command, and wait until the + * MPV_EVENT_SHUTDOWN event is received, or use mpv_terminate_destroy(). + */ +void mpv_detach_destroy(mpv_handle *ctx); +#endif + +/** + * Similar to mpv_destroy(), but brings the player and all clients down + * as well, and waits until all of them are destroyed. This function blocks. The + * advantage over mpv_destroy() is that while mpv_destroy() merely + * detaches the client handle from the player, this function quits the player, + * waits until all other clients are destroyed (i.e. all mpv_handles are + * detached), and also waits for the final termination of the player. + * + * Since mpv_destroy() is called somewhere on the way, it's not safe to + * call other functions concurrently on the same context. + * + * Since mpv client API version 1.29: + * The first call on any mpv_handle will block until the core is destroyed. + * This means it will wait until other mpv_handle have been destroyed. If you + * want asynchronous destruction, just run the "quit" command, and then react + * to the MPV_EVENT_SHUTDOWN event. + * If another mpv_handle already called mpv_terminate_destroy(), this call will + * not actually block. It will destroy the mpv_handle, and exit immediately, + * while other mpv_handles might still be uninitializing. + * + * Before mpv client API version 1.29: + * If this is called on a mpv_handle that was not created with mpv_create(), + * this function will merely send a quit command and then call + * mpv_destroy(), without waiting for the actual shutdown. + */ +void mpv_terminate_destroy(mpv_handle *ctx); + +/** + * Create a new client handle connected to the same player core as ctx. This + * context has its own event queue, its own mpv_request_event() state, its own + * mpv_request_log_messages() state, its own set of observed properties, and + * its own state for asynchronous operations. Otherwise, everything is shared. + * + * This handle should be destroyed with mpv_destroy() if no longer + * needed. The core will live as long as there is at least 1 handle referencing + * it. Any handle can make the core quit, which will result in every handle + * receiving MPV_EVENT_SHUTDOWN. + * + * This function can not be called before the main handle was initialized with + * mpv_initialize(). The new handle is always initialized, unless ctx=NULL was + * passed. + * + * @param ctx Used to get the reference to the mpv core; handle-specific + * settings and parameters are not used. + * If NULL, this function behaves like mpv_create() (ignores name). + * @param name The client name. This will be returned by mpv_client_name(). If + * the name is already in use, or contains non-alphanumeric + * characters (other than '_'), the name is modified to fit. + * If NULL, an arbitrary name is automatically chosen. + * @return a new handle, or NULL on error + */ +mpv_handle *mpv_create_client(mpv_handle *ctx, const char *name); + +/** + * This is the same as mpv_create_client(), but the created mpv_handle is + * treated as a weak reference. If all mpv_handles referencing a core are + * weak references, the core is automatically destroyed. (This still goes + * through normal uninit of course. Effectively, if the last non-weak mpv_handle + * is destroyed, then the weak mpv_handles receive MPV_EVENT_SHUTDOWN and are + * asked to terminate as well.) + * + * Note if you want to use this like refcounting: you have to be aware that + * mpv_terminate_destroy() _and_ mpv_destroy() for the last non-weak + * mpv_handle will block until all weak mpv_handles are destroyed. + */ +mpv_handle *mpv_create_weak_client(mpv_handle *ctx, const char *name); + +/** + * Load a config file. This loads and parses the file, and sets every entry in + * the config file's default section as if mpv_set_option_string() is called. + * + * The filename should be an absolute path. If it isn't, the actual path used + * is unspecified. (Note: an absolute path starts with '/' on UNIX.) If the + * file wasn't found, MPV_ERROR_INVALID_PARAMETER is returned. + * + * If a fatal error happens when parsing a config file, MPV_ERROR_OPTION_ERROR + * is returned. Errors when setting options as well as other types or errors + * are ignored (even if options do not exist). You can still try to capture + * the resulting error messages with mpv_request_log_messages(). Note that it's + * possible that some options were successfully set even if any of these errors + * happen. + * + * @param filename absolute path to the config file on the local filesystem + * @return error code + */ +int mpv_load_config_file(mpv_handle *ctx, const char *filename); + +#if MPV_ENABLE_DEPRECATED + +/** + * This does nothing since mpv 0.23.0 (API version 1.24). Below is the + * description of the old behavior. + * + * Stop the playback thread. This means the core will stop doing anything, and + * only run and answer to client API requests. This is sometimes useful; for + * example, no new frame will be queued to the video output, so doing requests + * which have to wait on the video output can run instantly. + * + * Suspension is reentrant and recursive for convenience. Any thread can call + * the suspend function multiple times, and the playback thread will remain + * suspended until the last thread resumes it. Note that during suspension, all + * clients still have concurrent access to the core, which is serialized through + * a single mutex. + * + * Call mpv_resume() to resume the playback thread. You must call mpv_resume() + * for each mpv_suspend() call. Calling mpv_resume() more often than + * mpv_suspend() is not allowed. + * + * Calling this on an uninitialized player (see mpv_create()) will deadlock. + * + * @deprecated This function, as well as mpv_resume(), are deprecated, and + * will stop doing anything soon. Their semantics were never + * well-defined, and their usefulness is extremely limited. The + * calls will remain stubs in order to keep ABI compatibility. + */ +void mpv_suspend(mpv_handle *ctx); + +/** + * See mpv_suspend(). + */ +void mpv_resume(mpv_handle *ctx); + +#endif + +/** + * Return the internal time in microseconds. This has an arbitrary start offset, + * but will never wrap or go backwards. + * + * Note that this is always the real time, and doesn't necessarily have to do + * with playback time. For example, playback could go faster or slower due to + * playback speed, or due to playback being paused. Use the "time-pos" property + * instead to get the playback status. + * + * Unlike other libmpv APIs, this can be called at absolutely any time (even + * within wakeup callbacks), as long as the context is valid. + * + * Safe to be called from mpv render API threads. + */ +int64_t mpv_get_time_us(mpv_handle *ctx); + +/** + * Data format for options and properties. The API functions to get/set + * properties and options support multiple formats, and this enum describes + * them. + */ +typedef enum mpv_format { + /** + * Invalid. Sometimes used for empty values. + */ + MPV_FORMAT_NONE = 0, + /** + * The basic type is char*. It returns the raw property string, like + * using ${=property} in input.conf (see input.rst). + * + * NULL isn't an allowed value. + * + * Warning: although the encoding is usually UTF-8, this is not always the + * case. File tags often store strings in some legacy codepage, + * and even filenames don't necessarily have to be in UTF-8 (at + * least on Linux). If you pass the strings to code that requires + * valid UTF-8, you have to sanitize it in some way. + * On Windows, filenames are always UTF-8, and libmpv converts + * between UTF-8 and UTF-16 when using win32 API functions. See + * the "Encoding of filenames" section for details. + * + * Example for reading: + * + * char *result = NULL; + * if (mpv_get_property(ctx, "property", MPV_FORMAT_STRING, &result) < 0) + * goto error; + * printf("%s\n", result); + * mpv_free(result); + * + * Or just use mpv_get_property_string(). + * + * Example for writing: + * + * char *value = "the new value"; + * // yep, you pass the address to the variable + * // (needed for symmetry with other types and mpv_get_property) + * mpv_set_property(ctx, "property", MPV_FORMAT_STRING, &value); + * + * Or just use mpv_set_property_string(). + * + */ + MPV_FORMAT_STRING = 1, + /** + * The basic type is char*. It returns the OSD property string, like + * using ${property} in input.conf (see input.rst). In many cases, this + * is the same as the raw string, but in other cases it's formatted for + * display on OSD. It's intended to be human readable. Do not attempt to + * parse these strings. + * + * Only valid when doing read access. The rest works like MPV_FORMAT_STRING. + */ + MPV_FORMAT_OSD_STRING = 2, + /** + * The basic type is int. The only allowed values are 0 ("no") + * and 1 ("yes"). + * + * Example for reading: + * + * int result; + * if (mpv_get_property(ctx, "property", MPV_FORMAT_FLAG, &result) < 0) + * goto error; + * printf("%s\n", result ? "true" : "false"); + * + * Example for writing: + * + * int flag = 1; + * mpv_set_property(ctx, "property", MPV_FORMAT_FLAG, &flag); + */ + MPV_FORMAT_FLAG = 3, + /** + * The basic type is int64_t. + */ + MPV_FORMAT_INT64 = 4, + /** + * The basic type is double. + */ + MPV_FORMAT_DOUBLE = 5, + /** + * The type is mpv_node. + * + * For reading, you usually would pass a pointer to a stack-allocated + * mpv_node value to mpv, and when you're done you call + * mpv_free_node_contents(&node). + * You're expected not to write to the data - if you have to, copy it + * first (which you have to do manually). + * + * For writing, you construct your own mpv_node, and pass a pointer to the + * API. The API will never write to your data (and copy it if needed), so + * you're free to use any form of allocation or memory management you like. + * + * Warning: when reading, always check the mpv_node.format member. For + * example, properties might change their type in future versions + * of mpv, or sometimes even during runtime. + * + * Example for reading: + * + * mpv_node result; + * if (mpv_get_property(ctx, "property", MPV_FORMAT_NODE, &result) < 0) + * goto error; + * printf("format=%d\n", (int)result.format); + * mpv_free_node_contents(&result). + * + * Example for writing: + * + * mpv_node value; + * value.format = MPV_FORMAT_STRING; + * value.u.string = "hello"; + * mpv_set_property(ctx, "property", MPV_FORMAT_NODE, &value); + */ + MPV_FORMAT_NODE = 6, + /** + * Used with mpv_node only. Can usually not be used directly. + */ + MPV_FORMAT_NODE_ARRAY = 7, + /** + * See MPV_FORMAT_NODE_ARRAY. + */ + MPV_FORMAT_NODE_MAP = 8, + /** + * A raw, untyped byte array. Only used only with mpv_node, and only in + * some very special situations. (Currently, only for the screenshot-raw + * command.) + */ + MPV_FORMAT_BYTE_ARRAY = 9 +} mpv_format; + +/** + * Generic data storage. + * + * If mpv writes this struct (e.g. via mpv_get_property()), you must not change + * the data. In some cases (mpv_get_property()), you have to free it with + * mpv_free_node_contents(). If you fill this struct yourself, you're also + * responsible for freeing it, and you must not call mpv_free_node_contents(). + */ +typedef struct mpv_node { + union { + char *string; /** valid if format==MPV_FORMAT_STRING */ + int flag; /** valid if format==MPV_FORMAT_FLAG */ + int64_t int64; /** valid if format==MPV_FORMAT_INT64 */ + double double_; /** valid if format==MPV_FORMAT_DOUBLE */ + /** + * valid if format==MPV_FORMAT_NODE_ARRAY + * or if format==MPV_FORMAT_NODE_MAP + */ + struct mpv_node_list *list; + /** + * valid if format==MPV_FORMAT_BYTE_ARRAY + */ + struct mpv_byte_array *ba; + } u; + /** + * Type of the data stored in this struct. This value rules what members in + * the given union can be accessed. The following formats are currently + * defined to be allowed in mpv_node: + * + * MPV_FORMAT_STRING (u.string) + * MPV_FORMAT_FLAG (u.flag) + * MPV_FORMAT_INT64 (u.int64) + * MPV_FORMAT_DOUBLE (u.double_) + * MPV_FORMAT_NODE_ARRAY (u.list) + * MPV_FORMAT_NODE_MAP (u.list) + * MPV_FORMAT_BYTE_ARRAY (u.ba) + * MPV_FORMAT_NONE (no member) + * + * If you encounter a value you don't know, you must not make any + * assumptions about the contents of union u. + */ + mpv_format format; +} mpv_node; + +/** + * (see mpv_node) + */ +typedef struct mpv_node_list { + /** + * Number of entries. Negative values are not allowed. + */ + int num; + /** + * MPV_FORMAT_NODE_ARRAY: + * values[N] refers to value of the Nth item + * + * MPV_FORMAT_NODE_MAP: + * values[N] refers to value of the Nth key/value pair + * + * If num > 0, values[0] to values[num-1] (inclusive) are valid. + * Otherwise, this can be NULL. + */ + mpv_node *values; + /** + * MPV_FORMAT_NODE_ARRAY: + * unused (typically NULL), access is not allowed + * + * MPV_FORMAT_NODE_MAP: + * keys[N] refers to key of the Nth key/value pair. If num > 0, keys[0] to + * keys[num-1] (inclusive) are valid. Otherwise, this can be NULL. + * The keys are in random order. The only guarantee is that keys[N] belongs + * to the value values[N]. NULL keys are not allowed. + */ + char **keys; +} mpv_node_list; + +/** + * (see mpv_node) + */ +typedef struct mpv_byte_array { + /** + * Pointer to the data. In what format the data is stored is up to whatever + * uses MPV_FORMAT_BYTE_ARRAY. + */ + void *data; + /** + * Size of the data pointed to by ptr. + */ + size_t size; +} mpv_byte_array; + +/** + * Frees any data referenced by the node. It doesn't free the node itself. + * Call this only if the mpv client API set the node. If you constructed the + * node yourself (manually), you have to free it yourself. + * + * If node->format is MPV_FORMAT_NONE, this call does nothing. Likewise, if + * the client API sets a node with this format, this function doesn't need to + * be called. (This is just a clarification that there's no danger of anything + * strange happening in these cases.) + */ +void mpv_free_node_contents(mpv_node *node); + +/** + * Set an option. Note that you can't normally set options during runtime. It + * works in uninitialized state (see mpv_create()), and in some cases in at + * runtime. + * + * Using a format other than MPV_FORMAT_NODE is equivalent to constructing a + * mpv_node with the given format and data, and passing the mpv_node to this + * function. + * + * Note: this is semi-deprecated. For most purposes, this is not needed anymore. + * Starting with mpv version 0.21.0 (version 1.23) most options can be set + * with mpv_set_property() (and related functions), and even before + * mpv_initialize(). In some obscure corner cases, using this function + * to set options might still be required (see below, and also section + * "Inconsistencies between options and properties" on the manpage). Once + * these are resolved, the option setting functions might be fully + * deprecated. + * + * The following options still need to be set either _before_ + * mpv_initialize() with mpv_set_property() (or related functions), or + * with mpv_set_option() (or related functions) at any time: + * - options shadowed by deprecated properties: + * - demuxer (property deprecated in 0.21.0) + * - idle (property deprecated in 0.21.0) + * - fps (property deprecated in 0.21.0) + * - cache (property deprecated in 0.21.0) + * - length (property deprecated in 0.10.0) + * - audio-samplerate (property deprecated in 0.10.0) + * - audio-channels (property deprecated in 0.10.0) + * - audio-format (property deprecated in 0.10.0) + * - deprecated options shadowed by properties: + * - chapter (option deprecated in 0.21.0) + * - playlist-pos (option deprecated in 0.21.0) + * The deprecated properties were removed in mpv 0.23.0. + * + * @param name Option name. This is the same as on the mpv command line, but + * without the leading "--". + * @param format see enum mpv_format. + * @param[in] data Option value (according to the format). + * @return error code + */ +int mpv_set_option(mpv_handle *ctx, const char *name, mpv_format format, + void *data); + +/** + * Convenience function to set an option to a string value. This is like + * calling mpv_set_option() with MPV_FORMAT_STRING. + * + * @return error code + */ +int mpv_set_option_string(mpv_handle *ctx, const char *name, const char *data); + +/** + * Send a command to the player. Commands are the same as those used in + * input.conf, except that this function takes parameters in a pre-split + * form. + * + * The commands and their parameters are documented in input.rst. + * + * Does not use OSD and string expansion by default (unlike mpv_command_string() + * and input.conf). + * + * @param[in] args NULL-terminated list of strings. Usually, the first item + * is the command, and the following items are arguments. + * @return error code + */ +int mpv_command(mpv_handle *ctx, const char **args); + +/** + * Same as mpv_command(), but allows passing structured data in any format. + * In particular, calling mpv_command() is exactly like calling + * mpv_command_node() with the format set to MPV_FORMAT_NODE_ARRAY, and + * every arg passed in order as MPV_FORMAT_STRING. + * + * Does not use OSD and string expansion by default. + * + * The args argument can have one of the following formats: + * + * MPV_FORMAT_NODE_ARRAY: + * Positional arguments. Each entry is an argument using an arbitrary + * format (the format must be compatible to the used command). Usually, + * the first item is the command name (as MPV_FORMAT_STRING). The order + * of arguments is as documented in each command description. + * + * MPV_FORMAT_NODE_MAP: + * Named arguments. This requires at least an entry with the key "name" + * to be present, which must be a string, and contains the command name. + * The special entry "_flags" is optional, and if present, must be an + * array of strings, each being a command prefix to apply. All other + * entries are interpreted as arguments. They must use the argument names + * as documented in each command description. Some commands do not + * support named arguments at all, and must use MPV_FORMAT_NODE_ARRAY. + * + * @param[in] args mpv_node with format set to one of the values documented + * above (see there for details) + * @param[out] result Optional, pass NULL if unused. If not NULL, and if the + * function succeeds, this is set to command-specific return + * data. You must call mpv_free_node_contents() to free it + * (again, only if the command actually succeeds). + * Not many commands actually use this at all. + * @return error code (the result parameter is not set on error) + */ +int mpv_command_node(mpv_handle *ctx, mpv_node *args, mpv_node *result); + +/** + * This is essentially identical to mpv_command() but it also returns a result. + * + * Does not use OSD and string expansion by default. + * + * @param[in] args NULL-terminated list of strings. Usually, the first item + * is the command, and the following items are arguments. + * @param[out] result Optional, pass NULL if unused. If not NULL, and if the + * function succeeds, this is set to command-specific return + * data. You must call mpv_free_node_contents() to free it + * (again, only if the command actually succeeds). + * Not many commands actually use this at all. + * @return error code (the result parameter is not set on error) + */ +int mpv_command_ret(mpv_handle *ctx, const char **args, mpv_node *result); + +/** + * Same as mpv_command, but use input.conf parsing for splitting arguments. + * This is slightly simpler, but also more error prone, since arguments may + * need quoting/escaping. + * + * This also has OSD and string expansion enabled by default. + */ +int mpv_command_string(mpv_handle *ctx, const char *args); + +/** + * Same as mpv_command, but run the command asynchronously. + * + * Commands are executed asynchronously. You will receive a + * MPV_EVENT_COMMAND_REPLY event. This event will also have an + * error code set if running the command failed. For commands that + * return data, the data is put into mpv_event_command.result. + * + * The only case when you do not receive an event is when the function call + * itself fails. This happens only if parsing the command itself (or otherwise + * validating it) fails, i.e. the return code of the API call is not 0 or + * positive. + * + * Safe to be called from mpv render API threads. + * + * @param reply_userdata the value mpv_event.reply_userdata of the reply will + * be set to (see section about asynchronous calls) + * @param args NULL-terminated list of strings (see mpv_command()) + * @return error code (if parsing or queuing the command fails) + */ +int mpv_command_async(mpv_handle *ctx, uint64_t reply_userdata, + const char **args); + +/** + * Same as mpv_command_node(), but run it asynchronously. Basically, this + * function is to mpv_command_node() what mpv_command_async() is to + * mpv_command(). + * + * See mpv_command_async() for details. + * + * Safe to be called from mpv render API threads. + * + * @param reply_userdata the value mpv_event.reply_userdata of the reply will + * be set to (see section about asynchronous calls) + * @param args as in mpv_command_node() + * @return error code (if parsing or queuing the command fails) + */ +int mpv_command_node_async(mpv_handle *ctx, uint64_t reply_userdata, + mpv_node *args); + +/** + * Signal to all async requests with the matching ID to abort. This affects + * the following API calls: + * + * mpv_command_async + * mpv_command_node_async + * + * All of these functions take a reply_userdata parameter. This API function + * tells all requests with the matching reply_userdata value to try to return + * as soon as possible. If there are multiple requests with matching ID, it + * aborts all of them. + * + * This API function is mostly asynchronous itself. It will not wait until the + * command is aborted. Instead, the command will terminate as usual, but with + * some work not done. How this is signaled depends on the specific command (for + * example, the "subprocess" command will indicate it by "killed_by_us" set to + * true in the result). How long it takes also depends on the situation. The + * aborting process is completely asynchronous. + * + * Not all commands may support this functionality. In this case, this function + * will have no effect. The same is true if the request using the passed + * reply_userdata has already terminated, has not been started yet, or was + * never in use at all. + * + * You have to be careful of race conditions: the time during which the abort + * request will be effective is _after_ e.g. mpv_command_async() has returned, + * and before the command has signaled completion with MPV_EVENT_COMMAND_REPLY. + * + * @param reply_userdata ID of the request to be aborted (see above) + */ +void mpv_abort_async_command(mpv_handle *ctx, uint64_t reply_userdata); + +/** + * Set a property to a given value. Properties are essentially variables which + * can be queried or set at runtime. For example, writing to the pause property + * will actually pause or unpause playback. + * + * If the format doesn't match with the internal format of the property, access + * usually will fail with MPV_ERROR_PROPERTY_FORMAT. In some cases, the data + * is automatically converted and access succeeds. For example, MPV_FORMAT_INT64 + * is always converted to MPV_FORMAT_DOUBLE, and access using MPV_FORMAT_STRING + * usually invokes a string parser. The same happens when calling this function + * with MPV_FORMAT_NODE: the underlying format may be converted to another + * type if possible. + * + * Using a format other than MPV_FORMAT_NODE is equivalent to constructing a + * mpv_node with the given format and data, and passing the mpv_node to this + * function. (Before API version 1.21, this was different.) + * + * Note: starting with mpv 0.21.0 (client API version 1.23), this can be used to + * set options in general. It even can be used before mpv_initialize() + * has been called. If called before mpv_initialize(), setting properties + * not backed by options will result in MPV_ERROR_PROPERTY_UNAVAILABLE. + * In some cases, properties and options still conflict. In these cases, + * mpv_set_property() accesses the options before mpv_initialize(), and + * the properties after mpv_initialize(). These conflicts will be removed + * in mpv 0.23.0. See mpv_set_option() for further remarks. + * + * @param name The property name. See input.rst for a list of properties. + * @param format see enum mpv_format. + * @param[in] data Option value. + * @return error code + */ +int mpv_set_property(mpv_handle *ctx, const char *name, mpv_format format, + void *data); + +/** + * Convenience function to set a property to a string value. + * + * This is like calling mpv_set_property() with MPV_FORMAT_STRING. + */ +int mpv_set_property_string(mpv_handle *ctx, const char *name, const char *data); + +/** + * Set a property asynchronously. You will receive the result of the operation + * as MPV_EVENT_SET_PROPERTY_REPLY event. The mpv_event.error field will contain + * the result status of the operation. Otherwise, this function is similar to + * mpv_set_property(). + * + * Safe to be called from mpv render API threads. + * + * @param reply_userdata see section about asynchronous calls + * @param name The property name. + * @param format see enum mpv_format. + * @param[in] data Option value. The value will be copied by the function. It + * will never be modified by the client API. + * @return error code if sending the request failed + */ +int mpv_set_property_async(mpv_handle *ctx, uint64_t reply_userdata, + const char *name, mpv_format format, void *data); + +/** + * Read the value of the given property. + * + * If the format doesn't match with the internal format of the property, access + * usually will fail with MPV_ERROR_PROPERTY_FORMAT. In some cases, the data + * is automatically converted and access succeeds. For example, MPV_FORMAT_INT64 + * is always converted to MPV_FORMAT_DOUBLE, and access using MPV_FORMAT_STRING + * usually invokes a string formatter. + * + * @param name The property name. + * @param format see enum mpv_format. + * @param[out] data Pointer to the variable holding the option value. On + * success, the variable will be set to a copy of the option + * value. For formats that require dynamic memory allocation, + * you can free the value with mpv_free() (strings) or + * mpv_free_node_contents() (MPV_FORMAT_NODE). + * @return error code + */ +int mpv_get_property(mpv_handle *ctx, const char *name, mpv_format format, + void *data); + +/** + * Return the value of the property with the given name as string. This is + * equivalent to mpv_get_property() with MPV_FORMAT_STRING. + * + * See MPV_FORMAT_STRING for character encoding issues. + * + * On error, NULL is returned. Use mpv_get_property() if you want fine-grained + * error reporting. + * + * @param name The property name. + * @return Property value, or NULL if the property can't be retrieved. Free + * the string with mpv_free(). + */ +char *mpv_get_property_string(mpv_handle *ctx, const char *name); + +/** + * Return the property as "OSD" formatted string. This is the same as + * mpv_get_property_string, but using MPV_FORMAT_OSD_STRING. + * + * @return Property value, or NULL if the property can't be retrieved. Free + * the string with mpv_free(). + */ +char *mpv_get_property_osd_string(mpv_handle *ctx, const char *name); + +/** + * Get a property asynchronously. You will receive the result of the operation + * as well as the property data with the MPV_EVENT_GET_PROPERTY_REPLY event. + * You should check the mpv_event.error field on the reply event. + * + * Safe to be called from mpv render API threads. + * + * @param reply_userdata see section about asynchronous calls + * @param name The property name. + * @param format see enum mpv_format. + * @return error code if sending the request failed + */ +int mpv_get_property_async(mpv_handle *ctx, uint64_t reply_userdata, + const char *name, mpv_format format); + +/** + * Get a notification whenever the given property changes. You will receive + * updates as MPV_EVENT_PROPERTY_CHANGE. Note that this is not very precise: + * for some properties, it may not send updates even if the property changed. + * This depends on the property, and it's a valid feature request to ask for + * better update handling of a specific property. (For some properties, like + * ``clock``, which shows the wall clock, this mechanism doesn't make too + * much sense anyway.) + * + * Property changes are coalesced: the change events are returned only once the + * event queue becomes empty (e.g. mpv_wait_event() would block or return + * MPV_EVENT_NONE), and then only one event per changed property is returned. + * + * You always get an initial change notification. This is meant to initialize + * the user's state to the current value of the property. + * + * Normally, change events are sent only if the property value changes according + * to the requested format. mpv_event_property will contain the property value + * as data member. + * + * Warning: if a property is unavailable or retrieving it caused an error, + * MPV_FORMAT_NONE will be set in mpv_event_property, even if the + * format parameter was set to a different value. In this case, the + * mpv_event_property.data field is invalid. + * + * If the property is observed with the format parameter set to MPV_FORMAT_NONE, + * you get low-level notifications whether the property _may_ have changed, and + * the data member in mpv_event_property will be unset. With this mode, you + * will have to determine yourself whether the property really changed. On the + * other hand, this mechanism can be faster and uses less resources. + * + * Observing a property that doesn't exist is allowed. (Although it may still + * cause some sporadic change events.) + * + * Keep in mind that you will get change notifications even if you change a + * property yourself. Try to avoid endless feedback loops, which could happen + * if you react to the change notifications triggered by your own change. + * + * Only the mpv_handle on which this was called will receive the property + * change events, or can unobserve them. + * + * Safe to be called from mpv render API threads. + * + * @param reply_userdata This will be used for the mpv_event.reply_userdata + * field for the received MPV_EVENT_PROPERTY_CHANGE + * events. (Also see section about asynchronous calls, + * although this function is somewhat different from + * actual asynchronous calls.) + * If you have no use for this, pass 0. + * Also see mpv_unobserve_property(). + * @param name The property name. + * @param format see enum mpv_format. Can be MPV_FORMAT_NONE to omit values + * from the change events. + * @return error code (usually fails only on OOM or unsupported format) + */ +int mpv_observe_property(mpv_handle *mpv, uint64_t reply_userdata, + const char *name, mpv_format format); + +/** + * Undo mpv_observe_property(). This will remove all observed properties for + * which the given number was passed as reply_userdata to mpv_observe_property. + * + * Safe to be called from mpv render API threads. + * + * @param registered_reply_userdata ID that was passed to mpv_observe_property + * @return negative value is an error code, >=0 is number of removed properties + * on success (includes the case when 0 were removed) + */ +int mpv_unobserve_property(mpv_handle *mpv, uint64_t registered_reply_userdata); + +typedef enum mpv_event_id { + /** + * Nothing happened. Happens on timeouts or sporadic wakeups. + */ + MPV_EVENT_NONE = 0, + /** + * Happens when the player quits. The player enters a state where it tries + * to disconnect all clients. Most requests to the player will fail, and + * the client should react to this and quit with mpv_destroy() as soon as + * possible. + */ + MPV_EVENT_SHUTDOWN = 1, + /** + * See mpv_request_log_messages(). + */ + MPV_EVENT_LOG_MESSAGE = 2, + /** + * Reply to a mpv_get_property_async() request. + * See also mpv_event and mpv_event_property. + */ + MPV_EVENT_GET_PROPERTY_REPLY = 3, + /** + * Reply to a mpv_set_property_async() request. + * (Unlike MPV_EVENT_GET_PROPERTY, mpv_event_property is not used.) + */ + MPV_EVENT_SET_PROPERTY_REPLY = 4, + /** + * Reply to a mpv_command_async() or mpv_command_node_async() request. + * See also mpv_event and mpv_event_command. + */ + MPV_EVENT_COMMAND_REPLY = 5, + /** + * Notification before playback start of a file (before the file is loaded). + */ + MPV_EVENT_START_FILE = 6, + /** + * Notification after playback end (after the file was unloaded). + * See also mpv_event and mpv_event_end_file. + */ + MPV_EVENT_END_FILE = 7, + /** + * Notification when the file has been loaded (headers were read etc.), and + * decoding starts. + */ + MPV_EVENT_FILE_LOADED = 8, +#if MPV_ENABLE_DEPRECATED + /** + * The list of video/audio/subtitle tracks was changed. (E.g. a new track + * was found. This doesn't necessarily indicate a track switch; for this, + * MPV_EVENT_TRACK_SWITCHED is used.) + * + * @deprecated This is equivalent to using mpv_observe_property() on the + * "track-list" property. The event is redundant, and might + * be removed in the far future. + */ + MPV_EVENT_TRACKS_CHANGED = 9, + /** + * A video/audio/subtitle track was switched on or off. + * + * @deprecated This is equivalent to using mpv_observe_property() on the + * "vid", "aid", and "sid" properties. The event is redundant, + * and might be removed in the far future. + */ + MPV_EVENT_TRACK_SWITCHED = 10, +#endif + /** + * Idle mode was entered. In this mode, no file is played, and the playback + * core waits for new commands. (The command line player normally quits + * instead of entering idle mode, unless --idle was specified. If mpv + * was started with mpv_create(), idle mode is enabled by default.) + */ + MPV_EVENT_IDLE = 11, +#if MPV_ENABLE_DEPRECATED + /** + * Playback was paused. This indicates the user pause state. + * + * The user pause state is the state the user requested (changed with the + * "pause" property). There is an internal pause state too, which is entered + * if e.g. the network is too slow (the "core-idle" property generally + * indicates whether the core is playing or waiting). + * + * This event is sent whenever any pause states change, not only the user + * state. You might get multiple events in a row while these states change + * independently. But the event ID sent always indicates the user pause + * state. + * + * If you don't want to deal with this, use mpv_observe_property() on the + * "pause" property and ignore MPV_EVENT_PAUSE/UNPAUSE. Likewise, the + * "core-idle" property tells you whether video is actually playing or not. + * + * @deprecated The event is redundant with mpv_observe_property() as + * mentioned above, and might be removed in the far future. + */ + MPV_EVENT_PAUSE = 12, + /** + * Playback was unpaused. See MPV_EVENT_PAUSE for not so obvious details. + * + * @deprecated The event is redundant with mpv_observe_property() as + * explained in the MPV_EVENT_PAUSE comments, and might be + * removed in the far future. + */ + MPV_EVENT_UNPAUSE = 13, + /** + * Sent every time after a video frame is displayed. Note that currently, + * this will be sent in lower frequency if there is no video, or playback + * is paused - but that will be removed in the future, and it will be + * restricted to video frames only. + * + * @deprecated Use mpv_observe_property() with relevant properties instead + * (such as "playback-time"). + */ + MPV_EVENT_TICK = 14, + /** + * @deprecated This was used internally with the internal "script_dispatch" + * command to dispatch keyboard and mouse input for the OSC. + * It was never useful in general and has been completely + * replaced with "script-binding". + * This event never happens anymore, and is included in this + * header only for compatibility. + */ + MPV_EVENT_SCRIPT_INPUT_DISPATCH = 15, +#endif + /** + * Triggered by the script-message input command. The command uses the + * first argument of the command as client name (see mpv_client_name()) to + * dispatch the message, and passes along all arguments starting from the + * second argument as strings. + * See also mpv_event and mpv_event_client_message. + */ + MPV_EVENT_CLIENT_MESSAGE = 16, + /** + * Happens after video changed in some way. This can happen on resolution + * changes, pixel format changes, or video filter changes. The event is + * sent after the video filters and the VO are reconfigured. Applications + * embedding a mpv window should listen to this event in order to resize + * the window if needed. + * Note that this event can happen sporadically, and you should check + * yourself whether the video parameters really changed before doing + * something expensive. + */ + MPV_EVENT_VIDEO_RECONFIG = 17, + /** + * Similar to MPV_EVENT_VIDEO_RECONFIG. This is relatively uninteresting, + * because there is no such thing as audio output embedding. + */ + MPV_EVENT_AUDIO_RECONFIG = 18, +#if MPV_ENABLE_DEPRECATED + /** + * Happens when metadata (like file tags) is possibly updated. (It's left + * unspecified whether this happens on file start or only when it changes + * within a file.) + * + * @deprecated This is equivalent to using mpv_observe_property() on the + * "metadata" property. The event is redundant, and might + * be removed in the far future. + */ + MPV_EVENT_METADATA_UPDATE = 19, +#endif + /** + * Happens when a seek was initiated. Playback stops. Usually it will + * resume with MPV_EVENT_PLAYBACK_RESTART as soon as the seek is finished. + */ + MPV_EVENT_SEEK = 20, + /** + * There was a discontinuity of some sort (like a seek), and playback + * was reinitialized. Usually happens after seeking, or ordered chapter + * segment switches. The main purpose is allowing the client to detect + * when a seek request is finished. + */ + MPV_EVENT_PLAYBACK_RESTART = 21, + /** + * Event sent due to mpv_observe_property(). + * See also mpv_event and mpv_event_property. + */ + MPV_EVENT_PROPERTY_CHANGE = 22, +#if MPV_ENABLE_DEPRECATED + /** + * Happens when the current chapter changes. + * + * @deprecated This is equivalent to using mpv_observe_property() on the + * "chapter" property. The event is redundant, and might + * be removed in the far future. + */ + MPV_EVENT_CHAPTER_CHANGE = 23, +#endif + /** + * Happens if the internal per-mpv_handle ringbuffer overflows, and at + * least 1 event had to be dropped. This can happen if the client doesn't + * read the event queue quickly enough with mpv_wait_event(), or if the + * client makes a very large number of asynchronous calls at once. + * + * Event delivery will continue normally once this event was returned + * (this forces the client to empty the queue completely). + */ + MPV_EVENT_QUEUE_OVERFLOW = 24, + /** + * Triggered if a hook handler was registered with mpv_hook_add(), and the + * hook is invoked. If you receive this, you must handle it, and continue + * the hook with mpv_hook_continue(). + * See also mpv_event and mpv_event_hook. + */ + MPV_EVENT_HOOK = 25, + // Internal note: adjust INTERNAL_EVENT_BASE when adding new events. +} mpv_event_id; + +/** + * Return a string describing the event. For unknown events, NULL is returned. + * + * Note that all events actually returned by the API will also yield a non-NULL + * string with this function. + * + * @param event event ID, see see enum mpv_event_id + * @return A static string giving a short symbolic name of the event. It + * consists of lower-case alphanumeric characters and can include "-" + * characters. This string is suitable for use in e.g. scripting + * interfaces. + * The string is completely static, i.e. doesn't need to be deallocated, + * and is valid forever. + */ +const char *mpv_event_name(mpv_event_id event); + +typedef struct mpv_event_property { + /** + * Name of the property. + */ + const char *name; + /** + * Format of the data field in the same struct. See enum mpv_format. + * This is always the same format as the requested format, except when + * the property could not be retrieved (unavailable, or an error happened), + * in which case the format is MPV_FORMAT_NONE. + */ + mpv_format format; + /** + * Received property value. Depends on the format. This is like the + * pointer argument passed to mpv_get_property(). + * + * For example, for MPV_FORMAT_STRING you get the string with: + * + * char *value = *(char **)(event_property->data); + * + * Note that this is set to NULL if retrieving the property failed (the + * format will be MPV_FORMAT_NONE). + */ + void *data; +} mpv_event_property; + +/** + * Numeric log levels. The lower the number, the more important the message is. + * MPV_LOG_LEVEL_NONE is never used when receiving messages. The string in + * the comment after the value is the name of the log level as used for the + * mpv_request_log_messages() function. + * Unused numeric values are unused, but reserved for future use. + */ +typedef enum mpv_log_level { + MPV_LOG_LEVEL_NONE = 0, /// "no" - disable absolutely all messages + MPV_LOG_LEVEL_FATAL = 10, /// "fatal" - critical/aborting errors + MPV_LOG_LEVEL_ERROR = 20, /// "error" - simple errors + MPV_LOG_LEVEL_WARN = 30, /// "warn" - possible problems + MPV_LOG_LEVEL_INFO = 40, /// "info" - informational message + MPV_LOG_LEVEL_V = 50, /// "v" - noisy informational message + MPV_LOG_LEVEL_DEBUG = 60, /// "debug" - very noisy technical information + MPV_LOG_LEVEL_TRACE = 70, /// "trace" - extremely noisy +} mpv_log_level; + +typedef struct mpv_event_log_message { + /** + * The module prefix, identifies the sender of the message. As a special + * case, if the message buffer overflows, this will be set to the string + * "overflow" (which doesn't appear as prefix otherwise), and the text + * field will contain an informative message. + */ + const char *prefix; + /** + * The log level as string. See mpv_request_log_messages() for possible + * values. The level "no" is never used here. + */ + const char *level; + /** + * The log message. It consists of 1 line of text, and is terminated with + * a newline character. (Before API version 1.6, it could contain multiple + * or partial lines.) + */ + const char *text; + /** + * The same contents as the level field, but as a numeric ID. + * Since API version 1.6. + */ + mpv_log_level log_level; +} mpv_event_log_message; + +/// Since API version 1.9. +typedef enum mpv_end_file_reason { + /** + * The end of file was reached. Sometimes this may also happen on + * incomplete or corrupted files, or if the network connection was + * interrupted when playing a remote file. It also happens if the + * playback range was restricted with --end or --frames or similar. + */ + MPV_END_FILE_REASON_EOF = 0, + /** + * Playback was stopped by an external action (e.g. playlist controls). + */ + MPV_END_FILE_REASON_STOP = 2, + /** + * Playback was stopped by the quit command or player shutdown. + */ + MPV_END_FILE_REASON_QUIT = 3, + /** + * Some kind of error happened that lead to playback abort. Does not + * necessarily happen on incomplete or broken files (in these cases, both + * MPV_END_FILE_REASON_ERROR or MPV_END_FILE_REASON_EOF are possible). + * + * mpv_event_end_file.error will be set. + */ + MPV_END_FILE_REASON_ERROR = 4, + /** + * The file was a playlist or similar. When the playlist is read, its + * entries will be appended to the playlist after the entry of the current + * file, the entry of the current file is removed, and a MPV_EVENT_END_FILE + * event is sent with reason set to MPV_END_FILE_REASON_REDIRECT. Then + * playback continues with the playlist contents. + * Since API version 1.18. + */ + MPV_END_FILE_REASON_REDIRECT = 5, +} mpv_end_file_reason; + +typedef struct mpv_event_end_file { + /** + * Corresponds to the values in enum mpv_end_file_reason (the "int" type + * will be replaced with mpv_end_file_reason on the next ABI bump). + * + * Unknown values should be treated as unknown. + */ + int reason; + /** + * If reason==MPV_END_FILE_REASON_ERROR, this contains a mpv error code + * (one of MPV_ERROR_...) giving an approximate reason why playback + * failed. In other cases, this field is 0 (no error). + * Since API version 1.9. + */ + int error; +} mpv_event_end_file; + +#if MPV_ENABLE_DEPRECATED +/** @deprecated see MPV_EVENT_SCRIPT_INPUT_DISPATCH for remarks + */ +typedef struct mpv_event_script_input_dispatch { + int arg0; + const char *type; +} mpv_event_script_input_dispatch; +#endif + +typedef struct mpv_event_client_message { + /** + * Arbitrary arguments chosen by the sender of the message. If num_args > 0, + * you can access args[0] through args[num_args - 1] (inclusive). What + * these arguments mean is up to the sender and receiver. + * None of the valid items are NULL. + */ + int num_args; + const char **args; +} mpv_event_client_message; + +typedef struct mpv_event_hook { + /** + * The hook name as passed to mpv_hook_add(). + */ + const char *name; + /** + * Internal ID that must be passed to mpv_hook_continue(). + */ + uint64_t id; +} mpv_event_hook; + +// Since API version 1.102. +typedef struct mpv_event_command { + /** + * Result data of the command. Note that success/failure is signaled + * separately via mpv_event.error. This field is only for result data + * in case of success. Most commands leave it at MPV_FORMAT_NONE. Set + * to MPV_FORMAT_NONE on failure. + */ + mpv_node result; +} mpv_event_command; + +typedef struct mpv_event { + /** + * One of mpv_event. Keep in mind that later ABI compatible releases might + * add new event types. These should be ignored by the API user. + */ + mpv_event_id event_id; + /** + * This is mainly used for events that are replies to (asynchronous) + * requests. It contains a status code, which is >= 0 on success, or < 0 + * on error (a mpv_error value). Usually, this will be set if an + * asynchronous request fails. + * Used for: + * MPV_EVENT_GET_PROPERTY_REPLY + * MPV_EVENT_SET_PROPERTY_REPLY + * MPV_EVENT_COMMAND_REPLY + */ + int error; + /** + * If the event is in reply to a request (made with this API and this + * API handle), this is set to the reply_userdata parameter of the request + * call. Otherwise, this field is 0. + * Used for: + * MPV_EVENT_GET_PROPERTY_REPLY + * MPV_EVENT_SET_PROPERTY_REPLY + * MPV_EVENT_COMMAND_REPLY + * MPV_EVENT_PROPERTY_CHANGE + * MPV_EVENT_HOOK + */ + uint64_t reply_userdata; + /** + * The meaning and contents of the data member depend on the event_id: + * MPV_EVENT_GET_PROPERTY_REPLY: mpv_event_property* + * MPV_EVENT_PROPERTY_CHANGE: mpv_event_property* + * MPV_EVENT_LOG_MESSAGE: mpv_event_log_message* + * MPV_EVENT_CLIENT_MESSAGE: mpv_event_client_message* + * MPV_EVENT_END_FILE: mpv_event_end_file* + * MPV_EVENT_HOOK: mpv_event_hook* + * MPV_EVENT_COMMAND_REPLY* mpv_event_command* + * other: NULL + * + * Note: future enhancements might add new event structs for existing or new + * event types. + */ + void *data; +} mpv_event; + +/** + * Enable or disable the given event. + * + * Some events are enabled by default. Some events can't be disabled. + * + * (Informational note: currently, all events are enabled by default, except + * MPV_EVENT_TICK.) + * + * Safe to be called from mpv render API threads. + * + * @param event See enum mpv_event_id. + * @param enable 1 to enable receiving this event, 0 to disable it. + * @return error code + */ +int mpv_request_event(mpv_handle *ctx, mpv_event_id event, int enable); + +/** + * Enable or disable receiving of log messages. These are the messages the + * command line player prints to the terminal. This call sets the minimum + * required log level for a message to be received with MPV_EVENT_LOG_MESSAGE. + * + * @param min_level Minimal log level as string. Valid log levels: + * no fatal error warn info v debug trace + * The value "no" disables all messages. This is the default. + * An exception is the value "terminal-default", which uses the + * log level as set by the "--msg-level" option. This works + * even if the terminal is disabled. (Since API version 1.19.) + * Also see mpv_log_level. + * @return error code + */ +int mpv_request_log_messages(mpv_handle *ctx, const char *min_level); + +/** + * Wait for the next event, or until the timeout expires, or if another thread + * makes a call to mpv_wakeup(). Passing 0 as timeout will never wait, and + * is suitable for polling. + * + * The internal event queue has a limited size (per client handle). If you + * don't empty the event queue quickly enough with mpv_wait_event(), it will + * overflow and silently discard further events. If this happens, making + * asynchronous requests will fail as well (with MPV_ERROR_EVENT_QUEUE_FULL). + * + * Only one thread is allowed to call this on the same mpv_handle at a time. + * The API won't complain if more than one thread calls this, but it will cause + * race conditions in the client when accessing the shared mpv_event struct. + * Note that most other API functions are not restricted by this, and no API + * function internally calls mpv_wait_event(). Additionally, concurrent calls + * to different mpv_handles are always safe. + * + * As long as the timeout is 0, this is safe to be called from mpv render API + * threads. + * + * @param timeout Timeout in seconds, after which the function returns even if + * no event was received. A MPV_EVENT_NONE is returned on + * timeout. A value of 0 will disable waiting. Negative values + * will wait with an infinite timeout. + * @return A struct containing the event ID and other data. The pointer (and + * fields in the struct) stay valid until the next mpv_wait_event() + * call, or until the mpv_handle is destroyed. You must not write to + * the struct, and all memory referenced by it will be automatically + * released by the API on the next mpv_wait_event() call, or when the + * context is destroyed. The return value is never NULL. + */ +mpv_event *mpv_wait_event(mpv_handle *ctx, double timeout); + +/** + * Interrupt the current mpv_wait_event() call. This will wake up the thread + * currently waiting in mpv_wait_event(). If no thread is waiting, the next + * mpv_wait_event() call will return immediately (this is to avoid lost + * wakeups). + * + * mpv_wait_event() will receive a MPV_EVENT_NONE if it's woken up due to + * this call. But note that this dummy event might be skipped if there are + * already other events queued. All what counts is that the waiting thread + * is woken up at all. + * + * Safe to be called from mpv render API threads. + */ +void mpv_wakeup(mpv_handle *ctx); + +/** + * Set a custom function that should be called when there are new events. Use + * this if blocking in mpv_wait_event() to wait for new events is not feasible. + * + * Keep in mind that the callback will be called from foreign threads. You + * must not make any assumptions of the environment, and you must return as + * soon as possible (i.e. no long blocking waits). Exiting the callback through + * any other means than a normal return is forbidden (no throwing exceptions, + * no longjmp() calls). You must not change any local thread state (such as + * the C floating point environment). + * + * You are not allowed to call any client API functions inside of the callback. + * In particular, you should not do any processing in the callback, but wake up + * another thread that does all the work. The callback is meant strictly for + * notification only, and is called from arbitrary core parts of the player, + * that make no considerations for reentrant API use or allowing the callee to + * spend a lot of time doing other things. Keep in mind that it's also possible + * that the callback is called from a thread while a mpv API function is called + * (i.e. it can be reentrant). + * + * In general, the client API expects you to call mpv_wait_event() to receive + * notifications, and the wakeup callback is merely a helper utility to make + * this easier in certain situations. Note that it's possible that there's + * only one wakeup callback invocation for multiple events. You should call + * mpv_wait_event() with no timeout until MPV_EVENT_NONE is reached, at which + * point the event queue is empty. + * + * If you actually want to do processing in a callback, spawn a thread that + * does nothing but call mpv_wait_event() in a loop and dispatches the result + * to a callback. + * + * Only one wakeup callback can be set. + * + * @param cb function that should be called if a wakeup is required + * @param d arbitrary userdata passed to cb + */ +void mpv_set_wakeup_callback(mpv_handle *ctx, void (*cb)(void *d), void *d); + +/** + * Block until all asynchronous requests are done. This affects functions like + * mpv_command_async(), which return immediately and return their result as + * events. + * + * This is a helper, and somewhat equivalent to calling mpv_wait_event() in a + * loop until all known asynchronous requests have sent their reply as event, + * except that the event queue is not emptied. + * + * In case you called mpv_suspend() before, this will also forcibly reset the + * suspend counter of the given handle. + */ +void mpv_wait_async_requests(mpv_handle *ctx); + +/** + * A hook is like a synchronous event that blocks the player. You register + * a hook handler with this function. You will get an event, which you need + * to handle, and once things are ready, you can let the player continue with + * mpv_hook_continue(). + * + * Currently, hooks can't be removed explicitly. But they will be implicitly + * removed if the mpv_handle it was registered with is destroyed. This also + * continues the hook if it was being handled by the destroyed mpv_handle (but + * this should be avoided, as it might mess up order of hook execution). + * + * Hook handlers are ordered globally by priority and order of registration. + * Handlers for the same hook with same priority are invoked in order of + * registration (the handler registered first is run first). Handlers with + * lower priority are run first (which seems backward). + * + * See the "Hooks" section in the manpage to see which hooks are currently + * defined. + * + * Some hooks might be reentrant (so you get multiple MPV_EVENT_HOOK for the + * same hook). If this can happen for a specific hook type, it will be + * explicitly documented in the manpage. + * + * Only the mpv_handle on which this was called will receive the hook events, + * or can "continue" them. + * + * @param reply_userdata This will be used for the mpv_event.reply_userdata + * field for the received MPV_EVENT_HOOK events. + * If you have no use for this, pass 0. + * @param name The hook name. This should be one of the documented names. But + * if the name is unknown, the hook event will simply be never + * raised. + * @param priority See remarks above. Use 0 as a neutral default. + * @return error code (usually fails only on OOM) + */ +int mpv_hook_add(mpv_handle *ctx, uint64_t reply_userdata, + const char *name, int priority); + +/** + * Respond to a MPV_EVENT_HOOK event. You must call this after you have handled + * the event. There is no way to "cancel" or "stop" the hook. + * + * Calling this will will typically unblock the player for whatever the hook + * is responsible for (e.g. for the "on_load" hook it lets it continue + * playback). + * + * It is explicitly undefined behavior to call this more than once for each + * MPV_EVENT_HOOK, to pass an incorrect ID, or to call this on a mpv_handle + * different from the one that registered the handler and received the event. + * + * @param id This must be the value of the mpv_event_hook.id field for the + * corresponding MPV_EVENT_HOOK. + * @return error code + */ +int mpv_hook_continue(mpv_handle *ctx, uint64_t id); + +#if MPV_ENABLE_DEPRECATED + +/** + * Return a UNIX file descriptor referring to the read end of a pipe. This + * pipe can be used to wake up a poll() based processing loop. The purpose of + * this function is very similar to mpv_set_wakeup_callback(), and provides + * a primitive mechanism to handle coordinating a foreign event loop and the + * libmpv event loop. The pipe is non-blocking. It's closed when the mpv_handle + * is destroyed. This function always returns the same value (on success). + * + * This is in fact implemented using the same underlying code as for + * mpv_set_wakeup_callback() (though they don't conflict), and it is as if each + * callback invocation writes a single 0 byte to the pipe. When the pipe + * becomes readable, the code calling poll() (or select()) on the pipe should + * read all contents of the pipe and then call mpv_wait_event(c, 0) until + * no new events are returned. The pipe contents do not matter and can just + * be discarded. There is not necessarily one byte per readable event in the + * pipe. For example, the pipes are non-blocking, and mpv won't block if the + * pipe is full. Pipes are normally limited to 4096 bytes, so if there are + * more than 4096 events, the number of readable bytes can not equal the number + * of events queued. Also, it's possible that mpv does not write to the pipe + * once it's guaranteed that the client was already signaled. See the example + * below how to do it correctly. + * + * Example: + * + * int pipefd = mpv_get_wakeup_pipe(mpv); + * if (pipefd < 0) + * error(); + * while (1) { + * struct pollfd pfds[1] = { + * { .fd = pipefd, .events = POLLIN }, + * }; + * // Wait until there are possibly new mpv events. + * poll(pfds, 1, -1); + * if (pfds[0].revents & POLLIN) { + * // Empty the pipe. Doing this before calling mpv_wait_event() + * // ensures that no wakeups are missed. It's not so important to + * // make sure the pipe is really empty (it will just cause some + * // additional wakeups in unlikely corner cases). + * char unused[256]; + * read(pipefd, unused, sizeof(unused)); + * while (1) { + * mpv_event *ev = mpv_wait_event(mpv, 0); + * // If MPV_EVENT_NONE is received, the event queue is empty. + * if (ev->event_id == MPV_EVENT_NONE) + * break; + * // Process the event. + * ... + * } + * } + * } + * + * @deprecated this function will be removed in the future. If you need this + * functionality, use mpv_set_wakeup_callback(), create a pipe + * manually, and call write() on your pipe in the callback. + * + * @return A UNIX FD of the read end of the wakeup pipe, or -1 on error. + * On MS Windows/MinGW, this will always return -1. + */ +int mpv_get_wakeup_pipe(mpv_handle *ctx); + +/** + * @deprecated use render.h + */ +typedef enum mpv_sub_api { + /** + * For using mpv's OpenGL renderer on an external OpenGL context. + * mpv_get_sub_api(MPV_SUB_API_OPENGL_CB) returns mpv_opengl_cb_context*. + * This context can be used with mpv_opengl_cb_* functions. + * Will return NULL if unavailable (if OpenGL support was not compiled in). + * See opengl_cb.h for details. + * + * @deprecated use render.h + */ + MPV_SUB_API_OPENGL_CB = 1 +} mpv_sub_api; + +/** + * This is used for additional APIs that are not strictly part of the core API. + * See the individual mpv_sub_api member values. + * + * @deprecated use render.h + */ +void *mpv_get_sub_api(mpv_handle *ctx, mpv_sub_api sub_api); + +#endif + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/mpvdemo/mpv/include/opengl_cb.h b/mpvdemo/mpv/include/opengl_cb.h new file mode 100644 index 0000000..6820681 --- /dev/null +++ b/mpvdemo/mpv/include/opengl_cb.h @@ -0,0 +1,339 @@ +/* Copyright (C) 2017 the mpv developers + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef MPV_CLIENT_API_OPENGL_CB_H_ +#define MPV_CLIENT_API_OPENGL_CB_H_ + +#include "client.h" + +#if !MPV_ENABLE_DEPRECATED +#error "This header and all API provided by it is deprecated. Use render.h instead." +#else + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * + * Overview + * -------- + * + * Warning: this API is deprecated. A very similar API is provided by render.h + * and render_gl.h. The deprecated API is emulated with the new API. + * + * This API can be used to make mpv render into a foreign OpenGL context. It + * can be used to handle video display. + * + * The renderer needs to be explicitly initialized with mpv_opengl_cb_init_gl(), + * and then video can be drawn with mpv_opengl_cb_draw(). The user thread can + * be notified by new frames with mpv_opengl_cb_set_update_callback(). + * + * You can output and embed video without this API by setting the mpv "wid" + * option to a native window handle (see "Embedding the video window" section + * in the client.h header). In general, using the opengl-cb API is recommended, + * because window embedding can cause various issues, especially with GUI + * toolkits and certain platforms. + * + * OpenGL interop + * -------------- + * + * This assumes the OpenGL context lives on a certain thread controlled by the + * API user. The following functions require access to the OpenGL context: + * mpv_opengl_cb_init_gl + * mpv_opengl_cb_draw + * mpv_opengl_cb_uninit_gl + * + * The OpenGL context is indirectly accessed through the OpenGL function + * pointers returned by the get_proc_address callback in mpv_opengl_cb_init_gl. + * Generally, mpv will not load the system OpenGL library when using this API. + * + * Only "desktop" OpenGL version 2.1 and later and OpenGL ES version 2.0 and + * later are supported. With OpenGL 2.1, the GL_ARB_texture_rg is required. The + * renderer was written for the OpenGL 3.x core profile, with additional support + * for OpenGL 2.1 and OpenGL ES 2.0. + * + * Note that some hardware decoding interop API (as set with the "hwdec" option) + * may actually access some sort of host API, such as EGL. + * + * OpenGL state + * ------------ + * + * OpenGL has a large amount of implicit state. All the mpv functions mentioned + * above expect that the OpenGL state is reasonably set to OpenGL standard + * defaults. Likewise, mpv will attempt to leave the OpenGL context with + * standard defaults. The following state is excluded from this: + * + * - the glViewport state + * - the glScissor state (but GL_SCISSOR_TEST is in its default value) + * - glBlendFuncSeparate() state (but GL_BLEND is in its default value) + * - glClearColor() state + * - mpv may overwrite the callback set with glDebugMessageCallback() + * - mpv always disables GL_DITHER at init + * + * Messing with the state could be avoided by creating shared OpenGL contexts, + * but this is avoided for the sake of compatibility and interoperability. + * + * On OpenGL 2.1, mpv will strictly call functions like glGenTextures() to + * create OpenGL objects. You will have to do the same. This ensures that + * objects created by mpv and the API users don't clash. Also, legacy state + * must be either in its defaults, or not interfere with core state. + * + * Threading + * --------- + * + * The mpv_opengl_cb_* functions can be called from any thread, under the + * following conditions: + * - only one of the mpv_opengl_cb_* functions can be called at the same time + * (unless they belong to different mpv cores created by mpv_create()) + * - for functions which need an OpenGL context (see above) the OpenGL context + * must be "current" in the current thread, and it must be the same context + * as used with mpv_opengl_cb_init_gl() + * - never can be called from within the callbacks set with + * mpv_set_wakeup_callback() or mpv_opengl_cb_set_update_callback() + * + * Context and handle lifecycle + * ---------------------------- + * + * Video initialization will fail if the OpenGL context was not initialized yet + * (with mpv_opengl_cb_init_gl()). Likewise, mpv_opengl_cb_uninit_gl() will + * disable video. + * + * When the mpv core is destroyed (e.g. via mpv_terminate_destroy()), the OpenGL + * context must have been uninitialized. If this doesn't happen, undefined + * behavior will result. + * + * Hardware decoding + * ----------------- + * + * Hardware decoding via opengl_cb is fully supported, but requires some + * additional setup. (At least if direct hardware decoding modes are wanted, + * instead of copying back surface data from GPU to CPU RAM.) + * + * While "normal" mpv loads the OpenGL hardware decoding interop on demand, + * this can't be done with opengl_cb for internal technical reasons. Instead, + * it loads them by default, even if hardware decoding is not going to be used. + * In older mpv releases, this had to be done by setting the + * "opengl-hwdec-interop" or "hwdec-preload" options before calling + * mpv_opengl_cb_init_gl(). You can still use the newer "gpu-hwdec-interop" + * option to prevent loading of interop, or to load only a specific interop. + * + * There may be certain requirements on the OpenGL implementation: + * - Windows: ANGLE is required (although in theory GL/DX interop could be used) + * - Intel/Linux: EGL is required, and also a glMPGetNativeDisplay() callback + * must be provided (see sections below) + * - nVidia/Linux: Both GLX and EGL should work (GLX is required if vdpau is + * used, e.g. due to old drivers.) + * - OSX: CGL is required (CGLGetCurrentContext() returning non-NULL) + * - iOS: EAGL is required (EAGLContext.currentContext returning non-nil) + * + * Once these things are setup, hardware decoding can be enabled/disabled at + * any time by setting the "hwdec" property. + * + * Special windowing system interop considerations + * ------------------------------------------------ + * + * In some cases, libmpv needs to have access to the windowing system's handles. + * This can be a pointer to a X11 "Display" for example. Usually this is needed + * only for hardware decoding. + * + * You can communicate these handles to libmpv by adding a pseudo-OpenGL + * extension "GL_MP_MPGetNativeDisplay" to the additional extension string when + * calling mpv_opengl_cb_init_gl(). The get_proc_address callback should resolve + * a function named "glMPGetNativeDisplay", which has the signature: + * + * void* GLAPIENTRY glMPGetNativeDisplay(const char* name) + * + * See below what names are defined. Usually, libmpv will use the native handle + * up until mpv_opengl_cb_uninit_gl() is called. If the name is not anything + * you know/expected, return NULL from the function. + */ + +// Legacy - not supported anymore. +struct mpv_opengl_cb_window_pos { + int x; // left coordinates of window (usually 0) + int y; // top coordinates of window (usually 0) + int width; // width of GL window + int height; // height of GL window +}; + +// Legacy - not supported anymore. +struct mpv_opengl_cb_drm_params { + // DRM fd (int). set this to -1 if invalid. + int fd; + + // currently used crtc id + int crtc_id; + + // currently used connector id + int connector_id; + + // pointer to the drmModeAtomicReq that is being used for the renderloop. + // This atomic request pointer should be usually created at every renderloop. + struct _drmModeAtomicReq *atomic_request; +}; + +/** + * nVidia/Linux via VDPAU requires GLX, which does not have this problem (the + * GLX API can return the current X11 Display). + * + * Windowing system interop on MS win32 + * ------------------------------------ + * + * You should use ANGLE, and make sure your application and libmpv are linked + * to the same ANGLE DLLs. libmpv will pick the device context (needed for + * hardware decoding) from the current ANGLE EGL context. + */ + +/** + * Opaque context, returned by mpv_get_sub_api(MPV_SUB_API_OPENGL_CB). + * + * A context is bound to the mpv_handle it was retrieved from. The context + * will always be the same (for the same mpv_handle), and is valid until the + * mpv_handle it belongs to is released. + */ +typedef struct mpv_opengl_cb_context mpv_opengl_cb_context; + +typedef void (*mpv_opengl_cb_update_fn)(void *cb_ctx); +typedef void *(*mpv_opengl_cb_get_proc_address_fn)(void *fn_ctx, const char *name); + +/** + * Set the callback that notifies you when a new video frame is available, or + * if the video display configuration somehow changed and requires a redraw. + * Similar to mpv_set_wakeup_callback(), you must not call any mpv API from + * the callback, and all the other listed restrictions apply (such as not + * exiting the callback by throwing exceptions). + * + * @param callback callback(callback_ctx) is called if the frame should be + * redrawn + * @param callback_ctx opaque argument to the callback + */ +void mpv_opengl_cb_set_update_callback(mpv_opengl_cb_context *ctx, + mpv_opengl_cb_update_fn callback, + void *callback_ctx); + +/** + * Initialize the mpv OpenGL state. This retrieves OpenGL function pointers via + * get_proc_address, and creates OpenGL objects needed by mpv internally. It + * will also call APIs needed for rendering hardware decoded video in OpenGL, + * according to the mpv "hwdec" option. + * + * You must free the associated state at some point by calling the + * mpv_opengl_cb_uninit_gl() function. Not doing so may result in memory leaks + * or worse. + * + * @param exts optional _additional_ extension string, can be NULL + * @param get_proc_address callback used to retrieve function pointers to OpenGL + * functions. This is used for both standard functions + * and extension functions. (The extension string is + * checked whether extensions are really available.) + * The callback will be called from this function only + * (it is not stored and never used later). + * Usually, GL context APIs do this for you (e.g. with + * glXGetProcAddressARB or wglGetProcAddress), but + * some APIs do not always return pointers for all + * standard functions (even if present); in this case + * you have to compensate by looking up these functions + * yourself. + * @param get_proc_address_ctx arbitrary opaque user context passed to the + * get_proc_address callback + * @return error code (same as normal mpv_* API), including but not limited to: + * MPV_ERROR_UNSUPPORTED: the OpenGL version is not supported + * (or required extensions are missing) + * MPV_ERROR_INVALID_PARAMETER: the OpenGL state was already initialized + */ +int mpv_opengl_cb_init_gl(mpv_opengl_cb_context *ctx, const char *exts, + mpv_opengl_cb_get_proc_address_fn get_proc_address, + void *get_proc_address_ctx); + +/** + * Render video. Requires that the OpenGL state is initialized. + * + * The video will use the full provided framebuffer. Options like "panscan" are + * applied to determine which part of the video should be visible and how the + * video should be scaled. You can change these options at runtime by using the + * mpv property API. + * + * The renderer will reconfigure itself every time the output rectangle/size + * is changed. (If you want to do animations, it might be better to do the + * animation on a FBO instead.) + * + * This function implicitly pulls a video frame from the internal queue and + * renders it. If no new frame is available, the previous frame is redrawn. + * The update callback set with mpv_opengl_cb_set_update_callback() notifies + * you when a new frame was added. + * + * @param fbo The framebuffer object to render on. Because the renderer might + * manage multiple FBOs internally for the purpose of video + * postprocessing, it will always bind and unbind FBOs itself. If + * you want mpv to render on the main framebuffer, pass 0. + * @param w Width of the framebuffer. This is either the video size if the fbo + * parameter is 0, or the allocated size of the texture backing the + * fbo. The renderer will always use the full size of the fbo. + * @param h Height of the framebuffer. Same as with the w parameter, except + * that this parameter can be negative. In this case, the video + * frame will be rendered flipped. + * @return 0 + */ +int mpv_opengl_cb_draw(mpv_opengl_cb_context *ctx, int fbo, int w, int h); + +/** + * Deprecated. Use mpv_opengl_cb_draw(). This function is equivalent to: + * + * int mpv_opengl_cb_render(mpv_opengl_cb_context *ctx, int fbo, int vp[4]) + * { return mpv_opengl_cb_draw(ctx, fbo, vp[2], vp[3]); } + * + * vp[0] and vp[1] used to have a meaning, but are ignored in newer versions. + * + * This function will be removed in the future without version bump (this API + * was never marked as stable). + */ +int mpv_opengl_cb_render(mpv_opengl_cb_context *ctx, int fbo, int vp[4]); + +/** + * Tell the renderer that a frame was flipped at the given time. This is + * optional, but can help the player to achieve better timing. + * + * Note that calling this at least once informs libmpv that you will use this + * function. If you use it inconsistently, expect bad video playback. + * + * If this is called while no video or no OpenGL is initialized, it is ignored. + * + * @param time The mpv time (using mpv_get_time_us()) at which the flip call + * returned. If 0 is passed, mpv_get_time_us() is used instead. + * Currently, this parameter is ignored. + * @return error code + */ +int mpv_opengl_cb_report_flip(mpv_opengl_cb_context *ctx, int64_t time); + +/** + * Destroy the mpv OpenGL state. + * + * If video is still active (e.g. a file playing), video will be disabled + * forcefully. + * + * Calling this multiple times is ok. + * + * @return error code + */ +int mpv_opengl_cb_uninit_gl(mpv_opengl_cb_context *ctx); + +#ifdef __cplusplus +} +#endif + +#endif /* else #if MPV_ENABLE_DEPRECATED */ + +#endif diff --git a/mpvdemo/mpv/include/render.h b/mpvdemo/mpv/include/render.h new file mode 100644 index 0000000..293de3c --- /dev/null +++ b/mpvdemo/mpv/include/render.h @@ -0,0 +1,626 @@ +/* Copyright (C) 2018 the mpv developers + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef MPV_CLIENT_API_RENDER_H_ +#define MPV_CLIENT_API_RENDER_H_ + +#include "client.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * Overview + * -------- + * + * This API can be used to make mpv render using supported graphic APIs (such + * as OpenGL). It can be used to handle video display. + * + * The renderer needs to be created with mpv_render_context_create() before + * you start playback (or otherwise cause a VO to be created). Then (with most + * backends) mpv_render_context_render() can be used to explicitly render the + * current video frame. Use mpv_render_context_set_update_callback() to get + * notified when there is a new frame to draw. + * + * Preferably rendering should be done in a separate thread. If you call + * normal libmpv API functions on the renderer thread, deadlocks can result + * (these are made non-fatal with timeouts, but user experience will obviously + * suffer). See "Threading" section below. + * + * You can output and embed video without this API by setting the mpv "wid" + * option to a native window handle (see "Embedding the video window" section + * in the client.h header). In general, using the render API is recommended, + * because window embedding can cause various issues, especially with GUI + * toolkits and certain platforms. + * + * Supported backends + * ------------------ + * + * OpenGL: via MPV_RENDER_API_TYPE_OPENGL, see render_gl.h header. + * + * Threading + * --------- + * + * You are recommended to do rendering on a separate thread than normal libmpv + * use. + * + * The mpv_render_* functions can be called from any thread, under the + * following conditions: + * - only one of the mpv_render_* functions can be called at the same time + * (unless they belong to different mpv cores created by mpv_create()) + * - never can be called from within the callbacks set with + * mpv_set_wakeup_callback() or mpv_render_context_set_update_callback() + * - if the OpenGL backend is used, for all functions the OpenGL context + * must be "current" in the calling thread, and it must be the same OpenGL + * context as the mpv_render_context was created with. Otherwise, undefined + * behavior will occur. + * - the thread does not call libmpv API functions other than the mpv_render_* + * functions, except APIs which are declared as safe (see below). Likewise, + * there must be no lock or wait dependency from the render thread to a + * thread using other libmpv functions. Basically, the situation that your + * render thread waits for a "not safe" libmpv API function to return must + * not happen. If you ignore this requirement, deadlocks can happen, which + * are made non-fatal with timeouts; then playback quality will be degraded, + * and the message + * mpv_render_context_render() not being called or stuck. + * is logged. If you set MPV_RENDER_PARAM_ADVANCED_CONTROL, you promise that + * this won't happen, and must absolutely guarantee it, or a real deadlock + * will freeze the mpv core thread forever. + * + * libmpv functions which are safe to call from a render thread are: + * - functions marked with "Safe to be called from mpv render API threads." + * - client.h functions which don't have an explicit or implicit mpv_handle + * parameter + * - mpv_render_* functions; but only for the same mpv_render_context pointer. + * If the pointer is different, mpv_render_context_free() is not safe. (The + * reason is that if MPV_RENDER_PARAM_ADVANCED_CONTROL is set, it may have + * to process still queued requests from the core, which it can do only for + * the current context, while requests for other contexts would deadlock. + * Also, it may have to wait and block for the core to terminate the video + * chain to make sure no resources are used after context destruction.) + * - if the mpv_handle parameter refers to a different mpv core than the one + * you're rendering for (very obscure, but allowed) + * + * Note about old libmpv version: + * + * Before API version 1.105 (basically in mpv 0.29.x), simply enabling + * MPV_RENDER_PARAM_ADVANCED_CONTROL could cause deadlock issues. This can + * be worked around by setting the "vd-lavc-dr" option to "no". + * In addition, you were required to call all mpv_render*() API functions + * from the same thread on which mpv_render_context_create() was originally + * run (for the same the mpv_render_context). Not honoring it led to UB + * (deadlocks, use of invalid pthread_t handles), even if you moved your GL + * context to a different thread correctly. + * These problems were addressed in API version 1.105 (mpv 0.30.0). + * + * Context and handle lifecycle + * ---------------------------- + * + * Video initialization will fail if the render context was not initialized yet + * (with mpv_render_context_create()), or it will revert to a VO that creates + * its own window. + * + * Currently, there can be only 1 mpv_render_context at a time per mpv core. + * + * Calling mpv_render_context_free() while a VO is using the render context is + * active will disable video. + * + * You must free the context with mpv_render_context_free() before the mpv core + * is destroyed. If this doesn't happen, undefined behavior will result. + */ + +/** + * Opaque context, returned by mpv_render_context_create(). + */ +typedef struct mpv_render_context mpv_render_context; + +/** + * Parameters for mpv_render_param (which is used in a few places such as + * mpv_render_context_create(). + * + * Also see mpv_render_param for conventions and how to use it. + */ +typedef enum mpv_render_param_type { + /** + * Not a valid value, but also used to terminate a params array. Its value + * is always guaranteed to be 0 (even if the ABI changes in the future). + */ + MPV_RENDER_PARAM_INVALID = 0, + /** + * The render API to use. Valid for mpv_render_context_create(). + * + * Type: char* + * + * Defined APIs: + * + * MPV_RENDER_API_TYPE_OPENGL: + * OpenGL desktop 2.1 or later (preferably core profile compatible to + * OpenGL 3.2), or OpenGLES 2.0 or later. + * Providing MPV_RENDER_PARAM_OPENGL_INIT_PARAMS is required. + * It is expected that an OpenGL context is valid and "current" when + * calling mpv_render_* functions (unless specified otherwise). It + * must be the same context for the same mpv_render_context. + */ + MPV_RENDER_PARAM_API_TYPE = 1, + /** + * Required parameters for initializing the OpenGL renderer. Valid for + * mpv_render_context_create(). + * Type: mpv_opengl_init_params* + */ + MPV_RENDER_PARAM_OPENGL_INIT_PARAMS = 2, + /** + * Describes a GL render target. Valid for mpv_render_context_render(). + * Type: mpv_opengl_fbo* + */ + MPV_RENDER_PARAM_OPENGL_FBO = 3, + /** + * Control flipped rendering. Valid for mpv_render_context_render(). + * Type: int* + * If the value is set to 0, render normally. Otherwise, render it flipped, + * which is needed e.g. when rendering to an OpenGL default framebuffer + * (which has a flipped coordinate system). + */ + MPV_RENDER_PARAM_FLIP_Y = 4, + /** + * Control surface depth. Valid for mpv_render_context_render(). + * Type: int* + * This implies the depth of the surface passed to the render function in + * bits per channel. If omitted or set to 0, the renderer will assume 8. + * Typically used to control dithering. + */ + MPV_RENDER_PARAM_DEPTH = 5, + /** + * ICC profile blob. Valid for mpv_render_context_set_parameter(). + * Type: mpv_byte_array* + * Set an ICC profile for use with the "icc-profile-auto" option. (If the + * option is not enabled, the ICC data will not be used.) + */ + MPV_RENDER_PARAM_ICC_PROFILE = 6, + /** + * Ambient light in lux. Valid for mpv_render_context_set_parameter(). + * Type: int* + * This can be used for automatic gamma correction. + */ + MPV_RENDER_PARAM_AMBIENT_LIGHT = 7, + /** + * X11 Display, sometimes used for hwdec. Valid for + * mpv_render_context_create(). The Display must stay valid for the lifetime + * of the mpv_render_context. + * Type: Display* + */ + MPV_RENDER_PARAM_X11_DISPLAY = 8, + /** + * Wayland display, sometimes used for hwdec. Valid for + * mpv_render_context_create(). The wl_display must stay valid for the + * lifetime of the mpv_render_context. + * Type: struct wl_display* + */ + MPV_RENDER_PARAM_WL_DISPLAY = 9, + /** + * Better control about rendering and enabling some advanced features. Valid + * for mpv_render_context_create(). + * + * This conflates multiple requirements the API user promises to abide if + * this option is enabled: + * + * - The API user's render thread, which is calling the mpv_render_*() + * functions, never waits for the core. Otherwise deadlocks can happen. + * See "Threading" section. + * - The callback set with mpv_render_context_set_update_callback() can now + * be called even if there is no new frame. The API user should call the + * mpv_render_context_update() function, and interpret the return value + * for whether a new frame should be rendered. + * - Correct functionality is impossible if the update callback is not set, + * or not set soon enough after mpv_render_context_create() (the core can + * block while waiting for you to call mpv_render_context_update(), and + * if the update callback is not correctly set, it will deadlock, or + * block for too long). + * + * In general, setting this option will enable the following features (and + * possibly more): + * + * - "Direct rendering", which means the player decodes directly to a + * texture, which saves a copy per video frame ("vd-lavc-dr" option + * needs to be enabled, and the rendering backend as well as the + * underlying GPU API/driver needs to have support for it). + * - Rendering screenshots with the GPU API if supported by the backend + * (instead of using a suboptimal software fallback via libswscale). + * + * Warning: do not just add this without reading the "Threading" section + * above, and then wondering that deadlocks happen. The + * requirements are tricky. But also note that even if advanced + * control is disabled, not adhering to the rules will lead to + * playback problems. Enabling advanced controls simply makes + * violating these rules fatal. + * + * Type: int*: 0 for disable (default), 1 for enable + */ + MPV_RENDER_PARAM_ADVANCED_CONTROL = 10, + /** + * Return information about the next frame to render. Valid for + * mpv_render_context_get_info(). + * + * Type: mpv_render_frame_info* + * + * It strictly returns information about the _next_ frame. The implication + * is that e.g. mpv_render_context_update()'s return value will have + * MPV_RENDER_UPDATE_FRAME set, and the user is supposed to call + * mpv_render_context_render(). If there is no next frame, then the + * return value will have is_valid set to 0. + */ + MPV_RENDER_PARAM_NEXT_FRAME_INFO = 11, + /** + * Enable or disable video timing. Valid for mpv_render_context_render(). + * + * Type: int*: 0 for disable, 1 for enable (default) + * + * When video is timed to audio, the player attempts to render video a bit + * ahead, and then do a blocking wait until the target display time is + * reached. This blocks mpv_render_context_render() for up to the amount + * specified with the "video-timing-offset" global option. You can set + * this parameter to 0 to disable this kind of waiting. If you do, it's + * recommended to use the target time value in mpv_render_frame_info to + * wait yourself, or to set the "video-timing-offset" to 0 instead. + * + * Disabling this without doing anything in addition will result in A/V sync + * being slightly off. + */ + MPV_RENDER_PARAM_BLOCK_FOR_TARGET_TIME = 12, + /** + * Use to skip rendering in mpv_render_context_render(). + * + * Type: int*: 0 for rendering (default), 1 for skipping + * + * If this is set, you don't need to pass a target surface to the render + * function (and if you do, it's completely ignored). This can still call + * into the lower level APIs (i.e. if you use OpenGL, the OpenGL context + * must be set). + * + * Be aware that the render API will consider this frame as having been + * rendered. All other normal rules also apply, for example about whether + * you have to call mpv_render_context_report_swap(). It also does timing + * in the same way. + */ + MPV_RENDER_PARAM_SKIP_RENDERING = 13, + /** + * Deprecated. Not supported. Use MPV_RENDER_PARAM_DRM_DISPLAY_V2 instead. + * Type : struct mpv_opengl_drm_params* + */ + MPV_RENDER_PARAM_DRM_DISPLAY = 14, + /** + * DRM draw surface size, contains draw surface dimensions. + * Valid for mpv_render_context_create(). + * Type : struct mpv_opengl_drm_draw_surface_size* + */ + MPV_RENDER_PARAM_DRM_DRAW_SURFACE_SIZE = 15, + /** + * DRM display, contains drm display handles. + * Valid for mpv_render_context_create(). + * Type : struct mpv_opengl_drm_params_v2* + */ + MPV_RENDER_PARAM_DRM_DISPLAY_V2 = 16, +} mpv_render_param_type; + +/** + * For backwards compatibility with the old naming of + * MPV_RENDER_PARAM_DRM_DRAW_SURFACE_SIZE + */ +#define MPV_RENDER_PARAM_DRM_OSD_SIZE MPV_RENDER_PARAM_DRM_DRAW_SURFACE_SIZE + +/** + * Used to pass arbitrary parameters to some mpv_render_* functions. The + * meaning of the data parameter is determined by the type, and each + * MPV_RENDER_PARAM_* documents what type the value must point to. + * + * Each value documents the required data type as the pointer you cast to + * void* and set on mpv_render_param.data. For example, if MPV_RENDER_PARAM_FOO + * documents the type as Something* , then the code should look like this: + * + * Something foo = {...}; + * mpv_render_param param; + * param.type = MPV_RENDER_PARAM_FOO; + * param.data = & foo; + * + * Normally, the data field points to exactly 1 object. If the type is char*, + * it points to a 0-terminated string. + * + * In all cases (unless documented otherwise) the pointers need to remain + * valid during the call only. Unless otherwise documented, the API functions + * will not write to the params array or any data pointed to it. + * + * As a convention, parameter arrays are always terminated by type==0. There + * is no specific order of the parameters required. The order of the 2 fields in + * this struct is guaranteed (even after ABI changes). + */ +typedef struct mpv_render_param { + enum mpv_render_param_type type; + void *data; +} mpv_render_param; + + +/** + * Predefined values for MPV_RENDER_PARAM_API_TYPE. + */ +#define MPV_RENDER_API_TYPE_OPENGL "opengl" + +/** + * Flags used in mpv_render_frame_info.flags. Each value represents a bit in it. + */ +typedef enum mpv_render_frame_info_flag { + /** + * Set if there is actually a next frame. If unset, there is no next frame + * yet, and other flags and fields that require a frame to be queued will + * be unset. + * + * This is set for _any_ kind of frame, even for redraw requests. + * + * Note that when this is unset, it simply means no new frame was + * decoded/queued yet, not necessarily that the end of the video was + * reached. A new frame can be queued after some time. + * + * If the return value of mpv_render_context_render() had the + * MPV_RENDER_UPDATE_FRAME flag set, this flag will usually be set as well, + * unless the frame is rendered, or discarded by other asynchronous events. + */ + MPV_RENDER_FRAME_INFO_PRESENT = 1 << 0, + /** + * If set, the frame is not an actual new video frame, but a redraw request. + * For example if the video is paused, and an option that affects video + * rendering was changed (or any other reason), an update request can be + * issued and this flag will be set. + * + * Typically, redraw frames will not be subject to video timing. + * + * Implies MPV_RENDER_FRAME_INFO_PRESENT. + */ + MPV_RENDER_FRAME_INFO_REDRAW = 1 << 1, + /** + * If set, this is supposed to reproduce the previous frame perfectly. This + * is usually used for certain "video-sync" options ("display-..." modes). + * Typically the renderer will blit the video from a FBO. Unset otherwise. + * + * Implies MPV_RENDER_FRAME_INFO_PRESENT. + */ + MPV_RENDER_FRAME_INFO_REPEAT = 1 << 2, + /** + * If set, the player timing code expects that the user thread blocks on + * vsync (by either delaying the render call, or by making a call to + * mpv_render_context_report_swap() at vsync time). + * + * Implies MPV_RENDER_FRAME_INFO_PRESENT. + */ + MPV_RENDER_FRAME_INFO_BLOCK_VSYNC = 1 << 3, +} mpv_render_frame_info_flag; + +/** + * Information about the next video frame that will be rendered. Can be + * retrieved with MPV_RENDER_PARAM_NEXT_FRAME_INFO. + */ +typedef struct mpv_render_frame_info { + /** + * A bitset of mpv_render_frame_info_flag values (i.e. multiple flags are + * combined with bitwise or). + */ + uint64_t flags; + /** + * Absolute time at which the frame is supposed to be displayed. This is in + * the same unit and base as the time returned by mpv_get_time_us(). For + * frames that are redrawn, or if vsync locked video timing is used (see + * "video-sync" option), then this can be 0. The "video-timing-offset" + * option determines how much "headroom" the render thread gets (but a high + * enough frame rate can reduce it anyway). mpv_render_context_render() will + * normally block until the time is elapsed, unless you pass it + * MPV_RENDER_PARAM_BLOCK_FOR_TARGET_TIME = 0. + */ + int64_t target_time; +} mpv_render_frame_info; + +/** + * Initialize the renderer state. Depending on the backend used, this will + * access the underlying GPU API and initialize its own objects. + * + * You must free the context with mpv_render_context_free(). Not doing so before + * the mpv core is destroyed may result in memory leaks or crashes. + * + * Currently, only at most 1 context can exists per mpv core (it represents the + * main video output). + * + * You should pass the following parameters: + * - MPV_RENDER_PARAM_API_TYPE to select the underlying backend/GPU API. + * - Backend-specific init parameter, like MPV_RENDER_PARAM_OPENGL_INIT_PARAMS. + * - Setting MPV_RENDER_PARAM_ADVANCED_CONTROL and following its rules is + * strongly recommended. + * - If you want to use hwdec, possibly hwdec interop resources. + * + * @param res set to the context (on success) or NULL (on failure). The value + * is never read and always overwritten. + * @param mpv handle used to get the core (the mpv_render_context won't depend + * on this specific handle, only the core referenced by it) + * @param params an array of parameters, terminated by type==0. It's left + * unspecified what happens with unknown parameters. At least + * MPV_RENDER_PARAM_API_TYPE is required, and most backends will + * require another backend-specific parameter. + * @return error code, including but not limited to: + * MPV_ERROR_UNSUPPORTED: the OpenGL version is not supported + * (or required extensions are missing) + * MPV_ERROR_NOT_IMPLEMENTED: an unknown API type was provided, or + * support for the requested API was not + * built in the used libmpv binary. + * MPV_ERROR_INVALID_PARAMETER: at least one of the provided parameters was + * not valid. + */ +int mpv_render_context_create(mpv_render_context **res, mpv_handle *mpv, + mpv_render_param *params); + +/** + * Attempt to change a single parameter. Not all backends and parameter types + * support all kinds of changes. + * + * @param ctx a valid render context + * @param param the parameter type and data that should be set + * @return error code. If a parameter could actually be changed, this returns + * success, otherwise an error code depending on the parameter type + * and situation. + */ +int mpv_render_context_set_parameter(mpv_render_context *ctx, + mpv_render_param param); + +/** + * Retrieve information from the render context. This is NOT a counterpart to + * mpv_render_context_set_parameter(), because you generally can't read + * parameters set with it, and this function is not meant for this purpose. + * Instead, this is for communicating information from the renderer back to the + * user. See mpv_render_param_type; entries which support this function + * explicitly mention it, and for other entries you can assume it will fail. + * + * You pass param with param.type set and param.data pointing to a variable + * of the required data type. The function will then overwrite that variable + * with the returned value (at least on success). + * + * @param ctx a valid render context + * @param param the parameter type and data that should be retrieved + * @return error code. If a parameter could actually be retrieved, this returns + * success, otherwise an error code depending on the parameter type + * and situation. MPV_ERROR_NOT_IMPLEMENTED is used for unknown + * param.type, or if retrieving it is not supported. + */ +int mpv_render_context_get_info(mpv_render_context *ctx, + mpv_render_param param); + +typedef void (*mpv_render_update_fn)(void *cb_ctx); + +/** + * Set the callback that notifies you when a new video frame is available, or + * if the video display configuration somehow changed and requires a redraw. + * Similar to mpv_set_wakeup_callback(), you must not call any mpv API from + * the callback, and all the other listed restrictions apply (such as not + * exiting the callback by throwing exceptions). + * + * This can be called from any thread, except from an update callback. In case + * of the OpenGL backend, no OpenGL state or API is accessed. + * + * Calling this will raise an update callback immediately. + * + * @param callback callback(callback_ctx) is called if the frame should be + * redrawn + * @param callback_ctx opaque argument to the callback + */ +void mpv_render_context_set_update_callback(mpv_render_context *ctx, + mpv_render_update_fn callback, + void *callback_ctx); + +/** + * The API user is supposed to call this when the update callback was invoked + * (like all mpv_render_* functions, this has to happen on the render thread, + * and _not_ from the update callback itself). + * + * This is optional if MPV_RENDER_PARAM_ADVANCED_CONTROL was not set (default). + * Otherwise, it's a hard requirement that this is called after each update + * callback. If multiple update callback happened, and the function could not + * be called sooner, it's OK to call it once after the last callback. + * + * If an update callback happens during or after this function, the function + * must be called again at the soonest possible time. + * + * If MPV_RENDER_PARAM_ADVANCED_CONTROL was set, this will do additional work + * such as allocating textures for the video decoder. + * + * @return a bitset of mpv_render_update_flag values (i.e. multiple flags are + * combined with bitwise or). Typically, this will tell the API user + * what should happen next. E.g. if the MPV_RENDER_UPDATE_FRAME flag is + * set, mpv_render_context_render() should be called. If flags unknown + * to the API user are set, or if the return value is 0, nothing needs + * to be done. + */ +uint64_t mpv_render_context_update(mpv_render_context *ctx); + +/** + * Flags returned by mpv_render_context_update(). Each value represents a bit + * in the function's return value. + */ +typedef enum mpv_render_update_flag { + /** + * A new video frame must be rendered. mpv_render_context_render() must be + * called. + */ + MPV_RENDER_UPDATE_FRAME = 1 << 0, +} mpv_render_context_flag; + +/** + * Render video. + * + * Typically renders the video to a target surface provided via mpv_render_param + * (the details depend on the backend in use). Options like "panscan" are + * applied to determine which part of the video should be visible and how the + * video should be scaled. You can change these options at runtime by using the + * mpv property API. + * + * The renderer will reconfigure itself every time the target surface + * configuration (such as size) is changed. + * + * This function implicitly pulls a video frame from the internal queue and + * renders it. If no new frame is available, the previous frame is redrawn. + * The update callback set with mpv_render_context_set_update_callback() + * notifies you when a new frame was added. The details potentially depend on + * the backends and the provided parameters. + * + * Generally, libmpv will invoke your update callback some time before the video + * frame should be shown, and then lets this function block until the supposed + * display time. This will limit your rendering to video FPS. You can prevent + * this by setting the "video-timing-offset" global option to 0. (This applies + * only to "audio" video sync mode.) + * + * You should pass the following parameters: + * - Backend-specific target object, such as MPV_RENDER_PARAM_OPENGL_FBO. + * - Possibly transformations, such as MPV_RENDER_PARAM_FLIP_Y. + * + * @param ctx a valid render context + * @param params an array of parameters, terminated by type==0. Which parameters + * are required depends on the backend. It's left unspecified what + * happens with unknown parameters. + * @return error code + */ +int mpv_render_context_render(mpv_render_context *ctx, mpv_render_param *params); + +/** + * Tell the renderer that a frame was flipped at the given time. This is + * optional, but can help the player to achieve better timing. + * + * Note that calling this at least once informs libmpv that you will use this + * function. If you use it inconsistently, expect bad video playback. + * + * If this is called while no video is initialized, it is ignored. + * + * @param ctx a valid render context + */ +void mpv_render_context_report_swap(mpv_render_context *ctx); + +/** + * Destroy the mpv renderer state. + * + * If video is still active (e.g. a file playing), video will be disabled + * forcefully. + * + * @param ctx a valid render context. After this function returns, this is not + * a valid pointer anymore. NULL is also allowed and does nothing. + */ +void mpv_render_context_free(mpv_render_context *ctx); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/mpvdemo/mpv/include/render_gl.h b/mpvdemo/mpv/include/render_gl.h new file mode 100644 index 0000000..cb141df --- /dev/null +++ b/mpvdemo/mpv/include/render_gl.h @@ -0,0 +1,216 @@ +/* Copyright (C) 2018 the mpv developers + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef MPV_CLIENT_API_RENDER_GL_H_ +#define MPV_CLIENT_API_RENDER_GL_H_ + +#include "render.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * OpenGL backend + * -------------- + * + * This header contains definitions for using OpenGL with the render.h API. + * + * OpenGL interop + * -------------- + * + * The OpenGL backend has some special rules, because OpenGL itself uses + * implicit per-thread contexts, which causes additional API problems. + * + * This assumes the OpenGL context lives on a certain thread controlled by the + * API user. All mpv_render_* APIs have to be assumed to implicitly use the + * OpenGL context if you pass a mpv_render_context using the OpenGL backend, + * unless specified otherwise. + * + * The OpenGL context is indirectly accessed through the OpenGL function + * pointers returned by the get_proc_address callback in mpv_opengl_init_params. + * Generally, mpv will not load the system OpenGL library when using this API. + * + * OpenGL state + * ------------ + * + * OpenGL has a large amount of implicit state. All the mpv functions mentioned + * above expect that the OpenGL state is reasonably set to OpenGL standard + * defaults. Likewise, mpv will attempt to leave the OpenGL context with + * standard defaults. The following state is excluded from this: + * + * - the glViewport state + * - the glScissor state (but GL_SCISSOR_TEST is in its default value) + * - glBlendFuncSeparate() state (but GL_BLEND is in its default value) + * - glClearColor() state + * - mpv may overwrite the callback set with glDebugMessageCallback() + * - mpv always disables GL_DITHER at init + * + * Messing with the state could be avoided by creating shared OpenGL contexts, + * but this is avoided for the sake of compatibility and interoperability. + * + * On OpenGL 2.1, mpv will strictly call functions like glGenTextures() to + * create OpenGL objects. You will have to do the same. This ensures that + * objects created by mpv and the API users don't clash. Also, legacy state + * must be either in its defaults, or not interfere with core state. + * + * API use + * ------- + * + * The mpv_render_* API is used. That API supports multiple backends, and this + * section documents specifics for the OpenGL backend. + * + * Use mpv_render_context_create() with MPV_RENDER_PARAM_API_TYPE set to + * MPV_RENDER_API_TYPE_OPENGL, and MPV_RENDER_PARAM_OPENGL_INIT_PARAMS provided. + * + * Call mpv_render_context_render() with MPV_RENDER_PARAM_OPENGL_FBO to render + * the video frame to an FBO. + * + * Hardware decoding + * ----------------- + * + * Hardware decoding via this API is fully supported, but requires some + * additional setup. (At least if direct hardware decoding modes are wanted, + * instead of copying back surface data from GPU to CPU RAM.) + * + * There may be certain requirements on the OpenGL implementation: + * + * - Windows: ANGLE is required (although in theory GL/DX interop could be used) + * - Intel/Linux: EGL is required, and also the native display resource needs + * to be provided (e.g. MPV_RENDER_PARAM_X11_DISPLAY for X11 and + * MPV_RENDER_PARAM_WL_DISPLAY for Wayland) + * - nVidia/Linux: Both GLX and EGL should work (GLX is required if vdpau is + * used, e.g. due to old drivers.) + * - OSX: CGL is required (CGLGetCurrentContext() returning non-NULL) + * - iOS: EAGL is required (EAGLContext.currentContext returning non-nil) + * + * Once these things are setup, hardware decoding can be enabled/disabled at + * any time by setting the "hwdec" property. + */ + +/** + * For initializing the mpv OpenGL state via MPV_RENDER_PARAM_OPENGL_INIT_PARAMS. + */ +typedef struct mpv_opengl_init_params { + /** + * This retrieves OpenGL function pointers, and will use them in subsequent + * operation. + * Usually, you can simply call the GL context APIs from this callback (e.g. + * glXGetProcAddressARB or wglGetProcAddress), but some APIs do not always + * return pointers for all standard functions (even if present); in this + * case you have to compensate by looking up these functions yourself when + * libmpv wants to resolve them through this callback. + * libmpv will not normally attempt to resolve GL functions on its own, nor + * does it link to GL libraries directly. + */ + void *(*get_proc_address)(void *ctx, const char *name); + /** + * Value passed as ctx parameter to get_proc_address(). + */ + void *get_proc_address_ctx; + /** + * This should not be used. It is deprecated and will be removed or ignored + * when the opengl_cb API is removed. + */ + const char *extra_exts; +} mpv_opengl_init_params; + +/** + * For MPV_RENDER_PARAM_OPENGL_FBO. + */ +typedef struct mpv_opengl_fbo { + /** + * Framebuffer object name. This must be either a valid FBO generated by + * glGenFramebuffers() that is complete and color-renderable, or 0. If the + * value is 0, this refers to the OpenGL default framebuffer. + */ + int fbo; + /** + * Valid dimensions. This must refer to the size of the framebuffer. This + * must always be set. + */ + int w, h; + /** + * Underlying texture internal format (e.g. GL_RGBA8), or 0 if unknown. If + * this is the default framebuffer, this can be an equivalent. + */ + int internal_format; +} mpv_opengl_fbo; + +/** + * Deprecated. For MPV_RENDER_PARAM_DRM_DISPLAY. + */ +typedef struct mpv_opengl_drm_params { + int fd; + int crtc_id; + int connector_id; + struct _drmModeAtomicReq **atomic_request_ptr; + int render_fd; +} mpv_opengl_drm_params; + +/** + * For MPV_RENDER_PARAM_DRM_DRAW_SURFACE_SIZE. + */ +typedef struct mpv_opengl_drm_draw_surface_size { + /** + * size of the draw plane surface in pixels. + */ + int width, height; +} mpv_opengl_drm_draw_surface_size; + +/** + * For MPV_RENDER_PARAM_DRM_DISPLAY_V2. + */ +typedef struct mpv_opengl_drm_params_v2 { + /** + * DRM fd (int). Set to -1 if invalid. + */ + int fd; + + /** + * Currently used crtc id + */ + int crtc_id; + + /** + * Currently used connector id + */ + int connector_id; + + /** + * Pointer to a drmModeAtomicReq pointer that is being used for the renderloop. + * This pointer should hold a pointer to the atomic request pointer + * The atomic request pointer is usually changed at every renderloop. + */ + struct _drmModeAtomicReq **atomic_request_ptr; + + /** + * DRM render node. Used for VAAPI interop. + * Set to -1 if invalid. + */ + int render_fd; +} mpv_opengl_drm_params_v2; + + +/** + * For backwards compatibility with the old naming of mpv_opengl_drm_draw_surface_size + */ +#define mpv_opengl_drm_osd_size mpv_opengl_drm_draw_surface_size + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/mpvdemo/mpv/include/stream_cb.h b/mpvdemo/mpv/include/stream_cb.h new file mode 100644 index 0000000..63593d7 --- /dev/null +++ b/mpvdemo/mpv/include/stream_cb.h @@ -0,0 +1,240 @@ +/* Copyright (C) 2017 the mpv developers + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef MPV_CLIENT_API_STREAM_CB_H_ +#define MPV_CLIENT_API_STREAM_CB_H_ + +#include "client.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * Warning: this API is not stable yet. + * + * Overview + * -------- + * + * This API can be used to make mpv read from a stream with a custom + * implementation. This interface is inspired by funopen on BSD and + * fopencookie on linux. The stream is backed by user-defined callbacks + * which can implement customized open, read, seek, size and close behaviors. + * + * Usage + * ----- + * + * Register your stream callbacks with the mpv_stream_cb_add_ro() function. You + * have to provide a mpv_stream_cb_open_ro_fn callback to it (open_fn argument). + * + * Once registered, you can `loadfile myprotocol://myfile`. Your open_fn will be + * invoked with the URI and you must fill out the provided mpv_stream_cb_info + * struct. This includes your stream callbacks (like read_fn), and an opaque + * cookie, which will be passed as the first argument to all the remaining + * stream callbacks. + * + * Note that your custom callbacks must not invoke libmpv APIs as that would + * cause a deadlock. (Unless you call a different mpv_handle than the one the + * callback was registered for, and the mpv_handles refer to different mpv + * instances.) + * + * Stream lifetime + * --------------- + * + * A stream remains valid until its close callback has been called. It's up to + * libmpv to call the close callback, and the libmpv user cannot close it + * directly with the stream_cb API. + * + * For example, if you consider your custom stream to become suddenly invalid + * (maybe because the underlying stream died), libmpv will continue using your + * stream. All you can do is returning errors from each callback, until libmpv + * gives up and closes it. + * + * Protocol registration and lifetime + * ---------------------------------- + * + * Protocols remain registered until the mpv instance is terminated. This means + * in particular that it can outlive the mpv_handle that was used to register + * it, but once mpv_terminate_destroy() is called, your registered callbacks + * will not be called again. + * + * Protocol unregistration is finished after the mpv core has been destroyed + * (e.g. after mpv_terminate_destroy() has returned). + * + * If you do not call mpv_terminate_destroy() yourself (e.g. plugin-style code), + * you will have to deal with the registration or even streams outliving your + * code. Here are some possible ways to do this: + * - call mpv_terminate_destroy(), which destroys the core, and will make sure + * all streams are closed once this function returns + * - you refcount all resources your stream "cookies" reference, so that it + * doesn't matter if streams live longer than expected + * - create "cancellation" semantics: after your protocol has been unregistered, + * notify all your streams that are still opened, and make them drop all + * referenced resources - then return errors from the stream callbacks as + * long as the stream is still opened + * + */ + +/** + * Read callback used to implement a custom stream. The semantics of the + * callback match read(2) in blocking mode. Short reads are allowed (you can + * return less bytes than requested, and libmpv will retry reading the rest + * with another call). If no data can be immediately read, the callback must + * block until there is new data. A return of 0 will be interpreted as final + * EOF, although libmpv might retry the read, or seek to a different position. + * + * @param cookie opaque cookie identifying the stream, + * returned from mpv_stream_cb_open_fn + * @param buf buffer to read data into + * @param size of the buffer + * @return number of bytes read into the buffer + * @return 0 on EOF + * @return -1 on error + */ +typedef int64_t (*mpv_stream_cb_read_fn)(void *cookie, char *buf, uint64_t nbytes); + +/** + * Seek callback used to implement a custom stream. + * + * Note that mpv will issue a seek to position 0 immediately after opening. This + * is used to test whether the stream is seekable (since seekability might + * depend on the URI contents, not just the protocol). Return + * MPV_ERROR_UNSUPPORTED if seeking is not implemented for this stream. This + * seek also serves to establish the fact that streams start at position 0. + * + * This callback can be NULL, in which it behaves as if always returning + * MPV_ERROR_UNSUPPORTED. + * + * @param cookie opaque cookie identifying the stream, + * returned from mpv_stream_cb_open_fn + * @param offset target absolut stream position + * @return the resulting offset of the stream + * MPV_ERROR_UNSUPPORTED or MPV_ERROR_GENERIC if the seek failed + */ +typedef int64_t (*mpv_stream_cb_seek_fn)(void *cookie, int64_t offset); + +/** + * Size callback used to implement a custom stream. + * + * Return MPV_ERROR_UNSUPPORTED if no size is known. + * + * This callback can be NULL, in which it behaves as if always returning + * MPV_ERROR_UNSUPPORTED. + * + * @param cookie opaque cookie identifying the stream, + * returned from mpv_stream_cb_open_fn + * @return the total size in bytes of the stream + */ +typedef int64_t (*mpv_stream_cb_size_fn)(void *cookie); + +/** + * Close callback used to implement a custom stream. + * + * @param cookie opaque cookie identifying the stream, + * returned from mpv_stream_cb_open_fn + */ +typedef void (*mpv_stream_cb_close_fn)(void *cookie); + +/** + * Cancel callback used to implement a custom stream. + * + * This callback is used to interrupt any current or future read and seek + * operations. It will be called from a separate thread than the demux + * thread, and should not block. + * + * This callback can be NULL. + * + * Available since API 1.106. + * + * @param cookie opaque cookie identifying the stream, + * returned from mpv_stream_cb_open_fn + */ +typedef void (*mpv_stream_cb_cancel_fn)(void *cookie); + +/** + * See mpv_stream_cb_open_ro_fn callback. + */ +typedef struct mpv_stream_cb_info { + /** + * Opaque user-provided value, which will be passed to the other callbacks. + * The close callback will be called to release the cookie. It is not + * interpreted by mpv. It doesn't even need to be a valid pointer. + * + * The user sets this in the mpv_stream_cb_open_ro_fn callback. + */ + void *cookie; + + /** + * Callbacks set by the user in the mpv_stream_cb_open_ro_fn callback. Some + * of them are optional, and can be left unset. + * + * The following callbacks are mandatory: read_fn, close_fn + */ + mpv_stream_cb_read_fn read_fn; + mpv_stream_cb_seek_fn seek_fn; + mpv_stream_cb_size_fn size_fn; + mpv_stream_cb_close_fn close_fn; + mpv_stream_cb_cancel_fn cancel_fn; /* since API 1.106 */ +} mpv_stream_cb_info; + +/** + * Open callback used to implement a custom read-only (ro) stream. The user + * must set the callback fields in the passed info struct. The cookie field + * also can be set to store state associated to the stream instance. + * + * Note that the info struct is valid only for the duration of this callback. + * You can't change the callbacks or the pointer to the cookie at a later point. + * + * Each stream instance created by the open callback can have different + * callbacks. + * + * The close_fn callback will terminate the stream instance. The pointers to + * your callbacks and cookie will be discarded, and the callbacks will not be + * called again. + * + * @param user_data opaque user data provided via mpv_stream_cb_add() + * @param uri name of the stream to be opened (with protocol prefix) + * @param info fields which the user should fill + * @return 0 on success, MPV_ERROR_LOADING_FAILED if the URI cannot be opened. + */ +typedef int (*mpv_stream_cb_open_ro_fn)(void *user_data, char *uri, + mpv_stream_cb_info *info); + +/** + * Add a custom stream protocol. This will register a protocol handler under + * the given protocol prefix, and invoke the given callbacks if an URI with the + * matching protocol prefix is opened. + * + * The "ro" is for read-only - only read-only streams can be registered with + * this function. + * + * The callback remains registered until the mpv core is registered. + * + * If a custom stream with the same name is already registered, then the + * MPV_ERROR_INVALID_PARAMETER error is returned. + * + * @param protocol protocol prefix, for example "foo" for "foo://" URIs + * @param user_data opaque pointer passed into the mpv_stream_cb_open_fn + * callback. + * @return error code + */ +int mpv_stream_cb_add_ro(mpv_handle *ctx, const char *protocol, void *user_data, + mpv_stream_cb_open_ro_fn open_fn); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/mpvdemo/mpv/include64/client.h b/mpvdemo/mpv/include64/client.h new file mode 100644 index 0000000..ce880e1 --- /dev/null +++ b/mpvdemo/mpv/include64/client.h @@ -0,0 +1,1979 @@ +/* Copyright (C) 2017 the mpv developers + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/* + * Note: the client API is licensed under ISC (see above) to enable + * other wrappers outside of mpv. But keep in mind that the + * mpv core is by default still GPLv2+ - unless built with + * --enable-lgpl, which makes it LGPLv2+. + */ + +#ifndef MPV_CLIENT_API_H_ +#define MPV_CLIENT_API_H_ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * Mechanisms provided by this API + * ------------------------------- + * + * This API provides general control over mpv playback. It does not give you + * direct access to individual components of the player, only the whole thing. + * It's somewhat equivalent to MPlayer's slave mode. You can send commands, + * retrieve or set playback status or settings with properties, and receive + * events. + * + * The API can be used in two ways: + * 1) Internally in mpv, to provide additional features to the command line + * player. Lua scripting uses this. (Currently there is no plugin API to + * get a client API handle in external user code. It has to be a fixed + * part of the player at compilation time.) + * 2) Using mpv as a library with mpv_create(). This basically allows embedding + * mpv in other applications. + * + * Documentation + * ------------- + * + * The libmpv C API is documented directly in this header. Note that most + * actual interaction with this player is done through + * options/commands/properties, which can be accessed through this API. + * Essentially everything is done with them, including loading a file, + * retrieving playback progress, and so on. + * + * These are documented elsewhere: + * * http://mpv.io/manual/master/#options + * * http://mpv.io/manual/master/#list-of-input-commands + * * http://mpv.io/manual/master/#properties + * + * You can also look at the examples here: + * * https://github.com/mpv-player/mpv-examples/tree/master/libmpv + * + * Event loop + * ---------- + * + * In general, the API user should run an event loop in order to receive events. + * This event loop should call mpv_wait_event(), which will return once a new + * mpv client API is available. It is also possible to integrate client API + * usage in other event loops (e.g. GUI toolkits) with the + * mpv_set_wakeup_callback() function, and then polling for events by calling + * mpv_wait_event() with a 0 timeout. + * + * Note that the event loop is detached from the actual player. Not calling + * mpv_wait_event() will not stop playback. It will eventually congest the + * event queue of your API handle, though. + * + * Synchronous vs. asynchronous calls + * ---------------------------------- + * + * The API allows both synchronous and asynchronous calls. Synchronous calls + * have to wait until the playback core is ready, which currently can take + * an unbounded time (e.g. if network is slow or unresponsive). Asynchronous + * calls just queue operations as requests, and return the result of the + * operation as events. + * + * Asynchronous calls + * ------------------ + * + * The client API includes asynchronous functions. These allow you to send + * requests instantly, and get replies as events at a later point. The + * requests are made with functions carrying the _async suffix, and replies + * are returned by mpv_wait_event() (interleaved with the normal event stream). + * + * A 64 bit userdata value is used to allow the user to associate requests + * with replies. The value is passed as reply_userdata parameter to the request + * function. The reply to the request will have the reply + * mpv_event->reply_userdata field set to the same value as the + * reply_userdata parameter of the corresponding request. + * + * This userdata value is arbitrary and is never interpreted by the API. Note + * that the userdata value 0 is also allowed, but then the client must be + * careful not accidentally interpret the mpv_event->reply_userdata if an + * event is not a reply. (For non-replies, this field is set to 0.) + * + * Asynchronous calls may be reordered in arbitrarily with other synchronous + * and asynchronous calls. If you want a guaranteed order, you need to wait + * until asynchronous calls report completion before doing the next call. + * + * See also the section "Asynchronous command details" in the manpage. + * + * Multithreading + * -------------- + * + * The client API is generally fully thread-safe, unless otherwise noted. + * Currently, there is no real advantage in using more than 1 thread to access + * the client API, since everything is serialized through a single lock in the + * playback core. + * + * Basic environment requirements + * ------------------------------ + * + * This documents basic requirements on the C environment. This is especially + * important if mpv is used as library with mpv_create(). + * + * - The LC_NUMERIC locale category must be set to "C". If your program calls + * setlocale(), be sure not to use LC_ALL, or if you do, reset LC_NUMERIC + * to its sane default: setlocale(LC_NUMERIC, "C"). + * - If a X11 based VO is used, mpv will set the xlib error handler. This error + * handler is process-wide, and there's no proper way to share it with other + * xlib users within the same process. This might confuse GUI toolkits. + * - mpv uses some other libraries that are not library-safe, such as Fribidi + * (used through libass), ALSA, FFmpeg, and possibly more. + * - The FPU precision must be set at least to double precision. + * - On Windows, mpv will call timeBeginPeriod(1). + * - On memory exhaustion, mpv will kill the process. + * - In certain cases, mpv may start sub processes (such as with the ytdl + * wrapper script). + * - Using UNIX IPC (off by default) will override the SIGPIPE signal handler, + * and set it to SIG_IGN. + * - mpv will reseed the legacy C random number generator by calling srand() at + * some random point once. + * - mpv may start sub processes, so overriding SIGCHLD, or waiting on all PIDs + * (such as calling wait()) by the parent process or any other library within + * the process must be avoided. libmpv itself only waits for its own PIDs. + * + * Encoding of filenames + * --------------------- + * + * mpv uses UTF-8 everywhere. + * + * On some platforms (like Linux), filenames actually do not have to be UTF-8; + * for this reason libmpv supports non-UTF-8 strings. libmpv uses what the + * kernel uses and does not recode filenames. At least on Linux, passing a + * string to libmpv is like passing a string to the fopen() function. + * + * On Windows, filenames are always UTF-8, libmpv converts between UTF-8 and + * UTF-16 when using win32 API functions. libmpv never uses or accepts + * filenames in the local 8 bit encoding. It does not use fopen() either; + * it uses _wfopen(). + * + * On OS X, filenames and other strings taken/returned by libmpv can have + * inconsistent unicode normalization. This can sometimes lead to problems. + * You have to hope for the best. + * + * Also see the remarks for MPV_FORMAT_STRING. + * + * Embedding the video window + * -------------------------- + * + * Using the render API (in render_cb.h) is recommended. This API requires + * you to create and maintain an OpenGL context, to which you can render + * video using a specific API call. This API does not include keyboard or mouse + * input directly. + * + * There is an older way to embed the native mpv window into your own. You have + * to get the raw window handle, and set it as "wid" option. This works on X11, + * win32, and OSX only. It's much easier to use than the render API, but + * also has various problems. + * + * Also see client API examples and the mpv manpage. There is an extensive + * discussion here: + * https://github.com/mpv-player/mpv-examples/tree/master/libmpv#methods-of-embedding-the-video-window + * + * Compatibility + * ------------- + * + * mpv development doesn't stand still, and changes to mpv internals as well as + * to its interface can cause compatibility issues to client API users. + * + * The API is versioned (see MPV_CLIENT_API_VERSION), and changes to it are + * documented in DOCS/client-api-changes.rst. The C API itself will probably + * remain compatible for a long time, but the functionality exposed by it + * could change more rapidly. For example, it's possible that options are + * renamed, or change the set of allowed values. + * + * Defensive programming should be used to potentially deal with the fact that + * options, commands, and properties could disappear, change their value range, + * or change the underlying datatypes. It might be a good idea to prefer + * MPV_FORMAT_STRING over other types to decouple your code from potential + * mpv changes. + * + * Also see: DOCS/compatibility.rst + * + * Future changes + * -------------- + * + * This are the planned changes that will most likely be done on the next major + * bump of the library: + * + * - remove all symbols and include files that are marked as deprecated + * - reassign enum numerical values to remove gaps + * - remove the mpv_opengl_init_params.extra_exts field + * - change the type of mpv_event_end_file.reason + * - disabling all events by default + */ + +/** + * The version is incremented on each API change. The 16 lower bits form the + * minor version number, and the 16 higher bits the major version number. If + * the API becomes incompatible to previous versions, the major version + * number is incremented. This affects only C part, and not properties and + * options. + * + * Every API bump is described in DOCS/client-api-changes.rst + * + * You can use MPV_MAKE_VERSION() and compare the result with integer + * relational operators (<, >, <=, >=). + */ +#define MPV_MAKE_VERSION(major, minor) (((major) << 16) | (minor) | 0UL) +#define MPV_CLIENT_API_VERSION MPV_MAKE_VERSION(1, 107) + +/** + * The API user is allowed to "#define MPV_ENABLE_DEPRECATED 0" before + * including any libmpv headers. Then deprecated symbols will be excluded + * from the headers. (Of course, deprecated properties and commands and + * other functionality will still work.) + */ +#ifndef MPV_ENABLE_DEPRECATED +#define MPV_ENABLE_DEPRECATED 1 +#endif + +/** + * Return the MPV_CLIENT_API_VERSION the mpv source has been compiled with. + */ +unsigned long mpv_client_api_version(void); + +/** + * Client context used by the client API. Every client has its own private + * handle. + */ +typedef struct mpv_handle mpv_handle; + +/** + * List of error codes than can be returned by API functions. 0 and positive + * return values always mean success, negative values are always errors. + */ +typedef enum mpv_error { + /** + * No error happened (used to signal successful operation). + * Keep in mind that many API functions returning error codes can also + * return positive values, which also indicate success. API users can + * hardcode the fact that ">= 0" means success. + */ + MPV_ERROR_SUCCESS = 0, + /** + * The event ringbuffer is full. This means the client is choked, and can't + * receive any events. This can happen when too many asynchronous requests + * have been made, but not answered. Probably never happens in practice, + * unless the mpv core is frozen for some reason, and the client keeps + * making asynchronous requests. (Bugs in the client API implementation + * could also trigger this, e.g. if events become "lost".) + */ + MPV_ERROR_EVENT_QUEUE_FULL = -1, + /** + * Memory allocation failed. + */ + MPV_ERROR_NOMEM = -2, + /** + * The mpv core wasn't configured and initialized yet. See the notes in + * mpv_create(). + */ + MPV_ERROR_UNINITIALIZED = -3, + /** + * Generic catch-all error if a parameter is set to an invalid or + * unsupported value. This is used if there is no better error code. + */ + MPV_ERROR_INVALID_PARAMETER = -4, + /** + * Trying to set an option that doesn't exist. + */ + MPV_ERROR_OPTION_NOT_FOUND = -5, + /** + * Trying to set an option using an unsupported MPV_FORMAT. + */ + MPV_ERROR_OPTION_FORMAT = -6, + /** + * Setting the option failed. Typically this happens if the provided option + * value could not be parsed. + */ + MPV_ERROR_OPTION_ERROR = -7, + /** + * The accessed property doesn't exist. + */ + MPV_ERROR_PROPERTY_NOT_FOUND = -8, + /** + * Trying to set or get a property using an unsupported MPV_FORMAT. + */ + MPV_ERROR_PROPERTY_FORMAT = -9, + /** + * The property exists, but is not available. This usually happens when the + * associated subsystem is not active, e.g. querying audio parameters while + * audio is disabled. + */ + MPV_ERROR_PROPERTY_UNAVAILABLE = -10, + /** + * Error setting or getting a property. + */ + MPV_ERROR_PROPERTY_ERROR = -11, + /** + * General error when running a command with mpv_command and similar. + */ + MPV_ERROR_COMMAND = -12, + /** + * Generic error on loading (usually used with mpv_event_end_file.error). + */ + MPV_ERROR_LOADING_FAILED = -13, + /** + * Initializing the audio output failed. + */ + MPV_ERROR_AO_INIT_FAILED = -14, + /** + * Initializing the video output failed. + */ + MPV_ERROR_VO_INIT_FAILED = -15, + /** + * There was no audio or video data to play. This also happens if the + * file was recognized, but did not contain any audio or video streams, + * or no streams were selected. + */ + MPV_ERROR_NOTHING_TO_PLAY = -16, + /** + * When trying to load the file, the file format could not be determined, + * or the file was too broken to open it. + */ + MPV_ERROR_UNKNOWN_FORMAT = -17, + /** + * Generic error for signaling that certain system requirements are not + * fulfilled. + */ + MPV_ERROR_UNSUPPORTED = -18, + /** + * The API function which was called is a stub only. + */ + MPV_ERROR_NOT_IMPLEMENTED = -19, + /** + * Unspecified error. + */ + MPV_ERROR_GENERIC = -20 +} mpv_error; + +/** + * Return a string describing the error. For unknown errors, the string + * "unknown error" is returned. + * + * @param error error number, see enum mpv_error + * @return A static string describing the error. The string is completely + * static, i.e. doesn't need to be deallocated, and is valid forever. + */ +const char *mpv_error_string(int error); + +/** + * General function to deallocate memory returned by some of the API functions. + * Call this only if it's explicitly documented as allowed. Calling this on + * mpv memory not owned by the caller will lead to undefined behavior. + * + * @param data A valid pointer returned by the API, or NULL. + */ +void mpv_free(void *data); + +/** + * Return the name of this client handle. Every client has its own unique + * name, which is mostly used for user interface purposes. + * + * @return The client name. The string is read-only and is valid until the + * mpv_handle is destroyed. + */ +const char *mpv_client_name(mpv_handle *ctx); + +/** + * Create a new mpv instance and an associated client API handle to control + * the mpv instance. This instance is in a pre-initialized state, + * and needs to be initialized to be actually used with most other API + * functions. + * + * Some API functions will return MPV_ERROR_UNINITIALIZED in the uninitialized + * state. You can call mpv_set_property() (or mpv_set_property_string() and + * other variants, and before mpv 0.21.0 mpv_set_option() etc.) to set initial + * options. After this, call mpv_initialize() to start the player, and then use + * e.g. mpv_command() to start playback of a file. + * + * The point of separating handle creation and actual initialization is that + * you can configure things which can't be changed during runtime. + * + * Unlike the command line player, this will have initial settings suitable + * for embedding in applications. The following settings are different: + * - stdin/stdout/stderr and the terminal will never be accessed. This is + * equivalent to setting the --no-terminal option. + * (Technically, this also suppresses C signal handling.) + * - No config files will be loaded. This is roughly equivalent to using + * --config=no. Since libmpv 1.15, you can actually re-enable this option, + * which will make libmpv load config files during mpv_initialize(). If you + * do this, you are strongly encouraged to set the "config-dir" option too. + * (Otherwise it will load the mpv command line player's config.) + * For example: + * mpv_set_option_string(mpv, "config-dir", "/my/path"); // set config root + * mpv_set_option_string(mpv, "config", "yes"); // enable config loading + * (call mpv_initialize() _after_ this) + * - Idle mode is enabled, which means the playback core will enter idle mode + * if there are no more files to play on the internal playlist, instead of + * exiting. This is equivalent to the --idle option. + * - Disable parts of input handling. + * - Most of the different settings can be viewed with the command line player + * by running "mpv --show-profile=libmpv". + * + * All this assumes that API users want a mpv instance that is strictly + * isolated from the command line player's configuration, user settings, and + * so on. You can re-enable disabled features by setting the appropriate + * options. + * + * The mpv command line parser is not available through this API, but you can + * set individual options with mpv_set_property(). Files for playback must be + * loaded with mpv_command() or others. + * + * Note that you should avoid doing concurrent accesses on the uninitialized + * client handle. (Whether concurrent access is definitely allowed or not has + * yet to be decided.) + * + * @return a new mpv client API handle. Returns NULL on error. Currently, this + * can happen in the following situations: + * - out of memory + * - LC_NUMERIC is not set to "C" (see general remarks) + */ +mpv_handle *mpv_create(void); + +/** + * Initialize an uninitialized mpv instance. If the mpv instance is already + * running, an error is returned. + * + * This function needs to be called to make full use of the client API if the + * client API handle was created with mpv_create(). + * + * Only the following options are required to be set _before_ mpv_initialize(): + * - options which are only read at initialization time: + * - config + * - config-dir + * - input-conf + * - load-scripts + * - script + * - player-operation-mode + * - input-app-events (OSX) + * - all encoding mode options + * + * @return error code + */ +int mpv_initialize(mpv_handle *ctx); + +/** + * Disconnect and destroy the mpv_handle. ctx will be deallocated with this + * API call. + * + * If the last mpv_handle is detached, the core player is destroyed. In + * addition, if there are only weak mpv_handles (such as created by + * mpv_create_weak_client() or internal scripts), these mpv_handles will + * be sent MPV_EVENT_SHUTDOWN. This function may block until these clients + * have responded to the shutdown event, and the core is finally destroyed. + */ +void mpv_destroy(mpv_handle *ctx); + +#if MPV_ENABLE_DEPRECATED +/** + * @deprecated use mpv_destroy(), which has exactly the same semantics (the + * deprecation is a mere rename) + * + * Since mpv client API version 1.29: + * If the last mpv_handle is detached, the core player is destroyed. In + * addition, if there are only weak mpv_handles (such as created by + * mpv_create_weak_client() or internal scripts), these mpv_handles will + * be sent MPV_EVENT_SHUTDOWN. This function may block until these clients + * have responded to the shutdown event, and the core is finally destroyed. + * + * Before mpv client API version 1.29: + * This left the player running. If you want to be sure that the + * player is terminated, send a "quit" command, and wait until the + * MPV_EVENT_SHUTDOWN event is received, or use mpv_terminate_destroy(). + */ +void mpv_detach_destroy(mpv_handle *ctx); +#endif + +/** + * Similar to mpv_destroy(), but brings the player and all clients down + * as well, and waits until all of them are destroyed. This function blocks. The + * advantage over mpv_destroy() is that while mpv_destroy() merely + * detaches the client handle from the player, this function quits the player, + * waits until all other clients are destroyed (i.e. all mpv_handles are + * detached), and also waits for the final termination of the player. + * + * Since mpv_destroy() is called somewhere on the way, it's not safe to + * call other functions concurrently on the same context. + * + * Since mpv client API version 1.29: + * The first call on any mpv_handle will block until the core is destroyed. + * This means it will wait until other mpv_handle have been destroyed. If you + * want asynchronous destruction, just run the "quit" command, and then react + * to the MPV_EVENT_SHUTDOWN event. + * If another mpv_handle already called mpv_terminate_destroy(), this call will + * not actually block. It will destroy the mpv_handle, and exit immediately, + * while other mpv_handles might still be uninitializing. + * + * Before mpv client API version 1.29: + * If this is called on a mpv_handle that was not created with mpv_create(), + * this function will merely send a quit command and then call + * mpv_destroy(), without waiting for the actual shutdown. + */ +void mpv_terminate_destroy(mpv_handle *ctx); + +/** + * Create a new client handle connected to the same player core as ctx. This + * context has its own event queue, its own mpv_request_event() state, its own + * mpv_request_log_messages() state, its own set of observed properties, and + * its own state for asynchronous operations. Otherwise, everything is shared. + * + * This handle should be destroyed with mpv_destroy() if no longer + * needed. The core will live as long as there is at least 1 handle referencing + * it. Any handle can make the core quit, which will result in every handle + * receiving MPV_EVENT_SHUTDOWN. + * + * This function can not be called before the main handle was initialized with + * mpv_initialize(). The new handle is always initialized, unless ctx=NULL was + * passed. + * + * @param ctx Used to get the reference to the mpv core; handle-specific + * settings and parameters are not used. + * If NULL, this function behaves like mpv_create() (ignores name). + * @param name The client name. This will be returned by mpv_client_name(). If + * the name is already in use, or contains non-alphanumeric + * characters (other than '_'), the name is modified to fit. + * If NULL, an arbitrary name is automatically chosen. + * @return a new handle, or NULL on error + */ +mpv_handle *mpv_create_client(mpv_handle *ctx, const char *name); + +/** + * This is the same as mpv_create_client(), but the created mpv_handle is + * treated as a weak reference. If all mpv_handles referencing a core are + * weak references, the core is automatically destroyed. (This still goes + * through normal uninit of course. Effectively, if the last non-weak mpv_handle + * is destroyed, then the weak mpv_handles receive MPV_EVENT_SHUTDOWN and are + * asked to terminate as well.) + * + * Note if you want to use this like refcounting: you have to be aware that + * mpv_terminate_destroy() _and_ mpv_destroy() for the last non-weak + * mpv_handle will block until all weak mpv_handles are destroyed. + */ +mpv_handle *mpv_create_weak_client(mpv_handle *ctx, const char *name); + +/** + * Load a config file. This loads and parses the file, and sets every entry in + * the config file's default section as if mpv_set_option_string() is called. + * + * The filename should be an absolute path. If it isn't, the actual path used + * is unspecified. (Note: an absolute path starts with '/' on UNIX.) If the + * file wasn't found, MPV_ERROR_INVALID_PARAMETER is returned. + * + * If a fatal error happens when parsing a config file, MPV_ERROR_OPTION_ERROR + * is returned. Errors when setting options as well as other types or errors + * are ignored (even if options do not exist). You can still try to capture + * the resulting error messages with mpv_request_log_messages(). Note that it's + * possible that some options were successfully set even if any of these errors + * happen. + * + * @param filename absolute path to the config file on the local filesystem + * @return error code + */ +int mpv_load_config_file(mpv_handle *ctx, const char *filename); + +#if MPV_ENABLE_DEPRECATED + +/** + * This does nothing since mpv 0.23.0 (API version 1.24). Below is the + * description of the old behavior. + * + * Stop the playback thread. This means the core will stop doing anything, and + * only run and answer to client API requests. This is sometimes useful; for + * example, no new frame will be queued to the video output, so doing requests + * which have to wait on the video output can run instantly. + * + * Suspension is reentrant and recursive for convenience. Any thread can call + * the suspend function multiple times, and the playback thread will remain + * suspended until the last thread resumes it. Note that during suspension, all + * clients still have concurrent access to the core, which is serialized through + * a single mutex. + * + * Call mpv_resume() to resume the playback thread. You must call mpv_resume() + * for each mpv_suspend() call. Calling mpv_resume() more often than + * mpv_suspend() is not allowed. + * + * Calling this on an uninitialized player (see mpv_create()) will deadlock. + * + * @deprecated This function, as well as mpv_resume(), are deprecated, and + * will stop doing anything soon. Their semantics were never + * well-defined, and their usefulness is extremely limited. The + * calls will remain stubs in order to keep ABI compatibility. + */ +void mpv_suspend(mpv_handle *ctx); + +/** + * See mpv_suspend(). + */ +void mpv_resume(mpv_handle *ctx); + +#endif + +/** + * Return the internal time in microseconds. This has an arbitrary start offset, + * but will never wrap or go backwards. + * + * Note that this is always the real time, and doesn't necessarily have to do + * with playback time. For example, playback could go faster or slower due to + * playback speed, or due to playback being paused. Use the "time-pos" property + * instead to get the playback status. + * + * Unlike other libmpv APIs, this can be called at absolutely any time (even + * within wakeup callbacks), as long as the context is valid. + * + * Safe to be called from mpv render API threads. + */ +int64_t mpv_get_time_us(mpv_handle *ctx); + +/** + * Data format for options and properties. The API functions to get/set + * properties and options support multiple formats, and this enum describes + * them. + */ +typedef enum mpv_format { + /** + * Invalid. Sometimes used for empty values. + */ + MPV_FORMAT_NONE = 0, + /** + * The basic type is char*. It returns the raw property string, like + * using ${=property} in input.conf (see input.rst). + * + * NULL isn't an allowed value. + * + * Warning: although the encoding is usually UTF-8, this is not always the + * case. File tags often store strings in some legacy codepage, + * and even filenames don't necessarily have to be in UTF-8 (at + * least on Linux). If you pass the strings to code that requires + * valid UTF-8, you have to sanitize it in some way. + * On Windows, filenames are always UTF-8, and libmpv converts + * between UTF-8 and UTF-16 when using win32 API functions. See + * the "Encoding of filenames" section for details. + * + * Example for reading: + * + * char *result = NULL; + * if (mpv_get_property(ctx, "property", MPV_FORMAT_STRING, &result) < 0) + * goto error; + * printf("%s\n", result); + * mpv_free(result); + * + * Or just use mpv_get_property_string(). + * + * Example for writing: + * + * char *value = "the new value"; + * // yep, you pass the address to the variable + * // (needed for symmetry with other types and mpv_get_property) + * mpv_set_property(ctx, "property", MPV_FORMAT_STRING, &value); + * + * Or just use mpv_set_property_string(). + * + */ + MPV_FORMAT_STRING = 1, + /** + * The basic type is char*. It returns the OSD property string, like + * using ${property} in input.conf (see input.rst). In many cases, this + * is the same as the raw string, but in other cases it's formatted for + * display on OSD. It's intended to be human readable. Do not attempt to + * parse these strings. + * + * Only valid when doing read access. The rest works like MPV_FORMAT_STRING. + */ + MPV_FORMAT_OSD_STRING = 2, + /** + * The basic type is int. The only allowed values are 0 ("no") + * and 1 ("yes"). + * + * Example for reading: + * + * int result; + * if (mpv_get_property(ctx, "property", MPV_FORMAT_FLAG, &result) < 0) + * goto error; + * printf("%s\n", result ? "true" : "false"); + * + * Example for writing: + * + * int flag = 1; + * mpv_set_property(ctx, "property", MPV_FORMAT_FLAG, &flag); + */ + MPV_FORMAT_FLAG = 3, + /** + * The basic type is int64_t. + */ + MPV_FORMAT_INT64 = 4, + /** + * The basic type is double. + */ + MPV_FORMAT_DOUBLE = 5, + /** + * The type is mpv_node. + * + * For reading, you usually would pass a pointer to a stack-allocated + * mpv_node value to mpv, and when you're done you call + * mpv_free_node_contents(&node). + * You're expected not to write to the data - if you have to, copy it + * first (which you have to do manually). + * + * For writing, you construct your own mpv_node, and pass a pointer to the + * API. The API will never write to your data (and copy it if needed), so + * you're free to use any form of allocation or memory management you like. + * + * Warning: when reading, always check the mpv_node.format member. For + * example, properties might change their type in future versions + * of mpv, or sometimes even during runtime. + * + * Example for reading: + * + * mpv_node result; + * if (mpv_get_property(ctx, "property", MPV_FORMAT_NODE, &result) < 0) + * goto error; + * printf("format=%d\n", (int)result.format); + * mpv_free_node_contents(&result). + * + * Example for writing: + * + * mpv_node value; + * value.format = MPV_FORMAT_STRING; + * value.u.string = "hello"; + * mpv_set_property(ctx, "property", MPV_FORMAT_NODE, &value); + */ + MPV_FORMAT_NODE = 6, + /** + * Used with mpv_node only. Can usually not be used directly. + */ + MPV_FORMAT_NODE_ARRAY = 7, + /** + * See MPV_FORMAT_NODE_ARRAY. + */ + MPV_FORMAT_NODE_MAP = 8, + /** + * A raw, untyped byte array. Only used only with mpv_node, and only in + * some very special situations. (Currently, only for the screenshot-raw + * command.) + */ + MPV_FORMAT_BYTE_ARRAY = 9 +} mpv_format; + +/** + * Generic data storage. + * + * If mpv writes this struct (e.g. via mpv_get_property()), you must not change + * the data. In some cases (mpv_get_property()), you have to free it with + * mpv_free_node_contents(). If you fill this struct yourself, you're also + * responsible for freeing it, and you must not call mpv_free_node_contents(). + */ +typedef struct mpv_node { + union { + char *string; /** valid if format==MPV_FORMAT_STRING */ + int flag; /** valid if format==MPV_FORMAT_FLAG */ + int64_t int64; /** valid if format==MPV_FORMAT_INT64 */ + double double_; /** valid if format==MPV_FORMAT_DOUBLE */ + /** + * valid if format==MPV_FORMAT_NODE_ARRAY + * or if format==MPV_FORMAT_NODE_MAP + */ + struct mpv_node_list *list; + /** + * valid if format==MPV_FORMAT_BYTE_ARRAY + */ + struct mpv_byte_array *ba; + } u; + /** + * Type of the data stored in this struct. This value rules what members in + * the given union can be accessed. The following formats are currently + * defined to be allowed in mpv_node: + * + * MPV_FORMAT_STRING (u.string) + * MPV_FORMAT_FLAG (u.flag) + * MPV_FORMAT_INT64 (u.int64) + * MPV_FORMAT_DOUBLE (u.double_) + * MPV_FORMAT_NODE_ARRAY (u.list) + * MPV_FORMAT_NODE_MAP (u.list) + * MPV_FORMAT_BYTE_ARRAY (u.ba) + * MPV_FORMAT_NONE (no member) + * + * If you encounter a value you don't know, you must not make any + * assumptions about the contents of union u. + */ + mpv_format format; +} mpv_node; + +/** + * (see mpv_node) + */ +typedef struct mpv_node_list { + /** + * Number of entries. Negative values are not allowed. + */ + int num; + /** + * MPV_FORMAT_NODE_ARRAY: + * values[N] refers to value of the Nth item + * + * MPV_FORMAT_NODE_MAP: + * values[N] refers to value of the Nth key/value pair + * + * If num > 0, values[0] to values[num-1] (inclusive) are valid. + * Otherwise, this can be NULL. + */ + mpv_node *values; + /** + * MPV_FORMAT_NODE_ARRAY: + * unused (typically NULL), access is not allowed + * + * MPV_FORMAT_NODE_MAP: + * keys[N] refers to key of the Nth key/value pair. If num > 0, keys[0] to + * keys[num-1] (inclusive) are valid. Otherwise, this can be NULL. + * The keys are in random order. The only guarantee is that keys[N] belongs + * to the value values[N]. NULL keys are not allowed. + */ + char **keys; +} mpv_node_list; + +/** + * (see mpv_node) + */ +typedef struct mpv_byte_array { + /** + * Pointer to the data. In what format the data is stored is up to whatever + * uses MPV_FORMAT_BYTE_ARRAY. + */ + void *data; + /** + * Size of the data pointed to by ptr. + */ + size_t size; +} mpv_byte_array; + +/** + * Frees any data referenced by the node. It doesn't free the node itself. + * Call this only if the mpv client API set the node. If you constructed the + * node yourself (manually), you have to free it yourself. + * + * If node->format is MPV_FORMAT_NONE, this call does nothing. Likewise, if + * the client API sets a node with this format, this function doesn't need to + * be called. (This is just a clarification that there's no danger of anything + * strange happening in these cases.) + */ +void mpv_free_node_contents(mpv_node *node); + +/** + * Set an option. Note that you can't normally set options during runtime. It + * works in uninitialized state (see mpv_create()), and in some cases in at + * runtime. + * + * Using a format other than MPV_FORMAT_NODE is equivalent to constructing a + * mpv_node with the given format and data, and passing the mpv_node to this + * function. + * + * Note: this is semi-deprecated. For most purposes, this is not needed anymore. + * Starting with mpv version 0.21.0 (version 1.23) most options can be set + * with mpv_set_property() (and related functions), and even before + * mpv_initialize(). In some obscure corner cases, using this function + * to set options might still be required (see below, and also section + * "Inconsistencies between options and properties" on the manpage). Once + * these are resolved, the option setting functions might be fully + * deprecated. + * + * The following options still need to be set either _before_ + * mpv_initialize() with mpv_set_property() (or related functions), or + * with mpv_set_option() (or related functions) at any time: + * - options shadowed by deprecated properties: + * - demuxer (property deprecated in 0.21.0) + * - idle (property deprecated in 0.21.0) + * - fps (property deprecated in 0.21.0) + * - cache (property deprecated in 0.21.0) + * - length (property deprecated in 0.10.0) + * - audio-samplerate (property deprecated in 0.10.0) + * - audio-channels (property deprecated in 0.10.0) + * - audio-format (property deprecated in 0.10.0) + * - deprecated options shadowed by properties: + * - chapter (option deprecated in 0.21.0) + * - playlist-pos (option deprecated in 0.21.0) + * The deprecated properties were removed in mpv 0.23.0. + * + * @param name Option name. This is the same as on the mpv command line, but + * without the leading "--". + * @param format see enum mpv_format. + * @param[in] data Option value (according to the format). + * @return error code + */ +int mpv_set_option(mpv_handle *ctx, const char *name, mpv_format format, + void *data); + +/** + * Convenience function to set an option to a string value. This is like + * calling mpv_set_option() with MPV_FORMAT_STRING. + * + * @return error code + */ +int mpv_set_option_string(mpv_handle *ctx, const char *name, const char *data); + +/** + * Send a command to the player. Commands are the same as those used in + * input.conf, except that this function takes parameters in a pre-split + * form. + * + * The commands and their parameters are documented in input.rst. + * + * Does not use OSD and string expansion by default (unlike mpv_command_string() + * and input.conf). + * + * @param[in] args NULL-terminated list of strings. Usually, the first item + * is the command, and the following items are arguments. + * @return error code + */ +int mpv_command(mpv_handle *ctx, const char **args); + +/** + * Same as mpv_command(), but allows passing structured data in any format. + * In particular, calling mpv_command() is exactly like calling + * mpv_command_node() with the format set to MPV_FORMAT_NODE_ARRAY, and + * every arg passed in order as MPV_FORMAT_STRING. + * + * Does not use OSD and string expansion by default. + * + * The args argument can have one of the following formats: + * + * MPV_FORMAT_NODE_ARRAY: + * Positional arguments. Each entry is an argument using an arbitrary + * format (the format must be compatible to the used command). Usually, + * the first item is the command name (as MPV_FORMAT_STRING). The order + * of arguments is as documented in each command description. + * + * MPV_FORMAT_NODE_MAP: + * Named arguments. This requires at least an entry with the key "name" + * to be present, which must be a string, and contains the command name. + * The special entry "_flags" is optional, and if present, must be an + * array of strings, each being a command prefix to apply. All other + * entries are interpreted as arguments. They must use the argument names + * as documented in each command description. Some commands do not + * support named arguments at all, and must use MPV_FORMAT_NODE_ARRAY. + * + * @param[in] args mpv_node with format set to one of the values documented + * above (see there for details) + * @param[out] result Optional, pass NULL if unused. If not NULL, and if the + * function succeeds, this is set to command-specific return + * data. You must call mpv_free_node_contents() to free it + * (again, only if the command actually succeeds). + * Not many commands actually use this at all. + * @return error code (the result parameter is not set on error) + */ +int mpv_command_node(mpv_handle *ctx, mpv_node *args, mpv_node *result); + +/** + * This is essentially identical to mpv_command() but it also returns a result. + * + * Does not use OSD and string expansion by default. + * + * @param[in] args NULL-terminated list of strings. Usually, the first item + * is the command, and the following items are arguments. + * @param[out] result Optional, pass NULL if unused. If not NULL, and if the + * function succeeds, this is set to command-specific return + * data. You must call mpv_free_node_contents() to free it + * (again, only if the command actually succeeds). + * Not many commands actually use this at all. + * @return error code (the result parameter is not set on error) + */ +int mpv_command_ret(mpv_handle *ctx, const char **args, mpv_node *result); + +/** + * Same as mpv_command, but use input.conf parsing for splitting arguments. + * This is slightly simpler, but also more error prone, since arguments may + * need quoting/escaping. + * + * This also has OSD and string expansion enabled by default. + */ +int mpv_command_string(mpv_handle *ctx, const char *args); + +/** + * Same as mpv_command, but run the command asynchronously. + * + * Commands are executed asynchronously. You will receive a + * MPV_EVENT_COMMAND_REPLY event. This event will also have an + * error code set if running the command failed. For commands that + * return data, the data is put into mpv_event_command.result. + * + * The only case when you do not receive an event is when the function call + * itself fails. This happens only if parsing the command itself (or otherwise + * validating it) fails, i.e. the return code of the API call is not 0 or + * positive. + * + * Safe to be called from mpv render API threads. + * + * @param reply_userdata the value mpv_event.reply_userdata of the reply will + * be set to (see section about asynchronous calls) + * @param args NULL-terminated list of strings (see mpv_command()) + * @return error code (if parsing or queuing the command fails) + */ +int mpv_command_async(mpv_handle *ctx, uint64_t reply_userdata, + const char **args); + +/** + * Same as mpv_command_node(), but run it asynchronously. Basically, this + * function is to mpv_command_node() what mpv_command_async() is to + * mpv_command(). + * + * See mpv_command_async() for details. + * + * Safe to be called from mpv render API threads. + * + * @param reply_userdata the value mpv_event.reply_userdata of the reply will + * be set to (see section about asynchronous calls) + * @param args as in mpv_command_node() + * @return error code (if parsing or queuing the command fails) + */ +int mpv_command_node_async(mpv_handle *ctx, uint64_t reply_userdata, + mpv_node *args); + +/** + * Signal to all async requests with the matching ID to abort. This affects + * the following API calls: + * + * mpv_command_async + * mpv_command_node_async + * + * All of these functions take a reply_userdata parameter. This API function + * tells all requests with the matching reply_userdata value to try to return + * as soon as possible. If there are multiple requests with matching ID, it + * aborts all of them. + * + * This API function is mostly asynchronous itself. It will not wait until the + * command is aborted. Instead, the command will terminate as usual, but with + * some work not done. How this is signaled depends on the specific command (for + * example, the "subprocess" command will indicate it by "killed_by_us" set to + * true in the result). How long it takes also depends on the situation. The + * aborting process is completely asynchronous. + * + * Not all commands may support this functionality. In this case, this function + * will have no effect. The same is true if the request using the passed + * reply_userdata has already terminated, has not been started yet, or was + * never in use at all. + * + * You have to be careful of race conditions: the time during which the abort + * request will be effective is _after_ e.g. mpv_command_async() has returned, + * and before the command has signaled completion with MPV_EVENT_COMMAND_REPLY. + * + * @param reply_userdata ID of the request to be aborted (see above) + */ +void mpv_abort_async_command(mpv_handle *ctx, uint64_t reply_userdata); + +/** + * Set a property to a given value. Properties are essentially variables which + * can be queried or set at runtime. For example, writing to the pause property + * will actually pause or unpause playback. + * + * If the format doesn't match with the internal format of the property, access + * usually will fail with MPV_ERROR_PROPERTY_FORMAT. In some cases, the data + * is automatically converted and access succeeds. For example, MPV_FORMAT_INT64 + * is always converted to MPV_FORMAT_DOUBLE, and access using MPV_FORMAT_STRING + * usually invokes a string parser. The same happens when calling this function + * with MPV_FORMAT_NODE: the underlying format may be converted to another + * type if possible. + * + * Using a format other than MPV_FORMAT_NODE is equivalent to constructing a + * mpv_node with the given format and data, and passing the mpv_node to this + * function. (Before API version 1.21, this was different.) + * + * Note: starting with mpv 0.21.0 (client API version 1.23), this can be used to + * set options in general. It even can be used before mpv_initialize() + * has been called. If called before mpv_initialize(), setting properties + * not backed by options will result in MPV_ERROR_PROPERTY_UNAVAILABLE. + * In some cases, properties and options still conflict. In these cases, + * mpv_set_property() accesses the options before mpv_initialize(), and + * the properties after mpv_initialize(). These conflicts will be removed + * in mpv 0.23.0. See mpv_set_option() for further remarks. + * + * @param name The property name. See input.rst for a list of properties. + * @param format see enum mpv_format. + * @param[in] data Option value. + * @return error code + */ +int mpv_set_property(mpv_handle *ctx, const char *name, mpv_format format, + void *data); + +/** + * Convenience function to set a property to a string value. + * + * This is like calling mpv_set_property() with MPV_FORMAT_STRING. + */ +int mpv_set_property_string(mpv_handle *ctx, const char *name, const char *data); + +/** + * Set a property asynchronously. You will receive the result of the operation + * as MPV_EVENT_SET_PROPERTY_REPLY event. The mpv_event.error field will contain + * the result status of the operation. Otherwise, this function is similar to + * mpv_set_property(). + * + * Safe to be called from mpv render API threads. + * + * @param reply_userdata see section about asynchronous calls + * @param name The property name. + * @param format see enum mpv_format. + * @param[in] data Option value. The value will be copied by the function. It + * will never be modified by the client API. + * @return error code if sending the request failed + */ +int mpv_set_property_async(mpv_handle *ctx, uint64_t reply_userdata, + const char *name, mpv_format format, void *data); + +/** + * Read the value of the given property. + * + * If the format doesn't match with the internal format of the property, access + * usually will fail with MPV_ERROR_PROPERTY_FORMAT. In some cases, the data + * is automatically converted and access succeeds. For example, MPV_FORMAT_INT64 + * is always converted to MPV_FORMAT_DOUBLE, and access using MPV_FORMAT_STRING + * usually invokes a string formatter. + * + * @param name The property name. + * @param format see enum mpv_format. + * @param[out] data Pointer to the variable holding the option value. On + * success, the variable will be set to a copy of the option + * value. For formats that require dynamic memory allocation, + * you can free the value with mpv_free() (strings) or + * mpv_free_node_contents() (MPV_FORMAT_NODE). + * @return error code + */ +int mpv_get_property(mpv_handle *ctx, const char *name, mpv_format format, + void *data); + +/** + * Return the value of the property with the given name as string. This is + * equivalent to mpv_get_property() with MPV_FORMAT_STRING. + * + * See MPV_FORMAT_STRING for character encoding issues. + * + * On error, NULL is returned. Use mpv_get_property() if you want fine-grained + * error reporting. + * + * @param name The property name. + * @return Property value, or NULL if the property can't be retrieved. Free + * the string with mpv_free(). + */ +char *mpv_get_property_string(mpv_handle *ctx, const char *name); + +/** + * Return the property as "OSD" formatted string. This is the same as + * mpv_get_property_string, but using MPV_FORMAT_OSD_STRING. + * + * @return Property value, or NULL if the property can't be retrieved. Free + * the string with mpv_free(). + */ +char *mpv_get_property_osd_string(mpv_handle *ctx, const char *name); + +/** + * Get a property asynchronously. You will receive the result of the operation + * as well as the property data with the MPV_EVENT_GET_PROPERTY_REPLY event. + * You should check the mpv_event.error field on the reply event. + * + * Safe to be called from mpv render API threads. + * + * @param reply_userdata see section about asynchronous calls + * @param name The property name. + * @param format see enum mpv_format. + * @return error code if sending the request failed + */ +int mpv_get_property_async(mpv_handle *ctx, uint64_t reply_userdata, + const char *name, mpv_format format); + +/** + * Get a notification whenever the given property changes. You will receive + * updates as MPV_EVENT_PROPERTY_CHANGE. Note that this is not very precise: + * for some properties, it may not send updates even if the property changed. + * This depends on the property, and it's a valid feature request to ask for + * better update handling of a specific property. (For some properties, like + * ``clock``, which shows the wall clock, this mechanism doesn't make too + * much sense anyway.) + * + * Property changes are coalesced: the change events are returned only once the + * event queue becomes empty (e.g. mpv_wait_event() would block or return + * MPV_EVENT_NONE), and then only one event per changed property is returned. + * + * You always get an initial change notification. This is meant to initialize + * the user's state to the current value of the property. + * + * Normally, change events are sent only if the property value changes according + * to the requested format. mpv_event_property will contain the property value + * as data member. + * + * Warning: if a property is unavailable or retrieving it caused an error, + * MPV_FORMAT_NONE will be set in mpv_event_property, even if the + * format parameter was set to a different value. In this case, the + * mpv_event_property.data field is invalid. + * + * If the property is observed with the format parameter set to MPV_FORMAT_NONE, + * you get low-level notifications whether the property _may_ have changed, and + * the data member in mpv_event_property will be unset. With this mode, you + * will have to determine yourself whether the property really changed. On the + * other hand, this mechanism can be faster and uses less resources. + * + * Observing a property that doesn't exist is allowed. (Although it may still + * cause some sporadic change events.) + * + * Keep in mind that you will get change notifications even if you change a + * property yourself. Try to avoid endless feedback loops, which could happen + * if you react to the change notifications triggered by your own change. + * + * Only the mpv_handle on which this was called will receive the property + * change events, or can unobserve them. + * + * Safe to be called from mpv render API threads. + * + * @param reply_userdata This will be used for the mpv_event.reply_userdata + * field for the received MPV_EVENT_PROPERTY_CHANGE + * events. (Also see section about asynchronous calls, + * although this function is somewhat different from + * actual asynchronous calls.) + * If you have no use for this, pass 0. + * Also see mpv_unobserve_property(). + * @param name The property name. + * @param format see enum mpv_format. Can be MPV_FORMAT_NONE to omit values + * from the change events. + * @return error code (usually fails only on OOM or unsupported format) + */ +int mpv_observe_property(mpv_handle *mpv, uint64_t reply_userdata, + const char *name, mpv_format format); + +/** + * Undo mpv_observe_property(). This will remove all observed properties for + * which the given number was passed as reply_userdata to mpv_observe_property. + * + * Safe to be called from mpv render API threads. + * + * @param registered_reply_userdata ID that was passed to mpv_observe_property + * @return negative value is an error code, >=0 is number of removed properties + * on success (includes the case when 0 were removed) + */ +int mpv_unobserve_property(mpv_handle *mpv, uint64_t registered_reply_userdata); + +typedef enum mpv_event_id { + /** + * Nothing happened. Happens on timeouts or sporadic wakeups. + */ + MPV_EVENT_NONE = 0, + /** + * Happens when the player quits. The player enters a state where it tries + * to disconnect all clients. Most requests to the player will fail, and + * the client should react to this and quit with mpv_destroy() as soon as + * possible. + */ + MPV_EVENT_SHUTDOWN = 1, + /** + * See mpv_request_log_messages(). + */ + MPV_EVENT_LOG_MESSAGE = 2, + /** + * Reply to a mpv_get_property_async() request. + * See also mpv_event and mpv_event_property. + */ + MPV_EVENT_GET_PROPERTY_REPLY = 3, + /** + * Reply to a mpv_set_property_async() request. + * (Unlike MPV_EVENT_GET_PROPERTY, mpv_event_property is not used.) + */ + MPV_EVENT_SET_PROPERTY_REPLY = 4, + /** + * Reply to a mpv_command_async() or mpv_command_node_async() request. + * See also mpv_event and mpv_event_command. + */ + MPV_EVENT_COMMAND_REPLY = 5, + /** + * Notification before playback start of a file (before the file is loaded). + */ + MPV_EVENT_START_FILE = 6, + /** + * Notification after playback end (after the file was unloaded). + * See also mpv_event and mpv_event_end_file. + */ + MPV_EVENT_END_FILE = 7, + /** + * Notification when the file has been loaded (headers were read etc.), and + * decoding starts. + */ + MPV_EVENT_FILE_LOADED = 8, +#if MPV_ENABLE_DEPRECATED + /** + * The list of video/audio/subtitle tracks was changed. (E.g. a new track + * was found. This doesn't necessarily indicate a track switch; for this, + * MPV_EVENT_TRACK_SWITCHED is used.) + * + * @deprecated This is equivalent to using mpv_observe_property() on the + * "track-list" property. The event is redundant, and might + * be removed in the far future. + */ + MPV_EVENT_TRACKS_CHANGED = 9, + /** + * A video/audio/subtitle track was switched on or off. + * + * @deprecated This is equivalent to using mpv_observe_property() on the + * "vid", "aid", and "sid" properties. The event is redundant, + * and might be removed in the far future. + */ + MPV_EVENT_TRACK_SWITCHED = 10, +#endif + /** + * Idle mode was entered. In this mode, no file is played, and the playback + * core waits for new commands. (The command line player normally quits + * instead of entering idle mode, unless --idle was specified. If mpv + * was started with mpv_create(), idle mode is enabled by default.) + */ + MPV_EVENT_IDLE = 11, +#if MPV_ENABLE_DEPRECATED + /** + * Playback was paused. This indicates the user pause state. + * + * The user pause state is the state the user requested (changed with the + * "pause" property). There is an internal pause state too, which is entered + * if e.g. the network is too slow (the "core-idle" property generally + * indicates whether the core is playing or waiting). + * + * This event is sent whenever any pause states change, not only the user + * state. You might get multiple events in a row while these states change + * independently. But the event ID sent always indicates the user pause + * state. + * + * If you don't want to deal with this, use mpv_observe_property() on the + * "pause" property and ignore MPV_EVENT_PAUSE/UNPAUSE. Likewise, the + * "core-idle" property tells you whether video is actually playing or not. + * + * @deprecated The event is redundant with mpv_observe_property() as + * mentioned above, and might be removed in the far future. + */ + MPV_EVENT_PAUSE = 12, + /** + * Playback was unpaused. See MPV_EVENT_PAUSE for not so obvious details. + * + * @deprecated The event is redundant with mpv_observe_property() as + * explained in the MPV_EVENT_PAUSE comments, and might be + * removed in the far future. + */ + MPV_EVENT_UNPAUSE = 13, + /** + * Sent every time after a video frame is displayed. Note that currently, + * this will be sent in lower frequency if there is no video, or playback + * is paused - but that will be removed in the future, and it will be + * restricted to video frames only. + * + * @deprecated Use mpv_observe_property() with relevant properties instead + * (such as "playback-time"). + */ + MPV_EVENT_TICK = 14, + /** + * @deprecated This was used internally with the internal "script_dispatch" + * command to dispatch keyboard and mouse input for the OSC. + * It was never useful in general and has been completely + * replaced with "script-binding". + * This event never happens anymore, and is included in this + * header only for compatibility. + */ + MPV_EVENT_SCRIPT_INPUT_DISPATCH = 15, +#endif + /** + * Triggered by the script-message input command. The command uses the + * first argument of the command as client name (see mpv_client_name()) to + * dispatch the message, and passes along all arguments starting from the + * second argument as strings. + * See also mpv_event and mpv_event_client_message. + */ + MPV_EVENT_CLIENT_MESSAGE = 16, + /** + * Happens after video changed in some way. This can happen on resolution + * changes, pixel format changes, or video filter changes. The event is + * sent after the video filters and the VO are reconfigured. Applications + * embedding a mpv window should listen to this event in order to resize + * the window if needed. + * Note that this event can happen sporadically, and you should check + * yourself whether the video parameters really changed before doing + * something expensive. + */ + MPV_EVENT_VIDEO_RECONFIG = 17, + /** + * Similar to MPV_EVENT_VIDEO_RECONFIG. This is relatively uninteresting, + * because there is no such thing as audio output embedding. + */ + MPV_EVENT_AUDIO_RECONFIG = 18, +#if MPV_ENABLE_DEPRECATED + /** + * Happens when metadata (like file tags) is possibly updated. (It's left + * unspecified whether this happens on file start or only when it changes + * within a file.) + * + * @deprecated This is equivalent to using mpv_observe_property() on the + * "metadata" property. The event is redundant, and might + * be removed in the far future. + */ + MPV_EVENT_METADATA_UPDATE = 19, +#endif + /** + * Happens when a seek was initiated. Playback stops. Usually it will + * resume with MPV_EVENT_PLAYBACK_RESTART as soon as the seek is finished. + */ + MPV_EVENT_SEEK = 20, + /** + * There was a discontinuity of some sort (like a seek), and playback + * was reinitialized. Usually happens after seeking, or ordered chapter + * segment switches. The main purpose is allowing the client to detect + * when a seek request is finished. + */ + MPV_EVENT_PLAYBACK_RESTART = 21, + /** + * Event sent due to mpv_observe_property(). + * See also mpv_event and mpv_event_property. + */ + MPV_EVENT_PROPERTY_CHANGE = 22, +#if MPV_ENABLE_DEPRECATED + /** + * Happens when the current chapter changes. + * + * @deprecated This is equivalent to using mpv_observe_property() on the + * "chapter" property. The event is redundant, and might + * be removed in the far future. + */ + MPV_EVENT_CHAPTER_CHANGE = 23, +#endif + /** + * Happens if the internal per-mpv_handle ringbuffer overflows, and at + * least 1 event had to be dropped. This can happen if the client doesn't + * read the event queue quickly enough with mpv_wait_event(), or if the + * client makes a very large number of asynchronous calls at once. + * + * Event delivery will continue normally once this event was returned + * (this forces the client to empty the queue completely). + */ + MPV_EVENT_QUEUE_OVERFLOW = 24, + /** + * Triggered if a hook handler was registered with mpv_hook_add(), and the + * hook is invoked. If you receive this, you must handle it, and continue + * the hook with mpv_hook_continue(). + * See also mpv_event and mpv_event_hook. + */ + MPV_EVENT_HOOK = 25, + // Internal note: adjust INTERNAL_EVENT_BASE when adding new events. +} mpv_event_id; + +/** + * Return a string describing the event. For unknown events, NULL is returned. + * + * Note that all events actually returned by the API will also yield a non-NULL + * string with this function. + * + * @param event event ID, see see enum mpv_event_id + * @return A static string giving a short symbolic name of the event. It + * consists of lower-case alphanumeric characters and can include "-" + * characters. This string is suitable for use in e.g. scripting + * interfaces. + * The string is completely static, i.e. doesn't need to be deallocated, + * and is valid forever. + */ +const char *mpv_event_name(mpv_event_id event); + +typedef struct mpv_event_property { + /** + * Name of the property. + */ + const char *name; + /** + * Format of the data field in the same struct. See enum mpv_format. + * This is always the same format as the requested format, except when + * the property could not be retrieved (unavailable, or an error happened), + * in which case the format is MPV_FORMAT_NONE. + */ + mpv_format format; + /** + * Received property value. Depends on the format. This is like the + * pointer argument passed to mpv_get_property(). + * + * For example, for MPV_FORMAT_STRING you get the string with: + * + * char *value = *(char **)(event_property->data); + * + * Note that this is set to NULL if retrieving the property failed (the + * format will be MPV_FORMAT_NONE). + */ + void *data; +} mpv_event_property; + +/** + * Numeric log levels. The lower the number, the more important the message is. + * MPV_LOG_LEVEL_NONE is never used when receiving messages. The string in + * the comment after the value is the name of the log level as used for the + * mpv_request_log_messages() function. + * Unused numeric values are unused, but reserved for future use. + */ +typedef enum mpv_log_level { + MPV_LOG_LEVEL_NONE = 0, /// "no" - disable absolutely all messages + MPV_LOG_LEVEL_FATAL = 10, /// "fatal" - critical/aborting errors + MPV_LOG_LEVEL_ERROR = 20, /// "error" - simple errors + MPV_LOG_LEVEL_WARN = 30, /// "warn" - possible problems + MPV_LOG_LEVEL_INFO = 40, /// "info" - informational message + MPV_LOG_LEVEL_V = 50, /// "v" - noisy informational message + MPV_LOG_LEVEL_DEBUG = 60, /// "debug" - very noisy technical information + MPV_LOG_LEVEL_TRACE = 70, /// "trace" - extremely noisy +} mpv_log_level; + +typedef struct mpv_event_log_message { + /** + * The module prefix, identifies the sender of the message. As a special + * case, if the message buffer overflows, this will be set to the string + * "overflow" (which doesn't appear as prefix otherwise), and the text + * field will contain an informative message. + */ + const char *prefix; + /** + * The log level as string. See mpv_request_log_messages() for possible + * values. The level "no" is never used here. + */ + const char *level; + /** + * The log message. It consists of 1 line of text, and is terminated with + * a newline character. (Before API version 1.6, it could contain multiple + * or partial lines.) + */ + const char *text; + /** + * The same contents as the level field, but as a numeric ID. + * Since API version 1.6. + */ + mpv_log_level log_level; +} mpv_event_log_message; + +/// Since API version 1.9. +typedef enum mpv_end_file_reason { + /** + * The end of file was reached. Sometimes this may also happen on + * incomplete or corrupted files, or if the network connection was + * interrupted when playing a remote file. It also happens if the + * playback range was restricted with --end or --frames or similar. + */ + MPV_END_FILE_REASON_EOF = 0, + /** + * Playback was stopped by an external action (e.g. playlist controls). + */ + MPV_END_FILE_REASON_STOP = 2, + /** + * Playback was stopped by the quit command or player shutdown. + */ + MPV_END_FILE_REASON_QUIT = 3, + /** + * Some kind of error happened that lead to playback abort. Does not + * necessarily happen on incomplete or broken files (in these cases, both + * MPV_END_FILE_REASON_ERROR or MPV_END_FILE_REASON_EOF are possible). + * + * mpv_event_end_file.error will be set. + */ + MPV_END_FILE_REASON_ERROR = 4, + /** + * The file was a playlist or similar. When the playlist is read, its + * entries will be appended to the playlist after the entry of the current + * file, the entry of the current file is removed, and a MPV_EVENT_END_FILE + * event is sent with reason set to MPV_END_FILE_REASON_REDIRECT. Then + * playback continues with the playlist contents. + * Since API version 1.18. + */ + MPV_END_FILE_REASON_REDIRECT = 5, +} mpv_end_file_reason; + +typedef struct mpv_event_end_file { + /** + * Corresponds to the values in enum mpv_end_file_reason (the "int" type + * will be replaced with mpv_end_file_reason on the next ABI bump). + * + * Unknown values should be treated as unknown. + */ + int reason; + /** + * If reason==MPV_END_FILE_REASON_ERROR, this contains a mpv error code + * (one of MPV_ERROR_...) giving an approximate reason why playback + * failed. In other cases, this field is 0 (no error). + * Since API version 1.9. + */ + int error; +} mpv_event_end_file; + +#if MPV_ENABLE_DEPRECATED +/** @deprecated see MPV_EVENT_SCRIPT_INPUT_DISPATCH for remarks + */ +typedef struct mpv_event_script_input_dispatch { + int arg0; + const char *type; +} mpv_event_script_input_dispatch; +#endif + +typedef struct mpv_event_client_message { + /** + * Arbitrary arguments chosen by the sender of the message. If num_args > 0, + * you can access args[0] through args[num_args - 1] (inclusive). What + * these arguments mean is up to the sender and receiver. + * None of the valid items are NULL. + */ + int num_args; + const char **args; +} mpv_event_client_message; + +typedef struct mpv_event_hook { + /** + * The hook name as passed to mpv_hook_add(). + */ + const char *name; + /** + * Internal ID that must be passed to mpv_hook_continue(). + */ + uint64_t id; +} mpv_event_hook; + +// Since API version 1.102. +typedef struct mpv_event_command { + /** + * Result data of the command. Note that success/failure is signaled + * separately via mpv_event.error. This field is only for result data + * in case of success. Most commands leave it at MPV_FORMAT_NONE. Set + * to MPV_FORMAT_NONE on failure. + */ + mpv_node result; +} mpv_event_command; + +typedef struct mpv_event { + /** + * One of mpv_event. Keep in mind that later ABI compatible releases might + * add new event types. These should be ignored by the API user. + */ + mpv_event_id event_id; + /** + * This is mainly used for events that are replies to (asynchronous) + * requests. It contains a status code, which is >= 0 on success, or < 0 + * on error (a mpv_error value). Usually, this will be set if an + * asynchronous request fails. + * Used for: + * MPV_EVENT_GET_PROPERTY_REPLY + * MPV_EVENT_SET_PROPERTY_REPLY + * MPV_EVENT_COMMAND_REPLY + */ + int error; + /** + * If the event is in reply to a request (made with this API and this + * API handle), this is set to the reply_userdata parameter of the request + * call. Otherwise, this field is 0. + * Used for: + * MPV_EVENT_GET_PROPERTY_REPLY + * MPV_EVENT_SET_PROPERTY_REPLY + * MPV_EVENT_COMMAND_REPLY + * MPV_EVENT_PROPERTY_CHANGE + * MPV_EVENT_HOOK + */ + uint64_t reply_userdata; + /** + * The meaning and contents of the data member depend on the event_id: + * MPV_EVENT_GET_PROPERTY_REPLY: mpv_event_property* + * MPV_EVENT_PROPERTY_CHANGE: mpv_event_property* + * MPV_EVENT_LOG_MESSAGE: mpv_event_log_message* + * MPV_EVENT_CLIENT_MESSAGE: mpv_event_client_message* + * MPV_EVENT_END_FILE: mpv_event_end_file* + * MPV_EVENT_HOOK: mpv_event_hook* + * MPV_EVENT_COMMAND_REPLY* mpv_event_command* + * other: NULL + * + * Note: future enhancements might add new event structs for existing or new + * event types. + */ + void *data; +} mpv_event; + +/** + * Enable or disable the given event. + * + * Some events are enabled by default. Some events can't be disabled. + * + * (Informational note: currently, all events are enabled by default, except + * MPV_EVENT_TICK.) + * + * Safe to be called from mpv render API threads. + * + * @param event See enum mpv_event_id. + * @param enable 1 to enable receiving this event, 0 to disable it. + * @return error code + */ +int mpv_request_event(mpv_handle *ctx, mpv_event_id event, int enable); + +/** + * Enable or disable receiving of log messages. These are the messages the + * command line player prints to the terminal. This call sets the minimum + * required log level for a message to be received with MPV_EVENT_LOG_MESSAGE. + * + * @param min_level Minimal log level as string. Valid log levels: + * no fatal error warn info v debug trace + * The value "no" disables all messages. This is the default. + * An exception is the value "terminal-default", which uses the + * log level as set by the "--msg-level" option. This works + * even if the terminal is disabled. (Since API version 1.19.) + * Also see mpv_log_level. + * @return error code + */ +int mpv_request_log_messages(mpv_handle *ctx, const char *min_level); + +/** + * Wait for the next event, or until the timeout expires, or if another thread + * makes a call to mpv_wakeup(). Passing 0 as timeout will never wait, and + * is suitable for polling. + * + * The internal event queue has a limited size (per client handle). If you + * don't empty the event queue quickly enough with mpv_wait_event(), it will + * overflow and silently discard further events. If this happens, making + * asynchronous requests will fail as well (with MPV_ERROR_EVENT_QUEUE_FULL). + * + * Only one thread is allowed to call this on the same mpv_handle at a time. + * The API won't complain if more than one thread calls this, but it will cause + * race conditions in the client when accessing the shared mpv_event struct. + * Note that most other API functions are not restricted by this, and no API + * function internally calls mpv_wait_event(). Additionally, concurrent calls + * to different mpv_handles are always safe. + * + * As long as the timeout is 0, this is safe to be called from mpv render API + * threads. + * + * @param timeout Timeout in seconds, after which the function returns even if + * no event was received. A MPV_EVENT_NONE is returned on + * timeout. A value of 0 will disable waiting. Negative values + * will wait with an infinite timeout. + * @return A struct containing the event ID and other data. The pointer (and + * fields in the struct) stay valid until the next mpv_wait_event() + * call, or until the mpv_handle is destroyed. You must not write to + * the struct, and all memory referenced by it will be automatically + * released by the API on the next mpv_wait_event() call, or when the + * context is destroyed. The return value is never NULL. + */ +mpv_event *mpv_wait_event(mpv_handle *ctx, double timeout); + +/** + * Interrupt the current mpv_wait_event() call. This will wake up the thread + * currently waiting in mpv_wait_event(). If no thread is waiting, the next + * mpv_wait_event() call will return immediately (this is to avoid lost + * wakeups). + * + * mpv_wait_event() will receive a MPV_EVENT_NONE if it's woken up due to + * this call. But note that this dummy event might be skipped if there are + * already other events queued. All what counts is that the waiting thread + * is woken up at all. + * + * Safe to be called from mpv render API threads. + */ +void mpv_wakeup(mpv_handle *ctx); + +/** + * Set a custom function that should be called when there are new events. Use + * this if blocking in mpv_wait_event() to wait for new events is not feasible. + * + * Keep in mind that the callback will be called from foreign threads. You + * must not make any assumptions of the environment, and you must return as + * soon as possible (i.e. no long blocking waits). Exiting the callback through + * any other means than a normal return is forbidden (no throwing exceptions, + * no longjmp() calls). You must not change any local thread state (such as + * the C floating point environment). + * + * You are not allowed to call any client API functions inside of the callback. + * In particular, you should not do any processing in the callback, but wake up + * another thread that does all the work. The callback is meant strictly for + * notification only, and is called from arbitrary core parts of the player, + * that make no considerations for reentrant API use or allowing the callee to + * spend a lot of time doing other things. Keep in mind that it's also possible + * that the callback is called from a thread while a mpv API function is called + * (i.e. it can be reentrant). + * + * In general, the client API expects you to call mpv_wait_event() to receive + * notifications, and the wakeup callback is merely a helper utility to make + * this easier in certain situations. Note that it's possible that there's + * only one wakeup callback invocation for multiple events. You should call + * mpv_wait_event() with no timeout until MPV_EVENT_NONE is reached, at which + * point the event queue is empty. + * + * If you actually want to do processing in a callback, spawn a thread that + * does nothing but call mpv_wait_event() in a loop and dispatches the result + * to a callback. + * + * Only one wakeup callback can be set. + * + * @param cb function that should be called if a wakeup is required + * @param d arbitrary userdata passed to cb + */ +void mpv_set_wakeup_callback(mpv_handle *ctx, void (*cb)(void *d), void *d); + +/** + * Block until all asynchronous requests are done. This affects functions like + * mpv_command_async(), which return immediately and return their result as + * events. + * + * This is a helper, and somewhat equivalent to calling mpv_wait_event() in a + * loop until all known asynchronous requests have sent their reply as event, + * except that the event queue is not emptied. + * + * In case you called mpv_suspend() before, this will also forcibly reset the + * suspend counter of the given handle. + */ +void mpv_wait_async_requests(mpv_handle *ctx); + +/** + * A hook is like a synchronous event that blocks the player. You register + * a hook handler with this function. You will get an event, which you need + * to handle, and once things are ready, you can let the player continue with + * mpv_hook_continue(). + * + * Currently, hooks can't be removed explicitly. But they will be implicitly + * removed if the mpv_handle it was registered with is destroyed. This also + * continues the hook if it was being handled by the destroyed mpv_handle (but + * this should be avoided, as it might mess up order of hook execution). + * + * Hook handlers are ordered globally by priority and order of registration. + * Handlers for the same hook with same priority are invoked in order of + * registration (the handler registered first is run first). Handlers with + * lower priority are run first (which seems backward). + * + * See the "Hooks" section in the manpage to see which hooks are currently + * defined. + * + * Some hooks might be reentrant (so you get multiple MPV_EVENT_HOOK for the + * same hook). If this can happen for a specific hook type, it will be + * explicitly documented in the manpage. + * + * Only the mpv_handle on which this was called will receive the hook events, + * or can "continue" them. + * + * @param reply_userdata This will be used for the mpv_event.reply_userdata + * field for the received MPV_EVENT_HOOK events. + * If you have no use for this, pass 0. + * @param name The hook name. This should be one of the documented names. But + * if the name is unknown, the hook event will simply be never + * raised. + * @param priority See remarks above. Use 0 as a neutral default. + * @return error code (usually fails only on OOM) + */ +int mpv_hook_add(mpv_handle *ctx, uint64_t reply_userdata, + const char *name, int priority); + +/** + * Respond to a MPV_EVENT_HOOK event. You must call this after you have handled + * the event. There is no way to "cancel" or "stop" the hook. + * + * Calling this will will typically unblock the player for whatever the hook + * is responsible for (e.g. for the "on_load" hook it lets it continue + * playback). + * + * It is explicitly undefined behavior to call this more than once for each + * MPV_EVENT_HOOK, to pass an incorrect ID, or to call this on a mpv_handle + * different from the one that registered the handler and received the event. + * + * @param id This must be the value of the mpv_event_hook.id field for the + * corresponding MPV_EVENT_HOOK. + * @return error code + */ +int mpv_hook_continue(mpv_handle *ctx, uint64_t id); + +#if MPV_ENABLE_DEPRECATED + +/** + * Return a UNIX file descriptor referring to the read end of a pipe. This + * pipe can be used to wake up a poll() based processing loop. The purpose of + * this function is very similar to mpv_set_wakeup_callback(), and provides + * a primitive mechanism to handle coordinating a foreign event loop and the + * libmpv event loop. The pipe is non-blocking. It's closed when the mpv_handle + * is destroyed. This function always returns the same value (on success). + * + * This is in fact implemented using the same underlying code as for + * mpv_set_wakeup_callback() (though they don't conflict), and it is as if each + * callback invocation writes a single 0 byte to the pipe. When the pipe + * becomes readable, the code calling poll() (or select()) on the pipe should + * read all contents of the pipe and then call mpv_wait_event(c, 0) until + * no new events are returned. The pipe contents do not matter and can just + * be discarded. There is not necessarily one byte per readable event in the + * pipe. For example, the pipes are non-blocking, and mpv won't block if the + * pipe is full. Pipes are normally limited to 4096 bytes, so if there are + * more than 4096 events, the number of readable bytes can not equal the number + * of events queued. Also, it's possible that mpv does not write to the pipe + * once it's guaranteed that the client was already signaled. See the example + * below how to do it correctly. + * + * Example: + * + * int pipefd = mpv_get_wakeup_pipe(mpv); + * if (pipefd < 0) + * error(); + * while (1) { + * struct pollfd pfds[1] = { + * { .fd = pipefd, .events = POLLIN }, + * }; + * // Wait until there are possibly new mpv events. + * poll(pfds, 1, -1); + * if (pfds[0].revents & POLLIN) { + * // Empty the pipe. Doing this before calling mpv_wait_event() + * // ensures that no wakeups are missed. It's not so important to + * // make sure the pipe is really empty (it will just cause some + * // additional wakeups in unlikely corner cases). + * char unused[256]; + * read(pipefd, unused, sizeof(unused)); + * while (1) { + * mpv_event *ev = mpv_wait_event(mpv, 0); + * // If MPV_EVENT_NONE is received, the event queue is empty. + * if (ev->event_id == MPV_EVENT_NONE) + * break; + * // Process the event. + * ... + * } + * } + * } + * + * @deprecated this function will be removed in the future. If you need this + * functionality, use mpv_set_wakeup_callback(), create a pipe + * manually, and call write() on your pipe in the callback. + * + * @return A UNIX FD of the read end of the wakeup pipe, or -1 on error. + * On MS Windows/MinGW, this will always return -1. + */ +int mpv_get_wakeup_pipe(mpv_handle *ctx); + +/** + * @deprecated use render.h + */ +typedef enum mpv_sub_api { + /** + * For using mpv's OpenGL renderer on an external OpenGL context. + * mpv_get_sub_api(MPV_SUB_API_OPENGL_CB) returns mpv_opengl_cb_context*. + * This context can be used with mpv_opengl_cb_* functions. + * Will return NULL if unavailable (if OpenGL support was not compiled in). + * See opengl_cb.h for details. + * + * @deprecated use render.h + */ + MPV_SUB_API_OPENGL_CB = 1 +} mpv_sub_api; + +/** + * This is used for additional APIs that are not strictly part of the core API. + * See the individual mpv_sub_api member values. + * + * @deprecated use render.h + */ +void *mpv_get_sub_api(mpv_handle *ctx, mpv_sub_api sub_api); + +#endif + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/mpvdemo/mpv/include64/opengl_cb.h b/mpvdemo/mpv/include64/opengl_cb.h new file mode 100644 index 0000000..6820681 --- /dev/null +++ b/mpvdemo/mpv/include64/opengl_cb.h @@ -0,0 +1,339 @@ +/* Copyright (C) 2017 the mpv developers + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef MPV_CLIENT_API_OPENGL_CB_H_ +#define MPV_CLIENT_API_OPENGL_CB_H_ + +#include "client.h" + +#if !MPV_ENABLE_DEPRECATED +#error "This header and all API provided by it is deprecated. Use render.h instead." +#else + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * + * Overview + * -------- + * + * Warning: this API is deprecated. A very similar API is provided by render.h + * and render_gl.h. The deprecated API is emulated with the new API. + * + * This API can be used to make mpv render into a foreign OpenGL context. It + * can be used to handle video display. + * + * The renderer needs to be explicitly initialized with mpv_opengl_cb_init_gl(), + * and then video can be drawn with mpv_opengl_cb_draw(). The user thread can + * be notified by new frames with mpv_opengl_cb_set_update_callback(). + * + * You can output and embed video without this API by setting the mpv "wid" + * option to a native window handle (see "Embedding the video window" section + * in the client.h header). In general, using the opengl-cb API is recommended, + * because window embedding can cause various issues, especially with GUI + * toolkits and certain platforms. + * + * OpenGL interop + * -------------- + * + * This assumes the OpenGL context lives on a certain thread controlled by the + * API user. The following functions require access to the OpenGL context: + * mpv_opengl_cb_init_gl + * mpv_opengl_cb_draw + * mpv_opengl_cb_uninit_gl + * + * The OpenGL context is indirectly accessed through the OpenGL function + * pointers returned by the get_proc_address callback in mpv_opengl_cb_init_gl. + * Generally, mpv will not load the system OpenGL library when using this API. + * + * Only "desktop" OpenGL version 2.1 and later and OpenGL ES version 2.0 and + * later are supported. With OpenGL 2.1, the GL_ARB_texture_rg is required. The + * renderer was written for the OpenGL 3.x core profile, with additional support + * for OpenGL 2.1 and OpenGL ES 2.0. + * + * Note that some hardware decoding interop API (as set with the "hwdec" option) + * may actually access some sort of host API, such as EGL. + * + * OpenGL state + * ------------ + * + * OpenGL has a large amount of implicit state. All the mpv functions mentioned + * above expect that the OpenGL state is reasonably set to OpenGL standard + * defaults. Likewise, mpv will attempt to leave the OpenGL context with + * standard defaults. The following state is excluded from this: + * + * - the glViewport state + * - the glScissor state (but GL_SCISSOR_TEST is in its default value) + * - glBlendFuncSeparate() state (but GL_BLEND is in its default value) + * - glClearColor() state + * - mpv may overwrite the callback set with glDebugMessageCallback() + * - mpv always disables GL_DITHER at init + * + * Messing with the state could be avoided by creating shared OpenGL contexts, + * but this is avoided for the sake of compatibility and interoperability. + * + * On OpenGL 2.1, mpv will strictly call functions like glGenTextures() to + * create OpenGL objects. You will have to do the same. This ensures that + * objects created by mpv and the API users don't clash. Also, legacy state + * must be either in its defaults, or not interfere with core state. + * + * Threading + * --------- + * + * The mpv_opengl_cb_* functions can be called from any thread, under the + * following conditions: + * - only one of the mpv_opengl_cb_* functions can be called at the same time + * (unless they belong to different mpv cores created by mpv_create()) + * - for functions which need an OpenGL context (see above) the OpenGL context + * must be "current" in the current thread, and it must be the same context + * as used with mpv_opengl_cb_init_gl() + * - never can be called from within the callbacks set with + * mpv_set_wakeup_callback() or mpv_opengl_cb_set_update_callback() + * + * Context and handle lifecycle + * ---------------------------- + * + * Video initialization will fail if the OpenGL context was not initialized yet + * (with mpv_opengl_cb_init_gl()). Likewise, mpv_opengl_cb_uninit_gl() will + * disable video. + * + * When the mpv core is destroyed (e.g. via mpv_terminate_destroy()), the OpenGL + * context must have been uninitialized. If this doesn't happen, undefined + * behavior will result. + * + * Hardware decoding + * ----------------- + * + * Hardware decoding via opengl_cb is fully supported, but requires some + * additional setup. (At least if direct hardware decoding modes are wanted, + * instead of copying back surface data from GPU to CPU RAM.) + * + * While "normal" mpv loads the OpenGL hardware decoding interop on demand, + * this can't be done with opengl_cb for internal technical reasons. Instead, + * it loads them by default, even if hardware decoding is not going to be used. + * In older mpv releases, this had to be done by setting the + * "opengl-hwdec-interop" or "hwdec-preload" options before calling + * mpv_opengl_cb_init_gl(). You can still use the newer "gpu-hwdec-interop" + * option to prevent loading of interop, or to load only a specific interop. + * + * There may be certain requirements on the OpenGL implementation: + * - Windows: ANGLE is required (although in theory GL/DX interop could be used) + * - Intel/Linux: EGL is required, and also a glMPGetNativeDisplay() callback + * must be provided (see sections below) + * - nVidia/Linux: Both GLX and EGL should work (GLX is required if vdpau is + * used, e.g. due to old drivers.) + * - OSX: CGL is required (CGLGetCurrentContext() returning non-NULL) + * - iOS: EAGL is required (EAGLContext.currentContext returning non-nil) + * + * Once these things are setup, hardware decoding can be enabled/disabled at + * any time by setting the "hwdec" property. + * + * Special windowing system interop considerations + * ------------------------------------------------ + * + * In some cases, libmpv needs to have access to the windowing system's handles. + * This can be a pointer to a X11 "Display" for example. Usually this is needed + * only for hardware decoding. + * + * You can communicate these handles to libmpv by adding a pseudo-OpenGL + * extension "GL_MP_MPGetNativeDisplay" to the additional extension string when + * calling mpv_opengl_cb_init_gl(). The get_proc_address callback should resolve + * a function named "glMPGetNativeDisplay", which has the signature: + * + * void* GLAPIENTRY glMPGetNativeDisplay(const char* name) + * + * See below what names are defined. Usually, libmpv will use the native handle + * up until mpv_opengl_cb_uninit_gl() is called. If the name is not anything + * you know/expected, return NULL from the function. + */ + +// Legacy - not supported anymore. +struct mpv_opengl_cb_window_pos { + int x; // left coordinates of window (usually 0) + int y; // top coordinates of window (usually 0) + int width; // width of GL window + int height; // height of GL window +}; + +// Legacy - not supported anymore. +struct mpv_opengl_cb_drm_params { + // DRM fd (int). set this to -1 if invalid. + int fd; + + // currently used crtc id + int crtc_id; + + // currently used connector id + int connector_id; + + // pointer to the drmModeAtomicReq that is being used for the renderloop. + // This atomic request pointer should be usually created at every renderloop. + struct _drmModeAtomicReq *atomic_request; +}; + +/** + * nVidia/Linux via VDPAU requires GLX, which does not have this problem (the + * GLX API can return the current X11 Display). + * + * Windowing system interop on MS win32 + * ------------------------------------ + * + * You should use ANGLE, and make sure your application and libmpv are linked + * to the same ANGLE DLLs. libmpv will pick the device context (needed for + * hardware decoding) from the current ANGLE EGL context. + */ + +/** + * Opaque context, returned by mpv_get_sub_api(MPV_SUB_API_OPENGL_CB). + * + * A context is bound to the mpv_handle it was retrieved from. The context + * will always be the same (for the same mpv_handle), and is valid until the + * mpv_handle it belongs to is released. + */ +typedef struct mpv_opengl_cb_context mpv_opengl_cb_context; + +typedef void (*mpv_opengl_cb_update_fn)(void *cb_ctx); +typedef void *(*mpv_opengl_cb_get_proc_address_fn)(void *fn_ctx, const char *name); + +/** + * Set the callback that notifies you when a new video frame is available, or + * if the video display configuration somehow changed and requires a redraw. + * Similar to mpv_set_wakeup_callback(), you must not call any mpv API from + * the callback, and all the other listed restrictions apply (such as not + * exiting the callback by throwing exceptions). + * + * @param callback callback(callback_ctx) is called if the frame should be + * redrawn + * @param callback_ctx opaque argument to the callback + */ +void mpv_opengl_cb_set_update_callback(mpv_opengl_cb_context *ctx, + mpv_opengl_cb_update_fn callback, + void *callback_ctx); + +/** + * Initialize the mpv OpenGL state. This retrieves OpenGL function pointers via + * get_proc_address, and creates OpenGL objects needed by mpv internally. It + * will also call APIs needed for rendering hardware decoded video in OpenGL, + * according to the mpv "hwdec" option. + * + * You must free the associated state at some point by calling the + * mpv_opengl_cb_uninit_gl() function. Not doing so may result in memory leaks + * or worse. + * + * @param exts optional _additional_ extension string, can be NULL + * @param get_proc_address callback used to retrieve function pointers to OpenGL + * functions. This is used for both standard functions + * and extension functions. (The extension string is + * checked whether extensions are really available.) + * The callback will be called from this function only + * (it is not stored and never used later). + * Usually, GL context APIs do this for you (e.g. with + * glXGetProcAddressARB or wglGetProcAddress), but + * some APIs do not always return pointers for all + * standard functions (even if present); in this case + * you have to compensate by looking up these functions + * yourself. + * @param get_proc_address_ctx arbitrary opaque user context passed to the + * get_proc_address callback + * @return error code (same as normal mpv_* API), including but not limited to: + * MPV_ERROR_UNSUPPORTED: the OpenGL version is not supported + * (or required extensions are missing) + * MPV_ERROR_INVALID_PARAMETER: the OpenGL state was already initialized + */ +int mpv_opengl_cb_init_gl(mpv_opengl_cb_context *ctx, const char *exts, + mpv_opengl_cb_get_proc_address_fn get_proc_address, + void *get_proc_address_ctx); + +/** + * Render video. Requires that the OpenGL state is initialized. + * + * The video will use the full provided framebuffer. Options like "panscan" are + * applied to determine which part of the video should be visible and how the + * video should be scaled. You can change these options at runtime by using the + * mpv property API. + * + * The renderer will reconfigure itself every time the output rectangle/size + * is changed. (If you want to do animations, it might be better to do the + * animation on a FBO instead.) + * + * This function implicitly pulls a video frame from the internal queue and + * renders it. If no new frame is available, the previous frame is redrawn. + * The update callback set with mpv_opengl_cb_set_update_callback() notifies + * you when a new frame was added. + * + * @param fbo The framebuffer object to render on. Because the renderer might + * manage multiple FBOs internally for the purpose of video + * postprocessing, it will always bind and unbind FBOs itself. If + * you want mpv to render on the main framebuffer, pass 0. + * @param w Width of the framebuffer. This is either the video size if the fbo + * parameter is 0, or the allocated size of the texture backing the + * fbo. The renderer will always use the full size of the fbo. + * @param h Height of the framebuffer. Same as with the w parameter, except + * that this parameter can be negative. In this case, the video + * frame will be rendered flipped. + * @return 0 + */ +int mpv_opengl_cb_draw(mpv_opengl_cb_context *ctx, int fbo, int w, int h); + +/** + * Deprecated. Use mpv_opengl_cb_draw(). This function is equivalent to: + * + * int mpv_opengl_cb_render(mpv_opengl_cb_context *ctx, int fbo, int vp[4]) + * { return mpv_opengl_cb_draw(ctx, fbo, vp[2], vp[3]); } + * + * vp[0] and vp[1] used to have a meaning, but are ignored in newer versions. + * + * This function will be removed in the future without version bump (this API + * was never marked as stable). + */ +int mpv_opengl_cb_render(mpv_opengl_cb_context *ctx, int fbo, int vp[4]); + +/** + * Tell the renderer that a frame was flipped at the given time. This is + * optional, but can help the player to achieve better timing. + * + * Note that calling this at least once informs libmpv that you will use this + * function. If you use it inconsistently, expect bad video playback. + * + * If this is called while no video or no OpenGL is initialized, it is ignored. + * + * @param time The mpv time (using mpv_get_time_us()) at which the flip call + * returned. If 0 is passed, mpv_get_time_us() is used instead. + * Currently, this parameter is ignored. + * @return error code + */ +int mpv_opengl_cb_report_flip(mpv_opengl_cb_context *ctx, int64_t time); + +/** + * Destroy the mpv OpenGL state. + * + * If video is still active (e.g. a file playing), video will be disabled + * forcefully. + * + * Calling this multiple times is ok. + * + * @return error code + */ +int mpv_opengl_cb_uninit_gl(mpv_opengl_cb_context *ctx); + +#ifdef __cplusplus +} +#endif + +#endif /* else #if MPV_ENABLE_DEPRECATED */ + +#endif diff --git a/mpvdemo/mpv/include64/render.h b/mpvdemo/mpv/include64/render.h new file mode 100644 index 0000000..293de3c --- /dev/null +++ b/mpvdemo/mpv/include64/render.h @@ -0,0 +1,626 @@ +/* Copyright (C) 2018 the mpv developers + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef MPV_CLIENT_API_RENDER_H_ +#define MPV_CLIENT_API_RENDER_H_ + +#include "client.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * Overview + * -------- + * + * This API can be used to make mpv render using supported graphic APIs (such + * as OpenGL). It can be used to handle video display. + * + * The renderer needs to be created with mpv_render_context_create() before + * you start playback (or otherwise cause a VO to be created). Then (with most + * backends) mpv_render_context_render() can be used to explicitly render the + * current video frame. Use mpv_render_context_set_update_callback() to get + * notified when there is a new frame to draw. + * + * Preferably rendering should be done in a separate thread. If you call + * normal libmpv API functions on the renderer thread, deadlocks can result + * (these are made non-fatal with timeouts, but user experience will obviously + * suffer). See "Threading" section below. + * + * You can output and embed video without this API by setting the mpv "wid" + * option to a native window handle (see "Embedding the video window" section + * in the client.h header). In general, using the render API is recommended, + * because window embedding can cause various issues, especially with GUI + * toolkits and certain platforms. + * + * Supported backends + * ------------------ + * + * OpenGL: via MPV_RENDER_API_TYPE_OPENGL, see render_gl.h header. + * + * Threading + * --------- + * + * You are recommended to do rendering on a separate thread than normal libmpv + * use. + * + * The mpv_render_* functions can be called from any thread, under the + * following conditions: + * - only one of the mpv_render_* functions can be called at the same time + * (unless they belong to different mpv cores created by mpv_create()) + * - never can be called from within the callbacks set with + * mpv_set_wakeup_callback() or mpv_render_context_set_update_callback() + * - if the OpenGL backend is used, for all functions the OpenGL context + * must be "current" in the calling thread, and it must be the same OpenGL + * context as the mpv_render_context was created with. Otherwise, undefined + * behavior will occur. + * - the thread does not call libmpv API functions other than the mpv_render_* + * functions, except APIs which are declared as safe (see below). Likewise, + * there must be no lock or wait dependency from the render thread to a + * thread using other libmpv functions. Basically, the situation that your + * render thread waits for a "not safe" libmpv API function to return must + * not happen. If you ignore this requirement, deadlocks can happen, which + * are made non-fatal with timeouts; then playback quality will be degraded, + * and the message + * mpv_render_context_render() not being called or stuck. + * is logged. If you set MPV_RENDER_PARAM_ADVANCED_CONTROL, you promise that + * this won't happen, and must absolutely guarantee it, or a real deadlock + * will freeze the mpv core thread forever. + * + * libmpv functions which are safe to call from a render thread are: + * - functions marked with "Safe to be called from mpv render API threads." + * - client.h functions which don't have an explicit or implicit mpv_handle + * parameter + * - mpv_render_* functions; but only for the same mpv_render_context pointer. + * If the pointer is different, mpv_render_context_free() is not safe. (The + * reason is that if MPV_RENDER_PARAM_ADVANCED_CONTROL is set, it may have + * to process still queued requests from the core, which it can do only for + * the current context, while requests for other contexts would deadlock. + * Also, it may have to wait and block for the core to terminate the video + * chain to make sure no resources are used after context destruction.) + * - if the mpv_handle parameter refers to a different mpv core than the one + * you're rendering for (very obscure, but allowed) + * + * Note about old libmpv version: + * + * Before API version 1.105 (basically in mpv 0.29.x), simply enabling + * MPV_RENDER_PARAM_ADVANCED_CONTROL could cause deadlock issues. This can + * be worked around by setting the "vd-lavc-dr" option to "no". + * In addition, you were required to call all mpv_render*() API functions + * from the same thread on which mpv_render_context_create() was originally + * run (for the same the mpv_render_context). Not honoring it led to UB + * (deadlocks, use of invalid pthread_t handles), even if you moved your GL + * context to a different thread correctly. + * These problems were addressed in API version 1.105 (mpv 0.30.0). + * + * Context and handle lifecycle + * ---------------------------- + * + * Video initialization will fail if the render context was not initialized yet + * (with mpv_render_context_create()), or it will revert to a VO that creates + * its own window. + * + * Currently, there can be only 1 mpv_render_context at a time per mpv core. + * + * Calling mpv_render_context_free() while a VO is using the render context is + * active will disable video. + * + * You must free the context with mpv_render_context_free() before the mpv core + * is destroyed. If this doesn't happen, undefined behavior will result. + */ + +/** + * Opaque context, returned by mpv_render_context_create(). + */ +typedef struct mpv_render_context mpv_render_context; + +/** + * Parameters for mpv_render_param (which is used in a few places such as + * mpv_render_context_create(). + * + * Also see mpv_render_param for conventions and how to use it. + */ +typedef enum mpv_render_param_type { + /** + * Not a valid value, but also used to terminate a params array. Its value + * is always guaranteed to be 0 (even if the ABI changes in the future). + */ + MPV_RENDER_PARAM_INVALID = 0, + /** + * The render API to use. Valid for mpv_render_context_create(). + * + * Type: char* + * + * Defined APIs: + * + * MPV_RENDER_API_TYPE_OPENGL: + * OpenGL desktop 2.1 or later (preferably core profile compatible to + * OpenGL 3.2), or OpenGLES 2.0 or later. + * Providing MPV_RENDER_PARAM_OPENGL_INIT_PARAMS is required. + * It is expected that an OpenGL context is valid and "current" when + * calling mpv_render_* functions (unless specified otherwise). It + * must be the same context for the same mpv_render_context. + */ + MPV_RENDER_PARAM_API_TYPE = 1, + /** + * Required parameters for initializing the OpenGL renderer. Valid for + * mpv_render_context_create(). + * Type: mpv_opengl_init_params* + */ + MPV_RENDER_PARAM_OPENGL_INIT_PARAMS = 2, + /** + * Describes a GL render target. Valid for mpv_render_context_render(). + * Type: mpv_opengl_fbo* + */ + MPV_RENDER_PARAM_OPENGL_FBO = 3, + /** + * Control flipped rendering. Valid for mpv_render_context_render(). + * Type: int* + * If the value is set to 0, render normally. Otherwise, render it flipped, + * which is needed e.g. when rendering to an OpenGL default framebuffer + * (which has a flipped coordinate system). + */ + MPV_RENDER_PARAM_FLIP_Y = 4, + /** + * Control surface depth. Valid for mpv_render_context_render(). + * Type: int* + * This implies the depth of the surface passed to the render function in + * bits per channel. If omitted or set to 0, the renderer will assume 8. + * Typically used to control dithering. + */ + MPV_RENDER_PARAM_DEPTH = 5, + /** + * ICC profile blob. Valid for mpv_render_context_set_parameter(). + * Type: mpv_byte_array* + * Set an ICC profile for use with the "icc-profile-auto" option. (If the + * option is not enabled, the ICC data will not be used.) + */ + MPV_RENDER_PARAM_ICC_PROFILE = 6, + /** + * Ambient light in lux. Valid for mpv_render_context_set_parameter(). + * Type: int* + * This can be used for automatic gamma correction. + */ + MPV_RENDER_PARAM_AMBIENT_LIGHT = 7, + /** + * X11 Display, sometimes used for hwdec. Valid for + * mpv_render_context_create(). The Display must stay valid for the lifetime + * of the mpv_render_context. + * Type: Display* + */ + MPV_RENDER_PARAM_X11_DISPLAY = 8, + /** + * Wayland display, sometimes used for hwdec. Valid for + * mpv_render_context_create(). The wl_display must stay valid for the + * lifetime of the mpv_render_context. + * Type: struct wl_display* + */ + MPV_RENDER_PARAM_WL_DISPLAY = 9, + /** + * Better control about rendering and enabling some advanced features. Valid + * for mpv_render_context_create(). + * + * This conflates multiple requirements the API user promises to abide if + * this option is enabled: + * + * - The API user's render thread, which is calling the mpv_render_*() + * functions, never waits for the core. Otherwise deadlocks can happen. + * See "Threading" section. + * - The callback set with mpv_render_context_set_update_callback() can now + * be called even if there is no new frame. The API user should call the + * mpv_render_context_update() function, and interpret the return value + * for whether a new frame should be rendered. + * - Correct functionality is impossible if the update callback is not set, + * or not set soon enough after mpv_render_context_create() (the core can + * block while waiting for you to call mpv_render_context_update(), and + * if the update callback is not correctly set, it will deadlock, or + * block for too long). + * + * In general, setting this option will enable the following features (and + * possibly more): + * + * - "Direct rendering", which means the player decodes directly to a + * texture, which saves a copy per video frame ("vd-lavc-dr" option + * needs to be enabled, and the rendering backend as well as the + * underlying GPU API/driver needs to have support for it). + * - Rendering screenshots with the GPU API if supported by the backend + * (instead of using a suboptimal software fallback via libswscale). + * + * Warning: do not just add this without reading the "Threading" section + * above, and then wondering that deadlocks happen. The + * requirements are tricky. But also note that even if advanced + * control is disabled, not adhering to the rules will lead to + * playback problems. Enabling advanced controls simply makes + * violating these rules fatal. + * + * Type: int*: 0 for disable (default), 1 for enable + */ + MPV_RENDER_PARAM_ADVANCED_CONTROL = 10, + /** + * Return information about the next frame to render. Valid for + * mpv_render_context_get_info(). + * + * Type: mpv_render_frame_info* + * + * It strictly returns information about the _next_ frame. The implication + * is that e.g. mpv_render_context_update()'s return value will have + * MPV_RENDER_UPDATE_FRAME set, and the user is supposed to call + * mpv_render_context_render(). If there is no next frame, then the + * return value will have is_valid set to 0. + */ + MPV_RENDER_PARAM_NEXT_FRAME_INFO = 11, + /** + * Enable or disable video timing. Valid for mpv_render_context_render(). + * + * Type: int*: 0 for disable, 1 for enable (default) + * + * When video is timed to audio, the player attempts to render video a bit + * ahead, and then do a blocking wait until the target display time is + * reached. This blocks mpv_render_context_render() for up to the amount + * specified with the "video-timing-offset" global option. You can set + * this parameter to 0 to disable this kind of waiting. If you do, it's + * recommended to use the target time value in mpv_render_frame_info to + * wait yourself, or to set the "video-timing-offset" to 0 instead. + * + * Disabling this without doing anything in addition will result in A/V sync + * being slightly off. + */ + MPV_RENDER_PARAM_BLOCK_FOR_TARGET_TIME = 12, + /** + * Use to skip rendering in mpv_render_context_render(). + * + * Type: int*: 0 for rendering (default), 1 for skipping + * + * If this is set, you don't need to pass a target surface to the render + * function (and if you do, it's completely ignored). This can still call + * into the lower level APIs (i.e. if you use OpenGL, the OpenGL context + * must be set). + * + * Be aware that the render API will consider this frame as having been + * rendered. All other normal rules also apply, for example about whether + * you have to call mpv_render_context_report_swap(). It also does timing + * in the same way. + */ + MPV_RENDER_PARAM_SKIP_RENDERING = 13, + /** + * Deprecated. Not supported. Use MPV_RENDER_PARAM_DRM_DISPLAY_V2 instead. + * Type : struct mpv_opengl_drm_params* + */ + MPV_RENDER_PARAM_DRM_DISPLAY = 14, + /** + * DRM draw surface size, contains draw surface dimensions. + * Valid for mpv_render_context_create(). + * Type : struct mpv_opengl_drm_draw_surface_size* + */ + MPV_RENDER_PARAM_DRM_DRAW_SURFACE_SIZE = 15, + /** + * DRM display, contains drm display handles. + * Valid for mpv_render_context_create(). + * Type : struct mpv_opengl_drm_params_v2* + */ + MPV_RENDER_PARAM_DRM_DISPLAY_V2 = 16, +} mpv_render_param_type; + +/** + * For backwards compatibility with the old naming of + * MPV_RENDER_PARAM_DRM_DRAW_SURFACE_SIZE + */ +#define MPV_RENDER_PARAM_DRM_OSD_SIZE MPV_RENDER_PARAM_DRM_DRAW_SURFACE_SIZE + +/** + * Used to pass arbitrary parameters to some mpv_render_* functions. The + * meaning of the data parameter is determined by the type, and each + * MPV_RENDER_PARAM_* documents what type the value must point to. + * + * Each value documents the required data type as the pointer you cast to + * void* and set on mpv_render_param.data. For example, if MPV_RENDER_PARAM_FOO + * documents the type as Something* , then the code should look like this: + * + * Something foo = {...}; + * mpv_render_param param; + * param.type = MPV_RENDER_PARAM_FOO; + * param.data = & foo; + * + * Normally, the data field points to exactly 1 object. If the type is char*, + * it points to a 0-terminated string. + * + * In all cases (unless documented otherwise) the pointers need to remain + * valid during the call only. Unless otherwise documented, the API functions + * will not write to the params array or any data pointed to it. + * + * As a convention, parameter arrays are always terminated by type==0. There + * is no specific order of the parameters required. The order of the 2 fields in + * this struct is guaranteed (even after ABI changes). + */ +typedef struct mpv_render_param { + enum mpv_render_param_type type; + void *data; +} mpv_render_param; + + +/** + * Predefined values for MPV_RENDER_PARAM_API_TYPE. + */ +#define MPV_RENDER_API_TYPE_OPENGL "opengl" + +/** + * Flags used in mpv_render_frame_info.flags. Each value represents a bit in it. + */ +typedef enum mpv_render_frame_info_flag { + /** + * Set if there is actually a next frame. If unset, there is no next frame + * yet, and other flags and fields that require a frame to be queued will + * be unset. + * + * This is set for _any_ kind of frame, even for redraw requests. + * + * Note that when this is unset, it simply means no new frame was + * decoded/queued yet, not necessarily that the end of the video was + * reached. A new frame can be queued after some time. + * + * If the return value of mpv_render_context_render() had the + * MPV_RENDER_UPDATE_FRAME flag set, this flag will usually be set as well, + * unless the frame is rendered, or discarded by other asynchronous events. + */ + MPV_RENDER_FRAME_INFO_PRESENT = 1 << 0, + /** + * If set, the frame is not an actual new video frame, but a redraw request. + * For example if the video is paused, and an option that affects video + * rendering was changed (or any other reason), an update request can be + * issued and this flag will be set. + * + * Typically, redraw frames will not be subject to video timing. + * + * Implies MPV_RENDER_FRAME_INFO_PRESENT. + */ + MPV_RENDER_FRAME_INFO_REDRAW = 1 << 1, + /** + * If set, this is supposed to reproduce the previous frame perfectly. This + * is usually used for certain "video-sync" options ("display-..." modes). + * Typically the renderer will blit the video from a FBO. Unset otherwise. + * + * Implies MPV_RENDER_FRAME_INFO_PRESENT. + */ + MPV_RENDER_FRAME_INFO_REPEAT = 1 << 2, + /** + * If set, the player timing code expects that the user thread blocks on + * vsync (by either delaying the render call, or by making a call to + * mpv_render_context_report_swap() at vsync time). + * + * Implies MPV_RENDER_FRAME_INFO_PRESENT. + */ + MPV_RENDER_FRAME_INFO_BLOCK_VSYNC = 1 << 3, +} mpv_render_frame_info_flag; + +/** + * Information about the next video frame that will be rendered. Can be + * retrieved with MPV_RENDER_PARAM_NEXT_FRAME_INFO. + */ +typedef struct mpv_render_frame_info { + /** + * A bitset of mpv_render_frame_info_flag values (i.e. multiple flags are + * combined with bitwise or). + */ + uint64_t flags; + /** + * Absolute time at which the frame is supposed to be displayed. This is in + * the same unit and base as the time returned by mpv_get_time_us(). For + * frames that are redrawn, or if vsync locked video timing is used (see + * "video-sync" option), then this can be 0. The "video-timing-offset" + * option determines how much "headroom" the render thread gets (but a high + * enough frame rate can reduce it anyway). mpv_render_context_render() will + * normally block until the time is elapsed, unless you pass it + * MPV_RENDER_PARAM_BLOCK_FOR_TARGET_TIME = 0. + */ + int64_t target_time; +} mpv_render_frame_info; + +/** + * Initialize the renderer state. Depending on the backend used, this will + * access the underlying GPU API and initialize its own objects. + * + * You must free the context with mpv_render_context_free(). Not doing so before + * the mpv core is destroyed may result in memory leaks or crashes. + * + * Currently, only at most 1 context can exists per mpv core (it represents the + * main video output). + * + * You should pass the following parameters: + * - MPV_RENDER_PARAM_API_TYPE to select the underlying backend/GPU API. + * - Backend-specific init parameter, like MPV_RENDER_PARAM_OPENGL_INIT_PARAMS. + * - Setting MPV_RENDER_PARAM_ADVANCED_CONTROL and following its rules is + * strongly recommended. + * - If you want to use hwdec, possibly hwdec interop resources. + * + * @param res set to the context (on success) or NULL (on failure). The value + * is never read and always overwritten. + * @param mpv handle used to get the core (the mpv_render_context won't depend + * on this specific handle, only the core referenced by it) + * @param params an array of parameters, terminated by type==0. It's left + * unspecified what happens with unknown parameters. At least + * MPV_RENDER_PARAM_API_TYPE is required, and most backends will + * require another backend-specific parameter. + * @return error code, including but not limited to: + * MPV_ERROR_UNSUPPORTED: the OpenGL version is not supported + * (or required extensions are missing) + * MPV_ERROR_NOT_IMPLEMENTED: an unknown API type was provided, or + * support for the requested API was not + * built in the used libmpv binary. + * MPV_ERROR_INVALID_PARAMETER: at least one of the provided parameters was + * not valid. + */ +int mpv_render_context_create(mpv_render_context **res, mpv_handle *mpv, + mpv_render_param *params); + +/** + * Attempt to change a single parameter. Not all backends and parameter types + * support all kinds of changes. + * + * @param ctx a valid render context + * @param param the parameter type and data that should be set + * @return error code. If a parameter could actually be changed, this returns + * success, otherwise an error code depending on the parameter type + * and situation. + */ +int mpv_render_context_set_parameter(mpv_render_context *ctx, + mpv_render_param param); + +/** + * Retrieve information from the render context. This is NOT a counterpart to + * mpv_render_context_set_parameter(), because you generally can't read + * parameters set with it, and this function is not meant for this purpose. + * Instead, this is for communicating information from the renderer back to the + * user. See mpv_render_param_type; entries which support this function + * explicitly mention it, and for other entries you can assume it will fail. + * + * You pass param with param.type set and param.data pointing to a variable + * of the required data type. The function will then overwrite that variable + * with the returned value (at least on success). + * + * @param ctx a valid render context + * @param param the parameter type and data that should be retrieved + * @return error code. If a parameter could actually be retrieved, this returns + * success, otherwise an error code depending on the parameter type + * and situation. MPV_ERROR_NOT_IMPLEMENTED is used for unknown + * param.type, or if retrieving it is not supported. + */ +int mpv_render_context_get_info(mpv_render_context *ctx, + mpv_render_param param); + +typedef void (*mpv_render_update_fn)(void *cb_ctx); + +/** + * Set the callback that notifies you when a new video frame is available, or + * if the video display configuration somehow changed and requires a redraw. + * Similar to mpv_set_wakeup_callback(), you must not call any mpv API from + * the callback, and all the other listed restrictions apply (such as not + * exiting the callback by throwing exceptions). + * + * This can be called from any thread, except from an update callback. In case + * of the OpenGL backend, no OpenGL state or API is accessed. + * + * Calling this will raise an update callback immediately. + * + * @param callback callback(callback_ctx) is called if the frame should be + * redrawn + * @param callback_ctx opaque argument to the callback + */ +void mpv_render_context_set_update_callback(mpv_render_context *ctx, + mpv_render_update_fn callback, + void *callback_ctx); + +/** + * The API user is supposed to call this when the update callback was invoked + * (like all mpv_render_* functions, this has to happen on the render thread, + * and _not_ from the update callback itself). + * + * This is optional if MPV_RENDER_PARAM_ADVANCED_CONTROL was not set (default). + * Otherwise, it's a hard requirement that this is called after each update + * callback. If multiple update callback happened, and the function could not + * be called sooner, it's OK to call it once after the last callback. + * + * If an update callback happens during or after this function, the function + * must be called again at the soonest possible time. + * + * If MPV_RENDER_PARAM_ADVANCED_CONTROL was set, this will do additional work + * such as allocating textures for the video decoder. + * + * @return a bitset of mpv_render_update_flag values (i.e. multiple flags are + * combined with bitwise or). Typically, this will tell the API user + * what should happen next. E.g. if the MPV_RENDER_UPDATE_FRAME flag is + * set, mpv_render_context_render() should be called. If flags unknown + * to the API user are set, or if the return value is 0, nothing needs + * to be done. + */ +uint64_t mpv_render_context_update(mpv_render_context *ctx); + +/** + * Flags returned by mpv_render_context_update(). Each value represents a bit + * in the function's return value. + */ +typedef enum mpv_render_update_flag { + /** + * A new video frame must be rendered. mpv_render_context_render() must be + * called. + */ + MPV_RENDER_UPDATE_FRAME = 1 << 0, +} mpv_render_context_flag; + +/** + * Render video. + * + * Typically renders the video to a target surface provided via mpv_render_param + * (the details depend on the backend in use). Options like "panscan" are + * applied to determine which part of the video should be visible and how the + * video should be scaled. You can change these options at runtime by using the + * mpv property API. + * + * The renderer will reconfigure itself every time the target surface + * configuration (such as size) is changed. + * + * This function implicitly pulls a video frame from the internal queue and + * renders it. If no new frame is available, the previous frame is redrawn. + * The update callback set with mpv_render_context_set_update_callback() + * notifies you when a new frame was added. The details potentially depend on + * the backends and the provided parameters. + * + * Generally, libmpv will invoke your update callback some time before the video + * frame should be shown, and then lets this function block until the supposed + * display time. This will limit your rendering to video FPS. You can prevent + * this by setting the "video-timing-offset" global option to 0. (This applies + * only to "audio" video sync mode.) + * + * You should pass the following parameters: + * - Backend-specific target object, such as MPV_RENDER_PARAM_OPENGL_FBO. + * - Possibly transformations, such as MPV_RENDER_PARAM_FLIP_Y. + * + * @param ctx a valid render context + * @param params an array of parameters, terminated by type==0. Which parameters + * are required depends on the backend. It's left unspecified what + * happens with unknown parameters. + * @return error code + */ +int mpv_render_context_render(mpv_render_context *ctx, mpv_render_param *params); + +/** + * Tell the renderer that a frame was flipped at the given time. This is + * optional, but can help the player to achieve better timing. + * + * Note that calling this at least once informs libmpv that you will use this + * function. If you use it inconsistently, expect bad video playback. + * + * If this is called while no video is initialized, it is ignored. + * + * @param ctx a valid render context + */ +void mpv_render_context_report_swap(mpv_render_context *ctx); + +/** + * Destroy the mpv renderer state. + * + * If video is still active (e.g. a file playing), video will be disabled + * forcefully. + * + * @param ctx a valid render context. After this function returns, this is not + * a valid pointer anymore. NULL is also allowed and does nothing. + */ +void mpv_render_context_free(mpv_render_context *ctx); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/mpvdemo/mpv/include64/render_gl.h b/mpvdemo/mpv/include64/render_gl.h new file mode 100644 index 0000000..cb141df --- /dev/null +++ b/mpvdemo/mpv/include64/render_gl.h @@ -0,0 +1,216 @@ +/* Copyright (C) 2018 the mpv developers + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef MPV_CLIENT_API_RENDER_GL_H_ +#define MPV_CLIENT_API_RENDER_GL_H_ + +#include "render.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * OpenGL backend + * -------------- + * + * This header contains definitions for using OpenGL with the render.h API. + * + * OpenGL interop + * -------------- + * + * The OpenGL backend has some special rules, because OpenGL itself uses + * implicit per-thread contexts, which causes additional API problems. + * + * This assumes the OpenGL context lives on a certain thread controlled by the + * API user. All mpv_render_* APIs have to be assumed to implicitly use the + * OpenGL context if you pass a mpv_render_context using the OpenGL backend, + * unless specified otherwise. + * + * The OpenGL context is indirectly accessed through the OpenGL function + * pointers returned by the get_proc_address callback in mpv_opengl_init_params. + * Generally, mpv will not load the system OpenGL library when using this API. + * + * OpenGL state + * ------------ + * + * OpenGL has a large amount of implicit state. All the mpv functions mentioned + * above expect that the OpenGL state is reasonably set to OpenGL standard + * defaults. Likewise, mpv will attempt to leave the OpenGL context with + * standard defaults. The following state is excluded from this: + * + * - the glViewport state + * - the glScissor state (but GL_SCISSOR_TEST is in its default value) + * - glBlendFuncSeparate() state (but GL_BLEND is in its default value) + * - glClearColor() state + * - mpv may overwrite the callback set with glDebugMessageCallback() + * - mpv always disables GL_DITHER at init + * + * Messing with the state could be avoided by creating shared OpenGL contexts, + * but this is avoided for the sake of compatibility and interoperability. + * + * On OpenGL 2.1, mpv will strictly call functions like glGenTextures() to + * create OpenGL objects. You will have to do the same. This ensures that + * objects created by mpv and the API users don't clash. Also, legacy state + * must be either in its defaults, or not interfere with core state. + * + * API use + * ------- + * + * The mpv_render_* API is used. That API supports multiple backends, and this + * section documents specifics for the OpenGL backend. + * + * Use mpv_render_context_create() with MPV_RENDER_PARAM_API_TYPE set to + * MPV_RENDER_API_TYPE_OPENGL, and MPV_RENDER_PARAM_OPENGL_INIT_PARAMS provided. + * + * Call mpv_render_context_render() with MPV_RENDER_PARAM_OPENGL_FBO to render + * the video frame to an FBO. + * + * Hardware decoding + * ----------------- + * + * Hardware decoding via this API is fully supported, but requires some + * additional setup. (At least if direct hardware decoding modes are wanted, + * instead of copying back surface data from GPU to CPU RAM.) + * + * There may be certain requirements on the OpenGL implementation: + * + * - Windows: ANGLE is required (although in theory GL/DX interop could be used) + * - Intel/Linux: EGL is required, and also the native display resource needs + * to be provided (e.g. MPV_RENDER_PARAM_X11_DISPLAY for X11 and + * MPV_RENDER_PARAM_WL_DISPLAY for Wayland) + * - nVidia/Linux: Both GLX and EGL should work (GLX is required if vdpau is + * used, e.g. due to old drivers.) + * - OSX: CGL is required (CGLGetCurrentContext() returning non-NULL) + * - iOS: EAGL is required (EAGLContext.currentContext returning non-nil) + * + * Once these things are setup, hardware decoding can be enabled/disabled at + * any time by setting the "hwdec" property. + */ + +/** + * For initializing the mpv OpenGL state via MPV_RENDER_PARAM_OPENGL_INIT_PARAMS. + */ +typedef struct mpv_opengl_init_params { + /** + * This retrieves OpenGL function pointers, and will use them in subsequent + * operation. + * Usually, you can simply call the GL context APIs from this callback (e.g. + * glXGetProcAddressARB or wglGetProcAddress), but some APIs do not always + * return pointers for all standard functions (even if present); in this + * case you have to compensate by looking up these functions yourself when + * libmpv wants to resolve them through this callback. + * libmpv will not normally attempt to resolve GL functions on its own, nor + * does it link to GL libraries directly. + */ + void *(*get_proc_address)(void *ctx, const char *name); + /** + * Value passed as ctx parameter to get_proc_address(). + */ + void *get_proc_address_ctx; + /** + * This should not be used. It is deprecated and will be removed or ignored + * when the opengl_cb API is removed. + */ + const char *extra_exts; +} mpv_opengl_init_params; + +/** + * For MPV_RENDER_PARAM_OPENGL_FBO. + */ +typedef struct mpv_opengl_fbo { + /** + * Framebuffer object name. This must be either a valid FBO generated by + * glGenFramebuffers() that is complete and color-renderable, or 0. If the + * value is 0, this refers to the OpenGL default framebuffer. + */ + int fbo; + /** + * Valid dimensions. This must refer to the size of the framebuffer. This + * must always be set. + */ + int w, h; + /** + * Underlying texture internal format (e.g. GL_RGBA8), or 0 if unknown. If + * this is the default framebuffer, this can be an equivalent. + */ + int internal_format; +} mpv_opengl_fbo; + +/** + * Deprecated. For MPV_RENDER_PARAM_DRM_DISPLAY. + */ +typedef struct mpv_opengl_drm_params { + int fd; + int crtc_id; + int connector_id; + struct _drmModeAtomicReq **atomic_request_ptr; + int render_fd; +} mpv_opengl_drm_params; + +/** + * For MPV_RENDER_PARAM_DRM_DRAW_SURFACE_SIZE. + */ +typedef struct mpv_opengl_drm_draw_surface_size { + /** + * size of the draw plane surface in pixels. + */ + int width, height; +} mpv_opengl_drm_draw_surface_size; + +/** + * For MPV_RENDER_PARAM_DRM_DISPLAY_V2. + */ +typedef struct mpv_opengl_drm_params_v2 { + /** + * DRM fd (int). Set to -1 if invalid. + */ + int fd; + + /** + * Currently used crtc id + */ + int crtc_id; + + /** + * Currently used connector id + */ + int connector_id; + + /** + * Pointer to a drmModeAtomicReq pointer that is being used for the renderloop. + * This pointer should hold a pointer to the atomic request pointer + * The atomic request pointer is usually changed at every renderloop. + */ + struct _drmModeAtomicReq **atomic_request_ptr; + + /** + * DRM render node. Used for VAAPI interop. + * Set to -1 if invalid. + */ + int render_fd; +} mpv_opengl_drm_params_v2; + + +/** + * For backwards compatibility with the old naming of mpv_opengl_drm_draw_surface_size + */ +#define mpv_opengl_drm_osd_size mpv_opengl_drm_draw_surface_size + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/mpvdemo/mpv/include64/stream_cb.h b/mpvdemo/mpv/include64/stream_cb.h new file mode 100644 index 0000000..63593d7 --- /dev/null +++ b/mpvdemo/mpv/include64/stream_cb.h @@ -0,0 +1,240 @@ +/* Copyright (C) 2017 the mpv developers + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef MPV_CLIENT_API_STREAM_CB_H_ +#define MPV_CLIENT_API_STREAM_CB_H_ + +#include "client.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * Warning: this API is not stable yet. + * + * Overview + * -------- + * + * This API can be used to make mpv read from a stream with a custom + * implementation. This interface is inspired by funopen on BSD and + * fopencookie on linux. The stream is backed by user-defined callbacks + * which can implement customized open, read, seek, size and close behaviors. + * + * Usage + * ----- + * + * Register your stream callbacks with the mpv_stream_cb_add_ro() function. You + * have to provide a mpv_stream_cb_open_ro_fn callback to it (open_fn argument). + * + * Once registered, you can `loadfile myprotocol://myfile`. Your open_fn will be + * invoked with the URI and you must fill out the provided mpv_stream_cb_info + * struct. This includes your stream callbacks (like read_fn), and an opaque + * cookie, which will be passed as the first argument to all the remaining + * stream callbacks. + * + * Note that your custom callbacks must not invoke libmpv APIs as that would + * cause a deadlock. (Unless you call a different mpv_handle than the one the + * callback was registered for, and the mpv_handles refer to different mpv + * instances.) + * + * Stream lifetime + * --------------- + * + * A stream remains valid until its close callback has been called. It's up to + * libmpv to call the close callback, and the libmpv user cannot close it + * directly with the stream_cb API. + * + * For example, if you consider your custom stream to become suddenly invalid + * (maybe because the underlying stream died), libmpv will continue using your + * stream. All you can do is returning errors from each callback, until libmpv + * gives up and closes it. + * + * Protocol registration and lifetime + * ---------------------------------- + * + * Protocols remain registered until the mpv instance is terminated. This means + * in particular that it can outlive the mpv_handle that was used to register + * it, but once mpv_terminate_destroy() is called, your registered callbacks + * will not be called again. + * + * Protocol unregistration is finished after the mpv core has been destroyed + * (e.g. after mpv_terminate_destroy() has returned). + * + * If you do not call mpv_terminate_destroy() yourself (e.g. plugin-style code), + * you will have to deal with the registration or even streams outliving your + * code. Here are some possible ways to do this: + * - call mpv_terminate_destroy(), which destroys the core, and will make sure + * all streams are closed once this function returns + * - you refcount all resources your stream "cookies" reference, so that it + * doesn't matter if streams live longer than expected + * - create "cancellation" semantics: after your protocol has been unregistered, + * notify all your streams that are still opened, and make them drop all + * referenced resources - then return errors from the stream callbacks as + * long as the stream is still opened + * + */ + +/** + * Read callback used to implement a custom stream. The semantics of the + * callback match read(2) in blocking mode. Short reads are allowed (you can + * return less bytes than requested, and libmpv will retry reading the rest + * with another call). If no data can be immediately read, the callback must + * block until there is new data. A return of 0 will be interpreted as final + * EOF, although libmpv might retry the read, or seek to a different position. + * + * @param cookie opaque cookie identifying the stream, + * returned from mpv_stream_cb_open_fn + * @param buf buffer to read data into + * @param size of the buffer + * @return number of bytes read into the buffer + * @return 0 on EOF + * @return -1 on error + */ +typedef int64_t (*mpv_stream_cb_read_fn)(void *cookie, char *buf, uint64_t nbytes); + +/** + * Seek callback used to implement a custom stream. + * + * Note that mpv will issue a seek to position 0 immediately after opening. This + * is used to test whether the stream is seekable (since seekability might + * depend on the URI contents, not just the protocol). Return + * MPV_ERROR_UNSUPPORTED if seeking is not implemented for this stream. This + * seek also serves to establish the fact that streams start at position 0. + * + * This callback can be NULL, in which it behaves as if always returning + * MPV_ERROR_UNSUPPORTED. + * + * @param cookie opaque cookie identifying the stream, + * returned from mpv_stream_cb_open_fn + * @param offset target absolut stream position + * @return the resulting offset of the stream + * MPV_ERROR_UNSUPPORTED or MPV_ERROR_GENERIC if the seek failed + */ +typedef int64_t (*mpv_stream_cb_seek_fn)(void *cookie, int64_t offset); + +/** + * Size callback used to implement a custom stream. + * + * Return MPV_ERROR_UNSUPPORTED if no size is known. + * + * This callback can be NULL, in which it behaves as if always returning + * MPV_ERROR_UNSUPPORTED. + * + * @param cookie opaque cookie identifying the stream, + * returned from mpv_stream_cb_open_fn + * @return the total size in bytes of the stream + */ +typedef int64_t (*mpv_stream_cb_size_fn)(void *cookie); + +/** + * Close callback used to implement a custom stream. + * + * @param cookie opaque cookie identifying the stream, + * returned from mpv_stream_cb_open_fn + */ +typedef void (*mpv_stream_cb_close_fn)(void *cookie); + +/** + * Cancel callback used to implement a custom stream. + * + * This callback is used to interrupt any current or future read and seek + * operations. It will be called from a separate thread than the demux + * thread, and should not block. + * + * This callback can be NULL. + * + * Available since API 1.106. + * + * @param cookie opaque cookie identifying the stream, + * returned from mpv_stream_cb_open_fn + */ +typedef void (*mpv_stream_cb_cancel_fn)(void *cookie); + +/** + * See mpv_stream_cb_open_ro_fn callback. + */ +typedef struct mpv_stream_cb_info { + /** + * Opaque user-provided value, which will be passed to the other callbacks. + * The close callback will be called to release the cookie. It is not + * interpreted by mpv. It doesn't even need to be a valid pointer. + * + * The user sets this in the mpv_stream_cb_open_ro_fn callback. + */ + void *cookie; + + /** + * Callbacks set by the user in the mpv_stream_cb_open_ro_fn callback. Some + * of them are optional, and can be left unset. + * + * The following callbacks are mandatory: read_fn, close_fn + */ + mpv_stream_cb_read_fn read_fn; + mpv_stream_cb_seek_fn seek_fn; + mpv_stream_cb_size_fn size_fn; + mpv_stream_cb_close_fn close_fn; + mpv_stream_cb_cancel_fn cancel_fn; /* since API 1.106 */ +} mpv_stream_cb_info; + +/** + * Open callback used to implement a custom read-only (ro) stream. The user + * must set the callback fields in the passed info struct. The cookie field + * also can be set to store state associated to the stream instance. + * + * Note that the info struct is valid only for the duration of this callback. + * You can't change the callbacks or the pointer to the cookie at a later point. + * + * Each stream instance created by the open callback can have different + * callbacks. + * + * The close_fn callback will terminate the stream instance. The pointers to + * your callbacks and cookie will be discarded, and the callbacks will not be + * called again. + * + * @param user_data opaque user data provided via mpv_stream_cb_add() + * @param uri name of the stream to be opened (with protocol prefix) + * @param info fields which the user should fill + * @return 0 on success, MPV_ERROR_LOADING_FAILED if the URI cannot be opened. + */ +typedef int (*mpv_stream_cb_open_ro_fn)(void *user_data, char *uri, + mpv_stream_cb_info *info); + +/** + * Add a custom stream protocol. This will register a protocol handler under + * the given protocol prefix, and invoke the given callbacks if an URI with the + * matching protocol prefix is opened. + * + * The "ro" is for read-only - only read-only streams can be registered with + * this function. + * + * The callback remains registered until the mpv core is registered. + * + * If a custom stream with the same name is already registered, then the + * MPV_ERROR_INVALID_PARAMETER error is returned. + * + * @param protocol protocol prefix, for example "foo" for "foo://" URIs + * @param user_data opaque pointer passed into the mpv_stream_cb_open_fn + * callback. + * @return error code + */ +int mpv_stream_cb_add_ro(mpv_handle *ctx, const char *protocol, void *user_data, + mpv_stream_cb_open_ro_fn open_fn); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/mpvdemo/mpv/mpv.cpp b/mpvdemo/mpv/mpv.cpp new file mode 100644 index 0000000..7f29954 --- /dev/null +++ b/mpvdemo/mpv/mpv.cpp @@ -0,0 +1,209 @@ +#include "mpv.h" +#include "mpvtool.h" + +MpvThread::MpvThread(QObject *parent) : QThread(parent) +{ + setObjectName("MpvThread"); + + stopped = false; + isPlay = false; + + url = "rtsp://192.168.1.200:554/1"; + + mpvPlayer = NULL; + + static bool isInit = false; + if (!isInit) { + isInit = true; + qDebug() << TIMEMS << "init mpv lib ok" << " version:" << MPV_CLIENT_API_VERSION; + } +} + +void MpvThread::run() +{ + while (!stopped) { + msleep(1); + } + + //线程结束后释放资源 + free(); + stopped = false; + isPlay = false; + //qDebug() << TIMEMS << "stop mpv thread"; +} + +void MpvThread::setUrl(const QString &url) +{ + this->url = url; +} + +QVariant MpvThread::getValue(const QString &name) +{ + if (mpvPlayer != NULL) { + return qtmpv::get_property(mpvPlayer, name); + } else { + return QVariant(); + } +} + +int MpvThread::setValue(const QString &name, const QVariant &value) +{ + if (mpvPlayer != NULL) { + return qtmpv::set_property(mpvPlayer, name, value); + } + + return -1; +} + +int MpvThread::setOption(const QString &name, const QVariant &value) +{ + if (mpvPlayer != NULL) { + return qtmpv::set_option_variant(mpvPlayer, name, value); + } + + return -1; +} + +QVariant MpvThread::command(const QVariant &args) +{ + if (mpvPlayer != NULL) { + return qtmpv::command_variant(mpvPlayer, args); + } + + return QVariant(); +} + +bool MpvThread::init() +{ + //创建实例 + if (mpvPlayer == NULL) { + mpvPlayer = mpv_create(); + } + + MpvWidget *widget = (MpvWidget *)this->parent(); + HWND wid = (HWND)widget->winId(); + mpv_set_option(mpvPlayer, "wid", MPV_FORMAT_INT64, &wid); + + //请求级别日志消息 + mpv_request_log_messages(mpvPlayer, "info"); + //启用默认绑定 + setValue("input-default-bindings", "yes"); + //启用键盘输入 + setValue("input-vo-keyboard", "yes"); + + //设置控制台打印 + setOption("terminal", "false"); + //设置消息级别 + setOption("msg-level", "all=v"); + + //设置硬件加速 none auto any d3d11va dxva2 + setOption("hwdec", "d3d11va"); + //设置通信协议 tcp udp + setOption("rtsp-transport", "tcp"); + //设置网络超时时间 单位秒 + setOption("network-timeout", 3); + + //初始化实例 + if (mpv_initialize(mpvPlayer) < 0) { + return false; + } + + QByteArray data = url.toUtf8(); + const char *args[] = {"loadfile", data.data(), NULL}; + if (mpv_command_async(mpvPlayer, 0, args) < 0) { + return false; + } + + //qDebug() << TIMEMS << "init mpv finsh"; + return true; +} + +void MpvThread::play() +{ + isPlay = true; + this->init(); +} + +void MpvThread::pause() +{ + setValue("pause", "yes"); +} + +void MpvThread::next() +{ + setValue("pause", "no"); +} + +void MpvThread::free() +{ + if (mpvPlayer != NULL) { + mpv_terminate_destroy(mpvPlayer); + mpvPlayer = NULL; + } + + //qDebug() << TIMEMS << "close mpv ok"; +} + +void MpvThread::stop() +{ + stopped = true; +} + +//实时视频显示窗体类 +MpvWidget::MpvWidget(QWidget *parent) : QWidget(parent) +{ + thread = new MpvThread(this); +} + +MpvWidget::~MpvWidget() +{ + close(); +} + +void MpvWidget::setUrl(const QString &url) +{ + thread->setUrl(url); +} + +void MpvWidget::open() +{ + //qDebug() << TIMEMS << "open video" << objectName(); + clear(); + + thread->play(); + thread->start(); +} + +void MpvWidget::pause() +{ + thread->pause(); +} + +void MpvWidget::next() +{ + thread->next(); +} + +void MpvWidget::close() +{ + //qDebug() << TIMEMS << "close video" << objectName(); + if (thread->isRunning()) { + thread->stop(); + thread->quit(); + thread->wait(3000); + } + + QTimer::singleShot(5, this, SLOT(clear())); +} + +void MpvWidget::restart() +{ + //qDebug() << TIMEMS << "restart video" << objectName(); + close(); + QTimer::singleShot(10, this, SLOT(open())); +} + +void MpvWidget::clear() +{ + update(); +} diff --git a/mpvdemo/mpv/mpv.h b/mpvdemo/mpv/mpv.h new file mode 100644 index 0000000..2880858 --- /dev/null +++ b/mpvdemo/mpv/mpv.h @@ -0,0 +1,85 @@ +#ifndef VLC_H +#define VLC_H + +#include +#if (QT_VERSION > QT_VERSION_CHECK(5,0,0)) +#include +#endif + +#include "mpvhead.h" + +class MpvThread : public QThread +{ + Q_OBJECT +public: + explicit MpvThread(QObject *parent = 0); + +protected: + void run(); + +private: + volatile bool stopped; //线程停止标志位 + volatile bool isPlay; //播放视频标志位 + + QString url; //视频流地址 + mpv_handle *mpvPlayer; //载体对象 + +signals: + //收到图片信号 + void receiveImage(const QImage &image); + +public slots: + //设置视频流地址 + void setUrl(const QString &url); + + //通用属性接口 + QVariant getValue(const QString &name); + int setValue(const QString &name, const QVariant &value); + int setOption(const QString &name, const QVariant &value); + QVariant command(const QVariant &args); + + //初始化视频对象 + bool init(); + //播放视频对象 + void play(); + //暂停播放 + void pause(); + //继续播放 + void next(); + //释放对象 + void free(); + //停止采集线程 + void stop(); +}; + +//实时视频显示窗体类 +class MpvWidget : public QWidget +{ + Q_OBJECT +public: + explicit MpvWidget(QWidget *parent = 0); + ~MpvWidget(); + +private: + MpvThread *thread; + +public slots: + //设置视频流地址 + void setUrl(const QString &url); + + //打开设备 + void open(); + //暂停 + void pause(); + //继续 + void next(); + //关闭设备 + void close(); + //重新加载 + void restart(); + //清空 + void clear(); + +}; + +#endif // VLC_H diff --git a/mpvdemo/mpv/mpv.pri b/mpvdemo/mpv/mpv.pri new file mode 100644 index 0000000..a43790c --- /dev/null +++ b/mpvdemo/mpv/mpv.pri @@ -0,0 +1,22 @@ +HEADERS += $$PWD/mpvhead.h +HEADERS += $$PWD/mpvtool.h +HEADERS += $$PWD/mpv.h +SOURCES += $$PWD/mpv.cpp + +#表示64位的构建套件 +contains(QT_ARCH, x86_64) { +strLib = winlib64 +strInclude = include64 +} else { +strLib = winlib +strInclude = include +} + +INCLUDEPATH += $$PWD/$$strInclude +win32 { +LIBS += -L$$PWD/$$strLib/ -llibmpv +} + +#需要自己改为对应目录下的库 +unix:!macx {} +macx {} diff --git a/mpvdemo/mpv/mpvhead.h b/mpvdemo/mpv/mpvhead.h new file mode 100644 index 0000000..78dcbb4 --- /dev/null +++ b/mpvdemo/mpv/mpvhead.h @@ -0,0 +1,26 @@ +#ifndef MPVHEAD_H +#define MPVHEAD_H + +# ifdef __cplusplus +extern "C" { +# endif + +#include +#include + +# ifdef __cplusplus +} +# endif + +#include "qdatetime.h" +#pragma execution_character_set("utf-8") + +#define TIMEMS qPrintable(QTime::currentTime().toString("HH:mm:ss zzz")) +#define TIME qPrintable(QTime::currentTime().toString("HH:mm:ss")) +#define QDATE qPrintable(QDate::currentDate().toString("yyyy-MM-dd")) +#define QTIME qPrintable(QTime::currentTime().toString("HH-mm-ss")) +#define DATETIME qPrintable(QDateTime::currentDateTime().toString("yyyy-MM-dd HH:mm:ss")) +#define STRDATETIME qPrintable(QDateTime::currentDateTime().toString("yyyy-MM-dd-HH-mm-ss")) +#define STRDATETIMEMS qPrintable(QDateTime::currentDateTime().toString("yyyy-MM-dd-HH-mm-ss-zzz")) + +#endif // MPVHEAD_H diff --git a/mpvdemo/mpv/mpvtool.h b/mpvdemo/mpv/mpvtool.h new file mode 100644 index 0000000..0f06166 --- /dev/null +++ b/mpvdemo/mpv/mpvtool.h @@ -0,0 +1,373 @@ +#ifndef MPVTOOL_H +#define MPVTOOL_H + +#include + +#include +#include +#include +#include +#include +#include +#include + +namespace qtmpv { + +// Wrapper around mpv_handle. Does refcounting under the hood. +class Handle +{ + struct container { + container(mpv_handle *h) : mpv(h) {} + ~container() { + mpv_terminate_destroy(mpv); + } + mpv_handle *mpv; + }; + QSharedPointer sptr; +public: + // Construct a new Handle from a raw mpv_handle with refcount 1. If the + // last Handle goes out of scope, the mpv_handle will be destroyed with + // mpv_terminate_destroy(). + // Never destroy the mpv_handle manually when using this wrapper. You + // will create dangling pointers. Just let the wrapper take care of + // destroying the mpv_handle. + // Never create multiple wrappers from the same raw mpv_handle; copy the + // wrapper instead (that's what it's for). + static Handle FromRawHandle(mpv_handle *handle) { + Handle h; + h.sptr = QSharedPointer(new container(handle)); + return h; + } + + // Return the raw handle; for use with the libmpv C API. + operator mpv_handle *() const { + return sptr ? (*sptr).mpv : 0; + } +}; + +static inline QVariant node_to_variant(const mpv_node *node) +{ + switch (node->format) { + case MPV_FORMAT_STRING: + return QVariant(QString::fromUtf8(node->u.string)); + case MPV_FORMAT_FLAG: + return QVariant(static_cast(node->u.flag)); + case MPV_FORMAT_INT64: + return QVariant(static_cast(node->u.int64)); + case MPV_FORMAT_DOUBLE: + return QVariant(node->u.double_); + case MPV_FORMAT_NODE_ARRAY: { + mpv_node_list *list = node->u.list; + QVariantList qlist; + for (int n = 0; n < list->num; n++) { + qlist.append(node_to_variant(&list->values[n])); + } + return QVariant(qlist); + } + case MPV_FORMAT_NODE_MAP: { + mpv_node_list *list = node->u.list; + QVariantMap qmap; + for (int n = 0; n < list->num; n++) { + qmap.insert(QString::fromUtf8(list->keys[n]), + node_to_variant(&list->values[n])); + } + return QVariant(qmap); + } + default: // MPV_FORMAT_NONE, unknown values (e.g. future extensions) + return QVariant(); + } +} + +struct node_builder { + node_builder(const QVariant &v) { + set(&node_, v); + } + ~node_builder() { + free_node(&node_); + } + mpv_node *node() { + return &node_; + } +private: + Q_DISABLE_COPY(node_builder) + mpv_node node_; + mpv_node_list *create_list(mpv_node *dst, bool is_map, int num) { + dst->format = is_map ? MPV_FORMAT_NODE_MAP : MPV_FORMAT_NODE_ARRAY; + mpv_node_list *list = new mpv_node_list(); + dst->u.list = list; + if (!list) { + goto err; + } + list->values = new mpv_node[num](); + if (!list->values) { + goto err; + } + if (is_map) { + list->keys = new char *[num](); + if (!list->keys) { + goto err; + } + } + return list; + err: + free_node(dst); + return NULL; + } + char *dup_qstring(const QString &s) { + QByteArray b = s.toUtf8(); + char *r = new char[b.size() + 1]; + if (r) { + std::memcpy(r, b.data(), b.size() + 1); + } + return r; + } + bool test_type(const QVariant &v, QMetaType::Type t) { + // The Qt docs say: "Although this function is declared as returning + // "QVariant::Type(obsolete), the return value should be interpreted + // as QMetaType::Type." + // So a cast really seems to be needed to avoid warnings (urgh). + return static_cast(v.type()) == static_cast(t); + } + void set(mpv_node *dst, const QVariant &src) { + if (test_type(src, QMetaType::QString)) { + dst->format = MPV_FORMAT_STRING; + dst->u.string = dup_qstring(src.toString()); + if (!dst->u.string) { + goto fail; + } + } else if (test_type(src, QMetaType::Bool)) { + dst->format = MPV_FORMAT_FLAG; + dst->u.flag = src.toBool() ? 1 : 0; + } else if (test_type(src, QMetaType::Int) || + test_type(src, QMetaType::LongLong) || + test_type(src, QMetaType::UInt) || + test_type(src, QMetaType::ULongLong)) { + dst->format = MPV_FORMAT_INT64; + dst->u.int64 = src.toLongLong(); + } else if (test_type(src, QMetaType::Double)) { + dst->format = MPV_FORMAT_DOUBLE; + dst->u.double_ = src.toDouble(); + } else if (src.canConvert()) { + QVariantList qlist = src.toList(); + mpv_node_list *list = create_list(dst, false, qlist.size()); + if (!list) { + goto fail; + } + list->num = qlist.size(); + for (int n = 0; n < qlist.size(); n++) { + set(&list->values[n], qlist[n]); + } + } else if (src.canConvert()) { + QVariantMap qmap = src.toMap(); + mpv_node_list *list = create_list(dst, true, qmap.size()); + if (!list) { + goto fail; + } + list->num = qmap.size(); + for (int n = 0; n < qmap.size(); n++) { + list->keys[n] = dup_qstring(qmap.keys()[n]); + if (!list->keys[n]) { + free_node(dst); + goto fail; + } + set(&list->values[n], qmap.values()[n]); + } + } else { + goto fail; + } + return; + fail: + dst->format = MPV_FORMAT_NONE; + } + void free_node(mpv_node *dst) { + switch (dst->format) { + case MPV_FORMAT_STRING: + delete[] dst->u.string; + break; + case MPV_FORMAT_NODE_ARRAY: + case MPV_FORMAT_NODE_MAP: { + mpv_node_list *list = dst->u.list; + if (list) { + for (int n = 0; n < list->num; n++) { + if (list->keys) { + delete[] list->keys[n]; + } + if (list->values) { + free_node(&list->values[n]); + } + } + delete[] list->keys; + delete[] list->values; + } + delete list; + break; + } + default: + ; + } + dst->format = MPV_FORMAT_NONE; + } +}; + +/** + * RAII wrapper that calls mpv_free_node_contents() on the pointer. + */ +struct node_autofree { + mpv_node *ptr; + node_autofree(mpv_node *a_ptr) : ptr(a_ptr) {} + ~node_autofree() { + mpv_free_node_contents(ptr); + } +}; + +/** + * Return the given property as mpv_node converted to QVariant, or QVariant() + * on error. + * + * @deprecated use get_property() instead + * + * @param name the property name + */ +static inline QVariant get_property_variant(mpv_handle *ctx, const QString &name) +{ + mpv_node node; + if (mpv_get_property(ctx, name.toUtf8().data(), MPV_FORMAT_NODE, &node) < 0) { + return QVariant(); + } + node_autofree f(&node); + return node_to_variant(&node); +} + +/** + * Set the given property as mpv_node converted from the QVariant argument. + + * @deprecated use set_property() instead + */ +static inline int set_property_variant(mpv_handle *ctx, const QString &name, + const QVariant &v) +{ + node_builder node(v); + return mpv_set_property(ctx, name.toUtf8().data(), MPV_FORMAT_NODE, node.node()); +} + +/** + * Set the given option as mpv_node converted from the QVariant argument. + * + * @deprecated use set_property() instead + */ +static inline int set_option_variant(mpv_handle *ctx, const QString &name, + const QVariant &v) +{ + node_builder node(v); + return mpv_set_option(ctx, name.toUtf8().data(), MPV_FORMAT_NODE, node.node()); +} + +/** + * mpv_command_node() equivalent. Returns QVariant() on error (and + * unfortunately, the same on success). + * + * @deprecated use command() instead + */ +static inline QVariant command_variant(mpv_handle *ctx, const QVariant &args) +{ + node_builder node(args); + mpv_node res; + if (mpv_command_node(ctx, node.node(), &res) < 0) { + return QVariant(); + } + node_autofree f(&res); + return node_to_variant(&res); +} + +/** + * This is used to return error codes wrapped in QVariant for functions which + * return QVariant. + * + * You can use get_error() or is_error() to extract the error status from a + * QVariant value. + */ +struct ErrorReturn { + /** + * enum mpv_error value (or a value outside of it if ABI was extended) + */ + int error; + + ErrorReturn() : error(0) {} + explicit ErrorReturn(int err) : error(err) {} +}; + +/** + * Return the mpv error code packed into a QVariant, or 0 (success) if it's not + * an error value. + * + * @return error code (<0) or success (>=0) + */ +static inline int get_error(const QVariant &v) +{ + if (!v.canConvert()) { + return 0; + } + return v.value().error; +} + +/** + * Return whether the QVariant carries a mpv error code. + */ +static inline bool is_error(const QVariant &v) +{ + return get_error(v) < 0; +} + +/** + * Return the given property as mpv_node converted to QVariant, or QVariant() + * on error. + * + * @param name the property name + * @return the property value, or an ErrorReturn with the error code + */ +static inline QVariant get_property(mpv_handle *ctx, const QString &name) +{ + mpv_node node; + int err = mpv_get_property(ctx, name.toUtf8().data(), MPV_FORMAT_NODE, &node); + if (err < 0) { + return QVariant::fromValue(ErrorReturn(err)); + } + node_autofree f(&node); + return node_to_variant(&node); +} + +/** + * Set the given property as mpv_node converted from the QVariant argument. + * + * @return mpv error code (<0 on error, >= 0 on success) + */ +static inline int set_property(mpv_handle *ctx, const QString &name, + const QVariant &v) +{ + node_builder node(v); + return mpv_set_property(ctx, name.toUtf8().data(), MPV_FORMAT_NODE, node.node()); +} + +/** + * mpv_command_node() equivalent. + * + * @param args command arguments, with args[0] being the command name as string + * @return the property value, or an ErrorReturn with the error code + */ +static inline QVariant command(mpv_handle *ctx, const QVariant &args) +{ + node_builder node(args); + mpv_node res; + int err = mpv_command_node(ctx, node.node(), &res); + if (err < 0) { + return QVariant::fromValue(ErrorReturn(err)); + } + node_autofree f(&res); + return node_to_variant(&res); +} + +} + +Q_DECLARE_METATYPE(qtmpv::ErrorReturn) + +#endif // MPVTOOL_H diff --git a/mpvdemo/mpv/winlib/libmpv.lib b/mpvdemo/mpv/winlib/libmpv.lib new file mode 100644 index 0000000000000000000000000000000000000000..b9daf989276ce48f3157b9aab1414923fb01fa7f GIT binary patch literal 761570 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 literal 0 HcmV?d00001 diff --git a/mpvdemo/mpv/winlib64/libmpv.lib b/mpvdemo/mpv/winlib64/libmpv.lib new file mode 100644 index 0000000000000000000000000000000000000000..2dac51ff4d3e82cb7cc0a9a2ce9f4607c9b3380f GIT binary patch literal 742344 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

lt6s+YM_MZq>M=JoLP)nX6B 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 z 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 literal 0 HcmV?d00001 diff --git a/mpvdemo/mpvdemo.pro b/mpvdemo/mpvdemo.pro new file mode 100644 index 0000000..d9843c3 --- /dev/null +++ b/mpvdemo/mpvdemo.pro @@ -0,0 +1,19 @@ +QT += core gui + +greaterThan(QT_MAJOR_VERSION, 4): QT += widgets + +TARGET = mpvdemo +TEMPLATE = app +MOC_DIR = temp/moc +RCC_DIR = temp/rcc +UI_DIR = temp/ui +OBJECTS_DIR = temp/obj +DESTDIR = $$PWD/../bin + +SOURCES += main.cpp widget.cpp +HEADERS += widget.h +FORMS += widget.ui +CONFIG += warn_off + +INCLUDEPATH += $$PWD/mpv +include ($$PWD/mpv/mpv.pri) diff --git a/mpvdemo/readme.txt b/mpvdemo/readme.txt new file mode 100644 index 0000000..4195b6d --- /dev/null +++ b/mpvdemo/readme.txt @@ -0,0 +1,9 @@ +ԺǵýdllļƵִļͬһĿ¼ +Ӧ汾dllļصַhttps://pan.baidu.com/s/13LDRu6mXC6gaADtrGprNVA ȡ: ujm7 + +շǿ汾https://blog.csdn.net/feiyangqingyun/article/details/107972067 + +2. վ㣺[https://gitee.com/feiyangqingyun](https://gitee.com/feiyangqingyun) +3. վ㣺[https://github.com/feiyangqingyun](https://github.com/feiyangqingyun) +4. ҳ[https://blog.csdn.net/feiyangqingyun](https://blog.csdn.net/feiyangqingyun) +5. ֪ҳ[https://www.zhihu.com/people/feiyangqingyun/](https://www.zhihu.com/people/feiyangqingyun/) \ No newline at end of file diff --git a/mpvdemo/widget.cpp b/mpvdemo/widget.cpp new file mode 100644 index 0000000..de3dd74 --- /dev/null +++ b/mpvdemo/widget.cpp @@ -0,0 +1,37 @@ +#pragma execution_character_set("utf-8") +#include "widget.h" +#include "ui_widget.h" + +Widget::Widget(QWidget *parent) : QWidget(parent), ui(new Ui::Widget) +{ + ui->setupUi(this); + + QStringList urls; + urls << "rtsp://wowzaec2demo.streamlock.net/vod/mp4:BigBuckBunny_115k.mov"; + urls << "rtsp://admin:Admin123456@192.168.1.64:554/Streaming/Channels/102?transportmode=unicast&profile=Profile_2"; + urls << "rtsp://192.168.1.108:554/cam/realmonitor?channel=1&subtype=0&unicast=true&proto=Onvif"; + urls << "rtsp://192.168.1.15:554/media/video1"; + urls << "rtsp://192.168.1.15:554/media/video2"; + urls << "rtsp://192.168.1.247:554/av0_0"; + urls << "rtsp://192.168.1.247:554/av0_1"; + ui->cboxUrl->addItems(urls); + ui->cboxUrl->setCurrentIndex(5); +} + +Widget::~Widget() +{ + delete ui; +} + +void Widget::on_btnOpen_clicked() +{ + if (ui->btnOpen->text() == "打开") { + ui->btnOpen->setText("关闭"); + QString url = ui->cboxUrl->currentText().trimmed(); + ui->playWidget->setUrl(url); + ui->playWidget->open(); + } else { + ui->btnOpen->setText("打开"); + ui->playWidget->close(); + } +} diff --git a/mpvdemo/widget.h b/mpvdemo/widget.h new file mode 100644 index 0000000..0d54597 --- /dev/null +++ b/mpvdemo/widget.h @@ -0,0 +1,24 @@ +#ifndef WIDGET_H +#define WIDGET_H + +#include + +QT_BEGIN_NAMESPACE +namespace Ui { class Widget; } +QT_END_NAMESPACE + +class Widget : public QWidget +{ + Q_OBJECT + +public: + Widget(QWidget *parent = 0); + ~Widget(); + +private slots: + void on_btnOpen_clicked(); + +private: + Ui::Widget *ui; +}; +#endif // WIDGET_H diff --git a/mpvdemo/widget.ui b/mpvdemo/widget.ui new file mode 100644 index 0000000..3419f47 --- /dev/null +++ b/mpvdemo/widget.ui @@ -0,0 +1,63 @@ + + + Widget + + + + 0 + 0 + 700 + 600 + + + + Widget + + + + + + + 0 + 0 + + + + + + + + + + + 0 + 0 + + + + true + + + + + + + 打开 + + + + + + + + + + MpvWidget + QWidget +

mpv.h
+ 1 + + + + + diff --git a/vlcdemo/readme.txt b/vlcdemo/readme.txt index db5b099..f58b6ae 100644 --- a/vlcdemo/readme.txt +++ b/vlcdemo/readme.txt @@ -6,12 +6,4 @@ 2. վ㣺[https://gitee.com/feiyangqingyun](https://gitee.com/feiyangqingyun) 3. վ㣺[https://github.com/feiyangqingyun](https://github.com/feiyangqingyun) 4. ҳ[https://blog.csdn.net/feiyangqingyun](https://blog.csdn.net/feiyangqingyun) -5. ֪ҳ[https://www.zhihu.com/people/feiyangqingyun/](https://www.zhihu.com/people/feiyangqingyun/) - -1. ߳ʵʱ -2. ͬʱƵƵ -3. ֧Qt汾ϵͳ -4. ʹ룬չǿ -5. ѡvlc2vlc3汾 -6. ѡ32λ64λvlc -7. ע;ϸ \ No newline at end of file +5. ֪ҳ[https://www.zhihu.com/people/feiyangqingyun/](https://www.zhihu.com/people/feiyangqingyun/) \ No newline at end of file diff --git a/vlcdemo/vlc/vlc.cpp b/vlcdemo/vlc/vlc.cpp index 474a308..151deaa 100644 --- a/vlcdemo/vlc/vlc.cpp +++ b/vlcdemo/vlc/vlc.cpp @@ -1,9 +1,5 @@ #include "vlc.h" -#pragma execution_character_set("utf-8") -#define TIMEMS qPrintable(QTime::currentTime().toString("HH:mm:ss zzz")) -#define STRDATETIME qPrintable(QDateTime::currentDateTime().toString("yyyy-MM-dd-HH-mm-ss")) - VlcThread::VlcThread(QObject *parent) : QThread(parent) { setObjectName("VlcThread"); @@ -34,7 +30,7 @@ void VlcThread::run() free(); stopped = false; isPlay = false; - //qDebug() << TIMEMS << "stop vlc thread"; + //qDebug() << TIMEMS << "stop vlc1 thread"; } void VlcThread::setUrl(const QString &url) @@ -136,7 +132,7 @@ void VlcThread::stop() //实时视频显示窗体类 VlcWidget::VlcWidget(QWidget *parent) : QWidget(parent) { - vlc = new VlcThread(this); + thread = new VlcThread(this); } VlcWidget::~VlcWidget() @@ -146,7 +142,7 @@ VlcWidget::~VlcWidget() void VlcWidget::setUrl(const QString &url) { - vlc->setUrl(url); + thread->setUrl(url); } void VlcWidget::open() @@ -154,27 +150,27 @@ void VlcWidget::open() //qDebug() << TIMEMS << "open video" << objectName(); clear(); - vlc->play(); - vlc->start(); + thread->play(); + thread->start(); } void VlcWidget::pause() { - vlc->pause(); + thread->pause(); } void VlcWidget::next() { - vlc->next(); + thread->next(); } void VlcWidget::close() { //qDebug() << TIMEMS << "close video" << objectName(); - if (vlc->isRunning()) { - vlc->stop(); - vlc->quit(); - vlc->wait(3000); + if (thread->isRunning()) { + thread->stop(); + thread->quit(); + thread->wait(3000); } QTimer::singleShot(5, this, SLOT(clear())); diff --git a/vlcdemo/vlc/vlc.h b/vlcdemo/vlc/vlc.h index 1d34e46..b72daeb 100644 --- a/vlcdemo/vlc/vlc.h +++ b/vlcdemo/vlc/vlc.h @@ -7,7 +7,6 @@ #endif #include "vlchead.h" -class VlcWidget; class VlcThread : public QThread { @@ -23,7 +22,6 @@ private: volatile bool isPlay; //播放视频标志位 QString url; //视频流地址 - libvlc_instance_t *vlcInst; //载体对象 libvlc_media_t *vlcMedia; //媒体对象 libvlc_media_player_t *vlcPlayer; //播放对象 @@ -60,7 +58,7 @@ public: ~VlcWidget(); private: - VlcThread *vlc; //实时视频采集对象 + VlcThread *thread; public slots: //设置视频流地址 diff --git a/vlcdemo/vlc/vlc.pri b/vlcdemo/vlc/vlc.pri index ac8a468..adc38f0 100644 --- a/vlcdemo/vlc/vlc.pri +++ b/vlcdemo/vlc/vlc.pri @@ -1,6 +1,6 @@ -HEADERS += $$PWD/vlchead.h -HEADERS += $$PWD/vlc.h -SOURCES += $$PWD/vlc.cpp +HEADERS += $$PWD/vlchead.h +HEADERS += $$PWD/vlc.h +SOURCES += $$PWD/vlc.cpp #如果用的是vlc3内核请将vlc2改成vlc3,两种内核不兼容,头文件也不一样,建议用vlc2 DEFINES += vlc2 diff --git a/vlcdemo/vlc/vlchead.h b/vlcdemo/vlc/vlchead.h index 517ae96..aa02cf7 100644 --- a/vlcdemo/vlc/vlchead.h +++ b/vlcdemo/vlc/vlchead.h @@ -1,37 +1,5 @@ -/***************************************************************************** - * vlc.h: global header for libvlc - ***************************************************************************** - * Copyright (C) 1998-2008 VLC authors and VideoLAN - * $Id: 8f39094bd4b15c99288cecd001f76fcc10565daa $ - * - * Authors: Vincent Seguin - * Samuel Hocevar - * Gildas Bazin - * Derk-Jan Hartman - * Pierre d'Herbemont - * - * This program 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, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. - *****************************************************************************/ - -#ifndef VLC_VLC_H -#define VLC_VLC_H 1 - -/** - * \file - * This file defines libvlc new external API - */ +#ifndef VLCHEAD_H +#define VLCHEAD_H 1 # ifdef __cplusplus extern "C" { @@ -68,4 +36,15 @@ extern "C" { } # endif -#endif /* _VLC_VLC_H */ +#include "qdatetime.h" +#pragma execution_character_set("utf-8") + +#define TIMEMS qPrintable(QTime::currentTime().toString("HH:mm:ss zzz")) +#define TIME qPrintable(QTime::currentTime().toString("HH:mm:ss")) +#define QDATE qPrintable(QDate::currentDate().toString("yyyy-MM-dd")) +#define QTIME qPrintable(QTime::currentTime().toString("HH-mm-ss")) +#define DATETIME qPrintable(QDateTime::currentDateTime().toString("yyyy-MM-dd HH:mm:ss")) +#define STRDATETIME qPrintable(QDateTime::currentDateTime().toString("yyyy-MM-dd-HH-mm-ss")) +#define STRDATETIMEMS qPrintable(QDateTime::currentDateTime().toString("yyyy-MM-dd-HH-mm-ss-zzz")) + +#endif // VLCHEAD_H diff --git a/vlcdemo/widget.cpp b/vlcdemo/widget.cpp index dfc815a..de3dd74 100644 --- a/vlcdemo/widget.cpp +++ b/vlcdemo/widget.cpp @@ -2,9 +2,7 @@ #include "widget.h" #include "ui_widget.h" -Widget::Widget(QWidget *parent) - : QWidget(parent) - , ui(new Ui::Widget) +Widget::Widget(QWidget *parent) : QWidget(parent), ui(new Ui::Widget) { ui->setupUi(this); @@ -17,6 +15,7 @@ Widget::Widget(QWidget *parent) urls << "rtsp://192.168.1.247:554/av0_0"; urls << "rtsp://192.168.1.247:554/av0_1"; ui->cboxUrl->addItems(urls); + ui->cboxUrl->setCurrentIndex(5); } Widget::~Widget() @@ -29,10 +28,10 @@ void Widget::on_btnOpen_clicked() if (ui->btnOpen->text() == "打开") { ui->btnOpen->setText("关闭"); QString url = ui->cboxUrl->currentText().trimmed(); - ui->vlcWidget->setUrl(url); - ui->vlcWidget->open(); + ui->playWidget->setUrl(url); + ui->playWidget->open(); } else { ui->btnOpen->setText("打开"); - ui->vlcWidget->close(); + ui->playWidget->close(); } } diff --git a/vlcdemo/widget.ui b/vlcdemo/widget.ui index 32b150f..b289ab8 100644 --- a/vlcdemo/widget.ui +++ b/vlcdemo/widget.ui @@ -15,7 +15,7 @@
- + 0