254 lines
9.4 KiB
Diff
254 lines
9.4 KiB
Diff
|
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
|
||
|
|