SpacingBat3 / WebCord

A Discord and SpaceBar :electron:-based client implemented without Discord API.
MIT License
1.96k stars 98 forks source link

Sound in screen share on Linux #154

Closed DeeBeeDouble closed 8 months ago

DeeBeeDouble commented 2 years ago

MOD edit / notes

This issue ticket was previously a request to integrate with the existing solution, which I didn't want to work on – mostly as both JavaScript / TypeScript based client and native Node modules would better integrate with the code and build system (i.e. a such binary I would have to provide would likely has to be somehow rebuilt outside of Electron Forge and probably depend on more libraries / binaries which normally would not be needed for packaging of my application). However it quickly became one of the most popular topics in WebCord and became a bit more generic about sound screen sharing on Linux. It is also very special for me because of the large community interest about resolving it, making it much closer to be finally resolved once and for good. So thank you all for participating in it!

MOD EDIT2: This is now resolved in Chromium, awaiting to be merged to Chromium stable and/or Electron. Right now this is a feature behind the flags, hopefully it will be enabled by the default and will work just fine. See bugs.chromium.org#1143761 and Chromium commit 9a3d5ea for more details on it.

@SpacingBat3

Original message by @DeeBeeDouble:

Hey,

I was always annoyed by the fact that discord on Linux doesn't support audio sharing. Then I found webcord and another project that allows you to share your audio in a browser.

Then I thought: "Wouldn't it be absolutely awesome if webcord could integrate this?" Currently, it's rather complicated to >install the sound support (especially for newcomers). An integration in with webcord could make this process much easier.

I can not really code, so I can't make any suggestions on how to do that or if it is even possible, but I would just love to see it and thought I could at least give it a try.

Have a nice day, everyone ;)

SpacingBat3 commented 2 years ago

I'll probably won't intergrate this project with mine, I think it's a bad practise to depend on third-party binaries. However, I might make a use of NPM and find a module (probably native ones) to directly manage the PulseAudio server. But since it would take me some time to work on that, don't expect it'll be available soon.

DeeBeeDouble commented 2 years ago

Yeah, that would also work. But it is also going to be compatible with pipewire? (Because you only mentioned PulseAudio)

SpacingBat3 commented 2 years ago

But it is also going to be compatible with pipewire?

If PipeWire replaces it, then yes.

DeeBeeDouble commented 2 years ago

But it is also going to be compatible with pipewire?

If PipeWire replaces it, then yes.

Pipewire already is the default for many major distros today, like the newest Ubuntu, Pop-Os, Fedora and so on. So I'd say pulse already got replaced.

Anyway, shouldn't it also be fine just to support pulse audio and let pipewire-pulse do the rest?

In the end, it's your decision, of course.

SpacingBat3 commented 2 years ago

Pipewire already is the default for many major distros today, like the newest Ubuntu, Pop-Os, Fedora and so on. So I'd say pulse already got replaced.

I thought about the PulseAudio server using PipeWire. On many distributions PipeWire can be either installed alongside PulseAudio (and probably used as a separate audio system) or replace PulseAudio when a separate package is installed (pipewire-pulse on Arch). So, it won't directly support the PipeWire, but rather use PulseAudio client implementation and connect to local PulseAudio server, regardless if it is a compatibility layer implemented by PipeWire or actual standalone PulseAudio server implementation.

I hope that clears things up.

DeeBeeDouble commented 2 years ago

Ohh okay, that makes sense, thanks.

SpacingBat3 commented 2 years ago

And here's a few more details about the audio issue on Linux:

1. Upstream issue: [Linux] System loopback audio capture (#1143761).

2. The most recently updated PulseAudio client module for Node.js: NPM icon@tmigone/pulseaudio.

3. This comment from https://github.com/electron/electron/issues/10515:

Then the corresponding deviceId can be used in the audio constraints and mixed with the video stream to get a video-with-audio stream.

  • I'll probably use another module (more recently updated and designed for TypeScript).
  • I'll take a look if there's any way to get directly a stream and somehow make it understandable for the renderer process. It's unlikely that's possible (Javascript+DOM is quite different than Javascript+node and sharing the types would probably require of manually converting them from one to another).
snoweuph commented 2 years ago

Hi, when work on this is started, will there be an extra branch for it, that can be watched to stay up to date?

SpacingBat3 commented 2 years ago

Hi, when work on this is started, will there be an extra branch for it, that can be watched to stay up to date?

Probably not. I doubt I will do any changes without experimenting them first. And I think there's no much work at implementing it (only a lot of testing and understanding the module I will decide to use), so there won't be made a lot of commits. But if I feel unsure about its implementation and have concerns whenever it will work or mess up something within my code, then I'll definitely open a new branch.

I'll inform on this issue on any progress done. Right now I give it a lower priority due to its complexity, but it might be worked on once I deal with most bugs I can fix, probably finish a Flatpak maker for Reforged project and have a lot of free time. So please be patient or work on it by yourself – it's a bit shame there was no revolutionary code Pull Request on WebCord πŸ˜‰.

snoweuph commented 2 years ago

I see that as a challange :)

