mirror of
				https://github.com/smartfrigde/armcord.git
				synced 2024-08-14 23:56:58 +00:00 
			
		
		
		
	[wip] linux audio screenshare
This commit is contained in:
		
							parent
							
								
									bed9e02059
								
							
						
					
					
						commit
						1a1c549f6c
					
				
					 4 changed files with 290 additions and 22 deletions
				
			
		
							
								
								
									
										65
									
								
								src/content/js/patchAudio.js
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										65
									
								
								src/content/js/patchAudio.js
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,65 @@ | |||
| navigator.mediaDevices.getDisplayMedia = getDisplayMedia; | ||||
| // ==UserScript==
 | ||||
| // @name         Screenshare with Audio
 | ||||
| // @namespace    https://github.com/edisionnano
 | ||||
| // @version      0.4
 | ||||
| // @updateURL https://openuserjs.org/meta/samantas5855/Screenshare_with_Audio.meta.js
 | ||||
| // @description  Screenshare with Audio on Discord
 | ||||
| // @author       Guest271314 and Samantas5855
 | ||||
| // @match        https://*.discord.com/*
 | ||||
| // @icon         https://www.google.com/s2/favicons?domain=discord.com
 | ||||
| // @grant        none
 | ||||
| // @license      MIT
 | ||||
| // ==/UserScript==
 | ||||
| 
 | ||||
| /* jshint esversion: 8 */ | ||||
| 
 | ||||
| navigator.mediaDevices.chromiumGetDisplayMedia = navigator.mediaDevices.getDisplayMedia; | ||||
| 
 | ||||
| const getAudioDevice = async (nameOfAudioDevice) => { | ||||
|     await navigator.mediaDevices.getUserMedia({ | ||||
|         audio: true | ||||
|     }); | ||||
|     await new Promise((r) => setTimeout(r, 1000)); | ||||
|     let devices = await navigator.mediaDevices.enumerateDevices(); | ||||
|     let audioDevice = devices.find(({label}) => label === nameOfAudioDevice); | ||||
|     return audioDevice; | ||||
| }; | ||||
| 
 | ||||
| const getDisplayMedia = async () => { | ||||
|     var id; | ||||
|     try { | ||||
|         let myDiscordAudioSink = await getAudioDevice("screenshareAudio"); | ||||
|         id = myDiscordAudioSink.deviceId; | ||||
|     } catch (error) { | ||||
|         id = "default"; | ||||
|     } | ||||
|     let captureSystemAudioStream = await navigator.mediaDevices.getUserMedia({ | ||||
|         audio: { | ||||
|             // We add our audio constraints here, to get a list of supported constraints use navigator.mediaDevices.getSupportedConstraints();
 | ||||
|             // We must capture a microphone, we use default since its the only deviceId that is the same for every Chromium user
 | ||||
|             deviceId: { | ||||
|                 exact: id | ||||
|             }, | ||||
|             // We want auto gain control, noise cancellation and noise suppression disabled so that our stream won't sound bad
 | ||||
|             autoGainControl: false, | ||||
|             echoCancellation: false, | ||||
|             noiseSuppression: false | ||||
|             // By default Chromium sets channel count for audio devices to 1, we want it to be stereo in case we find a way for Discord to accept stereo screenshare too
 | ||||
|             //channelCount: 2,
 | ||||
|             // You can set more audio constraints here, bellow are some examples
 | ||||
|             //latency: 0,
 | ||||
|             //sampleRate: 48000,
 | ||||
|             //sampleSize: 16,
 | ||||
|             //volume: 1.0
 | ||||
|         } | ||||
|     }); | ||||
|     let [track] = captureSystemAudioStream.getAudioTracks(); | ||||
|     const gdm = await navigator.mediaDevices.chromiumGetDisplayMedia({ | ||||
|         video: true, | ||||
|         audio: true | ||||
|     }); | ||||
|     gdm.addTrack(track); | ||||
|     return gdm; | ||||
| }; | ||||
| navigator.mediaDevices.getDisplayMedia = getDisplayMedia; | ||||
							
								
								
									
										79
									
								
								src/screenshare/audio.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										79
									
								
								src/screenshare/audio.ts
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,79 @@ | |||
| import {spawn} from "child_process"; | ||||
| import fs from "fs"; | ||||
| import {app} from "electron"; | ||||
| import path from "path"; | ||||
| 
 | ||||
| // quick and dirty way for audio screenshare on Linux
 | ||||
| // please PR a better non-shell solution that isn't a dependency mess
 | ||||
