Coverage Report

Created: 2026-01-26 07:36

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/mpv/video/out/vo_libmpv.c
Line
Count
Source
1
#include <assert.h>
2
#include <limits.h>
3
#include <math.h>
4
#include <stdatomic.h>
5
#include <stdbool.h>
6
#include <stdio.h>
7
#include <stdlib.h>
8
#include <string.h>
9
10
#include "mpv_talloc.h"
11
#include "common/common.h"
12
#include "misc/bstr.h"
13
#include "misc/dispatch.h"
14
#include "common/msg.h"
15
#include "options/m_config.h"
16
#include "options/options.h"
17
#include "aspect.h"
18
#include "dr_helper.h"
19
#include "vo.h"
20
#include "video/mp_image.h"
21
#include "sub/osd.h"
22
#include "osdep/threads.h"
23
#include "osdep/timer.h"
24
25
#include "common/global.h"
26
#include "player/client.h"
27
28
#include "libmpv.h"
29
30
#if HAVE_MACOS_COCOA_CB
31
#include "osdep/mac/app_bridge.h"
32
#endif
33
34
/*
35
 * mpv_render_context is managed by the host application - the host application
36
 * can access it any time, even if the VO is destroyed (or not created yet).
37
 *
38
 * - the libmpv user can mix render API and normal API; thus render API
39
 *   functions can wait on the core, but not the reverse
40
 * - the core does blocking calls into the VO thread, thus the VO functions
41
 *   can't wait on the user calling the API functions
42
 * - to make video timing work like it should, the VO thread waits on the
43
 *   render API user anyway, and the (unlikely) deadlock is avoided with
44
 *   a timeout
45
 *
46
 *  Locking:  mpv core > VO > mpv_render_context.lock > mp_client_api.lock
47
 *              > mpv_render_context.update_lock
48
 *  And: render thread > VO (wait for present)
49
 *       VO > render thread (wait for present done, via timeout)
50
 *
51
 *  Locking gets more complex with advanced_control enabled. Use
52
 *  mpv_render_context.dispatch with care; synchronous calls can add lock
53
 *  dependencies.
54
 */
55
56
struct vo_priv {
57
    struct mpv_render_context *ctx; // immutable after init
58
};
59
60
struct mpv_render_context {
61
    struct mp_log *log;
62
    struct mpv_global *global;
63
    struct mp_client_api *client_api;
64
65
    atomic_bool in_use;
66
67
    // --- Immutable after init
68
    struct mp_dispatch_queue *dispatch;
69
    bool advanced_control;
70
    struct dr_helper *dr;           // NULL if advanced_control disabled
71
72
    mp_mutex control_lock;
73
    // --- Protected by control_lock
74
    mp_render_cb_control_fn control_cb;
75
    void *control_cb_ctx;
76
77
    mp_mutex update_lock;
78
    mp_cond update_cond;     // paired with update_lock
79
80
    // --- Protected by update_lock
81
    mpv_render_update_fn update_cb;
82
    void *update_cb_ctx;
83
84
    mp_mutex lock;
85
    mp_cond video_wait;      // paired with lock
86
87
    // --- Protected by lock
88
    struct vo_frame *next_frame;    // next frame to draw
89
    int64_t present_count;          // incremented when next frame can be shown
90
    int64_t expected_flip_count;    // next vsync event for next_frame
91
    bool redrawing;                 // next_frame was a redraw request
92
    int64_t flip_count;
93
    struct vo_frame *cur_frame;
94
    struct mp_image_params img_params;
95
    int vp_w, vp_h;
96
    bool flip;
97
    bool imgfmt_supported[IMGFMT_END - IMGFMT_START];
98
    bool need_reconfig;
99
    bool need_resize;
100
    bool need_reset;
101
    bool need_update_external;
102
    struct vo *vo;
103
104
    // --- Mostly immutable after init.
105
    struct mp_hwdec_devices *hwdec_devs;
106
107
    // --- All of these can only be accessed from mpv_render_*() API, for
108
    //     which the user makes sure they're called synchronized.
109
    struct render_backend *renderer;
110
    struct m_config_cache *vo_opts_cache;
111
    struct mp_vo_opts *vo_opts;
112
};
113
114
const struct render_backend_fns *render_backends[] = {
115
    &render_backend_gpu,
116
    &render_backend_sw,
117
    NULL
118
};
119
120
static void update(struct mpv_render_context *ctx)
121
0
{
122
0
    mp_mutex_lock(&ctx->update_lock);
123
0
    if (ctx->update_cb)
124
0
        ctx->update_cb(ctx->update_cb_ctx);
125
126
0
    mp_cond_broadcast(&ctx->update_cond);
127
0
    mp_mutex_unlock(&ctx->update_lock);
128
0
}
129
130
void *get_mpv_render_param(mpv_render_param *params, mpv_render_param_type type,
131
                           void *def)
