diff -pruN 1.1.5-1/.github/actions/build/action.yml 1.2.0-1/.github/actions/build/action.yml
--- 1.1.5-1/.github/actions/build/action.yml	2024-07-14 16:58:47.000000000 +0000
+++ 1.2.0-1/.github/actions/build/action.yml	1970-01-01 00:00:00.000000000 +0000
@@ -1,94 +0,0 @@
-name: 'Build'
-description: 'Builds OBS and the plugin'
-inputs:
-  obs-version:
-    description: 'OBS Studio version'
-    required: true
-  additional-deps:
-    description: 'Extra system dependencies to install'
-    required: false
-    default: ''
-  additional-obs-cmake-flags:
-    description: 'Extra flags to pass to CMake when building OBS'
-    required: false
-    default: ''
-  plugin-output-path:
-    description: 'Path where the archive containing the built plugin will be saved'
-    required: true
-outputs:
-  random-number:
-    description: "Random number"
-    value: ${{ steps.random-number-generator.outputs.random-number }}
-    
-runs:
-  using: "composite"
-  steps:
-      - name: Restore OBS from cache
-        uses: actions/cache@v4
-        id: cache-obs
-        with:
-          path: for-${{ inputs.obs-version }}/obs/
-          key: ${{ inputs.obs-version }}
-      - name: Checkout OBS
-        if: steps.cache-obs.outputs.cache-hit != 'true'
-        uses: actions/checkout@v4
-        with:
-          repository: 'obsproject/obs-studio'
-          path: 'for-${{ inputs.obs-version }}/obs-src'
-          ref: ${{ inputs.obs-version }}
-          submodules: 'recursive'
-      - name: 'Install system dependencies'
-        shell: bash
-        run: |
-          sudo apt update
-          sudo apt install cmake ninja-build pkg-config clang clang-format build-essential curl ccache git zsh\
-                           libavcodec-dev libavdevice-dev libavfilter-dev libavformat-dev libavutil-dev libswresample-dev libswscale-dev\
-                           libcurl4-openssl-dev\
-                           libxcb1-dev libx11-xcb-dev\
-                           libgl1-mesa-dev\
-                           libglvnd-dev\
-                           libgles2-mesa\
-                           libgles2-mesa-dev\
-                           libpipewire-0.3-dev\
-                           $ADDITIONAL_DEPS
-        env:
-          ADDITIONAL_DEPS: ${{ inputs.additional-deps }}
-      - name: 'Configure OBS'
-        if: steps.cache-obs.outputs.cache-hit != 'true'
-        shell: bash
-        working-directory: for-${{ inputs.obs-version }}
-        run: cmake -B obs-src/build -S obs-src $ADDITIONAL_OBS_CMAKE_FLAGS -DENABLE_BROWSER=OFF -DENABLE_UI=OFF -DENABLE_SCRIPTING=OFF -DENABLE_PULSEAUDIO=OFF -DENABLE_WAYLAND=OFF -DENABLE_PLUGINS=OFF
-        env:
-          ADDITIONAL_OBS_CMAKE_FLAGS: ${{ inputs.additional-obs-cmake-flags }}
-      - name: 'Build OBS'
-        if: steps.cache-obs.outputs.cache-hit != 'true'
-        shell: bash
-        working-directory: for-${{ inputs.obs-version }}
-        run: cmake --build obs-src/build -j4
-      - name: 'Install OBS'
-        if: steps.cache-obs.outputs.cache-hit != 'true'
-        shell: bash
-        working-directory: for-${{ inputs.obs-version }}
-        run: cmake --install obs-src/build --prefix obs
-      - name: 'Checkout'
-        uses: actions/checkout@v4
-        with:
-          path: 'for-${{ inputs.obs-version }}/plugin'
-      - name: 'Configure'
-        shell: bash
-        working-directory: for-${{ inputs.obs-version }}
-        run: cmake -B plugin/build -S plugin -DCMAKE_BUILD_TYPE=RelWithDebInfo -Dlibobs_DIR="$PWD/obs/lib/cmake/libobs/"
-      - name: 'Build'
-        shell: bash
-        working-directory: for-${{ inputs.obs-version }}
-        run: cmake --build plugin/build -j4
-      - name: 'Package'
-        shell: bash
-        working-directory: for-${{ inputs.obs-version }}
-        run: |
-          mkdir -p linux-pipewire-audio/bin/64bit linux-pipewire-audio/data/locale
-          cp ./plugin/build/linux-pipewire-audio.so ./linux-pipewire-audio/bin/64bit/linux-pipewire-audio.so
-          cp ./plugin/data/locale/en-US.ini ./linux-pipewire-audio/data/locale/en-US.ini
-          tar -zcvf $PLUGIN_OUTPUT_PATH linux-pipewire-audio
-        env:
-          PLUGIN_OUTPUT_PATH: ${{ github.workspace }}/${{ inputs.plugin-output-path }}
diff -pruN 1.1.5-1/.github/workflows/main.yml 1.2.0-1/.github/workflows/main.yml
--- 1.1.5-1/.github/workflows/main.yml	2024-07-14 16:58:47.000000000 +0000
+++ 1.2.0-1/.github/workflows/main.yml	2025-02-14 08:30:21.000000000 +0000
@@ -13,32 +13,67 @@ on:
       - '**'
 
 jobs:
-  build-plugin-versions:
-    name: 'Build Plugin Versions'
+  build-plugin:
+    strategy:
+      matrix:
+        obs-version: ['28.0.0', '30.2.0']
+    name: 'Build Plugin'
     runs-on: ubuntu-latest
-
     steps:
-      - name: 'Checkout'
-        uses: actions/checkout@v4
-      - name: Build for OBS >= 28
-        uses: ./.github/actions/build
+      - name: Restore OBS from cache
+        uses: actions/cache@v4
+        id: cache-obs
         with:
-          obs-version: '28.0.0'
-          plugin-output-path: 'linux-pipewire-audio-obs28.tar.gz'
-      - name: 'Upload'
-        uses: actions/upload-artifact@v4
+          path: ${{ github.workspace }}/obs/
+          key: ${{ matrix.obs-version }}
+      - name: Checkout OBS
+        if: steps.cache-obs.outputs.cache-hit != 'true'
+        uses: actions/checkout@v4
         with:
-          name: linux-pipewire-audio-obs28
-          path: linux-pipewire-audio-obs28.tar.gz
-      - name: Build for OBS >= 30.2
-        uses: ./.github/actions/build
+          repository: 'obsproject/obs-studio'
+          path: 'obs-src'
+          ref: ${{ matrix.obs-version }}
+          submodules: 'recursive'
+      - name: 'Install system dependencies'
+        run: |
+          sudo apt update
+          sudo apt install cmake ninja-build pkg-config clang clang-format build-essential curl ccache git zsh\
+                           libavcodec-dev libavdevice-dev libavfilter-dev libavformat-dev libavutil-dev libswresample-dev libswscale-dev\
+                           libcurl4-openssl-dev\
+                           libxcb1-dev libx11-xcb-dev\
+                           libgl1-mesa-dev\
+                           libglvnd-dev\
+                           libgles2-mesa\
+                           libgles2-mesa-dev\
+                           libpipewire-0.3-dev\
+                           uthash-dev libjansson-dev
+      - name: 'Configure OBS'
+        if: steps.cache-obs.outputs.cache-hit != 'true'
+        run: cmake -B obs-src/build -S obs-src -DOBS_CMAKE_VERSION=3 -DENABLE_BROWSER=OFF -DENABLE_UI=OFF -DENABLE_SCRIPTING=OFF -DENABLE_PULSEAUDIO=OFF -DENABLE_WAYLAND=OFF -DENABLE_PLUGINS=OFF
+      - name: 'Build OBS'
+        if: steps.cache-obs.outputs.cache-hit != 'true'
+        run: cmake --build obs-src/build -j4
+      - name: 'Install OBS'
+        if: steps.cache-obs.outputs.cache-hit != 'true'
+        run: cmake --install obs-src/build --prefix obs
+      - name: 'Checkout'
+        uses: actions/checkout@v4
         with:
