Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow sharing of a region of a monitor #154

Open
Hubro opened this issue Jul 1, 2021 · 30 comments
Open

Allow sharing of a region of a monitor #154

Hubro opened this issue Jul 1, 2021 · 30 comments

Comments

@Hubro
Copy link

Hubro commented Jul 1, 2021

I realize #107 is still far away, but what about a workaround that will get us most of the way there with very little work?

2 out of my 3 monitors are rather huge, way too large to realistically screenshare the entire monitor in a teams meeting. (One of them is 5120x1440.)

Would it be possible to add a config option to specify a region of a monitor to screenshare?

For example:

output_name = DP1
output_region = 100,100 1920x1080

This would share a 1920x1080 region of my monitor starting at 100px from the left, 100px from the top. After setting this up, I could make a script using swaymsg to place the windows precisely within this rectangle.

This would avoid all the complexity of window selection, but still make it possible for people with large monitors (ultrawides, 40+ inch 4K monitors etc.) to screenshare. I think this would be a nice workaround until #107 becomes a possibility.


If this is already possible somehow, my bad. I have looked around but I'm not able to find anything.

@emersion
Copy link
Owner

emersion commented Jul 1, 2021

IIRC Mutter uses Pipewire metadata to do the cropping. @columbarius, is this correct?

To reduce the amount of pixel copies, the best would be to ask wlroots to do the cropping directly. The screencopy protocol already supports this.

I wonder how we can plumb all of this to the chooser. Hardcoding the region in the config file is fine for simple use-cases, but ideally we should allow users to directly select the region with slurp. That would also allow users to feed custom selection boxes to slurp if they want, and easily snap the cropped region to window boundaries via swaymsg. For instance with something like:

chooser_cmd = swaymsg -t get_tree | jq -r '.. | select(.pid? and .visible?) | .rect | "\(.x),\(.y) \(.width)x\(.height)"' | slurp

@Hubro
Copy link
Author

Hubro commented Jul 1, 2021

I assume this would still let me hard code a region if I really wanted to?

chooser_cmd = echo "100,100 1920x1080"

In any case, anything that would let me do screen sharing from my desktop PC without changing the resolution of my monitor I would greatly appreciate 😁

@emersion
Copy link
Owner

emersion commented Jul 1, 2021

Note, I'm not necessarily against an output_region config option. We already have output_name after all. I think it would be a good first step.

I'd also like to think how to allow users to interactively select the region, because that's what most people will want.

@columbarius
Copy link
Collaborator

IIRC Mutter uses Pipewire metadata to do the cropping. @columbarius, is this correct?

Yes. Using the crop metadata would make it really easy to add this feature in the screencast part.

I see the main issue in changing our chooser output format. We could either switch to a structured format like json, or extend our current format with some csv style formatting like: displayname;offset_x; offset_y;width_x;height_y

Note, I'm not necessarily against an output_region config option. We already have output_name after all. I think it would be a good first step.

I would see that option as an addition to the output_name. If we use output_name and output_region is specified use that too.

I guess slurp can return to relative coordinates of the rectangle to the screen instead of the global ones?

@emersion
Copy link
Owner

emersion commented Jul 1, 2021

I see the main issue in changing our chooser output format. We could either switch to a structured format like json, or extend our current format with some csv style formatting like: displayname;offset_x; offset_y;width_x;height_y

Right. If we pick a format that can begin with the output name and a separator, we can make it backwards-compatible.

Note, from a protocol PoV, the output name can contain any ASCII character. I'm a little worried about ;, which may very well be used by some compositors to generate a unique name in the future. Something like a space would be less likely to be used, something like a newline would be even less likely. Maybe a multi-line format would be more future-proof? e.g.

DP-1
region = 100,100 1920x1080

I guess slurp can return to relative coordinates of the rectangle to the screen instead of the global ones?

Not yet, but it would be easy to add e.g. %{ox} and %{oy} for this purpose.

@columbarius
Copy link
Collaborator

I like the multi-line with keys for new options idea.

@columbarius
Copy link
Collaborator

