/* * yt - A fully featured command line YouTube client * * Copyright (C) 2017 the mpv developers * Copyright (C) 2024 Benedikt Peetz <benedikt.peetz@b-peetz.de> * SPDX-License-Identifier: GPL-3.0-or-later * * This file is part of Yt. * * You should have received a copy of the License along with this program. * If not, see <https://www.gnu.org/licenses/gpl-3.0.txt>. */ #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 absolute 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 */ MPV_EXPORT int mpv_stream_cb_add_ro (mpv_handle *ctx, const char *protocol, void *user_data, mpv_stream_cb_open_ro_fn open_fn); #ifdef MPV_CPLUGIN_DYNAMIC_SYM MPV_DEFINE_SYM_PTR (mpv_stream_cb_add_ro) #define mpv_stream_cb_add_ro pfn_mpv_stream_cb_add_ro #endif #ifdef __cplusplus } #endif #endif