-          obs-version: '30.2.0'
-          additional-deps: 'uthash-dev libjansson-dev'
-          additional-obs-cmake-flags: '-DOBS_CMAKE_VERSION=3'
-          plugin-output-path: 'linux-pipewire-audio-obs30-2.tar.gz'
+          path: 'plugin'
+      - name: 'Configure'
+        run: cmake -B ./plugin/build -S ./plugin -DCMAKE_BUILD_TYPE=RelWithDebInfo -Dlibobs_DIR="$GITHUB_WORKSPACE/obs/lib/cmake/libobs/"
+      - name: 'Build'
+        run: cmake --build ./plugin/build -j4
+      - name: 'Package'
+        run: |
+          mkdir -p ./linux-pipewire-audio/bin/64bit
+          cp ./plugin/build/linux-pipewire-audio.so ./linux-pipewire-audio/bin/64bit/linux-pipewire-audio.so
+          cp -r ./plugin/data/ ./linux-pipewire-audio/data/
+          tar -zcvf linux-pipewire-audio-$OBS_VERSION.tar.gz linux-pipewire-audio
+        env:
+          OBS_VERSION: ${{ matrix.obs-version }}
       - name: 'Upload'
         uses: actions/upload-artifact@v4
         with:
-          name: linux-pipewire-audio-obs30-2
-          path: linux-pipewire-audio-obs30-2.tar.gz
\ No newline at end of file
+          path: linux-pipewire-audio-${{ matrix.obs-version }}.tar.gz
+          name: linux-pipewire-audio-${{ matrix.obs-version }}
diff -pruN 1.1.5-1/CMakeLists.txt 1.2.0-1/CMakeLists.txt
--- 1.1.5-1/CMakeLists.txt	2024-07-14 16:58:47.000000000 +0000
+++ 1.2.0-1/CMakeLists.txt	2025-02-14 08:30:21.000000000 +0000
@@ -33,6 +33,7 @@ set(linux-pipewire-audio_LIBRARIES
 )
 
 target_link_libraries(linux-pipewire-audio ${linux-pipewire-audio_LIBRARIES})