@Hubro
I just added the bits for damage and cropping via pipewire here: https://github.com/columbarius/xdg-desktop-portal-wlr/tree/cropping.
If you want to test/use it, you have to somehow add the dimensions in.

@Hubro
Copy link
Author

Hubro commented Jul 2, 2021

If you want to test/use it, you have to somehow add the dimensions in.

I would love nothing more than to test this, but I'll need more detailed instructions 😄

@columbarius
Copy link
Collaborator

The easiest way would be to hardcode the view here https://github.com/columbarius/xdg-desktop-portal-wlr/blob/cropping/src/screencast/screencast.c#L61 like

cast->crop.x =
cast->crop.y =
cast->crop.width =
cast->crop.height =

and compile it

@Hubro
Copy link
Author

Hubro commented Jul 3, 2021

@columbarius

diff --git a/src/screencast/screencast.c b/src/screencast/screencast.c
index b0ec1d7..6453b64 100644
--- a/src/screencast/screencast.c
+++ b/src/screencast/screencast.c
@@ -59,6 +59,10 @@ void xdpw_screencast_instance_init(struct xdpw_screencast_context *ctx,
        cast->framerate = cast->max_framerate;
        cast->with_cursor = with_cursor;
        cast->refcount = 1;
+       cast->crop.x = 1280;
+       cast->crop.y = 30;
+       cast->crop.width = 2560;
+       cast->crop.height = 1410;
        logprint(INFO, "xdpw: screencast instance %p has %d references", cast, cast->refcount);
        wl_list_insert(&ctx->screencast_instances, &cast->link);
        logprint(INFO, "xdpw: %d active screencast instances",
xdg-desktop-portal-wlr on  cropping [!] is 📦 v>=0.50.0 
➜ ninja -C build
ninja: Entering directory `build'
[1/2] Compiling C object xdg-desktop-portal-wlr.p/src_screencast_screencast.c.o
FAILED: xdg-desktop-portal-wlr.p/src_screencast_screencast.c.o 
cc -Ixdg-desktop-portal-wlr.p -I. -I.. -I../include -Iprotocols -I/usr/include/pipewire-0.3 -I/usr/include/spa-0.2 -fdiagnostics-color=always -D_FILE_OFFSET_BITS=64 -Wall -Winvalid-pch -Wextra -Werror -std=c11 -g -Wno-missing-braces -Wno-missing-field-initializers -Wno-unused-parameter -D_POSIX_C_SOURCE=200809L '-DSYSCONFDIR="/usr/local/etc"' -DHAVE_LIBSYSTEMD=1 -D_REENTRANT -MD -MQ xdg-desktop-portal-wlr.p/src_screencast_screencast.c.o -MF xdg-desktop-portal-wlr.p/src_screencast_screencast.c.o.d -o xdg-desktop-portal-wlr.p/src_screencast_screencast.c.o -c ../src/screencast/screencast.c
../src/screencast/screencast.c: In function ‘xdpw_screencast_instance_init’:
../src/screencast/screencast.c:62:13: error: ‘struct xdpw_screencast_instance’ has no member named ‘crop’
   62 |         cast->crop.x = 1280;
      |             ^~
../src/screencast/screencast.c:63:13: error: ‘struct xdpw_screencast_instance’ has no member named ‘crop’
   63 |         cast->crop.y = 30;
      |             ^~
../src/screencast/screencast.c:64:13: error: ‘struct xdpw_screencast_instance’ has no member named ‘crop’
   64 |         cast->crop.width = 2560;
      |             ^~
../src/screencast/screencast.c:65:13: error: ‘struct xdpw_screencast_instance’ has no member named ‘crop’
   65 |         cast->crop.height = 1410;
      |             ^~
ninja: build stopped: subcommand failed.

Did I screw something up here 🤔 I'm not really a C developer.

I don't see a crop member on xdpw_screencast_instance though:

struct xdpw_screencast_instance {
	// list
	struct wl_list link;

	// xdpw
	uint32_t refcount;
	struct xdpw_screencast_context *ctx;
	bool initialized;
	struct xdpw_frame simple_frame;

	// pipewire
	struct pw_stream *stream;
	struct spa_hook stream_listener;
	struct spa_video_info_raw pwr_format;
	uint32_t seq;
	uint32_t node_id;
	bool pwr_stream_state;
	uint32_t framerate;

	// wlroots
	struct zwlr_screencopy_frame_v1 *frame_callback;
	struct xdpw_wlr_output *target_output;
	uint32_t max_framerate;
	struct zwlr_screencopy_frame_v1 *wlr_frame;
	struct xdpw_screencopy_frame screencopy_frame;
	bool with_cursor;
	int err;
	bool quit;

	// fps limit
	struct fps_limit_state fps_limit;
};

Should I do this?

cast->simple_frame.crop.x = 1280;
cast->simple_frame.crop.y = 30;
cast->simple_frame.crop.width = 2560;
cast->simple_frame.crop.height = 1410;

@Hubro
Copy link
Author

Hubro commented Jul 3, 2021

Actually when I try to build your branch with no changes, I can't get it to work. It starts OK, but as soon as I try to record from pipewire through OBS:

2021/07/03 14:45:23 [INFO] - dbus: create session method invoked
2021/07/03 14:45:23 [INFO] - dbus: request_handle: /org/freedesktop/portal/desktop/request/1_1982/obs1
2021/07/03 14:45:23 [INFO] - dbus: session_handle: /org/freedesktop/portal/desktop/session/1_1982/obs1
2021/07/03 14:45:23 [INFO] - dbus: app_id:
2021/07/03 14:45:23 [INFO] - dbus: select sources method invoked
2021/07/03 14:45:23 [INFO] - dbus: request_handle: /org/freedesktop/portal/desktop/request/1_1982/obs2
2021/07/03 14:45:23 [INFO] - dbus: session_handle: /org/freedesktop/portal/desktop/session/1_1982/obs1
2021/07/03 14:45:23 [INFO] - dbus: app_id:
2021/07/03 14:45:23 [INFO] - dbus: option types:1
2021/07/03 14:45:23 [INFO] - dbus: option multiple: 0
2021/07/03 14:45:23 [INFO] - dbus: option cursor_mode:2
2021/07/03 14:45:23 [DEBUG] - dbus: select sources: found matching session /org/freedesktop/portal/desktop/session/1_1982/obs1
2021/07/03 14:45:23 [INFO] - wlroots: capturable output: Samsung Electric Company model: LC49G95T: id: 42 name: DP-1
2021/07/03 14:45:23 [DEBUG] - wlroots: output chooser called
2021/07/03 14:45:23 [DEBUG] - wlroots: output chooser called
2021/07/03 14:45:24 [DEBUG] - wlroots: output chooser selects DP-1
2021/07/03 14:45:24 [INFO] - xdpw: screencast instance 0x5593c289f890 has 1 references
2021/07/03 14:45:24 [INFO] - xdpw: 1 active screencast instances
2021/07/03 14:45:24 [INFO] - wlroots: output: DP-1
2021/07/03 14:45:24 [INFO] - dbus: start method invoked
2021/07/03 14:45:24 [INFO] - dbus: request_handle: /org/freedesktop/portal/desktop/request/1_1982/obs3
2021/07/03 14:45:24 [INFO] - dbus: session_handle: /org/freedesktop/portal/desktop/session/1_1982/obs1
2021/07/03 14:45:24 [INFO] - dbus: app_id:
2021/07/03 14:45:24 [INFO] - dbus: parent_window:
2021/07/03 14:45:24 [DEBUG] - dbus: start: found matching session /org/freedesktop/portal/desktop/session/1_1982/obs1
2021/07/03 14:45:24 [WARN] - wlroots: failed to import buffer
2021/07/03 14:45:24 [INFO] - pipewire: stream state changed to "connecting"
2021/07/03 14:45:24 [INFO] - pipewire: node id is -1
2021/07/03 14:45:24 [INFO] - pipewire: stream state changed to "paused"
2021/07/03 14:45:24 [INFO] - pipewire: node id is 45
2021/07/03 14:45:24 [INFO] - pipewire: stream state changed to "streaming"
2021/07/03 14:45:24 [INFO] - pipewire: node id is 45
2021/07/03 14:45:24 [DEBUG] - wlroots: pipewire and wlroots metadata are incompatible. Renegotiate stream
xdg-desktop-portal-wlr: ../src/screencast/fps_limit.c:27: fps_limit_measure_end: Assertion `!timespec_is_zero(&state->frame_last_time)' failed.
zsh: abort (core dumped)  ./build/xdg-desktop-portal-wlr -r -l DEBUG

If I build the master branch of https://github.com/emersion/xdg-desktop-portal-wlr, it works fine.

@columbarius
Copy link
Collaborator

columbarius commented Jul 5, 2021 via email

@Hubro
Copy link
Author

Hubro commented Jul 5, 2021

@columbarius Done! This wouldn't be the first edge case I've run into by having a 5120x1440 240Hz monitor... 😕

@Hubro
Copy link
Author

Hubro commented Jul 6, 2021

@columbarius I noticed you pushed some new stuff to the pull request, so I re-tested the cropping branch. It no longer crashes, and I can successfully capture the screen. However, I couldn't get it to crop the image.

diff --git a/src/screencast/screencast.c b/src/screencast/screencast.c
index 2a7288f..06646a5 100644
--- a/src/screencast/screencast.c
+++ b/src/screencast/screencast.c
@@ -59,6 +59,10 @@ void xdpw_screencast_instance_init(struct xdpw_screencast_context *ctx,
        cast->framerate = cast->max_framerate;
        cast->with_cursor = with_cursor;
        cast->refcount = 1;
+       cast->simple_frame.crop.x = 1280;
+       cast->simple_frame.crop.y = 30;
+       cast->simple_frame.crop.width = 2560;
+       cast->simple_frame.crop.height = 1410;
        logprint(INFO, "xdpw: screencast instance %p has %d references", cast, cast->refcount);
        wl_list_insert(&ctx->screencast_instances, &cast->link);
        logprint(INFO, "xdpw: %d active screencast instances",

image

Trace log while capturing in OBS: https://gist.githubusercontent.com/Hubro/2fd9dde8f19c33d3f436c74e6b0f8a6d/raw/e6c6e82d92c00339f06c1c566d50d4f573644dfc/STDOUT

@columbarius
Copy link
Collaborator

@Hubro Just noticed that i didn't increase the numbers of params pushed to pipewire, so it didn't register, that we want to announce cropping. Should be fixed now.

@Hubro
Copy link
Author

Hubro commented Jul 6, 2021

@columbarius Alright, we're getting closer 😁

For some reason, the window size reported to OBS is correct, and everything to the left of the crop is gone, but everything to the right of the capture is still visible, outside of the box... It's hard to explain, take a look at the screenshot:

image

This is with:

+       cast->simple_frame.crop.x = 1280;
+       cast->simple_frame.crop.y = 0;
+       cast->simple_frame.crop.width = 2560;
+       cast->simple_frame.crop.height = 1440;

Here is the TRACE: https://gist.githubusercontent.com/Hubro/2fd9dde8f19c33d3f436c74e6b0f8a6d/raw/aff3b6af47ea807a50c6d4da640da78e4e116759/STDOUT

@Hubro
Copy link
Author

Hubro commented Jul 6, 2021

Though I should mention this is probably only a breaking issue for OBS and other composable streaming/recording applications. For simple screen sharing use cases, only the contents of the rectangle is displayed anyway, so the desired effect is achieved!

image

Except that there is probably some wasted resources when transmitting the test of the picture outside of the rectangle to the application.

@columbarius
Copy link
Collaborator

Great! Then I consider this done until #141 is merged.
The wrong cropping could be a obs bug. I can reproduce the same strange behaviour with obs.

@columbarius
Copy link
Collaborator

Or if anyone wants to write the config file options or extend the chooser code, please add this to #156 ;).

@Hubro
Copy link
Author

Hubro commented Jul 6, 2021

The wrong cropping could be a obs bug.

I thought the point of doing the cropping in Pipewire was to avoid transmitting unnecessary amounts of data to the client 🤔 How could this be an OBS bug if nothing outside of the crop rectangle should be transmitted to OBS in the first place.

@emersion
Copy link
Owner

emersion commented Jul 6, 2021

Cropping with Pipewire metadata doesn't prevent the whole screen from being copied and sent to consumers. In other words, cropping with Pipewire metadata doesn't reduce bandwidth between the compositor and consumers, so is unlikely to help a lot with your use case.

We can do the cropping at the screencopy protocol level to help with that.

@columbarius
Copy link
Collaborator

No, cropping via pipewire is just a meta information and tells the consumer, that it should only use/import part of the buffer. The size of the buffer doesn't affect the transmission via pipewire at all, since pipewire only stores a file descriptor to some memory location where the buffer is stored. A consumer can then use that information to only copy the significant part of the buffer into it's own memory and save bandwidth there.

What we can't change atm is the buffer we obtain from the compositor, since the wlr_screencopy protocol doesn't support to specify anything else then a screen output buffer. That would be improved with #107.

@emersion
Copy link
Owner

emersion commented Jul 6, 2021

the wlr_screencopy protocol doesn't support to specify anything else then a screen output buffer

screencopy does support cropping.

@columbarius
Copy link
Collaborator

columbarius commented Jul 6, 2021

the wlr_screencopy protocol doesn't support to specify anything else then a screen output buffer

screencopy does support cropping.

ahh, there is capture_output_region ... missed that. thanks.

@Hubro
Copy link
Author

Hubro commented Jul 6, 2021

Ok I see, then this is indeed a bug in OBS. In fact, if I go into "Edit transform..." (Ctrl+E) and apply 1px of cropping to the right of the bounding box, the entire overflow disappears.

I wouldn't consider this issue completely solved until the crop can be configured in the config file though, so I'll leave it open for now 😄

Thank you guys for your work, people like you make using Wayland feasible!

@columbarius
Copy link
Collaborator

@Hubro I updated #156 with the option to do cropping with wlroots. Additional to the cropping region you have to specify

cast->cropmode = XDPW_CROP_WLROOTS / XDPW_CROP_PIPEWIRE;

depending on which you want to use. XDPW_CROP_WLROOTS should have much better performance, because it only copies the wanted region.
Looking forward to your report on how the performance is.

@Hubro
Copy link
Author

Hubro commented Jul 19, 2021

Looking forward to your report on how the performance is.

It works! And the performance is significantly better.

I look forward for all this to be available through the config file 😁 Meanwhile I will be using your branch as my daily driver @columbarius

@Hubro
Copy link
Author

Hubro commented Jun 22, 2022

@columbarius Good afternoon sir, I happened to notice you pushed this commit 😁 columbarius@6f1e972

Is it now possible to configure the crop region in a config file? Is it stable enough to be worth testing?

As I'm still running on commit 28f75f2b from July last year, patched to crop the center portion of my screen. It would be nice to be able to track a stable release again, so I'm looking forward to something like this being merged 😄

@columbarius
Copy link
Collaborator

@Hubro A fine evening sir, your polite question is much appreciated :)

Yes it is possible to configure the crop region via config file. Everything is tracked in #156 and the attached branch.

... Dang I just assumed that you would get notifications on the MR so I forgot to report changes in this issue. Feel free to use the branch. It is stable in the sense that it's feature set is finished and is not expected to change in the future, so feel free to use run it.

It won't be merged anytime soon, since I see it just as a stop gap until we can share single windows and to do this we have to switch to a new wayland-protocol, which is in development atm (If there is a reason I'm missing, please remember me). Until the new protocol is finished this transition is another reason, why I don't want to officially add this feature.
But I see you use case, so I will treat #156 as an officially supported patchset, which I will rebase onto the latest release, or any other commit I see "production ready" and useful for this, so if anything is not working, please report it (or better send a patch ;))

Feel free to create the equivalent of an AUR package for your distro of choice of this branch.

@tmccombs
Copy link

Regardless of whether we ever get window sharing, I would like to be able to share a region of a screen.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants