Coverage Report

Created: 2025-09-04 07:15

/src/mpv/player/osd.c
Line
Count
Source (jump to first uncovered line)
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 <stddef.h>
19
#include <stdbool.h>
20
#include <inttypes.h>
21
#include <math.h>
22
#include <limits.h>
23
#include <assert.h>
24
25
#include "mpv_talloc.h"
26
27
#include "common/msg.h"
28
#include "common/msg_control.h"
29
#include "options/options.h"
30
#include "common/common.h"
31
#include "options/m_property.h"
32
#include "filters/f_decoder_wrapper.h"
33
#include "common/encode.h"
34
35
#include "osdep/terminal.h"
36
#include "osdep/timer.h"
37
38
#include "demux/demux.h"
39
#include "stream/stream.h"
40
#include "sub/osd.h"
41
42
#include "video/out/vo.h"
43
44
#include "core.h"
45
#include "command.h"
46
47
186
#define saddf(var, ...) (*(var) = talloc_asprintf_append((*var), __VA_ARGS__))
48
49
// append time in the hh:mm:ss format (plus fractions if wanted)
50
static void sadd_hhmmssff(char **buf, double time, bool fractions)
51
78
{
52
78
    char *s = mp_format_time(time, fractions);
53
78
    *buf = talloc_strdup_append(*buf, s);
54
78
    talloc_free(s);
55
78
}
56
57
38
static void sadd_percentage(char **buf, double ratio) {
58
38
    if (ratio >= 0)
59
36
        *buf = talloc_asprintf_append(*buf, " (%.f%%)", ratio * 100);
60
38
}
61
62
static char *join_lines(void *ta_ctx, char **parts, int num_parts)
63
3.13k
{
64
3.13k
    char *res = talloc_strdup(ta_ctx, "");
65
3.17k
    for (int n = 0; n < num_parts; n++)
66
36
        res = talloc_asprintf_append(res, "%s%s", n ? "\n" : "", parts[n]);
67
3.13k
    return res;
68
3.13k
}
69
70
static bool term_osd_empty(char *text)
71
12.5k
{
72
12.5k
    return !text || !text[0] || !strcmp(text, TERM_MSG_0);
73
12.5k
}
74
75
static void term_osd_update(struct MPContext *mpctx)
76
514k
{
77
514k
    int num_parts = 0;
78
514k
    char *parts[4] = {0};
79
80
514k
    if (!mpctx->opts->use_terminal)
81
511k
        return;
82
83
3.13k
    if (!term_osd_empty(mpctx->term_osd_subs[0]))
84
0
        parts[num_parts++] = mpctx->term_osd_subs[0];
85
3.13k
    if (!term_osd_empty(mpctx->term_osd_subs[1]))
86
0
        parts[num_parts++] = mpctx->term_osd_subs[1];
87
3.13k
    if (!term_osd_empty(mpctx->term_osd_text))
88
0
        parts[num_parts++] = mpctx->term_osd_text;
89
3.13k
    if (!term_osd_empty(mpctx->term_osd_status))
90
36
        parts[num_parts++] = mpctx->term_osd_status;
91
92
3.13k
    char *s = join_lines(mpctx, parts, num_parts);
93
94
3.13k
    if (strcmp(mpctx->term_osd_contents, s) == 0 &&
95
3.13k
        mp_msg_has_status_line(mpctx->global))
96
0
    {
97
0
        talloc_free(s);
98
3.13k
    } else {
99
3.13k
        talloc_free(mpctx->term_osd_contents);
100
3.13k
        mpctx->term_osd_contents = s;
101
3.13k
        mp_msg(mpctx->statusline, MSGL_STATUS, "%s", s);
102
3.13k
    }
103
3.13k
}
104
105
static void term_osd_update_title(struct MPContext *mpctx)
106
514k
{
107
514k
    if (!mpctx->opts->use_terminal)
108
511k
        return;
109
110
3.13k
    char *s = mp_property_expand_escaped_string(mpctx, mpctx->opts->term_title);
111
3.13k
    if (bstr_equals(bstr0(s), bstr0(mpctx->term_osd_title))) {
112
3.12k
        talloc_free(s);
113
3.12k
        return;
114
3.12k
    }
115
116
16
    mp_msg_set_term_title(mpctx->statusline, s);
117
16
    mpctx->term_osd_title = talloc_steal(mpctx, s);
118
16
}
119
120
void term_osd_clear_subs(struct MPContext *mpctx)
121
452k
{
122
452k
    term_osd_set_subs(mpctx, NULL, 0);
123
452k
    term_osd_set_subs(mpctx, NULL, 1);
124
452k
}
125
126
void term_osd_set_subs(struct MPContext *mpctx, const char *text, int order)
127
905k
{
128
905k
    if (mpctx->video_out || !text || !mpctx->opts->subs_shared->sub_visibility[order])
129
905k
        text = ""; // disable
130
905k
    if (strcmp(mpctx->term_osd_subs[order] ? mpctx->term_osd_subs[order] : "", text) == 0)
131
905k
        return;
132
8
    talloc_replace(mpctx, mpctx->term_osd_subs[order], text);
133
8
    term_osd_update(mpctx);
134
8
}
135
136
static void term_osd_set_text_lazy(struct MPContext *mpctx, const char *text)
137
514k
{
138
514k
    bool video_osd = mpctx->video_out && mpctx->opts->video_osd;
139
514k
    if ((video_osd && mpctx->opts->term_osd != 1) || !text)
140
514k
        text = ""; // disable
141
514k
    talloc_replace(mpctx, mpctx->term_osd_text, text);
142
514k
}
143
144
static void term_osd_set_status_lazy(struct MPContext *mpctx, const char *text)
145
2.95k
{
146
2.95k
    talloc_replace(mpctx, mpctx->term_osd_status, text);
147
2.95k
}
148
149
static void add_term_osd_bar(struct MPContext *mpctx, char **line, int width)
150
0
{
151
0
    struct MPOpts *opts = mpctx->opts;
152
153
0
    if (width < 5)
154
0
        return;
155
156
0
    int pos = get_current_pos_ratio(mpctx, false) * (width - 3);
157
0
    pos = MPCLAMP(pos, 0, width - 3);
158
159
0
    bstr chars = bstr0(opts->term_osd_bar_chars);
160
0
    bstr parts[5];
161
0
    for (int n = 0; n < 5; n++)
162
0
        parts[n] = bstr_split_utf8(chars, &chars);
163
164
0
    saddf(line, "\r%.*s", BSTR_P(parts[0]));
165
0
    for (int n = 0; n < pos; n++)
166
0
        saddf(line, "%.*s", BSTR_P(parts[1]));
167
0
    saddf(line, "%.*s", BSTR_P(parts[2]));
168
0
    for (int n = 0; n < width - 3 - pos; n++)
169
0
        saddf(line, "%.*s", BSTR_P(parts[3]));
170
0
    saddf(line, "%.*s", BSTR_P(parts[4]));
171
0
}
172
173
static bool is_busy(struct MPContext *mpctx)
174
355
{
175
355
    return !mpctx->restart_complete && mp_time_sec() - mpctx->start_timestamp > 0.3;
176
355
}
177
178
static char *get_term_status_msg(struct MPContext *mpctx)
179
36
{
180
36
    struct MPOpts *opts = mpctx->opts;
181
182
36
    if (opts->status_msg)
183
0
        return mp_property_expand_escaped_string(mpctx, opts->status_msg);
184
185
36
    char *line = NULL;
186
187
    // Playback status
188
36
    if (is_busy(mpctx)) {
189
0
        saddf(&line, "(...) ");
190
36
    } else if (mpctx->paused_for_cache && !opts->pause) {
191
0
        saddf(&line, "(Buffering) ");
192
36
    } else if (mpctx->paused) {
193
0
        saddf(&line, "(Paused) ");
194
0
    }
195
196
36
    if (mpctx->ao_chain)
197
36
        saddf(&line, "A");
198
36
    if (mpctx->vo_chain)
199
36
        saddf(&line, "V");
200
36
    saddf(&line, ": ");
201
202
    // Playback position
203
36
    sadd_hhmmssff(&line, get_playback_time(mpctx), opts->osd_fractions);
204
36
    saddf(&line, " / ");
205
36
    sadd_hhmmssff(&line, get_time_length(mpctx), opts->osd_fractions);
206
207
36
    sadd_percentage(&line, get_current_pos_ratio(mpctx, false));
208
209
    // other
210
36
    if (opts->playback_speed != 1)
211
0
        saddf(&line, " x%4.2f", opts->playback_speed);
212
213
    // A-V sync
214
36
    if (mpctx->ao_chain && mpctx->vo_chain && !mpctx->vo_chain->is_sparse) {
215
36
        saddf(&line, " A-V:%7.3f", mpctx->last_av_difference);
216
36
        if (fabs(mpctx->total_avsync_change) > 0.05)
217
0
            saddf(&line, " ct:%7.3f", mpctx->total_avsync_change);
218
36
    }
219
220
36
    double position = get_current_pos_ratio(mpctx, true);
221
36
    char lavcbuf[80];
222
36
    if (encode_lavc_getstatus(mpctx->encode_lavc_ctx, lavcbuf, sizeof(lavcbuf),
223
36
            position) >= 0)
224
0
    {
225
        // encoding stats
226
0
        saddf(&line, " %s", lavcbuf);
227
36
    } else {
228
        // VO stats
229
36
        if (mpctx->vo_chain) {
230
36
            if (mpctx->display_sync_active) {
231
0
                char *r = mp_property_expand_string(mpctx,
232
0
                                            "${?vsync-ratio:${>vsync-ratio}}");
233
0
                if (r[0]) {
234
0
                    saddf(&line, " DS: %s/%"PRId64, r,
235
0
                          vo_get_delayed_count(mpctx->video_out));
236
0
                }
237
0
                talloc_free(r);
238
0
            }
239
36
            int64_t c = vo_get_drop_count(mpctx->video_out);
240
36
            struct mp_decoder_wrapper *dec = mpctx->vo_chain->track
241
36
                                        ? mpctx->vo_chain->track->dec : NULL;
242
36
            int dropped_frames =
243
36
                dec ? mp_decoder_wrapper_get_frames_dropped(dec) : 0;
244
36
            if (c > 0 || dropped_frames > 0) {
245
0
                saddf(&line, " Dropped: %"PRId64, c);
246
0
                if (dropped_frames)
247
0
                    saddf(&line, "/%d", dropped_frames);
248
0
            }
249
36
        }
250
36
    }
251
252
36
    if (mpctx->demuxer && demux_is_network_cached(mpctx->demuxer)) {
253
0
        saddf(&line, " Cache: ");
254
255
0
        struct demux_reader_state s;
256
0
        demux_get_reader_state(mpctx->demuxer, &s);
257
258
0
        if (s.ts_info.duration < 0) {
259
0
            saddf(&line, "???");
260
0
        } else if (s.ts_info.duration < 10) {
261
0
            saddf(&line, "%2.1fs", s.ts_info.duration);
262
0
        } else {
263
0
            saddf(&line, "%2ds", (int)s.ts_info.duration);
264
0
        }
265
0
        int64_t cache_size = s.fw_bytes;
266
0
        if (cache_size > 0) {
267
0
            if (cache_size >= 1024 * 1024) {
268
0
                saddf(&line, "/%lldMB", (long long)(cache_size / 1024 / 1024));
269
0
            } else {
270
0
                saddf(&line, "/%lldKB", (long long)(cache_size / 1024));
271
0
            }
272
0
        }
273
0
    }
274
275
36
    return line;
276
36
}
277
278
static void term_osd_print_status_lazy(struct MPContext *mpctx)
279
514k
{
280
514k
    struct MPOpts *opts = mpctx->opts;
281
282
514k
    term_osd_update_title(mpctx);
283
514k
    update_window_title(mpctx, false);
284
514k
    update_vo_playback_state(mpctx);
285
286
514k
    if (!opts->use_terminal)
287
511k
        return;
288
289
3.13k
    if (opts->quiet || !mpctx->playback_initialized || !mpctx->playing_msg_shown)
290
3.10k
    {
291
3.10k
        if (!mpctx->playing)
292
2.92k
            term_osd_set_status_lazy(mpctx, "");
293
3.10k
        return;
294
3.10k
    }
295
296
36
    char *line = get_term_status_msg(mpctx);
297
298
36
    if (opts->term_osd_bar) {
299
0
        saddf(&line, "\n");
300
0
        int w = 80, h = 24;
301
0
        terminal_get_size(&w, &h);
302
0
        add_term_osd_bar(mpctx, &line, w);
303
0
    }
304
305
36
    term_osd_set_status_lazy(mpctx, line);
306
36
    talloc_free(line);
307
36
}
308
309
PRINTF_ATTRIBUTE(4, 0)
310
static bool set_osd_msg_va(struct MPContext *mpctx, int level, int time,
311
                           const char *fmt, va_list ap)