it's a bit shame there was no revolutionary code Pull Request on WebCord wink.

ik some JS and TS and worked a little bit with electron before, maybe I can get something working, but I don't know, I will see :)

snoweuph commented 2 years ago

I've got a question: Does normal Discord in the Browser support Screenshare audio theoreticly or was that striped of it?

snoweuph commented 2 years ago

Wait, I've understand that my Question doesn't make sense, In my Question I was thinking that audio and video have seperate endpoints, but thats prolly not the case, they are probably connected, so its just a matter of injecting the audio into the video stream

DeeBeeDouble commented 2 years ago

Wait, I've understand that my Question doesn't make sense, In my Question I was thinking that audio and video have seperate endpoints, but thats prolly not the case, they are probably connected, so its just a matter of injecting the audio into the video stream

Not quite sure, maybe take a look in the other project and see how they did it. I mean that project: https://github.com/edisionnano/Screenshare-with-audio-on-Discord-with-Linux

snoweuph commented 2 years ago

Okay, thx for the info :+1:

snoweuph commented 2 years ago

I will also ask the creator of that if he maybe wants to help,m i kinda understnad how it works, but as someone who has made it himself he prolly will be much better at it

snoweuph commented 2 years ago

from understand droping Pulseaudio support will make this so much easier, the creator of that project is also saying the same.

How about making it a expermintal pipewire only feature for now that need to be enabled first?

edisionnano commented 2 years ago

The easiest way to get this working would be to drop pulseaudio support and make a native node addon with node-addon-api and cmake-js that uses rohrkabel capture audio from apps like my virtmic binary does. To match a pipewire node to an X11 window(s) you can use the PID key pipewire provides

SpacingBat3 commented 2 years ago

The easiest way to get this working would be to drop pulseaudio support and make a native node addon with node-addon-api

I think it would be great to preserve PulseAudio and X11 compatibility, since PipeWire is still not or can't be used everywhere. And to not reinvent the wheel, the existing module can be used then.

And native module implementation might not be a requirement as long as there's a way to communicate with PipeWire/PulseAudio via UNIX socket. This could mean a reimplementation of protocol / client, but could work across different processes – native modules don't play well with Electron's renderer process from my experience.

DeeBeeDouble commented 2 years ago

from understand droping Pulseaudio support will make this so much easier, the creator of that project is also saying the same.

How about making it a expermintal pipewire only feature for now that need to be enabled first?

I would be totally fine with only supporting pipewire for now if it makes things easier. Later we could then start working to make it compatible with PulseAudio if this is still needed by then.

StayBlue commented 2 years ago

One thing I think that is worth mentioning is that Discord seems to be working on updating their version of Electron to 17-x-y, which I would imagine would help with supporting screen sharing with audio on Linux, but who knows.

I read this article a while back written by Discord which describes some of the modifications they made to webRTC, so we might not even see the support for screen sharing with audio on Linux unless they somehow pull that in.

If the stars align, hopefully we'll see it happen within the next couple of months, but knowing Discord, who knows...

edisionnano commented 2 years ago

One thing I think that is worth mentioning is that Discord seems to be working on updating their version of Electron to 17-x-y, which I would imagine would help with supporting screen sharing with audio on Linux, but who knows.

I read this article a while back written by Discord which describes some of the modifications they made to webRTC, so we might not even see the support for screen sharing with audio on Linux unless they somehow pull that in.

If the stars align, hopefully we'll see it happen within the next couple of months, but knowing Discord, who knows...

Electron version is totally unrelated to Screenshare, they use a native node addon

SpacingBat3 commented 2 years ago

