From 59273a273729ac569f963654914cef7855b6b49b Mon Sep 17 00:00:00 2001
From: Julien Wadel <julien.wadel@belledonne-communications.com>
Date: Tue, 4 Mar 2025 10:53:53 +0100
Subject: [PATCH] Fix call volume typo in documentation. Add a manual test for
 checking device changed by doing manual OS updates. It can be run with
 options `--all --suite "Audio Routes --test "Simple call with real audio
 devices reload"` Add 2 tools function to detect volumes on record/playback.
 Move usable functions into global headers to avoid future code duplications:
 check device name and id. For tester, fix missing entitlements in plist to
 access microphones.

---
 build/osx/Info.plist.in       |  11 +++
 include/linphone/api/c-call.h |   4 +-
 tester/audio_routes_tester.c  | 157 ++++++++++++++++++++++++++++------
 tester/liblinphone_tester.c   |  74 ++++++++++++++++
 tester/liblinphone_tester.h   |  44 ++++++++++
 5 files changed, 264 insertions(+), 26 deletions(-)

diff --git a/build/osx/Info.plist.in b/build/osx/Info.plist.in
index f8d5ac7da2..ca6a1b0c96 100644
--- a/build/osx/Info.plist.in
+++ b/build/osx/Info.plist.in
@@ -41,5 +41,16 @@
 	<string>True</string>
 	<key>NSCameraUsageDescription</key>
 	<string>Allow camera for video sessions</string>
+	<key>NSMicrophoneUsageDescription</key>
+	<string>Allow microphone for audio sessions</string>
+	<key>NSBluetoothPeripheralUsageDescription</key>
+	<string>Use for wireless headsets</string>
+	<key>com.apple.security.device.camera</key>
+	<true/>
+	<key>com.apple.security.device.microphone</key>
+	<true/>
+	<key>com.apple.security.device.audio-input</key>
+	<true/>
+
 </dict>
 </plist>
