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