132
0
{
133
0
    for (int n = 0; params && params[n].type; n++) {
134
0
        if (params[n].type == type)
135
0
            return params[n].data;
136
0
    }
137
0
    return def;
138
0
}
139
140
static void forget_frames(struct mpv_render_context *ctx, bool all)
141
0
{
142
0
    mp_cond_broadcast(&ctx->video_wait);
143
0
    if (all) {
144
0
        talloc_free(ctx->cur_frame);
145
0
        ctx->cur_frame = NULL;
146
0
    }
147
0
}
148
149
static void dispatch_wakeup(void *ptr)
150
0
{
151
0
    struct mpv_render_context *ctx = ptr;
152
153
0
    update(ctx);
154
0
}
155
156
static struct mp_image *render_get_image(void *ptr, int imgfmt, int w, int h,
157
                                         int stride_align, int flags)
158
0
{
159
0
    struct mpv_render_context *ctx = ptr;
160
161
0
    return ctx->renderer->fns->get_image(ctx->renderer, imgfmt, w, h, stride_align, flags);
162
0
}
163
164
int mpv_render_context_create(mpv_render_context **res, mpv_handle *mpv,
165
                              mpv_render_param *params)
166
0
{
167
0
    mpv_render_context *ctx = talloc_zero(NULL, mpv_render_context);
168
0
    mp_mutex_init(&ctx->control_lock);
169
0
    mp_mutex_init(&ctx->lock);
170
0
    mp_mutex_init(&ctx->update_lock);
171
0
    mp_cond_init(&ctx->update_cond);
172
0
    mp_cond_init(&ctx->video_wait);
173
174
0
    ctx->global = mp_client_get_global(mpv);
175
0
    ctx->client_api = ctx->global->client_api;
176
0
    ctx->log = mp_log_new(ctx, ctx->global->log, "libmpv_render");
177
178
0
    ctx->vo_opts_cache = m_config_cache_alloc(ctx, ctx->global, &vo_sub_opts);
179
0
    ctx->vo_opts = ctx->vo_opts_cache->opts;
180
181
0
    ctx->dispatch = mp_dispatch_create(ctx);
182
0
    mp_dispatch_set_wakeup_fn(ctx->dispatch, dispatch_wakeup, ctx);
183
184
0
    if (GET_MPV_RENDER_PARAM(params, MPV_RENDER_PARAM_ADVANCED_CONTROL, int, 0))
185
0
        ctx->advanced_control = true;
186
187
0
    int err = MPV_ERROR_NOT_IMPLEMENTED;
188
0
    for (int n = 0; render_backends[n]; n++) {
189
0
        ctx->renderer = talloc_zero(NULL, struct render_backend);
190
0
        *ctx->renderer = (struct render_backend){
191
0
            .global = ctx->global,
192
0
            .log = ctx->log,
193
0
            .fns = render_backends[n],
194
0
        };
195
0
        err = ctx->renderer->fns->init(ctx->renderer, params);
196
0
        if (err >= 0)
197
0
            break;
198
0
        ctx->renderer->fns->destroy(ctx->renderer);
199
0
        talloc_free(ctx->renderer->priv);
200
0
        TA_FREEP(&ctx->renderer);
201
0
        if (err != MPV_ERROR_NOT_IMPLEMENTED)
202
0
            break;
203
0
    }
204
205
0
    if (err < 0) {
206
0
        mpv_render_context_free(ctx);
207
0
        return err;
208
0
    }
209
210
0
    ctx->hwdec_devs = ctx->renderer->hwdec_devs;
211
212
0
    for (int n = IMGFMT_START; n < IMGFMT_END; n++) {
213
0
        ctx->imgfmt_supported[n - IMGFMT_START] =
214
0
            ctx->renderer->fns->check_format(ctx->renderer, n);
215
0
    }
216
217
0
    if (ctx->renderer->fns->get_image && ctx->advanced_control)
218
0
        ctx->dr = dr_helper_create(ctx->dispatch, render_get_image, ctx);
219
220
0
    if (!mp_set_main_render_context(ctx->client_api, ctx, true)) {
221
0
        MP_ERR(ctx, "There is already a mpv_render_context set.\n");
222
0
        mpv_render_context_free(ctx);
223
0
        return MPV_ERROR_GENERIC;
224
0
    }
225
226
0
    *res = ctx;
227
0
    return 0;
228
0
}
229
230
void mpv_render_context_set_update_callback(mpv_render_context *ctx,
231
                                            mpv_render_update_fn callback,
232
                                            void *callback_ctx)
