Compare commits

...

21 Commits

Author SHA1 Message Date
Malte Jürgens
a10cfda56d fix usage of application.process.binary 2023-01-26 01:19:38 +01:00
Malte Jürgens
2a809e163e fix class names for tray settings 2023-01-26 00:45:14 +01:00
Malte Jürgens
bc317d5531 use application.process.binary if application.name is not available 2023-01-25 18:47:08 +01:00
Malte Jürgens
b4db987217 fix tray settings 2023-01-23 18:59:25 +01:00
Malte Jürgens
9f46e710a9 fix tray bug 2023-01-14 16:28:23 +01:00
Malte Jürgens
d6641a7a6e Merge pull request #88 from maltejur/tray
Implement Tray Icon
2023-01-14 14:51:46 +00:00
Malte Jürgens
b836be6530 Implement Tray Icon 2023-01-13 23:33:47 +01:00
Malte Jürgens
bfb0714b13 fix keybinds query selector 2023-01-09 22:20:11 +01:00
Malte Jürgens
374b854261 Update README 2023-01-09 22:19:45 +01:00
Malte Jürgens
1f6105f76b allow for building with qt6 2022-12-27 19:00:10 +01:00
Malte Jürgens
3071159332 Update screenshot 2022-12-11 17:55:04 +00:00
Malte Jürgens
f2de080e1b update labels 2022-11-22 16:39:58 +01:00
Malte Jürgens
f4a60f281d reworked stream dialog 2022-11-22 16:35:47 +01:00
Malte Jürgens
d693535d53 Revert "add clipboard permission"
This reverts commit b0a8815bb8.
2022-11-09 22:13:34 +01:00
Malte Jürgens
e41af697f7 fix error 2022-11-05 00:08:25 +01:00
Malte Jürgens
b0a8815bb8 add clipboard permission 2022-11-04 23:20:46 +01:00
Malte Jürgens
150fd4364e add --notify-send option 2022-11-04 23:16:49 +01:00
Malte Jürgens
a0a2924796 remove Vencord folder 2022-11-04 20:45:55 +00:00
Malte Jürgens
3c48621427 add developer field to metainfo 2022-10-28 21:57:32 +02:00
Malte Jürgens
9faabe1f3e Merge pull request #66 from kherchel/fix/keybinds
Fix mute and deafen keybinds disconnecting from the call
2022-10-25 18:18:08 +00:00
Kacper Herchel
68473d04d8 Fix mute and deafen keybinds disconnecting from the call instead of performing their appropriate action 2022-10-25 17:01:10 +02:00
12 changed files with 418 additions and 135 deletions

View File

