You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
gnome-shell/SOURCES/0001-screencast-Stop-record...

254 lines
9.4 KiB

From 67a4506d4d8a0cbbaca5df4adfc309e54e557aee Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jonas=20=C3=85dahl?= <jadahl@gmail.com>
Date: Tue, 5 Jan 2021 12:04:23 +0100
Subject: [PATCH] screencast: Stop recording when screen size or resource scale
change
Video encoders don't really handle changing the size of the video, and if
we'd e.g. change resolution while recording, we would end up with a corrupt
video file. Handle this more gracefully by stopping the recording if the
conditions change.
---
js/ui/screencast.js | 92 +++++++++++++++++++++++++++++++++++++++++---
src/shell-recorder.c | 50 ++++++++++++------------
src/shell-recorder.h | 1 +
3 files changed, 114 insertions(+), 29 deletions(-)
diff --git a/js/ui/screencast.js b/js/ui/screencast.js
index 0b0b14a8e..54f8fb5ae 100644
--- a/js/ui/screencast.js
+++ b/js/ui/screencast.js
@@ -1,6 +1,6 @@
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
-const { Gio, GLib, Shell } = imports.gi;
+const { Gio, GLib, Meta, Shell } = imports.gi;
const Signals = imports.signals;
const Main = imports.ui.main;
@@ -53,16 +53,27 @@ var ScreencastService = class {
this._stopRecordingForSender(name);
}
- _stopRecordingForSender(sender) {
+ _stopRecordingForSender(sender, closeNow=false) {
let recorder = this._recorders.get(sender);
if (!recorder)
return false;
Gio.bus_unwatch_name(recorder._watchNameId);
- recorder.close();
+ if (closeNow)
+ recorder.close_now();
+ else
+ recorder.close();
this._recorders.delete(sender);
this.emit('updated');
+ let connection = this._dbusImpl.get_connection();
+ let info = this._dbusImpl.get_info();
+ connection.emit_signal(sender,
+ this._dbusImpl.get_object_path(),
+ info ? info.name : null,
+ 'Stopped',
+ null);
+
return true;
}
@@ -78,6 +89,53 @@ var ScreencastService = class {
recorder.set_draw_cursor(options['draw-cursor']);
}
+ _ensureResourceScaleChangedHandler() {
+ if (this._resourceScaleChangedHandlerId)
+ return;
+
+ this._resourceScaleChangedHandlerId =
+ global.stage.connect('notify::resource-scale',
+ () => {
+ for (let sender of this._recorders.keys()) {
+ let recorder = this._recorders.get(sender);
+
+ if (!recorder.is_recording())
+ continue;
+
+ this._stopRecordingForSender(sender, true);
+ }
+ });
+ }
+
+ _ensureMonitorsChangedHandler() {
+ if (this._monitorsChangedHandlerId)
+ return;
+
+ this._monitorsChangedHandlerId = Main.layoutManager.connect('monitors-changed',
+ () => {
+ for (let sender of this._recorders.keys()) {
+ let recorder = this._recorders.get(sender);
+
+ if (!recorder.is_recording())
+ continue;
+
+ let geometry = recorder._geometry;
+ let screenWidth = global.screen_width;
+ let screenHeight = global.screen_height;
+
+ if (recorder._isAreaScreecast) {
+ if (geometry.x + geometry.width > screenWidth ||
+ geometry.y + geometry.height > screenHeight)
+ this._stopRecordingForSender(sender, true);
+ } else {
+ if (geometry.width != screenWidth ||
+ geometry.height != screenHeight)
+ this._stopRecordingForSender(sender, true);
+ }
+ }
+ });
+ }
+
ScreencastAsync(params, invocation) {
let returnValue = [false, ''];
if (!Main.sessionMode.allowScreencast ||
@@ -95,8 +153,20 @@ var ScreencastService = class {
this._applyOptionalParameters(recorder, options);
let [success, fileName] = recorder.record();
returnValue = [success, fileName ? fileName : ''];
- if (!success)
+ if (success) {
+ recorder._isAreaScreecast = false;
+ recorder._geometry =
+ new Meta.Rectangle({
+ x: 0,
+ y: 0,
+ width: global.screen_width,
+ height: global.screen_height
+ });
+ this._ensureResourceScaleChangedHandler();
+ this._ensureMonitorsChangedHandler();
+ } else {
this._stopRecordingForSender(sender);
+ }
}
invocation.return_value(GLib.Variant.new('(bs)', returnValue));
@@ -131,8 +201,20 @@ var ScreencastService = class {
this._applyOptionalParameters(recorder, options);
let [success, fileName] = recorder.record();
returnValue = [success, fileName ? fileName : ''];
- if (!success)
+ if (success) {
+ recorder._isAreaScreecast = true;
+ recorder._geometry =
+ new Meta.Rectangle({
+ x: x,
+ y: y,
+ width: width,
+ height: height
+ });
+ this._ensureResourceScaleChangedHandler();
+ this._ensureMonitorsChangedHandler();
+ } else {
this._stopRecordingForSender(sender);
+ }
}
invocation.return_value(GLib.Variant.new('(bs)', returnValue));
diff --git a/src/shell-recorder.c b/src/shell-recorder.c
index 0203ecf1c..e561a0152 100644
--- a/src/shell-recorder.c
+++ b/src/shell-recorder.c
@@ -511,21 +511,6 @@ recorder_update_size (ShellRecorder *recorder)
}
}
-static void
-recorder_on_stage_notify_size (GObject *object,
- GParamSpec *pspec,
- ShellRecorder *recorder)
-{
- recorder_update_size (recorder);
-
- /* This breaks the recording but tweaking the GStreamer pipeline a bit
- * might make it work, at least if the codec can handle a stream where
- * the frame size changes in the middle.
- */
- if (recorder->current_pipeline)
- recorder_pipeline_set_caps (recorder->current_pipeline);
-}
-
static gboolean
recorder_idle_redraw (gpointer data)
{
@@ -622,12 +607,6 @@ recorder_connect_stage_callbacks (ShellRecorder *recorder)
G_CALLBACK (recorder_on_stage_destroy), recorder);
g_signal_connect_after (recorder->stage, "paint",
G_CALLBACK (recorder_on_stage_paint), recorder);
- g_signal_connect (recorder->stage, "notify::width",
- G_CALLBACK (recorder_on_stage_notify_size), recorder);
- g_signal_connect (recorder->stage, "notify::height",
- G_CALLBACK (recorder_on_stage_notify_size), recorder);
- g_signal_connect (recorder->stage, "notify::resource-scale",
- G_CALLBACK (recorder_on_stage_notify_size), recorder);
}
static void
@@ -639,9 +618,6 @@ recorder_disconnect_stage_callbacks (ShellRecorder *recorder)
g_signal_handlers_disconnect_by_func (recorder->stage,
(void *)recorder_on_stage_paint,
recorder);
- g_signal_handlers_disconnect_by_func (recorder->stage,
- (void *)recorder_on_stage_notify_size,
- recorder);
/* We don't don't deselect for cursor changes in case someone else just
* happened to be selecting for cursor events on the same window; sending
@@ -1578,6 +1554,32 @@ shell_recorder_record (ShellRecorder *recorder,
return TRUE;
}
+/**
+ * shell_recorder_close_now:
+ * @recorder: the #ShellRecorder
+ *
+ * Stops recording immediately. It's possible to call shell_recorder_record()
+ * again to reopen a new recording stream, but unless change the recording
+ * filename, this may result in the old recording being overwritten.
+ */
+void
+shell_recorder_close_now (ShellRecorder *recorder)
+{
+ g_return_if_fail (SHELL_IS_RECORDER (recorder));
+ g_return_if_fail (recorder->state != RECORDER_STATE_CLOSED);
+
+ recorder_remove_update_pointer_timeout (recorder);
+ recorder_close_pipeline (recorder);
+
+ recorder->state = RECORDER_STATE_CLOSED;
+
+ /* Reenable after the recording */
+ meta_enable_unredirect_for_display (shell_global_get_display (shell_global_get ()));
+
+ /* Release the refcount we took when we started recording */
+ g_object_unref (recorder);
+}
+
/**
* shell_recorder_close:
* @recorder: the #ShellRecorder
diff --git a/src/shell-recorder.h b/src/shell-recorder.h
index c1e0e6368..1c3e6aab4 100644
--- a/src/shell-recorder.h
+++ b/src/shell-recorder.h
@@ -37,6 +37,7 @@ void shell_recorder_set_area (ShellRecorder *recorder,
gboolean shell_recorder_record (ShellRecorder *recorder,
char **filename_used);
void shell_recorder_close (ShellRecorder *recorder);
+void shell_recorder_close_now (ShellRecorder *recorder);
void shell_recorder_pause (ShellRecorder *recorder);
gboolean shell_recorder_is_recording (ShellRecorder *recorder);
--
2.27.0