233
0
{
234
0
    mp_mutex_lock(&ctx->update_lock);
235
0
    ctx->update_cb = callback;
236
0
    ctx->update_cb_ctx = callback_ctx;
237
0
    if (ctx->update_cb)
238
0
        ctx->update_cb(ctx->update_cb_ctx);
239
0
    mp_mutex_unlock(&ctx->update_lock);
240
0
}
241
242
void mp_render_context_set_control_callback(mpv_render_context *ctx,
243
                                            mp_render_cb_control_fn callback,
244
                                            void *callback_ctx)
245
0
{
246
0
    mp_mutex_lock(&ctx->control_lock);
247
0
    ctx->control_cb = callback;
248
0
    ctx->control_cb_ctx = callback_ctx;
249
0
    mp_mutex_unlock(&ctx->control_lock);
250
0
}
251
252
void mpv_render_context_free(mpv_render_context *ctx)
253
0
{
254
0
    if (!ctx)
255
0
        return;
256
257
    // From here on, ctx becomes invisible and cannot be newly acquired. Only
258
    // a VO could still hold a reference.
259
0
    mp_set_main_render_context(ctx->client_api, ctx, false);
260
261
0
    if (atomic_load(&ctx->in_use)) {
262
        // Start destroy the VO, and also bring down the decoder etc., which
263
        // still might be using the hwdec context or use DR images. The above
264
        // mp_set_main_render_context() call guarantees it can't come back (so
265
        // ctx->vo can't change to non-NULL).
266
        // In theory, this races with vo_libmpv exiting and another VO being
267
        // used, which is a harmless grotesque corner case.
268
0
        kill_video_async(ctx->client_api);
269
270
0
        while (atomic_load(&ctx->in_use)) {
271
            // As a nasty detail, we need to wait until the VO is released, but
272
            // also need to react to update() calls during it (the update calls
273
            // are supposed to trigger processing ctx->dispatch). We solve this
274
            // by making the VO uninit function call mp_dispatch_interrupt().
275
            //
276
            // Other than that, processing ctx->dispatch is needed to serve the
277
            // video decoder, which might still not be fully destroyed, and e.g.
278
            // performs calls to release DR images (or, as a grotesque corner
279
            // case may even try to allocate new ones).
280
            //
281
            // Once the VO is released, ctx->dispatch becomes truly inactive.
282
            // (The libmpv API user could call mpv_render_context_update() while
283
            // mpv_render_context_free() is being called, but of course this is
284
            // invalid.)
285
0
            mp_dispatch_queue_process(ctx->dispatch, INFINITY);
286
0
        }
287
0
    }
288
289
0
    mp_mutex_lock(&ctx->lock);
290
    // Barrier - guarantee uninit() has left the lock region. It will access ctx
291
    // until the lock has been released, so we must not proceed with destruction
292
    // before we can acquire the lock. (The opposite, uninit() acquiring the
293
    // lock, can not happen anymore at this point - we've waited for VO uninit,
294
    // and prevented that new VOs can be created.)
295
0
    mp_mutex_unlock(&ctx->lock);
296
297
0
    mp_assert(!atomic_load(&ctx->in_use));
298
0
    mp_assert(!ctx->vo);
299
300
    // With the dispatch queue not being served anymore, allow frame free
301
    // requests from this thread to be served directly.
302
0
    if (ctx->dr)
303
0
        dr_helper_acquire_thread(ctx->dr);
304
305
    // Possibly remaining outstanding work.
306
0
    mp_dispatch_queue_process(ctx->dispatch, 0);
307
308
0
    forget_frames(ctx, true);
309
310
0
    if (ctx->renderer) {
311
0
        ctx->renderer->fns->destroy(ctx->renderer);
312
0
        talloc_free(ctx->renderer->priv);
313
0
        talloc_free(ctx->renderer);
314
0
    }
315
0
    talloc_free(ctx->dr);
316
0
    talloc_free(ctx->dispatch);
317
318
0
    mp_cond_destroy(&ctx->update_cond);
319
0
    mp_cond_destroy(&ctx->video_wait);
320
0
    mp_mutex_destroy(&ctx->update_lock);
321
0
    mp_mutex_destroy(&ctx->lock);
322
0
    mp_mutex_destroy(&ctx->control_lock);
323
324
0
    talloc_free(ctx);
325
0
}
326
327
// Try to mark the context as "in exclusive use" (e.g. by a VO).
328
// Note: the function must not acquire any locks, because it's called with an
329
// external leaf lock held.
330
bool mp_render_context_acquire(mpv_render_context *ctx)
331
0
{
332
0
    bool prev = false;
333
0
    return atomic_compare_exchange_strong(&ctx->in_use, &prev, true);
334
0
}
335
336
int mpv_render_context_render(mpv_render_context *ctx, mpv_render_param *params)
337
0
{
338
0
    mp_mutex_lock(&ctx->lock);
339
340
0
    int do_render =
341
0
        !GET_MPV_RENDER_PARAM(params, MPV_RENDER_PARAM_SKIP_RENDERING, int, 0);
342
343
0
    if (do_render) {
344
0
        int vp_w, vp_h;
345
0
        int err = ctx->renderer->fns->get_target_size(ctx->renderer, params,
346
0
                                                    &vp_w, &vp_h);
347
0
        if (err < 0) {
348
0
            mp_mutex_unlock(&ctx->lock);
349
0
            return err;
350
0
        }
351
352
0
        if (ctx->vo && (ctx->vp_w != vp_w || ctx->vp_h != vp_h ||
353
0
                        ctx->need_resize))
354
0
        {
355
0
            ctx->vp_w = vp_w;
356
0
            ctx->vp_h = vp_h;
357
358
0
            m_config_cache_update(ctx->vo_opts_cache);
359
360
0
            struct mp_rect src, dst;
361
0
            struct mp_osd_res osd;
362
0
            mp_get_src_dst_rects(ctx->log, ctx->vo_opts, ctx->vo->driver->caps,
363
0
                                &ctx->img_params, vp_w, abs(vp_h),
364
0
                                1.0, &src, &dst, &osd);
365
366
0
            ctx->renderer->fns->resize(ctx->renderer, &src, &dst, &osd);
367
0
        }
368
0
        ctx->need_resize = false;
369
0
    }
370
371
0
    if (ctx->need_reconfig)
372
0
        ctx->renderer->fns->reconfig(ctx->renderer, &ctx->img_params);
373
0
    ctx->need_reconfig = false;
374
375
0
    if (ctx->need_update_external)
376
0
        ctx->renderer->fns->update_external(ctx->renderer, ctx->vo);
377
0
    ctx->need_update_external = false;
378
379
0
    if (ctx->need_reset) {
380
0
        ctx->renderer->fns->reset(ctx->renderer);
381
0
        if (ctx->cur_frame)
382
0
            ctx->cur_frame->still = true;
383
0
    }
384
0
    ctx->need_reset = false;
385
386
0
    struct vo_frame *frame = ctx->next_frame;
387
0
    int64_t wait_present_count = ctx->present_count;
388
0
    if (frame) {
389
0
        ctx->next_frame = NULL;
390
0
        if (!(frame->redraw || !frame->current))
391
0
            wait_present_count += 1;
392
0
        mp_cond_broadcast(&ctx->video_wait);
393
0
        talloc_free(ctx->cur_frame);
394
0
        ctx->cur_frame = vo_frame_ref(frame);
395
0
    } else {
396
0
        frame = vo_frame_ref(ctx->cur_frame);
397
0
        if (frame)
398
0
            frame->redraw = true;
399
0
        MP_STATS(ctx, "glcb-noframe");
400
0
    }
401
0
    struct vo_frame dummy = {0};
402
0
    if (!frame)
403
0
        frame = &dummy;
404
405
0
    mp_mutex_unlock(&ctx->lock);
406
407
0
    MP_STATS(ctx, "glcb-render");
408
409
0
    int err = 0;
410
411
0
    if (do_render)
412
0
        err = ctx->renderer->fns->render(ctx->renderer, params, frame);
413
414
0
    if (frame != &dummy)
415
0
        talloc_free(frame);
416
417
0
    if (GET_MPV_RENDER_PARAM(params, MPV_RENDER_PARAM_BLOCK_FOR_TARGET_TIME,
418
0
                             int, 1))
419
0
    {
420
0
        mp_mutex_lock(&ctx->lock);
421
0
        while (wait_present_count > ctx->present_count)
422
0
            mp_cond_wait(&ctx->video_wait, &ctx->lock);
423
0
        mp_mutex_unlock(&ctx->lock);
424
0
    }
425
426
0
    return err;
427
0
}
428
429
void mpv_render_context_report_swap(mpv_render_context *ctx)
430
0
{
431
0
    MP_STATS(ctx, "glcb-reportflip");
432
433
0
    mp_mutex_lock(&ctx->lock);
434
0
    ctx->flip_count += 1;
435
0
    mp_cond_broadcast(&ctx->video_wait);
436
0
    mp_mutex_unlock(&ctx->lock);
437
0
}
438
439
uint64_t mpv_render_context_update(mpv_render_context *ctx)
440
0
{
441
0
    uint64_t res = 0;
442
443
0
    mp_dispatch_queue_process(ctx->dispatch, 0);
444
445
0
    mp_mutex_lock(&ctx->lock);
446
0
    if (ctx->next_frame)
447
0
        res |= MPV_RENDER_UPDATE_FRAME;
448
0
    mp_mutex_unlock(&ctx->lock);
449
0
    return res;
450
0
}
451
452
int mpv_render_context_set_parameter(mpv_render_context *ctx,
453
                                     mpv_render_param param)
