Coverage Report

Created: 2026-09-28 06:42

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/mpv/video/out/vo.c
Line
Count
Source
1
/*
2
 * This file is part of mpv.
3
 *
4
 * mpv is free software; you can redistribute it and/or
5
 * modify it under the terms of the GNU Lesser General Public
6
 * License as published by the Free Software Foundation; either
7
 * version 2.1 of the License, or (at your option) any later version.
8
 *
9
 * mpv is distributed in the hope that it will be useful,
10
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
 * GNU Lesser General Public License for more details.
13
 *
14
 * You should have received a copy of the GNU Lesser General Public
15
 * License along with mpv.  If not, see <http://www.gnu.org/licenses/>.
16
 */
17
18
#include <assert.h>
19
#include <math.h>
20
#include <stdatomic.h>
21
#include <stdbool.h>
22
#include <stdio.h>
23
#include <stdlib.h>
24
#include <string.h>
25
26
#include "mpv_talloc.h"
27
28
#include "config.h"
29
#include "osdep/timer.h"
30
#include "osdep/threads.h"
31
#include "misc/dispatch.h"
32
#include "misc/rendezvous.h"
33
#include "options/options.h"
34
#include "misc/bstr.h"
35
#include "vo.h"
36
#include "aspect.h"
37
#include "dr_helper.h"
38
#include "input/input.h"
39
#include "options/m_config.h"
40
#include "common/msg.h"
41
#include "common/global.h"
42
#include "common/stats.h"
43
#include "video/hwdec.h"
44
#include "video/mp_image.h"
45
#include "sub/osd.h"
46
#include "osdep/io.h"
47
#include "osdep/threads.h"
48
49
extern const struct vo_driver video_out_mediacodec_embed;
50
extern const struct vo_driver video_out_x11;
51
extern const struct vo_driver video_out_vdpau;
52
extern const struct vo_driver video_out_xv;
53
extern const struct vo_driver video_out_gpu;
54
extern const struct vo_driver video_out_gpu_next;
55
extern const struct vo_driver video_out_libmpv;
56
extern const struct vo_driver video_out_null;
57
extern const struct vo_driver video_out_image;
58
extern const struct vo_driver video_out_lavc;
59
extern const struct vo_driver video_out_caca;
60
extern const struct vo_driver video_out_drm;
61
extern const struct vo_driver video_out_direct3d;
62
extern const struct vo_driver video_out_sdl;
63
extern const struct vo_driver video_out_vaapi;
64
extern const struct vo_driver video_out_dmabuf_wayland;
65
extern const struct vo_driver video_out_wlshm;
66
extern const struct vo_driver video_out_tct;
67
extern const struct vo_driver video_out_sixel;
68
extern const struct vo_driver video_out_kitty;
69
70
static const struct vo_driver *const video_out_drivers[] =
71
{
72
    // high-quality and well-supported VOs first:
73
    &video_out_gpu_next,
74
    &video_out_gpu,
75
76
#if HAVE_VDPAU
77
    &video_out_vdpau,
78
#endif
79
#if HAVE_DIRECT3D
80
    &video_out_direct3d,
81
#endif
82
#if HAVE_DMABUF_WAYLAND
83
    &video_out_dmabuf_wayland,
84
#endif
85
#if HAVE_WAYLAND && HAVE_MEMFD_CREATE
86
    &video_out_wlshm,
87
#endif
88
#if HAVE_XV
89
    &video_out_xv,
90
#endif
91
#if HAVE_ANDROID
92
    &video_out_mediacodec_embed,
93
#endif
94
#if HAVE_SDL2_VIDEO
95
    &video_out_sdl,
96
#endif
97
#if HAVE_VAAPI_X11 && HAVE_GPL
98
    &video_out_vaapi,
99
#endif
100
#if HAVE_X11
101
    &video_out_x11,
102
#endif
103
    &video_out_libmpv,
104
    &video_out_null,
105
106
    // should not be auto-selected
107
    &video_out_image,
108
    &video_out_tct,
109
#if HAVE_CACA
110
    &video_out_caca,
111
#endif
112
#if HAVE_DRM
113
    &video_out_drm,
114
#endif
115
#if HAVE_SIXEL
116
    &video_out_sixel,
117
#endif
118
    &video_out_kitty,
119
    &video_out_lavc,
120
};
121
122
struct vo_internal {
123
    mp_thread thread;
124
    struct mp_dispatch_queue *dispatch;
125
    struct dr_helper *dr_helper;
126
127
    // --- The following fields are protected by lock
128
    mp_mutex lock;
129
    mp_cond wakeup;
130
131
    bool need_wakeup;
132
    bool terminate;
133
134
    bool hasframe;
135
    bool hasframe_rendered;
136
    bool request_redraw;            // redraw request from player to VO
137
    bool want_redraw;               // redraw request from VO to player
138
    bool send_reset;                // send VOCTRL_RESET
139
    bool paused;
140
    bool visible;
141
    bool wakeup_on_done;
142
    int queued_events;              // event mask for the user
143
    int internal_events;            // event mask for us
144
145
    double nominal_vsync_interval;
146
147
    double vsync_interval;
148
    int64_t *vsync_samples;
149
    int num_vsync_samples;
150
    int64_t num_total_vsync_samples;
151
    int64_t prev_vsync;
152
    double base_vsync;
153
    int drop_point;
154
    double estimated_vsync_interval;
155
    double estimated_vsync_jitter;
156
    bool expecting_vsync;
157
    int64_t num_successive_vsyncs;
158
159
    int64_t flip_queue_offset; // queue flip events at most this much in advance
160
    int64_t timing_offset;     // same (but from options; not VO configured)
161
162
    int64_t delayed_count;
163
    int64_t drop_count;
164
    bool dropped_frame;             // the previous frame was dropped
165
166
    struct vo_frame *current_frame; // last frame queued to the VO
167
168
    int64_t wakeup_pts;             // time at which to pull frame from decoder
169
170
    bool rendering;                 // true if an image is being rendered
171
    struct vo_frame *frame_queued;  // should be drawn next
172
    int req_frames;                 // VO's requested value of num_frames
173
    int frame_refs;                 // max frames the VO may reference at once
174
    uint64_t current_frame_id;
175
176
    double display_fps;
177
    double reported_display_fps;
178
179
    struct stats_ctx *stats;
180
};
181
182
extern const struct m_sub_options gl_video_conf;
183
184
static void forget_frames(struct vo *vo);
185
static MP_THREAD_VOID vo_thread(void *ptr);
186
187
static bool get_desc(struct m_obj_desc *dst, int index)
188
3.55M
{
189
3.55M
    if (index >= MP_ARRAY_SIZE(video_out_drivers))
190
192k
        return false;
191
3.35M
    const struct vo_driver *vo = video_out_drivers[index];
192
3.35M
    *dst = (struct m_obj_desc) {
193
3.35M
        .name = vo->name,
194
3.35M
        .description = vo->description,
195
3.35M
        .priv_size = vo->priv_size,
196
3.35M
        .priv_defaults = vo->priv_defaults,
197
3.35M
        .options = vo->options,
198
3.35M
        .options_prefix = vo->options_prefix,
199
3.35M
        .global_opts = vo->global_opts,
200
3.35M
        .hidden = vo->encode,
201
3.35M
        .p = vo,
202
3.35M
    };
203
3.35M
    return true;
204
3.55M
}
205
206
// For the vo option
207
const struct m_obj_list vo_obj_list = {
208
    .get_desc = get_desc,
209
    .description = "video outputs",
210
    .aliases = {
211
        {"gl", "gpu"},
212
        {"direct3d_shaders", "direct3d"},
213
        {"opengl", "gpu"},
214
        {"opengl-cb", "libmpv"},
215
        {0}
216
    },
217
    .allow_trailer = true,
218
    .disallow_positional_parameters = true,
219
    .use_global_options = true,
220
};
221
222
static void dispatch_wakeup_cb(void *ptr)
223
548k
{
224
548k
    struct vo *vo = ptr;
225
548k
    vo_wakeup(vo);
226
548k
}
227
228
// Initialize or update options from vo->opts
229
static void read_opts(struct vo *vo)
230
53.3k
{
231
53.3k
    struct vo_internal *in = vo->in;
232
233
53.3k
    mp_mutex_lock(&in->lock);
234
53.3k
    in->timing_offset = (uint64_t)(MP_TIME_S_TO_NS(vo->opts->timing_offset));
235
53.3k
    mp_mutex_unlock(&in->lock);
236
53.3k
}
237
238
static void update_opts(void *p)
239
370k
{
240
370k
    struct vo *vo = p;
241
242
370k
    if (m_config_cache_update(vo->opts_cache)) {
243
0
        read_opts(vo);
244
0
        if (vo->driver->control) {
245
0
            vo->driver->control(vo, VOCTRL_VO_OPTS_CHANGED, NULL);
246
            // "Legacy" update of video position related options.
247
            // Unlike VOCTRL_VO_OPTS_CHANGED, often not propagated to backends.
248
0
            vo->driver->control(vo, VOCTRL_SET_PANSCAN, NULL);
249
0
        }
250
0
    }
251
370k
}
252
253
// Does not include thread- and VO uninit.
254
static void dealloc_vo(struct vo *vo)
255
56.6k
{
256
56.6k
    forget_frames(vo); // implicitly synchronized
257
258
    // These must be free'd before vo->in->dispatch.
259
56.6k
    talloc_free(vo->opts_cache);
260
56.6k
    mp_mutex_destroy(&vo->params_mutex);
261
262
56.6k
    mp_mutex_destroy(&vo->in->lock);
263
56.6k
    mp_cond_destroy(&vo->in->wakeup);
264
56.6k
    talloc_free(vo);
265
56.6k
}
266
267
static struct vo *vo_create(bool probing, struct mpv_global *global,
268
                            struct vo_extra *ex, char *name)