| //src https://github.com/edisionnano/Screenshare-with-audio-on-Discord-with-Linux
 | ||||
| interface SinkInput { | ||||
|     id: string; | ||||
|     properties: Record<string, string>; | ||||
| } | ||||
| 
 | ||||
| const parseSinkInputs = (input: string): SinkInput[] => { | ||||
|     const regex = /Sink Input #(\d+)\n([\s\S]+?)(?=\nSink Input #|\n{2,}|$)/g; | ||||
|     const result: SinkInput[] = []; | ||||
|     let match; | ||||
| 
 | ||||
|     while ((match = regex.exec(input))) { | ||||
|         const sinkInput: SinkInput = { | ||||
|             id: match[1], | ||||
|             properties: {} | ||||
|         }; | ||||
| 
 | ||||
|         const propertiesRegex = /(\w+)\s*=\s*"([^"]*)"/g; | ||||
|         let propertiesMatch; | ||||
| 
 | ||||
|         while ((propertiesMatch = propertiesRegex.exec(match[2]))) { | ||||
|             sinkInput.properties[propertiesMatch[1]] = propertiesMatch[2]; | ||||
|         } | ||||
| 
 | ||||
|         result.push(sinkInput); | ||||
|     } | ||||
| 
 | ||||
|     return result; | ||||
| }; | ||||
| 
 | ||||
| export function createVirtualDevice(sinkInput: number) { | ||||
|     var script = ` | ||||
|     SINK_NAME=armcord | ||||
|     export LC_ALL=C | ||||
|     DEFAULT_OUTPUT=$(pactl info|sed -n -e 's/^.*Default Sink: //p') | ||||
|     pactl load-module module-null-sink sink_name=$SINK_NAME | ||||
|     pactl move-sink-input ${sinkInput} $SINK_NAME | ||||
|     pactl load-module module-loopback source=$SINK_NAME.monitor sink=$DEFAULT_OUTPUT | ||||
|     if pactl info|grep -w "PipeWire">/dev/null; then | ||||
|         nohup pw-loopback --capture-props='node.target='$SINK_NAME --playback-props='media.class=Audio/Source node.name=virtmic node.description="virtmic"' >/dev/null & | ||||
|     else | ||||
|         pactl load-module module-remap-source master=$SINK_NAME.monitor source_name=virtmic source_properties=device.description=virtmic | ||||
|     fi`;
 | ||||
|     let scriptPath = path.join(app.getPath("temp"), "/", "script.sh"); | ||||
|     spawn("chmod +x " + scriptPath); | ||||
|     fs.writeFileSync(scriptPath, script, "utf-8"); | ||||
|     const exec = spawn(scriptPath); | ||||
| } | ||||
| 
 | ||||
| export function isAudioSupported(): boolean { | ||||
|     const pactl = spawn("pactl"); | ||||
| 
 | ||||
|     pactl.on("close", (code) => { | ||||
|         if (code == 0) { | ||||
|             return true; | ||||
|         } | ||||
|     }); | ||||
|     return false; | ||||
| } | ||||
| export function getSinks() { | ||||
|     const pactl = spawn("pactl list sink-inputs"); | ||||
| 
 | ||||
|     pactl.stderr.on("data", (data) => { | ||||
|         console.log(data); | ||||
|         return parseSinkInputs(data); | ||||
|     }); | ||||
|     pactl.on("close", (code) => { | ||||
|         if (code !== 0) { | ||||
|             throw Error("Couldn't get list of available apps for audio stream"); | ||||
|         } | ||||
|     }); | ||||
| } | ||||
|  | @ -1,10 +1,18 @@ | |||
| import {BrowserWindow, desktopCapturer, ipcMain, session} from "electron"; | ||||
| import path from "path"; | ||||
| import {iconPath} from "../main"; | ||||
| import {getSinks, isAudioSupported} from "./audio"; | ||||
| let capturerWindow: BrowserWindow; | ||||
| function registerCustomHandler(): void { | ||||
|     session.defaultSession.setDisplayMediaRequestHandler(async (request, callback) => { | ||||
|         console.log(request); | ||||
|         if (process.platform == "linux") { | ||||
|             let isAudio = isAudioSupported(); | ||||
|             if (isAudio) { | ||||
|                 console.log("audio supported"); | ||||
|                 getSinks(); | ||||
|             } | ||||
|         } | ||||
|         const sources = await desktopCapturer.getSources({ | ||||
|             types: ["screen", "window"] | ||||
|         }); | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue