diff --git a/.github/actions/build-macos-x86_64/action.yml b/.github/actions/build-macos-x86_64/action.yml index 70647c55..92c87333 100644 --- a/.github/actions/build-macos-x86_64/action.yml +++ b/.github/actions/build-macos-x86_64/action.yml @@ -55,8 +55,20 @@ runs: run: arch -x86_64 mvn -B jar:jar surefire:test -Pmacos-cross-x86_64 shell: bash + - name: Archive webrtc-java + uses: actions/upload-artifact@v5 + with: + name: webrtc-java-${{ inputs.platform-name }} + path: webrtc/target/*.jar + + - name: Archive webrtc-java-jni + uses: actions/upload-artifact@v5 + with: + name: webrtc-java-jni-${{ inputs.platform-name }} + path: webrtc-jni/target/*.jar + - name: Deploy - if: ${{ github.event_name != 'pull_request' }} + if: ${{ github.event_name != 'pull_request' && github.repository == 'devopvoid/webrtc-java' }} env: MAVEN_USERNAME: ${{ inputs.maven-username }} MAVEN_TOKEN: ${{ inputs.maven-password }} diff --git a/.github/actions/build/action.yml b/.github/actions/build/action.yml index ac9969b1..c68ac2d6 100644 --- a/.github/actions/build/action.yml +++ b/.github/actions/build/action.yml @@ -62,8 +62,20 @@ runs: run: mvn -B jar:jar surefire:test shell: bash + - name: Archive webrtc-java + uses: actions/upload-artifact@v5 + with: + name: webrtc-java-${{ inputs.platform-name }} + path: webrtc/target/*.jar + + - name: Archive webrtc-java-jni + uses: actions/upload-artifact@v5 + with: + name: webrtc-java-jni-${{ inputs.platform-name }} + path: webrtc-jni/target/*.jar + - name: Deploy - if: ${{ github.event_name != 'pull_request' }} + if: ${{ github.event_name != 'pull_request' && github.repository == 'devopvoid/webrtc-java' }} env: MAVEN_USERNAME: ${{ inputs.maven-username }} MAVEN_TOKEN: ${{ inputs.maven-password }} diff --git a/webrtc-jni/src/main/cpp/CMakeLists.txt b/webrtc-jni/src/main/cpp/CMakeLists.txt index a761f4c0..82f72ae9 100644 --- a/webrtc-jni/src/main/cpp/CMakeLists.txt +++ b/webrtc-jni/src/main/cpp/CMakeLists.txt @@ -110,7 +110,8 @@ elseif(LINUX) set(CXX_LIBS "-static-libgcc") endif() - target_link_libraries(${PROJECT_NAME} ${CXX_LIBS} pulse udev) + target_link_libraries(${PROJECT_NAME} ${CXX_LIBS} udev) + target_link_libraries(${PROJECT_NAME} dl) elseif(WIN32) target_link_libraries(${PROJECT_NAME} dwmapi.lib mf.lib mfreadwrite.lib mfplat.lib mfuuid.lib shcore.lib) endif() diff --git a/webrtc-jni/src/main/cpp/include/media/audio/linux/PulseAudioLoader.h b/webrtc-jni/src/main/cpp/include/media/audio/linux/PulseAudioLoader.h new file mode 100644 index 00000000..4ac742a3 --- /dev/null +++ b/webrtc-jni/src/main/cpp/include/media/audio/linux/PulseAudioLoader.h @@ -0,0 +1,164 @@ +/* + * Copyright 2019 Alex Andres + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PULSE_AUDIO_LOADER_H +#define PULSE_AUDIO_LOADER_H + +#include +#include +#include +#include "Exception.h" + +namespace jni +{ + namespace avdev + { + typedef pa_threaded_mainloop* (*pa_threaded_mainloop_new_t)(); + typedef void (*pa_threaded_mainloop_free_t)(pa_threaded_mainloop* m); + typedef int (*pa_threaded_mainloop_start_t)(pa_threaded_mainloop* m); + typedef void (*pa_threaded_mainloop_stop_t)(pa_threaded_mainloop* m); + typedef pa_mainloop_api* (*pa_threaded_mainloop_get_api_t)(pa_threaded_mainloop* m); + typedef void (*pa_threaded_mainloop_lock_t)(pa_threaded_mainloop* m); + typedef void (*pa_threaded_mainloop_unlock_t)(pa_threaded_mainloop* m); + typedef void (*pa_threaded_mainloop_wait_t)(pa_threaded_mainloop* m); + typedef void (*pa_threaded_mainloop_signal_t)(pa_threaded_mainloop* m, int wait_for_accept); + typedef int (*pa_threaded_mainloop_in_thread_t)(pa_threaded_mainloop* m); + + typedef pa_context* (*pa_context_new_t)(pa_mainloop_api* mainloop, const char* name); + typedef void (*pa_context_unref_t)(pa_context* c); + typedef void (*pa_context_set_state_callback_t)(pa_context* c, pa_context_notify_cb_t cb, void* userdata); + typedef void (*pa_context_set_subscribe_callback_t)(pa_context* c, pa_context_subscribe_cb_t cb, void* userdata); + typedef int (*pa_context_connect_t)(pa_context* c, const char* server, pa_context_flags_t flags, const pa_spawn_api* api); + typedef void (*pa_context_disconnect_t)(pa_context* c); + typedef pa_context_state_t (*pa_context_get_state_t)(const pa_context* c); + typedef pa_operation* (*pa_context_subscribe_t)(pa_context* c, pa_subscription_mask_t m, pa_context_success_cb_t cb, void* userdata); + typedef pa_operation* (*pa_context_get_server_info_t)(pa_context* c, pa_server_info_cb_t cb, void* userdata); + typedef pa_operation* (*pa_context_get_source_info_by_name_t)(pa_context* c, const char* name, pa_source_info_cb_t cb, void* userdata); + typedef pa_operation* (*pa_context_get_source_info_list_t)(pa_context* c, pa_source_info_cb_t cb, void* userdata); + typedef pa_operation* (*pa_context_get_source_info_by_index_t)(pa_context* c, uint32_t idx, pa_source_info_cb_t cb, void* userdata); + typedef pa_operation* (*pa_context_get_sink_info_by_name_t)(pa_context* c, const char* name, pa_sink_info_cb_t cb, void* userdata); + typedef pa_operation* (*pa_context_get_sink_info_list_t)(pa_context* c, pa_sink_info_cb_t cb, void* userdata); + typedef pa_operation* (*pa_context_get_sink_info_by_index_t)(pa_context* c, uint32_t idx, pa_sink_info_cb_t cb, void* userdata); + + typedef void (*pa_operation_unref_t)(pa_operation* o); + typedef pa_operation_state_t (*pa_operation_get_state_t)(const pa_operation* o); + typedef const char* (*pa_proplist_gets_t)(const pa_proplist* p, const char* key); + + class PulseAudioLoader { + public: + static PulseAudioLoader& instance() { + static PulseAudioLoader instance; + return instance; + } + + bool load() { + if (loaded) return true; + + // Try to open the library + lib_handle = dlopen("libpulse.so.0", RTLD_NOW); + if (!lib_handle) return false; + + #define LOAD_SYM(name) \ + name = (name##_t)dlsym(lib_handle, #name); \ + if (!name) { close(); return false; } + + LOAD_SYM(pa_threaded_mainloop_new); + LOAD_SYM(pa_threaded_mainloop_free); + LOAD_SYM(pa_threaded_mainloop_start); + LOAD_SYM(pa_threaded_mainloop_stop); + LOAD_SYM(pa_threaded_mainloop_get_api); + LOAD_SYM(pa_threaded_mainloop_lock); + LOAD_SYM(pa_threaded_mainloop_unlock); + LOAD_SYM(pa_threaded_mainloop_wait); + LOAD_SYM(pa_threaded_mainloop_signal); + LOAD_SYM(pa_threaded_mainloop_in_thread); + + LOAD_SYM(pa_context_new); + LOAD_SYM(pa_context_unref); + LOAD_SYM(pa_context_set_state_callback); + LOAD_SYM(pa_context_set_subscribe_callback); + LOAD_SYM(pa_context_connect); + LOAD_SYM(pa_context_disconnect); + LOAD_SYM(pa_context_get_state); + LOAD_SYM(pa_context_subscribe); + + LOAD_SYM(pa_context_get_server_info); + LOAD_SYM(pa_context_get_source_info_by_name); + LOAD_SYM(pa_context_get_source_info_list); + LOAD_SYM(pa_context_get_source_info_by_index); + LOAD_SYM(pa_context_get_sink_info_by_name); + LOAD_SYM(pa_context_get_sink_info_list); + LOAD_SYM(pa_context_get_sink_info_by_index); + + LOAD_SYM(pa_operation_unref); + LOAD_SYM(pa_operation_get_state); + LOAD_SYM(pa_proplist_gets); + + loaded = true; + return true; + } + + void close() { + if (lib_handle) { + dlclose(lib_handle); + lib_handle = nullptr; + } + loaded = false; + } + + bool isLoaded() const { return loaded; } + + pa_threaded_mainloop_new_t pa_threaded_mainloop_new; + pa_threaded_mainloop_free_t pa_threaded_mainloop_free; + pa_threaded_mainloop_start_t pa_threaded_mainloop_start; + pa_threaded_mainloop_stop_t pa_threaded_mainloop_stop; + pa_threaded_mainloop_get_api_t pa_threaded_mainloop_get_api; + pa_threaded_mainloop_lock_t pa_threaded_mainloop_lock; + pa_threaded_mainloop_unlock_t pa_threaded_mainloop_unlock; + pa_threaded_mainloop_wait_t pa_threaded_mainloop_wait; + pa_threaded_mainloop_signal_t pa_threaded_mainloop_signal; + pa_threaded_mainloop_in_thread_t pa_threaded_mainloop_in_thread; + + pa_context_new_t pa_context_new; + pa_context_unref_t pa_context_unref; + pa_context_set_state_callback_t pa_context_set_state_callback; + pa_context_set_subscribe_callback_t pa_context_set_subscribe_callback; + pa_context_connect_t pa_context_connect; + pa_context_disconnect_t pa_context_disconnect; + pa_context_get_state_t pa_context_get_state; + pa_context_subscribe_t pa_context_subscribe; + + pa_context_get_server_info_t pa_context_get_server_info; + pa_context_get_source_info_by_name_t pa_context_get_source_info_by_name; + pa_context_get_source_info_list_t pa_context_get_source_info_list; + pa_context_get_source_info_by_index_t pa_context_get_source_info_by_index; + pa_context_get_sink_info_by_name_t pa_context_get_sink_info_by_name; + pa_context_get_sink_info_list_t pa_context_get_sink_info_list; + pa_context_get_sink_info_by_index_t pa_context_get_sink_info_by_index; + + pa_operation_unref_t pa_operation_unref; + pa_operation_get_state_t pa_operation_get_state; + pa_proplist_gets_t pa_proplist_gets; + + private: + PulseAudioLoader() : loaded(false), lib_handle(nullptr) {} + bool loaded; + void* lib_handle; + }; + } // namespace avdev +} // namespace jni + +#endif \ No newline at end of file diff --git a/webrtc-jni/src/main/cpp/src/media/audio/linux/PulseAudioDeviceManager.cpp b/webrtc-jni/src/main/cpp/src/media/audio/linux/PulseAudioDeviceManager.cpp index dd1db433..bcdfd8c8 100644 --- a/webrtc-jni/src/main/cpp/src/media/audio/linux/PulseAudioDeviceManager.cpp +++ b/webrtc-jni/src/main/cpp/src/media/audio/linux/PulseAudioDeviceManager.cpp @@ -15,6 +15,7 @@ */ #include "media/audio/linux/PulseAudioDeviceManager.h" +#include "media/audio/linux/PulseAudioLoader.h" #include "Exception.h" namespace jni @@ -23,41 +24,49 @@ namespace jni { PulseAudioDeviceManager::PulseAudioDeviceManager() { - mainloop = pa_threaded_mainloop_new(); + // Try to load PulseAudio symbols dynamically + if (!PulseAudioLoader::instance().load()) { + throw Exception("PulseAudio: Library not found on system."); + } + + // Use the loader instance for function calls + auto& pa = PulseAudioLoader::instance(); + + mainloop = pa.pa_threaded_mainloop_new(); if (mainloop == 0) { throw Exception("PulseAudio: Could not create threaded mainloop"); } - if (pa_threaded_mainloop_start(mainloop) != 0) { - pa_threaded_mainloop_free(mainloop); + if (pa.pa_threaded_mainloop_start(mainloop) != 0) { + pa.pa_threaded_mainloop_free(mainloop); throw Exception("PulseAudio: Could not start threaded mainloop"); } - pa_mainloop_api * mainloopApi = pa_threaded_mainloop_get_api(mainloop); - context = pa_context_new(mainloopApi, "MediaDevices"); + pa_mainloop_api * mainloopApi = pa.pa_threaded_mainloop_get_api(mainloop); + context = pa.pa_context_new(mainloopApi, "MediaDevices"); if (!context) { - pa_threaded_mainloop_free(mainloop); + pa.pa_threaded_mainloop_free(mainloop); throw Exception("PulseAudio: Could not create context"); } - pa_context_set_state_callback(context, stateCallback, mainloop); - pa_context_set_subscribe_callback(context, subscribeCallback, this); + pa.pa_context_set_state_callback(context, stateCallback, mainloop); + pa.pa_context_set_subscribe_callback(context, subscribeCallback, this); - pa_threaded_mainloop_lock(mainloop); + pa.pa_threaded_mainloop_lock(mainloop); - if (pa_context_connect(context, nullptr, PA_CONTEXT_NOFLAGS, nullptr) < 0) { - pa_context_unref(context); - pa_threaded_mainloop_free(mainloop); + if (pa.pa_context_connect(context, nullptr, PA_CONTEXT_NOFLAGS, nullptr) < 0) { + pa.pa_context_unref(context); + pa.pa_threaded_mainloop_free(mainloop); throw Exception("PulseAudio: Could not connect to the context"); } while (true) { - pa_context_state_t contextState = pa_context_get_state(context); + pa_context_state_t contextState = pa.pa_context_get_state(context); if (contextState == PA_CONTEXT_FAILED || contextState == PA_CONTEXT_TERMINATED) { dispose(); @@ -67,20 +76,20 @@ namespace jni break; } - pa_threaded_mainloop_wait(mainloop); + pa.pa_threaded_mainloop_wait(mainloop); } int mask = PA_SUBSCRIPTION_MASK_SINK | PA_SUBSCRIPTION_MASK_SOURCE; pa_subscription_mask_t mask_t = static_cast(mask); - pa_operation * op = pa_context_subscribe(context, mask_t, nullptr, nullptr); + pa_operation * op = pa.pa_context_subscribe(context, mask_t, nullptr, nullptr); - pa_threaded_mainloop_unlock(mainloop); + pa.pa_threaded_mainloop_unlock(mainloop); if (!op) { throw Exception("PulseAudio: Failed to subscribe context"); } - pa_operation_unref(op); + pa.pa_operation_unref(op); } PulseAudioDeviceManager::~PulseAudioDeviceManager() @@ -90,21 +99,23 @@ namespace jni void PulseAudioDeviceManager::dispose() { + auto& pa = PulseAudioLoader::instance(); + if (!mainloop) { return; } - pa_threaded_mainloop_lock(mainloop); + pa.pa_threaded_mainloop_lock(mainloop); if (context) { - pa_context_set_state_callback(context, nullptr, nullptr); - pa_context_disconnect(context); - pa_context_unref(context); + pa.pa_context_set_state_callback(context, nullptr, nullptr); + pa.pa_context_disconnect(context); + pa.pa_context_unref(context); context = nullptr; } - pa_threaded_mainloop_stop(mainloop); - pa_threaded_mainloop_free(mainloop); + pa.pa_threaded_mainloop_stop(mainloop); + pa.pa_threaded_mainloop_free(mainloop); mainloop = nullptr; } @@ -114,30 +125,34 @@ namespace jni throw Exception("PulseAudio: No operation to process"); } - while (pa_operation_get_state(op) == PA_OPERATION_RUNNING) { - pa_threaded_mainloop_wait(main_loop); + auto& pa = PulseAudioLoader::instance(); + + while (pa.pa_operation_get_state(op) == PA_OPERATION_RUNNING) { + pa.pa_threaded_mainloop_wait(main_loop); } - pa_operation_unref(op); + pa.pa_operation_unref(op); } AudioDevicePtr PulseAudioDeviceManager::getDefaultAudioCaptureDevice() { - if (!pa_threaded_mainloop_in_thread(mainloop)) - pa_threaded_mainloop_lock(mainloop); + auto& pa = PulseAudioLoader::instance(); + + if (!pa.pa_threaded_mainloop_in_thread(mainloop)) + pa.pa_threaded_mainloop_lock(mainloop); - pa_operation * op = pa_context_get_server_info(context, serverInfoCallback, this); + pa_operation * op = pa.pa_context_get_server_info(context, serverInfoCallback, this); - if (!pa_threaded_mainloop_in_thread(mainloop)) + if (!pa.pa_threaded_mainloop_in_thread(mainloop)) iterate(mainloop, op); - op = pa_context_get_source_info_by_name(context, defaultCaptureName.c_str(), getSourceInfoCallback, this); + op = pa.pa_context_get_source_info_by_name(context, defaultCaptureName.c_str(), getSourceInfoCallback, this); - if (!pa_threaded_mainloop_in_thread(mainloop)) + if (!pa.pa_threaded_mainloop_in_thread(mainloop)) iterate(mainloop, op); - if (!pa_threaded_mainloop_in_thread(mainloop)) - pa_threaded_mainloop_unlock(mainloop); + if (!pa.pa_threaded_mainloop_in_thread(mainloop)) + pa.pa_threaded_mainloop_unlock(mainloop); AudioDevicePtr defaultDevice = std::make_shared(defaultCaptureDescName, defaultCaptureName); defaultDevice->directionType = AudioDeviceDirectionType::adtCapture; @@ -150,31 +165,35 @@ namespace jni return captureDevices.devices(); } - pa_threaded_mainloop_lock(mainloop); - pa_operation * op = pa_context_get_source_info_list(context, getSourceCallback, this); + auto& pa = PulseAudioLoader::instance(); + + pa.pa_threaded_mainloop_lock(mainloop); + pa_operation * op = pa.pa_context_get_source_info_list(context, getSourceCallback, this); iterate(mainloop, op); - pa_threaded_mainloop_unlock(mainloop); + pa.pa_threaded_mainloop_unlock(mainloop); return captureDevices.devices(); } AudioDevicePtr PulseAudioDeviceManager::getDefaultAudioPlaybackDevice() { - if (!pa_threaded_mainloop_in_thread(mainloop)) - pa_threaded_mainloop_lock(mainloop); + auto& pa = PulseAudioLoader::instance(); - pa_operation * op = pa_context_get_server_info(context, serverInfoCallback, this); + if (!pa.pa_threaded_mainloop_in_thread(mainloop)) + pa.pa_threaded_mainloop_lock(mainloop); - if (!pa_threaded_mainloop_in_thread(mainloop)) + pa_operation * op = pa.pa_context_get_server_info(context, serverInfoCallback, this); + + if (!pa.pa_threaded_mainloop_in_thread(mainloop)) iterate(mainloop, op); - op = pa_context_get_sink_info_by_name(context, defaultPlaybackName.c_str(), getSinkInfoCallback, this); + op = pa.pa_context_get_sink_info_by_name(context, defaultPlaybackName.c_str(), getSinkInfoCallback, this); - if (!pa_threaded_mainloop_in_thread(mainloop)) + if (!pa.pa_threaded_mainloop_in_thread(mainloop)) iterate(mainloop, op); - if (!pa_threaded_mainloop_in_thread(mainloop)) - pa_threaded_mainloop_unlock(mainloop); + if (!pa.pa_threaded_mainloop_in_thread(mainloop)) + pa.pa_threaded_mainloop_unlock(mainloop); AudioDevicePtr defaultDevice = std::make_shared(defaultPlaybackDescName, defaultPlaybackName); defaultDevice->directionType = AudioDeviceDirectionType::adtRender; @@ -187,10 +206,12 @@ namespace jni return playbackDevices.devices(); } - pa_threaded_mainloop_lock(mainloop); - pa_operation * op = pa_context_get_sink_info_list(context, getSinkCallback, this); + auto& pa = PulseAudioLoader::instance(); + + pa.pa_threaded_mainloop_lock(mainloop); + pa_operation * op = pa.pa_context_get_sink_info_list(context, getSinkCallback, this); iterate(mainloop, op); - pa_threaded_mainloop_unlock(mainloop); + pa.pa_threaded_mainloop_unlock(mainloop); return playbackDevices.devices(); } @@ -199,8 +220,10 @@ namespace jni { PulseAudioDeviceManager * engine = reinterpret_cast(userdata); + auto& pa = PulseAudioLoader::instance(); + if (last > 0) { - pa_threaded_mainloop_signal(engine->mainloop, 0); + pa.pa_threaded_mainloop_signal(engine->mainloop, 0); return; } @@ -212,7 +235,8 @@ namespace jni PulseAudioDeviceManager * engine = reinterpret_cast(userdata); if (last) { - pa_threaded_mainloop_signal(engine->mainloop, 0); + auto& pa = PulseAudioLoader::instance(); + pa.pa_threaded_mainloop_signal(engine->mainloop, 0); return; } @@ -226,7 +250,8 @@ namespace jni PulseAudioDeviceManager * engine = reinterpret_cast(userdata); if (last) { - pa_threaded_mainloop_signal(engine->mainloop, 0); + auto& pa = PulseAudioLoader::instance(); + pa.pa_threaded_mainloop_signal(engine->mainloop, 0); return; } @@ -238,7 +263,8 @@ namespace jni PulseAudioDeviceManager * engine = reinterpret_cast(userdata); if (last > 0) { - pa_threaded_mainloop_signal(engine->mainloop, 0); + auto& pa = PulseAudioLoader::instance(); + pa.pa_threaded_mainloop_signal(engine->mainloop, 0); return; } @@ -250,7 +276,8 @@ namespace jni PulseAudioDeviceManager * engine = reinterpret_cast(userdata); if (last) { - pa_threaded_mainloop_signal(engine->mainloop, 0); + auto& pa = PulseAudioLoader::instance(); + pa.pa_threaded_mainloop_signal(engine->mainloop, 0); return; } @@ -262,7 +289,8 @@ namespace jni PulseAudioDeviceManager * engine = reinterpret_cast(userdata); if (last) { - pa_threaded_mainloop_signal(engine->mainloop, 0); + auto& pa = PulseAudioLoader::instance(); + pa.pa_threaded_mainloop_signal(engine->mainloop, 0); return; } @@ -274,7 +302,7 @@ namespace jni auto device = std::make_shared(name, desc); if (devices.insertDevice(device)) { - if (isCapture) { + if (isCapture) { device->directionType = AudioDeviceDirectionType::adtCapture; } else { device->directionType = AudioDeviceDirectionType::adtRender; @@ -295,11 +323,11 @@ namespace jni } if (devices.removeDevice(it->second)) { - if (isCapture) { - it->second->directionType = AudioDeviceDirectionType::adtCapture; - } else { - it->second->directionType = AudioDeviceDirectionType::adtRender; - } + if (isCapture) { + it->second->directionType = AudioDeviceDirectionType::adtCapture; + } else { + it->second->directionType = AudioDeviceDirectionType::adtRender; + } notifyDeviceDisconnected(it->second); deviceMap.erase(it); @@ -308,17 +336,20 @@ namespace jni void PulseAudioDeviceManager::stateCallback(pa_context * ctx, void * userdata) { + auto& pa = PulseAudioLoader::instance(); pa_threaded_mainloop * mainloop = static_cast(userdata); - pa_threaded_mainloop_signal(mainloop, 0); + pa.pa_threaded_mainloop_signal(mainloop, 0); } void PulseAudioDeviceManager::serverInfoCallback(pa_context * ctx, const pa_server_info * info, void * userdata) { + auto& pa = PulseAudioLoader::instance(); + PulseAudioDeviceManager * engine = reinterpret_cast(userdata); engine->defaultCaptureName = info->default_source_name; engine->defaultPlaybackName = info->default_sink_name; - pa_threaded_mainloop_signal(engine->mainloop, 0); + pa.pa_threaded_mainloop_signal(engine->mainloop, 0); } void PulseAudioDeviceManager::subscribeCallback(pa_context * ctx, pa_subscription_event_type_t type, uint32_t idx, void * userdata) @@ -327,10 +358,11 @@ namespace jni unsigned facility = type & PA_SUBSCRIPTION_EVENT_FACILITY_MASK; unsigned operation = type & PA_SUBSCRIPTION_EVENT_TYPE_MASK; pa_operation * op = nullptr; + auto& pa = PulseAudioLoader::instance(); if (facility == PA_SUBSCRIPTION_EVENT_SOURCE) { if (operation == PA_SUBSCRIPTION_EVENT_NEW) { - op = pa_context_get_source_info_by_index(ctx, idx, newSourceCallback, engine); + op = pa.pa_context_get_source_info_by_index(ctx, idx, newSourceCallback, engine); } if (operation == PA_SUBSCRIPTION_EVENT_REMOVE) { engine->removeDevice(engine->captureDevices, idx, true); @@ -338,7 +370,7 @@ namespace jni } if (facility == PA_SUBSCRIPTION_EVENT_SINK) { if (operation == PA_SUBSCRIPTION_EVENT_NEW) { - op = pa_context_get_sink_info_by_index(ctx, idx, newSinkCallback, engine); + op = pa.pa_context_get_sink_info_by_index(ctx, idx, newSinkCallback, engine); } if (operation == PA_SUBSCRIPTION_EVENT_REMOVE) { engine->removeDevice(engine->playbackDevices, idx, false); @@ -346,28 +378,29 @@ namespace jni } if (op) { - pa_operation_unref(op); + pa.pa_operation_unref(op); } } void PulseAudioDeviceManager::fillAdditionalTypes(AudioDevicePtr device, pa_proplist * proplist) { // all property values see here https://docs.rs/libpulse-sys/latest/libpulse_sys/proplist/ const char *formFactor; - formFactor = pa_proplist_gets(proplist, PA_PROP_DEVICE_FORM_FACTOR); + auto& pa = PulseAudioLoader::instance(); + formFactor = pa.pa_proplist_gets(proplist, PA_PROP_DEVICE_FORM_FACTOR); std::string formFactorStr = ""; if (formFactor) { formFactorStr = std::string(formFactor); } const char *deviceTransport; - deviceTransport = pa_proplist_gets(proplist, PA_PROP_DEVICE_BUS); + deviceTransport = pa.pa_proplist_gets(proplist, PA_PROP_DEVICE_BUS); std::string deviceTransportStr = ""; if (deviceTransport) { deviceTransportStr = std::string(deviceTransport); } const char *propHDMI; - propHDMI = pa_proplist_gets(proplist, PA_PROP_DEVICE_BUS_PATH); + propHDMI = pa.pa_proplist_gets(proplist, PA_PROP_DEVICE_BUS_PATH); if (propHDMI && std::string(propHDMI).find("_hdmi") != std::string::npos) { deviceTransportStr = "hdmi"; }