Repository: electron/electron
Pull request: fix: pulseaudio stream and icon names

#### Description of Change

Fixes https://github.com/electron/electron/issues/27581

Patch Chromium to use `platform_util::GetXdgAppId()` with fallback to argv0 as PA_PROP_APPLICATION_ICON_NAME. \
And `electron::GetPossiblyOverriddenApplicationName()` for input stream pa_context name. 
Disable `AudioServiceOutOfProcess` "feature" to make the above possible, and make the old code, that changed output stream names, work.

This replaces hard-codded kBrowserDisplayName (chromium-browser) that was used for PA_PROP_APPLICATION_ICON_NAME, and PRODUCT_STRING (Chromium) that was used for pa_context names (now it's just a fallback).

This is done to make audio streams recognizable in tools like qpwgrapth and general audio managers, instead of having 20 "Chromium" outputs and "Chromium input" inputs, that are actually coming from completely different applications.

| Before  | After |
|-------|-----|
| <img width="441" height="402" alt="image" src="https://github.com/user-attachments/assets/3b6ef2b2-9b33-493c-974d-41c0974f6c4e" /> | <img width="443" height="409" alt="image" src="https://github.com/user-attachments/assets/70e899bf-6546-41e1-b826-73958290dbf7" /> |
| <img width="439" height="445" alt="image" src="https://github.com/user-attachments/assets/c499aff0-91ad-499a-875b-86727d2d3efb" /> | <img width="331" height="409" alt="image" src="https://github.com/user-attachments/assets/fbe53afd-f9bc-4f23-832c-83e00a612632" /> | 

First screenshot shows Plasma's tray audio manager, the second is from qpwgrapth

#### Checklist

- [ ] PR description included and stakeholders cc'd
- [ ] `npm test` passes
- [ ] tests are [changed or added](https://github.com/electron/electron/blob/main/docs/development/testing.md)
- [ ] relevant API documentation, tutorials, and examples are updated and follow the [documentation style guide](https://github.com/electron/electron/blob/main/docs/development/style-guide.md)
- [x] [PR release notes](https://github.com/electron/clerk/blob/main/README.md) describe the change in a way relevant to app developers, and are [capitalized, punctuated, and past tense](https://github.com/electron/clerk/blob/main/README.md#examples).

#### Release Notes

Notes: On Linux, apps should now display their names and icons in audio managers instead of all being labeled as Chromium.


Changed files:
filename: patches/chromium/.patches
patch: @@ -147,3 +147,4 @@ fix_pass_trigger_for_global_shortcuts_on_wayland.patch
 feat_plumb_node_integration_in_worker_through_workersettings.patch
 fix_restore_sdk_inputs_cross-toolchain_deps_for_macos.patch
 fix_use_fresh_lazynow_for_onendworkitemimpl_after_didruntask.patch
+fix_pulseaudio_stream_and_icon_names.patch

filename: patches/chromium/fix_pulseaudio_stream_and_icon_names.patch
patch: @@ -0,0 +1,120 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Damglador <vse.stopchanskyi@gmail.com>
+Date: Fri, 26 Dec 2025 21:26:43 +0100
+Subject: fix: pulseaudio stream and icon names
+
+Use platform_util::GetXdgAppId() with fallback to argv0 as PA_PROP_APPLICATION_ICON_NAME.
+Use electron::GetPossiblyOverriddenApplicationName()
+to set environment variable "ELECTRON_PA_APP_NAME" in audio_service.cc,
+to use it in pulse_util.cc for setting input/output pa_context name.
+
+This replaces hard-codded kBrowserDisplayName that was used for PA_PROP_APPLICATION_ICON_NAME,
+and PRODUCT_STRING that was used for pa_context names.
+
+This is done to make audio streams recognizable in tools like qpwgrapth and general audio managers,
+instead of having 20 "Chromium" outputs and "Chromium input" inputs, that are actually coming from
+completely different applications.
+
+This patch can be removed when upstream starts using AudioManager::SetGlobalAppName()
+for all pa_context names (and when actually works with AudioServiceOutOfProcess).
+
+diff --git a/content/browser/audio/audio_service.cc b/content/browser/audio/audio_service.cc
+index 70615782c50d18606c3baa42a223e54f8619bc07..fb67e69f9ff46b432236b46913a1b10dd8302887 100644
+--- a/content/browser/audio/audio_service.cc
++++ b/content/browser/audio/audio_service.cc
+@@ -29,6 +29,9 @@
+ #include "services/audio/public/mojom/audio_service.mojom.h"
+ #include "services/audio/service.h"
+ #include "services/audio/service_factory.h"
++#if BUILDFLAG(IS_LINUX)
++#include "electron/shell/common/application_info.h"
++#endif
+ 
+ #if BUILDFLAG(ENABLE_PASSTHROUGH_AUDIO_CODECS) && BUILDFLAG(IS_WIN)
+ #define PASS_EDID_ON_COMMAND_LINE 1
+@@ -109,6 +112,10 @@ void LaunchAudioServiceOutOfProcess(
+     mojo::PendingReceiver<audio::mojom::AudioService> receiver,
+     uint32_t codec_bitmask) {
+   std::vector<std::string> switches;
++#if BUILDFLAG(IS_LINUX)
++  // Set ELECTRON_PA_APP_NAME variable for pulse_util to grab and set pa_context name
++  setenv("ELECTRON_PA_APP_NAME", electron::GetPossiblyOverriddenApplicationName().c_str(), 1);
++#endif
+ #if BUILDFLAG(IS_MAC)
+   // On Mac, the audio service requires a CFRunLoop provided by a
+   // UI MessageLoop type, to run AVFoundation and CoreAudio code.
+diff --git a/media/audio/pulse/pulse_util.cc b/media/audio/pulse/pulse_util.cc
+index a08e42a464a3894cbf2b8e3cf8a320a33423b719..e5d69506e1585710a2540c91ca51cba7a4692575 100644
+--- a/media/audio/pulse/pulse_util.cc
++++ b/media/audio/pulse/pulse_util.cc
+@@ -10,6 +10,7 @@
+ #include <memory>
+ #include <type_traits>
+ 
++#include "base/command_line.h"
+ #include "base/files/file_path.h"
+ #include "base/logging.h"
+ #include "base/memory/ptr_util.h"
+@@ -20,6 +21,7 @@
+ #include "build/branding_buildflags.h"
+ #include "media/audio/audio_device_description.h"
+ #include "media/base/audio_timestamp_helper.h"
++#include "electron/shell/common/platform_util.h"
+ 
+ #if defined(DLOPEN_PULSEAUDIO)
+ #include "media/audio/pulse/pulse_stubs.h"
+@@ -36,10 +38,8 @@ namespace pulse {
+ namespace {
+ 
+ #if BUILDFLAG(GOOGLE_CHROME_BRANDING)
+-constexpr char kBrowserDisplayName[] = "google-chrome";
+ #define PRODUCT_STRING "Google Chrome"
+ #else
+-constexpr char kBrowserDisplayName[] = "chromium-browser";
+ #define PRODUCT_STRING "Chromium"
+ #endif
+ 
+@@ -236,7 +236,7 @@ bool InitPulse(pa_threaded_mainloop** mainloop, pa_context** context) {
+ 
+   pa_mainloop_api* pa_mainloop_api = pa_threaded_mainloop_get_api(pa_mainloop);
+   pa_context* pa_context =
+-      pa_context_new(pa_mainloop_api, PRODUCT_STRING " input");
++      pa_context_new(pa_mainloop_api, getenv("ELECTRON_PA_APP_NAME"));
+   if (!pa_context) {
+     pa_threaded_mainloop_free(pa_mainloop);
+     return false;
+@@ -464,8 +464,11 @@ bool CreateInputStream(pa_threaded_mainloop* mainloop,
+   // Create a new recording stream and
+   // tells PulseAudio what the stream icon should be.
+   ScopedPropertyList property_list;
++  const std::string cmd_name =
++    base::CommandLine::ForCurrentProcess()->GetProgram().BaseName().value();
++  const std::string app_id = platform_util::GetXdgAppId().value_or(cmd_name);
+   pa_proplist_sets(property_list.get(), PA_PROP_APPLICATION_ICON_NAME,
+-                   kBrowserDisplayName);
++                   app_id.c_str());
+   *stream = pa_stream_new_with_proplist(context, "RecordStream",
+                                         &sample_specifications, map,
+                                         property_list.get());
+@@ -526,7 +529,7 @@ bool CreateOutputStream(raw_ptr<pa_threaded_mainloop>* mainloop,
+ 
+   pa_mainloop_api* pa_mainloop_api = pa_threaded_mainloop_get_api(*mainloop);
+   *context = pa_context_new(
+-      pa_mainloop_api, app_name.empty() ? PRODUCT_STRING : app_name.c_str());
++      pa_mainloop_api, getenv("ELECTRON_PA_APP_NAME"));
+   RETURN_ON_FAILURE(*context, "Failed to create PulseAudio context.");
+ 
+   // A state callback must be set before calling pa_threaded_mainloop_lock() or
+@@ -574,8 +577,11 @@ bool CreateOutputStream(raw_ptr<pa_threaded_mainloop>* mainloop,
+   // Open playback stream and
+   // tell PulseAudio what the stream icon should be.
+   ScopedPropertyList property_list;
++  const std::string cmd_name =
++    base::CommandLine::ForCurrentProcess()->GetProgram().BaseName().value();
++  const std::string app_id = platform_util::GetXdgAppId().value_or(cmd_name);
+   pa_proplist_sets(property_list.get(), PA_PROP_APPLICATION_ICON_NAME,
+-                   kBrowserDisplayName);
++                   app_id.c_str());
+   *stream = pa_stream_new_with_proplist(
+       *context, "Playback", &sample_specifications, map, property_list.get());
+   RETURN_ON_FAILURE(*stream, "failed to create PA playback stream");