269
60.6k
{
270
60.6k
    mp_assert(ex->wakeup_cb);
271
272
60.6k
    struct mp_log *log = mp_log_new(NULL, global->log, "vo");
273
60.6k
    struct m_obj_desc desc;
274
60.6k
    if (!m_obj_list_find(&desc, &vo_obj_list, bstr0(name))) {
275
3.96k
        mp_msg(log, MSGL_ERR, "Video output %s not found!\n", name);
276
3.96k
        talloc_free(log);
277
3.96k
        return NULL;
278
56.6k
    };
279
56.6k
    struct vo *vo = talloc_ptrtype(NULL, vo);
280
56.6k
    *vo = (struct vo) {
281
56.6k
        .log = mp_log_new(vo, log, name),
282
56.6k
        .driver = desc.p,
283
56.6k
        .global = global,
284
56.6k
        .encode_lavc_ctx = ex->encode_lavc_ctx,
285
56.6k
        .input_ctx = ex->input_ctx,
286
56.6k
        .osd = ex->osd,
287
56.6k
        .monitor_par = 1,
288
56.6k
        .extra = *ex,
289
56.6k
        .probing = probing,
290
56.6k
        .in = talloc(vo, struct vo_internal),
291
56.6k
    };
292
56.6k
    mp_mutex_init(&vo->params_mutex);
293
56.6k
    talloc_steal(vo, log);
294
56.6k
    *vo->in = (struct vo_internal) {
295
56.6k
        .dispatch = mp_dispatch_create(vo),
296
56.6k
        .req_frames = 1,
297
56.6k
        .frame_refs = 2, // current_frame + frame_queued
298
56.6k
        .estimated_vsync_jitter = -1,
299
56.6k
        .stats = stats_ctx_create(vo, global, "vo"),
300
56.6k
    };
301
56.6k
    mp_dispatch_set_wakeup_fn(vo->in->dispatch, dispatch_wakeup_cb, vo);
302
56.6k
    mp_mutex_init(&vo->in->lock);
303
56.6k
    mp_cond_init(&vo->in->wakeup);
304
305
56.6k
    vo->opts_cache = m_config_cache_alloc(NULL, global, &vo_sub_opts);
306
56.6k
    vo->opts = vo->opts_cache->opts;
307
308
56.6k
    m_config_cache_set_dispatch_change_cb(vo->opts_cache, vo->in->dispatch,
309
56.6k
                                          update_opts, vo);
310
311
56.6k
    mp_input_set_mouse_transform(vo->input_ctx, NULL, NULL);
312
56.6k
    if (vo->driver->encode != !!vo->encode_lavc_ctx)
313
34
        goto error;
314
56.6k
    vo->priv = m_config_group_from_desc(vo, vo->log, global, &desc, name);
315
56.6k
    if (!vo->priv)
316
0
        goto error;
317
318
56.6k
    if (mp_thread_create(&vo->in->thread, vo_thread, vo))
319
0
        goto error;
320
56.6k
    if (mp_rendezvous(vo, 0) < 0) { // init barrier
321
3.30k
        mp_thread_join(vo->in->thread);
322
3.30k
        goto error;
323
3.30k
    }
324
53.3k
    return vo;
325
326
3.33k
error:
327
3.33k
    dealloc_vo(vo);
328
3.33k
    return NULL;
329
56.6k
}
330
331
struct vo *init_best_video_out(struct mpv_global *global, struct vo_extra *ex)
332
53.4k
{
333
53.4k
    struct mp_vo_opts *opts = mp_get_config_group(NULL, global, &vo_sub_opts);
334
53.4k
    struct m_obj_settings *vo_list = opts->video_driver_list;
335
53.4k
    struct vo *vo = NULL;
336
    // first try the preferred drivers, with their optional subdevice param:
337
53.4k
    if (vo_list && vo_list[0].name) {
338
60.5k
        for (int n = 0; vo_list[n].name; n++) {
339
            // Something like "-vo name," allows fallback to autoprobing.
340
60.4k
            if (strlen(vo_list[n].name) == 0)
341
84
                goto autoprobe;
342
60.4k
            bool p = !!vo_list[n + 1].name;
343
60.3k
            vo = vo_create(p, global, ex, vo_list[n].name);
344
60.3k
            if (vo)
345
53.3k
                goto done;
346
60.3k
        }
347
89
        goto done;
348
53.4k
    }
349
84
autoprobe:
350
    // now try the rest...
351
336
    for (int i = 0; i < MP_ARRAY_SIZE(video_out_drivers); i++) {
352
336
        const struct vo_driver *driver = video_out_drivers[i];
353
336
        if (driver == &video_out_null)
354
84
            break;
355
252
        vo = vo_create(true, global, ex, (char *)driver->name);
356
252
        if (vo)
357
0
            goto done;
358
252
    }
359
53.4k
done:
360
53.4k
    talloc_free(opts);
361
53.4k
    return vo;
362
84
}
363
364
static void terminate_vo(void *p)
365
53.3k
{
366
53.3k
    struct vo *vo = p;
367
53.3k
    struct vo_internal *in = vo->in;
368
53.3k
    in->terminate = true;
369
53.3k
}
370
371
void vo_destroy(struct vo *vo)
372
53.3k
{
373
53.3k
    struct vo_internal *in = vo->in;
374
53.3k
    mp_dispatch_run(in->dispatch, terminate_vo, vo);
375
53.3k
    mp_thread_join(vo->in->thread);
376
53.3k
    dealloc_vo(vo);
377
53.3k
}
378
379
// Wakeup the playloop to queue new video frames etc.
380
static void wakeup_core(struct vo *vo)
381
783k
{
382
783k
    vo->extra.wakeup_cb(vo->extra.wakeup_ctx);
383
783k
}
384
385
// Drop timing information on discontinuities like seeking.
386
// Always called locked.
387
static void reset_vsync_timings(struct vo *vo)
388
492k
{
389
492k
    struct vo_internal *in = vo->in;
390
492k
    in->drop_point = 0;
391
492k
    in->base_vsync = 0;
392
492k
    in->expecting_vsync = false;
393
492k
    in->num_successive_vsyncs = 0;
394
492k
}
395
396
static double vsync_stddef(struct vo *vo, double ref_vsync)
397
0
{
398
0
    struct vo_internal *in = vo->in;
399
0
    double jitter = 0;
400
0
    for (int n = 0; n < in->num_vsync_samples; n++) {
401
0
        double diff = in->vsync_samples[n] - ref_vsync;
402
0
        jitter += diff * diff;
403
0
    }
404
0
    return sqrt(jitter / in->num_vsync_samples);
405
0
}
406
407
0
#define MAX_VSYNC_SAMPLES 1000
408
0
#define DELAY_VSYNC_SAMPLES 10
409
410
// Check if we should switch to measured average display FPS if it seems
411
// "better" then the system-reported one. (Note that small differences are
412
// handled as drift instead.)
413
static void check_estimated_display_fps(struct vo *vo)
414
0
{
415
0
    struct vo_internal *in = vo->in;
416
417
0
    bool use_estimated = false;
418
0
    if (in->num_total_vsync_samples >= MAX_VSYNC_SAMPLES / 2 &&
419
0
        in->estimated_vsync_interval <= 1e9 / 20.0 &&
420
0
        in->estimated_vsync_interval >= 1e9 / 400.0)
421
0
    {
422
0
        for (int n = 0; n < in->num_vsync_samples; n++) {
423
0
            if (fabs(in->vsync_samples[n] - in->estimated_vsync_interval)
424
0
                >= in->estimated_vsync_interval / 4)
425
0
                goto done;
426
0
        }
427
0
        double mjitter = vsync_stddef(vo, in->estimated_vsync_interval);
428
0
        double njitter = vsync_stddef(vo, in->nominal_vsync_interval);
429
0
        if (mjitter * 1.01 < njitter)
430
0
            use_estimated = true;
431
0
        done: ;
432
0
    }
433
0
    if (use_estimated == (fabs(in->vsync_interval - in->nominal_vsync_interval) < 1e9)) {
434
0
        if (use_estimated) {
435
0
            MP_TRACE(vo, "adjusting display FPS to a value closer to %.3f Hz\n",
436
0
                       1e9 / in->estimated_vsync_interval);
437
0
        } else {
438
0
            MP_TRACE(vo, "switching back to assuming display fps = %.3f Hz\n",
439
0
                       1e9 / in->nominal_vsync_interval);
440
0
        }
441
0
    }
442
0
    in->vsync_interval = use_estimated ? in->estimated_vsync_interval
443
0
                                       : in->nominal_vsync_interval;
444
0
}
445
446
// Attempt to detect vsyncs delayed/skipped by the driver. This tries to deal
447
// with strong jitter too, because some drivers have crap vsync timing.
448
static void vsync_skip_detection(struct vo *vo)
449
0
{
450
0
    struct vo_internal *in = vo->in;
451
452
0
    int window = 4;
453
0
    double t_r = in->prev_vsync, t_e = in->base_vsync, diff = 0.0, desync_early = 0.0;
454
0
    for (int n = 0; n < in->drop_point; n++) {
455
0
        diff += t_r - t_e;
456
0
        t_r -= in->vsync_samples[n];
457
0
        t_e -= in->vsync_interval;
458
0
        if (n == window + 1)
459
0
            desync_early = diff / window;
460
0
    }
461
0
    double desync = diff / in->num_vsync_samples;
462
0
    if (in->drop_point > window * 2 &&
463
0
        fabs(desync - desync_early) >= in->vsync_interval * 3 / 4)
464
0
    {
465
        // Assume a drop. An underflow can technically speaking not be a drop
466
        // (it's up to the driver what this is supposed to mean), but no reason
467
        // to treat it differently.
468
0
        in->base_vsync = in->prev_vsync;
469
0
        in->delayed_count += 1;
470
0
        in->drop_point = 0;
471
0
        MP_STATS(vo, "vo-delayed");
472
0
    }
473
0
    if (in->drop_point > 10)
474
0
        in->base_vsync += desync / 10;  // smooth out drift
475
0
}
476
477
// Always called locked.
478
static void update_vsync_timing_after_swap(struct vo *vo,
479
                                           struct vo_vsync_info *vsync)