diff --git a/include/linphone/api/c-call.h b/include/linphone/api/c-call.h
index b7819607c3..ea67de1e2e 100644
--- a/include/linphone/api/c-call.h
+++ b/include/linphone/api/c-call.h
@@ -763,14 +763,14 @@ LINPHONE_PUBLIC LinphoneChatRoom *linphone_call_get_chat_room(LinphoneCall *call
 /**
  * Gets the mesured playback volume level (received from remote) in dbm0.
  * @param call The call. @notnil
- * @return float Volume level in percentage.
+ * @return float Volume level in dbm0.
  */
 LINPHONE_PUBLIC float linphone_call_get_play_volume(const LinphoneCall *call);
 
 /**
  * Gets the mesured record volume level (sent to remote) in dbm0.
  * @param call The call. @notnil
- * @return float Volume level in percentage.
+ * @return float Volume level in dbm0.
  */
 LINPHONE_PUBLIC float linphone_call_get_record_volume(const LinphoneCall *call);
 
diff --git a/tester/audio_routes_tester.c b/tester/audio_routes_tester.c
index bd9c0faad2..5ef077a3df 100644
--- a/tester/audio_routes_tester.c
+++ b/tester/audio_routes_tester.c
@@ -34,10 +34,6 @@
 #include "linphone/lpconfig.h"
 #include "tester_utils.h"
 
-static int audio_device_name_match(LinphoneAudioDevice *audio_device, const char *name) {
-	return strcmp(linphone_audio_device_get_device_name(audio_device), name);
-}
-
 static void register_device(LinphoneCoreManager *mgr, MSSndCardDesc *card_desc) {
 
 	// Get number of devices before loading
@@ -346,15 +342,15 @@ static void call_with_audio_device_change_using_public_api(void) {
 	bctbx_list_t *dev_found = NULL;
 
 	// 1st device
-	dev_found =
-	    bctbx_list_find_custom(audio_devices, (bctbx_compare_func)audio_device_name_match, DUMMY_TEST_SOUNDCARD);
+	dev_found = bctbx_list_find_custom(audio_devices, (bctbx_compare_func)liblinphone_tester_audio_device_name_match,
+	                                   DUMMY_TEST_SOUNDCARD);
 	BC_ASSERT_PTR_NOT_NULL(dev_found);
 	LinphoneAudioDevice *current_dev = (dev_found) ? (LinphoneAudioDevice *)bctbx_list_get_data(dev_found) : NULL;
 	BC_ASSERT_PTR_NOT_NULL(current_dev);
 	linphone_audio_device_ref(current_dev);
 
 	// device dummy_playback in the list
-	dev_found = bctbx_list_find_custom(audio_devices, (bctbx_compare_func)audio_device_name_match,
+	dev_found = bctbx_list_find_custom(audio_devices, (bctbx_compare_func)liblinphone_tester_audio_device_name_match,
 	                                   DUMMY_PLAYBACK_TEST_SOUNDCARD);
 	BC_ASSERT_PTR_NOT_NULL(dev_found);
 	LinphoneAudioDevice *playback_dev = (dev_found) ? (LinphoneAudioDevice *)bctbx_list_get_data(dev_found) : NULL;
@@ -362,7 +358,7 @@ static void call_with_audio_device_change_using_public_api(void) {
 	linphone_audio_device_ref(playback_dev);
 
 	// device dummy_capture in the list
-	dev_found = bctbx_list_find_custom(audio_devices, (bctbx_compare_func)audio_device_name_match,
+	dev_found = bctbx_list_find_custom(audio_devices, (bctbx_compare_func)liblinphone_tester_audio_device_name_match,
 	                                   DUMMY_CAPTURE_TEST_SOUNDCARD);
 	BC_ASSERT_PTR_NOT_NULL(dev_found);
 	LinphoneAudioDevice *capture_dev = (dev_found) ? (LinphoneAudioDevice *)bctbx_list_get_data(dev_found) : NULL;
@@ -1549,6 +1545,114 @@ static void simple_call_with_audio_devices_reload(void) {
 	linphone_core_manager_destroy(marie);
 }
 
+/**
+ * @brief simple_call_with_real_audio_devices_reload
+ * Tests:
+ *  - Default device is selected on startup.
+ *  - Current default device is capturing and send audio while in call.
+ *  - Device list is updated on manual change: Activation/Deactivation/System default change.
+ *  - After changing a device, the new device is capturing and send audio while still in call.
+ *
+ *
+ * Steps:
+ *  1) Use "Default" device that should follow the system device.
+ *  2) Make a call.
+ *  3) Change the capture device (1min to do it)
+ *  4) Check if sound is received.
+ *  5) Change again the capture device (1min to do it)
+ *  6) Check if sound is received.
+ *
+ *  TODO:
+ *      - implement an automatic change by deactivating/reactivating devices using system API.
+ *      - check on playback devices.
+ *
+ */
+
+static void simple_call_with_real_audio_devices_reload(void) {
+	// Default device names (currently tested: Mac/Windows)
+	const char *defaultCaptureName = "Default Capture";
+	const char *defaultPlaybackName = "Default Playback";
+
+	ms_warning("[SCWRADR] Running manual test: Please check the console for steps.");
+
+	bctbx_list_t *lcs = NULL;
+	LinphoneCoreManager *marie = linphone_core_manager_new("marie_rc");
+	lcs = bctbx_list_append(lcs, marie->lc);
+	LinphoneCoreManager *pauline =
+	    linphone_core_manager_new(transport_supported(LinphoneTransportTls) ? "pauline_rc" : "pauline_tcp_rc");
+	lcs = bctbx_list_append(lcs, pauline->lc);
+
+	bctbx_list_t *marie_devices_start = linphone_core_get_extended_audio_devices(marie->lc);
+	bctbx_list_t *devices_it = marie_devices_start;
+
+	// Default device should be present event if no real devices.
+	BC_ASSERT_TRUE(!!devices_it);
+	const LinphoneAudioDevice *device = linphone_core_get_default_input_audio_device(marie->lc);
+	bool_t capture_set = device ? liblinphone_tester_audio_device_name_match(device, defaultCaptureName) == 0 : FALSE;
+	device = linphone_core_get_default_output_audio_device(marie->lc);
+	bool_t playback_set = device ? liblinphone_tester_audio_device_name_match(device, defaultPlaybackName) == 0 : FALSE;
+
+	BC_ASSERT_TRUE(capture_set);
+	BC_ASSERT_TRUE(playback_set);
+
+	// Setting default device to run test
+	if (!devices_it) ms_message("[SCWRADR] No devices");
+	else if (!capture_set || !playback_set) {
+		ms_message("[SCWRADR] Current devices list:");
+		while (devices_it) {
+			ms_message("[SCWRADR] %s",
+			           linphone_audio_device_get_device_name((LinphoneAudioDevice *)bctbx_list_get_data(devices_it)));
+			LinphoneAudioDevice *device = (LinphoneAudioDevice *)bctbx_list_get_data(devices_it);
+			if (!capture_set &&
+			    (linphone_audio_device_get_capabilities(device) & LinphoneAudioDeviceCapabilityRecord) &&
+			    liblinphone_tester_audio_device_name_match(device, defaultCaptureName) == 0) {
+				capture_set = TRUE;
+				linphone_core_set_default_input_audio_device(marie->lc, device);
+			}
+			if (!playback_set && (linphone_audio_device_get_capabilities(device) & LinphoneAudioDeviceCapabilityPlay) &&
+			    liblinphone_tester_audio_device_name_match(device, defaultPlaybackName) == 0) {
+				playback_set = TRUE;
+				linphone_core_set_default_output_audio_device(marie->lc, device);
+			}
+
+			devices_it = bctbx_list_next(devices_it);
+		}
+	}
+
+	ms_message("[SCWRADR] Making call");
+	BC_ASSERT_TRUE(call(marie, pauline));
+
+	ms_message("[SCWRADR] Checking if volume is received on 200ms");
+	BC_ASSERT_TRUE(liblinphone_tester_sound_detection(marie, pauline, 20000, "[SCWRADR]") == 0);
+
+	ms_message("[SCWRADR] Waiting for changing CAPTURE device (1min)");
+	BC_ASSERT_TRUE(wait_for_list(lcs, &marie->stat.number_of_LinphoneCoreAudioDevicesListUpdated,
+	                             marie->stat.number_of_LinphoneCoreAudioDevicesListUpdated + 1, 60000));
+	bctbx_list_t *new_list = linphone_core_get_extended_audio_devices(marie->lc);
+	bool_t is_new = FALSE;
+
+	bctbx_list_t *devices_changed = liblinphone_tester_find_changing_devices(marie_devices_start, new_list, &is_new);
+	devices_it = devices_changed;
+	while (devices_it) {
+		LinphoneAudioDevice *d = (LinphoneAudioDevice *)bctbx_list_get_data(devices_it);
+		ms_message("[SCWRADR] Changing Devices detected : %s", linphone_audio_device_get_device_name(d));
+		devices_it = bctbx_list_next(devices_it);
+	}
+
+	BC_ASSERT_TRUE(liblinphone_tester_sound_detection(marie, pauline, 20000, "[SCWRADR]") == 0);
+
+	end_call(marie, pauline);
+
+	BC_ASSERT_EQUAL(marie->stat.number_of_LinphoneCoreLastCallEnded, 1, int, "%d");
+	BC_ASSERT_EQUAL(pauline->stat.number_of_LinphoneCoreLastCallEnded, 1, int, "%d");
+
+	bctbx_list_free_with_data(devices_changed, (void (*)(void *))linphone_audio_device_unref);
+	bctbx_list_free_with_data(new_list, (void (*)(void *))linphone_audio_device_unref);
+	bctbx_list_free_with_data(marie_devices_start, (void (*)(void *))linphone_audio_device_unref);
+	linphone_core_manager_destroy(pauline);
+	linphone_core_manager_destroy(marie);
+}
+
 static void
 simple_conference_with_audio_device_change_base(bool_t during_setup, bool_t before_all_join, bool_t after_all_join) {
 	bctbx_list_t *lcs = NULL;
@@ -2008,32 +2112,34 @@ static void conference_with_simple_audio_device_change(void) {
 
 	// As new devices are prepended, they can be easily accessed and we do not run the risk of gettting a device whose
 	// type is Unknown 1st device
-	dev_found =
-	    bctbx_list_find_custom(marie_audio_devices, (bctbx_compare_func)audio_device_name_match, DUMMY_TEST_SOUNDCARD);
+	dev_found = bctbx_list_find_custom(
+	    marie_audio_devices, (bctbx_compare_func)liblinphone_tester_audio_device_name_match, DUMMY_TEST_SOUNDCARD);
 	BC_ASSERT_PTR_NOT_NULL(dev_found);
 	LinphoneAudioDevice *marie_dev0 = (dev_found) ? (LinphoneAudioDevice *)bctbx_list_get_data(dev_found) : NULL;
 	BC_ASSERT_PTR_NOT_NULL(marie_dev0);
 	linphone_audio_device_ref(marie_dev0);
 
 	// 2nd device
-	dev_found =
-	    bctbx_list_find_custom(marie_audio_devices, (bctbx_compare_func)audio_device_name_match, DUMMY2_TEST_SOUNDCARD);
+	dev_found = bctbx_list_find_custom(
+	    marie_audio_devices, (bctbx_compare_func)liblinphone_tester_audio_device_name_match, DUMMY2_TEST_SOUNDCARD);
 	BC_ASSERT_PTR_NOT_NULL(dev_found);
 	LinphoneAudioDevice *marie_dev1 = (dev_found) ? (LinphoneAudioDevice *)bctbx_list_get_data(dev_found) : NULL;
 	BC_ASSERT_PTR_NOT_NULL(marie_dev1);
 	linphone_audio_device_ref(marie_dev1);
 
 	// device dummy_playback in the list
-	dev_found = bctbx_list_find_custom(marie_audio_devices, (bctbx_compare_func)audio_device_name_match,
-	                                   DUMMY_PLAYBACK_TEST_SOUNDCARD);
+	dev_found =
+	    bctbx_list_find_custom(marie_audio_devices, (bctbx_compare_func)liblinphone_tester_audio_device_name_match,
+	                           DUMMY_PLAYBACK_TEST_SOUNDCARD);
 	BC_ASSERT_PTR_NOT_NULL(dev_found);
 	LinphoneAudioDevice *marie_playback = (dev_found) ? (LinphoneAudioDevice *)bctbx_list_get_data(dev_found) : NULL;
 	BC_ASSERT_PTR_NOT_NULL(marie_playback);
 	linphone_audio_device_ref(marie_playback);
 
 	// device dummy_capture in the list
-	dev_found = bctbx_list_find_custom(marie_audio_devices, (bctbx_compare_func)audio_device_name_match,
-	                                   DUMMY_CAPTURE_TEST_SOUNDCARD);
+	dev_found =
+	    bctbx_list_find_custom(marie_audio_devices, (bctbx_compare_func)liblinphone_tester_audio_device_name_match,
+	                           DUMMY_CAPTURE_TEST_SOUNDCARD);
 	BC_ASSERT_PTR_NOT_NULL(dev_found);
 	LinphoneAudioDevice *marie_capture = (dev_found) ? (LinphoneAudioDevice *)bctbx_list_get_data(dev_found) : NULL;
 	BC_ASSERT_PTR_NOT_NULL(marie_capture);
@@ -2092,32 +2198,34 @@ static void conference_with_simple_audio_device_change(void) {
 
 	// As new devices are prepended, they can be easily accessed and we do not run the risk of gettting a device whose
 	// type is Unknown 1st device
-	dev_found =
-	    bctbx_list_find_custom(laure_audio_devices, (bctbx_compare_func)audio_device_name_match, DUMMY2_TEST_SOUNDCARD);
+	dev_found = bctbx_list_find_custom(
+	    laure_audio_devices, (bctbx_compare_func)liblinphone_tester_audio_device_name_match, DUMMY2_TEST_SOUNDCARD);
 	BC_ASSERT_PTR_NOT_NULL(dev_found);
 	LinphoneAudioDevice *laure_dev0 = (dev_found) ? (LinphoneAudioDevice *)bctbx_list_get_data(dev_found) : NULL;
 	BC_ASSERT_PTR_NOT_NULL(laure_dev0);
 	linphone_audio_device_ref(laure_dev0);
 
 	// 2nd device
-	dev_found =
-	    bctbx_list_find_custom(laure_audio_devices, (bctbx_compare_func)audio_device_name_match, DUMMY_TEST_SOUNDCARD);
+	dev_found = bctbx_list_find_custom(
+	    laure_audio_devices, (bctbx_compare_func)liblinphone_tester_audio_device_name_match, DUMMY_TEST_SOUNDCARD);
 	BC_ASSERT_PTR_NOT_NULL(dev_found);
 	LinphoneAudioDevice *laure_dev1 = (dev_found) ? (LinphoneAudioDevice *)bctbx_list_get_data(dev_found) : NULL;
 	BC_ASSERT_PTR_NOT_NULL(laure_dev1);
 	linphone_audio_device_ref(laure_dev1);
 
 	// device dummy_playback in the list
-	dev_found = bctbx_list_find_custom(laure_audio_devices, (bctbx_compare_func)audio_device_name_match,
-	                                   DUMMY_PLAYBACK_TEST_SOUNDCARD);
+	dev_found =
+	    bctbx_list_find_custom(laure_audio_devices, (bctbx_compare_func)liblinphone_tester_audio_device_name_match,
+	                           DUMMY_PLAYBACK_TEST_SOUNDCARD);
 	BC_ASSERT_PTR_NOT_NULL(dev_found);
 	LinphoneAudioDevice *laure_playback = (dev_found) ? (LinphoneAudioDevice *)bctbx_list_get_data(dev_found) : NULL;
 	BC_ASSERT_PTR_NOT_NULL(laure_playback);
 	linphone_audio_device_ref(laure_playback);
 
 	// device dummy_capture in the list
-	dev_found = bctbx_list_find_custom(laure_audio_devices, (bctbx_compare_func)audio_device_name_match,
-	                                   DUMMY_CAPTURE_TEST_SOUNDCARD);
+	dev_found =
+	    bctbx_list_find_custom(laure_audio_devices, (bctbx_compare_func)liblinphone_tester_audio_device_name_match,
+	                           DUMMY_CAPTURE_TEST_SOUNDCARD);
 	BC_ASSERT_PTR_NOT_NULL(dev_found);
 	LinphoneAudioDevice *laure_capture = (dev_found) ? (LinphoneAudioDevice *)bctbx_list_get_data(dev_found) : NULL;
 	BC_ASSERT_PTR_NOT_NULL(laure_capture);
@@ -2501,6 +2609,7 @@ static void simple_conference_with_audio_device_change_using_public_api(void) {
 
 test_t audio_routes_tests[] = {
     TEST_NO_TAG("Simple call with audio devices reload", simple_call_with_audio_devices_reload),
+    TEST_ONE_TAG("Simple call with real audio devices reload", simple_call_with_real_audio_devices_reload, "skip"),
     TEST_NO_TAG("Call with disconnecting device before ringback", call_with_disconnecting_device_before_ringback),
     TEST_NO_TAG("Call with disconnecting device during ringback", call_with_disconnecting_device_during_ringback),
     TEST_NO_TAG("Call with disconnecting device after ringback", call_with_disconnecting_device_after_ringback),
diff --git a/tester/liblinphone_tester.c b/tester/liblinphone_tester.c
index 912c8c05bb..fa2f7ced33 100644
--- a/tester/liblinphone_tester.c
+++ b/tester/liblinphone_tester.c
@@ -719,3 +719,77 @@ float liblinphone_tester_get_cpu_bogomips(void) {
 #endif
 	return ret;
 }
+
+int liblinphone_tester_audio_device_name_match(const LinphoneAudioDevice *audio_device, const char *name) {
+	return strcmp(linphone_audio_device_get_device_name(audio_device), name);
+}
+
+int liblinphone_tester_audio_device_match(const LinphoneAudioDevice *a, LinphoneAudioDevice *b) {
+	return strcmp(linphone_audio_device_get_id(a), linphone_audio_device_get_id(b));
+}
+
+bctbx_list_t *liblinphone_tester_find_changing_devices(bctbx_list_t *a, bctbx_list_t *b, bool_t *is_new) {
+	bctbx_list_t *devices_changed = NULL;
+	bctbx_list_t *device_it = a;
+	bctbx_list_t *dev_found = NULL;
+	if (!a && !b) return NULL;
+	// Check for disconnected device
+	while (device_it) {
+		LinphoneAudioDevice *device = (LinphoneAudioDevice *)bctbx_list_get_data(device_it);
+		dev_found = bctbx_list_find_custom(b, (bctbx_compare_func)liblinphone_tester_audio_device_match, device);
+		device_it = bctbx_list_next(device_it);
+		if (!dev_found) {
+			devices_changed = bctbx_list_append(devices_changed, device);
+			linphone_audio_device_ref(device);
+			*is_new = FALSE;
+		}
+	}
+	// Check for connected device
+	if (!devices_changed) {
+		device_it = b;
+		while (device_it) {
+			LinphoneAudioDevice *device = (LinphoneAudioDevice *)bctbx_list_get_data(device_it);
+			dev_found = bctbx_list_find_custom(a, (bctbx_compare_func)liblinphone_tester_audio_device_match, device);
+			device_it = bctbx_list_next(device_it);
+			if (!dev_found) {
+				devices_changed = bctbx_list_append(devices_changed, device);
+				linphone_audio_device_ref(device);
+				*is_new = TRUE;
+			}
+		}
+	}
+
+	return devices_changed;
+}
+
+int liblinphone_tester_sound_detection(LinphoneCoreManager *a,
+                                       LinphoneCoreManager *b,
+                                       int timeout_ms,
+                                       const char *log_tag) {
+	int have_sound_count = 0;
+	const float silence_threshold = -20.f;
+
+	LinphoneCall *calls[2] = {linphone_core_get_current_call(a->lc), linphone_core_get_current_call(b->lc)};
+	MSTimeSpec start;
+
+	liblinphone_tester_clock_start(&start);
+	while (have_sound_count < 2 &&
+	       !liblinphone_tester_clock_elapsed(
+	           &start, timeout_ms)) { // We want to avoid potential sound spikes while disconnection.
+		float record_levels[2] = {linphone_call_get_record_volume(calls[0]), linphone_call_get_record_volume(calls[1])};
+		float playback_levels[2] = {linphone_call_get_play_volume(calls[0]), linphone_call_get_play_volume(calls[1])};
+		bool_t have_sounds[2] = {record_levels[1] > silence_threshold && playback_levels[0] > silence_threshold,
+		                         record_levels[0] > silence_threshold && playback_levels[1] > silence_threshold};
+
+		// Note: Sounds are send from Pauline to Marie without passing by the capture device (send from file)
+		// The test must check for Marie => Pauline, because the sound comes directly from the capture device.
+		// At this point, playback device cannot be tested without complex code (device monitoring).
+		if (have_sounds[0] && have_sounds[1]) ++have_sound_count;
+		else have_sound_count = 0;
+		if (log_tag)
+			ms_message("%s Record => Playback levels: %f => %f, %f => %f", log_tag, record_levels[0],
+			           playback_levels[1], record_levels[1], playback_levels[0]);
+		wait_for_until(a->lc, b->lc, NULL, 0, 100);
+	}
+	return have_sound_count >= 2 ? 0 : -1;
+}
diff --git a/tester/liblinphone_tester.h b/tester/liblinphone_tester.h
index 6af62496af..fb8f5c84ea 100644
--- a/tester/liblinphone_tester.h
+++ b/tester/liblinphone_tester.h
@@ -1251,6 +1251,50 @@ int liblinphone_tester_check_recorded_audio(const char *hellopath, const char *r
 
 /* returns a CPU bogomips indication. Only supported for linux.*/
 float liblinphone_tester_get_cpu_bogomips(void);
+
+/**
+ * @brief liblinphone_tester_audio_device_name_match compare device name with string
+ *
+ * @param audio_device the audio device to compare @notnil
+ * @param name the name to find in device name
+ * @return the result of strcmp
+ */
+int liblinphone_tester_audio_device_name_match(const LinphoneAudioDevice *audio_device, const char *name);
+
+/**
+ * @brief liblinphone_tester_audio_device_match compare device ids between 2 devices
+ *
+ * @param a the first device
+ * @param b the second device
+ * @return the result of strcmp
+ */
+int liblinphone_tester_audio_device_match(const LinphoneAudioDevice *a, LinphoneAudioDevice *b);
+
+/**
+ * @brief liblinphone_tester_find_changing_devices Find the device that is missed/new into 2 lists.
+ *
+ * @param a first list to check. @maybenil
+ * @param b second list to check. @maybenil
+ * @param is_new TRUE if the changed device is new.
+ * @return the device that changed or NULL if no change. Set is_new if the device has been
+ * connected. @tobefreed @maybenil
+ */
+bctbx_list_t *liblinphone_tester_find_changing_devices(bctbx_list_t *a, bctbx_list_t *b, bool_t *is_new);
+
+/**
+ * @brief liblinphone_tester_sound_detection Check sounds between 2 Core on 200ms and exit the function on detection.
+ *
+ * @param a the first core manager to retrieve current call and make wait loop @notnil
+ * @param b the second core manager to retrieve current call and make wait loop @notnil
+ * @param timeout_ms timeout in ms
+ * @param log_tag Use this tag to monitor level in logs. If NULL, log is switched off @maybenil
+ * @return -1 if timed out else 0.
+ */
+int liblinphone_tester_sound_detection(LinphoneCoreManager *a,
+                                       LinphoneCoreManager *b,
+                                       int timeout_ms,
+                                       const char *log_tag);
+
 #ifdef __cplusplus
 };
 #endif
-- 
GitLab