Compare commits
	
		
			3 Commits
		
	
	
		
			v1.8.1
			...
			virtmic-re
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|  | e6449ad807 | ||
|  | e919e3ea25 | ||
|  | f31901073e | 
							
								
								
									
										3
									
								
								.gitmodules
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										3
									
								
								.gitmodules
									
									
									
									
										vendored
									
									
								
							| @@ -1,3 +1,6 @@ | |||||||
| [submodule "submodules/rohrkabel"] | [submodule "submodules/rohrkabel"] | ||||||
| 	path = submodules/rohrkabel | 	path = submodules/rohrkabel | ||||||
| 	url = https://github.com/Soundux/rohrkabel | 	url = https://github.com/Soundux/rohrkabel | ||||||
|  | [submodule "submodules/channel"] | ||||||
|  | 	path = submodules/channel | ||||||
|  | 	url = https://github.com/Soundux/channel.git | ||||||
|   | |||||||
| @@ -63,15 +63,20 @@ if(GIT_FOUND AND EXISTS "${PROJECT_SOURCE_DIR}/.git") | |||||||
|   endif() |   endif() | ||||||
| endif() | endif() | ||||||
|  |  | ||||||
| if(NOT EXISTS "${PROJECT_SOURCE_DIR}/submodules/rohrkabel/CMakeLists.txt") | function(add_git_subdirectory SUBMODULE) | ||||||
|   message(FATAL_ERROR "Rohrkabel was not found since you are not in a Git checkout or have GIT_SUBMODULE disabled. Please provide rohrkabel manually to `./submodules/rohrkabel`.") |   if(NOT EXISTS "${PROJECT_SOURCE_DIR}/submodules/${SUBMODULE}/CMakeLists.txt") | ||||||
| endif() |     message(FATAL_ERROR "Submodule ${SUBMODULE} was not found since you are not in a Git checkout or have GIT_SUBMODULE disabled. Please provide ${SUBMODULE} manually to `./submodules/${SUBMODULE}`.") | ||||||
|  |   endif() | ||||||
|  |  | ||||||
| add_subdirectory(submodules/rohrkabel) |   add_subdirectory(submodules/${SUBMODULE}) | ||||||
|  | endfunction() | ||||||
|  |  | ||||||
|  | add_git_subdirectory(rohrkabel) | ||||||
|  | add_git_subdirectory(channel) | ||||||
|  |  | ||||||
| add_executable(discord-screenaudio ${discord-screenaudio_SRC}) | add_executable(discord-screenaudio ${discord-screenaudio_SRC}) | ||||||
|  |  | ||||||
| target_link_libraries(discord-screenaudio Qt::Widgets Qt::WebEngineWidgets rohrkabel) | target_link_libraries(discord-screenaudio Qt::Widgets Qt::WebEngineWidgets rohrkabel channel) | ||||||
|  |  | ||||||
| if(KF5Notifications_FOUND) | if(KF5Notifications_FOUND) | ||||||
|   target_link_libraries(discord-screenaudio KF5::Notifications) |   target_link_libraries(discord-screenaudio KF5::Notifications) | ||||||
|   | |||||||
| @@ -62,9 +62,6 @@ You have multiple options: | |||||||
| With apt: | 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` | `apt install -y build-essential cmake qtbase5-dev qtwebengine5-dev libkf5notifications-dev libkf5xmlgui-dev libkf5globalaccel-dev pkg-config libpipewire-0.3-dev git` | ||||||
|  |  | ||||||
| With dnf: |  | ||||||
| `dnf install @development-tools cmake qt5-qtbase-devel qt5-qtwebengine-devel kf5-knotifications-devel kf5-kxmlgui-devel kf5-kglobalaccel-devel pkgconfig pipewire-devel git` |  | ||||||
|  |  | ||||||
| ### Building | ### Building | ||||||
|  |  | ||||||
| First, clone the repository: | First, clone the repository: | ||||||
|   | |||||||
| @@ -282,11 +282,10 @@ void DiscordPage::javaScriptConsoleMessage( | |||||||
|           ansi += "\033[" + cssAnsiColorMap[color] + "m"; |           ansi += "\033[" + cssAnsiColorMap[color] + "m"; | ||||||
|       } |       } | ||||||
|     } |     } | ||||||
|     if (endOfStyles < lines.length()) |     qDebug(discordLog) << (ansi + lines[0].trimmed() + "\033[0m " + | ||||||
|       qDebug(discordLog) << (ansi + lines[0].trimmed() + "\033[0m " + |                            lines[endOfStyles].trimmed()) | ||||||
|                              lines[endOfStyles].trimmed()) |                               .toUtf8() | ||||||
|                                 .toUtf8() |                               .constData(); | ||||||
|                                 .constData(); |  | ||||||
|     for (auto line : lines.mid(endOfStyles + 1)) { |     for (auto line : lines.mid(endOfStyles + 1)) { | ||||||
|       qDebug(discordLog) << line.toUtf8().constData(); |       qDebug(discordLog) << line.toUtf8().constData(); | ||||||
|     } |     } | ||||||
|   | |||||||
| @@ -24,9 +24,6 @@ int main(int argc, char *argv[]) { | |||||||
|       "Custom Discord client with the ability to stream audio on Linux"); |       "Custom Discord client with the ability to stream audio on Linux"); | ||||||
|   parser.addHelpOption(); |   parser.addHelpOption(); | ||||||
|   parser.addVersionOption(); |   parser.addVersionOption(); | ||||||
|   QCommandLineOption virtmicOption("virtmic", "Start the Virtual Microphone", |  | ||||||
|                                    "target"); |  | ||||||
|   parser.addOption(virtmicOption); |  | ||||||
|   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); | ||||||
| @@ -36,10 +33,6 @@ int main(int argc, char *argv[]) { | |||||||
|  |  | ||||||
|   parser.process(app); |   parser.process(app); | ||||||
|  |  | ||||||
|   if (parser.isSet(virtmicOption)) { |  | ||||||
|     Virtmic::start(parser.value(virtmicOption)); |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   qputenv("QTWEBENGINE_CHROMIUM_FLAGS", |   qputenv("QTWEBENGINE_CHROMIUM_FLAGS", | ||||||
|           "--enable-features=WebRTCPipeWireCapturer " + |           "--enable-features=WebRTCPipeWireCapturer " + | ||||||
|               qgetenv("QTWEBENGINE_CHROMIUM_FLAGS")); |               qgetenv("QTWEBENGINE_CHROMIUM_FLAGS")); | ||||||
|   | |||||||
| @@ -30,7 +30,6 @@ MainWindow::MainWindow(bool useNotifySend, QWidget *parent) | |||||||
|   m_centralWidget = new CentralWidget(this); |   m_centralWidget = new CentralWidget(this); | ||||||
|   setCentralWidget(m_centralWidget); |   setCentralWidget(m_centralWidget); | ||||||
|   setupTrayIcon(); |   setupTrayIcon(); | ||||||
|   setMinimumSize(800, 300); |  | ||||||
|   if (m_settings->contains("geometry")) { |   if (m_settings->contains("geometry")) { | ||||||
|     restoreGeometry(m_settings->value("geometry").toByteArray()); |     restoreGeometry(m_settings->value("geometry").toByteArray()); | ||||||
|   } else { |   } else { | ||||||
|   | |||||||
| @@ -1,6 +1,7 @@ | |||||||
| #pragma once | #pragma once | ||||||
|  |  | ||||||
| #include "centralwidget.h" | #include "centralwidget.h" | ||||||
|  | #include "virtmic.h" | ||||||
|  |  | ||||||
| #include <QMainWindow> | #include <QMainWindow> | ||||||
| #include <QMenu> | #include <QMenu> | ||||||
|   | |||||||
| @@ -1,4 +1,5 @@ | |||||||
| #include "streamdialog.h" | #include "streamdialog.h" | ||||||
|  | #include "mainwindow.h" | ||||||
| #include "virtmic.h" | #include "virtmic.h" | ||||||
|  |  | ||||||
| #include <QComboBox> | #include <QComboBox> | ||||||
|   | |||||||
| @@ -42,12 +42,12 @@ void UserScript::setupHelpMenu() { | |||||||
|   aboutData.addCredit( |   aboutData.addCredit( | ||||||
|       "Curve", "For creating the Rohrkabel library used in this project.", |       "Curve", "For creating the Rohrkabel library used in this project.", | ||||||
|       QString(), "https://github.com/Curve"); |       QString(), "https://github.com/Curve"); | ||||||
|   aboutData.addComponent("Rohrkabel", "A C++ RAII Pipewire-API Wrapper", "1.3", |   aboutData.addComponent("Rohrkabel", "A C++ RAII Pipewire-API Wrapper", "1.5", | ||||||
|                          "https://github.com/Soundux/rohrkabel"); |                          "https://github.com/Soundux/rohrkabel"); | ||||||
|   aboutData.addComponent("arRPC", |   aboutData.addComponent( | ||||||
|                          "An open implementation of Discord's local RPC " |       "Soundux/channel ", | ||||||
|                          "servers<br>Copyright (c) 2022 OpenAsar", |       "A C++ implementation of Rust's std::sync::mpsc::channel", nullptr, | ||||||
|                          nullptr, "https://github.com/OpenAsar/arrpc"); |       "https://github.com/Soundux/channel"); | ||||||
|   m_helpMenu = new KHelpMenu(MainWindow::instance(), aboutData); |   m_helpMenu = new KHelpMenu(MainWindow::instance(), aboutData); | ||||||
| #endif | #endif | ||||||
| } | } | ||||||
|   | |||||||
							
								
								
									
										340
									
								
								src/virtmic.cpp
									
									
									
									
									
								
							
							
						
						
									
										340
									
								
								src/virtmic.cpp
									
									
									
									
									
								
							| @@ -1,194 +1,174 @@ | |||||||
| #include "virtmic.h" | #include "virtmic.h" | ||||||
| #include "log.h" | #include "log.h" | ||||||
|  |  | ||||||
| #include <rohrkabel/loop/main.hpp> | QThread virtmicThread; | ||||||
| #include <rohrkabel/registry/registry.hpp> | std::unique_ptr<pipewire::sender<Virtmic::set_target, Virtmic::terminate>> | ||||||
|  |     senderr; | ||||||
|  | std::unique_ptr<cr::receiver<Virtmic::new_targets>> receiverr; | ||||||
|  |  | ||||||
| namespace Virtmic { | void Virtmic::instance() { | ||||||
|  |   if (!virtmicThread.isRunning()) { | ||||||
| const QStringList EXCLUDE_TARGETS{"Chromium input", "discord-screenaudio"}; |     auto [main_sender, main_receiver] = cr::channel<new_targets>(); | ||||||
|  |     auto [pw_sender, pw_receiver] = pipewire::channel<set_target, terminate>(); | ||||||
| const std::string nullstr = ""; |     Virtmic *virtmic = | ||||||
| const std::string &getTarget(const pipewire::spa::dict &props) { |         new Virtmic(std::move(pw_receiver), std::move(main_sender)); | ||||||
|   if (props.count("media.class") && |     virtmic->moveToThread(&virtmicThread); | ||||||
|       props.at("media.class") == "Stream/Output/Audio") { |     virtmicThread.start(); | ||||||
|     if (props.count("application.name") && props.at("application.name") != "") |     QMetaObject::invokeMethod(virtmic, "run"); | ||||||
|       return props.at("application.name"); |     receiverr = std::make_unique<cr::receiver<Virtmic::new_targets>>( | ||||||
|     else if (props.count("application.process.binary") && |         std::move(main_receiver)); | ||||||
|              props.at("application.process.binary") != "") |     senderr = std::make_unique< | ||||||
|       return props.at("application.process.binary"); |         pipewire::sender<Virtmic::set_target, Virtmic::terminate>>( | ||||||
|     else |         std::move(pw_sender)); | ||||||
|       return props.at("node.name"); |  | ||||||
|   } else |  | ||||||
|     return nullstr; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| QString qGetTarget(const pipewire::spa::dict &props) { |  | ||||||
|   return QString::fromStdString(getTarget(props)); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| QVector<QString> getTargets() { |  | ||||||
|   auto main_loop = pipewire::main_loop(); |  | ||||||
|   auto context = pipewire::context(main_loop); |  | ||||||
|   auto core = pipewire::core(context); |  | ||||||
|   auto reg = pipewire::registry(core); |  | ||||||
|  |  | ||||||
|   QVector<QString> targets; |  | ||||||
|  |  | ||||||
|   auto reg_listener = reg.listen<pipewire::registry_listener>(); |  | ||||||
|   reg_listener.on<pipewire::registry_event::global>( |  | ||||||
|       [&](const pipewire::global &global) { |  | ||||||
|         if (global.type == pipewire::node::type) { |  | ||||||
|           auto node = reg.bind<pipewire::node>(global.id); |  | ||||||
|           auto info = node.info(); |  | ||||||
|           QString name = qGetTarget(info.props); |  | ||||||
|           if (name != "" && !EXCLUDE_TARGETS.contains(name) && |  | ||||||
|               !targets.contains(name)) { |  | ||||||
|             targets.append(name); |  | ||||||
|           } |  | ||||||
|         } |  | ||||||
|       }); |  | ||||||
|   core.update(); |  | ||||||
|  |  | ||||||
|   return targets; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void start(QString _target) { |  | ||||||
|   std::map<std::uint32_t, pipewire::port> ports; |  | ||||||
|   std::unique_ptr<pipewire::port> virt_fl, virt_fr; |  | ||||||
|  |  | ||||||
|   std::map<std::uint32_t, pipewire::node_info> nodes; |  | ||||||
|   std::map<std::uint32_t, pipewire::link_factory> links; |  | ||||||
|  |  | ||||||
|   auto main_loop = pipewire::main_loop(); |  | ||||||
|   auto context = pipewire::context(main_loop); |  | ||||||
|   auto core = pipewire::core(context); |  | ||||||
|   auto reg = pipewire::registry(core); |  | ||||||
|  |  | ||||||
|   auto link = [&](const std::string &target, pipewire::core &core) { |  | ||||||
|     for (const auto &[port_id, port] : ports) { |  | ||||||
|       if (!virt_fl || !virt_fr) |  | ||||||
|         continue; |  | ||||||
|  |  | ||||||
|       if (links.count(port_id)) |  | ||||||
|         continue; |  | ||||||
|  |  | ||||||
|       if (port.info().direction == pipewire::port_direction::input) |  | ||||||
|         continue; |  | ||||||
|  |  | ||||||
|       if (!port.info().props.count("node.id")) |  | ||||||
|         continue; |  | ||||||
|  |  | ||||||
|       auto parent_id = std::stoul(port.info().props["node.id"]); |  | ||||||
|  |  | ||||||
|       if (!nodes.count(parent_id)) |  | ||||||
|         continue; |  | ||||||
|  |  | ||||||
|       auto &parent = nodes.at(parent_id); |  | ||||||
|       std::string name = getTarget(parent.props); |  | ||||||
|       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(name)) |  | ||||||
|                                   .arg(port_id) |  | ||||||
|                                   .arg(fl ? virt_fl->info().id |  | ||||||
|                                           : virt_fr->info().id) |  | ||||||
|                                   .toUtf8() |  | ||||||
|                                   .data(); |  | ||||||
|       } |  | ||||||
|     } |  | ||||||
|   }; |  | ||||||
|  |  | ||||||
|   std::string target = _target.toUtf8().toStdString(); |  | ||||||
|  |  | ||||||
|   auto virtual_mic = core.create("adapter", |  | ||||||
|                                  {{"node.name", "discord-screenaudio-virtmic"}, |  | ||||||
|                                   {"media.class", "Audio/Source/Virtual"}, |  | ||||||
|                                   {"factory.name", "support.null-audio-sink"}, |  | ||||||
|                                   {"audio.channels", "2"}, |  | ||||||
|                                   {"audio.position", "FL,FR"}}, |  | ||||||
|                                  pipewire::node::type, pipewire::node::version, |  | ||||||
|                                  pipewire::update_strategy::none); |  | ||||||
|  |  | ||||||
|   if (target == "[None]") { |  | ||||||
|     while (true) { |  | ||||||
|       main_loop.run(); |  | ||||||
|     } |  | ||||||
|     return; |  | ||||||
|   } |   } | ||||||
|  | } | ||||||
|  |  | ||||||
|   auto reg_events = reg.listen<pipewire::registry_listener>(); | void Virtmic::setTarget(QString target) { | ||||||
|   reg_events.on<pipewire::registry_event::global>( |   senderr.get()->send<Virtmic::set_target>({target}); | ||||||
|       [&](const pipewire::global &global) { | } | ||||||
|         if (global.type == pipewire::node::type) { |  | ||||||
|           auto node = reg.bind<pipewire::node>(global.id); |  | ||||||
|           auto info = node.info(); |  | ||||||
|           std::string name = getTarget(info.props); |  | ||||||
|           if (name == nullstr) |  | ||||||
|             return; |  | ||||||
|           qDebug(virtmicLog) << QString("Added: %1") |  | ||||||
|                                     .arg(QString::fromStdString(name)) |  | ||||||
|                                     .toUtf8() |  | ||||||
|                                     .data(); |  | ||||||
|  |  | ||||||
|           if (!nodes.count(global.id)) { | void Virtmic::getTargets() { senderr.get()->send<Virtmic::get_targets>(); } | ||||||
|             nodes.emplace(global.id, node.info()); |  | ||||||
|             link(target, core); |  | ||||||
|           } |  | ||||||
|         } |  | ||||||
|         if (global.type == pipewire::port::type) { |  | ||||||
|           auto port = reg.bind<pipewire::port>(global.id); |  | ||||||
|           auto info = port.info(); |  | ||||||
|  |  | ||||||
|           if (info.props.count("node.id")) { | Virtmic::Virtmic(pipewire::receiver<set_target, terminate> receiver, | ||||||
|             auto node_id = std::stoul(info.props["node.id"]); |                  cr::sender<new_targets> sender) { | ||||||
|  |   m_receiver = std::make_unique<pipewire::receiver<set_target, terminate>>( | ||||||
|             if (node_id == virtual_mic.id() && |       std::move(receiver)); | ||||||
|                 info.direction == pipewire::port_direction::input) { |   m_sender = std::make_unique<cr::sender<new_targets>>(std::move(sender)); | ||||||
|               if (info.props["audio.channel"] == "FL") { |   virtual_mic = std::make_unique<pipewire::proxy>( | ||||||
|                 virt_fl = std::make_unique<pipewire::port>(std::move(port)); |       std::move(*core.create("adapter", | ||||||
|               } else { |                              {{"node.name", "discord-screenaudio-virtmic"}, | ||||||
|                 virt_fr = std::make_unique<pipewire::port>(std::move(port)); |                               {"media.class", "Audio/Source/Virtual"}, | ||||||
|               } |                               {"factory.name", "support.null-audio-sink"}, | ||||||
|             } else { |                               {"audio.channels", "2"}, | ||||||
|               ports.emplace(global.id, std::move(port)); |                               {"audio.position", "FL,FR"}}, | ||||||
|             } |                              pipewire::node::type, pipewire::node::version, | ||||||
|  |                              pipewire::update_strategy::none) | ||||||
|             link(target, core); |                      .get())); | ||||||
|           } |   metadata_listener.on<pipewire::registry_event::global>( | ||||||
|         } |       [&](const auto &global) { globalEvent(global); }); | ||||||
|       }); |   metadata_listener.on<pipewire::registry_event::global_removed>( | ||||||
|  |       [&](const std::uint32_t id) { globalRemovedEvent(id); }); | ||||||
|   reg_events.on<pipewire::registry_event::global_removed>( | } | ||||||
|       [&](const std::uint32_t id) { |  | ||||||
|         if (nodes.count(id)) { |  | ||||||
|           auto info = nodes.at(id); |  | ||||||
|           std::string name = getTarget(info.props); |  | ||||||
|           if (name == nullstr) |  | ||||||
|             return; |  | ||||||
|           qDebug(virtmicLog) << QString("Removed: %1") |  | ||||||
|                                     .arg(QString::fromStdString(name)) |  | ||||||
|                                     .toUtf8() |  | ||||||
|                                     .data(); |  | ||||||
|           nodes.erase(id); |  | ||||||
|         } |  | ||||||
|         if (ports.count(id)) { |  | ||||||
|           ports.erase(id); |  | ||||||
|         } |  | ||||||
|         if (links.count(id)) { |  | ||||||
|           links.erase(id); |  | ||||||
|         } |  | ||||||
|       }); |  | ||||||
|  |  | ||||||
|  | void Virtmic::run() { | ||||||
|   while (true) { |   while (true) { | ||||||
|     main_loop.run(); |     main_loop.run(); | ||||||
|   } |   } | ||||||
| } | } | ||||||
|  |  | ||||||
| } // namespace Virtmic | void Virtmic::link() { | ||||||
|  |   for (const auto &[port_id, port] : ports) { | ||||||
|  |     auto port_info = port.info(); | ||||||
|  |     if (!virt_fl || !virt_fr) | ||||||
|  |       continue; | ||||||
|  |  | ||||||
|  |     if (links.count(port_id)) | ||||||
|  |       continue; | ||||||
|  |  | ||||||
|  |     if (port_info.direction == pipewire::port_direction::input) | ||||||
|  |       continue; | ||||||
|  |  | ||||||
|  |     if (!port_info.props.count("node.id")) | ||||||
|  |       continue; | ||||||
|  |  | ||||||
|  |     auto parent_id = std::stoul(port_info.props["node.id"]); | ||||||
|  |  | ||||||
|  |     if (!nodes.count(parent_id)) | ||||||
|  |       continue; | ||||||
|  |  | ||||||
|  |     auto &parent = nodes[parent_id]; | ||||||
|  |     QString name; | ||||||
|  |     if (parent.props.count("application.name") && | ||||||
|  |         parent.props["application.name"] != "") | ||||||
|  |       name = QString::fromStdString(parent.props["application.name"]); | ||||||
|  |     else | ||||||
|  |       name = QString::fromStdString(parent.props["application.process.binary"]); | ||||||
|  |  | ||||||
|  |     if (name == target || | ||||||
|  |         (target == "[All Desktop Audio]" && !EXCLUDE_TARGETS.contains(name))) { | ||||||
|  |       auto fl = port_info.props["audio.channel"] == "FL"; | ||||||
|  |       links.emplace(port_id, | ||||||
|  |                     *core.create_simple<pipewire::link>(fl ? virt_fl->info().id | ||||||
|  |                                                            : virt_fr->info().id, | ||||||
|  |                                                         port_id) | ||||||
|  |                          .get()); | ||||||
|  |       qDebug(virtmicLog) << QString("Link: %1:%2 -> %3") | ||||||
|  |                                 .arg(name) | ||||||
|  |                                 .arg(port_id) | ||||||
|  |                                 .arg(fl ? virt_fl->info().id | ||||||
|  |                                         : virt_fr->info().id) | ||||||
|  |                                 .toUtf8() | ||||||
|  |                                 .data(); | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void Virtmic::unlink() { links.clear(); } | ||||||
|  |  | ||||||
|  | void Virtmic::globalEvent(const pipewire::global &global) { | ||||||
|  |   if (global.type == pipewire::node::type) { | ||||||
|  |     auto node = *reg.bind<pipewire::node>(global.id).get(); | ||||||
|  |     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(name)) | ||||||
|  |                               .toUtf8() | ||||||
|  |                               .data(); | ||||||
|  |  | ||||||
|  |     if (!nodes.count(global.id)) { | ||||||
|  |       nodes.emplace(global.id, node.info()); | ||||||
|  |       link(); | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |   if (global.type == pipewire::port::type) { | ||||||
|  |     auto port = *reg.bind<pipewire::port>(global.id).get(); | ||||||
|  |     auto info = port.info(); | ||||||
|  |  | ||||||
|  |     if (info.props.count("node.id")) { | ||||||
|  |       auto node_id = std::stoul(info.props["node.id"]); | ||||||
|  |  | ||||||
|  |       if (node_id == virtual_mic.get()->id() && | ||||||
|  |           info.direction == pipewire::port_direction::input) { | ||||||
|  |         if (info.props["audio.channel"] == "FL") { | ||||||
|  |           virt_fl = std::make_unique<pipewire::port>(std::move(port)); | ||||||
|  |         } else { | ||||||
|  |           virt_fr = std::make_unique<pipewire::port>(std::move(port)); | ||||||
|  |         } | ||||||
|  |       } else { | ||||||
|  |         ports.emplace(global.id, std::move(port)); | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       link(); | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void Virtmic::globalRemovedEvent(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(name)) | ||||||
|  |                               .toUtf8() | ||||||
|  |                               .data(); | ||||||
|  |     nodes.erase(id); | ||||||
|  |   } | ||||||
|  |   if (ports.count(id)) { | ||||||
|  |     ports.erase(id); | ||||||
|  |   } | ||||||
|  |   if (links.count(id)) { | ||||||
|  |     links.erase(id); | ||||||
|  |   } | ||||||
|  | } | ||||||
|   | |||||||
| @@ -1,12 +1,63 @@ | |||||||
| #pragma once | #pragma once | ||||||
|  |  | ||||||
| #include <QString> | #include <cr/channel.hpp> | ||||||
| #include <QVector> |  | ||||||
| #include <iostream> | #include <iostream> | ||||||
|  | #include <rohrkabel/channel/channel.hpp> | ||||||
|  | #include <rohrkabel/main_loop.hpp> | ||||||
|  | #include <rohrkabel/registry/registry.hpp> | ||||||
|  |  | ||||||
| namespace Virtmic { | #include <QMap> | ||||||
|  | #include <QScopedPointer> | ||||||
|  | #include <QStringList> | ||||||
|  | #include <QThread> | ||||||
|  |  | ||||||
| QVector<QString> getTargets(); | class Virtmic : public QObject { | ||||||
| void start(QString _target); |   Q_OBJECT | ||||||
|  | public: | ||||||
|  |   static void setTarget(QString target); | ||||||
|  |   static void getTargets(); | ||||||
|  |  | ||||||
| } // namespace Virtmic | public: | ||||||
|  |   struct set_target { | ||||||
|  |     QString name; | ||||||
|  |   }; | ||||||
|  |   struct get_targets {}; | ||||||
|  |   struct terminate {}; | ||||||
|  |   struct new_targets { | ||||||
|  |     QStringList targets; | ||||||
|  |   }; | ||||||
|  |  | ||||||
|  | protected: | ||||||
|  |   static void instance(); | ||||||
|  |  | ||||||
|  | protected: | ||||||
|  |   Virtmic(pipewire::receiver<set_target, terminate> receiver, | ||||||
|  |           cr::sender<new_targets> sender); | ||||||
|  |   void run(); | ||||||
|  |  | ||||||
|  | private: | ||||||
|  |   std::unique_ptr<pipewire::receiver<set_target, terminate>> m_receiver; | ||||||
|  |   std::unique_ptr<cr::sender<new_targets>> m_sender; | ||||||
|  |  | ||||||
|  |   const QStringList EXCLUDE_TARGETS{"Chromium input", "discord-screenaudio"}; | ||||||
|  |   QString target; | ||||||
|  |  | ||||||
|  |   pipewire::main_loop main_loop = pipewire::main_loop(); | ||||||
|  |   pipewire::context context = pipewire::context(main_loop); | ||||||
|  |   pipewire::core core = pipewire::core(context); | ||||||
|  |   pipewire::registry reg = pipewire::registry(core); | ||||||
|  |  | ||||||
|  |   pipewire::registry_listener metadata_listener = | ||||||
|  |       reg.listen<pipewire::registry_listener>(); | ||||||
|  |   std::unique_ptr<pipewire::proxy> virtual_mic; | ||||||
|  |  | ||||||
|  |   std::map<uint32_t, pipewire::port> ports; | ||||||
|  |   std::unique_ptr<pipewire::port> virt_fl, virt_fr; | ||||||
|  |   std::map<uint32_t, pipewire::node_info> nodes; | ||||||
|  |   std::map<uint32_t, pipewire::link> links; | ||||||
|  |  | ||||||
|  |   void link(); | ||||||
|  |   void unlink(); | ||||||
|  |   void globalEvent(const pipewire::global &global); | ||||||
|  |   void globalRemovedEvent(const std::uint32_t id); | ||||||
|  | }; | ||||||
|   | |||||||
							
								
								
									
										1
									
								
								submodules/channel
									
									
									
									
									
										Submodule
									
								
							
							
								
								
								
								
								
							
						
						
									
										1
									
								
								submodules/channel
									
									
									
									
									
										Submodule
									
								
							 Submodule submodules/channel added at 6977815409
									
								
							 Submodule submodules/rohrkabel updated: 04bfb921c4...8a7705be07
									
								
							
		Reference in New Issue
	
	Block a user