480
363k
{
481
363k
    struct vo_internal *in = vo->in;
482
483
363k
    int64_t vsync_time = vsync->last_queue_display_time;
484
363k
    int64_t prev_vsync = in->prev_vsync;
485
363k
    in->prev_vsync = vsync_time;
486
487
363k
    if (!in->expecting_vsync) {
488
363k
        reset_vsync_timings(vo);
489
363k
        return;
490
363k
    }
491
492
0
    in->num_successive_vsyncs++;
493
0
    if (in->num_successive_vsyncs <= DELAY_VSYNC_SAMPLES) {
494
0
        in->base_vsync = vsync_time;
495
0
        return;
496
0
    }
497
498
0
    if (vsync_time <= 0 || vsync_time <= prev_vsync) {
499
0
        in->prev_vsync = 0;
500
0
        in->base_vsync = 0;
501
0
        return;
502
0
    }
503
504
0
    if (prev_vsync <= 0)
505
0
        return;
506
507
0
    if (in->num_vsync_samples >= MAX_VSYNC_SAMPLES)
508
0
        in->num_vsync_samples -= 1;
509
0
    MP_TARRAY_INSERT_AT(in, in->vsync_samples, in->num_vsync_samples, 0,
510
0
                        vsync_time - prev_vsync);
511
0
    in->drop_point = MPMIN(in->drop_point + 1, in->num_vsync_samples);
512
0
    in->num_total_vsync_samples += 1;
513
0
    if (in->base_vsync) {
514
0
        in->base_vsync += in->vsync_interval;
515
0
    } else {
516
0
        in->base_vsync = vsync_time;
517
0
    }
518
519
0
    double avg = 0;
520
0
    for (int n = 0; n < in->num_vsync_samples; n++) {
521
0
        mp_assert(in->vsync_samples[n] > 0);
522
0
        avg += in->vsync_samples[n];
523
0
    }
524
0
    in->estimated_vsync_interval = avg / in->num_vsync_samples;
525
0
    in->estimated_vsync_jitter =
526
0
        vsync_stddef(vo, in->vsync_interval) / in->vsync_interval;
527
528
0
    check_estimated_display_fps(vo);
529
0
    vsync_skip_detection(vo);
530
531
0
    MP_STATS(vo, "value %f jitter", in->estimated_vsync_jitter);
532
0
    MP_STATS(vo, "value %f vsync-diff", MP_TIME_NS_TO_S(in->vsync_samples[0]));
533
0
}
534
535
// to be called from VO thread only
536
static void update_display_fps(struct vo *vo)
537
1.55M
{
538
1.55M
    struct vo_internal *in = vo->in;
539
1.55M
    mp_mutex_lock(&in->lock);
540
1.55M
    if (in->internal_events & VO_EVENT_WIN_STATE) {
541
53.3k
        in->internal_events &= ~(unsigned)VO_EVENT_WIN_STATE;
542
543
53.3k
        mp_mutex_unlock(&in->lock);
544
545
53.3k
        double fps = 0;
546
53.3k
        vo->driver->control(vo, VOCTRL_GET_DISPLAY_FPS, &fps);
547
548
53.3k
        mp_mutex_lock(&in->lock);
549
550
53.3k
        in->reported_display_fps = fps;
551
53.3k
    }
552
553
1.55M
    double display_fps = vo->opts->display_fps_override;
554
1.55M
    if (display_fps <= 0)
555
1.55M
        display_fps = in->reported_display_fps;
556
557
1.55M
    if (in->display_fps != display_fps) {
558
56
        in->nominal_vsync_interval =  display_fps > 0 ? 1e9 / display_fps : 0;
559
56
        in->vsync_interval = MPMAX(in->nominal_vsync_interval, 1);
560
56
        in->display_fps = display_fps;
561
562
56
        MP_VERBOSE(vo, "Assuming %f FPS for display sync.\n", display_fps);
563
564
        // make sure to update the player
565
56
        in->queued_events |= VO_EVENT_WIN_STATE;
566
56
        wakeup_core(vo);
567
56
    }
568
569
1.55M
    mp_mutex_unlock(&in->lock);
570
1.55M
}
571
572
static void check_vo_caps(struct vo *vo)
573
25.6k
{
574
25.6k
    int rot = vo->params->rotate;
575
25.6k
    if (rot) {
576
151
        bool ok = rot % 90 ? false : (vo->driver->caps & VO_CAP_ROTATE90);
577
151
        if (!ok) {
578
151
           MP_WARN(vo, "Video is flagged as rotated by %d degrees, but the "
579
151
                   "video output does not support this.\n", rot);
580
151
        }
581
151
    }
582
25.6k
    if (vo->params->vflip && !(vo->driver->caps & VO_CAP_VFLIP))
583
25.6k
        MP_WARN(vo, "Video is flagged as vertically flipped, but the "
584
25.6k
                    "video output does not support this.\n");
585
25.6k
}
586
587
static void run_reconfig(void *p)
588
25.6k
{
589
25.6k
    void **pp = p;
590
25.6k
    struct vo *vo = pp[0];
591
25.6k
    struct mp_image *img = pp[1];
592
25.6k
    int *ret = pp[2];
593
594
25.6k
    struct mp_image_params *params = &img->params;
595
596
25.6k
    struct vo_internal *in = vo->in;
597
598
25.6k
    MP_VERBOSE(vo, "reconfig to %s\n", mp_image_params_to_str(params));
599
600
25.6k
    update_opts(vo);
601
602
25.6k
    mp_image_params_get_dsize(params, &vo->dwidth, &vo->dheight);
603
604
25.6k
    mp_mutex_lock(&vo->params_mutex);
605
25.6k
    talloc_free(vo->params);
606
25.6k
    vo->params = talloc_dup(vo, params);
607
25.6k
    vo->has_peak_detect_values = false;
608
25.6k
    mp_mutex_unlock(&vo->params_mutex);
609
610
25.6k
    if (vo->driver->reconfig2) {
611
0
        *ret = vo->driver->reconfig2(vo, img);
612
25.6k
    } else {
613
25.6k
        *ret = vo->driver->reconfig(vo, vo->params);
614
25.6k
    }
615
25.6k
    vo->config_ok = *ret >= 0;
616
25.6k
    if (vo->config_ok) {
617
25.6k
        check_vo_caps(vo);
618
25.6k
    } else {
619
0
        mp_mutex_lock(&vo->params_mutex);
620
0
        talloc_free(vo->params);
621
0
        vo->params = NULL;
622
0
        vo->has_peak_detect_values = false;
623
0
        mp_mutex_unlock(&vo->params_mutex);
624
0
    }
625
626
25.6k
    mp_mutex_lock(&in->lock);
627
25.6k
    talloc_free(in->current_frame);
628
25.6k
    in->current_frame = NULL;
629
25.6k
    forget_frames(vo);
630
25.6k
    reset_vsync_timings(vo);
631
25.6k
    mp_mutex_unlock(&in->lock);
632
633
25.6k
    update_display_fps(vo);
634
25.6k
}
635
636
int vo_reconfig(struct vo *vo, struct mp_image_params *params)
637
5
{
638
5
    int ret;
639
5
    struct mp_image dummy = {0};
640
5
    mp_image_set_params(&dummy, params);
641
5
    void *p[] = {vo, &dummy, &ret};
642
5
    mp_dispatch_run(vo->in->dispatch, run_reconfig, p);
643
5
    return ret;
644
5
}
645
646
int vo_reconfig2(struct vo *vo, struct mp_image *img)
647
25.6k
{
648
25.6k
    int ret;
649
25.6k
    void *p[] = {vo, img, &ret};
650
25.6k
    mp_dispatch_run(vo->in->dispatch, run_reconfig, p);
651
25.6k
    return ret;
652
25.6k
}
653
654
static void run_control(void *p)
655
344k
{
656
344k
    void **pp = p;
657
344k
    struct vo *vo = pp[0];
658
344k
    int request = (intptr_t)pp[1];
659
344k
    void *data = pp[2];
660
344k
    update_opts(vo);
661
344k
    int ret = vo->driver->control(vo, request, data);
662
344k
    if (pp[3])
663
157k
        *(int *)pp[3] = ret;
664
344k
}
665
666
int vo_control(struct vo *vo, int request, void *data)
667
157k
{
668
157k
    int ret;
669
157k
    void *p[] = {vo, (void *)(intptr_t)request, data, &ret};
670
157k
    mp_dispatch_run(vo->in->dispatch, run_control, p);
671
157k
    return ret;
672
157k
}
673
674
// Run vo_control() without waiting for a reply.
675
// (Only works for some VOCTRLs.)
676
void vo_control_async(struct vo *vo, int request, void *data)
677
187k
{
678
187k
    void *p[4] = {vo, (void *)(intptr_t)request, NULL, NULL};
679
187k
    void **d = talloc_memdup(NULL, p, sizeof(p));
680
681
187k
    switch (request) {
682
26.9k
    case VOCTRL_UPDATE_PLAYBACK_STATE:
683
26.9k
        d[2] = talloc_dup(d, (struct voctrl_playback_state *)data);
684
26.9k
        break;
685
34.5k
    case VOCTRL_KILL_SCREENSAVER:
686
160k
    case VOCTRL_RESTORE_SCREENSAVER:
687
160k
        break;
688
0
    default:
689
0
        abort(); // requires explicit support
690
187k
    }
691
692
187k
    mp_dispatch_enqueue_autofree(vo->in->dispatch, run_control, d);
693
187k
}
694
695
// must be called locked
696
static void forget_frames(struct vo *vo)
697
234k
{
698
234k
    struct vo_internal *in = vo->in;
699
234k
    in->hasframe = false;
700
234k
    in->hasframe_rendered = false;
701
234k
    in->drop_count = 0;
702
234k
    in->delayed_count = 0;
703
234k
    talloc_free(in->frame_queued);
704
234k
    in->frame_queued = NULL;
705
234k
    in->current_frame_id += VO_MAX_REQ_FRAMES + 1;
706
    // don't unref current_frame; we always want to be able to redraw it
707
234k
    if (in->current_frame) {
708
44.9k
        in->current_frame->num_vsyncs = 0; // but reset future repeats
709
44.9k
        in->current_frame->display_synced = false; // mark discontinuity
710
44.9k
    }
711
234k
}
712
713
// VOs which have no special requirements on UI event loops etc. can set the
714
// vo_driver.wait_events callback to this (and leave vo_driver.wakeup unset).
715
// This function must not be used or called for other purposes.
716
void vo_wait_default(struct vo *vo, int64_t until_time)
717
953k
{
718
953k
    struct vo_internal *in = vo->in;
719
720
953k
    mp_mutex_lock(&in->lock);
721
953k
    if (!in->need_wakeup)
722
896k
        mp_cond_timedwait_until(&in->wakeup, &in->lock, until_time);
723
953k
    mp_mutex_unlock(&in->lock);
724
953k
}
725
726
// Called unlocked.
727
static void wait_vo(struct vo *vo, int64_t until_time)
728
953k
{
729
953k
    struct vo_internal *in = vo->in;
730
731
953k
    if (vo->driver->wait_events) {
732
0
        vo->driver->wait_events(vo, until_time);
733
953k
    } else {
734
953k
        vo_wait_default(vo, until_time);
735
953k
    }
736
953k
    mp_mutex_lock(&in->lock);
737
953k
    in->need_wakeup = false;
738
953k
    mp_mutex_unlock(&in->lock);
739
953k
}
740
741
static void wakeup_locked(struct vo *vo)
742
1.08M
{
743
1.08M
    struct vo_internal *in = vo->in;
744
745
1.08M
    mp_cond_broadcast(&in->wakeup);
746
1.08M
    if (vo->driver->wakeup)
747
0
        vo->driver->wakeup(vo);
748
1.08M
    in->need_wakeup = true;
749
1.08M
}
750
751
// Wakeup VO thread, and make it check for new events with VOCTRL_CHECK_EVENTS.
752
// To be used by threaded VO backends.
753
void vo_wakeup(struct vo *vo)
754
548k
{
755
548k
    struct vo_internal *in = vo->in;
756
757
548k
    mp_mutex_lock(&in->lock);
758
548k
    wakeup_locked(vo);
759
548k
    mp_mutex_unlock(&in->lock);
760
548k
}
761
762
static int64_t get_current_frame_end(struct vo *vo)
763
31.6k
{
764
31.6k
    struct vo_internal *in = vo->in;
765
31.6k
    if (!in->current_frame)
766
22.4k
        return -1;
767
9.25k
    return in->current_frame->pts + MPMAX(in->current_frame->duration, 0);
768
31.6k
}
769
770
static int64_t get_display_synced_frame_end(struct vo *vo)
771
31.6k
{
772
31.6k
    struct vo_internal *in = vo->in;
773
31.6k
    mp_assert(!in->frame_queued);
774
31.6k
    int64_t res = 0;
775
31.6k
    if (in->base_vsync && in->vsync_interval > 1 && in->current_frame) {
776
0
        res = in->base_vsync;
777
0
        int extra = !!in->rendering;
778
0
        res += (in->current_frame->num_vsyncs + extra) * in->vsync_interval;
779
0
        if (!in->current_frame->display_synced)
780
0
            res = 0;
781
0
    }
782
31.6k
    return res;
783
31.6k
}
784
785
static bool still_displaying(struct vo *vo)
786
32.9k
{
787
32.9k
    struct vo_internal *in = vo->in;
788
32.9k
    bool working = in->rendering || in->frame_queued;
789
32.9k
    if (working)
790
1.22k
        goto done;
791
792
31.6k
    int64_t frame_end = get_display_synced_frame_end(vo);
793
31.6k
    if (frame_end > 0) {
794
0
        working = frame_end > in->base_vsync;
795
0
        goto done;
796
0
    }
797
798
31.6k
    frame_end = get_current_frame_end(vo);
799
31.6k
    if (frame_end < 0)
800
22.4k
        goto done;
801
9.25k
    working = mp_time_ns() < frame_end;
802
803
32.9k
done:
804
32.9k
    return working && in->hasframe;
805
9.25k
}
806
807
// Return true if there is still a frame being displayed (or queued).
808
bool vo_still_displaying(struct vo *vo)
809
32.9k
{
810
32.9k
    mp_mutex_lock(&vo->in->lock);
811
32.9k
    bool res = still_displaying(vo);
812
32.9k
    mp_mutex_unlock(&vo->in->lock);
813
32.9k
    return res;
814
32.9k
}
815
816
// Make vo issue a wakeup once vo_still_displaying() becomes false.
817
void vo_request_wakeup_on_done(struct vo *vo)
818
0
{
819
0
    struct vo_internal *in = vo->in;
820
0
    mp_mutex_lock(&vo->in->lock);
821
0
    if (still_displaying(vo)) {
822
0
        in->wakeup_on_done = true;
823
0
    } else {
824
0
        wakeup_core(vo);
825
0
    }
826
0
    mp_mutex_unlock(&vo->in->lock);
827
0
}
828
829
// Whether vo_queue_frame() can be called. If the VO is not ready yet, the
830
// function will return false, and the VO will call the wakeup callback once
831
// it's ready.
832
// next_pts is the exact time when the next frame should be displayed. If the
833
// VO is ready, but the time is too "early", return false, and call the wakeup
834
// callback once the time is right.
835
// If next_pts is negative, disable any timing and draw the frame as fast as
836
// possible.
837
bool vo_is_ready_for_frame(struct vo *vo, int64_t next_pts)
838
582k
{
839
582k
    struct vo_internal *in = vo->in;
840
582k
    mp_mutex_lock(&in->lock);
841
582k
    bool r = vo->config_ok && !in->frame_queued &&
842
551k
             (!in->current_frame || in->current_frame->num_vsyncs < 1);
843
582k
    if (r && next_pts >= 0) {
844
        // Don't show the frame too early - it would basically freeze the
845
        // display by disallowing OSD redrawing or VO interaction.
846
        // Actually render the frame at earliest the given offset before target
847
        // time.
848
363k
        next_pts -= in->timing_offset;
849
363k
        next_pts -= in->flip_queue_offset;
850
363k
        int64_t now = mp_time_ns();
851
363k
        if (next_pts > now)
852
0
            r = false;
853
363k
        if (!in->wakeup_pts || next_pts < in->wakeup_pts) {
854
363k
            in->wakeup_pts = next_pts;
855
            // If we have to wait, update the vo thread's timer.
856
363k
            if (!r)
857
0
                wakeup_locked(vo);
858
363k
        }
859
363k
    }
860
582k
    mp_mutex_unlock(&in->lock);
861
582k
    return r;
862
582k
}
863
864
// Check if the VO reports that the mpv window is visible.
865
bool vo_is_visible(struct vo *vo)
866
0
{
867
0
    struct vo_internal *in = vo->in;
868
0
    mp_mutex_lock(&in->lock);
869
0
    bool r = in->visible;
870
0
    mp_mutex_unlock(&in->lock);
871
0
    return r;
872
0
}
873
874
// Direct the VO thread to put the currently queued image on the screen.
875
// vo_is_ready_for_frame() must have returned true before this call.
876
// Ownership of frame is handed to the vo.
877
void vo_queue_frame(struct vo *vo, struct vo_frame *frame)
878
363k
{
879
363k
    struct vo_internal *in = vo->in;
880
363k
    mp_mutex_lock(&in->lock);
881
363k
    mp_assert(vo->config_ok && !in->frame_queued &&
882
363k
           (!in->current_frame || in->current_frame->num_vsyncs < 1));
883
363k
    in->hasframe = true;
884
363k
    frame->frame_id = ++(in->current_frame_id);
885
363k
    in->frame_queued = frame;
886
363k
    in->wakeup_pts = frame->display_synced
887
363k
                   ? 0 : frame->pts + MPMAX(frame->duration, 0);
888
363k
    wakeup_locked(vo);
889
363k
    mp_mutex_unlock(&in->lock);
890
363k
}
891
892
// If a frame is currently being rendered (or queued), wait until it's done.
893
// Otherwise, return immediately.
894
void vo_wait_frame(struct vo *vo)
895
22.4k
{
896
22.4k
    struct vo_internal *in = vo->in;
897
22.4k
    mp_mutex_lock(&in->lock);
898
44.8k
    while (in->frame_queued || in->rendering)
899
22.4k
        mp_cond_wait(&in->wakeup, &in->lock);
900
22.4k
    mp_mutex_unlock(&in->lock);
901
22.4k
}
902
903
// Wait until realtime is >= ts
904
// called without lock
905
static void wait_until(struct vo *vo, int64_t target)
906
363k
{
907
363k
    struct vo_internal *in = vo->in;
908
363k
    mp_mutex_lock(&in->lock);
909
363k
    while (target > mp_time_ns()) {
910
0
        if (in->queued_events & VO_EVENT_LIVE_RESIZING)
911
0
            break;
912
0
        if (mp_cond_timedwait_until(&in->wakeup, &in->lock, target))
913
0
            break;
914
0
    }
915
363k
    mp_mutex_unlock(&in->lock);
916
363k
}
917
918
static bool render_frame(struct vo *vo)
919
1.47M
{
920
1.47M
    struct vo_internal *in = vo->in;
921
1.47M
    struct vo_frame *frame = NULL;
922
1.47M
    bool more_frames = false;
923
924
1.47M
    update_display_fps(vo);
925
926
1.47M
    mp_mutex_lock(&in->lock);
927
928
1.47M
    if (in->frame_queued) {
929
363k
        talloc_free(in->current_frame);
930
363k
        in->current_frame = in->frame_queued;
931
363k
        in->frame_queued = NULL;
932
1.10M
    } else if (in->paused || !in->current_frame || !in->hasframe ||
933
81.8k
               (in->current_frame->display_synced && in->current_frame->num_vsyncs < 1) ||
934
81.8k
               !in->current_frame->display_synced)
935
1.10M
    {
936
1.10M
        goto done;
937
1.10M
    }
938
939
363k
    frame = vo_frame_ref(in->current_frame);
940
363k
    mp_assert(frame);
941
942
363k
    if (frame->display_synced) {
943
0
        frame->pts = 0;
944
0
        frame->duration = -1;
945
0
    }
946
947
363k
    int64_t now = mp_time_ns();
948
363k
    int64_t pts = frame->pts;
949
363k
    int64_t duration = frame->duration;
950
363k
    int64_t end_time = pts + duration;
951
952
    // Time at which we should flip_page on the VO.
953
363k
    int64_t target = frame->display_synced ? 0 : pts - in->flip_queue_offset;
954
955
    // "normal" strict drop threshold.
956
363k
    in->dropped_frame = duration >= 0 && end_time < now;
957
958
363k
    in->dropped_frame &= !frame->display_synced;
959
363k
    in->dropped_frame &= !(vo->driver->caps & VO_CAP_FRAMEDROP);
960
363k
    in->dropped_frame &= frame->can_drop;
961
    // Even if we're hopelessly behind, rather degrade to 10 FPS playback,
962
    // instead of just freezing the display forever.
963
363k
    in->dropped_frame &= now - in->prev_vsync < MP_TIME_MS_TO_NS(100);
964
363k
    in->dropped_frame &= in->hasframe_rendered;
965
966
    // Setup parameters for the next time this frame is drawn. ("frame" is the
967
    // frame currently drawn, while in->current_frame is the potentially next.)
968
363k
    in->current_frame->repeat = true;
969
363k
    if (frame->display_synced) {
970
        // Increment the offset only if it's not the last vsync. The current_frame
971
        // can still be reused. This is mostly important for redraws that might
972
        // overshoot the target vsync point.
973
0
        if (in->current_frame->num_vsyncs > 1) {
974
0
            in->current_frame->vsync_offset += in->current_frame->vsync_interval;
975
0
            in->current_frame->ideal_frame_vsync += in->current_frame->ideal_frame_vsync_duration;
976
0
        }
977
0
        in->dropped_frame |= in->current_frame->num_vsyncs < 1;
978
0
    }
979
363k
    if (in->current_frame->num_vsyncs > 0)
980
363k
        in->current_frame->num_vsyncs -= 1;
981
982
    // Always render when paused (it's typically the last frame for a while).
983
363k
    in->dropped_frame &= !in->paused;
984
985
363k
    bool use_vsync = in->current_frame->display_synced && !in->paused;
986
363k
    if (use_vsync && !in->expecting_vsync) // first DS frame in a row
987
0
        in->prev_vsync = now;
988
363k
    in->expecting_vsync = use_vsync;
989
990
363k
    if (in->dropped_frame) {
991
0
        in->drop_count += 1;
992
0
        wakeup_core(vo);
993
363k
    } else {
994
363k
        in->rendering = true;
995
363k
        in->hasframe_rendered = true;
996
363k
        int64_t prev_drop_count = vo->in->drop_count;
997
        // Can the core queue new video now? Non-display-sync uses a separate
998
        // timer instead, but possibly benefits from preparing a frame early.
999
363k
        bool can_queue = !in->frame_queued &&
1000
363k
            (in->current_frame->num_vsyncs < 1 || !use_vsync);
1001
        // This draw covers every redraw request made so far.
1002
363k
        in->request_redraw = false;
1003
363k
        mp_mutex_unlock(&in->lock);
1004
1005
363k
        if (can_queue)
1006
363k
            wakeup_core(vo);
1007
1008
363k
        stats_time_start(in->stats, "video-draw");
1009
1010
363k
        in->visible = vo->driver->draw_frame(vo, frame);
1011
1012
363k
        stats_time_end(in->stats, "video-draw");
1013
1014
363k
        wait_until(vo, target);
1015
1016
363k
        stats_time_start(in->stats, "video-flip");
1017
1018
363k
        vo->driver->flip_page(vo);
1019
1020
363k
        struct vo_vsync_info vsync = {
1021
363k
            .last_queue_display_time = -1,
1022
363k
            .skipped_vsyncs = -1,
1023
363k
        };
1024
363k
        if (vo->driver->get_vsync)
1025
0
            vo->driver->get_vsync(vo, &vsync);
1026
1027
        // Make up some crap if presentation feedback is missing.
1028
363k
        if (vsync.last_queue_display_time <= 0)
1029
363k
            vsync.last_queue_display_time = mp_time_ns();
1030
1031
363k
        stats_time_end(in->stats, "video-flip");
1032
1033
363k
        mp_mutex_lock(&in->lock);
1034
363k
        in->dropped_frame = prev_drop_count < vo->in->drop_count;
1035
363k
        in->rendering = false;
1036
1037
363k
        update_vsync_timing_after_swap(vo, &vsync);
1038
363k
    }
1039
1040
363k
    if (vo->driver->caps & VO_CAP_NORETAIN) {
1041
0
        talloc_free(in->current_frame);
1042
0
        in->current_frame = NULL;
1043
0
    }
1044
1045
363k
    if (in->dropped_frame)
1046
363k
        MP_STATS(vo, "drop-vo");
1047
1048
    // A redraw request that arrived while the VO was unlocked, will be handled
1049
    // by the already queued frame. Clear pending redraw requests to avoid
1050
    // unnecessary wakeups.
1051
363k
    if (in->frame_queued)
1052
17
        in->request_redraw = false;
1053
1054
363k
    if (in->current_frame && in->current_frame->num_vsyncs &&
1055
0
        in->current_frame->display_synced)
1056
0
        more_frames = true;
1057
1058
363k
    if (in->frame_queued && in->frame_queued->display_synced)
1059
0
        more_frames = true;
1060
1061
363k
    mp_cond_broadcast(&in->wakeup); // for vo_wait_frame()
1062
1063
1.47M
done:
1064
1.47M
    if (!(vo->driver->caps & VO_CAP_FRAMEOWNER) || in->dropped_frame)
1065
1.47M
        talloc_free(frame);
1066
1.47M
    mp_mutex_unlock(&in->lock);
1067
1068
1.47M
    return more_frames;
1069
363k
}
1070
1071
static void do_redraw(struct vo *vo)
1072
519k
{
1073
519k
    struct vo_internal *in = vo->in;
1074
1075
519k
    if (!vo->config_ok)
1076
502k
        return;
1077
1078
16.4k
    mp_mutex_lock(&in->lock);
1079
16.4k
    in->request_redraw = false;
1080
1081
16.4k
    if (vo->driver->caps & (VO_CAP_NORETAIN | VO_CAP_UNTIMED)) {
1082
8
        mp_mutex_unlock(&in->lock);
1083
8
        return;
1084
8
    }
1085
1086
16.4k
    bool full_redraw = in->dropped_frame;
1087
16.4k
    struct vo_frame *frame = vo_frame_ref(in->current_frame);
1088
16.4k
    if (frame)
1089
16.0k
        in->dropped_frame = false;
1090
16.4k
    struct vo_frame dummy = {0};
1091
16.4k
    if (!frame)
1092
400
        frame = &dummy;
1093
16.4k
    frame->redraw = !full_redraw; // unconditionally redraw if it was dropped
1094
16.4k
    frame->repeat = false;
1095
16.4k
    frame->still = true;
1096
16.4k
    frame->pts = 0;
1097
16.4k
    frame->duration = -1;
1098
16.4k
    mp_mutex_unlock(&in->lock);
1099
1100
16.4k
    vo->driver->draw_frame(vo, frame);
1101
16.4k
    vo->driver->flip_page(vo);
1102
1103
16.4k
    if (frame != &dummy && !(vo->driver->caps & VO_CAP_FRAMEOWNER))
1104
16.0k
        talloc_free(frame);
1105
16.4k
}
1106
1107
static struct mp_image *get_image_vo(void *ctx, int imgfmt, int w, int h,
1108
                                     int stride_align, int flags)
