Blame SOURCES/0001-screencast-Stop-recording-when-screen-size-or-resour.patch

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