From ad37b447b7af8774be64162108f482f140844cf8 Mon Sep 17 00:00:00 2001 From: Naveen Kumar Date: Wed, 20 May 2026 07:21:20 +0000 Subject: [PATCH] wayland: send content frame rate surface hints --- player/misc.c | 27 ++++++++++++++++++++ video/out/meson.build | 9 +++++++ video/out/vo.h | 6 +++++ video/out/wayland_common.c | 51 ++++++++++++++++++++++++++++++++++++++ video/out/wayland_common.h | 6 +++++ 5 files changed, 99 insertions(+) diff --git a/player/misc.c b/player/misc.c index e6e3fc2e89f24..a8ef770990f95 100644 --- a/player/misc.c +++ b/player/misc.c @@ -17,10 +17,13 @@ #include #include +#include #include #include #include +#include + #include "mpv_talloc.h" #include "osdep/io.h" @@ -174,6 +177,28 @@ void issue_refresh_seek(struct MPContext *mpctx, enum seek_precision min_prec) queue_seek(mpctx, MPSEEK_ABSOLUTE, get_current_time(mpctx), min_prec, 0); } +static void update_content_frame_rate(struct MPContext *mpctx, struct track *track) +{ + struct voctrl_content_frame_rate frame_rate = { + .numerator = 0, + .denominator = 1, + }; + + if (track && track->vo_c && !track->image) { + double fps = track->vo_c->filter->container_fps; + if (fps > 0) { + AVRational rate = av_d2q(fps, INT_MAX); + if (rate.num > 0 && rate.den > 0) { + frame_rate.numerator = rate.num; + frame_rate.denominator = rate.den; + } + } + } + + if (mpctx->video_out) + vo_control(mpctx->video_out, VOCTRL_CONTENT_FRAME_RATE, &frame_rate); +} + void update_content_type(struct MPContext *mpctx, struct track *track) { enum mp_content_type content_type; @@ -186,6 +211,8 @@ void update_content_type(struct MPContext *mpctx, struct track *track) } if (mpctx->video_out) vo_control(mpctx->video_out, VOCTRL_CONTENT_TYPE, &content_type); + + update_content_frame_rate(mpctx, track); } void update_vo_playback_state(struct MPContext *mpctx) diff --git a/video/out/meson.build b/video/out/meson.build index 722627c8256c7..3e60d45beff22 100644 --- a/video/out/meson.build +++ b/video/out/meson.build @@ -1,3 +1,5 @@ +fs = import('fs') + wl_protocol_dir = wayland['deps'][2].get_variable(pkgconfig: 'pkgdatadir', internal: 'pkgdatadir') protocols = [[wl_protocol_dir, 'stable/linux-dmabuf/linux-dmabuf-v1.xml'], [wl_protocol_dir, 'stable/presentation-time/presentation-time.xml'], @@ -17,6 +19,13 @@ protocols = [[wl_protocol_dir, 'stable/linux-dmabuf/linux-dmabuf-v1.xml'], wl_protocols_source = [] wl_protocols_headers = [] +features += {'wayland-protocols-content-frame-rate': fs.is_file(join_paths( + wl_protocol_dir, 'staging/content-frame-rate/content-frame-rate-v1.xml'))} + +if features['wayland-protocols-content-frame-rate'] + protocols += [[wl_protocol_dir, 'staging/content-frame-rate/content-frame-rate-v1.xml']] +endif + foreach v: ['1.39', '1.41', '1.44', '1.47', '1.48'] features += {'wayland-protocols-' + v.replace('.', '-'): wayland['deps'][2].version().version_compare('>=' + v)} diff --git a/video/out/vo.h b/video/out/vo.h index a98b0f9496853..65072134387ed 100644 --- a/video/out/vo.h +++ b/video/out/vo.h @@ -88,6 +88,7 @@ enum mp_voctrl { VOCTRL_SET_CURSOR_VISIBILITY, // bool* VOCTRL_CONTENT_TYPE, // enum mp_content_type* + VOCTRL_CONTENT_FRAME_RATE, // struct voctrl_content_frame_rate* VOCTRL_KILL_SCREENSAVER, VOCTRL_RESTORE_SCREENSAVER, @@ -142,6 +143,11 @@ enum mp_content_type { MP_CONTENT_VIDEO, }; +struct voctrl_content_frame_rate { + uint32_t numerator; + uint32_t denominator; +}; + #define VO_TRUE true #define VO_FALSE false #define VO_ERROR -1 diff --git a/video/out/wayland_common.c b/video/out/wayland_common.c index f60f9dad1831a..8f1c891164e69 100644 --- a/video/out/wayland_common.c +++ b/video/out/wayland_common.c @@ -51,6 +51,9 @@ #include "xdg-shell.h" #include "viewporter.h" #include "content-type-v1.h" +#if HAVE_WAYLAND_PROTOCOLS_CONTENT_FRAME_RATE +#include "content-frame-rate-v1.h" +#endif #include "single-pixel-buffer-v1.h" #include "fractional-scale-v1.h" #include "tablet-unstable-v2.h" @@ -2857,6 +2860,14 @@ static void registry_handle_add(void *data, struct wl_registry *reg, uint32_t id wl->content_type_manager = wl_registry_bind(reg, id, &wp_content_type_manager_v1_interface, ver); } +#if HAVE_WAYLAND_PROTOCOLS_CONTENT_FRAME_RATE + if (!strcmp(interface, wp_content_frame_rate_manager_v1_interface.name) && found++) { + ver = 1; + wl->content_frame_rate_manager = wl_registry_bind( + reg, id, &wp_content_frame_rate_manager_v1_interface, ver); + } +#endif + if (!strcmp(interface, wp_single_pixel_buffer_manager_v1_interface.name) && found++) { ver = 1; wl->single_pixel_manager = wl_registry_bind(reg, id, &wp_single_pixel_buffer_manager_v1_interface, ver); @@ -3807,6 +3818,20 @@ static void set_content_type(struct vo_wayland_state *wl) } } +static void set_content_frame_rate(struct vo_wayland_state *wl) +{ +#if HAVE_WAYLAND_PROTOCOLS_CONTENT_FRAME_RATE + if (!wl->content_frame_rate_manager || !wl->content_frame_rate) + return; + + uint32_t den = wl->current_content_frame_rate_den ? + wl->current_content_frame_rate_den : 1; + wp_content_frame_rate_v1_set_frame_rate( + wl->content_frame_rate, + wl->current_content_frame_rate_num, + den); +#endif +} static void set_cursor(struct vo_wayland_seat *s, struct wl_surface *cursor_surface, int32_t hotspot_x, int32_t hotspot_y) { @@ -4277,6 +4302,13 @@ int vo_wayland_control(struct vo *vo, int *events, int request, void *arg) set_content_type(wl); return VO_TRUE; } + case VOCTRL_CONTENT_FRAME_RATE: { + struct voctrl_content_frame_rate *content_frame_rate = arg; + wl->current_content_frame_rate_num = content_frame_rate->numerator; + wl->current_content_frame_rate_den = content_frame_rate->denominator; + set_content_frame_rate(wl); + return VO_TRUE; + } case VOCTRL_GET_FOCUSED: { *(bool *)arg = wl->focused; return VO_TRUE; @@ -4590,6 +4622,17 @@ bool vo_wayland_init(struct vo *vo) wp_content_type_manager_v1_interface.name); } +#if HAVE_WAYLAND_PROTOCOLS_CONTENT_FRAME_RATE + if (wl->content_frame_rate_manager) { + wl->content_frame_rate = + wp_content_frame_rate_manager_v1_get_surface_content_frame_rate( + wl->content_frame_rate_manager, wl->surface); + } else { + MP_VERBOSE(wl, "Compositor doesn't support the %s protocol!\n", + wp_content_frame_rate_manager_v1_interface.name); + } +#endif + if (!wl->single_pixel_manager) { MP_VERBOSE(wl, "Compositor doesn't support the %s protocol!\n", wp_single_pixel_buffer_manager_v1_interface.name); @@ -4811,6 +4854,14 @@ void vo_wayland_uninit(struct vo *vo) if (wl->content_type_manager) wp_content_type_manager_v1_destroy(wl->content_type_manager); +#if HAVE_WAYLAND_PROTOCOLS_CONTENT_FRAME_RATE + if (wl->content_frame_rate) + wp_content_frame_rate_v1_destroy(wl->content_frame_rate); + + if (wl->content_frame_rate_manager) + wp_content_frame_rate_manager_v1_destroy(wl->content_frame_rate_manager); +#endif + if (wl->devman) wl_data_device_manager_destroy(wl->devman); diff --git a/video/out/wayland_common.h b/video/out/wayland_common.h index f3f40fb013aaf..3cee24006cc07 100644 --- a/video/out/wayland_common.h +++ b/video/out/wayland_common.h @@ -125,6 +125,12 @@ struct vo_wayland_state { struct wp_content_type_v1 *content_type; int current_content_type; + /* content-frame-rate */ + struct wp_content_frame_rate_manager_v1 *content_frame_rate_manager; + struct wp_content_frame_rate_v1 *content_frame_rate; + uint32_t current_content_frame_rate_num; + uint32_t current_content_frame_rate_den; + /* cursor-shape */ struct wp_cursor_shape_manager_v1 *cursor_shape_manager;