@StayBlueee your comment does not change anything in WebCord's situation – I don't want or plan to depend on their proprietary module just to support Linux sound screen share. And WebCord's already at 19.x.y, so it has to do nothing with Electron as @edisionnano said. I've flagged your comment and all replies to it as off-topic for this reason.

DeeBeeDouble commented 2 years ago

There is a new discord client that already supports audio share. Just wanted to let you guys know because I guess some inspiration would make it easier to implement. Also, there is a mention on Reddit that it should be implemented into webcord with over 90 upvotes, so it is definitely a requested feature.

SpacingBat3 commented 2 years ago

@DeeBeeDouble it's not the issue to implement in any way. I want to implement it the proper way, at best to have a node module extension to record audio and transfer that data back to Node and at least implement a code to create a virtual sink for all audio output devices and use that as the stream input device. I may play with Rust to achieve this, I have a very little experience writting native Node modules and manipulating the Buffer data. I may also try to develop a code with TypeScript by trying to connect to PulseAudio Unix socket and try manage it using it. The better might be a fully TypeScript-made solution for faster compilation and cross-platform support, yet slower runtime execution (that might not be that slow to affect the overall app's performance).

DeeBeeDouble commented 2 years ago

@DeeBeeDouble it's not the issue to implement in any way. I want to implement it the proper way, at best to have a node module extension to record audio and transfer that data back to Node and at least implement a code to create a virtual sink for all audio output devices and use that as the stream input device. I may play with Rust to achieve this, I have a very little experience writting native Node modules and manipulating the Buffer data. I may also try to develop a code with TypeScript by trying to connect to PulseAudio Unix socket and try manage it using it. The better might be a fully TypeScript-made solution for faster compilation and cross-platform support, yet slower runtime execution (that might not be that slow to affect the overall app's performance).

Alright, even better

SpacingBat3 commented 2 years ago

I've realised that actually it might not be the best idea to use any stream for the screen recording. You see, any modification is likely to be found by Discord if they want to. Using a MediaTrack that has different properties than expected would likely make WebCord easier to discover as a modified client. This is why I think it would be better to actually set the default device to monitor or virtual sink and rely on Chromium's code for recording audio rather than picking it directly by WebCord.

I may add a flag to forcefully enable audio in screen recording to mitigate this issue, so with the right configuration (monitor as default, microphone as selected input device) you should have both working microphone and system audio.

edisionnano commented 2 years ago

I've realised that actually it might not be the best idea to use any stream for the screen recording. You see, any modification is likely to be found by Discord if they want to. Using a MediaTrack that has different properties than expected would likely make WebCord easier to discover as a modified client. This is why I think it would be better to actually set the default device to monitor or virtual sink and rely on Chromium's code for recording audio rather than picking it directly by WebCord.

I may add a flag to forcefully enable audio in screen recording to mitigate this issue, so with the right configuration (monitor as default, microphone as selected input device) you should have both working microphone and system audio.

If you are going to record the monitor then users will hear their voice back on the stream, also Chromium can't capture monitors

SpacingBat3 commented 2 years ago

If you are going to record the monitor then users will hear their voice back on the stream (...)