454
0
{
455
0
    return ctx->renderer->fns->set_parameter(ctx->renderer, param);
456
0
}
457
458
int mpv_render_context_get_info(mpv_render_context *ctx,
459
                                mpv_render_param param)
460
0
{
461
0
    int res = MPV_ERROR_NOT_IMPLEMENTED;
462
0
    mp_mutex_lock(&ctx->lock);
463
464
0
    switch (param.type) {
465
0
    case MPV_RENDER_PARAM_NEXT_FRAME_INFO: {
466
0
        mpv_render_frame_info *info = param.data;
467
0
        *info = (mpv_render_frame_info){0};
468
0
        struct vo_frame *frame = ctx->next_frame;
469
0
        if (frame) {
470
0
            info->flags =
471
0
                MPV_RENDER_FRAME_INFO_PRESENT |
472
0
                (frame->redraw ? MPV_RENDER_FRAME_INFO_REDRAW : 0) |
473
0
                (frame->repeat ? MPV_RENDER_FRAME_INFO_REPEAT : 0) |
474
0
                (frame->display_synced && !frame->redraw ?
475
0
                    MPV_RENDER_FRAME_INFO_BLOCK_VSYNC : 0);
476
0
            info->target_time = frame->pts;
477
0
        }
478
0
        res = 0;
479
0
        break;
480
0
    }
481
0
    default:;
482
0
    }
483
484
0
    mp_mutex_unlock(&ctx->lock);
485
0
    return res;
486
0
}
487
488
static bool draw_frame(struct vo *vo, struct vo_frame *frame)
489
0
{
490
0
    struct vo_priv *p = vo->priv;
491
0
    struct mpv_render_context *ctx = p->ctx;
492
493
0
    mp_mutex_lock(&ctx->lock);
494
0
    mp_assert(!ctx->next_frame);
495
0
    ctx->next_frame = vo_frame_ref(frame);
496
0
    ctx->expected_flip_count = ctx->flip_count + 1;
497
0
    ctx->redrawing = frame->redraw || !frame->current;
498
0
    mp_mutex_unlock(&ctx->lock);
499
500
0
    update(ctx);
501
0
    return VO_TRUE;
502
0
}
503
504
static void flip_page(struct vo *vo)
505
0
{
506
0
    struct vo_priv *p = vo->priv;
507
0
    struct mpv_render_context *ctx = p->ctx;
508
0
    int64_t until = mp_time_ns() + MP_TIME_MS_TO_NS(200);
509
510
0
    mp_mutex_lock(&ctx->lock);
511
512
    // Wait until frame was rendered
513
0
    while (ctx->next_frame) {
514
0
        if (mp_cond_timedwait_until(&ctx->video_wait, &ctx->lock, until)) {
515
0
            if (ctx->next_frame) {
516
0
                MP_VERBOSE(vo, "mpv_render_context_render() not being called "
517
0
                           "or stuck.\n");
518
0
                goto done;
519
0
            }
520
0
        }
521
0
    }
522
523
    // Unblock mpv_render_context_render().
524
0
    ctx->present_count += 1;
525
0
    mp_cond_broadcast(&ctx->video_wait);
526
527
0
    if (ctx->redrawing)
528
0
        goto done; // do not block for redrawing
529
530
    // Wait until frame was presented
531
0
    while (ctx->expected_flip_count > ctx->flip_count) {
532
        // mpv_render_report_swap() is declared as optional API.
533
        // Assume the user calls it consistently _if_ it's called at all.
534
0
        if (!ctx->flip_count)
535
0
            break;
536
0
        if (mp_cond_timedwait_until(&ctx->video_wait, &ctx->lock, until)) {
537
0
            MP_VERBOSE(vo, "mpv_render_report_swap() not being called.\n");
538
0
            goto done;
539
0
        }
540
0
    }
541
542
0
done:
543
544
    // Cleanup after the API user is not reacting, or is being unusually slow.
545
0
    if (ctx->next_frame) {
546
0
        talloc_free(ctx->cur_frame);
547
0
        ctx->cur_frame = ctx->next_frame;
548
0
        ctx->next_frame = NULL;
549
0
        ctx->present_count += 2;
550
0
        mp_cond_signal(&ctx->video_wait);
551
0
        vo_increment_drop_count(vo, 1);
552
0
    }
553
554
0
    mp_mutex_unlock(&ctx->lock);
555
0
}
556
557
static int query_format(struct vo *vo, int format)
558
0
{
559
0
    struct vo_priv *p = vo->priv;
560
0
    struct mpv_render_context *ctx = p->ctx;
561
562
0
    bool ok = false;
563
0
    mp_mutex_lock(&ctx->lock);
564
0
    if (format >= IMGFMT_START && format < IMGFMT_END)
565
0
        ok = ctx->imgfmt_supported[format - IMGFMT_START];
566
0
    mp_mutex_unlock(&ctx->lock);
567
0
    return ok;
568
0
}
569
570
static void run_control_on_render_thread(void *p)
571
0
{
572
0
    void **args = p;
573
0
    struct mpv_render_context *ctx = args[0];
574
0
    int request = (intptr_t)args[1];
575
0
    void *data = args[2];
576
0
    int ret = VO_NOTIMPL;
577
578
0
    switch (request) {
579
0
    case VOCTRL_SCREENSHOT: {
580
0
        mp_mutex_lock(&ctx->lock);
581
0
        struct vo_frame *frame = vo_frame_ref(ctx->cur_frame);
582
0
        mp_mutex_unlock(&ctx->lock);
583
0
        if (frame && ctx->renderer->fns->screenshot)
584
0
            ctx->renderer->fns->screenshot(ctx->renderer, frame, data);
585
0
        talloc_free(frame);
586
0
        break;
587
0
    }
588
0
    case VOCTRL_PERFORMANCE_DATA: {
589
0
        if (ctx->renderer->fns->perfdata) {
590
0
            ctx->renderer->fns->perfdata(ctx->renderer, data);
591
0
            ret = VO_TRUE;
592
0
        }
593
0
        break;
594
0
    }
595
0
    }
596
597
0
    *(int *)args[3] = ret;
598
0
}
599
600
static int control(struct vo *vo, uint32_t request, void *data)
601
0
{
602
0
    struct vo_priv *p = vo->priv;
603
0
    struct mpv_render_context *ctx = p->ctx;
604
605
0
    switch (request) {
606
0
    case VOCTRL_RESET:
607
0
        mp_mutex_lock(&ctx->lock);
608
0
        forget_frames(ctx, false);
609
0
        ctx->need_reset = true;
610
0
        mp_mutex_unlock(&ctx->lock);
611
0
        vo->want_redraw = true;
612
0
        return VO_TRUE;
613
0
    case VOCTRL_PAUSE:
614
0
        vo->want_redraw = true;
615
0
        return VO_TRUE;
616
0
    case VOCTRL_SET_PANSCAN:
617
0
        mp_mutex_lock(&ctx->lock);
618
0
        ctx->need_resize = true;
619
0
        mp_mutex_unlock(&ctx->lock);
620
0
        vo->want_redraw = true;
621
0
        return VO_TRUE;
622
0
    case VOCTRL_UPDATE_RENDER_OPTS:
623
0
        mp_mutex_lock(&ctx->lock);
624
0
        ctx->need_update_external = true;
625
0
        mp_mutex_unlock(&ctx->lock);
626
0
        vo->want_redraw = true;
627
0
        return VO_TRUE;
628
0
    }
629
630
    // VOCTRLs to be run on the renderer thread (if possible at all).
631
0
    if (ctx->advanced_control) {
632
0
        switch (request) {
633
0
        case VOCTRL_SCREENSHOT:
634
0
        case VOCTRL_PERFORMANCE_DATA: {
635
0
            int ret;
636
0
            void *args[] = {ctx, (void *)(intptr_t)request, data, &ret};
637
0
            mp_dispatch_run(ctx->dispatch, run_control_on_render_thread, args);
638
0
            return ret;
639
0
        }
640
0
        }
641
0
    }
642
643
0
    int r = VO_NOTIMPL;
644
0
    mp_mutex_lock(&ctx->control_lock);
645
0
    if (ctx->control_cb) {
646
0
        int events = 0;
647
0
        r = p->ctx->control_cb(vo, p->ctx->control_cb_ctx,
648
0
                               &events, request, data);
649
0
        vo_event(vo, events);
650
0
    }
651
0
    mp_mutex_unlock(&ctx->control_lock);
652
653
0
    return r;
654
0
}
655
656
static struct mp_image *get_image(struct vo *vo, int imgfmt, int w, int h,
657
                                  int stride_align, int flags)