+target_compile_options(linux-pipewire-audio PRIVATE -Wall)
 
 include_directories(SYSTEM
 	${linux-pipewire-audio_INCLUDES}
@@ -40,5 +41,5 @@ include_directories(SYSTEM
 
 set_target_properties(linux-pipewire-audio PROPERTIES PREFIX "")
 
-install(TARGETS linux-pipewire-audio LIBRARY DESTINATION ${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBDIR}/obs-plugins)
-install(DIRECTORY data/locale DESTINATION ${CMAKE_INSTALL_PREFIX}/share/obs/obs-plugins/linux-pipewire-audio)
\ No newline at end of file
+install(TARGETS linux-pipewire-audio LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}/obs-plugins)
+install(DIRECTORY data/locale DESTINATION ${CMAKE_INSTALL_DATADIR}/obs/obs-plugins/linux-pipewire-audio)
diff -pruN 1.1.5-1/README.md 1.2.0-1/README.md
--- 1.1.5-1/README.md	2024-07-14 16:58:47.000000000 +0000
+++ 1.2.0-1/README.md	2025-02-14 08:30:21.000000000 +0000
@@ -17,10 +17,21 @@ compatibility layer should be enough, bu
 If applications aren't showing up in the plugin, your system may be missing one of those components.  
 See the [PipeWire wiki](https://gitlab.freedesktop.org/pipewire/pipewire/-/wikis/home) for more info.
 
-### Binary installation
+### Installation
 Get the `linux-pipewire-audio-(version).tar.gz` archive from the [latest release](https://github.com/dimtpap/obs-pipewire-audio-capture/releases/latest)  
 If OBS Studio is installed as a
-- Regular package: Extract the archive in `~/.config/obs-studio/plugins/`
+- Regular package: Extract the archive in `~/.config/obs-studio/plugins/`  
+  Your files should look like this
+  ```
+  /home/user/.config/obs-studio/plugins
+  ├── linux-pipewire-audio
+  │   ├── bin
+  │   │   └── 64bit
+  │   │       └── linux-pipewire-audio.so
+  │   └── data
+  │       └── locale
+  │           ...           
+  ```
 - Flatpak:
 > [!IMPORTANT]
 > ***THIS INSTALLATION METHOD IS UNSUPPORTED BY THE OBS STUDIO TEAM AND CAN BREAK AT ANY TIME***  
@@ -29,18 +40,16 @@ If OBS Studio is installed as a
 > against the latest OBS Studio.
 > 
 > Note that native OBS Studio packages do not have this problem.
-  - Extract the archive in `~/.var/app/com.obsproject.Studio/config/obs-studio/plugins/`  
-    - Note: If the plugin isn't working try running OBS using `flatpak run --filesystem=xdg-run/pipewire-0 com.obsproject.Studio`
-  or run `flatpak override --filesystem=xdg-run/pipewire-0 com.obsproject.Studio` and then open OBS as usual
 
-### Building and installing
+  Extract the archive in `~/.var/app/com.obsproject.Studio/config/obs-studio/plugins/`  
+    Note: If the plugin isn't working run `flatpak override --filesystem=xdg-run/pipewire-0 com.obsproject.Studio`
+### Building
 Ensure you have CMake, PipeWire and OBS Studio/libobs development packages, then in the repo's root:
 ```sh
 cmake -B build -DCMAKE_INSTALL_PREFIX="/usr" -DCMAKE_BUILD_TYPE=RelWithDebInfo
-cd build
-make
-make install #May need root
+cmake --build build
+# To install it system-wide:
+cmake --install build
 ```
 ## Inclusion in upstream OBS Studio
-
 This plugin is currently in the process of being worked on to merge into upstream OBS Studio. See https://github.com/obsproject/obs-studio/pull/6207
diff -pruN 1.1.5-1/data/locale/en-US.ini 1.2.0-1/data/locale/en-US.ini
--- 1.1.5-1/data/locale/en-US.ini	2024-07-14 16:58:47.000000000 +0000
+++ 1.2.0-1/data/locale/en-US.ini	2025-02-14 08:30:21.000000000 +0000
@@ -1,9 +1,15 @@
 PipeWireAudioCaptureInput="Audio Input Capture (PipeWire)"
 PipeWireAudioCaptureOutput="Audio Output Capture (PipeWire)"
 PipeWireAudioCaptureApplication="Application Audio Capture (PipeWire)"
+AppCaptureMode="Capture Mode"
+SingleApp="Single application"
+MultipleApps="Multiple applications"
 MatchPriority="Match Priority"
-MatchBinaryFirst="Match by binary name, fallback to app name"
-MatchAppNameFirst="Match by app name, fallback to binary name"
+MatchBinaryFirst="Match by executable name, fallback to app name"
+MatchAppNameFirst="Match by app name, fallback to executable name"
 Device="Device"
 Application="Application"
-ExceptApp="Capture all apps except selected"
\ No newline at end of file
+Applications="Applications"
+ExceptApp="Capture all apps except selected"
+SelectedApps="Selected Apps"
+AddToSelected="Add selection"
\ No newline at end of file
diff -pruN 1.1.5-1/data/locale/fr-FR.ini 1.2.0-1/data/locale/fr-FR.ini
--- 1.1.5-1/data/locale/fr-FR.ini	2024-07-14 16:58:47.000000000 +0000
+++ 1.2.0-1/data/locale/fr-FR.ini	2025-02-14 08:30:21.000000000 +0000
@@ -1,9 +1,15 @@
 PipeWireAudioCaptureInput="Capture d'entrée audio (PipeWire)"
 PipeWireAudioCaptureOutput="Capture de sortie audio (PipeWire)"
 PipeWireAudioCaptureApplication="Capture d'application audio (PipeWire)"
+AppCaptureMode="Mode de capture"
+SingleApp="Application unique"
+MultipleApps="Applications multiples"
 MatchPriority="Priorité de correspondance"
-MatchBinaryFirst="Correspondance par nom de binaire, repli par nom d'application"
-MatchAppNameFirst="Correspondance par nom d'application, repli par nom de binaire"
+MatchBinaryFirst="Correspondance par nom d'executable, repli par nom d'application"
+MatchAppNameFirst="Correspondance par nom d'application, repli par nom d'executable"
 Device="Appareil"
 Application="Application"
+Applications="Applications"
 ExceptApp="Capturer toutes les applications sauf celles sélectionnées"
+SelectedApps="Applications sélectionnées"
+AddToSelected="Ajouter à la sélection"
diff -pruN 1.1.5-1/data/locale/pt-BR.ini 1.2.0-1/data/locale/pt-BR.ini
--- 1.1.5-1/data/locale/pt-BR.ini	1970-01-01 00:00:00.000000000 +0000
+++ 1.2.0-1/data/locale/pt-BR.ini	2025-02-14 08:30:21.000000000 +0000
@@ -0,0 +1,9 @@
+PipeWireAudioCaptureInput="Captura de Entrada de Áudio (PipeWire)"
+PipeWireAudioCaptureOutput="Saída de Captura de Áudio (PipeWire)"
+PipeWireAudioCaptureApplication="Captura de Áudio de Aplicativo (PipeWire)"
+MatchPriority="Prioridade da Correspondência"
+MatchBinaryFirst="Corresponder ao nome do binário, se falhar, ao nome do app"
+MatchAppNameFirst="Corresponder pelo nome do app, se falhar, ao nome do binário"
+Device="Dispositivo"
+Application="Aplicativo"
+ExceptApp="Capturar todos os apps, exceto o selecionado"
diff -pruN 1.1.5-1/debian/changelog 1.2.0-1/debian/changelog
--- 1.1.5-1/debian/changelog	2024-07-24 07:30:29.000000000 +0000
+++ 1.2.0-1/debian/changelog	2025-04-11 17:45:09.000000000 +0000
@@ -1,3 +1,11 @@
+obs-pipewire-audio-capture (1.2.0-1) unstable; urgency=medium
+
+  * New upstream release.
+  * Bump Standards-Version to 4.7.2.
+  * Bump Copyright years to 2025.
+
+ -- Rhonda D'Vine <rhonda@debian.org>  Fri, 11 Apr 2025 19:45:09 +0200
+
 obs-pipewire-audio-capture (1.1.5-1) unstable; urgency=medium
 
   * Initial release. (Closes: #1056654)
diff -pruN 1.1.5-1/debian/control 1.2.0-1/debian/control
--- 1.1.5-1/debian/control	2024-07-24 07:30:29.000000000 +0000
+++ 1.2.0-1/debian/control	2025-04-11 17:45:09.000000000 +0000
@@ -9,7 +9,7 @@ Build-Depends:
  pkgconf,
  libobs-dev,
  libpipewire-0.3-dev,
-Standards-Version: 4.7.0
+Standards-Version: 4.7.2
 Homepage: https://github.com/dimtpap/obs-pipewire-audio-capture/
 Vcs-Browser: https://salsa.debian.org/debian/obs-pipewire-audio-capture
 Vcs-Git: https://salsa.debian.org/debian/obs-pipewire-audio-capture.git
diff -pruN 1.1.5-1/debian/copyright 1.2.0-1/debian/copyright
--- 1.1.5-1/debian/copyright	2024-07-24 07:30:29.000000000 +0000
+++ 1.2.0-1/debian/copyright	2025-04-11 17:45:09.000000000 +0000
@@ -6,13 +6,13 @@ Upstream-Contact: Dimitris Papaioannou <
 Files:
  *
 Copyright:
- 2022-2024 Dimitris Papaioannou <dimtpap@protonmail.com>
+ 2022-2025 Dimitris Papaioannou <dimtpap@protonmail.com>
 License: GPL-2+
 
 Files:
  debian/*
 Copyright:
- 2024 Rhonda D'Vine <rhonda@debian.org>
+ 2024-2025 Rhonda D'Vine <rhonda@debian.org>
 License: MIT
 
 License: GPL-2+
diff -pruN 1.1.5-1/src/pipewire-audio-capture-app.c 1.2.0-1/src/pipewire-audio-capture-app.c
--- 1.1.5-1/src/pipewire-audio-capture-app.c	2024-07-14 16:58:47.000000000 +0000
+++ 1.2.0-1/src/pipewire-audio-capture-app.c	2025-02-14 08:30:21.000000000 +0000
@@ -36,7 +36,7 @@ struct target_node {
 	uint32_t client_id;
 	uint32_t id;
 	struct obs_pw_audio_proxy_list ports;
-	uint32_t *p_n_targets;
+	uint32_t *p_n_nodes;
 
 	struct spa_hook node_listener;
 };
@@ -63,7 +63,16 @@ struct capture_sink_port {
 	uint32_t id;
 };
 
-enum match_priority { BINARY_NAME, APP_NAME };
+enum capture_mode { CAPTURE_MODE_SINGLE, CAPTURE_MODE_MULTIPLE };
+enum match_priority { MATCH_PRIORITY_BINARY_NAME, MATCH_PRIORITY_APP_NAME };
+
+#define SETTING_CAPTURE_MODE "CaptureMode"
+#define SETTING_MATCH_PRIORITY "MatchPriorty"
+#define SETTING_EXCLUDE_SELECTIONS "ExceptApp"
+#define SETTING_SELECTION_SINGLE "TargetName"
+#define SETTING_SELECTION_MULTIPLE "apps"
+#define SETTING_AVAILABLE_APPS "AppToAdd"
+#define SETTING_ADD_TO_SELECTIONS "AddToSelected"
 
 /** This source basically works like this:
     - Keep track of output streams and their ports, system sinks and the default sink
@@ -74,6 +83,8 @@ enum match_priority { BINARY_NAME, APP_N
     - Connect any registered or new stream ports to the sink
 */
 struct obs_pw_audio_capture_app {
+	obs_source_t *source;
+
 	struct obs_pw_audio_instance pw;
 
 	/** The app capture sink automatically mixes
@@ -104,12 +115,13 @@ struct obs_pw_audio_capture_app {
 
 	struct obs_pw_audio_proxy_list clients;
 
-	struct obs_pw_audio_proxy_list targets;
-	uint32_t n_targets;
+	struct obs_pw_audio_proxy_list nodes;
+	uint32_t n_nodes;
 
+	enum capture_mode capture_mode;
 	enum match_priority match_priority;
-	struct dstr target;
-	bool except_app;
+	bool except;
+	DARRAY(const char *) selections;
 };
 
 /* System sinks */
@@ -198,7 +210,7 @@ static void node_destroy_cb(void *data)
 
 	obs_pw_audio_proxy_list_clear(&node->ports);
 
-	(*node->p_n_targets)--;
+	(*node->p_n_nodes)--;
 
 	bfree((void *)node->binary);
 	bfree((void *)node->app_name);
@@ -259,40 +271,39 @@ static void register_target_node(struct
 	node->binary = NULL;
 	node->id = global_id;
 	node->client_id = client_id;
-	node->p_n_targets = &pwac->n_targets;
+	node->p_n_nodes = &pwac->n_nodes;
 	obs_pw_audio_proxy_list_init(&node->ports, NULL, port_destroy_cb);
 
-	pwac->n_targets++;
+	pwac->n_nodes++;
 
-	obs_pw_audio_proxy_list_append(&pwac->targets, node_proxy);
+	obs_pw_audio_proxy_list_append(&pwac->nodes, node_proxy);
 	pw_proxy_add_object_listener(node_proxy, &node->node_listener, &node_events, node);
 }
 
 static bool node_is_targeted(struct obs_pw_audio_capture_app *pwac, struct target_node *node)
 {
-	if (dstr_is_empty(&pwac->target)) {
-		return false;
-	}
+	bool targeted = false;
+	for (size_t i = 0; i < pwac->selections.num && !targeted; i++) {
+		const char *selection = pwac->selections.array[i];
 
-	bool targeted = (dstr_cmpi(&pwac->target, node->binary) == 0 || dstr_cmpi(&pwac->target, node->app_name) == 0 ||
-					 dstr_cmpi(&pwac->target, node->name) == 0);
+		targeted = (astrcmpi(selection, node->binary) == 0 || astrcmpi(selection, node->app_name) == 0 ||
+					astrcmpi(selection, node->name) == 0);
 
-	if (!targeted && node->client_id) {
-		struct obs_pw_audio_proxy_list_iter iter;
-		obs_pw_audio_proxy_list_iter_init(&iter, &pwac->clients);
-
-		struct target_client *client;
-		while (obs_pw_audio_proxy_list_iter_next(&iter, (void **)&client)) {
-			if (client->id == node->client_id) {
-				targeted =
-					(dstr_cmpi(&pwac->target, client->binary) == 0 || dstr_cmpi(&pwac->target, client->app_name) == 0);
+		if (!targeted && node->client_id) {
+			struct obs_pw_audio_proxy_list_iter iter;
+			obs_pw_audio_proxy_list_iter_init(&iter, &pwac->clients);
 
-				break;
+			struct target_client *client;
+			while (obs_pw_audio_proxy_list_iter_next(&iter, (void **)&client)) {
+				if (client->id == node->client_id) {
+					targeted = (astrcmpi(selection, client->binary) == 0 || astrcmpi(selection, client->app_name) == 0);
+					break;
+				}
 			}
 		}
 	}
 
-	return targeted ^ pwac->except_app;
+	return targeted ^ pwac->except;
 }
 /* ------------------------------------------------- */
 
@@ -346,8 +357,6 @@ static void link_port_to_sink(struct obs
 														PW_VERSION_LINK, &link_props->dict,
 														sizeof(struct capture_sink_link));
 
-	obs_pw_audio_instance_sync(&pwac->pw);
-
 	pw_properties_free(link_props);
 
 	if (!link_proxy) {
@@ -443,26 +452,20 @@ static void destroy_sink_links(struct ob
 	obs_pw_audio_proxy_list_clear(&pwac->sink.links);
 }
 
-static void connect_targets(struct obs_pw_audio_capture_app *pwac, const char *target, bool except)
+static void connect_targets(struct obs_pw_audio_capture_app *pwac)
 {
-	pwac->except_app = except;
-
-	if (target) {
-		dstr_copy(&pwac->target, target);
-	}
-
 	if (!pwac->sink.proxy) {
 		return;
 	}
 
 	destroy_sink_links(pwac);
 
-	if (dstr_is_empty(&pwac->target)) {
+	if (pwac->selections.num == 0) {
 		return;
 	}
 
 	struct obs_pw_audio_proxy_list_iter iter;
-	obs_pw_audio_proxy_list_iter_init(&iter, &pwac->targets);
+	obs_pw_audio_proxy_list_iter_init(&iter, &pwac->nodes);
 
 	struct target_node *node;
 	while (obs_pw_audio_proxy_list_iter_next(&iter, (void **)&node)) {
@@ -481,18 +484,17 @@ static bool make_capture_sink(struct obs
 	 * and because of https://gitlab.freedesktop.org/pipewire/pipewire/-/merge_requests/1564#note_1861698, which works the same for
 	 * deciding what nodes need an audio adapter.
 	 */
-	struct pw_properties *sink_props =
-		pw_properties_new(PW_KEY_NODE_NAME, "OBS", PW_KEY_NODE_DESCRIPTION, "OBS App Audio Capture Sink",
-						  PW_KEY_FACTORY_NAME, "support.null-audio-sink", PW_KEY_MEDIA_CLASS, "Audio/Sink/Internal",
-						  PW_KEY_NODE_VIRTUAL, "true", SPA_KEY_AUDIO_POSITION, position, NULL);
+	struct pw_properties *sink_props = pw_properties_new(PW_KEY_FACTORY_NAME, "support.null-audio-sink",
+														 PW_KEY_MEDIA_CLASS, "Audio/Sink/Internal", PW_KEY_NODE_VIRTUAL,
+														 "true", SPA_KEY_AUDIO_POSITION, position, NULL);
+
+	pw_properties_setf(sink_props, PW_KEY_NODE_NAME, "OBS: %s", obs_source_get_name(pwac->source));
 
 	pw_properties_setf(sink_props, PW_KEY_AUDIO_CHANNELS, "%u", channels);
 
 	pwac->sink.proxy =
 		pw_core_create_object(pwac->pw.core, "adapter", PW_TYPE_INTERFACE_Node, PW_VERSION_NODE, &sink_props->dict, 0);
 
-	obs_pw_audio_instance_sync(&pwac->pw);
-
 	pw_properties_free(sink_props);
 
 	if (!pwac->sink.proxy) {
@@ -521,7 +523,7 @@ static bool make_capture_sink(struct obs
 	blog(LOG_INFO, "[pipewire] Created app capture sink %u with %u channels and position %s", pwac->sink.id, channels,
 		 position);
 
-	connect_targets(pwac, NULL, pwac->except_app);
+	connect_targets(pwac);
 
 	pwac->sink.autoconnect_targets = true;
 
@@ -547,7 +549,6 @@ static void destroy_capture_sink(struct
 
 	pwac->sink.autoconnect_targets = false;
 	pw_proxy_destroy(pwac->sink.proxy);
-	obs_pw_audio_instance_sync(&pwac->pw);
 }
 /* ------------------------------------------------- */
 
@@ -703,7 +704,7 @@ static void on_global_cb(void *data, uin
 		} else if (astrcmpi(dir, "out") == 0) {
 			/* Possibly a target port */
 			struct obs_pw_audio_proxy_list_iter iter;
-			obs_pw_audio_proxy_list_iter_init(&iter, &pwac->targets);
+			obs_pw_audio_proxy_list_iter_init(&iter, &pwac->nodes);
 
 			struct target_node *temp, *node = NULL;
 			while (obs_pw_audio_proxy_list_iter_next(&iter, (void **)&temp)) {
@@ -772,40 +773,52 @@ static const struct pw_registry_events r
 /* ------------------------------------------------- */
 
 /* Source */
-static void *pipewire_audio_capture_app_create(obs_data_t *settings, obs_source_t *source)
+static bool add_app_clicked(obs_properties_t *properties, obs_property_t *property, void *data)
 {
-	struct obs_pw_audio_capture_app *pwac = bzalloc(sizeof(struct obs_pw_audio_capture_app));
+	UNUSED_PARAMETER(properties);
+	UNUSED_PARAMETER(property);
 
-	if (!obs_pw_audio_instance_init(&pwac->pw, &registry_events, pwac, true, false, source)) {
-		obs_pw_audio_instance_destroy(&pwac->pw);
+	obs_source_t *source = data;
 
-		bfree(pwac);
-		return NULL;
+	obs_data_t *settings = obs_source_get_settings(source);
+	const char *app_to_add = obs_data_get_string(settings, SETTING_AVAILABLE_APPS);
+
+	obs_data_array_t *selections = obs_data_get_array(settings, SETTING_SELECTION_MULTIPLE);
+	if (obs_data_array_count(selections) == 0) {
+		obs_data_array_release(selections);
+
+		selections = obs_data_array_create();
+		obs_data_set_array(settings, SETTING_SELECTION_MULTIPLE, selections);
 	}
 
-	obs_pw_audio_proxy_list_init(&pwac->targets, NULL, node_destroy_cb);
-	obs_pw_audio_proxy_list_init(&pwac->clients, NULL, client_destroy_cb);
-	obs_pw_audio_proxy_list_init(&pwac->sink.links, link_bound_cb, link_destroy_cb);
-	obs_pw_audio_proxy_list_init(&pwac->system_sinks, NULL, system_sink_destroy_cb);
+	/* Don't add if selection is already in the list */
 
-	pwac->sink.id = SPA_ID_INVALID;
-	dstr_init(&pwac->sink.position);
+	bool should_add = true;
+	for (size_t i = 0; i < obs_data_array_count(selections) && should_add; i++) {
+		obs_data_t *item = obs_data_array_item(selections, i);
 
-	pwac->match_priority = obs_data_get_int(settings, "MatchPriority");
-	dstr_init_copy(&pwac->target, obs_data_get_string(settings, "TargetName"));
-	pwac->except_app = obs_data_get_bool(settings, "ExceptApp");
+		should_add = astrcmpi(obs_data_get_string(item, "value"), app_to_add) != 0;
 
-	obs_pw_audio_instance_sync(&pwac->pw);
-	pw_thread_loop_wait(pwac->pw.thread_loop);
-	pw_thread_loop_unlock(pwac->pw.thread_loop);
+		obs_data_release(item);
+	}
 
-	return pwac;
-}
+	if (should_add) {
+		obs_data_t *new_entry = obs_data_create();
+		obs_data_set_bool(new_entry, "hidden", false);
+		obs_data_set_bool(new_entry, "selected", false);
+		obs_data_set_string(new_entry, "value", app_to_add);
 
-static void pipewire_audio_capture_app_defaults(obs_data_t *settings)
-{
-	obs_data_set_default_int(settings, "MatchPriority", BINARY_NAME);
-	obs_data_set_default_bool(settings, "ExceptApp", false);
+		obs_data_array_push_back(selections, new_entry);
+
+		obs_data_release(new_entry);
+
+		obs_source_update(source, settings);
+	}
+
+	obs_data_array_release(selections);
+	obs_data_release(settings);
+
+	return should_add;
 }
 
 static int cmp_targets(const void *a, const void *b)
@@ -819,33 +832,26 @@ static const char *choose_display_string
 										 const char *app_name)
 {
 	switch (pwac->match_priority) {
-	case BINARY_NAME:
+	case MATCH_PRIORITY_BINARY_NAME:
 		return binary ? binary : app_name;
-	case APP_NAME:
+	case MATCH_PRIORITY_APP_NAME:
 		return app_name ? app_name : binary;
+	default:
+		return NULL;
 	}
 }
 
-static bool match_priority_modified(void *data, obs_properties_t *properties, obs_property_t *property,
-									obs_data_t *settings)
+static void populate_avaiable_apps_list(obs_property_t *list, struct obs_pw_audio_capture_app *pwac)
 {
-	UNUSED_PARAMETER(property);
-	UNUSED_PARAMETER(settings);
-
-	struct obs_pw_audio_capture_app *pwac = data;
-
-	obs_property_t *targets = obs_properties_get(properties, "TargetName");
-	obs_property_list_clear(targets);
-
-	DARRAY(char *) targets_arr;
-	da_init(targets_arr);
+	DARRAY(const char *) targets;
+	da_init(targets);
 
 	pw_thread_loop_lock(pwac->pw.thread_loop);
 
-	da_reserve(targets_arr, pwac->n_targets);
+	da_reserve(targets, pwac->n_nodes);
 
 	struct obs_pw_audio_proxy_list_iter iter;
-	obs_pw_audio_proxy_list_iter_init(&iter, &pwac->targets);
+	obs_pw_audio_proxy_list_iter_init(&iter, &pwac->nodes);
 
 	struct target_node *node;
 	while (obs_pw_audio_proxy_list_iter_next(&iter, (void **)&node)) {
@@ -855,7 +861,7 @@ static bool match_priority_modified(void
 			display = node->name;
 		}
 
-		da_push_back(targets_arr, &display);
+		da_push_back(targets, &display);
 	}
 
 	obs_pw_audio_proxy_list_iter_init(&iter, &pwac->clients);
@@ -865,45 +871,199 @@ static bool match_priority_modified(void
 		const char *display = choose_display_string(pwac, client->binary, client->app_name);
 
 		if (display) {
-			da_push_back(targets_arr, &display);
+			da_push_back(targets, &display);
 		}
 	}
 
 	/* Show just one entry per target */
 
-	qsort(targets_arr.array, targets_arr.num, sizeof(char *), cmp_targets);
+	qsort(targets.array, targets.num, sizeof(const char *), cmp_targets);
 
-	for (size_t i = 0; i < targets_arr.num; i++) {
-		if (i == 0 || strcmp(targets_arr.array[i - 1], targets_arr.array[i]) != 0) {
-			obs_property_list_add_string(targets, targets_arr.array[i], NULL);
+	for (size_t i = 0; i < targets.num; i++) {
+		if (i == 0 || strcmp(targets.array[i - 1], targets.array[i]) != 0) {
+			obs_property_list_add_string(list, targets.array[i], targets.array[i]);
 		}
 	}
 
 	pw_thread_loop_unlock(pwac->pw.thread_loop);
 
-	da_free(targets_arr);
+	da_free(targets);
+}
+
+static bool capture_mode_modified(void *data, obs_properties_t *properties, obs_property_t *property,
+								  obs_data_t *settings)
+{
+	UNUSED_PARAMETER(property);
+
+	struct obs_pw_audio_capture_app *pwac = data;
+
+	enum capture_mode mode = obs_data_get_int(settings, SETTING_CAPTURE_MODE);
+
+	switch (mode) {
+	case CAPTURE_MODE_SINGLE: {
+		obs_properties_remove_by_name(properties, SETTING_SELECTION_MULTIPLE);
+		obs_properties_remove_by_name(properties, SETTING_AVAILABLE_APPS);
+		obs_properties_remove_by_name(properties, SETTING_ADD_TO_SELECTIONS);
+
+		obs_property_t *available_apps = obs_properties_add_list(properties, SETTING_SELECTION_SINGLE,
+																 obs_module_text("Application"),
+																 OBS_COMBO_TYPE_EDITABLE, OBS_COMBO_FORMAT_STRING);
+
+		populate_avaiable_apps_list(available_apps, pwac);
+
+		break;
+	}
+	case CAPTURE_MODE_MULTIPLE: {
+		obs_properties_remove_by_name(properties, SETTING_SELECTION_SINGLE);
+
+		obs_properties_add_editable_list(properties, SETTING_SELECTION_MULTIPLE, obs_module_text("SelectedApps"),
+										 OBS_EDITABLE_LIST_TYPE_STRINGS, NULL, NULL);
+
+		obs_property_t *available_apps = obs_properties_add_list(properties, SETTING_AVAILABLE_APPS,
+																 obs_module_text("Applications"), OBS_COMBO_TYPE_LIST,
+																 OBS_COMBO_FORMAT_STRING);
+
+		populate_avaiable_apps_list(available_apps, pwac);
+
+		obs_properties_add_button2(properties, SETTING_ADD_TO_SELECTIONS, obs_module_text("AddToSelected"),
+								   add_app_clicked, pwac->source);
+
+		break;
+	}
+	}
 
 	return true;
 }
 
-static obs_properties_t *pipewire_audio_capture_app_properties(void *data)
+static bool match_priority_modified(void *data, obs_properties_t *properties, obs_property_t *property,
+									obs_data_t *settings)
 {
+	UNUSED_PARAMETER(property);
+
 	struct obs_pw_audio_capture_app *pwac = data;
 
-	obs_properties_t *p = obs_properties_create();
+	enum capture_mode mode = obs_data_get_int(settings, SETTING_CAPTURE_MODE);
+
+	obs_property_t *targets = NULL;
+	switch (mode) {
+	default:
+	case CAPTURE_MODE_SINGLE:
+		targets = obs_properties_get(properties, SETTING_SELECTION_SINGLE);
+		break;
+	case CAPTURE_MODE_MULTIPLE:
+		targets = obs_properties_get(properties, SETTING_AVAILABLE_APPS);
+		break;
+	}
+
+	if (targets == NULL) {
+		return false;
+	}
+
+	obs_property_list_clear(targets);
+
+	populate_avaiable_apps_list(targets, pwac);
+
+	return true;
+}
+
+static void build_selections(struct obs_pw_audio_capture_app *pwac, obs_data_t *settings)
+{
+	switch (pwac->capture_mode) {
+	case CAPTURE_MODE_SINGLE: {
+		const char *selection = bstrdup(obs_data_get_string(settings, SETTING_SELECTION_SINGLE));
+		da_push_back(pwac->selections, &selection);
+		break;
+	}
+	case CAPTURE_MODE_MULTIPLE: {
+		obs_data_array_t *selections = obs_data_get_array(settings, SETTING_SELECTION_MULTIPLE);
+		for (size_t i = 0; i < obs_data_array_count(selections); i++) {
+			obs_data_t *item = obs_data_array_item(selections, i);
+
+			const char *selection = bstrdup(obs_data_get_string(item, "value"));
+			da_push_back(pwac->selections, &selection);
+
+			obs_data_release(item);
+		}
+		obs_data_array_release(selections);
+		break;
+	}
+	}
+}
+
+static void clear_selections(struct obs_pw_audio_capture_app *pwac)
+{
+	for (size_t i = 0; i < pwac->selections.num; i++) {
+		const char *selection = pwac->selections.array[i];
+		bfree((void *)selection);
+	}
+
+	pwac->selections.num = 0;
+}
+
+static void *pipewire_audio_capture_app_create(obs_data_t *settings, obs_source_t *source)
+{
+	struct obs_pw_audio_capture_app *pwac = bzalloc(sizeof(struct obs_pw_audio_capture_app));
+
+	if (!obs_pw_audio_instance_init(&pwac->pw, &registry_events, pwac, true, false, source)) {
+		obs_pw_audio_instance_destroy(&pwac->pw);
+
+		bfree(pwac);
+		return NULL;
+	}
+
+	pwac->source = source;
+
+	obs_pw_audio_proxy_list_init(&pwac->nodes, NULL, node_destroy_cb);
+	obs_pw_audio_proxy_list_init(&pwac->clients, NULL, client_destroy_cb);
+	obs_pw_audio_proxy_list_init(&pwac->sink.links, link_bound_cb, link_destroy_cb);
+	obs_pw_audio_proxy_list_init(&pwac->system_sinks, NULL, system_sink_destroy_cb);
+
+	pwac->sink.id = SPA_ID_INVALID;
+	dstr_init(&pwac->sink.position);
+
+	pwac->capture_mode = obs_data_get_int(settings, SETTING_CAPTURE_MODE);
+	pwac->match_priority = obs_data_get_int(settings, SETTING_MATCH_PRIORITY);
+	pwac->except = obs_data_get_bool(settings, SETTING_EXCLUDE_SELECTIONS);
+
+	da_init(pwac->selections);
+	build_selections(pwac, settings);
+
+	pw_thread_loop_unlock(pwac->pw.thread_loop);
 
-	obs_property_t *match_priority = obs_properties_add_list(p, "MatchPriority", obs_module_text("MatchPriority"),
-															 OBS_COMBO_TYPE_LIST, OBS_COMBO_FORMAT_INT);
-	obs_property_list_add_int(match_priority, obs_module_text("MatchBinaryFirst"), BINARY_NAME);
-	obs_property_list_add_int(match_priority, obs_module_text("MatchAppNameFirst"), APP_NAME);
+	return pwac;
+}
 
-	obs_properties_add_list(p, "TargetName", obs_module_text("Application"), OBS_COMBO_TYPE_EDITABLE,
-							OBS_COMBO_FORMAT_STRING);
+static void pipewire_audio_capture_app_defaults(obs_data_t *settings)
+{
+	obs_data_set_default_int(settings, SETTING_CAPTURE_MODE, CAPTURE_MODE_SINGLE);
+	obs_data_set_default_int(settings, SETTING_MATCH_PRIORITY, MATCH_PRIORITY_BINARY_NAME);
+	obs_data_set_default_bool(settings, SETTING_EXCLUDE_SELECTIONS, false);
+
+	obs_data_array_t *arr = obs_data_array_create();
+	obs_data_set_default_array(settings, SETTING_SELECTION_MULTIPLE, arr);
+	obs_data_array_release(arr);
+}
 
-	obs_properties_add_bool(p, "ExceptApp", obs_module_text("ExceptApp"));
+static obs_properties_t *pipewire_audio_capture_app_properties(void *data)
+{
+	struct obs_pw_audio_capture_app *pwac = data;
 
+	obs_properties_t *p = obs_properties_create();
+
+	obs_property_t *capture_mode = obs_properties_add_list(p, SETTING_CAPTURE_MODE, obs_module_text("AppCaptureMode"),
+														   OBS_COMBO_TYPE_LIST, OBS_COMBO_FORMAT_INT);
+	obs_property_list_add_int(capture_mode, obs_module_text("SingleApp"), CAPTURE_MODE_SINGLE);
+	obs_property_list_add_int(capture_mode, obs_module_text("MultipleApps"), CAPTURE_MODE_MULTIPLE);
+	obs_property_set_modified_callback2(capture_mode, capture_mode_modified, pwac);
+
+	obs_property_t *match_priority = obs_properties_add_list(
+		p, SETTING_MATCH_PRIORITY, obs_module_text("MatchPriority"), OBS_COMBO_TYPE_LIST, OBS_COMBO_FORMAT_INT);
+	obs_property_list_add_int(match_priority, obs_module_text("MatchBinaryFirst"), MATCH_PRIORITY_BINARY_NAME);
+	obs_property_list_add_int(match_priority, obs_module_text("MatchAppNameFirst"), MATCH_PRIORITY_APP_NAME);
 	obs_property_set_modified_callback2(match_priority, match_priority_modified, pwac);
 
+	obs_properties_add_bool(p, SETTING_EXCLUDE_SELECTIONS, obs_module_text("ExceptApp"));
+
 	return p;
 }
 
@@ -911,21 +1071,17 @@ static void pipewire_audio_capture_app_u
 {
 	struct obs_pw_audio_capture_app *pwac = data;
 
-	pwac->match_priority = obs_data_get_int(settings, "MatchPriority");
-	const char *new_target = obs_data_get_string(settings, "TargetName");
-	bool except = obs_data_get_bool(settings, "ExceptApp");
-
 	pw_thread_loop_lock(pwac->pw.thread_loop);
 
-	if (except == pwac->except_app && dstr_cmpi(&pwac->target, new_target) == 0) {
-		pw_thread_loop_unlock(pwac->pw.thread_loop);
-		return;
-	}
+	pwac->capture_mode = obs_data_get_int(settings, SETTING_CAPTURE_MODE);
+	pwac->match_priority = obs_data_get_int(settings, SETTING_MATCH_PRIORITY);
+	pwac->except = obs_data_get_bool(settings, SETTING_EXCLUDE_SELECTIONS);
 
-	connect_targets(pwac, new_target, except);
+	clear_selections(pwac);
+	build_selections(pwac, settings);
+
+	connect_targets(pwac);
 
-	obs_pw_audio_instance_sync(&pwac->pw);
-	pw_thread_loop_wait(pwac->pw.thread_loop);
 	pw_thread_loop_unlock(pwac->pw.thread_loop);
 }
 
@@ -953,7 +1109,7 @@ static void pipewire_audio_capture_app_d
 
 	pw_thread_loop_lock(pwac->pw.thread_loop);
 
-	obs_pw_audio_proxy_list_clear(&pwac->targets);
+	obs_pw_audio_proxy_list_clear(&pwac->nodes);
 	obs_pw_audio_proxy_list_clear(&pwac->system_sinks);
 
 	obs_pw_audio_proxy_list_clear(&pwac->clients);
@@ -970,7 +1126,9 @@ static void pipewire_audio_capture_app_d
 	obs_pw_audio_instance_destroy(&pwac->pw);
 
 	dstr_free(&pwac->sink.position);
-	dstr_free(&pwac->target);
+
+	clear_selections(pwac);
+	da_free(pwac->selections);
 
 	bfree(pwac);
 }
diff -pruN 1.1.5-1/src/pipewire-audio-capture-device.c 1.2.0-1/src/pipewire-audio-capture-device.c
--- 1.1.5-1/src/pipewire-audio-capture-device.c	2024-07-14 16:58:47.000000000 +0000
+++ 1.2.0-1/src/pipewire-audio-capture-device.c	2025-02-14 08:30:21.000000000 +0000
@@ -37,10 +37,13 @@ struct target_node {
 };
 
 enum capture_type {
-	INPUT,
-	OUTPUT,
+	CAPTURE_TYPE_INPUT,
+	CAPTURE_TYPE_OUTPUT,
 };
 
+#define SETTING_TARGET_SERIAL "TargetId"
+#define SETTING_TARGET_NAME "TargetName"
+
 struct obs_pw_audio_capture_device {
 	obs_source_t *source;
 
@@ -142,12 +145,15 @@ static void on_node_info_cb(void *data,
 
 	struct obs_pw_audio_capture_device *pwac = n->pwac;
 
-	/** If this is the default device and the stream is not already connected to it
-	  * or the stream is unconnected and this node has the desired target name */
-	if ((pwac->default_info.autoconnect && pwac->connected_serial != n->serial &&
-		 !dstr_is_empty(&pwac->default_info.name) && dstr_cmp(&pwac->default_info.name, n->name) == 0) ||
-		(pw_stream_get_state(pwac->pw.audio.stream, NULL) == PW_STREAM_STATE_UNCONNECTED &&
-		 !dstr_is_empty(&pwac->target_name) && dstr_cmp(&pwac->target_name, n->name) == 0)) {
+	bool not_streamed = pwac->connected_serial != n->serial;
+	bool has_default_node_name = !dstr_is_empty(&pwac->default_info.name) &&
+								 dstr_cmp(&pwac->default_info.name, n->name) == 0;
+	bool is_new_default_node = not_streamed && has_default_node_name;
+
+	bool stream_is_unconnected = pw_stream_get_state(pwac->pw.audio.stream, NULL) == PW_STREAM_STATE_UNCONNECTED;
+	bool node_has_target_name = !dstr_is_empty(&pwac->target_name) && dstr_cmp(&pwac->target_name, n->name) == 0;
+
+	if ((pwac->default_info.autoconnect && is_new_default_node) || (stream_is_unconnected && node_has_target_name)) {
 		start_streaming(pwac, n);
 	}
 }
@@ -239,9 +245,9 @@ static void on_global_cb(void *data, uin
 		}
 
 		/* Target device */
-		if ((pwac->capture_type == INPUT &&
+		if ((pwac->capture_type == CAPTURE_TYPE_INPUT &&
 			 (strcmp(media_class, "Audio/Source") == 0 || strcmp(media_class, "Audio/Source/Virtual") == 0)) ||
-			(pwac->capture_type == OUTPUT &&
+			(pwac->capture_type == CAPTURE_TYPE_OUTPUT &&
 			 (strcmp(media_class, "Audio/Sink") == 0 || strcmp(media_class, "Audio/Duplex") == 0))) {
 
 			const char *ser = spa_dict_lookup(props, PW_KEY_OBJECT_SERIAL);
@@ -268,7 +274,8 @@ static void on_global_cb(void *data, uin
 		}
 
 		if (!obs_pw_audio_default_node_metadata_listen(&pwac->default_info.metadata, &pwac->pw, id,
-													   pwac->capture_type == OUTPUT, default_node_cb, pwac)) {
+													   pwac->capture_type == CAPTURE_TYPE_OUTPUT, default_node_cb,
+													   pwac)) {
 			blog(LOG_WARNING, "[pipewire] Failed to get default metadata, cannot detect default audio devices");
 		}
 	}
@@ -285,7 +292,8 @@ static void *pipewire_audio_capture_crea
 {
 	struct obs_pw_audio_capture_device *pwac = bzalloc(sizeof(struct obs_pw_audio_capture_device));
 
-	if (!obs_pw_audio_instance_init(&pwac->pw, &registry_events, pwac, capture_type == OUTPUT, true, source)) {
+	if (!obs_pw_audio_instance_init(&pwac->pw, &registry_events, pwac, capture_type == CAPTURE_TYPE_OUTPUT, true,
+									source)) {
 		obs_pw_audio_instance_destroy(&pwac->pw);
 
 		bfree(pwac);
@@ -299,19 +307,17 @@ static void *pipewire_audio_capture_crea
 
 	obs_pw_audio_proxy_list_init(&pwac->targets, NULL, node_destroy_cb);
 
-	if (obs_data_get_int(settings, "TargetId") != PW_ID_ANY) {
+	if (obs_data_get_int(settings, SETTING_TARGET_SERIAL) != PW_ID_ANY) {
 		/** Reset id setting, PipeWire node ids may not persist between sessions.
 		  * Connecting to saved target will happen based on the TargetName setting
 		  * once target has connected */
-		obs_data_set_int(settings, "TargetId", 0);
+		obs_data_set_int(settings, SETTING_TARGET_SERIAL, 0);
 	} else {
 		pwac->default_info.autoconnect = true;
 	}
 
-	dstr_init_copy(&pwac->target_name, obs_data_get_string(settings, "TargetName"));
+	dstr_init_copy(&pwac->target_name, obs_data_get_string(settings, SETTING_TARGET_NAME));
 
-	obs_pw_audio_instance_sync(&pwac->pw);
-	pw_thread_loop_wait(pwac->pw.thread_loop);
 	pw_thread_loop_unlock(pwac->pw.thread_loop);
 
 	return pwac;
@@ -319,17 +325,17 @@ static void *pipewire_audio_capture_crea
 
 static void *pipewire_audio_capture_input_create(obs_data_t *settings, obs_source_t *source)
 {
-	return pipewire_audio_capture_create(settings, source, INPUT);
+	return pipewire_audio_capture_create(settings, source, CAPTURE_TYPE_INPUT);
 }
 
 static void *pipewire_audio_capture_output_create(obs_data_t *settings, obs_source_t *source)
 {
-	return pipewire_audio_capture_create(settings, source, OUTPUT);
+	return pipewire_audio_capture_create(settings, source, CAPTURE_TYPE_OUTPUT);
 }
 
 static void pipewire_audio_capture_defaults(obs_data_t *settings)
 {
-	obs_data_set_default_int(settings, "TargetId", PW_ID_ANY);
+	obs_data_set_default_int(settings, SETTING_TARGET_SERIAL, PW_ID_ANY);
 }
 
 static obs_properties_t *pipewire_audio_capture_properties(void *data)
@@ -338,8 +344,8 @@ static obs_properties_t *pipewire_audio_
 
 	obs_properties_t *p = obs_properties_create();
 
-	obs_property_t *targets_list =
-		obs_properties_add_list(p, "TargetId", obs_module_text("Device"), OBS_COMBO_TYPE_LIST, OBS_COMBO_FORMAT_INT);
+	obs_property_t *targets_list = obs_properties_add_list(p, SETTING_TARGET_SERIAL, obs_module_text("Device"),
+														   OBS_COMBO_TYPE_LIST, OBS_COMBO_FORMAT_INT);
 
 	obs_property_list_add_int(targets_list, obs_module_text("Default"), PW_ID_ANY);
 
@@ -347,7 +353,7 @@ static obs_properties_t *pipewire_audio_
 		obs_data_t *settings = obs_source_get_settings(pwac->source);
 		/* Saved target serial may be different from connected because a previously connected
 		   node may have been replaced by one with the same name */
-		obs_data_set_int(settings, "TargetId", pwac->connected_serial);
+		obs_data_set_int(settings, SETTING_TARGET_SERIAL, pwac->connected_serial);
 		obs_data_release(settings);
 	}
 
@@ -370,7 +376,7 @@ static void pipewire_audio_capture_updat
 {
 	struct obs_pw_audio_capture_device *pwac = data;
 
-	uint32_t new_node_serial = obs_data_get_int(settings, "TargetId");
+	uint32_t new_node_serial = obs_data_get_int(settings, SETTING_TARGET_SERIAL);
 
 	pw_thread_loop_lock(pwac->pw.thread_loop);
 
@@ -383,7 +389,7 @@ static void pipewire_audio_capture_updat
 		if (new_node) {
 			start_streaming(pwac, new_node);
 
-			obs_data_set_string(settings, "TargetName", pwac->target_name.array);
+			obs_data_set_string(settings, SETTING_TARGET_NAME, pwac->target_name.array);
 		}
 	}
 
diff -pruN 1.1.5-1/src/pipewire-audio.c 1.2.0-1/src/pipewire-audio.c
--- 1.1.5-1/src/pipewire-audio.c	2024-07-14 16:58:47.000000000 +0000
+++ 1.2.0-1/src/pipewire-audio.c	2025-02-14 08:30:21.000000000 +0000
@@ -201,7 +201,7 @@ static void on_process_cb(void *data)
 		out.data[i] = buf->datas[i].data;
 	}
 
-	if (s->info.sample_rate * s->pos->clock.rate_diff) {
+	if (s->info.sample_rate && s->pos->clock.rate_diff) {
 		/** Taken from PipeWire's implementation of JACK's jack_get_cycle_times
 		  * (https://gitlab.freedesktop.org/pipewire/pipewire/-/blob/0.3.52/pipewire-jack/src/pipewire-jack.c#L5639)
 		  * which is used in the linux-jack plugin to correctly set the timestamp
@@ -280,9 +280,9 @@ int obs_pw_audio_stream_connect(struct o
 		SPA_FORMAT_mediaSubtype, SPA_POD_Id(SPA_MEDIA_SUBTYPE_raw), SPA_FORMAT_AUDIO_channels,
 		SPA_POD_Int(audio_channels), SPA_FORMAT_AUDIO_position,
 		SPA_POD_Array(sizeof(enum spa_audio_channel), SPA_TYPE_Id, audio_channels, pos), SPA_FORMAT_AUDIO_format,
-		SPA_POD_CHOICE_ENUM_Id(8, SPA_AUDIO_FORMAT_U8, SPA_AUDIO_FORMAT_S16_LE, SPA_AUDIO_FORMAT_S32_LE,
-							   SPA_AUDIO_FORMAT_F32_LE, SPA_AUDIO_FORMAT_U8P, SPA_AUDIO_FORMAT_S16P,
-							   SPA_AUDIO_FORMAT_S32P, SPA_AUDIO_FORMAT_F32P));
+		SPA_POD_CHOICE_ENUM_Id(9, SPA_AUDIO_FORMAT_F32P, SPA_AUDIO_FORMAT_U8, SPA_AUDIO_FORMAT_S16_LE,
+							   SPA_AUDIO_FORMAT_S32_LE, SPA_AUDIO_FORMAT_F32_LE, SPA_AUDIO_FORMAT_U8P,
+							   SPA_AUDIO_FORMAT_S16P, SPA_AUDIO_FORMAT_S32P, SPA_AUDIO_FORMAT_F32P));
 
 	struct pw_properties *stream_props = pw_properties_new(NULL, NULL);
 	pw_properties_setf(stream_props, PW_KEY_TARGET_OBJECT, "%u", target_serial);
@@ -348,13 +348,15 @@ bool obs_pw_audio_instance_init(struct o
 	}
 	pw_registry_add_listener(pw->registry, &pw->registry_listener, registry_events, registry_cb_data);
 
+	struct pw_properties *stream_props = pw_properties_new(
+		PW_KEY_MEDIA_NAME, obs_source_get_name(stream_output), PW_KEY_MEDIA_TYPE, "Audio", PW_KEY_MEDIA_CATEGORY,
+		"Capture", PW_KEY_MEDIA_ROLE, "Production", PW_KEY_NODE_WANT_DRIVER, stream_want_driver ? "true" : "false",
+		PW_KEY_STREAM_CAPTURE_SINK, stream_capture_sink ? "true" : "false", NULL);
+
+	pw_properties_setf(stream_props, PW_KEY_NODE_NAME, "OBS: %s", obs_source_get_name(stream_output));
+
 	pw->audio.output = stream_output;
-	pw->audio.stream =
-		pw_stream_new(pw->core, "OBS",
-					  pw_properties_new(PW_KEY_NODE_NAME, "OBS", PW_KEY_NODE_DESCRIPTION, "OBS Audio Capture",
-										PW_KEY_MEDIA_TYPE, "Audio", PW_KEY_MEDIA_CATEGORY, "Capture", PW_KEY_MEDIA_ROLE,
-										"Production", PW_KEY_NODE_WANT_DRIVER, stream_want_driver ? "true" : "false",
-										PW_KEY_STREAM_CAPTURE_SINK, stream_capture_sink ? "true" : "false", NULL));
+	pw->audio.stream = pw_stream_new(pw->core, obs_source_get_name(stream_output), stream_props);
 
 	if (!pw->audio.stream) {
 		blog(LOG_WARNING, "[pipewire] Failed to create stream");
