From 60d8683ae750905929f974aa38a83725de02532a Mon Sep 17 00:00:00 2001 From: "Owen W. Taylor" Date: Mon, 30 Jan 2012 16:44:32 -0500 Subject: [PATCH] ShellRecorder: drop frames to approximate the target framerate Instead of adding every rendered frame into the recording, drop frames and only buffer and record enough frames to match the target framerate. Increase the default frame rate from 15 to 30, since now that we're actually enforcing framerate, it's noticeable that 15fps is not smooth. https://bugzilla.gnome.org/show_bug.cgi?id=669066 --- data/org.gnome.shell.gschema.xml.in | 2 +- src/shell-recorder.c | 40 +++++++++++++++++++++-------- 2 files changed, 30 insertions(+), 12 deletions(-) diff --git a/data/org.gnome.shell.gschema.xml.in b/data/org.gnome.shell.gschema.xml.in index 325f4089a..00bba5d05 100644 --- a/data/org.gnome.shell.gschema.xml.in +++ b/data/org.gnome.shell.gschema.xml.in @@ -108,7 +108,7 @@ - 15 + 30 <_summary>Framerate used for recording screencasts. <_description> The framerate of the resulting screencast recordered diff --git a/src/shell-recorder.c b/src/shell-recorder.c index 0575856db..2e1f3aeea 100644 --- a/src/shell-recorder.c +++ b/src/shell-recorder.c @@ -77,6 +77,7 @@ struct _ShellRecorder { GSList *pipelines; /* all pipelines */ GstClockTime start_time; /* When we started recording (adjusted for pauses) */ + GstClockTime last_frame_time; /* Timestamp for the last frame */ GstClockTime pause_time; /* When the pipeline was paused */ /* GSource IDs for different timeouts and idles */ @@ -117,14 +118,12 @@ enum { G_DEFINE_TYPE(ShellRecorder, shell_recorder, G_TYPE_OBJECT); -/* The number of frames per second we configure for the GStreamer pipeline. - * (the number of frames we actually write into the GStreamer pipeline is - * based entirely on how fast clutter is drawing.) Using 60fps seems high - * but the observed smoothness is a lot better than for 30fps when encoding - * as theora for a minimal size increase. This may be an artifact of the - * encoding process. +/* The default value of the target frame rate; we'll never record more + * than this many frames per second, though we may record less if the + * screen isn't being redrawn. 30 is a compromise between smoothness + * and the size of the recording. */ -#define DEFAULT_FRAMES_PER_SECOND 15 +#define DEFAULT_FRAMES_PER_SECOND 30 /* The time (in milliseconds) between querying the server for the cursor * position. @@ -525,6 +524,7 @@ recorder_record_frame (ShellRecorder *recorder) GstBuffer *buffer; guint8 *data; guint size; + GstClockTime now; /* If we get into the red zone, stop buffering new frames; 13/16 is * a bit more than the 3/4 threshold for a red indicator to keep the @@ -532,6 +532,17 @@ recorder_record_frame (ShellRecorder *recorder) if (recorder->memory_used > (recorder->memory_target * 13) / 16) return; + /* Drop frames to get down to something like the target frame rate; since frames + * are generated with VBlank sync, we don't have full control anyways, so we just + * drop frames if the interval since the last frame is less than 75% of the + * desired inter-frame interval. + */ + now = get_wall_time(); + if (now - recorder->last_frame_time < (3 * 1000000000LL / (4 * recorder->framerate))) + return; + + recorder->last_frame_time = now; + size = recorder->stage_width * recorder->stage_height * 4; data = shell_screen_grabber_grab (recorder->grabber, @@ -541,8 +552,7 @@ recorder_record_frame (ShellRecorder *recorder) GST_BUFFER_SIZE(buffer) = size; GST_BUFFER_MALLOCDATA(buffer) = GST_BUFFER_DATA(buffer) = data; - GST_BUFFER_TIMESTAMP(buffer) = get_wall_time() - recorder->start_time; - + GST_BUFFER_TIMESTAMP(buffer) = now - recorder->start_time; recorder_draw_cursor (recorder, buffer); @@ -1568,9 +1578,15 @@ shell_recorder_new (ClutterStage *stage) * @recorder: the #ShellRecorder * @framerate: Framerate used for resulting video in frames-per-second. * - * Sets the number of frames per second we configure for the GStreamer pipeline. + * Sets the number of frames per second we try to record. Less frames + * will be recorded when the screen doesn't need to be redrawn this + * quickly. (This value will also be set as the framerate for the + * GStreamer pipeline; whether that has an effect on the resulting + * video will depend on the details of the pipeline and the codec. The + * default encoding to webm format doesn't pay attention to the pipeline + * framerate.) * - * The default value is 15. + * The default value is 30. */ void shell_recorder_set_framerate (ShellRecorder *recorder, @@ -1672,6 +1688,7 @@ shell_recorder_record (ShellRecorder *recorder) * pause */ recorder->start_time = recorder->start_time + (get_wall_time() - recorder->pause_time); + recorder->last_frame_time = 0; } else { @@ -1679,6 +1696,7 @@ shell_recorder_record (ShellRecorder *recorder) return FALSE; recorder->start_time = get_wall_time(); + recorder->last_frame_time = 0; } recorder->state = RECORDER_STATE_RECORDING;