312
0
{
313
0
    if (level > mpctx->opts->osd_level)
314
0
        return false;
315
316
0
    talloc_free(mpctx->osd_msg_text);
317
0
    mpctx->osd_msg_text = talloc_vasprintf(mpctx, fmt, ap);
318
0
    mpctx->osd_show_pos = false;
319
0
    mpctx->osd_msg_next_duration = time / 1000.0;
320
0
    mpctx->osd_force_update = true;
321
0
    mp_wakeup_core(mpctx);
322
0
    if (mpctx->osd_msg_next_duration <= 0)
323
0
        mpctx->osd_msg_visible = mp_time_sec();
324
0
    return true;
325
0
}
326
327
bool set_osd_msg(struct MPContext *mpctx, int level, int time,
328
                 const char *fmt, ...)
329
0
{
330
0
    va_list ap;
331
0
    va_start(ap, fmt);
332
0
    bool r = set_osd_msg_va(mpctx, level, time, fmt, ap);
333
0
    va_end(ap);
334
0
    return r;
335
0
}
336
337
// type: mp_osd_font_codepoints, ASCII, or OSD_BAR_*
338
void set_osd_bar(struct MPContext *mpctx, int type,
339
                 double min, double max, double neutral, double val)
340
0
{
341
0
    struct MPOpts *opts = mpctx->opts;
342
0
    bool video_osd = mpctx->video_out && mpctx->opts->video_osd;
343
0
    if (opts->osd_level < 1 || !opts->osd_bar_visible || !video_osd)
344
0
        return;
345
346
0
    mpctx->osd_visible = mp_time_sec() + opts->osd_duration / 1000.0;
347
0
    mpctx->osd_progbar.type = type;
348
0
    mpctx->osd_progbar.value = (val - min) / (max - min);
349
0
    mpctx->osd_progbar.num_stops = 0;
350
0
    if (neutral > min && neutral < max) {
351
0
        float pos = (neutral - min) / (max - min);
352
0
        MP_TARRAY_APPEND(mpctx, mpctx->osd_progbar.stops,
353
0
                         mpctx->osd_progbar.num_stops, pos);
354
0
    }
355
0
    osd_set_progbar(mpctx->osd, &mpctx->osd_progbar);
356
0
    mp_wakeup_core(mpctx);
357
0
}
358
359
// Update a currently displayed bar of the same type, without resetting the
360
// timer.
361
static void update_osd_bar(struct MPContext *mpctx, int type,
362
                           double min, double max, double val)