1109
0
{
1110
0
    struct vo *vo = ctx;
1111
0
    return vo->driver->get_image(vo, imgfmt, w, h, stride_align, flags);
1112
0
}
1113
1114
static MP_THREAD_VOID vo_thread(void *ptr)
1115
56.6k
{
1116
56.6k
    struct vo *vo = ptr;
1117
56.6k
    struct vo_internal *in = vo->in;
1118
56.6k
    bool vo_paused = false;
1119
1120
56.6k
    mp_thread_set_name("vo");
1121
1122
56.6k
    if (vo->driver->get_image) {
1123
2.98k
        in->dr_helper = dr_helper_create(in->dispatch, get_image_vo, vo);
1124
2.98k
        dr_helper_acquire_thread(in->dr_helper);
1125
2.98k
    }
1126
1127
56.6k
    int r = vo->driver->preinit(vo) ? -1 : 0;
1128
56.6k
    mp_rendezvous(vo, r); // init barrier
1129
56.6k
    if (r < 0)
1130
3.30k
        goto done;
1131
1132
53.3k
    read_opts(vo);
1133
53.3k
    update_display_fps(vo);
1134
53.3k
    vo_event(vo, VO_EVENT_WIN_STATE);
1135
1136
1.52M
    while (1) {
1137
1.52M
        mp_dispatch_queue_process(vo->in->dispatch, 0);
1138
1.52M
        if (in->terminate)
1139
53.3k
            break;
1140
1.47M
        stats_event(in->stats, "iterations");
1141
1.47M
        vo->driver->control(vo, VOCTRL_CHECK_EVENTS, NULL);
1142
1.47M
        bool working = render_frame(vo);
1143
1.47M
        int64_t now = mp_time_ns();
1144
1.47M
        int64_t wait_until = now + MP_TIME_S_TO_NS(working ? 0 : 1000);
1145
1.47M
        bool wakeup_on_done = false;
1146
1.47M
        int64_t wakeup_core_after = 0;
1147
1148
1.47M
        mp_mutex_lock(&in->lock);
1149
1.47M
        if (in->wakeup_pts) {
1150
366k
            if (in->wakeup_pts > now) {
1151
0
                wait_until = MPMIN(wait_until, in->wakeup_pts);
1152
366k
            } else {
1153
366k
                in->wakeup_pts = 0;
1154
366k
                wakeup_core(vo);
1155
366k
            }
1156
366k
        }
1157
1.47M
        if (vo->want_redraw) {
1158
3
            in->want_redraw = true;
1159
3
            wakeup_core(vo);
1160
3
        }
1161
1.47M
        if ((!working && !in->rendering && !in->frame_queued) && in->wakeup_on_done) {
1162
            // At this point we know VO is going to sleep
1163
0
            int64_t frame_end = get_current_frame_end(vo);
1164
0
            if (frame_end >= 0)
1165
0
                wakeup_core_after = frame_end;
1166
0
            wakeup_on_done = true;
1167
0
            in->wakeup_on_done = false;
1168
0
        }
1169
1.47M
        vo->want_redraw = false;
1170
1.47M
        bool redraw = in->request_redraw;
1171
1.47M
        bool send_reset = in->send_reset;
1172
1.47M
        in->send_reset = false;
1173
1.47M
        bool send_pause = in->paused != vo_paused;
1174
1.47M
        vo_paused = in->paused;
1175
1.47M
        mp_mutex_unlock(&in->lock);
1176
1177
1.47M
        if (send_reset)
1178
98.2k
            vo->driver->control(vo, VOCTRL_RESET, NULL);
1179
1.47M
        if (send_pause)
1180
4.31k
            vo->driver->control(vo, vo_paused ? VOCTRL_PAUSE : VOCTRL_RESUME, NULL);
1181
1.47M
        if (wait_until > now && redraw) {
1182
            // Allow manual redraws at most at display fps.
1183
519k
            int64_t max_interval = in->vsync_interval > 1 ? in->vsync_interval : 0;
1184
            // Some windowing platforms break if we submit frames too fast.
1185
519k
            if (vo->previous_redraw_time + max_interval <= now) {
1186
519k
                vo->driver->control(vo, VOCTRL_REDRAW, NULL);
1187
519k
                do_redraw(vo); // now is a good time
1188
519k
                vo->previous_redraw_time = now;
1189
519k
            } else {
1190
146
                wait_vo(vo, now + max_interval);
1191
146
            }
1192
519k
            continue;
1193
519k
        }
1194
953k
        if (vo->want_redraw) // might have been set by VOCTRLs
1195
0
            wait_until = 0;
1196
1197
953k
        if (wait_until <= now)
1198
0
            continue;
1199
1200
953k
        if (wakeup_on_done) {
1201
            // At this point wait_until should be longer than frame duration
1202
0
            if (wakeup_core_after >= 0 && wait_until >= wakeup_core_after) {
1203
0
                wait_vo(vo, wakeup_core_after);
1204
0
                mp_mutex_lock(&in->lock);
1205
0
                in->need_wakeup = true;
1206
0
                mp_mutex_unlock(&in->lock);
1207
0
            }
1208
0
            wakeup_core(vo);
1209
0
        }
1210
1211
953k
        wait_vo(vo, wait_until);
1212
953k
    }
1213
53.3k
    forget_frames(vo); // implicitly synchronized
1214
53.3k
    talloc_free(in->current_frame);
1215
53.3k
    in->current_frame = NULL;
1216
53.3k
    vo->driver->uninit(vo);
1217
56.6k
done:
1218
56.6k
    TA_FREEP(&in->dr_helper);
1219
56.6k
    MP_THREAD_RETURN();
1220
53.3k
}
1221
1222
void vo_set_paused(struct vo *vo, bool paused)
1223
50.0k
{
1224
50.0k
    struct vo_internal *in = vo->in;
1225
50.0k
    mp_mutex_lock(&in->lock);
1226
50.0k
    if (in->paused != paused) {
1227
4.49k
        in->paused = paused;
1228
4.49k
        if (in->paused && in->dropped_frame) {
1229
0
            in->request_redraw = true;
1230
0
            wakeup_core(vo);
1231
0
        }
1232
4.49k
        reset_vsync_timings(vo);
1233
4.49k
        wakeup_locked(vo);
1234
4.49k
    }
1235
50.0k
    mp_mutex_unlock(&in->lock);
1236
50.0k
}
1237
1238
int64_t vo_get_drop_count(struct vo *vo)
1239
12
{
1240
12
    mp_mutex_lock(&vo->in->lock);
1241
12
    int64_t r = vo->in->drop_count;
1242
12
    mp_mutex_unlock(&vo->in->lock);
1243
12
    return r;
1244
12
}
1245
1246
void vo_increment_drop_count(struct vo *vo, int64_t n)
1247
0
{
1248
0
    mp_mutex_lock(&vo->in->lock);
1249
0
    vo->in->drop_count += n;
1250
0
    mp_mutex_unlock(&vo->in->lock);
1251
0
}
1252
1253
// Make the VO redraw the OSD at some point in the future.
1254
void vo_redraw(struct vo *vo)
1255
17.9k
{
1256
17.9k
    struct vo_internal *in = vo->in;
1257
17.9k
    mp_mutex_lock(&in->lock);
1258
17.9k
    if (!in->request_redraw) {
1259
17.9k
        in->request_redraw = true;
1260
17.9k
        in->want_redraw = false;
1261
17.9k
        wakeup_locked(vo);
1262
17.9k
    }
1263
17.9k
    mp_mutex_unlock(&in->lock);
1264
17.9k
}
1265
1266
bool vo_want_redraw(struct vo *vo)
1267
307k
{
1268
307k
    struct vo_internal *in = vo->in;
1269
307k
    mp_mutex_lock(&in->lock);
1270
307k
    bool r = in->want_redraw;
1271
307k
    mp_mutex_unlock(&in->lock);
1272
307k
    return r;
1273
307k
}
1274
1275
void vo_seek_reset(struct vo *vo)
1276
99.2k
{
1277
99.2k
    struct vo_internal *in = vo->in;
1278
99.2k
    mp_mutex_lock(&in->lock);
1279
99.2k
    forget_frames(vo);
1280
99.2k
    reset_vsync_timings(vo);
1281
99.2k
    in->send_reset = true;
1282
99.2k
    wakeup_locked(vo);
1283
99.2k
    mp_mutex_unlock(&in->lock);
1284
99.2k
}
1285
1286
// Whether at least 1 frame was queued or rendered since last seek or reconfig.
1287
bool vo_has_frame(struct vo *vo)
1288
364k
{
1289
364k
    return vo->in->hasframe;
1290
364k
}
1291
1292
static void run_query_format(void *p)
1293
124k
{
1294
124k
    void **pp = p;
1295
124k
    struct vo *vo = pp[0];
1296
124k
    uint8_t *list = pp[1];
1297
69.8M
    for (int format = IMGFMT_START; format < IMGFMT_END; format++)
1298
69.6M
        list[format - IMGFMT_START] = vo->driver->query_format(vo, format);
1299
124k
}
1300
1301
// For each item in the list (allocated as uint8_t[IMGFMT_END - IMGFMT_START]),
1302
// set the supported format flags.
1303
void vo_query_formats(struct vo *vo, uint8_t *list)
1304
124k
{
1305
124k
    void *p[] = {vo, list};
1306
124k
    mp_dispatch_run(vo->in->dispatch, run_query_format, p);
1307
124k
}
1308
1309
// Calculate the appropriate source and destination rectangle to
1310
// get a correctly scaled picture, including pan-scan.
1311
// out_src: visible part of the video
1312
// out_dst: area of screen covered by the video source rectangle
1313
// out_osd: OSD size, OSD margins, etc.
1314
// Must be called from the VO thread only.
1315
void vo_get_src_dst_rects(struct vo *vo, struct mp_rect *out_src,
1316
                          struct mp_rect *out_dst, struct mp_osd_res *out_osd)