@@ -13,24 +13,27 @@ set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -DDEBUG")
string(TIMESTAMP TIMESTAMP %s)
# set(CMAKE_AUTOUIC ON)
find_package(Qt5 CONFIG REQUIRED COMPONENTS
Widgets
WebEngineWidgets
)
find_package(Qt5 COMPONENTS Widgets)
if (Qt5_FOUND)
find_package(Qt5 CONFIG REQUIRED COMPONENTS Widgets WebEngineWidgets)
find_package(KF5Notifications)
if(KF5Notifications_FOUND)
find_package(KF5Notifications)
if(KF5Notifications_FOUND)
add_definitions( -DKNOTIFICATIONS )
endif()
endif()
find_package(KF5XmlGui)
if(KF5XmlGui_FOUND)
find_package(KF5XmlGui)
if(KF5XmlGui_FOUND)
add_definitions( -DKXMLGUI )
endif()
endif()
find_package(KF5GlobalAccel)
if(KF5GlobalAccel_FOUND)
find_package(KF5GlobalAccel)
if(KF5GlobalAccel_FOUND)
add_definitions( -DKGLOBALACCEL )
endif()
else()
message(WARNING "Qt 5 was not found on your system and Qt 6 will be used. You will not be able to use any features using KDE Frameworks.")
find_package(Qt6 CONFIG REQUIRED COMPONENTS Widgets WebEngineWidgets)
endif()
set(discord-screenaudio_SRC
@@ -66,7 +69,7 @@ add_subdirectory(submodules/rohrkabel)
add_executable(discord-screenaudio ${discord-screenaudio_SRC})
target_link_libraries(discord-screenaudio Qt5::Widgets Qt5::WebEngineWidgets rohrkabel)
target_link_libraries(discord-screenaudio Qt::Widgets Qt::WebEngineWidgets rohrkabel)
if(KF5Notifications_FOUND)
target_link_libraries(discord-screenaudio KF5::Notifications)

View File

@@ -11,7 +11,7 @@ Unlike a lot of other solutions, the audio here is directly fed into the
screenshare and not passed to the user microphone
([see explanation](#how-it-works)).
![Screenshot_20220925_112945](https://user-images.githubusercontent.com/48161361/192137080-33466cf7-8c56-4373-90c6-01ea74b6fb83.png)
![Screenshot_20221211_185028](https://user-images.githubusercontent.com/48161361/206920213-58a8091a-d8f9-4bb7-ae3d-3f8581b84d24.png)
The purpose of this project is **not** to provide an alternative to the original
Discord client. Rather, it should be used in addition to the original client in
@@ -50,6 +50,8 @@ You have multiple options:
### Requirements
- Basic building tools
- An up-to-date system (I can't guarantee that it works on Debian or Ubuntu
20/21)
- CMake
- Qt5 and QtWebEngine
- **PipeWire** (it currently doesn't work with PulseAudio)
@@ -57,7 +59,7 @@ You have multiple options:
- _Kf5Notifications (optional, for better notifications)_
- _KXMLGui and KGlobalAccel (optional, for keybinds)_
On Debian:
With apt:
`apt install -y build-essential cmake qtbase5-dev qtwebengine5-dev libkf5notifications-dev libkf5xmlgui-dev libkf5globalaccel-dev pkg-config libpipewire-0.3-dev git`
### Building

View File

@@ -4,6 +4,7 @@
<metadata_license>CC0-1.0</metadata_license>
<project_license>GPL-3.0+</project_license>
<name>discord-screenaudio</name>
<developer_name>Malte Jürgens</developer_name>
<releases>
<release version="${DISCORD_SCEENAUDIO_VERSION_FULL}" timestamp="${TIMESTAMP}" />
</releases>

View File

@@ -25,7 +25,7 @@ const getAudioDevice = async (nameOfAudioDevice) => {
return audioDevice;
};
function setGetDisplayMedia(overrideArgs = undefined) {
function setGetDisplayMedia(video = true, overrideArgs = undefined) {
const getDisplayMedia = async (...args) => {
var id;
try {
@@ -63,6 +63,7 @@ function setGetDisplayMedia(overrideArgs = undefined) {
: args || [{ video: true, audio: true }])
);
gdm.addTrack(track);
if (!video) for (const track of gdm.getVideoTracks()) track.enabled = false;
return gdm;
};
navigator.mediaDevices.getDisplayMedia = getDisplayMedia;
@@ -74,6 +75,52 @@ const clonedElements = [];
const hiddenElements = [];
let wasStreamActive = false;
function createButton(text, onClick) {
const button = document.createElement("button");
button.style.marginBottom = "20px";
button.classList =
"button-f2h6uQ lookFilled-yCfaCM colorBrand-I6CyqQ sizeSmall-wU2dO- grow-2sR_-F";
button.innerText = text;
button.addEventListener("click", onClick);
return button;
}
function createSwitch(text, enabled, onClick) {
const container = document.createElement("div");
container.style.marginBottom = "20px";
container.className = "labelRow-NnoUIp";
const label = document.createElement("label");
label.innerText = text;
label.className = "title-2yADjX";
container.appendChild(label);
const svg = document.createElement("div");
container.appendChild(svg);
function setSvgDisabled() {
svg.innerHTML = `<div class="container-1QtPKm default-colors disabled-3_3z1m" style="opacity: 0.3; background-color: rgb(114, 118, 125);"><svg class="slider-HJFN2i" viewBox="0 0 28 20" preserveAspectRatio="xMinYMid meet" aria-hidden="true" style="left: -3px;"><rect fill="white" x="4" y="0" height="20" width="20" rx="10"></rect><svg viewBox="0 0 20 20" fill="none"><path fill="rgba(114, 118, 125, 1)" d="M5.13231 6.72963L6.7233 5.13864L14.855 13.2704L13.264 14.8614L5.13231 6.72963Z"></path><path fill="rgba(114, 118, 125, 1)" d="M13.2704 5.13864L14.8614 6.72963L6.72963 14.8614L5.13864 13.2704L13.2704 5.13864Z"></path></svg></svg><input id="uid_75" type="checkbox" class="input-125oad" tabindex="-1" disabled=""></div>`;
}
function setSvgEnabled() {
svg.innerHTML = `<div class="container-1QtPKm default-colors checked-16gMAN" style="opacity: 1; background-color: rgb(59, 165, 92);"><svg class="slider-HJFN2i" viewBox="0 0 28 20" preserveAspectRatio="xMinYMid meet" aria-hidden="true" style="left: 12px;"><rect fill="white" x="4" y="0" height="20" width="20" rx="10"></rect><svg viewBox="0 0 20 20" fill="none"><path fill="rgba(59, 165, 92, 1)" d="M7.89561 14.8538L6.30462 13.2629L14.3099 5.25755L15.9009 6.84854L7.89561 14.8538Z"></path><path fill="rgba(59, 165, 92, 1)" d="M4.08643 11.0903L5.67742 9.49929L9.4485 13.2704L7.85751 14.8614L4.08643 11.0903Z"></path></svg></svg><input id="uid_74" type="checkbox" class="input-125oad" tabindex="0" checked=""></div>`;
}
function updateSvg() {
if (enabled) setSvgEnabled();
else setSvgDisabled();
}
container.addEventListener("click", () => {
enabled = !enabled;
updateSvg();
onClick(enabled);
});
updateSvg();
return container;
}
setInterval(() => {
const streamActive =
document.getElementsByClassName("panel-2ZFCRb activityPanel-9icbyU")
@@ -111,9 +158,16 @@ setInterval(() => {
const initialDisplay = el.style.display;
window.discordScreenaudioStartStream = (width, height, frameRate) => {
window.discordScreenaudioResolutionString = `${height}p ${frameRate}FPS`;
setGetDisplayMedia({
window.discordScreenaudioStartStream = (
video,
width,
height,
frameRate
) => {
window.discordScreenaudioResolutionString = video
? `${height}p ${frameRate}FPS`
: "Audio Only";
setGetDisplayMedia(video, {
audio: true,
video: { width, height, frameRate },
});
@@ -163,7 +217,7 @@ setInterval(() => {
document
.getElementById("keybinds-tab")
?.getElementsByClassName(
"container-3jbRo5 info-1hMolH fontSize16-3zr6Io browserNotice-1u-Y5o"
"container-3jbRo5 info-1hMolH browserNotice-1u-Y5o"
).length
) {
const el = document
@@ -171,26 +225,37 @@ setInterval(() => {
.getElementsByClassName("children-1xdcWE")[0];
const div = document.createElement("div");
div.style.marginBottom = "50px";
const button = document.createElement("button");
button.classList =
"button-f2h6uQ lookFilled-yCfaCM colorBrand-I6CyqQ sizeSmall-wU2dO- grow-2sR_-F";
button.innerText = "Edit Global Keybinds";
button.addEventListener("click", () => {
div.appendChild(
createButton("Edit Global Keybinds", () => {
console.log("!discord-screenaudio-keybinds");
});
div.appendChild(button);
})
);
el.innerHTML = "";
el.appendChild(div);
}
const muteBtn = document.getElementsByClassName(
const buttonContainer =
document.getElementsByClassName("container-YkUktl")[0];
if (!buttonContainer) {
console.log(
"dsa: Cannot locate Mute/Deafen/Settings button container, please report this on GitHub"
);
}
const muteBtn = buttonContainer
? buttonContainer.getElementsByClassName(
"button-12Fmur enabled-9OeuTA button-f2h6uQ lookBlank-21BCro colorBrand-I6CyqQ grow-2sR_-F"
)[0];
window.discordScreenaudioToggleMute = () => muteBtn.click();
const deafenBtn = document.getElementsByClassName(
)[0]
: null;
window.discordScreenaudioToggleMute = () => muteBtn && muteBtn.click();
const deafenBtn = buttonContainer
? buttonContainer.getElementsByClassName(
"button-12Fmur enabled-9OeuTA button-f2h6uQ lookBlank-21BCro colorBrand-I6CyqQ grow-2sR_-F"
)[1];
window.discordScreenaudioToggleDeafen = () => deafenBtn.click();
)[1]
: null;
window.discordScreenaudioToggleDeafen = () => deafenBtn && deafenBtn.click();
if (window.discordScreenaudioResolutionString) {
for (const el of document.getElementsByClassName(
@@ -199,6 +264,52 @@ setInterval(() => {
el.innerHTML = window.discordScreenaudioResolutionString;
}
}
const accountTab = document.getElementById("my-account-tab");
if (accountTab) {
const discordScreenaudioSettings = document.getElementById(
"discord-screenaudio-settings"
);
if (!discordScreenaudioSettings) {
const firstDivider = accountTab.getElementsByClassName(
"divider-3nqZNm marginTop40-Q4o1tS"
)[0];
if (firstDivider) {
const section = document.createElement("div");
section.className = "marginTop40-Q4o1tS";
section.id = "discord-screenaudio-settings";
const title = document.createElement("h2");
title.className =
"h1-3iMExa title-lXcL8p defaultColor-3Olr-9 defaultMarginh1-1UYutH";
title.innerText = "discord-screenaudio";
section.appendChild(title);
section.appendChild(
createButton("Edit Global Keybinds", () => {
console.log("!discord-screenaudio-keybinds");
})
);
section.appendChild(
createSwitch(
"Move discord-screenaudio to the system tray instead of closing",
window.discordScreenaudioTrayEnabled,
(enabled) => {
window.discordScreenaudioTrayEnabled = enabled;
console.log(`!discord-screenaudio-tray-${enabled}`);
}
)
);
const divider = document.createElement("div");
divider.className = "divider-3nqZNm marginTop40-Q4o1tS";
firstDivider.after(section);
section.after(divider);
}
}
}
}, 500);
// Fix for broken discord notifications after restart

View File

@@ -55,9 +55,14 @@ DiscordPage::DiscordPage(QWidget *parent) : QWebEnginePage(parent) {
injectScriptFile("userscript.js", ":/assets/userscript.js");
injectScriptText("version.js",
QString("window.discordScreenaudioVersion = '%1';")
.arg(QApplication::applicationVersion()));
injectScriptText("vars.js",
QString("window.discordScreenaudioVersion = '%1'; "
"window.discordScreenaudioTrayEnabled = %2;")
.arg(QApplication::applicationVersion())
.arg(MainWindow::instance()
->settings()
->value("trayIcon", false)
.toBool()));
#ifdef KXMLGUI
injectScriptText("xmlgui.js", "window.discordScreenaudioKXMLGUI = true;");
@@ -186,10 +191,8 @@ void DiscordPage::stopVirtmic() {
}
void DiscordPage::startVirtmic(QString target) {
if (target != "None") {
qDebug(virtmicLog) << "Starting Virtmic with target" << target;
m_virtmicProcess.start(QApplication::arguments()[0], {"--virtmic", target});
}
}
void DiscordPage::javaScriptConsoleMessage(
@@ -223,6 +226,10 @@ void DiscordPage::javaScriptConsoleMessage(
"(KXmlGui and KGlobalAccel are not available).",
QMessageBox::Ok);
#endif
} else if (message == "!discord-screenaudio-tray-true") {
MainWindow::instance()->setTrayIcon(true);
} else if (message == "!discord-screenaudio-tray-false") {
MainWindow::instance()->setTrayIcon(false);
} else if (message.startsWith("dsa: ")) {
qDebug(userscriptLog) << message.mid(5).toUtf8().constData();
} else {
@@ -230,16 +237,18 @@ void DiscordPage::javaScriptConsoleMessage(
}
}
void DiscordPage::startStream(QString target, uint width, uint height,
uint frameRate) {
void DiscordPage::startStream(bool video, bool audio, uint width, uint height,
uint frameRate, QString target) {
stopVirtmic();
startVirtmic(target);
startVirtmic(audio ? target : "[None]");
// Wait a bit for the virtmic to start
QTimer::singleShot(target == "None" ? 0 : 200, [=]() {
runJavaScript(QString("window.discordScreenaudioStartStream(%1, %2, %3);")
.arg(width)
.arg(height)
.arg(frameRate));
QTimer::singleShot(200, [=]() {
runJavaScript(
QString("window.discordScreenaudioStartStream(%1, %2, %3, %4);")
.arg(video)
.arg(video ? width : 32)
.arg(video ? height : 16)
.arg(video ? frameRate : 1));
});
}

View File

@@ -47,7 +47,8 @@ private:
private Q_SLOTS:
void featurePermissionRequested(const QUrl &securityOrigin,
QWebEnginePage::Feature feature);
void startStream(QString target, uint width, uint height, uint frameRate);
void startStream(bool video, bool audio, uint width, uint height,
uint frameRate, QString target);
};
// Will immediately get destroyed again but is needed for navigation to

View File

@@ -30,6 +30,9 @@ int main(int argc, char *argv[]) {
QCommandLineOption degubOption("remote-debugging",
"Open Chromium Remote Debugging on port 9222");
parser.addOption(degubOption);
QCommandLineOption notifySendOption(
"notify-send", "Use notify-send instead of QT/KF5 notifications");
parser.addOption(notifySendOption);
parser.process(app);
@@ -46,7 +49,7 @@ int main(int argc, char *argv[]) {
"--remote-debugging-port=9222 " +
qgetenv("QTWEBENGINE_CHROMIUM_FLAGS"));
MainWindow w;
MainWindow w(parser.isSet(notifySendOption));
w.show();
return app.exec();

View File

@@ -24,10 +24,14 @@
MainWindow *MainWindow::m_instance = nullptr;
MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) {
MainWindow::MainWindow(bool useNotifySend, QWidget *parent)
: QMainWindow(parent) {
assert(MainWindow::m_instance == nullptr);
MainWindow::m_instance = this;
m_useNotifySend = useNotifySend;
setupSettings();
setupWebView();
setupTrayIcon();
resize(1000, 700);
showMaximized();
}
@@ -40,22 +44,37 @@ void MainWindow::setupWebView() {
m_webView = new QWebEngineView(this);
m_webView->setPage(page);
#ifdef KNOTIFICATIONS
if (m_useKF5Notifications || m_useNotifySend)
QWebEngineProfile::defaultProfile()->setNotificationPresenter(
[&](std::unique_ptr<QWebEngineNotification> notificationInfo) {
KNotification *notification = new KNotification("discordNotification");
if (m_useNotifySend) {
auto title = notificationInfo->title();
auto message = notificationInfo->message();
auto image_path =
QString("/tmp/discord-screenaudio-%1.png").arg(title);
notificationInfo->icon().save(image_path);
QProcess::execute("notify-send",
{"--icon", image_path, "--app-name",
"discord-screenaudio", title, message});
} else if (m_useKF5Notifications) {
#ifdef KNOTIFICATIONS
KNotification *notification =
new KNotification("discordNotification");
notification->setTitle(notificationInfo->title());
notification->setText(notificationInfo->message());
notification->setPixmap(QPixmap::fromImage(notificationInfo->icon()));
notification->setPixmap(
QPixmap::fromImage(notificationInfo->icon()));
notification->setDefaultAction("View");
connect(notification, &KNotification::defaultActivated,
[&, notificationInfo = std::move(notificationInfo)]() {
notificationInfo->click();
show();
activateWindow();
});
notification->sendEvent();
});
#endif
}
});
setCentralWidget(m_webView);
}
@@ -71,6 +90,72 @@ void MainWindow::fullScreenRequested(
}
}
void MainWindow::closeEvent(QCloseEvent *event) { QApplication::quit(); }
void MainWindow::setupTrayIcon() {
if (m_settings->value("trayIcon", false).toBool() == false ||
m_trayIcon != nullptr)
return;
auto aboutAction = new QAction(
"discord-screenaudio v" + QString(DISCORD_SCEENAUDIO_VERSION_FULL), this);
aboutAction->setIcon(QIcon(":assets/de.shorsh.discord-screenaudio.png"));
aboutAction->setEnabled(false);
auto exitAction = new QAction("Exit", this);
connect(exitAction, &QAction::triggered, []() { QApplication::quit(); });
m_trayIconMenu = new QMenu(this);
m_trayIconMenu->addAction(aboutAction);
m_trayIconMenu->addAction(exitAction);
m_trayIcon = new QSystemTrayIcon(this);
m_trayIcon->setContextMenu(m_trayIconMenu);
m_trayIcon->setIcon(QIcon(":assets/de.shorsh.discord-screenaudio.png"));
m_trayIcon->show();
connect(m_trayIcon, &QSystemTrayIcon::activated, [this](auto reason) {
if (reason == QSystemTrayIcon::Trigger) {
if (isVisible()) {
hide();
} else {
show();
activateWindow();
}
}
});
}
void MainWindow::cleanTrayIcon() {
if (m_trayIcon == nullptr)
return;
m_trayIcon->hide();
m_trayIconMenu->deleteLater();
m_trayIcon->deleteLater();
m_trayIconMenu = nullptr;
m_trayIcon = nullptr;
}
void MainWindow::setupSettings() {
m_settings = new QSettings("maltejur", "discord-screenaudio", this);
m_settings->beginGroup("settings");
m_settings->endGroup();
}
QSettings *MainWindow::settings() const { return m_settings; }
void MainWindow::setTrayIcon(bool enabled) {
m_settings->setValue("trayIcon", enabled);
if (enabled) {
setupTrayIcon();
} else {
cleanTrayIcon();
}
}
void MainWindow::closeEvent(QCloseEvent *event) {
if (m_settings->value("trayIcon", false).toBool()) {
hide();
} else
QApplication::quit();
}
MainWindow *MainWindow::instance() { return m_instance; }

View File

@@ -3,8 +3,11 @@
#include "discordpage.h"
#include <QMainWindow>
#include <QMenu>
#include <QScopedPointer>
#include <QSettings>
#include <QString>
#include <QSystemTrayIcon>
#include <QVector>
#include <QWebEnginePage>
#include <QWebEngineProfile>
@@ -14,17 +17,33 @@ class MainWindow : public QMainWindow {
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = nullptr);
explicit MainWindow(bool useNotifySend = false, QWidget *parent = nullptr);
static MainWindow *instance();
QSettings *settings() const;
private:
void setupWebView();
void setupTrayIcon();
void cleanTrayIcon();
void setupSettings();
QWebEngineView *m_webView;
QWebEngineProfile *prepareProfile();
DiscordPage *m_discordPage;
void closeEvent(QCloseEvent *event) override;
QSystemTrayIcon *m_trayIcon = nullptr;
QMenu *m_trayIconMenu;
QSettings *m_settings;
bool m_wasMaximized;
static MainWindow *m_instance;
bool m_useNotifySend;
#ifdef KNOTIFICATIONS
bool m_useKF5Notifications = true;
#else
bool m_useKF5Notifications = false;
#endif
public Q_SLOTS:
void setTrayIcon(bool enabled);
private Q_SLOTS:
void fullScreenRequested(QWebEngineFullScreenRequest fullScreenRequest);

View File

@@ -12,53 +12,77 @@
StreamDialog::StreamDialog() : QWidget() {
setAttribute(Qt::WA_QuitOnClose, false);
{
auto layout = new QVBoxLayout(this);
layout->setSizeConstraint(QLayout::SetFixedSize);
auto targetLabel = new QLabel(this);
targetLabel->setText("Which app do you want to stream sound from?");
layout->addWidget(targetLabel);
m_videoGroupBox = new QGroupBox(this);
m_videoGroupBox->setTitle("Video");
m_videoGroupBox->setCheckable(true);
layout->addWidget(m_videoGroupBox);
auto targetHBox = new QHBoxLayout(this);
layout->addLayout(targetHBox);
{
auto videoLayout = new QVBoxLayout(this);
m_videoGroupBox->setLayout(videoLayout);
auto resolutionLabel = new QLabel(this);
resolutionLabel->setText("Resolution");
videoLayout->addWidget(resolutionLabel);
m_resolutionComboBox = new QComboBox(this);
m_resolutionComboBox->addItem("2160p", "3840x2160");
m_resolutionComboBox->addItem("1440p", "2560x1440");
m_resolutionComboBox->addItem("1080p", "1920x1080");
m_resolutionComboBox->addItem("720p", "1280x720");
m_resolutionComboBox->addItem("480p", "854x480");
m_resolutionComboBox->addItem("360p", "640x360");
m_resolutionComboBox->addItem("240p", "426x240");
m_resolutionComboBox->setCurrentText("720p");
videoLayout->addWidget(m_resolutionComboBox);
auto framerateLabel = new QLabel(this);
framerateLabel->setText("Framerate");
videoLayout->addWidget(framerateLabel);
m_framerateComboBox = new QComboBox(this);
m_framerateComboBox->addItem("144 FPS", 144);
m_framerateComboBox->addItem("60 FPS", 60);
m_framerateComboBox->addItem("30 FPS", 30);
m_framerateComboBox->addItem("15 FPS", 15);
m_framerateComboBox->addItem("5 FPS", 5);
m_framerateComboBox->setCurrentText("30 FPS");
videoLayout->addWidget(m_framerateComboBox);
}
m_audioGroupBox = new QGroupBox(this);
m_audioGroupBox->setCheckable(true);
m_audioGroupBox->setTitle("Audio");
layout->addWidget(m_audioGroupBox);
{
auto audioLayout = new QVBoxLayout(this);
m_audioGroupBox->setLayout(audioLayout);
auto targetLabel = new QLabel(this);
targetLabel->setText("Audio Source");
audioLayout->addWidget(targetLabel);
{
auto targetLayout = new QHBoxLayout(this);
audioLayout->addLayout(targetLayout);
m_targetComboBox = new QComboBox(this);
updateTargets();
targetHBox->addWidget(m_targetComboBox);
targetLayout->addWidget(m_targetComboBox);
auto refreshTargetsButton = new QPushButton(this);
refreshTargetsButton->setFixedSize(30, 30);
refreshTargetsButton->setIcon(QIcon::fromTheme("view-refresh"));
connect(refreshTargetsButton, &QPushButton::clicked, this,
&StreamDialog::updateTargets);
targetHBox->addWidget(refreshTargetsButton);
auto qualityLabel = new QLabel(this);
qualityLabel->setText("Stream Quality");
layout->addWidget(qualityLabel);
auto qualityHBox = new QHBoxLayout(this);
layout->addLayout(qualityHBox);
m_qualityResolutionComboBox = new QComboBox(this);
m_qualityResolutionComboBox->addItem("2160p", "3840x2160");
m_qualityResolutionComboBox->addItem("1440p", "2560x1440");
m_qualityResolutionComboBox->addItem("1080p", "1920x1080");
m_qualityResolutionComboBox->addItem("720p", "1280x720");
m_qualityResolutionComboBox->addItem("480p", "854x480");
m_qualityResolutionComboBox->addItem("360p", "640x360");
m_qualityResolutionComboBox->addItem("240p", "426x240");
m_qualityResolutionComboBox->setCurrentText("720p");
qualityHBox->addWidget(m_qualityResolutionComboBox);
m_qualityFPSComboBox = new QComboBox(this);
m_qualityFPSComboBox->addItem("144 FPS", 144);
m_qualityFPSComboBox->addItem("60 FPS", 60);
m_qualityFPSComboBox->addItem("30 FPS", 30);
m_qualityFPSComboBox->addItem("15 FPS", 15);
m_qualityFPSComboBox->addItem("5 FPS", 5);
m_qualityFPSComboBox->setCurrentText("30 FPS");
qualityHBox->addWidget(m_qualityFPSComboBox);
targetLayout->addWidget(refreshTargetsButton);
}
}
auto button = new QPushButton(this);
button->setText("Start Stream");
@@ -66,16 +90,18 @@ StreamDialog::StreamDialog() : QWidget() {
layout->addWidget(button, Qt::AlignRight | Qt::AlignBottom);
setLayout(layout);
}
setWindowTitle("discord-screenaudio Stream Dialog");
}
void StreamDialog::startStream() {
auto resolution =
m_qualityResolutionComboBox->currentData().toString().split('x');
emit requestedStreamStart(m_targetComboBox->currentText(),
auto resolution = m_resolutionComboBox->currentData().toString().split('x');
emit requestedStreamStart(m_videoGroupBox->isChecked(),
m_audioGroupBox->isChecked(),
resolution[0].toUInt(), resolution[1].toUInt(),
m_qualityFPSComboBox->currentData().toUInt());
m_framerateComboBox->currentData().toUInt(),
m_targetComboBox->currentText());
setHidden(true);
}
@@ -83,7 +109,6 @@ void StreamDialog::updateTargets() {
auto lastTarget = m_targetComboBox->currentText();
m_targetComboBox->clear();
m_targetComboBox->addItem("[None]");
m_targetComboBox->addItem("[All Desktop Audio]");
for (auto target : Virtmic::getTargets()) {
m_targetComboBox->addItem(target);

View File

@@ -2,6 +2,7 @@
#include <QComboBox>
#include <QDialog>
#include <QGroupBox>
#include <QWidget>
class StreamDialog : public QWidget {
@@ -12,12 +13,14 @@ public:
private:
QComboBox *m_targetComboBox;
QComboBox *m_qualityResolutionComboBox;
QComboBox *m_qualityFPSComboBox;
QComboBox *m_resolutionComboBox;
QComboBox *m_framerateComboBox;
QGroupBox *m_videoGroupBox;
QGroupBox *m_audioGroupBox;
Q_SIGNALS:
void requestedStreamStart(QString target, uint width, uint height,
uint frameRate);
void requestedStreamStart(bool video, bool audio, uint width, uint height,
uint frameRate, QString target);
public Q_SLOTS:
void updateTargets();

View File

@@ -22,7 +22,13 @@ QVector<QString> getTargets() {
if (global.type == pipewire::node::type) {
auto node = reg.bind<pipewire::node>(global.id);
auto info = node.info();
auto name = QString::fromStdString(info.props["application.name"]);
QString name;
if (info.props.count("application.name") &&
info.props["application.name"] != "")
name = QString::fromStdString(info.props["application.name"]);
else
name = QString::fromStdString(
info.props["application.process.binary"]);
if (name != "" && !EXCLUDE_TARGETS.contains(name) &&
!targets.contains(name)) {
@@ -67,7 +73,12 @@ void start(QString _target) {
continue;
auto &parent = nodes.at(parent_id);
auto name = parent.props["application.name"];
std::string name;
if (parent.props.count("application.name") &&
parent.props["application.name"] != "")
name = parent.props["application.name"];
else
name = parent.props["application.process.binary"];
if (name == target ||
(target == "[All Desktop Audio]" &&
@@ -78,8 +89,7 @@ void start(QString _target) {
core.create<pipewire::link_factory>(
{fl ? virt_fl->info().id : virt_fr->info().id, port_id}));
qDebug(virtmicLog) << QString("Link: %1:%2 -> %3")
.arg(QString::fromStdString(
parent.props["application.name"]))
.arg(QString::fromStdString(name))
.arg(port_id)
.arg(fl ? virt_fl->info().id
: virt_fr->info().id)
@@ -112,11 +122,17 @@ void start(QString _target) {
[&](const pipewire::global &global) {
if (global.type == pipewire::node::type) {
auto node = reg.bind<pipewire::node>(global.id);
if (!node.info().props.count("application.name"))
auto info = node.info();
std::string name;
if (info.props.count("application.name") &&
info.props["application.name"] != "")
name = info.props["application.name"];
else if (info.props.count("application.process.binary")) {
name = info.props["application.process.binary"];
} else
return;
qDebug(virtmicLog) << QString("Added: %1")
.arg(QString::fromStdString(
node.info().props["application.name"]))
.arg(QString::fromStdString(name))
.toUtf8()
.data();
@@ -152,9 +168,14 @@ void start(QString _target) {
[&](const std::uint32_t id) {
if (nodes.count(id)) {
auto info = nodes.at(id);
std::string name;
if (info.props.count("application.name") &&
info.props["application.name"] != "")
name = info.props["application.name"];
else
name = info.props["application.process.binary"];
qDebug(virtmicLog) << QString("Removed: %1")
.arg(QString::fromStdString(
info.props["application.name"].data()))
.arg(QString::fromStdString(name))
.toUtf8()
.data();
nodes.erase(id);