screen-cast/src: Record follow up frame after timeout

During animation or other things that cause multiple frames in a row
being painted, we might skip recording frames if the max framerate is
reached.

Doing so means we might end up skipping the last frame in a series,
ending with the last frame we sent was not the last one, making things
appear to get stuck sometimes.

Handle this by creating a timeout if we ever throttle, and at the time
the timeout callback is triggered, make sure we eventually send an up to
date frame.

This is handle differently depending on the source type. A monitor
source type reports 1x1 pixel damage on each view its monitor overlaps,
while a window source type simply records a frame from the surface
directly, except without recording a timestamp, so that timestamps
always refer to when damage actually happened.

https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1361
This commit is contained in:
Jonas Ådahl
2020-07-03 23:57:31 +02:00
parent 7adc24d3a6
commit 9bab8e8751
5 changed files with 139 additions and 5 deletions

View File

@ -92,6 +92,7 @@ typedef struct _MetaScreenCastStreamSrcPrivate
int video_stride;
int64_t last_frame_timestamp_us;
guint follow_up_frame_source_id;
GHashTable *dmabuf_handles;
@ -156,6 +157,15 @@ meta_screen_cast_stream_src_record_to_framebuffer (MetaScreenCastStreamSrc *src
return klass->record_to_framebuffer (src, framebuffer, error);
}
static void
meta_screen_cast_stream_src_record_follow_up (MetaScreenCastStreamSrc *src)
{
MetaScreenCastStreamSrcClass *klass =
META_SCREEN_CAST_STREAM_SRC_GET_CLASS (src);
klass->record_follow_up (src);
}
static void
meta_screen_cast_stream_src_set_cursor_metadata (MetaScreenCastStreamSrc *src,
struct spa_meta_cursor *spa_meta_cursor)
@ -442,6 +452,43 @@ do_record_frame (MetaScreenCastStreamSrc *src,
return FALSE;
}
gboolean
meta_screen_cast_stream_src_pending_follow_up_frame (MetaScreenCastStreamSrc *src)
{
MetaScreenCastStreamSrcPrivate *priv =
meta_screen_cast_stream_src_get_instance_private (src);
return priv->follow_up_frame_source_id != 0;
}
static gboolean
follow_up_frame_cb (gpointer user_data)
{
MetaScreenCastStreamSrc *src = user_data;
MetaScreenCastStreamSrcPrivate *priv =
meta_screen_cast_stream_src_get_instance_private (src);
priv->follow_up_frame_source_id = 0;
meta_screen_cast_stream_src_record_follow_up (src);
return G_SOURCE_REMOVE;
}
static void
maybe_schedule_follow_up_frame (MetaScreenCastStreamSrc *src,
int64_t timeout_us)
{
MetaScreenCastStreamSrcPrivate *priv =
meta_screen_cast_stream_src_get_instance_private (src);
if (priv->follow_up_frame_source_id)
return;
priv->follow_up_frame_source_id = g_timeout_add (us2ms (timeout_us),
follow_up_frame_cb,
src);
}
void
meta_screen_cast_stream_src_maybe_record_frame (MetaScreenCastStreamSrc *src,
MetaScreenCastRecordFlag flags)
@ -457,11 +504,24 @@ meta_screen_cast_stream_src_maybe_record_frame (MetaScreenCastStreamSrc *src,
now_us = g_get_monotonic_time ();
if (priv->video_format.max_framerate.num > 0 &&
priv->last_frame_timestamp_us != 0 &&
(now_us - priv->last_frame_timestamp_us <
((1000000 * priv->video_format.max_framerate.denom) /
priv->video_format.max_framerate.num)))
return;
priv->last_frame_timestamp_us != 0)
{
int64_t min_interval_us;
int64_t time_since_last_frame_us;
min_interval_us = ((1000000 * priv->video_format.max_framerate.denom) /
priv->video_format.max_framerate.num);
time_since_last_frame_us = now_us - priv->last_frame_timestamp_us;
if (time_since_last_frame_us < min_interval_us)
{
int64_t timeout_us;
timeout_us = min_interval_us - time_since_last_frame_us;
maybe_schedule_follow_up_frame (src, timeout_us);
return;
}
}
if (!priv->pipewire_stream)
return;
@ -481,6 +541,7 @@ meta_screen_cast_stream_src_maybe_record_frame (MetaScreenCastStreamSrc *src,
if (!(flags & META_SCREEN_CAST_RECORD_FLAG_CURSOR_ONLY))
{
g_clear_handle_id (&priv->follow_up_frame_source_id, g_source_remove);
if (do_record_frame (src, spa_buffer, data, &error))
{
struct spa_meta_region *spa_meta_video_crop;