1317
3
{
1318
3
    if (!vo->params) {
1319
0
        *out_src = *out_dst = (struct mp_rect){0};
1320
0
        *out_osd = (struct mp_osd_res){0};
1321
0
        return;
1322
0
    }
1323
3
    mp_get_src_dst_rects(vo->log, vo->opts, vo->driver->caps, vo->params,
1324
3
                         vo->dwidth, vo->dheight, vo->monitor_par,
1325
3
                         out_src, out_dst, out_osd);
1326
3
}
1327
1328
// flip_page[_timed] will be called offset_us nanoseconds too early.
1329
// (For vo_vdpau, which does its own timing.)
1330
// num_req_frames set the requested number of requested vo_frame.frames.
1331
// (For vo_gpu interpolation.)
1332
// num_frame_refs sets the total number of frames the VO may reference at the
1333
// same time, including retained past frames. (For sizing fixed hardware
1334
// decoder surface pools.)
1335
void vo_set_queue_params(struct vo *vo, int64_t offset_ns, int num_req_frames,
1336
                         int num_frame_refs)
1337
0
{
1338
0
    struct vo_internal *in = vo->in;
1339
0
    mp_mutex_lock(&in->lock);
1340
0
    in->flip_queue_offset = offset_ns;
1341
0
    in->req_frames = MPCLAMP(num_req_frames, 1, VO_MAX_REQ_FRAMES);
1342
0
    in->frame_refs = MPCLAMP(num_frame_refs, in->req_frames, 2 * VO_MAX_REQ_FRAMES);
1343
0
    mp_mutex_unlock(&in->lock);
1344
0
}
1345
1346
int vo_get_num_req_frames(struct vo *vo)
1347
363k
{
1348
363k
    struct vo_internal *in = vo->in;
1349
363k
    mp_mutex_lock(&in->lock);
1350
363k
    int res = in->req_frames;
1351
363k
    mp_mutex_unlock(&in->lock);
1352
363k
    return res;
1353
363k
}
1354
1355
int vo_get_num_frame_refs(struct vo *vo)
1356
0
{
1357
0
    struct vo_internal *in = vo->in;
1358
0
    mp_mutex_lock(&in->lock);
1359
0
    int res = in->frame_refs;
1360
0
    mp_mutex_unlock(&in->lock);
1361
0
    return res;
1362
0
}
1363
1364
double vo_get_vsync_interval(struct vo *vo)
1365
0
{
1366
0
    struct vo_internal *in = vo->in;
1367
0
    mp_mutex_lock(&in->lock);
1368
0
    double res = vo->in->vsync_interval > 1 ? vo->in->vsync_interval : -1;
1369
0
    mp_mutex_unlock(&in->lock);
1370
0
    return res;
1371
0
}
1372
1373
double vo_get_estimated_vsync_interval(struct vo *vo)
1374
57
{
1375
57
    struct vo_internal *in = vo->in;
1376
57
    mp_mutex_lock(&in->lock);
1377
57
    double res = in->estimated_vsync_interval;
1378
57
    mp_mutex_unlock(&in->lock);
1379
57
    return res;
1380
57
}
1381
1382
double vo_get_estimated_vsync_jitter(struct vo *vo)
1383
51
{
1384
51
    struct vo_internal *in = vo->in;
1385
51
    mp_mutex_lock(&in->lock);
1386
51
    double res = in->estimated_vsync_jitter;
1387
51
    mp_mutex_unlock(&in->lock);
1388
51
    return res;
1389
51
}
1390
1391
// Get the time in seconds at after which the currently rendering frame will
1392
// end. Returns positive values if the frame is yet to be finished, negative
1393
// values if it already finished.
1394
// This can only be called while no new frame is queued (after
1395
// vo_is_ready_for_frame). Returns 0 for non-display synced frames, or if the
1396
// deadline for continuous display was missed.
1397
double vo_get_delay(struct vo *vo)
1398
0
{
1399
0
    struct vo_internal *in = vo->in;
1400
0
    mp_mutex_lock(&in->lock);
1401
0
    int64_t res = get_display_synced_frame_end(vo);
1402
0
    mp_mutex_unlock(&in->lock);
1403
0
    return res ? MP_TIME_NS_TO_S(res - mp_time_ns()) : 0;
1404
0
}
1405
1406
void vo_discard_timing_info(struct vo *vo)
1407
0
{
1408
0
    struct vo_internal *in = vo->in;
1409
0
    mp_mutex_lock(&in->lock);
1410
0
    reset_vsync_timings(vo);
1411
0
    mp_mutex_unlock(&in->lock);
1412
0
}
1413
1414
int64_t vo_get_delayed_count(struct vo *vo)
1415
192
{
1416
192
    struct vo_internal *in = vo->in;
1417
192
    mp_mutex_lock(&in->lock);
1418
192
    int64_t res = vo->in->delayed_count;
1419
192
    mp_mutex_unlock(&in->lock);
1420
192
    return res;
1421
192
}
1422
1423
double vo_get_display_fps(struct vo *vo)
1424
176
{
1425
176
    struct vo_internal *in = vo->in;
1426
176
    mp_mutex_lock(&in->lock);
1427
176
    double res = vo->in->display_fps;
1428
176
    mp_mutex_unlock(&in->lock);
1429
176
    return res;
1430
176
}
1431
1432
void * vo_get_display_swapchain(struct vo *vo)
1433
63
{
1434
63
    return vo->display_swapchain;
1435
63
}
1436
1437
// Set specific event flags, and wakeup the playback core if needed.
1438
// vo_query_and_reset_events() can retrieve the events again.
1439
void vo_event(struct vo *vo, int event)
1440
53.3k
{
1441
53.3k
    struct vo_internal *in = vo->in;
1442
53.3k
    mp_mutex_lock(&in->lock);
1443
53.3k
    if ((in->queued_events & event & VO_EVENTS_USER) != (event & VO_EVENTS_USER))
1444
53.2k
        wakeup_core(vo);
1445
53.3k
    if (event)
1446
53.3k
        wakeup_locked(vo);
1447
53.3k
    in->queued_events |= event;
1448
53.3k
    in->internal_events |= event;
1449
53.3k
    mp_mutex_unlock(&in->lock);
1450
53.3k
}
1451
1452
// Check event flags set with vo_event(). Return the mask of events that was
1453
// set and included in the events parameter. Clear the returned events.
1454
int vo_query_and_reset_events(struct vo *vo, int events)
1455
920k
{
1456
920k
    struct vo_internal *in = vo->in;
1457
920k
    mp_mutex_lock(&in->lock);
1458
920k
    int r = in->queued_events & events;
1459
920k
    in->queued_events &= ~(unsigned)r;
1460
920k
    mp_mutex_unlock(&in->lock);
1461
920k
    return r;
1462
920k
}
1463
1464
struct mp_image *vo_get_current_frame(struct vo *vo)
1465
196
{
1466
196
    struct vo_internal *in = vo->in;
1467
196
    mp_mutex_lock(&in->lock);
1468
196
    struct mp_image *r = NULL;
1469
196
    if (vo->in->current_frame)
1470
68
        r = mp_image_new_ref(vo->in->current_frame->current);
1471
196
    mp_mutex_unlock(&in->lock);
1472
196
    return r;
1473
196
}
1474
1475
struct vo_frame *vo_get_current_vo_frame(struct vo *vo)
1476
0
{
1477
0
    struct vo_internal *in = vo->in;
1478
0
    mp_mutex_lock(&in->lock);
1479
0
    struct vo_frame *r = vo_frame_ref(vo->in->current_frame);
1480
0
    mp_mutex_unlock(&in->lock);
1481
0
    return r;
1482
0
}
1483
1484
struct mp_image *vo_get_image(struct vo *vo, int imgfmt, int w, int h,
1485
                              int stride_align, int flags)
