Compare commits
17 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f2de080e1b | ||
|
|
f4a60f281d | ||
|
|
d693535d53 | ||
|
|
e41af697f7 | ||
|
|
b0a8815bb8 | ||
|
|
150fd4364e | ||
|
|
a0a2924796 | ||
|
|
3c48621427 | ||
|
|
9faabe1f3e | ||
|
|
68473d04d8 | ||
|
|
7c8f72b8d8 | ||
|
|
27cdd9f9a5 | ||
|
|
d184823ee9 | ||
|
|
dd2beed4eb | ||
|
|
3740553aba | ||
|
|
555f5c62f5 | ||
|
|
af3c62c263 |
@@ -23,6 +23,16 @@ if(KF5Notifications_FOUND)
|
|||||||
add_definitions( -DKNOTIFICATIONS )
|
add_definitions( -DKNOTIFICATIONS )
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
find_package(KF5XmlGui)
|
||||||
|
if(KF5XmlGui_FOUND)
|
||||||
|
add_definitions( -DKXMLGUI )
|
||||||
|
endif()
|
||||||
|
|
||||||
|
find_package(KF5GlobalAccel)
|
||||||
|
if(KF5GlobalAccel_FOUND)
|
||||||
|
add_definitions( -DKGLOBALACCEL )
|
||||||
|
endif()
|
||||||
|
|
||||||
set(discord-screenaudio_SRC
|
set(discord-screenaudio_SRC
|
||||||
src/main.cpp
|
src/main.cpp
|
||||||
src/mainwindow.cpp
|
src/mainwindow.cpp
|
||||||
@@ -62,6 +72,12 @@ if(KF5Notifications_FOUND)
|
|||||||
target_link_libraries(discord-screenaudio KF5::Notifications)
|
target_link_libraries(discord-screenaudio KF5::Notifications)
|
||||||
install(FILES assets/discord-screenaudio.notifyrc DESTINATION ${CMAKE_INSTALL_PREFIX}/share/knotifications5)
|
install(FILES assets/discord-screenaudio.notifyrc DESTINATION ${CMAKE_INSTALL_PREFIX}/share/knotifications5)
|
||||||
endif()
|
endif()
|
||||||
|
if(KF5XmlGui_FOUND)
|
||||||
|
target_link_libraries(discord-screenaudio KF5::XmlGui)
|
||||||
|
endif()
|
||||||
|
if(KF5GlobalAccel_FOUND)
|
||||||
|
target_link_libraries(discord-screenaudio KF5::GlobalAccel)
|
||||||
|
endif()
|
||||||
|
|
||||||
install(TARGETS discord-screenaudio DESTINATION bin)
|
install(TARGETS discord-screenaudio DESTINATION bin)
|
||||||
install(FILES assets/de.shorsh.discord-screenaudio.png DESTINATION ${CMAKE_INSTALL_PREFIX}/share/icons/hicolor/256x256/apps)
|
install(FILES assets/de.shorsh.discord-screenaudio.png DESTINATION ${CMAKE_INSTALL_PREFIX}/share/icons/hicolor/256x256/apps)
|
||||||
|
|||||||
@@ -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
|
screenshare and not passed to the user microphone
|
||||||
([see explanation](#how-it-works)).
|
([see explanation](#how-it-works)).
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
The purpose of this project is **not** to provide an alternative to the original
|
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
|
Discord client. Rather, it should be used in addition to the original client in
|
||||||
@@ -51,12 +51,14 @@ You have multiple options:
|
|||||||
|
|
||||||
- Basic building tools
|
- Basic building tools
|
||||||
- CMake
|
- CMake
|
||||||
- Qt5, QtWebEngine and Kf5Notifications
|
- Qt5 and QtWebEngine
|
||||||
- **PipeWire** (it currently doesn't work with PulseAudio)
|
- **PipeWire** (it currently doesn't work with PulseAudio)
|
||||||
- Git
|
- Git
|
||||||
|
- _Kf5Notifications (optional, for better notifications)_
|
||||||
|
- _KXMLGui and KGlobalAccel (optional, for keybinds)_
|
||||||
|
|
||||||
On Debian:
|
On Debian:
|
||||||
`apt install -y build-essential cmake qtbase5-dev qtwebengine5-dev libkf5notifications-dev pkg-config libpipewire-0.3-dev git`
|
`apt install -y build-essential cmake qtbase5-dev qtwebengine5-dev libkf5notifications-dev libkf5xmlgui-dev libkf5globalaccel-dev pkg-config libpipewire-0.3-dev git`
|
||||||
|
|
||||||
### Building
|
### Building
|
||||||
|
|
||||||
|
|||||||
@@ -2,5 +2,7 @@
|
|||||||
Type=Application
|
Type=Application
|
||||||
Name=discord-screenaudio
|
Name=discord-screenaudio
|
||||||
Exec=discord-screenaudio
|
Exec=discord-screenaudio
|
||||||
|
Comment=A custom discord client that supports streaming with audio on Linux.
|
||||||
Icon=de.shorsh.discord-screenaudio
|
Icon=de.shorsh.discord-screenaudio
|
||||||
|
Categories=Network;InstantMessaging;
|
||||||
Terminal=false
|
Terminal=false
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
<metadata_license>CC0-1.0</metadata_license>
|
<metadata_license>CC0-1.0</metadata_license>
|
||||||
<project_license>GPL-3.0+</project_license>
|
<project_license>GPL-3.0+</project_license>
|
||||||
<name>discord-screenaudio</name>
|
<name>discord-screenaudio</name>
|
||||||
|
<developer_name>Malte Jürgens</developer_name>
|
||||||
<releases>
|
<releases>
|
||||||
<release version="${DISCORD_SCEENAUDIO_VERSION_FULL}" timestamp="${TIMESTAMP}" />
|
<release version="${DISCORD_SCEENAUDIO_VERSION_FULL}" timestamp="${TIMESTAMP}" />
|
||||||
</releases>
|
</releases>
|
||||||
@@ -29,7 +30,7 @@
|
|||||||
<screenshots>
|
<screenshots>
|
||||||
<screenshot type="default">
|
<screenshot type="default">
|
||||||
<image>
|
<image>
|
||||||
https://user-images.githubusercontent.com/48161361/179571245-11ea05f3-fb5e-4aef-9132-2736e122ef04.png
|
https://user-images.githubusercontent.com/48161361/192137080-33466cf7-8c56-4373-90c6-01ea74b6fb83.png
|
||||||
</image>
|
</image>
|
||||||
</screenshot>
|
</screenshot>
|
||||||
</screenshots>
|
</screenshots>
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ const getAudioDevice = async (nameOfAudioDevice) => {
|
|||||||
return audioDevice;
|
return audioDevice;
|
||||||
};
|
};
|
||||||
|
|
||||||
function setGetDisplayMedia(overrideArgs = undefined) {
|
function setGetDisplayMedia(video = true, overrideArgs = undefined) {
|
||||||
const getDisplayMedia = async (...args) => {
|
const getDisplayMedia = async (...args) => {
|
||||||
var id;
|
var id;
|
||||||
try {
|
try {
|
||||||
@@ -63,6 +63,7 @@ function setGetDisplayMedia(overrideArgs = undefined) {
|
|||||||
: args || [{ video: true, audio: true }])
|
: args || [{ video: true, audio: true }])
|
||||||
);
|
);
|
||||||
gdm.addTrack(track);
|
gdm.addTrack(track);
|
||||||
|
if (!video) for (const track of gdm.getVideoTracks()) track.enabled = false;
|
||||||
return gdm;
|
return gdm;
|
||||||
};
|
};
|
||||||
navigator.mediaDevices.getDisplayMedia = getDisplayMedia;
|
navigator.mediaDevices.getDisplayMedia = getDisplayMedia;
|
||||||
@@ -111,9 +112,16 @@ setInterval(() => {
|
|||||||
|
|
||||||
const initialDisplay = el.style.display;
|
const initialDisplay = el.style.display;
|
||||||
|
|
||||||
window.discordScreenaudioStartStream = (width, height, frameRate) => {
|
window.discordScreenaudioStartStream = (
|
||||||
window.discordScreenaudioResolutionString = `${height}p ${frameRate}FPS`;
|
video,
|
||||||
setGetDisplayMedia({
|
width,
|
||||||
|
height,
|
||||||
|
frameRate
|
||||||
|
) => {
|
||||||
|
window.discordScreenaudioResolutionString = video
|
||||||
|
? `${height}p ${frameRate}FPS`
|
||||||
|
: "Audio Only";
|
||||||
|
setGetDisplayMedia(video, {
|
||||||
audio: true,
|
audio: true,
|
||||||
video: { width, height, frameRate },
|
video: { width, height, frameRate },
|
||||||
});
|
});
|
||||||
@@ -135,12 +143,21 @@ setInterval(() => {
|
|||||||
document.getElementsByClassName("dirscordScreenaudioAboutText").length == 0
|
document.getElementsByClassName("dirscordScreenaudioAboutText").length == 0
|
||||||
) {
|
) {
|
||||||
for (const el of document.getElementsByClassName("info-3pQQBb")) {
|
for (const el of document.getElementsByClassName("info-3pQQBb")) {
|
||||||
const aboutEl = document.createElement("div");
|
let aboutEl;
|
||||||
|
if (window.discordScreenaudioKXMLGUI) {
|
||||||
|
aboutEl = document.createElement("a");
|
||||||
|
aboutEl.addEventListener("click", () => {
|
||||||
|
console.log("!discord-screenaudio-about");
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
aboutEl = document.createElement("div");
|
||||||
|
}
|
||||||
aboutEl.innerText = `discord-screenaudio ${window.discordScreenaudioVersion}`;
|
aboutEl.innerText = `discord-screenaudio ${window.discordScreenaudioVersion}`;
|
||||||
aboutEl.style.fontSize = "12px";
|
aboutEl.style.fontSize = "12px";
|
||||||
aboutEl.style.color = "var(--text-muted)";
|
aboutEl.style.color = "var(--text-muted)";
|
||||||
aboutEl.style.textTransform = "none";
|
aboutEl.style.textTransform = "none";
|
||||||
aboutEl.classList.add("dirscordScreenaudioAboutText");
|
aboutEl.classList.add("dirscordScreenaudioAboutText");
|
||||||
|
aboutEl.style.cursor = "pointer";
|
||||||
el.appendChild(aboutEl);
|
el.appendChild(aboutEl);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -149,6 +166,54 @@ setInterval(() => {
|
|||||||
document.getElementById("manage-streams-change-windows")?.remove();
|
document.getElementById("manage-streams-change-windows")?.remove();
|
||||||
document.querySelector(`[aria-label="Stream Settings"]`)?.remove();
|
document.querySelector(`[aria-label="Stream Settings"]`)?.remove();
|
||||||
|
|
||||||
|
// Add event listener for keybind tab
|
||||||
|
if (
|
||||||
|
document
|
||||||
|
.getElementById("keybinds-tab")
|
||||||
|
?.getElementsByClassName(
|
||||||
|
"container-3jbRo5 info-1hMolH fontSize16-3zr6Io browserNotice-1u-Y5o"
|
||||||
|
).length
|
||||||
|
) {
|
||||||
|
const el = document
|
||||||
|
.getElementById("keybinds-tab")
|
||||||
|
.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", () => {
|
||||||
|
console.log("!discord-screenaudio-keybinds");
|
||||||
|
});
|
||||||
|
div.appendChild(button);
|
||||||
|
el.innerHTML = "";
|
||||||
|
el.appendChild(div);
|
||||||
|
}
|
||||||
|
|
||||||
|
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]
|
||||||
|
: 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]
|
||||||
|
: null;
|
||||||
|
|
||||||
|
window.discordScreenaudioToggleDeafen = () => deafenBtn && deafenBtn.click();
|
||||||
|
|
||||||
if (window.discordScreenaudioResolutionString) {
|
if (window.discordScreenaudioResolutionString) {
|
||||||
for (const el of document.getElementsByClassName(
|
for (const el of document.getElementsByClassName(
|
||||||
"qualityIndicator-39wQDy"
|
"qualityIndicator-39wQDy"
|
||||||
|
|||||||
@@ -1,10 +1,26 @@
|
|||||||
#include "discordpage.h"
|
#include "discordpage.h"
|
||||||
#include "log.h"
|
#include "log.h"
|
||||||
|
#include "mainwindow.h"
|
||||||
#include "virtmic.h"
|
#include "virtmic.h"
|
||||||
|
|
||||||
|
#ifdef KXMLGUI
|
||||||
|
#include <KAboutData>
|
||||||
|
#include <KHelpMenu>
|
||||||
|
#include <KShortcutsDialog>
|
||||||
|
#include <KXmlGuiWindow>
|
||||||
|
#include <QAction>
|
||||||
|
|
||||||
|
#ifdef KGLOBALACCEL
|
||||||
|
#include <KGlobalAccel>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
#include <QApplication>
|
#include <QApplication>
|
||||||
#include <QDesktopServices>
|
#include <QDesktopServices>
|
||||||
#include <QFile>
|
#include <QFile>
|
||||||
|
#include <QMessageBox>
|
||||||
|
#include <QNetworkReply>
|
||||||
#include <QTimer>
|
#include <QTimer>
|
||||||
#include <QWebChannel>
|
#include <QWebChannel>
|
||||||
#include <QWebEngineScript>
|
#include <QWebEngineScript>
|
||||||
@@ -33,51 +49,94 @@ DiscordPage::DiscordPage(QWidget *parent) : QWebEnginePage(parent) {
|
|||||||
settings()->setAttribute(QWebEngineSettings::PlaybackRequiresUserGesture,
|
settings()->setAttribute(QWebEngineSettings::PlaybackRequiresUserGesture,
|
||||||
false);
|
false);
|
||||||
settings()->setAttribute(QWebEngineSettings::JavascriptCanOpenWindows, false);
|
settings()->setAttribute(QWebEngineSettings::JavascriptCanOpenWindows, false);
|
||||||
|
settings()->setAttribute(QWebEngineSettings::ScrollAnimatorEnabled, true);
|
||||||
|
|
||||||
setUrl(QUrl("https://discord.com/app"));
|
setUrl(QUrl("https://discord.com/app"));
|
||||||
|
|
||||||
injectScript(":/assets/userscript.js");
|
injectScriptFile("userscript.js", ":/assets/userscript.js");
|
||||||
injectVersion(QApplication::applicationVersion());
|
|
||||||
|
injectScriptText("version.js",
|
||||||
|
QString("window.discordScreenaudioVersion = '%1';")
|
||||||
|
.arg(QApplication::applicationVersion()));
|
||||||
|
|
||||||
|
#ifdef KXMLGUI
|
||||||
|
injectScriptText("xmlgui.js", "window.discordScreenaudioKXMLGUI = true;");
|
||||||
|
|
||||||
|
KAboutData aboutData(
|
||||||
|
"discord-screenaudio", "discord-screenaudio",
|
||||||
|
QApplication::applicationVersion(),
|
||||||
|
"Custom Discord client with the ability to stream audio on Linux",
|
||||||
|
KAboutLicense::GPL_V3, "Copyright 2022 (C) Malte Jürgens");
|
||||||
|
aboutData.setBugAddress("https://github.com/maltejur/discord-screenaudio");
|
||||||
|
aboutData.addAuthor("Malte Jürgens", "Author", "maltejur@dismail.de",
|
||||||
|
"https://github.com/maltejur");
|
||||||
|
aboutData.addCredit("edisionnano",
|
||||||
|
"For creating and documenting the approach for streaming "
|
||||||
|
"audio in Discord used in this project.",
|
||||||
|
QString(),
|
||||||
|
"https://github.com/edisionnano/"
|
||||||
|
"Screenshare-with-audio-on-Discord-with-Linux");
|
||||||
|
aboutData.addCredit(
|
||||||
|
"Curve", "For creating the Rohrkabel library used in this project.",
|
||||||
|
QString(), "https://github.com/Curve");
|
||||||
|
aboutData.addComponent("Rohrkabel", "A C++ RAII Pipewire-API Wrapper", "1.3",
|
||||||
|
"https://github.com/Soundux/rohrkabel");
|
||||||
|
m_helpMenu = new KHelpMenu(parent, aboutData);
|
||||||
|
|
||||||
|
#ifdef KGLOBALACCEL
|
||||||
|
injectScriptText("kglobalaccel.js",
|
||||||
|
"window.discordScreenaudioKGLOBALACCEL = true;");
|
||||||
|
|
||||||
|
auto toggleMuteAction = new QAction(this);
|
||||||
|
toggleMuteAction->setText("Toggle Mute");
|
||||||
|
toggleMuteAction->setIcon(QIcon::fromTheme("microphone-sensitivity-muted"));
|
||||||
|
connect(toggleMuteAction, &QAction::triggered, this,
|
||||||
|
&DiscordPage::toggleMute);
|
||||||
|
|
||||||
|
auto toggleDeafenAction = new QAction(this);
|
||||||
|
toggleDeafenAction->setText("Toggle Deafen");
|
||||||
|
toggleDeafenAction->setIcon(QIcon::fromTheme("audio-volume-muted"));
|
||||||
|
connect(toggleDeafenAction, &QAction::triggered, this,
|
||||||
|
&DiscordPage::toggleDeafen);
|
||||||
|
|
||||||
|
m_actionCollection = new KActionCollection(this);
|
||||||
|
m_actionCollection->addAction("toggleMute", toggleMuteAction);
|
||||||
|
KGlobalAccel::setGlobalShortcut(toggleMuteAction, QList<QKeySequence>{});
|
||||||
|
m_actionCollection->addAction("toggleDeafen", toggleDeafenAction);
|
||||||
|
KGlobalAccel::setGlobalShortcut(toggleDeafenAction, QList<QKeySequence>{});
|
||||||
|
|
||||||
|
m_shortcutsDialog = new KShortcutsDialog(KShortcutsEditor::GlobalAction);
|
||||||
|
m_shortcutsDialog->addCollection(m_actionCollection);
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
connect(&m_streamDialog, &StreamDialog::requestedStreamStart, this,
|
connect(&m_streamDialog, &StreamDialog::requestedStreamStart, this,
|
||||||
&DiscordPage::startStream);
|
&DiscordPage::startStream);
|
||||||
}
|
}
|
||||||
|
|
||||||
void DiscordPage::injectScript(QString source) {
|
void DiscordPage::injectScriptText(QString name, QString content) {
|
||||||
qDebug(mainLog) << "Injecting " << source;
|
qDebug(mainLog) << "Injecting " << name;
|
||||||
|
|
||||||
QFile userscript(source);
|
|
||||||
|
|
||||||
if (!userscript.open(QIODevice::ReadOnly)) {
|
|
||||||
qFatal("Failed to load %s with error: %s", source.toLatin1().constData(),
|
|
||||||
userscript.errorString().toLatin1().constData());
|
|
||||||
} else {
|
|
||||||
QByteArray userscriptJs = userscript.readAll();
|
|
||||||
|
|
||||||
QWebEngineScript script;
|
QWebEngineScript script;
|
||||||
|
|
||||||
script.setSourceCode(userscriptJs);
|
script.setSourceCode(content);
|
||||||
script.setName("userscript.js");
|
script.setName(name);
|
||||||
script.setWorldId(QWebEngineScript::MainWorld);
|
script.setWorldId(QWebEngineScript::MainWorld);
|
||||||
script.setInjectionPoint(QWebEngineScript::DocumentCreation);
|
script.setInjectionPoint(QWebEngineScript::DocumentCreation);
|
||||||
script.setRunsOnSubFrames(false);
|
script.setRunsOnSubFrames(false);
|
||||||
|
|
||||||
scripts().insert(script);
|
scripts().insert(script);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void DiscordPage::injectVersion(QString version) {
|
void DiscordPage::injectScriptFile(QString name, QString source) {
|
||||||
QWebEngineScript script;
|
QFile file(source);
|
||||||
|
|
||||||
auto code = QString("window.discordScreenaudioVersion = '%1';").arg(version);
|
if (!file.open(QIODevice::ReadOnly)) {
|
||||||
|
qFatal("Failed to load %s with error: %s", source.toLatin1().constData(),
|
||||||
script.setSourceCode(code);
|
file.errorString().toLatin1().constData());
|
||||||
script.setName("version.js");
|
} else {
|
||||||
script.setWorldId(QWebEngineScript::MainWorld);
|
injectScriptText(name, file.readAll());
|
||||||
script.setInjectionPoint(QWebEngineScript::DocumentCreation);
|
}
|
||||||
script.setRunsOnSubFrames(false);
|
|
||||||
|
|
||||||
scripts().insert(script);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void DiscordPage::featurePermissionRequested(const QUrl &securityOrigin,
|
void DiscordPage::featurePermissionRequested(const QUrl &securityOrigin,
|
||||||
@@ -127,10 +186,8 @@ void DiscordPage::stopVirtmic() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void DiscordPage::startVirtmic(QString target) {
|
void DiscordPage::startVirtmic(QString target) {
|
||||||
if (target != "None") {
|
|
||||||
qDebug(virtmicLog) << "Starting Virtmic with target" << target;
|
qDebug(virtmicLog) << "Starting Virtmic with target" << target;
|
||||||
m_virtmicProcess.start(QApplication::arguments()[0], {"--virtmic", target});
|
m_virtmicProcess.start(QApplication::arguments()[0], {"--virtmic", target});
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void DiscordPage::javaScriptConsoleMessage(
|
void DiscordPage::javaScriptConsoleMessage(
|
||||||
@@ -144,6 +201,26 @@ void DiscordPage::javaScriptConsoleMessage(
|
|||||||
m_streamDialog.updateTargets();
|
m_streamDialog.updateTargets();
|
||||||
} else if (message == "!discord-screenaudio-stream-stopped") {
|
} else if (message == "!discord-screenaudio-stream-stopped") {
|
||||||
stopVirtmic();
|
stopVirtmic();
|
||||||
|
} else if (message == "!discord-screenaudio-about") {
|
||||||
|
#ifdef KXMLGUI
|
||||||
|
m_helpMenu->aboutApplication();
|
||||||
|
#endif
|
||||||
|
} else if (message == "!discord-screenaudio-keybinds") {
|
||||||
|
#ifdef KXMLGUI
|
||||||
|
#ifdef KGLOBALACCEL
|
||||||
|
m_shortcutsDialog->show();
|
||||||
|
#else
|
||||||
|
QMessageBox::information(MainWindow::instance(), "discord-screenaudio",
|
||||||
|
"Keybinds are not supported on this platform "
|
||||||
|
"(KGlobalAccel is not available).",
|
||||||
|
QMessageBox::Ok);
|
||||||
|
#endif
|
||||||
|
#else
|
||||||
|
QMessageBox::information(MainWindow::instance(), "discord-screenaudio",
|
||||||
|
"Keybinds are not supported on this platform "
|
||||||
|
"(KXmlGui and KGlobalAccel are not available).",
|
||||||
|
QMessageBox::Ok);
|
||||||
|
#endif
|
||||||
} else if (message.startsWith("dsa: ")) {
|
} else if (message.startsWith("dsa: ")) {
|
||||||
qDebug(userscriptLog) << message.mid(5).toUtf8().constData();
|
qDebug(userscriptLog) << message.mid(5).toUtf8().constData();
|
||||||
} else {
|
} else {
|
||||||
@@ -151,15 +228,27 @@ void DiscordPage::javaScriptConsoleMessage(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void DiscordPage::startStream(QString target, uint width, uint height,
|
void DiscordPage::startStream(bool video, bool audio, uint width, uint height,
|
||||||
uint frameRate) {
|
uint frameRate, QString target) {
|
||||||
stopVirtmic();
|
stopVirtmic();
|
||||||
startVirtmic(target);
|
startVirtmic(audio ? target : "[None]");
|
||||||
// Wait a bit for the virtmic to start
|
// Wait a bit for the virtmic to start
|
||||||
QTimer::singleShot(target == "None" ? 0 : 200, [=]() {
|
QTimer::singleShot(200, [=]() {
|
||||||
runJavaScript(QString("window.discordScreenaudioStartStream(%1, %2, %3);")
|
runJavaScript(
|
||||||
.arg(width)
|
QString("window.discordScreenaudioStartStream(%1, %2, %3, %4);")
|
||||||
.arg(height)
|
.arg(video)
|
||||||
.arg(frameRate));
|
.arg(video ? width : 32)
|
||||||
|
.arg(video ? height : 16)
|
||||||
|
.arg(video ? frameRate : 1));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void DiscordPage::toggleMute() {
|
||||||
|
qDebug(shortcutLog) << "Toggling mute";
|
||||||
|
runJavaScript("window.discordScreenaudioToggleMute();");
|
||||||
|
}
|
||||||
|
|
||||||
|
void DiscordPage::toggleDeafen() {
|
||||||
|
qDebug(shortcutLog) << "Toggling deafen";
|
||||||
|
runJavaScript("window.discordScreenaudioToggleDeafen();");
|
||||||
|
}
|
||||||
|
|||||||
@@ -3,6 +3,12 @@
|
|||||||
#include "streamdialog.h"
|
#include "streamdialog.h"
|
||||||
#include "virtmic.h"
|
#include "virtmic.h"
|
||||||
|
|
||||||
|
#ifdef KXMLGUI
|
||||||
|
#include <KActionCollection>
|
||||||
|
#include <KHelpMenu>
|
||||||
|
#include <KShortcutsDialog>
|
||||||
|
#endif
|
||||||
|
|
||||||
#include <QProcess>
|
#include <QProcess>
|
||||||
#include <QWebEngineFullScreenRequest>
|
#include <QWebEngineFullScreenRequest>
|
||||||
#include <QWebEnginePage>
|
#include <QWebEnginePage>
|
||||||
@@ -16,6 +22,13 @@ public:
|
|||||||
private:
|
private:
|
||||||
StreamDialog m_streamDialog;
|
StreamDialog m_streamDialog;
|
||||||
QProcess m_virtmicProcess;
|
QProcess m_virtmicProcess;
|
||||||
|
#ifdef KXMLGUI
|
||||||
|
KHelpMenu *m_helpMenu;
|
||||||
|
#ifdef KGLOBALACCEL
|
||||||
|
KActionCollection *m_actionCollection;
|
||||||
|
KShortcutsDialog *m_shortcutsDialog;
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
bool acceptNavigationRequest(const QUrl &url,
|
bool acceptNavigationRequest(const QUrl &url,
|
||||||
QWebEnginePage::NavigationType type,
|
QWebEnginePage::NavigationType type,
|
||||||
bool isMainFrame) override;
|
bool isMainFrame) override;
|
||||||
@@ -24,15 +37,18 @@ private:
|
|||||||
javaScriptConsoleMessage(QWebEnginePage::JavaScriptConsoleMessageLevel level,
|
javaScriptConsoleMessage(QWebEnginePage::JavaScriptConsoleMessageLevel level,
|
||||||
const QString &message, int lineNumber,
|
const QString &message, int lineNumber,
|
||||||
const QString &sourceID) override;
|
const QString &sourceID) override;
|
||||||
void injectScript(QString source);
|
void injectScriptText(QString name, QString content);
|
||||||
void injectVersion(QString version);
|
void injectScriptFile(QString name, QString source);
|
||||||
void stopVirtmic();
|
void stopVirtmic();
|
||||||
void startVirtmic(QString target);
|
void startVirtmic(QString target);
|
||||||
|
void toggleMute();
|
||||||
|
void toggleDeafen();
|
||||||
|
|
||||||
private Q_SLOTS:
|
private Q_SLOTS:
|
||||||
void featurePermissionRequested(const QUrl &securityOrigin,
|
void featurePermissionRequested(const QUrl &securityOrigin,
|
||||||
QWebEnginePage::Feature feature);
|
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
|
// Will immediately get destroyed again but is needed for navigation to
|
||||||
|
|||||||
@@ -4,3 +4,4 @@ Q_LOGGING_CATEGORY(mainLog, "main");
|
|||||||
Q_LOGGING_CATEGORY(discordLog, "discord");
|
Q_LOGGING_CATEGORY(discordLog, "discord");
|
||||||
Q_LOGGING_CATEGORY(userscriptLog, "userscript");
|
Q_LOGGING_CATEGORY(userscriptLog, "userscript");
|
||||||
Q_LOGGING_CATEGORY(virtmicLog, "virtmic");
|
Q_LOGGING_CATEGORY(virtmicLog, "virtmic");
|
||||||
|
Q_LOGGING_CATEGORY(shortcutLog, "shortcut");
|
||||||
|
|||||||
@@ -6,3 +6,4 @@ Q_DECLARE_LOGGING_CATEGORY(mainLog);
|
|||||||
Q_DECLARE_LOGGING_CATEGORY(discordLog);
|
Q_DECLARE_LOGGING_CATEGORY(discordLog);
|
||||||
Q_DECLARE_LOGGING_CATEGORY(userscriptLog);
|
Q_DECLARE_LOGGING_CATEGORY(userscriptLog);
|
||||||
Q_DECLARE_LOGGING_CATEGORY(virtmicLog);
|
Q_DECLARE_LOGGING_CATEGORY(virtmicLog);
|
||||||
|
Q_DECLARE_LOGGING_CATEGORY(shortcutLog);
|
||||||
|
|||||||
10
src/main.cpp
10
src/main.cpp
@@ -1,6 +1,10 @@
|
|||||||
#include "mainwindow.h"
|
#include "mainwindow.h"
|
||||||
#include "virtmic.h"
|
#include "virtmic.h"
|
||||||
|
|
||||||
|
#ifdef KXMLGUI
|
||||||
|
#include <KAboutData>
|
||||||
|
#endif
|
||||||
|
|
||||||
#include <QApplication>
|
#include <QApplication>
|
||||||
#include <QCommandLineParser>
|
#include <QCommandLineParser>
|
||||||
#include <QLoggingCategory>
|
#include <QLoggingCategory>
|
||||||
@@ -26,6 +30,10 @@ int main(int argc, char *argv[]) {
|
|||||||
QCommandLineOption degubOption("remote-debugging",
|
QCommandLineOption degubOption("remote-debugging",
|
||||||
"Open Chromium Remote Debugging on port 9222");
|
"Open Chromium Remote Debugging on port 9222");
|
||||||
parser.addOption(degubOption);
|
parser.addOption(degubOption);
|
||||||
|
QCommandLineOption notifySendOption(
|
||||||
|
"notify-send", "Use notify-send instead of QT/KF5 notifications");
|
||||||
|
parser.addOption(notifySendOption);
|
||||||
|
|
||||||
parser.process(app);
|
parser.process(app);
|
||||||
|
|
||||||
if (parser.isSet(virtmicOption)) {
|
if (parser.isSet(virtmicOption)) {
|
||||||
@@ -41,7 +49,7 @@ int main(int argc, char *argv[]) {
|
|||||||
"--remote-debugging-port=9222 " +
|
"--remote-debugging-port=9222 " +
|
||||||
qgetenv("QTWEBENGINE_CHROMIUM_FLAGS"));
|
qgetenv("QTWEBENGINE_CHROMIUM_FLAGS"));
|
||||||
|
|
||||||
MainWindow w;
|
MainWindow w(parser.isSet(notifySendOption));
|
||||||
w.show();
|
w.show();
|
||||||
|
|
||||||
return app.exec();
|
return app.exec();
|
||||||
|
|||||||
@@ -22,7 +22,13 @@
|
|||||||
#include <QWebEngineSettings>
|
#include <QWebEngineSettings>
|
||||||
#include <QWidget>
|
#include <QWidget>
|
||||||
|
|
||||||
MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) {
|
MainWindow *MainWindow::m_instance = nullptr;
|
||||||
|
|
||||||
|
MainWindow::MainWindow(bool useNotifySend, QWidget *parent)
|
||||||
|
: QMainWindow(parent) {
|
||||||
|
assert(MainWindow::m_instance == nullptr);
|
||||||
|
MainWindow::m_instance = this;
|
||||||
|
m_useNotifySend = useNotifySend;
|
||||||
setupWebView();
|
setupWebView();
|
||||||
resize(1000, 700);
|
resize(1000, 700);
|
||||||
showMaximized();
|
showMaximized();
|
||||||
@@ -36,13 +42,26 @@ void MainWindow::setupWebView() {
|
|||||||
m_webView = new QWebEngineView(this);
|
m_webView = new QWebEngineView(this);
|
||||||
m_webView->setPage(page);
|
m_webView->setPage(page);
|
||||||
|
|
||||||
#ifdef KNOTIFICATIONS
|
if (m_useKF5Notifications || m_useNotifySend)
|
||||||
QWebEngineProfile::defaultProfile()->setNotificationPresenter(
|
QWebEngineProfile::defaultProfile()->setNotificationPresenter(
|
||||||
[&](std::unique_ptr<QWebEngineNotification> notificationInfo) {
|
[&](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->setTitle(notificationInfo->title());
|
||||||
notification->setText(notificationInfo->message());
|
notification->setText(notificationInfo->message());
|
||||||
notification->setPixmap(QPixmap::fromImage(notificationInfo->icon()));
|
notification->setPixmap(
|
||||||
|
QPixmap::fromImage(notificationInfo->icon()));
|
||||||
notification->setDefaultAction("View");
|
notification->setDefaultAction("View");
|
||||||
connect(notification, &KNotification::defaultActivated,
|
connect(notification, &KNotification::defaultActivated,
|
||||||
[&, notificationInfo = std::move(notificationInfo)]() {
|
[&, notificationInfo = std::move(notificationInfo)]() {
|
||||||
@@ -50,8 +69,9 @@ void MainWindow::setupWebView() {
|
|||||||
activateWindow();
|
activateWindow();
|
||||||
});
|
});
|
||||||
notification->sendEvent();
|
notification->sendEvent();
|
||||||
});
|
|
||||||
#endif
|
#endif
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
setCentralWidget(m_webView);
|
setCentralWidget(m_webView);
|
||||||
}
|
}
|
||||||
@@ -68,3 +88,5 @@ void MainWindow::fullScreenRequested(
|
|||||||
}
|
}
|
||||||
|
|
||||||
void MainWindow::closeEvent(QCloseEvent *event) { QApplication::quit(); }
|
void MainWindow::closeEvent(QCloseEvent *event) { QApplication::quit(); }
|
||||||
|
|
||||||
|
MainWindow *MainWindow::instance() { return m_instance; }
|
||||||
|
|||||||
@@ -14,7 +14,8 @@ class MainWindow : public QMainWindow {
|
|||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit MainWindow(QWidget *parent = nullptr);
|
explicit MainWindow(bool useNotifySend = false, QWidget *parent = nullptr);
|
||||||
|
static MainWindow *instance();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void setupWebView();
|
void setupWebView();
|
||||||
@@ -23,6 +24,13 @@ private:
|
|||||||
DiscordPage *m_discordPage;
|
DiscordPage *m_discordPage;
|
||||||
void closeEvent(QCloseEvent *event) override;
|
void closeEvent(QCloseEvent *event) override;
|
||||||
bool m_wasMaximized;
|
bool m_wasMaximized;
|
||||||
|
static MainWindow *m_instance;
|
||||||
|
bool m_useNotifySend;
|
||||||
|
#ifdef KNOTIFICATIONS
|
||||||
|
bool m_useKF5Notifications = true;
|
||||||
|
#else
|
||||||
|
bool m_useKF5Notifications = false;
|
||||||
|
#endif
|
||||||
|
|
||||||
private Q_SLOTS:
|
private Q_SLOTS:
|
||||||
void fullScreenRequested(QWebEngineFullScreenRequest fullScreenRequest);
|
void fullScreenRequested(QWebEngineFullScreenRequest fullScreenRequest);
|
||||||
|
|||||||
@@ -12,53 +12,77 @@
|
|||||||
StreamDialog::StreamDialog() : QWidget() {
|
StreamDialog::StreamDialog() : QWidget() {
|
||||||
setAttribute(Qt::WA_QuitOnClose, false);
|
setAttribute(Qt::WA_QuitOnClose, false);
|
||||||
|
|
||||||
|
{
|
||||||
auto layout = new QVBoxLayout(this);
|
auto layout = new QVBoxLayout(this);
|
||||||
layout->setSizeConstraint(QLayout::SetFixedSize);
|
layout->setSizeConstraint(QLayout::SetFixedSize);
|
||||||
|
|
||||||
auto targetLabel = new QLabel(this);
|
m_videoGroupBox = new QGroupBox(this);
|
||||||
targetLabel->setText("Which app do you want to stream sound from?");
|
m_videoGroupBox->setTitle("Video");
|
||||||
layout->addWidget(targetLabel);
|
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);
|
m_targetComboBox = new QComboBox(this);
|
||||||
updateTargets();
|
updateTargets();
|
||||||
targetHBox->addWidget(m_targetComboBox);
|
targetLayout->addWidget(m_targetComboBox);
|
||||||
|
|
||||||
auto refreshTargetsButton = new QPushButton(this);
|
auto refreshTargetsButton = new QPushButton(this);
|
||||||
refreshTargetsButton->setFixedSize(30, 30);
|
refreshTargetsButton->setFixedSize(30, 30);
|
||||||
refreshTargetsButton->setIcon(QIcon::fromTheme("view-refresh"));
|
refreshTargetsButton->setIcon(QIcon::fromTheme("view-refresh"));
|
||||||
connect(refreshTargetsButton, &QPushButton::clicked, this,
|
connect(refreshTargetsButton, &QPushButton::clicked, this,
|
||||||
&StreamDialog::updateTargets);
|
&StreamDialog::updateTargets);
|
||||||
targetHBox->addWidget(refreshTargetsButton);
|
targetLayout->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);
|
|
||||||
|
|
||||||
auto button = new QPushButton(this);
|
auto button = new QPushButton(this);
|
||||||
button->setText("Start Stream");
|
button->setText("Start Stream");
|
||||||
@@ -66,16 +90,18 @@ StreamDialog::StreamDialog() : QWidget() {
|
|||||||
layout->addWidget(button, Qt::AlignRight | Qt::AlignBottom);
|
layout->addWidget(button, Qt::AlignRight | Qt::AlignBottom);
|
||||||
|
|
||||||
setLayout(layout);
|
setLayout(layout);
|
||||||
|
}
|
||||||
|
|
||||||
setWindowTitle("discord-screenaudio Stream Dialog");
|
setWindowTitle("discord-screenaudio Stream Dialog");
|
||||||
}
|
}
|
||||||
|
|
||||||
void StreamDialog::startStream() {
|
void StreamDialog::startStream() {
|
||||||
auto resolution =
|
auto resolution = m_resolutionComboBox->currentData().toString().split('x');
|
||||||
m_qualityResolutionComboBox->currentData().toString().split('x');
|
emit requestedStreamStart(m_videoGroupBox->isChecked(),
|
||||||
emit requestedStreamStart(m_targetComboBox->currentText(),
|
m_audioGroupBox->isChecked(),
|
||||||
resolution[0].toUInt(), resolution[1].toUInt(),
|
resolution[0].toUInt(), resolution[1].toUInt(),
|
||||||
m_qualityFPSComboBox->currentData().toUInt());
|
m_framerateComboBox->currentData().toUInt(),
|
||||||
|
m_targetComboBox->currentText());
|
||||||
setHidden(true);
|
setHidden(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -83,7 +109,6 @@ void StreamDialog::updateTargets() {
|
|||||||
auto lastTarget = m_targetComboBox->currentText();
|
auto lastTarget = m_targetComboBox->currentText();
|
||||||
|
|
||||||
m_targetComboBox->clear();
|
m_targetComboBox->clear();
|
||||||
m_targetComboBox->addItem("[None]");
|
|
||||||
m_targetComboBox->addItem("[All Desktop Audio]");
|
m_targetComboBox->addItem("[All Desktop Audio]");
|
||||||
for (auto target : Virtmic::getTargets()) {
|
for (auto target : Virtmic::getTargets()) {
|
||||||
m_targetComboBox->addItem(target);
|
m_targetComboBox->addItem(target);
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
#include <QComboBox>
|
#include <QComboBox>
|
||||||
#include <QDialog>
|
#include <QDialog>
|
||||||
|
#include <QGroupBox>
|
||||||
#include <QWidget>
|
#include <QWidget>
|
||||||
|
|
||||||
class StreamDialog : public QWidget {
|
class StreamDialog : public QWidget {
|
||||||
@@ -12,12 +13,14 @@ public:
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
QComboBox *m_targetComboBox;
|
QComboBox *m_targetComboBox;
|
||||||
QComboBox *m_qualityResolutionComboBox;
|
QComboBox *m_resolutionComboBox;
|
||||||
QComboBox *m_qualityFPSComboBox;
|
QComboBox *m_framerateComboBox;
|
||||||
|
QGroupBox *m_videoGroupBox;
|
||||||
|
QGroupBox *m_audioGroupBox;
|
||||||
|
|
||||||
Q_SIGNALS:
|
Q_SIGNALS:
|
||||||
void requestedStreamStart(QString target, uint width, uint height,
|
void requestedStreamStart(bool video, bool audio, uint width, uint height,
|
||||||
uint frameRate);
|
uint frameRate, QString target);
|
||||||
|
|
||||||
public Q_SLOTS:
|
public Q_SLOTS:
|
||||||
void updateTargets();
|
void updateTargets();
|
||||||
|
|||||||
Reference in New Issue
Block a user