typedef struct sound_stream_info_s* sound_stream_info_h;
/**
+ * @brief Sound stream ducking handle.
+ * @since_tizen 5.5
+ */
+typedef struct sound_stream_ducking_s* sound_stream_ducking_h;
+
+/**
* @brief Enumeration for sound stream type.
* @since_tizen 3.0
*/
typedef void (*sound_manager_volume_changed_cb) (sound_type_e type, unsigned int volume, void *user_data);
/**
+ * @brief Called when the ducking activation or deactivation is finished.
+ * @since_tizen 5.5
+ *
+ * @remarks This function is invoked by the internal thread of the sound manager.
+ * Therefore it is recommended not to call functions which update the UI from this callback.
+ * @remarks @a stream_ducking is the same handle for which the callback was set in the sound_manager_create_stream_ducking() call,
+ * so it should not be released in this callback.
+ *
+ * @param[in] stream_ducking The stream ducking handle
+ * @param[in] is_ducked The flag whether it's ducked or not
+ * @param[in] user_data The user data passed from the callback registration function
+ * @pre You should register this callback when sound_manager_create_stream_ducking() is called.
+ * @see sound_manager_create_stream_ducking()
+ */
+typedef void (*sound_stream_ducking_state_changed_cb) (sound_stream_ducking_h stream_ducking, bool is_ducked, void *user_data);
+
+/**
* @}
*/
int sound_manager_remove_volume_changed_cb(int id);
/**
+ * @brief Creates a handle for stream ducking.
+ * @since_tizen 5.5
+ *
+ * @remarks @a stream_ducking should be released using sound_manager_destroy_stream_ducking().
+ *
+ * @param[in] target_stream The type of target stream
+ * @param[in] callback The ducking state changed callback function (optional, this can be NULL)
+ * @param[in] user_data The user data to be passed to the callback function (optional, this can be NULL)
+ * @param[out] stream_ducking The handle of stream ducking
+ * @return @c 0 on success,
+ * otherwise a negative error value
+ * @retval #SOUND_MANAGER_ERROR_NONE Success
+ * @retval #SOUND_MANAGER_ERROR_INVALID_PARAMETER Invalid parameter
+ * @retval #SOUND_MANAGER_ERROR_INTERNAL Internal error inside the sound system
+ * @see sound_manager_destroy_stream_ducking()
+ * @see sound_manager_is_ducked()
+ * @see sound_manager_activate_ducking()
+ * @see sound_manager_deactivate_ducking()
+ */
+int sound_manager_create_stream_ducking(sound_stream_type_e target_stream, sound_stream_ducking_state_changed_cb callback, void *user_data, sound_stream_ducking_h *stream_ducking);
+
+/**
+ * @brief Checks if the stream is ducked.
+ * @since_tizen 5.5
+ *
+ * @param[in] stream_ducking The handle of stream ducking
+ * @param[out] is_ducked Whether the stream is ducked or not: (@c true = ducked, @c false = unducked)
+ * @return @c 0 on success,
+ * otherwise a negative error value
+ * @retval #SOUND_MANAGER_ERROR_NONE Success
+ * @retval #SOUND_MANAGER_ERROR_INVALID_PARAMETER Invalid parameter
+ * @retval #SOUND_MANAGER_ERROR_INTERNAL Internal error inside the sound system
+ * @see sound_manager_create_stream_ducking()
+ * @see sound_manager_destroy_stream_ducking()
+ * @see sound_manager_activate_ducking()
+ * @see sound_manager_deactivate_ducking()
+ */
+int sound_manager_is_ducked(sound_stream_ducking_h stream_ducking, bool *is_ducked);
+
+/**
+ * @brief Activates ducking, asynchronously.
+ * @since_tizen 5.5
+ * @privlevel public
+ * @privilege %http://tizen.org/privilege/volume.set
+ * @remarks If ducking is activated successfully, the volume of all sound streams
+ * matched with target_stream (set in sound_manager_create_stream_ducking())
+ * is decreased by @a ratio for @a duration. The change is system-wide.
+ * @param[in] stream_ducking The handle of stream ducking
+ * @param[in] duration The duration for ducking (msec, 0 <= duration <= 3000)
+ * @param[in] ratio The volume ratio after ducked (0.0 < ratio < 1.0)
+ * @return @c 0 on success,
+ * otherwise a negative error value
+ * @retval #SOUND_MANAGER_ERROR_NONE Success
+ * @retval #SOUND_MANAGER_ERROR_PERMISSION_DENIED Permission denied
+ * @retval #SOUND_MANAGER_ERROR_INVALID_PARAMETER Invalid parameter
+ * @retval #SOUND_MANAGER_ERROR_INVALID_STATE Invalid state
+ * @retval #SOUND_MANAGER_ERROR_INTERNAL Internal error inside the sound system
+ * @pre The stream should be unducked before calling this function,
+ * otherwise, #SOUND_MANAGER_ERROR_INVALID_STATE is returned.
+ * @see sound_manager_create_stream_ducking()
+ * @see sound_manager_destroy_stream_ducking()
+ * @see sound_manager_is_ducked()
+ * @see sound_manager_deactivate_ducking()
+ */
+int sound_manager_activate_ducking(sound_stream_ducking_h stream_ducking, unsigned int duration, double ratio);
+
+/**
+ * @brief Deactivates ducking, asynchronously.
+ * @since_tizen 5.5
+ * @privlevel public
+ * @privilege %http://tizen.org/privilege/volume.set
+ * @param[in] stream_ducking The handle of stream ducking
+ * @return @c 0 on success,
+ * otherwise a negative error value
+ * @retval #SOUND_MANAGER_ERROR_NONE Success
+ * @retval #SOUND_MANAGER_ERROR_PERMISSION_DENIED Permission denied
+ * @retval #SOUND_MANAGER_ERROR_INVALID_PARAMETER Invalid parameter
+ * @retval #SOUND_MANAGER_ERROR_INVALID_STATE Invalid state
+ * @retval #SOUND_MANAGER_ERROR_INTERNAL Internal error inside the sound system
+ * @pre The stream should be ducked before calling this function,
+ * otherwise, #SOUND_MANAGER_ERROR_INVALID_STATE is returned.
+ * @see sound_manager_create_stream_ducking()
+ * @see sound_manager_destroy_stream_ducking()
+ * @see sound_manager_is_ducked()
+ * @see sound_manager_activate_ducking()
+ */
+int sound_manager_deactivate_ducking(sound_stream_ducking_h stream_ducking);
+
+/**
+ * @brief Destroys the handle for stream ducking.
+ * @since_tizen 5.5
+ *
+ * @param[in] stream_ducking The handle of stream ducking
+ * @return @c 0 on success,
+ * otherwise a negative error value
+ * @retval #SOUND_MANAGER_ERROR_NONE Success
+ * @retval #SOUND_MANAGER_ERROR_INVALID_PARAMETER Invalid parameter
+ * @retval #SOUND_MANAGER_ERROR_INTERNAL Internal error inside the sound system
+ * @see sound_manager_create_stream_ducking()
+ * @see sound_manager_is_ducked()
+ * @see sound_manager_activate_ducking()
+ * @see sound_manager_deactivate_ducking()
+ */
+int sound_manager_destroy_stream_ducking(sound_stream_ducking_h stream_ducking);
+
+/**
* @}
*/
#define SOUND_STREAM_DIRECTION_MAX 2
#define SOUND_DEVICE_TYPE_LEN 64
#define SOUND_SAMPLE_FORMAT_LEN 12
+#define SOUND_DUCKING_ARR_MAX 128
#define DIRECTION_OUT_STR "out"
#define SOUND_TYPE_MASTER_STR "master"
bool is_set;
} manual_route_info_s;
+typedef struct _sound_pa_info_s {
+ pa_threaded_mainloop *mainloop;
+ pa_context *context;
+ unsigned int index;
+} sound_pa_info_s;
+
typedef struct _sound_stream_info_s {
- unsigned int pa_index;
int focus_id;
char *stream_type;
bool is_focus_unavailable;
bool is_requesting;
- pa_threaded_mainloop *pa_mainloop;
- pa_context *pa_context;
+ sound_pa_info_s pa_info;
stream_conf_info_s stream_conf_info;
unsigned int prev_acquired_focus;
unsigned int acquired_focus;
pthread_mutex_t focus_cb_mutex;
} sound_stream_info_s;
+typedef struct _sound_stream_ducking_s {
+ char *target_stream;
+ bool is_ducked;
+ unsigned int duration;
+ double ratio;
+ sound_pa_info_s pa_info;
+ sound_stream_ducking_state_changed_cb user_cb;
+ void *user_data;
+} sound_stream_ducking_s;
+
typedef enum {
_VSTREAM_STATE_READY,
_VSTREAM_STATE_RUNNING,
void _pa_stream_state_cb(pa_stream *s, void * userdata);
+int _make_pa_connection(sound_pa_info_s *pa_info, const char *context_name);
+
int _make_pa_connection_and_register_focus(sound_stream_info_s *stream_h, sound_stream_focus_state_changed_cb callback, void *user_data);
+void _destroy_pa_connection(sound_pa_info_s *pa_info);
+
int _destroy_pa_connection_and_unregister_focus(sound_stream_info_s *stream_h);
int _add_device_for_stream_routing(sound_stream_info_s *stream_info, sound_device_h device);
int _set_acm_master_mode(bool on);
+int _activate_ducking(uint32_t stream_index, bool enable, const char *target_stream, uint32_t duration, double ratio);
+
+int _get_ducking_state(uint32_t stream_index, bool *is_ducked);
+
#ifdef __cplusplus
}
#endif
Name: capi-media-sound-manager
Summary: Sound Manager library
-Version: 0.5.30
+Version: 0.5.31
Release: 0
Group: Multimedia/API
License: Apache-2.0
#include "sound_manager.h"
#include "sound_manager_private.h"
+static int ducking_arr_count;
+static unsigned int ducking_cb_subs_id;
+static pthread_mutex_t ducking_mutex;
+static sound_stream_ducking_s *ducking_arr[SOUND_DUCKING_ARR_MAX];
+
_focus_watch_info_s focus_watch_info_arr[SOUND_STREAM_INFO_ARR_MAX];
+static void _ducking_state_changed_cb(int pa_index, bool is_ducked, void *user_data)
+{
+ int i = 0;
+
+ SM_ENTER_CRITICAL_SECTION(&ducking_mutex);
+
+ for (i = 0 ; i < SOUND_DUCKING_ARR_MAX ; i++) {
+ if (ducking_arr[i] && ducking_arr[i]->pa_info.index == pa_index)
+ break;
+ }
+
+ if (i < SOUND_DUCKING_ARR_MAX) {
+ LOGI("ducking state changed [i:%d,ducked:%u]", pa_index, is_ducked);
+
+ if (ducking_arr[i]->user_cb)
+ ducking_arr[i]->user_cb((sound_stream_ducking_h)ducking_arr[i], is_ducked, ducking_arr[i]->user_data);
+ else
+ LOGW("no user callback for pa_index %d", pa_index);
+ } else {
+ LOGE("could not found for pa_index %d", pa_index);
+ }
+
+ SM_LEAVE_CRITICAL_SECTION(&ducking_mutex);
+
+ return;
+}
+
int sound_manager_get_max_volume(sound_type_e type, int *max)
{
const char *volume_type = NULL;
if (ret == MM_ERROR_NONE) {
*stream_info = (sound_stream_info_h)stream_h;
LOGI("stream_h(%p), pa_index(%u), focus_id(%d), user_cb(%p), ret(0x%x)",
- stream_h, stream_h->pa_index, stream_h->focus_id, stream_h->user_cb, ret);
+ stream_h, stream_h->pa_info.index, stream_h->focus_id, stream_h->user_cb, ret);
}
}
if (ret == MM_ERROR_NONE) {
stream_h->acquired_focus |= focus_mask;
stream_h->prev_acquired_focus |= focus_mask;
- _update_focus_status(stream_h->pa_index, (unsigned int)stream_h->acquired_focus);
+ _update_focus_status(stream_h->pa_info.index, (unsigned int)stream_h->acquired_focus);
}
LOGI("acquired_focus[0x%x], prev[0x%x]", stream_h->acquired_focus, stream_h->prev_acquired_focus);
if (ret == MM_ERROR_NONE) {
stream_h->acquired_focus &= ~focus_mask;
stream_h->prev_acquired_focus &= ~focus_mask;
- _update_focus_status(stream_h->pa_index, (unsigned int)stream_h->acquired_focus);
+ _update_focus_status(stream_h->pa_info.index, (unsigned int)stream_h->acquired_focus);
}
LOGI("acquired_focus[0x%x], prev[0x%x]", stream_h->acquired_focus, stream_h->prev_acquired_focus);
if (ret == MM_ERROR_NONE) {
stream_h->acquired_focus |= focus_mask;
stream_h->prev_acquired_focus |= focus_mask;
- _update_focus_status(stream_h->pa_index, (unsigned int)stream_h->acquired_focus);
+ _update_focus_status(stream_h->pa_info.index, (unsigned int)stream_h->acquired_focus);
}
LEAVE:
if (ret == MM_ERROR_NONE) {
stream_h->acquired_focus = 0;
stream_h->prev_acquired_focus = 0;
- _update_focus_status(stream_h->pa_index, (unsigned int)stream_h->acquired_focus);
+ _update_focus_status(stream_h->pa_info.index, (unsigned int)stream_h->acquired_focus);
}
LEAVE:
SM_NULL_ARG_CHECK(device);
SM_NULL_ARG_CHECK(is_on);
- ret = mm_sound_is_stream_on_device(stream_h->pa_index, device, is_on);
+ ret = mm_sound_is_stream_on_device(stream_h->pa_info.index, device, is_on);
return _convert_sound_manager_error_code(__func__, ret);
}
return _convert_sound_manager_error_code(__func__, ret);
}
+int sound_manager_create_stream_ducking(sound_stream_type_e target_stream, sound_stream_ducking_state_changed_cb callback, void *user_data, sound_stream_ducking_h *stream_ducking)
+{
+ int i = 0;
+ int ret = MM_ERROR_NONE;
+ sound_stream_ducking_s *new_ducking = NULL;
+
+ SM_NULL_ARG_CHECK(stream_ducking);
+
+ SM_ENTER_CRITICAL_SECTION_WITH_RETURN(&ducking_mutex, SOUND_MANAGER_ERROR_INTERNAL);
+
+ for (i = 0 ; i < SOUND_DUCKING_ARR_MAX ; i++)
+ if (ducking_arr[i] == NULL)
+ break;
+
+ if (i == SOUND_DUCKING_ARR_MAX) {
+ LOGE("ducking array is full");
+ ret = MM_ERROR_SOUND_INTERNAL;
+ goto LEAVE;
+ }
+
+ new_ducking = (sound_stream_ducking_s *)calloc(1, sizeof(sound_stream_ducking_s));
+ if (!new_ducking) {
+ ret = MM_ERROR_OUT_OF_MEMORY;
+ goto LEAVE;
+ }
+
+ ret = _convert_stream_type(target_stream, &new_ducking->target_stream);
+ if (ret != MM_ERROR_NONE)
+ goto LEAVE;
+
+ ret = _make_pa_connection(&new_ducking->pa_info, "SOUND_MANAGER_STREAM_DUCKING");
+
+LEAVE:
+ if (ret == MM_ERROR_NONE) {
+ if (++ducking_arr_count == 1) {
+ /* subscribe ducking finished signal */
+ ret = mm_sound_add_ducking_state_changed_callback((mm_sound_ducking_state_changed_cb)_ducking_state_changed_cb,
+ NULL, &ducking_cb_subs_id);
+ if (ret != MM_ERROR_NONE) {
+ ducking_arr_count = 0;
+ goto LEAVE;
+ }
+
+ LOGI("ducking state changed cb subs id %d", ducking_cb_subs_id);
+ }
+
+ new_ducking->user_cb = callback;
+ new_ducking->user_data = user_data;
+
+ ducking_arr[i] = new_ducking;
+ *stream_ducking = (sound_stream_ducking_h)new_ducking;
+
+ LOGI("new stream_ducking(%p), target_stream(%s), pa_index(%u), user_cb(%p)",
+ new_ducking, new_ducking->target_stream, new_ducking->pa_info.index, new_ducking->user_cb);
+ } else {
+ if (new_ducking) {
+ _destroy_pa_connection(&new_ducking->pa_info);
+ free(new_ducking);
+ }
+ }
+
+ SM_LEAVE_CRITICAL_SECTION(&ducking_mutex);
+
+ return _convert_sound_manager_error_code(__func__, ret);
+}
+
+int sound_manager_destroy_stream_ducking(sound_stream_ducking_h stream_ducking)
+{
+ int i = 0;
+ int ret = MM_ERROR_NONE;
+ bool is_ducked = false;
+ sound_stream_ducking_s *ducking = (sound_stream_ducking_s*)stream_ducking;
+
+ LOGI(">> enter %p", ducking);
+
+ SM_INSTANCE_CHECK(ducking);
+
+ ret = _get_ducking_state(ducking->pa_info.index, &is_ducked);
+ if (ret != MM_ERROR_NONE)
+ return _convert_sound_manager_error_code(__func__, ret);
+
+ SM_ENTER_CRITICAL_SECTION_WITH_RETURN(&ducking_mutex, SOUND_MANAGER_ERROR_INTERNAL);
+
+ if (!is_ducked) {
+ _destroy_pa_connection(&ducking->pa_info);
+ } else {
+ LOGE("ducked now, it should be deactivated first.");
+ ret = MM_ERROR_SOUND_INVALID_STATE;
+ }
+
+ if (ret == MM_ERROR_NONE) {
+ LOGI("destroy stream ducking(%p)", ducking);
+
+ for (i = 0 ; i < SOUND_DUCKING_ARR_MAX ; i++)
+ if (ducking_arr[i] == ducking)
+ ducking_arr[i] = NULL;
+
+ if (--ducking_arr_count == 0) {
+ /* unsubscribe ducking finished signal */
+ if (mm_sound_remove_ducking_state_changed_callback(ducking_cb_subs_id) != MM_ERROR_NONE)
+ LOGW("mm_sound_remove_ducking_state_changed_callback(id:%u) failed", ducking_cb_subs_id);
+
+ ducking_cb_subs_id = 0;
+ }
+
+ free(ducking);
+ }
+
+ SM_LEAVE_CRITICAL_SECTION(&ducking_mutex);
+
+ return _convert_sound_manager_error_code(__func__, ret);
+}
+
+int sound_manager_is_ducked(sound_stream_ducking_h stream_ducking, bool *is_ducked)
+{
+ int ret = MM_ERROR_NONE;
+ sound_stream_ducking_s *ducking = (sound_stream_ducking_s*)stream_ducking;
+
+ LOGI(">> enter %p", ducking);
+
+ SM_INSTANCE_CHECK(ducking);
+ SM_NULL_ARG_CHECK(is_ducked);
+
+ ret = _get_ducking_state(ducking->pa_info.index, is_ducked);
+
+ return _convert_sound_manager_error_code(__func__, ret);
+}
+
+int sound_manager_activate_ducking(sound_stream_ducking_h stream_ducking, unsigned int duration, double ratio)
+{
+ int ret = MM_ERROR_NONE;
+ bool is_ducked = false;
+ sound_stream_ducking_s *ducking = (sound_stream_ducking_s*)stream_ducking;
+
+ if (duration > 3000 || ratio >= 1.0 || ratio <= 0.0) {
+ LOGE("Invalid params : duration(%u) or ratio(%lf)", duration, ratio);
+ return SOUND_MANAGER_ERROR_INVALID_PARAMETER;
+ }
+
+ LOGI(">> enter %p - duration(%u), ratio(%lf)", ducking, duration, ratio);
+
+ SM_INSTANCE_CHECK(ducking);
+
+ ret = _get_ducking_state(ducking->pa_info.index, &is_ducked);
+ if (ret != MM_ERROR_NONE)
+ return _convert_sound_manager_error_code(__func__, ret);
+
+ SM_ENTER_CRITICAL_SECTION_WITH_RETURN(&ducking_mutex, SOUND_MANAGER_ERROR_INTERNAL);
+
+ if (!is_ducked) {
+ ducking->duration = duration;
+ ducking->ratio = ratio;
+
+ ret = _activate_ducking(ducking->pa_info.index,
+ true, ducking->target_stream, ducking->duration, ducking->ratio);
+ } else {
+ LOGE("already ducked");
+ ret = MM_ERROR_SOUND_INVALID_STATE;
+ }
+
+ SM_LEAVE_CRITICAL_SECTION(&ducking_mutex);
+
+ return _convert_sound_manager_error_code(__func__, ret);
+}
+
+int sound_manager_deactivate_ducking(sound_stream_ducking_h stream_ducking)
+{
+ int ret = MM_ERROR_NONE;
+ bool is_ducked = false;
+ sound_stream_ducking_s *ducking = (sound_stream_ducking_s*)stream_ducking;
+
+ LOGI(">> enter %p", ducking);
+
+ SM_INSTANCE_CHECK(ducking);
+
+ ret = _get_ducking_state(ducking->pa_info.index, &is_ducked);
+ if (ret != MM_ERROR_NONE)
+ return _convert_sound_manager_error_code(__func__, ret);
+
+ SM_ENTER_CRITICAL_SECTION_WITH_RETURN(&ducking_mutex, SOUND_MANAGER_ERROR_INTERNAL);
+
+ if (is_ducked) {
+ ret = _activate_ducking(ducking->pa_info.index,
+ false, ducking->target_stream, ducking->duration, ducking->ratio);
+ } else {
+ LOGE("not ducked");
+ ret = MM_ERROR_SOUND_INVALID_STATE;
+ }
+
+ SM_LEAVE_CRITICAL_SECTION(&ducking_mutex);
+
+ return _convert_sound_manager_error_code(__func__, ret);
+}
if (!ret) {
*stream_info = (sound_stream_info_h)stream_h;
LOGI("stream_h(%p), pa_index(%u), focus_id(%d), user_cb(%p), ret(0x%x)",
- stream_h, stream_h->pa_index, stream_h->focus_id, stream_h->user_cb, ret);
+ stream_h, stream_h->pa_info.index, stream_h->focus_id, stream_h->user_cb, ret);
}
}
SM_INSTANCE_CHECK(stream_h);
SM_NULL_ARG_CHECK(name);
- ret = _set_route_option(stream_h->pa_index, name, value);
+ ret = _set_route_option(stream_h->pa_info.index, name, value);
return _convert_sound_manager_error_code(__func__, ret);
}
SM_INSTANCE_CHECK(stream_h);
SM_NULL_ARG_CHECK(index);
- *index = stream_h->pa_index;
- LOGI("stream_index[%u]", stream_h->pa_index);
+ *index = stream_h->pa_info.index;
+ LOGI("stream_index[%u]", stream_h->pa_info.index);
return _convert_sound_manager_error_code(__func__, ret);
}
SM_NULL_ARG_CHECK(stream_h);
SM_NULL_ARG_CHECK(is_on);
- ret = mm_sound_is_stream_on_device_by_id(stream_h->pa_index, device_id, is_on);
+ ret = mm_sound_is_stream_on_device_by_id(stream_h->pa_info.index, device_id, is_on);
return _convert_sound_manager_error_code(__func__, ret);
}
#define PA_STREAM_MANAGER_METHOD_NAME_GET_CURRENT_MEDIA_ROUTING_PATH "GetCurrentMediaRoutingPath"
#define PA_STREAM_MANAGER_METHOD_NAME_UPDATE_FOCUS_STATUS "UpdateFocusStatus"
#define PA_STREAM_MANAGER_METHOD_NAME_CHECK_STREAM_EXIST_BY_PID "CheckStreamExistByPid"
+#define PA_STREAM_MANAGER_METHOD_NAME_ACTIVATE_DUCKING "ActivateDucking"
+#define PA_STREAM_MANAGER_METHOD_NAME_GET_DUCKING_STATE "GetDuckingState"
#define PA_DEVICE_MANAGER_OBJECT_PATH "/org/pulseaudio/DeviceManager"
#define PA_DEVICE_MANAGER_INTERFACE "org.pulseaudio.DeviceManager"
#define VCONF_PATH_PREFIX_VOLUME "file/private/sound/volume/"
#define VCONF_PATH_MAX 64
-#define DBUS_ERR_MSG_MAX 24
-
//LCOV_EXCL_START
int _convert_dbus_error(const char *error_msg)
{
int ret = MM_ERROR_NONE;
- const char ch = '.';
- char *ret_str;
if (!error_msg)
return MM_ERROR_SOUND_INTERNAL;
- /* parsing dbus error message, for example,
- * GDBus.Error:org.freedesktop.DBus.Error.InvalidArgs:
- * org.tizen.multimedia.audio.InvalidArgument */
- if (!(ret_str = strrchr(error_msg, ch)))
- return MM_ERROR_SOUND_INTERNAL;
-
- ret_str = ret_str + 1;
- if (ret_str[0] == '\0')
- return MM_ERROR_SOUND_INTERNAL;
-
- if (!strncmp(ret_str, "InvalidArgument", DBUS_ERR_MSG_MAX))
+ if (strstr(error_msg, "InvalidArgument"))
ret = MM_ERROR_INVALID_ARGUMENT;
- else if (!strncmp(ret_str, "InvalidOperation", DBUS_ERR_MSG_MAX))
+ else if (strstr(error_msg, "InvalidOperation"))
ret = MM_ERROR_SOUND_INVALID_OPERATION;
- else if (!strncmp(ret_str, "PolicyInternal", DBUS_ERR_MSG_MAX))
+ else if (strstr(error_msg, "PolicyInternal"))
ret = MM_ERROR_POLICY_INTERNAL;
+ else if (strstr(error_msg, "AccessDenied"))
+ ret = MM_ERROR_SOUND_PERMISSION_DENIED;
else
ret = MM_ERROR_SOUND_INTERNAL;
- LOGE("%s => 0x%x", ret_str, ret);
+ LOGE("%s => 0x%x", error_msg, ret);
return ret;
}
int _convert_stream_type(sound_stream_type_e stream_type_enum, char **stream_type)
{
- int ret = MM_ERROR_NONE;
-
if (stream_type == NULL)
return MM_ERROR_INVALID_ARGUMENT;
default:
//LCOV_EXCL_START
LOGE("could not find the stream_type[%d] in this switch case statement", stream_type_enum);
- ret = MM_ERROR_SOUND_INTERNAL;
- break;
+ return MM_ERROR_SOUND_INTERNAL;
//LCOV_EXCL_STOP
}
+
LOGI("stream_type[%s]", *stream_type);
- return ret;
+ return MM_ERROR_NONE;
}
//LCOV_EXCL_START
stream_info->acquired_focus |= focus_type;
if (state == FOCUS_IS_ACQUIRED)
- _update_focus_status(stream_info->pa_index, (unsigned int)stream_info->acquired_focus);
+ _update_focus_status(stream_info->pa_info.index, (unsigned int)stream_info->acquired_focus);
LOGI("[FOCUS USER CALLBACK(%p) START]", stream_info->user_cb);
stream_info->user_cb((sound_stream_info_h)stream_info, focus_type, state, change_reason,
LOGI("[FOCUS USER CALLBACK(%p) END]", stream_info->user_cb);
if (state == FOCUS_IS_RELEASED)
- _update_focus_status(stream_info->pa_index, (unsigned int)stream_info->acquired_focus);
+ _update_focus_status(stream_info->pa_info.index, (unsigned int)stream_info->acquired_focus);
if (state == FOCUS_IS_RELEASED)
stream_info->prev_acquired_focus &= ~focus_type;
void _pa_context_state_cb(pa_context *c, void *userdata)
{
pa_context_state_t state;
- sound_stream_info_s *stream_info_h = (sound_stream_info_s*)userdata;
+ sound_pa_info_s *pa_info = (sound_pa_info_s *)userdata;
assert(c);
state = pa_context_get_state(c);
- LOGI("[%p] context state = [%d]", stream_info_h, state);
+ LOGI("[%p] context state = [%d]", pa_info, state);
switch (state) {
case PA_CONTEXT_READY:
case PA_CONTEXT_TERMINATED:
case PA_CONTEXT_FAILED:
- pa_threaded_mainloop_signal(stream_info_h->pa_mainloop, 0);
+ pa_threaded_mainloop_signal(pa_info->mainloop, 0);
break;
case PA_CONTEXT_UNCONNECTED:
case PA_CONTEXT_CONNECTING:
}
//LCOV_EXCL_STOP
-int _make_pa_connection_and_register_focus(sound_stream_info_s *stream_h, sound_stream_focus_state_changed_cb callback, void *user_data)
+int _make_pa_connection(sound_pa_info_s *pa_info, const char *context_name)
{
- int ret = MM_ERROR_NONE;
int pa_ret = PA_OK;
- int i = 0;
- bool is_focus_cb_thread = false;
-
- if ((ret = mm_sound_focus_is_cb_thread(&is_focus_cb_thread, NULL)))
- return ret;
-
- if (is_focus_cb_thread)
- return MM_ERROR_SOUND_INVALID_OPERATION;
-
- /* get configuration information of this stream type */
- if ((ret = _get_stream_conf_info(stream_h->stream_type, &stream_h->stream_conf_info)))
- return ret;
- LOGI("stream_conf_info : stream type[%s], priority[%d], route type[%d]",
- stream_h->stream_type, stream_h->stream_conf_info.priority, stream_h->stream_conf_info.route_type);
+ SM_NULL_ARG_CHECK_FOR_PRIV(pa_info);
- if (!(stream_h->pa_mainloop = pa_threaded_mainloop_new()))
+ if (!(pa_info->mainloop = pa_threaded_mainloop_new()))
goto PA_ERROR;
- if (!(stream_h->pa_context = pa_context_new(pa_threaded_mainloop_get_api(stream_h->pa_mainloop), "SOUND_MANAGER_STREAM_INFO")))
+ if (!(pa_info->context = pa_context_new(pa_threaded_mainloop_get_api(pa_info->mainloop), context_name)))
goto PA_ERROR;
- pa_context_set_state_callback(stream_h->pa_context, _pa_context_state_cb, stream_h);
+ pa_context_set_state_callback(pa_info->context, _pa_context_state_cb, pa_info);
- if (pa_context_connect(stream_h->pa_context, NULL, 0, NULL) < 0) {
- pa_ret = pa_context_errno(stream_h->pa_context);//LCOV_EXCL_LINE
+ if (pa_context_connect(pa_info->context, NULL, 0, NULL) < 0) {
+ pa_ret = pa_context_errno(pa_info->context);//LCOV_EXCL_LINE
goto PA_ERROR;
}
- pa_threaded_mainloop_lock(stream_h->pa_mainloop);
+ pa_threaded_mainloop_lock(pa_info->mainloop);
- if (pa_threaded_mainloop_start(stream_h->pa_mainloop) < 0)
+ if (pa_threaded_mainloop_start(pa_info->mainloop) < 0)
goto PA_ERROR_WITH_UNLOCK;
/* wait for ready state of the context */
for (;;) {
pa_context_state_t state;
- state = pa_context_get_state(stream_h->pa_context);
+ state = pa_context_get_state(pa_info->context);
if (state == PA_CONTEXT_READY)
break;
if (!PA_CONTEXT_IS_GOOD(state)) {
- pa_ret = pa_context_errno(stream_h->pa_context);//LCOV_EXCL_LINE
+ pa_ret = pa_context_errno(pa_info->context);//LCOV_EXCL_LINE
goto PA_ERROR_WITH_UNLOCK;
}
- pa_threaded_mainloop_wait(stream_h->pa_mainloop);
+ pa_threaded_mainloop_wait(pa_info->mainloop);
}
/* get index of this context */
- stream_h->pa_index = pa_context_get_index(stream_h->pa_context);
+ pa_info->index = pa_context_get_index(pa_info->context);
- pa_threaded_mainloop_unlock(stream_h->pa_mainloop);
+ pa_threaded_mainloop_unlock(pa_info->mainloop);
+
+ return MM_ERROR_NONE;
+//LCOV_EXCL_START
+PA_ERROR_WITH_UNLOCK:
+ pa_threaded_mainloop_unlock(pa_info->mainloop);
+
+PA_ERROR:
+ _destroy_pa_connection(pa_info);
+ LOGE("pa_ret %d", pa_ret);
+
+ return MM_ERROR_SOUND_INTERNAL;
+//LCOV_EXCL_STOP
+}
+
+int _make_pa_connection_and_register_focus(sound_stream_info_s *stream_h, sound_stream_focus_state_changed_cb callback, void *user_data)
+{
+ int ret = MM_ERROR_NONE;
+ int i = 0;
+ bool is_focus_cb_thread = false;
+
+ if ((ret = mm_sound_focus_is_cb_thread(&is_focus_cb_thread, NULL)))
+ return ret;
+
+ if (is_focus_cb_thread)
+ return MM_ERROR_SOUND_INVALID_OPERATION;
+
+ /* get configuration information of this stream type */
+ if ((ret = _get_stream_conf_info(stream_h->stream_type, &stream_h->stream_conf_info)))
+ return ret;
+
+ LOGI("stream_conf_info : stream type[%s], priority[%d], route type[%d]",
+ stream_h->stream_type, stream_h->stream_conf_info.priority, stream_h->stream_conf_info.route_type);
+
+ if ((ret = _make_pa_connection(&stream_h->pa_info, "SOUND_MANAGER_STREAM_INFO")))
+ goto ERROR;
/* register focus */
if (!stream_h->is_focus_unavailable) {
} else {
LOGE("failed to register focus, ret(0x%x)", ret);//LCOV_EXCL_LINE
/* disconnect */
- goto PA_ERROR;
+ goto ERROR;
}
}
goto SUCCESS;
//LCOV_EXCL_START
-PA_ERROR_WITH_UNLOCK:
- pa_threaded_mainloop_unlock(stream_h->pa_mainloop);
-
-PA_ERROR:
+ERROR:
for (i = 0; i < AVAIL_DEVICES_MAX; i++) {
SM_SAFE_FREE(stream_h->stream_conf_info.avail_in_devices[i]);
SM_SAFE_FREE(stream_h->stream_conf_info.avail_out_devices[i]);
SM_SAFE_FREE(stream_h->stream_conf_info.volume_type);
- if (stream_h->pa_context) {
- pa_context_disconnect(stream_h->pa_context);
- pa_context_unref(stream_h->pa_context);
- stream_h->pa_context = NULL;
- }
- if (stream_h->pa_mainloop) {
- pa_threaded_mainloop_free(stream_h->pa_mainloop);
- stream_h->pa_mainloop = NULL;
- }
+ _destroy_pa_connection(&stream_h->pa_info);
+
ret = MM_ERROR_SOUND_INTERNAL;
- LOGE("pa_ret(%d), ret(0x%x)", pa_ret, ret);
//LCOV_EXCL_STOP
SUCCESS:
return ret;
}
+void _destroy_pa_connection(sound_pa_info_s *pa_info)
+{
+ if (!pa_info) {
+ LOGW("NULL pa info - skip..");
+ return;
+ }
+
+ LOGI("[%p][%p]", pa_info->mainloop, pa_info->context);
+
+ if (pa_info->mainloop)
+ pa_threaded_mainloop_stop(pa_info->mainloop);
+
+ if (pa_info->context) {
+ pa_context_disconnect(pa_info->context);
+ pa_context_unref(pa_info->context);
+ pa_info->context = NULL;
+ }
+
+ if (pa_info->mainloop) {
+ pa_threaded_mainloop_free(pa_info->mainloop);
+ pa_info->mainloop = NULL;
+ }
+
+ return;
+}
+
int _destroy_pa_connection_and_unregister_focus(sound_stream_info_s *stream_h)
{
int i = 0;
if (is_focus_cb_thread)
return MM_ERROR_SOUND_INVALID_OPERATION;
- if (stream_h->pa_mainloop)
- pa_threaded_mainloop_stop(stream_h->pa_mainloop);
-
- if (stream_h->pa_context) {
- pa_context_disconnect(stream_h->pa_context);
- pa_context_unref(stream_h->pa_context);
- stream_h->pa_context = NULL;
- }
-
- if (stream_h->pa_mainloop) {
- pa_threaded_mainloop_free(stream_h->pa_mainloop);
- stream_h->pa_mainloop = NULL;
- }
+ _destroy_pa_connection(&stream_h->pa_info);
/* unregister focus */
if (!stream_h->is_focus_unavailable) {
for (i = 0; i < AVAIL_DEVICES_MAX; i++) {
if (stream_info->manual_route_info.route_in_devices[i] ||
stream_info->manual_route_info.route_out_devices[i])
- return _set_manual_route_info(stream_info->pa_index, &stream_info->manual_route_info);
+ return _set_manual_route_info(stream_info->pa_info.index, &stream_info->manual_route_info);
}
return MM_ERROR_SOUND_INVALID_STATE;
memset((*virtual_stream), 0, sizeof(virtual_sound_stream_info_s));
(*virtual_stream)->stream_type = stream_info->stream_type;
- (*virtual_stream)->pa_mainloop = stream_info->pa_mainloop;
- (*virtual_stream)->pa_context = stream_info->pa_context;
+ (*virtual_stream)->pa_mainloop = stream_info->pa_info.mainloop;
+ (*virtual_stream)->pa_context = stream_info->pa_info.context;
(*virtual_stream)->pa_proplist = pa_proplist_new();
pa_proplist_sets((*virtual_stream)->pa_proplist, PA_PROP_MEDIA_ROLE, (*virtual_stream)->stream_type);
- pa_proplist_setf((*virtual_stream)->pa_proplist, PA_PROP_MEDIA_PARENT_ID, "%u", stream_info->pa_index);
+ pa_proplist_setf((*virtual_stream)->pa_proplist, PA_PROP_MEDIA_PARENT_ID, "%u", stream_info->pa_info.index);
(*virtual_stream)->state = _VSTREAM_STATE_READY;
(*virtual_stream)->stream_info = stream_info;
return ret;
}
//LCOV_EXCL_STOP
+
+int _activate_ducking(uint32_t stream_index, bool enable, const char *target_stream, uint32_t duration, double ratio)
+{
+ int ret = MM_ERROR_NONE;
+ GDBusConnection *conn = NULL;
+ GError *err = NULL;
+ GVariant *result = NULL;
+ const gchar *dbus_ret = NULL;
+
+ if ((ret = __get_dbus_connection(&conn)))
+ return ret;
+
+ result = g_dbus_connection_call_sync(conn,
+ PA_BUS_NAME,
+ PA_STREAM_MANAGER_OBJECT_PATH,
+ PA_STREAM_MANAGER_INTERFACE,
+ PA_STREAM_MANAGER_METHOD_NAME_ACTIVATE_DUCKING,
+ g_variant_new("(ubsud)", stream_index, enable, target_stream, duration, ratio),
+ G_VARIANT_TYPE("(s)"),
+ G_DBUS_CALL_FLAGS_NONE,
+ 2000,
+ NULL,
+ &err);
+ if (!result || err) {
+ LOGE("g_dbus_connection_call_sync() for ACTIVATE_DUCKING error");
+ ret = _convert_dbus_error(err ? err->message : NULL);
+ if (err)
+ g_error_free(err);
+ goto LEAVE;
+ }
+
+ g_variant_get(result, "(&s)", &dbus_ret);
+
+ LOGI("g_dbus_connection_call_sync() success, method return value is (%s)", dbus_ret);
+
+ if (strncmp("STREAM_MANAGER_RETURN_OK", dbus_ret, strlen(dbus_ret)))
+ ret = MM_ERROR_SOUND_INTERNAL;
+
+LEAVE:
+ g_variant_unref(result);
+ g_object_unref(conn);
+
+ return ret;
+}
+
+int _get_ducking_state(uint32_t stream_index, bool *is_ducked)
+{
+ int ret = MM_ERROR_NONE;
+ gboolean _is_ducked = FALSE;
+ GDBusConnection *conn = NULL;
+ GError *err = NULL;
+ GVariant *result = NULL;
+ const gchar *dbus_ret = NULL;
+
+ SM_NULL_ARG_CHECK_FOR_PRIV(is_ducked);
+
+ if ((ret = __get_dbus_connection(&conn)))
+ return ret;
+
+ result = g_dbus_connection_call_sync(conn,
+ PA_BUS_NAME,
+ PA_STREAM_MANAGER_OBJECT_PATH,
+ PA_STREAM_MANAGER_INTERFACE,
+ PA_STREAM_MANAGER_METHOD_NAME_GET_DUCKING_STATE,
+ g_variant_new("(u)", stream_index),
+ G_VARIANT_TYPE("(bs)"),
+ G_DBUS_CALL_FLAGS_NONE,
+ 1000,
+ NULL,
+ &err);
+ if (!result || err) {
+ LOGE("g_dbus_connection_call_sync() for GET_DUCKING_STATE error");
+ ret = _convert_dbus_error(err ? err->message : NULL);
+ if (err)
+ g_error_free(err);
+ goto LEAVE;
+ }
+
+ g_variant_get(result, "(b&s)", &_is_ducked, &dbus_ret);
+
+ LOGI("dbus return value is (%s)", dbus_ret);
+
+ if (!strncmp("STREAM_MANAGER_RETURN_OK", dbus_ret, strlen(dbus_ret))) {
+ *is_ducked = (bool)_is_ducked;
+ LOGI("is_ducked %u", *is_ducked);
+ } else {
+ ret = MM_ERROR_SOUND_INTERNAL;
+ }
+
+ if (strncmp("STREAM_MANAGER_RETURN_OK", dbus_ret, strlen(dbus_ret)))
+ ret = MM_ERROR_SOUND_INTERNAL;
+
+LEAVE:
+ g_variant_unref(result);
+ g_object_unref(conn);
+
+ return ret;
+}
CURRENT_STATUS_GET_MASTER_VOLUME,
CURRENT_STATUS_SET_ACM_MASTER_MODE,
#endif
+ CURRENT_STATUS_CREATE_STREAM_DUCKING,
+ CURRENT_STATUS_DESTROY_STREAM_DUCKING,
+ CURRENT_STATUS_ACTIVATE_DUCKING,
+ CURRENT_STATUS_DEACTIVATE_DUCKING,
+ CURRENT_STATUS_GET_DUCKING_STATE,
};
sound_device_mask_e g_device_mask = SOUND_DEVICE_ALL_MASK;
sound_stream_info_h g_stream_info_h = NULL;
virtual_sound_stream_h g_vstream_h = NULL;
+sound_stream_ducking_h g_stream_ducking_h = NULL;
int g_volume_cb_id;
int g_device_conn_cb_id;
int g_device_state_cb_id;
return;
}
+void ducking_state_changed_cb(sound_stream_ducking_h stream_ducking, bool is_ducked, void *user_data)
+{
+ g_print("stream_ducking %p, is_ducked %u\n", stream_ducking, is_ducked);
+
+ return;
+}
+
void quit_program()
{
g_main_loop_quit(g_loop);
else if (strncmp(cmd, "acm", MAX_CMD_LEN) == 0)
g_menu_state = CURRENT_STATUS_SET_ACM_MASTER_MODE;
#endif
+ else if (strncmp(cmd, "csd", MAX_CMD_LEN) == 0)
+ g_menu_state = CURRENT_STATUS_CREATE_STREAM_DUCKING;
+ else if (strncmp(cmd, "dsd", MAX_CMD_LEN) == 0)
+ g_menu_state = CURRENT_STATUS_DESTROY_STREAM_DUCKING;
+ else if (strncmp(cmd, "adk", MAX_CMD_LEN) == 0)
+ g_menu_state = CURRENT_STATUS_ACTIVATE_DUCKING;
+ else if (strncmp(cmd, "ddk", MAX_CMD_LEN) == 0)
+ g_menu_state = CURRENT_STATUS_DEACTIVATE_DUCKING;
+ else if (strncmp(cmd, "gds", MAX_CMD_LEN) == 0)
+ g_menu_state = CURRENT_STATUS_GET_DUCKING_STATE;
else if (strncmp(cmd, "q", MAX_CMD_LEN) == 0) {
g_print("closing the test suite\n");
quit_program();
g_print("mgv. *Get Master Volume \t");
g_print("msv. *Set Master Volume \n");
#endif
+ g_print("csd. Create Stream Ducking\t");
+ g_print("dsd. Destroy Stream Ducking\n");
+ g_print("adk. Activate Ducking\t\t");
+ g_print("ddk. Deactivate Ducking\n");
+ g_print("gds. Get Ducking State\n");
g_print("-----------------------------------------------------------------------------------------\n");
g_print(" DEVICE MODULE \n");
g_print("-----------------------------------------------------------------------------------------\n");
else if (g_menu_state == CURRENT_STATUS_SET_ACM_MASTER_MODE)
g_print("*** input master mode enable or disable (0:disable 1:enable)\n");
#endif
+ else if (g_menu_state == CURRENT_STATUS_CREATE_STREAM_DUCKING)
+ g_print("*** input target stream (0:MEDIA 1:SYSTEM 2:ALARM 3:NOTIFICATION 4:EMERGENCY 5:VOICE_INFORMATION 6:VOICE_RECOGNITION 7:RINGTONE_VOIP 8:VOIP 9:MEDIA_EXTERNAL_ONLY\n");
+ else if (g_menu_state == CURRENT_STATUS_DESTROY_STREAM_DUCKING)
+ g_print("*** press enter to destroy stream ducking\n");
+ else if (g_menu_state == CURRENT_STATUS_ACTIVATE_DUCKING) {
+ if (flag == 0)
+ g_print("*** input duration (0 <= duration <= 3000) and ratio (0.0 < ratio < 1.0)\n");
+ flag = 1;
+ } else if (g_menu_state == CURRENT_STATUS_DEACTIVATE_DUCKING)
+ g_print("*** press enter to deactivate ducking\n");
+ else if (g_menu_state == CURRENT_STATUS_GET_DUCKING_STATE)
+ g_print("*** press enter to get ducking state\n");
else {
g_print("*** unknown status.\n");
quit_program();
break;
}
#endif
+ case CURRENT_STATUS_CREATE_STREAM_DUCKING: {
+ int ret = SOUND_MANAGER_ERROR_NONE;
+ sound_stream_type_e target_stream = SOUND_STREAM_TYPE_MEDIA;
+
+ if (g_stream_ducking_h) {
+ g_print("fail to create stream ducking, g_stream_ducking_h(%p) is already set\n", g_stream_ducking_h);
+ reset_menu_state();
+ break;
+ }
+
+ target_stream = (sound_stream_type_e)atoi(cmd);
+
+ ret = sound_manager_create_stream_ducking(target_stream,
+ ducking_state_changed_cb, NULL, &g_stream_ducking_h);
+ if (ret)
+ g_print("fail to sound_manager_create_stream_ducking(t:%u), ret(0x%x)\n", target_stream, ret);
+
+ reset_menu_state();
+ break;
+ }
+ case CURRENT_STATUS_DESTROY_STREAM_DUCKING: {
+ int ret = SOUND_MANAGER_ERROR_NONE;
+ ret = sound_manager_destroy_stream_ducking(g_stream_ducking_h);
+ if (ret)
+ g_print("fail to sound_manager_destroy_stream_ducking(), ret(0x%x)\n", ret);
+ else
+ g_stream_ducking_h = NULL;
+
+ reset_menu_state();
+ break;
+ }
+ case CURRENT_STATUS_ACTIVATE_DUCKING: {
+ int ret = SOUND_MANAGER_ERROR_NONE;
+ static int cnt = 0;
+ static unsigned int duration = 0;
+ static double ratio = 0;
+
+ switch (cnt) {
+ case 0:
+ duration = (unsigned int)atoi(cmd);
+ cnt++;
+ break;
+ case 1:
+ ratio = (double)atof(cmd);
+
+ ret = sound_manager_activate_ducking(g_stream_ducking_h, duration, ratio);
+ if (ret)
+ g_print("fail to activate_ducking(%u,%lf), ret(0x%x)\n", duration, ratio, ret);
+ else
+ g_print("success to sound_manager_activate_ducking()\n");
+
+ cnt = 0;
+ reset_menu_state();
+ break;
+ }
+ break;
+ }
+ case CURRENT_STATUS_DEACTIVATE_DUCKING: {
+ int ret = SOUND_MANAGER_ERROR_NONE;
+
+ ret = sound_manager_deactivate_ducking(g_stream_ducking_h);
+ if (ret)
+ g_print("fail to sound_manager_deactivate_ducking(), ret(0x%x)\n", ret);
+ else
+ g_print("success to sound_manager_deactivate_ducking()\n");
+
+ reset_menu_state();
+ break;
+ }
+ case CURRENT_STATUS_GET_DUCKING_STATE: {
+ int ret = SOUND_MANAGER_ERROR_NONE;
+ bool is_ducked = false;
+
+ ret = sound_manager_is_ducked(g_stream_ducking_h, &is_ducked);
+ if (ret)
+ g_print("fail to sound_manager_is_ducked(), ret(0x%x)\n", ret);
+ else
+ g_print("success to sound_manager_is_ducked() - is_ducked %u\n", is_ducked);
+
+ reset_menu_state();
+ break;
+ }
}
g_timeout_add(100, timeout_menu_display, 0);
}