1486
23.3k
{
1487
23.3k
    if (vo->driver->get_image_ts)
1488
0
        return vo->driver->get_image_ts(vo, imgfmt, w, h, stride_align, flags);
1489
23.3k
    if (vo->in->dr_helper)
1490
0
        return dr_helper_get_image(vo->in->dr_helper, imgfmt, w, h, stride_align, flags);
1491
23.3k
    return NULL;
1492
23.3k
}
1493
1494
static void destroy_frame(void *p)
1495
743k
{
1496
743k
    struct vo_frame *frame = p;
1497
1.48M
    for (int n = 0; n < frame->num_frames; n++)
1498
743k
        talloc_free(frame->frames[n]);
1499
743k
}
1500
1501
// Return a new reference to the given frame. The image pointers are also new
1502
// references. Calling talloc_free() on the frame unrefs all currently set
1503
// image references. (Assuming current==frames[0].)
1504
struct vo_frame *vo_frame_ref(struct vo_frame *frame)
1505
743k
{
1506
743k
    if (!frame)
1507
400
        return NULL;
1508
1509
743k
    struct vo_frame *new = talloc_ptrtype(NULL, new);
1510
743k
    talloc_set_destructor(new, destroy_frame);
1511
743k
    *new = *frame;
1512
1.48M
    for (int n = 0; n < frame->num_frames; n++)
1513
743k
        new->frames[n] = mp_image_new_ref(frame->frames[n]);
1514
743k
    new->current = new->num_frames ? new->frames[0] : NULL;
1515
743k
    return new;
1516
743k
}
1517
1518
/*
1519
 * lookup an integer in a table, table must have 0 as the last key
1520
 * param: key key to search for
1521
 * returns translation corresponding to key or "to" value of last mapping
1522
 *         if not found.
1523
 */
1524
int lookup_keymap_table(const struct mp_keymap *map, int key)
1525
0
{
1526
0
    while (map->from && map->from != key)
1527
0
        map++;
1528
0
    return map->to;
1529
0
}
1530
1531
struct mp_image_params vo_get_current_params(struct vo *vo)
1532
2.37k
{
1533
2.37k
    struct mp_image_params p = {0};
1534
2.37k
    mp_mutex_lock(&vo->params_mutex);
1535
2.37k
    if (vo->params)
1536
2.21k
        p = *vo->params;
1537
2.37k
    mp_mutex_unlock(&vo->params_mutex);
1538
2.37k
    return p;
1539
2.37k
}
1540
1541
struct mp_image_params vo_get_target_params(struct vo *vo)
1542
24
{
1543
24
    struct mp_image_params p = {0};
1544
24
    mp_mutex_lock(&vo->params_mutex);
1545
24
    if (vo->target_params)
1546
0
        p = *vo->target_params;
1547
24
    mp_mutex_unlock(&vo->params_mutex);
1548
24
    return p;
1549
24
}