Compare commits

...

12 Commits

Author SHA1 Message Date
Malte Jürgens
c4f6c33f9d display correct steam resolution 2022-09-21 17:32:41 +02:00
Malte Jürgens
758ffa8cb5 Update README (#48) 2022-09-18 12:04:04 +02:00
Malte Jürgens
753da8b1d3 automatically detect if kf5notifications is installed 2022-08-29 14:23:00 +02:00
Vortex Acherontic
5f37ab314f Changed location of openSUSE package (#34) 2022-08-21 07:21:18 +02:00
Malte Jürgens
c7bff3f428 add refresh button to stream dialog 2022-08-04 15:51:03 +02:00
Malte Jürgens
34e23f7187 cmake: use version from text file without suffix 2022-08-04 15:47:04 +02:00
Malte Jürgens
c325dd6798 allow sharing all desktop audio 2022-08-04 15:27:39 +02:00
Malte Jürgens
b42fde85d1 only allow to share application audio 2022-08-04 14:57:09 +02:00
Malte Jürgens
cc379ada85 fix metainfo again 2022-08-04 11:10:29 +02:00
Malte Jürgens
fdb6145bbd improve logging 2022-08-02 12:41:32 +02:00
Malte Jürgens
8bd4d1896a poll for enumerateDevices() to get available 2022-08-02 11:56:08 +02:00
Malte Jürgens
1a2d09fe3d remove useless main.h again 2022-08-02 11:46:37 +02:00
14 changed files with 132 additions and 59 deletions

1
.gitignore vendored
View File

@@ -1 +1,2 @@
/build
.vscode

View File

@@ -18,10 +18,9 @@ find_package(Qt5 CONFIG REQUIRED COMPONENTS
WebEngineWidgets
)
option(KF5NOTIFICATIONS "Use KF5Notifications for enhanced system notifications" ON)
if(KF5NOTIFICATIONS)
add_definitions( -DKF5NOTIFICATIONS )
find_package(KF5Notifications)
find_package(KF5Notifications)
if(KF5Notifications_FOUND)
add_definitions( -DKNOTIFICATIONS )
endif()
set(discord-screenaudio_SRC
@@ -30,6 +29,7 @@ set(discord-screenaudio_SRC
src/virtmic.cpp
src/discordpage.cpp
src/streamdialog.cpp
src/log.cpp
resources.qrc
)
@@ -58,7 +58,7 @@ add_executable(discord-screenaudio ${discord-screenaudio_SRC})
target_link_libraries(discord-screenaudio Qt5::Widgets Qt5::WebEngineWidgets rohrkabel)
if(KF5NOTIFICATIONS)
if(KF5Notifications_FOUND)
target_link_libraries(discord-screenaudio KF5::Notifications)
install(FILES assets/discord-screenaudio.notifyrc DESTINATION ${CMAKE_INSTALL_PREFIX}/share/knotifications5)
endif()

View File

@@ -1,12 +1,16 @@
# discord-screenaudio
A very WIP custom discord client that supports streaming with audio on Linux,
made possible by the
A custom discord client that supports streaming with audio on Linux, made
possible by the
[great work](https://github.com/edisionnano/Screenshare-with-audio-on-Discord-with-Linux)
of [@edisionnano](https://github.com/edisionnano) and the
[Rohrkabel library](https://github.com/Soundux/rohrkabel) by
[@Curve](https://github.com/Curve).
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_20220718_194357](https://user-images.githubusercontent.com/48161361/179571245-11ea05f3-fb5e-4aef-9132-2736e122ef04.png)
The purpose of this project is **not** to provide an alternative to the original
@@ -23,8 +27,9 @@ told you and it's not my fault.
## Known Issues
- Only works with **PipeWire**
- Can only share primary screen on X11 (no other screens or specific applications)
(see [#1](https://github.com/maltejur/discord-screenaudio/issues/1))
- Can only share primary screen on X11 (no other screens or specific
applications) (see
[#1](https://github.com/maltejur/discord-screenaudio/issues/1))
## Installation
@@ -36,7 +41,7 @@ You have multiple options:
[`discord-screenaudio`](https://aur.archlinux.org/packages/discord-screenaudio)
from the AUR
- If you are on openSUSE, you can use the
[Open Build Service package](https://software.opensuse.org/download.html?project=home%3AVortexAcherontic&package=discord-screenaudio)
[Open Build Service package](https://software.opensuse.org//download.html?project=games%3Atools&package=discord-screenaudio)
by [@VortexAcherontic](https://github.com/VortexAcherontic)
- You can [build it yourself](#building-from-source)

View File

@@ -8,13 +8,18 @@
<release version="${DISCORD_SCEENAUDIO_VERSION_FULL}" timestamp="${TIMESTAMP}" />
</releases>
<summary>A very WIP custom discord client that supports streaming with audio on Linux</summary>
<summary>A custom discord client that supports streaming with audio on Linux</summary>
<description>
<p>A very WIP custom discord client that supports streaming with audio on Linux, made possible by the great work of @edisionnano and the Rohrkabel library by @Curve.</p>
<p>A custom discord client that supports streaming with audio on Linux, made possible by the great work of @edisionnano and the Rohrkabel library by @Curve.</p>
<p>Unlike a lot of other solutions, the audio here is directly fed into the screenshare and not passed to the user microphone.</p>
<p>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 case you want to stream something, maybe used with a second account. For anything else, this client has way too many things that work less well than in the original client.</p>
<p>Technically this could be against Discord's TOS, so be warned. Discord probably won't ban you for using this, but if they do I told you and it's not my fault.</p>
<p>Known issues:<ul><li>Only works with PipeWire</li><li>Can only share primary screen on X11</li></ul></p>
<p>Known issues:</p>
<ul>
<li>Only works with PipeWire</li>
<li>Can only share primary screen on X11</li>
</ul>
</description>
<launchable type="desktop-id">

View File

@@ -11,9 +11,17 @@ const getAudioDevice = async (nameOfAudioDevice) => {
await navigator.mediaDevices.getUserMedia({
audio: true,
});
await sleep(500);
let devices = await navigator.mediaDevices.enumerateDevices();
let audioDevice = devices.find(({ label }) => label === nameOfAudioDevice);
let audioDevice;
while (audioDevice === undefined) {
let devices = await navigator.mediaDevices.enumerateDevices();
audioDevice = devices.find(({ label }) => label === nameOfAudioDevice);
if (!audioDevice)
console.log(
`dsa: Did not find '${nameOfAudioDevice}', trying again in 100ms`
);
await sleep(100);
}
console.log(`dsa: Found '${nameOfAudioDevice}'`);
return audioDevice;
};
@@ -104,6 +112,7 @@ setInterval(() => {
const initialDisplay = el.style.display;
window.discordScreenaudioStartStream = (width, height, frameRate) => {
window.discordScreenaudioResolutionString = `${height}p ${frameRate}FPS`;
setGetDisplayMedia({
audio: true,
video: { width, height, frameRate },
@@ -139,6 +148,14 @@ setInterval(() => {
// Remove stream settings if stream is active
document.getElementById("manage-streams-change-windows")?.remove();
document.querySelector(`[aria-label="Stream Settings"]`)?.remove();
if (window.discordScreenaudioResolutionString) {
for (const el of document.getElementsByClassName(
"qualityIndicator-39wQDy"
)) {
el.innerHTML = window.discordScreenaudioResolutionString;
}
}
}, 500);
// Fix for broken discord notifications after restart

View File

@@ -1,4 +1,5 @@
#include "discordpage.h"
#include "log.h"
#include "virtmic.h"
#include <QApplication>
@@ -43,7 +44,7 @@ DiscordPage::DiscordPage(QWidget *parent) : QWebEnginePage(parent) {
}
void DiscordPage::injectScript(QString source) {
qDebug() << "[main ] Injecting " << source;
qDebug(mainLog) << "Injecting " << source;
QFile userscript(source);
@@ -87,8 +88,8 @@ void DiscordPage::featurePermissionRequested(const QUrl &securityOrigin,
if (feature == QWebEnginePage::Feature::MediaAudioCapture) {
if (m_virtmicProcess.state() == QProcess::NotRunning) {
qDebug() << "[virtmic] Starting Virtmic with no target to make sure "
"Discord can find all the audio devices";
qDebug(virtmicLog) << "Starting Virtmic with no target to make sure "
"Discord can find all the audio devices";
m_virtmicProcess.start(QApplication::arguments()[0],
{"--virtmic", "None"});
}
@@ -119,7 +120,7 @@ QWebEnginePage *DiscordPage::createWindow(QWebEnginePage::WebWindowType type) {
void DiscordPage::stopVirtmic() {
if (m_virtmicProcess.state() == QProcess::Running) {
qDebug() << "[virtmic] Stopping Virtmic";
qDebug(virtmicLog) << "Stopping Virtmic";
m_virtmicProcess.kill();
m_virtmicProcess.waitForFinished();
}
@@ -127,7 +128,7 @@ void DiscordPage::stopVirtmic() {
void DiscordPage::startVirtmic(QString target) {
if (target != "None") {
qDebug() << "[virtmic] Starting Virtmic with target" << target;
qDebug(virtmicLog) << "Starting Virtmic with target" << target;
m_virtmicProcess.start(QApplication::arguments()[0], {"--virtmic", target});
}
}
@@ -143,8 +144,10 @@ void DiscordPage::javaScriptConsoleMessage(
m_streamDialog.updateTargets();
} else if (message == "!discord-screenaudio-stream-stopped") {
stopVirtmic();
} else if (message.startsWith("dsa: ")) {
qDebug(userscriptLog) << message.mid(5).toUtf8().constData();
} else {
qDebug() << "[discord]" << message;
qDebug(discordLog) << message;
}
}

6
src/log.cpp Normal file
View File

@@ -0,0 +1,6 @@
#include "log.h"
Q_LOGGING_CATEGORY(mainLog, "main");
Q_LOGGING_CATEGORY(discordLog, "discord");
Q_LOGGING_CATEGORY(userscriptLog, "userscript");
Q_LOGGING_CATEGORY(virtmicLog, "virtmic");

8
src/log.h Normal file
View File

@@ -0,0 +1,8 @@
#pragma once
#include <QLoggingCategory>
Q_DECLARE_LOGGING_CATEGORY(mainLog);
Q_DECLARE_LOGGING_CATEGORY(discordLog);
Q_DECLARE_LOGGING_CATEGORY(userscriptLog);
Q_DECLARE_LOGGING_CATEGORY(virtmicLog);

View File

@@ -1,10 +1,9 @@
#include "main.h"
#include "mainwindow.h"
#include "virtmic.h"
#include <QApplication>
#include <QCommandLineParser>
#include <QLoggingCategory>
int main(int argc, char *argv[]) {
QApplication app(argc, argv);
@@ -14,6 +13,8 @@ int main(int argc, char *argv[]) {
QApplication::setApplicationVersion(DISCORD_SCEENAUDIO_VERSION_FULL);
QApplication::setDesktopFileName("de.shorsh.discord-screenaudio");
qSetMessagePattern("[%{category}] %{message}");
QCommandLineParser parser;
parser.setApplicationDescription(
"Custom Discord client with the ability to stream audio on Linux");

View File

@@ -1,6 +0,0 @@
#pragma once
#include <QString>
int main(int argc, char *argv[]);
void addToEnvVar(const char *var, const char *val);

View File

@@ -1,7 +1,7 @@
#include "mainwindow.h"
#include "virtmic.h"
#ifdef KF5NOTIFICATIONS
#ifdef KNOTIFICATIONS
#include <KNotification>
#endif
@@ -36,7 +36,7 @@ void MainWindow::setupWebView() {
m_webView = new QWebEngineView(this);
m_webView->setPage(page);
#ifdef KF5NOTIFICATIONS
#ifdef KNOTIFICATIONS
QWebEngineProfile::defaultProfile()->setNotificationPresenter(
[&](std::unique_ptr<QWebEngineNotification> notificationInfo) {
KNotification *notification = new KNotification("discordNotification");

View File

@@ -19,9 +19,19 @@ StreamDialog::StreamDialog() : QWidget() {
targetLabel->setText("Which app do you want to stream sound from?");
layout->addWidget(targetLabel);
auto targetHBox = new QHBoxLayout(this);
layout->addLayout(targetHBox);
m_targetComboBox = new QComboBox(this);
updateTargets();
layout->addWidget(m_targetComboBox);
targetHBox->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");
@@ -70,9 +80,17 @@ void StreamDialog::startStream() {
}
void StreamDialog::updateTargets() {
auto lastTarget = m_targetComboBox->currentText();
m_targetComboBox->clear();
m_targetComboBox->addItem("None");
m_targetComboBox->addItem("[None]");
m_targetComboBox->addItem("[All Desktop Audio]");
for (auto target : Virtmic::getTargets()) {
m_targetComboBox->addItem(target);
}
if (m_targetComboBox->findText(lastTarget) != -1)
m_targetComboBox->setCurrentText(lastTarget);
else
m_targetComboBox->setCurrentText("[All Desktop Audio]");
}

View File

@@ -1,10 +1,13 @@
#include "virtmic.h"
#include "log.h"
#include <rohrkabel/loop/main.hpp>
#include <rohrkabel/registry/registry.hpp>
namespace Virtmic {
const QStringList EXCLUDE_TARGETS{"Chromium input", "discord-screenaudio"};
QVector<QString> getTargets() {
auto main_loop = pipewire::main_loop();
auto context = pipewire::context(main_loop);
@@ -19,11 +22,11 @@ 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"]);
if (info.props.count("node.name")) {
auto name = QString::fromStdString(info.props["node.name"]);
if (!targets.contains(name))
targets.append(name);
if (name != "" && !EXCLUDE_TARGETS.contains(name) &&
!targets.contains(name)) {
targets.append(name);
}
}
});
@@ -64,20 +67,24 @@ void start(QString _target) {
continue;
auto &parent = nodes.at(parent_id);
auto name = parent.props["application.name"];
if (parent.props["node.name"].find(target) != std::string::npos) {
std::cout << "[virtmic] "
<< "Link : " << target << ":" << port_id << " -> ";
if (port.info().props["audio.channel"] == "FL") {
links.emplace(port_id, core.create<pipewire::link_factory>(
{virt_fl->info().id, port_id}));
std::cout << "[virtmic] " << virt_fl->info().id << std::endl;
} else {
links.emplace(port_id, core.create<pipewire::link_factory>(
{virt_fr->info().id, port_id}));
std::cout << "[virtmic] " << virt_fr->info().id << std::endl;
}
if (name == target ||
(target == "[All Desktop Audio]" &&
!EXCLUDE_TARGETS.contains(QString::fromStdString(name)))) {
auto fl = port.info().props["audio.channel"] == "FL";
links.emplace(
port_id,
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(port_id)
.arg(fl ? virt_fl->info().id
: virt_fr->info().id)
.toUtf8()
.data();
}
}
};
@@ -93,7 +100,7 @@ void start(QString _target) {
pipewire::node::type, pipewire::node::version,
pipewire::update_strategy::none);
if (target == "None") {
if (target == "[None]") {
while (true) {
main_loop.run();
}
@@ -105,9 +112,13 @@ void start(QString _target) {
[&](const pipewire::global &global) {
if (global.type == pipewire::node::type) {
auto node = reg.bind<pipewire::node>(global.id);
std::cout << "[virtmic] "
<< "Added : " << node.info().props["node.name"]
<< std::endl;
if (!node.info().props.count("application.name"))
return;
qDebug(virtmicLog) << QString("Added: %1")
.arg(QString::fromStdString(
node.info().props["application.name"]))
.toUtf8()
.data();
if (!nodes.count(global.id)) {
nodes.emplace(global.id, node.info());
@@ -141,8 +152,11 @@ void start(QString _target) {
[&](const std::uint32_t id) {
if (nodes.count(id)) {
auto info = nodes.at(id);
std::cout << "[virtmic] "
<< "Removed: " << info.props["node.name"] << std::endl;
qDebug(virtmicLog) << QString("Removed: %1")
.arg(QString::fromStdString(
info.props["application.name"].data()))
.toUtf8()
.data();
nodes.erase(id);
}
if (ports.count(id)) {

View File

@@ -138,11 +138,12 @@ function(determine_version source_dir var_prefix)
# | exact tag | 0.9.0 |
# | exact tag, dirty | 0.9.0+git20180604 |
# | after tag | 0.9.0+git20180604.1.085039f |
# | no tag, version.txt | 0.9.0+git20180604.2ee02af |
# | no tag, version.txt | 0.9.0 |
# | no tag, no version.txt| 0.0.0+git20180604.2ee02af |
string(TIMESTAMP date "%Y%m%d" UTC)
set(version_suffix "")
if (NOT ${local_prefix}_VERSION_NCOMMITS EQUAL 0)
if (has_version_txt)
elseif (NOT ${local_prefix}_VERSION_NCOMMITS EQUAL 0)
# 0.9.0+git20150604.4.289818b
string(APPEND version_suffix "+git${date}.${${local_prefix}_VERSION_NCOMMITS}.${${local_prefix}_VERSION_GIT_REV}")
elseif ((NOT has_release_tag) AND ((NOT has_version_txt) OR ("${base_version}" STREQUAL "0.0.0") OR (NOT "${revision}" STREQUAL "")))