This is actually an issue in Windows (see #186). Maybe there also could be a way to create a such MediaStream and spoof some values that are considered as abnormal (like it is already done with getUserMedia and getDisplayMedia functions), but that would be a lot of work. I think having any kind of audio support is better than a lack of it, through.

SpacingBat3 commented 2 years ago

(...), also Chromium can't capture monitors

Yes and no. For me it works when the monitor is set as the default device (in system settings, likely pavucontrol on many distributions), so in ideal environment WebCord would set a monitor of the default input device it detects (somehow) or at least find out whenever the default device is monitor or not and microphone is set to non-default device (for an recommended setup – this should be possible as WebCord can read a lot about the current Discord configuration). This is why I believe Chromium is able to capture monitors, yet it is designed to not list monitors (I guess that could be confusing to end-users and maybe even used maliciously in some cases).

edisionnano commented 2 years ago

This is actually an issue in Windows (see #186). Maybe there also could be a way to create a such MediaStream and spoof some values that are considered as abnormal (like it is already done with getUserMedia and getDisplayMedia functions), but that would be a lot of work. I think having any kind of audio support is better than a lack of it, through.

There's a middle ground solution, create a virtual microphone. Way more proper solution than capturing the monitor. If you ask me no audio at all is better than users hearing their voice back. The true proper solution would be to fork electron and create a function that makes a mediastream which can accept audio frames from a callback like discord does for video (directvideo) but it's too much work for minimal gain.

Yes and no. For me it works when the monitor is set as the default device (in system settings, likely pavucontrol on many distributions), so in ideal environment WebCord would set a monitor of the default input device it detects (somehow) or at least find out whenever the default device is monitor or not and microphone is set to non-default device (for an recommended setup – this should be possible as WebCord can read a lot about the current Discord configuration). This is why I believe Chromium is able to capture monitors, yet it is designed to not list monitors (I guess that could be confusing to end-users and maybe even used maliciously in some cases).

So basically the plan is to set the monitor as the default input device system wide?

SpacingBat3 commented 2 years ago

The most recently updated PulseAudio client module for Node.js

As a side note, I've played with this module and even if I've patched it to work with Unix sockets, it did failed further at least for some requests, IDK whenever it is because I use PipeWire or I used it improperly. Personally I would develop something like that even for PipeWire if I could find any specs about the socket communication (i.e. how packet is designed? is is encoded string or binary data with the specific construction?). This is what's going probably to keep it blocking.

SpacingBat3 commented 2 years ago

so basically the plan is to set the monitor as the default input device system wide?

At least for now, but I may just add a flag to force enable audio capture. User will need to configure it by himself if he wants to.

There's a middle ground solution, create a virtual microphone.

I guess you mean the virtual sink (at least as of the PulseAudio), but since I believe it takes the output as the source, what really does it change? And even if you would set up it to, the problem is that Discord could just distinguish WebCord if any abnormal difference would exists – and by experimenting with MediaStream API, I've noticed a lot of methods to actually detect this. WebCord would need to spoof each function so the returned MediaTrack containing the audio will look like the one that is returned normally with Chromium audio recording.

The true proper solution would be to fork electron (...)

It's more an issue with Chromium, but definitely can be fixed on the Electron side. Personally I think desktop capture on Electron is somewhat experimental by itself, WebCord does a lot nowadays to bypass the sandboxing between the processes and mitigate differences between Chromium and Electron to actually make it functional. A lot of that could actually be prevented on Electron side, e.g. by implementing a function that works in native process (probably as a part of desktopCapturer API) and which uses a callback (either asynchronous or synchronous returning a promise) to return an information needed to capture screen (e.g. window, audio device, whenever it denies a requests or resolves it with stream) that is invoked on getDisplayMedia to replace a current implementation using getUserMedia with non-standard constrains.

edisionnano commented 2 years ago

~The most recently updated PulseAudio client module for Node.js~

As a side note, I've played with this module and even if I've patched it to work with Unix sockets, it did failed further at least for some requests, IDK whenever it is because I use PipeWire or I used it improperly. Personally I would develop something like that even for PipeWire if I could find any specs about the socket communication (i.e. how packet is designed? is is encoded string or binary data with the specific construction?). This is what's going probably to keep it blocking.

What do you need the sockets for?

so basically the plan is to set the monitor as the default input device system wide?

At least for now, but I may just add a flag to force enable audio capture. User will need to configure it by himself if he wants to.

That actually sounds like a bad solution.

There's a middle ground solution, create a virtual microphone.

I guess you mean the virtual sink (at least as of the PulseAudio), but since I believe it takes the output as the source, what really does it change? And even if you would set up it to, the problem is that Discord could just distinguish WebCord if any abnormal difference would exists – and by experimenting with MediaStream API, I've noticed a lot of methods to actually detect this. WebCord would need to spoof each function so the returned MediaTrack containing the audio will look like the one that is returned normally with Chromium audio recording.

You create a virtual microphone which carries the audio of the application you want to share, same way I do it on my method.

The true proper solution would be to fork electron (...)

It's more an issue with Chromium, but definitely can be fixed on the Electron side. Personally I think desktop capture on Electron is somewhat experimental by itself, WebCord does a lot nowadays to bypass the sandboxing between the processes and mitigate differences between Chromium and Electron to actually make it functional. A lot of that could actually be prevented on Electron side, e.g. by implementing a function that works in native process (probably as a part of desktopCapturer API) and which uses a callback (either asynchronous or synchronous returning a promise) to return an information needed to capture screen (e.g. window, audio device, whenever it denies a requests or resolves it with stream) that is invoked on getDisplayMedia to replace a current implementation using getUserMedia with non-standard constrains.

Electron is Chromium and WebCord uses Electron which is why I said fork Electron. Discord on the browser calls getDisplayMedia but Electron doesn't have that, it has getUserMedia for everything with different arguments. I assume you redefine it in JS at some point to call getUserMedia. The plan is to fork electron, make a function that is exposed to JS and creates a new MediaStream with an audio track that accepts audio frames from a callback (on a native addon you dlopen null and dlsym the function) and then redefine getDisplayMedia to call this function and add this track to the screenshare stream. That would have been a proper solution. But it's too much work when you can just make a native node addon that creates a virtual mic that carries an app's audio same way I do it.

SpacingBat3 commented 2 years ago

What do you need the sockets for?

For communication with the server (both PulseAudio and PipeWire are servers and use that). Other solution is using libraries to control PulseAudio/PipeWire through. Sockets would allow me to implement a direct PulseAudio control and data exchange with nothing but Node API and TypeScript. And it doesn't need to be fully implemented, only for a particular feature support, like getting a data from application steams or creating a virtual sink. I know where socket usually is for both PipeWire and PulseAudio, I just need a documentation or analyse the libraries in order to understand how to communicate with it.

That actually sounds like a bad solution.

It's not the final solution and sure it is bad. But basically takes no time to implement and is better than nothing before I come with something more complex.

You create a virtual microphone which carries the audio of the application you want to share, same way I do it on my method.

It's not just that it is not possible, it is that in WebCord I'm a bit sophisticated about Discord practises and aware that they could target specific modifications done to it and either block them or users using them. MediaTrack and enumerateDevices gives a lot of details that would need to be spoofed in order to make it indistinguishable from the System Audio stream returned by Chromium (i.e. it should spoof ids and constrains in either MediaTrack methods or properties so Discord can't find out that easily whenever this is a stock Chromium or custom client). So I consider this solution under one circumstance: it won't be enabled by the default and end-user would need to check an option for that. This way WebCord does not break ToS in visible way, making it safe enough to survive any possible future action done by Discord to prevent mods.

Other than this, integrating your binary is a bit of the problem, since it is precompiled. At best, it would be great to use system tools to achieve the same you did in order to avoid compiling anything (and possibly making it functional on any OS architecture and platform, even those unofficially supported by Electron yet still having binaries that could be used to run WebCord with) or use the modules (they will be recompiled when needed on build process via Electron Forge). I may take a look on both of these.


As a side note, I plan to move all that work outside of WebCord if it ever be done, so it will be possible to use it in other Electron-based applications.

edisionnano commented 2 years ago

For communication with the server (both PulseAudio and PipeWire are servers and use that). Other solution is using libraries to control PulseAudio/PipeWire through. Sockets would allow me to implement a direct PulseAudio control and data exchange with nothing but Node API and TypeScript. And it doesn't need to be fully implemented, only for a particular feature support, like getting a data from application steams or creating a virtual sink. I know where socket usually is for both PipeWire and PulseAudio, I just need a documentation or analyse the libraries in order to understand how to communicate with it.

Tbf, I'd just drop PulseAudio in your case, it's dead. Pipewire makes this whole endeavor a lot easier. And yeah my idea was to use the API through libraries. Creating a sink is a bit messy, on Pipewire you can just capture the audio, properly.

It's not the final solution and sure it is bad. But basically takes no time to implement and is better than nothing before I come with something more complex.

As a non final solution you can just make a virtual microphone which carries app audio and capture that, it's very easy.

It's not just that it is not possible, it is that in WebCord I'm a bit sophisticated about Discord practises and aware that they could target specific modifications done to it and either block them or users using them. MediaTrack and enumerateDevices gives a lot of details that would need to be spoofed in order to make it indistinguishable from the System Audio stream returned by Chromium (i.e. it should spoof ids and constrains in either MediaTrack methods or properties so Discord can't find out that easily whenever this is a stock Chromium or custom client). So I consider this solution under one circumstance: it won't be enabled by the default and end-user would need to check an option for that. This way WebCord does not break ToS in visible way, making it safe enough to survive any possible future action done by Discord to prevent mods.

Other than this, integrating your binary is a bit of the problem, since it is precompiled. At best, it would be great to use system tools to achieve the same you did in order to avoid compiling anything (and possibly making it functional on any OS architecture and platform, even those unofficially supported by Electron yet still having binaries that could be used to run WebCord with) or use the modules (they will be recompiled when needed on build process via Electron Forge). I may take a look on both of these.

As a side note, I plan to move all that work outside of WebCord if it ever be done, so it will be possible to use it in other Electron-based applications.

Webcord is not a client, it's a browser. You are loading the browser version of Discord and not doing any modifications to it, you are only modifying the browser. I believe using any browser isn't against the Discord TOS. So no reason to go paranoid over discord analysing the MediaStream tracks. But yeah it being an option and not enabled by default isn't a bad idea. As for "my" binary, it's just one of the rohrkabel examples which is open source.

Taza53 commented 2 years ago

https://github.com/maltejur/discord-screenaudio work well with certain limitations

NyaomiDEV commented 2 years ago

Maybe there also could be a way to create a such MediaStream and spoof some values that are considered as abnormal (like it is already done with getUserMedia and getDisplayMedia functions), but that would be a lot of work. I think having any kind of audio support is better than a lack of it, through.

And there is!

Assuming you get the audio frames somewhat to the renderer process (native module????), you can make AudioData objects with them, and then you can pipe all this juicy data to the WritableStream exposed by a MediaStreamTrackGenerator object that you can create yourself with { kind: "audio" } as its arguments. Then you add the generator as a MediaStreamTrack (not linking since I guess everyone here is familiar with it) to the screenshare MediaStream and you're basically golden.

Further reference: https://developer.mozilla.org/en-US/docs/Web/API/Insertable_Streams_for_MediaStreamTrack_API

Now, is this "detectable" by Discord? I mean, of course it could be, and if you want to be totally sure that no one detects anything and that everything looks legit you have to create a virtual microphone that outputs audio data and then capture that one, but this approach surely results in a way better experience for the end user, as the virtual mic could be wrongfully selected by users in other apps and it overall would clog up the PipeWire graph (a virtual mic is essentially a virtual sink, a virtual sink monitor (which is a source) and a virtual source which is not marked as a monitor).

For the native module, I guess that there's a lot of reusable code from the other discord-screenaudio client and from obs-pipewire-audio-capture which does not rely on external libraries and pulls everything off just by using the PipeWire API, which is arguably nice.

Now for the most exciting part, let's use desktopCapturer to our advantage (at least on X11): As you can read from Electron's documentation, calling getSources returns a promise with windows and screens; now, what's exciting about that is that window IDs are something like window:<X Window ID>:<Is it the current process or not>, and that the X Window ID can be used on tools like xprop (and libraries too, such as the NodeJS x11 library) to query a specific atom which is _NET_WM_PID which yields... the application PID! And from here it's an easy win: now we have to go through all the applications in the PipeWire API and find the ones with the matching PID, which is stored in the application.process.id property.

This way we can avoid "manual selections" of source video and audio and just compute them ourselves! Though, I don't know how something similar can be achieved on Wayland.

SpacingBat3 commented 2 years ago

@NyaomiDEV Thank you for your information! While reading this I've realised I forgot about Wayland capturer, which I'm unsure if returns audio or not. It's definitely using PipeWire and Chromium has no control over it, so maybe it returns window/screen audio which is interpreted as System Audio? I need to test it and maybe Wayland users will get it supported out-of-the-box, with no workarounds involved.

edisionnano commented 2 years ago

@NyaomiDEV Thank you for your information! While reading this I've realised I forgot about Wayland capturer, which I'm unsure if returns audio or not. It's definitely using PipeWire and Chromium has no control over it, so maybe it returns window/screen audio which is interpreted as System Audio? I need to test it and maybe Wayland users will get it supported out-of-the-box, with no workarounds involved.

I doubt pipewire screen capture contains an audio stream. Btw, pipewire can also capture screen/windows on X11 too if the wm implements it, I think mutter does. But yeah capturing an app's audio with pipewire is easy, all you have to know is the pid. Alternative capture everything excluding webcord's pid or node name/description. The idea is that you use the .global and .global_remove events to keep track of new nodes and nodes being removed and captures the ones that fit your criteria, there's an OBS plugin that does this if you are interested.

snoweuph commented 2 years ago

Pipewire in KWin allows me to Capture Whole Screens and Even to Capture A Composition of Screen (multiple Screens together)

NyaomiDEV commented 2 years ago

Btw, pipewire can also capture screen/windows on X11 too if the wm implements it, I think mutter does.

I think Plasma doesn't (just checked). Definitely not something to rely upon as the portal implementation (and thus pipewire) was made specifically to address screensharing on Wayland.

edisionnano commented 2 years ago

Pipewire in KWin allows me to Capture Whole Screens and Even to Capture A Composition of Screen (multiple Screens together)

Wayland only probably

snoweuph commented 2 years ago

Pipewire in KWin allows me to Capture Whole Screens and Even to Capture A Composition of Screen (multiple Screens together)

Wayland only probably

Well, I've actually didn't use X11 with KDE yet

GarciaLnk commented 2 years ago

I've compiled a version with the latest commits and screen share is broken when using the --force-audio-share-support flag, the window/screen selection screen doesn't pop up and it's stuck on a grey screen while it prints this error on console:

[75204:0801/040415.101717:ERROR:bad_message.cc(29)] Terminating renderer for bad IPC message, reason 263

I'm on the latest Fedora KDE so I'm using Wayland+PipeWire.

SpacingBat3 commented 2 years ago

I've compiled a version with the latest commits and screen share is broken when using the --force-audio-share-support flag, the window/screen selection screen doesn't pop up and it's stuck on a grey screen while it prints this error on console:

[75204:0801/040415.101717:ERROR:bad_message.cc(29)] Terminating renderer for bad IPC message, reason 263

I'm on the latest Fedora KDE so I'm using Wayland+PipeWire.

This should be now fixed. It will make it to WebCord 3.6.0 (which is why I've added this issue to the WebCord 3.6.0 milestone).

GarciaLnk commented 2 years ago

This should be now fixed. It will make it to WebCord 3.6.0 (which is why I've added this issue to the WebCord 3.6.0 milestone).

The error is gone but unfortunately audio sharing still doesn't work for me 😒

SpacingBat3 commented 2 years ago

The error is gone but unfortunately audio sharing still doesn't work for me 😒

I need to look myself how it works and if any MediaTrack is used for audio.

The flag is to enforce audio support, but not fix any bugs in Chromium with it. It does what it does, without any guarantees how exactly it will work – that mostly depends how it works on Chromium.

At least X11 is going to work. It's terrible hack, but there's a lot of the work involved to get something better. Maybe I could actually look at implementing PipeWire communication via socket, find out how it is encoded/decoded and develop a generic module like someone did with PulseAudio. As a reference, I will take a look how native library is made. Then I suppose I would probably need to pipe the Node WritableStream stream to the DOM ones.

Now, is this "detectable" by Discord?

As long as stream name is System Audio and everything is returned exactly like in case of Chromium's system audio stream, we should be safe. In other way Discord could develop a targeted code to find out if streams are legitimate just by checking their props.

NyaomiDEV commented 2 years ago

I looked at the recent flag implementation and I am impressed it actually spawns two "Chromium input" instances. Problem for now is, we cannot have a way of telling which is which other than by trying to flip one. And I specifically have to mess with the PipeWire graph to get it to work. Wonky asf, but it sorta works, and it's better than nothing; still I would not ship it as is.

A request though: can we have it as an option in the settings menu?

SpacingBat3 commented 2 years ago

still I would not ship it as is

I've made it as a flag since it is not meant to be something that everyone should be able to set up. Officially, there's no audio Linux, even if the one can set it with PipeWire or PulseAudio. I agree this should not be called as a fix of this issue, which is why I do not plan to close it.

I need to think how to implement it through, because I still think about enabling Chromium sandbox in main window preload and drop Node API there. With both contextIsolation enabled and sandbox it might seem that I need to leak an IPC listener to the injected script and use that to send a Buffer.

edisionnano commented 2 years ago

I looked at the recent flag implementation and I am impressed it actually spawns two "Chromium input" instances. Problem for now is, we cannot have a way of telling which is which other than by trying to flip one. And I specifically have to mess with the PipeWire graph to get it to work. Wonky asf, but it sorta works, and it's better than nothing; still I would not ship it as is.

A request though: can we have it as an option in the settings menu?

If you capture the same device in Chromium twice it will show as one on pavucontrol, however if you capture the device by name and default (which could be the same device) it will show as two because I assume Chromium creates another capture. There's no way, as far as I am concerned, to tell which is which and this is the reason I force it to capture virtmic using javascript and it works out of the box, no need to force different devices manually and have to find out which is which.