658
0
{
659
0
    struct vo_priv *p = vo->priv;
660
0
    struct mpv_render_context *ctx = p->ctx;
661
662
0
    if (ctx->dr)
663
0
        return dr_helper_get_image(ctx->dr, imgfmt, w, h, stride_align, flags);
664
665
0
    return NULL;
666
0
}
667
668
static int reconfig(struct vo *vo, struct mp_image_params *params)
669
0
{
670
0
    struct vo_priv *p = vo->priv;
671
0
    struct mpv_render_context *ctx = p->ctx;
672
673
0
    mp_mutex_lock(&ctx->lock);
674
0
    forget_frames(ctx, true);
675
0
    ctx->img_params = *params;
676
0
    ctx->need_reconfig = true;
677
0
    ctx->need_resize = true;
678
0
    mp_mutex_unlock(&ctx->lock);
679
680
0
    control(vo, VOCTRL_RECONFIG, NULL);
681
682
0
    return 0;
683
0
}
684
685
static void uninit(struct vo *vo)
686
0
{
687
0
    struct vo_priv *p = vo->priv;
688
0
    struct mpv_render_context *ctx = p->ctx;
689
690
0
    control(vo, VOCTRL_UNINIT, NULL);
691
692
0
    mp_mutex_lock(&ctx->lock);
693
694
0
    forget_frames(ctx, true);
695
0
    ctx->img_params = (struct mp_image_params){0};
696
0
    ctx->need_reconfig = true;
697
0
    ctx->need_resize = true;
698
0
    ctx->need_update_external = true;
699
0
    ctx->need_reset = true;
700
0
    ctx->vo = NULL;
701
702
    // The following do not normally need ctx->lock, however, ctx itself may
703
    // become invalid once we release ctx->lock.
704
0
    bool prev_in_use = atomic_exchange(&ctx->in_use, false);
705
0
    mp_assert(prev_in_use); // obviously must have been set
706
0
    mp_dispatch_interrupt(ctx->dispatch);
707
708
0
    mp_mutex_unlock(&ctx->lock);
709
0
}
710
711
static int preinit(struct vo *vo)
712
276
{
713
#if HAVE_MACOS_COCOA_CB
714
    cocoa_init_cocoa_cb();
715
#else
716
276
    if (vo->probing)
717
275
        return -1;
718
1
#endif
719
720
1
    struct vo_priv *p = vo->priv;
721
722
1
    struct mpv_render_context *ctx =
723
1
        mp_client_api_acquire_render_context(vo->global->client_api);
724
1
    p->ctx = ctx;
725
726
1
    if (!ctx) {
727
1
        if (!vo->probing)
728
1
            MP_FATAL(vo, "No render context set.\n");
729
1
        return -1;
730
1
    }
731
732
0
    mp_mutex_lock(&ctx->lock);
733
0
    ctx->vo = vo;
734
0
    ctx->need_resize = true;
735
0
    ctx->need_update_external = true;
736
0
    mp_mutex_unlock(&ctx->lock);
737
738
0
    vo->hwdec_devs = ctx->hwdec_devs;
739
0
    control(vo, VOCTRL_PREINIT, NULL);
740
741
0
    return 0;
742
1
}
743
744
const struct vo_driver video_out_libmpv = {
745
    .description = "render API for libmpv",
746
    .name = "libmpv",
747
    .caps = VO_CAP_ROTATE90 | VO_CAP_VFLIP,
748
    .preinit = preinit,
749
    .query_format = query_format,
750
    .reconfig = reconfig,
751
    .control = control,
752
    .get_image_ts = get_image,
753
    .draw_frame = draw_frame,
754
    .flip_page = flip_page,
755
    .uninit = uninit,
756
    .priv_size = sizeof(struct vo_priv),
757
};