363
0
{
364
0
    if (mpctx->osd_progbar.type != type)
365
0
        return;
366
367
0
    float new_value = (val - min) / (max - min);
368
0
    if (new_value != mpctx->osd_progbar.value) {
369
0
        mpctx->osd_progbar.value = new_value;
370
0
        osd_set_progbar(mpctx->osd, &mpctx->osd_progbar);
371
0
    }
372
0
}
373
374
void set_osd_bar_chapters(struct MPContext *mpctx, int type)
375
469k
{
376
469k
    if (mpctx->osd_progbar.type != type)
377
469k
        return;
378
379
0
    mpctx->osd_progbar.num_stops = 0;
380
0
    double len = get_time_length(mpctx);
381
0
    if (len > 0) {
382
        // Always render the loop points, even if they're incomplete.
383
0
        double ab[2];
384
0
        bool valid = get_ab_loop_times(mpctx, ab);
385
0
        for (int n = 0; n < 2; n++) {
386
0
            if (ab[n] != MP_NOPTS_VALUE) {
387
0
                MP_TARRAY_APPEND(mpctx, mpctx->osd_progbar.stops,
388
0
                                 mpctx->osd_progbar.num_stops, ab[n] / len);
389
0
            }
390
0
        }
391
0
        if (!valid) {
392
0
            int num = get_chapter_count(mpctx);
393
0
            for (int n = 0; n < num; n++) {
394
0
                double time = chapter_start_time(mpctx, n);
395
0
                if (time >= 0) {
396
0
                    float pos = time / len;
397
0
                    MP_TARRAY_APPEND(mpctx, mpctx->osd_progbar.stops,
398
0
                                     mpctx->osd_progbar.num_stops, pos);
399
0
                }
400
0
            }
401
0
        }
402
0
    }
403
0
    osd_set_progbar(mpctx->osd, &mpctx->osd_progbar);
404
0
    mp_wakeup_core(mpctx);
405
0
}
406
407
// osd_function is the symbol appearing in the video status, such as OSD_PLAY
408
void set_osd_function(struct MPContext *mpctx, int osd_function)
409
0
{
410
0
    struct MPOpts *opts = mpctx->opts;
411
412
0
    mpctx->osd_function = osd_function;
413
0
    mpctx->osd_function_visible = mp_time_sec() + opts->osd_duration / 1000.0;
414
0
    mpctx->osd_force_update = true;
415
0
    mp_wakeup_core(mpctx);
416
0
}
417
418
void get_current_osd_sym(struct MPContext *mpctx, char *buf, size_t buf_size)
419
319
{
420
319
    int sym = mpctx->osd_function;
421
319
    if (!sym) {
422
319
        if (is_busy(mpctx) || (mpctx->paused_for_cache && !mpctx->opts->pause)) {
423
239
            sym = OSD_CLOCK;
424
239
        } else if (mpctx->paused || mpctx->step_frames) {
425
0
            sym = OSD_PAUSE;
426
80
        } else if (mpctx->play_dir < 0 ) {
427
0
            sym = OSD_REV;
428
80
        } else {
429
80
            sym = OSD_PLAY;
430
80
        }
431
319
    }
432
319
    osd_get_function_sym(buf, buf_size, sym);
433
319
}
434
435
static void sadd_osd_status(char **buffer, struct MPContext *mpctx, int level)
436
79.2k
{
437
79.2k
    mp_assert(level >= 0 && level <= 3);
438
79.2k
    if (level == 0)
439
2
        return;
440
79.2k
    char *msg = mpctx->opts->osd_msg[level - 1];
441
442
79.2k
    if (msg && msg[0]) {
443
7.20k
        char *text = mp_property_expand_escaped_string(mpctx, msg);
444
7.20k
        *buffer = talloc_strdup_append(*buffer, text);
445
7.20k
        talloc_free(text);
446
72.0k
    } else if (level >= 2) {
447
4
        bool fractions = mpctx->opts->osd_fractions;
448
4
        char sym[10];
449
4
        get_current_osd_sym(mpctx, sym, sizeof(sym));
450
4
        saddf(buffer, "%s ", sym);
451
4
        char *custom_msg = mpctx->opts->osd_status_msg;
452
4
        if (custom_msg && level == 3) {
453
0
            char *text = mp_property_expand_escaped_string(mpctx, custom_msg);
454
0
            *buffer = talloc_strdup_append(*buffer, text);
455
0
            talloc_free(text);
456
4
        } else {
457
4
            sadd_hhmmssff(buffer, get_playback_time(mpctx), fractions);
458
4
            if (level == 3) {
459
2
                saddf(buffer, " / ");
460
2
                sadd_hhmmssff(buffer, get_time_length(mpctx), fractions);
461
2
                sadd_percentage(buffer, get_current_pos_ratio(mpctx, false));
462
2
            }
463
4
        }
464
4
    }
465
79.2k
}
466
467
// OSD messages initiated by seeking commands are added lazily with this
468
// function, because multiple successive seek commands can be coalesced.
469
static void add_seek_osd_messages(struct MPContext *mpctx)
470
514k
{
471
514k
    if (mpctx->add_osd_seek_info & OSD_SEEK_INFO_BAR) {
472
0
        double pos = get_current_pos_ratio(mpctx, false);
473
0
        set_osd_bar(mpctx, OSD_BAR_SEEK, 0, 1, 0, MPCLAMP(pos, 0, 1));
474
0
        set_osd_bar_chapters(mpctx, OSD_BAR_SEEK);
475
0
    }
476
514k
    if (mpctx->add_osd_seek_info & OSD_SEEK_INFO_TEXT) {
477
        // Never in term-osd mode
478
0
        bool video_osd = mpctx->video_out && mpctx->opts->video_osd;
479
0
        if (video_osd && mpctx->opts->term_osd != 1) {
480
0
            if (set_osd_msg(mpctx, 1, mpctx->opts->osd_duration, ""))
481
0
                mpctx->osd_show_pos = true;
482
0
        }
483
0
    }
484
514k
    if (mpctx->add_osd_seek_info & OSD_SEEK_INFO_CHAPTER_TEXT) {
485
0
        char *chapter = chapter_display_name(mpctx, get_current_chapter(mpctx));
486
0
        set_osd_msg(mpctx, 1, mpctx->opts->osd_duration,
487
0
                     "Chapter: %s", chapter);
488
0
        talloc_free(chapter);
489
0
    }
490
514k
    if (mpctx->add_osd_seek_info & OSD_SEEK_INFO_CURRENT_FILE) {
491
0
        if (mpctx->filename) {
492
0
            set_osd_msg(mpctx, 1, mpctx->opts->osd_duration, "%s",
493
0
                        mpctx->filename);
494
0
        }
495
0
    }
496
514k
    mpctx->add_osd_seek_info = 0;
497
514k
}
498
499
// Update the OSD text (both on VO and terminal status line).
500
void update_osd_msg(struct MPContext *mpctx)
501
2.11M
{
502
2.11M
    struct MPOpts *opts = mpctx->opts;
503
2.11M
    struct osd_state *osd = mpctx->osd;
504
505
2.11M
    double now = mp_time_sec();
506
507
2.11M
    if (!mpctx->osd_force_update) {
508
        // Assume nothing is going on at all.
509
1.75M
        if (!mpctx->osd_idle_update)
510
146k
            return;
511
512
1.61M
        double delay = 0.050; // update the OSD at most this often
513
1.61M
        double diff = now - mpctx->osd_last_update;
514
1.61M
        if (diff < delay) {
515
1.45M
            mp_set_timeout(mpctx, delay - diff);
516
1.45M
            return;
517
1.45M
        }
518
1.61M
    }
519
514k
    mpctx->osd_force_update = false;
520
514k
    mpctx->osd_idle_update = false;
521
514k
    mpctx->osd_last_update = now;
522
523
514k
    if (mpctx->osd_visible) {
524
0
        double sleep = mpctx->osd_visible - now;
525
0
        if (sleep > 0) {
526
0
            mp_set_timeout(mpctx, sleep);
527
0
            mpctx->osd_idle_update = true;
528
0
        } else {
529
0
            mpctx->osd_visible = 0;
530
0
            mpctx->osd_progbar.type = -1; // disable
531
0
            osd_set_progbar(mpctx->osd, &mpctx->osd_progbar);
532
0
        }
533
0
    }
534
535
514k
    if (mpctx->osd_function_visible) {
536
0
        double sleep = mpctx->osd_function_visible - now;
537
0
        if (sleep > 0) {
538
0
            mp_set_timeout(mpctx, sleep);
539
0
            mpctx->osd_idle_update = true;
540
0
        } else {
541
0
            mpctx->osd_function_visible = 0;
542
0
            mpctx->osd_function = 0;
543
0
        }
544
0
    }
545
546
514k
    if (mpctx->osd_msg_next_duration > 0) {
547
        // This is done to avoid cutting the OSD message short if slow commands
548
        // are executed between setting the OSD message and showing it.
549
0
        mpctx->osd_msg_visible = now + mpctx->osd_msg_next_duration;
550
0
        mpctx->osd_msg_next_duration = 0;
551
0
    }
552
553
514k
    if (mpctx->osd_msg_visible) {
554
0
        double sleep = mpctx->osd_msg_visible - now;
555
0
        if (sleep > 0) {
556
0
            mp_set_timeout(mpctx, sleep);
557
0
            mpctx->osd_idle_update = true;
558
0
        } else {
559
0
            talloc_free(mpctx->osd_msg_text);
560
0
            mpctx->osd_msg_text = NULL;
561
0
            mpctx->osd_msg_visible = 0;
562
0
            mpctx->osd_show_pos = false;
563
0
        }
564
0
    }
565
566
514k
    add_seek_osd_messages(mpctx);
567
568
514k
    if (mpctx->osd_progbar.type == OSD_BAR_SEEK) {
569
0
        double pos = get_current_pos_ratio(mpctx, false);
570
0
        update_osd_bar(mpctx, OSD_BAR_SEEK, 0, 1, MPCLAMP(pos, 0, 1));
571
0
    }
572
573
514k
    term_osd_set_text_lazy(mpctx, mpctx->osd_msg_text);
574
514k
    term_osd_print_status_lazy(mpctx);
575
514k
    term_osd_update(mpctx);
576
577
514k
    if (!opts->video_osd)
578
435k
        return;
579
580
79.2k
    int osd_level = opts->osd_level;
581
79.2k
    if (mpctx->osd_show_pos)
582
0
        osd_level = 3;
583
584
79.2k
    char *text = NULL;
585
79.2k
    sadd_osd_status(&text, mpctx, osd_level);
586
79.2k
    if (mpctx->osd_msg_text && mpctx->osd_msg_text[0]) {
587
0
        text = talloc_asprintf_append(text, "%s%s", text ? "\n" : "",
588
0
                                      mpctx->osd_msg_text);
589
0
    }
590
79.2k
    osd_set_text(osd, text);
591
79.2k
    talloc_free(text);
592
79.2k
}