From: Ronan Le Martret Date: Fri, 5 Sep 2014 07:58:03 +0000 (+0200) Subject: meta-tizen: Fix pulseaudio. X-Git-Tag: rev_ivi_2015_02_04~309 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=338cc17a215a973602e8b8c01afb5fd011f9994e;p=scm%2Fbb%2Ftizen-distro.git meta-tizen: Fix pulseaudio. Change-Id: Ibc8e7d6c261df47e722d16858d6dae348ada42c3 (From meta-tizen rev: a830a659142e439a19e11f4b79b3104695778cd7) Signed-off-by: Ronan Le Martret Signed-off-by: Patrick Ohly --- diff --git a/meta-tizen/recipes-multimedia/pulseaudio/pulseaudio_5.0.bbappend b/meta-tizen/recipes-multimedia/pulseaudio/pulseaudio_5.0.bbappend index 1c2694a..017b273 100644 --- a/meta-tizen/recipes-multimedia/pulseaudio/pulseaudio_5.0.bbappend +++ b/meta-tizen/recipes-multimedia/pulseaudio/pulseaudio_5.0.bbappend @@ -1,4 +1,110 @@ FILESEXTRAPATHS_prepend := "${THISDIR}/pulseaudio_5.0" SRC_URI += "file://change_libsystemd_to_libsystemd-login_in_configure.patch" -SRC_URI += "file://changes-to-pa-simple-api-samsung.patch" +#SRC_URI += "file://changes-to-pa-simple-api-samsung.patch" + +B="${S}" + +SRC_URI += "file://0001-volume-ramp-additions-to-the-low-level-infra.patch" +SRC_URI += "file://0002-volume-ramp-add-volume-ramping-to-sink.patch" +SRC_URI += "file://0003-volume-ramp-adding-volume-ramping-to-sink-input.patch" +SRC_URI += "file://0004-build-sys-install-files-for-a-module-development.patch" +SRC_URI += "file://0005-jack-detection-fix-for-wired-headset.patch" +SRC_URI += "file://0006-make-pa_thread_mq_done-safe-for-subsequent-calls.patch" +SRC_URI += "file://0007-node-manager-adding-external-node-manager-API.patch" +SRC_URI += "file://0008-node-manager-adding-node-support-for-pactl.patch" +SRC_URI += "file://0009-add-internal-corking-state-for-sink-input.patch" +SRC_URI += "file://0010-bluetooth-Add-basic-support-for-HEADSET-profiles.patch" +SRC_URI += "file://0011-bluetooth-Create-Handsfree-Audio-Agent-NULL-backend.patch" +SRC_URI += "file://0012-bluetooth-Create-Handsfree-Audio-Agent-oFono-backend.patch" +SRC_URI += "file://0013-bluetooth-Monitor-D-Bus-signals.patch" +SRC_URI += "file://0014-bluetooth-Create-pa_bluetooth_dbus_send_and_add_to_p.patch" +SRC_URI += "file://0015-bluetooth-Register-Unregister-Handsfree-Audio-Agent-.patch" +SRC_URI += "file://0016-bluetooth-List-HandsfreeAudioCard-objects-from-oFono.patch" +SRC_URI += "file://0017-bluetooth-Parse-HandsfreeAudioCard-properties.patch" +SRC_URI += "file://0018-bluetooth-Implement-transport-acquire-for-hf_audio_a.patch" +SRC_URI += "file://0019-bluetooth-Track-oFono-service.patch" +SRC_URI += "file://0020-bluetooth-Handle-CardAdded-signal.patch" +SRC_URI += "file://0021-bluetooth-Handle-CardRemoved-signal.patch" +SRC_URI += "file://0022-bluetooth-Implement-org.ofono.HandsfreeAudioAgent.Ne.patch" +SRC_URI += "file://0023-bluetooth-Fix-not-handle-fd-in-DEFER_SETUP-state.patch" +SRC_URI += "file://0024-bluetooth-Suspend-sink-source-on-HFP-s-stream-HUP.patch" +SRC_URI += "file://0025-bluetooth-Implement-transport-release-for-hf_audio_a.patch" +SRC_URI += "file://0026-bluetooth-Fixes-HFP-audio-transfer-when-initiator.patch" +SRC_URI += "file://0027-bluetooth-Set-off-profile-as-default-for-newly-creat.patch" +SRC_URI += "file://0028-fix-ofono-and-pulseaudio-starting-order-assert.patch" +SRC_URI += "file://0029-hfp-do-safe-strcmp-in-dbus-handler.patch" +SRC_URI += "file://0030-add-parameter-to-define-key-used-in-stream-restore.patch" +SRC_URI += "file://0031-increase-alsa-rewind-safeguard.patch" +SRC_URI += "file://0032-fix-for-profile-change-prototype-in-bluez5-patch.patch" +SRC_URI += "file://0033-changes-to-pa-simple-api-samsung.patch" +SRC_URI += "file://0034-add-support-for-samsung-power-management-samsung.patch" +SRC_URI += "file://0035-Add-preload-fileter-for-resample-samsung.patch.gz" +SRC_URI += "file://0036-Enhance-for-echo-cancel-samsung.patch" +SRC_URI += "file://0037-add-support-for-dlog-samsung.patch" +SRC_URI += "file://0038-add-policy-module-samsung.patch" +SRC_URI += "file://0039-add-bluetooth-a2dp-aptx-codec-support-samsung.patch" +SRC_URI += "file://0040-create-pa_ready-file-samsung.patch" +SRC_URI += "file://0041-set-alsa-suspend-timeout-to-zero-samsung.patch" +SRC_URI += "file://0042-cope-with-possible-infinite-waiting-in-startup-samsu.patch" +SRC_URI += "file://0043-use-udev-only-for-usb-devices-samsung.patch" +SRC_URI += "file://0044-fixes-and-improvements-to-makefile-and-configure-in-.patch" +SRC_URI += "file://0045-fix-warning-in-gconf-helper.patch" +SRC_URI += "file://0046-volume-ramp-add-client-api-support-for-volume-rampin.patch" +SRC_URI += "file://0047-adjust-default-bluetooth-profile-to-off.patch" +SRC_URI += "file://0048-Add-bt_profile_set-patch-which-fixed-bt-a2dp-hsp-pro.patch" +SRC_URI += "file://0049-added-pulseaudio.service-file.patch" +SRC_URI += "file://0050-.gitignore-Add-pulsecore-config.h.patch" +SRC_URI += "file://0051-pactl-Fix-crash-with-older-servers.patch" +SRC_URI += "file://0052-volume-Increase-PA_SW_VOLUME_SNPRINT_DB_MAX.patch" +SRC_URI += "file://0053-sink-input-source-output-Fix-mute-saving.patch" +SRC_URI += "file://0054-direction-Add-a-couple-of-direction-helper-functions.patch" +SRC_URI += "file://0055-core-util-Add-pa_join.patch" +SRC_URI += "file://0056-dynarray-Add-pa_dynarray_get_raw_array.patch" +SRC_URI += "file://0057-dynarray-Add-PA_DYNARRAY_FOREACH.patch" +SRC_URI += "file://0058-dynarray-Add-pa_dynarray_remove_last.patch" +SRC_URI += "file://0059-dynarray-Add-pa_dynarray_remove_all.patch" +SRC_URI += "file://0060-hashmap-Add-pa_hashmap_remove_and_free.patch" +SRC_URI += "file://0061-device-port-Add-pa_device_port.active.patch" +SRC_URI += "file://0062-sink-source-Assign-to-reference_volume-from-only-one.patch" +SRC_URI += "file://0063-sink-input-source-output-Assign-to-volume-from-only-.patch" +SRC_URI += "file://0064-sink-source-Return-early-from-set_mute.patch" +SRC_URI += "file://0065-sink-input-source-output-Add-logging-to-set_mute.patch" +SRC_URI += "file://0066-sink-source-Allow-calling-set_mute-during-initializa.patch" +SRC_URI += "file://0067-echo-cancel-Remove-redundant-get_mute-callback.patch" +SRC_URI += "file://0068-sink-source-Call-set_mute-from-mute_changed.patch" +SRC_URI += "file://0069-sink-source-Assign-to-s-muted-from-only-one-place.patch" +SRC_URI += "file://0070-sink-input-source-output-Remove-redundant-get_mute-f.patch" +SRC_URI += "file://0071-solaris-tunnel-Remove-some-redundant-boolean-convers.patch" +SRC_URI += "file://0072-sink-source-Add-hooks-for-volume-changes.patch" +SRC_URI += "file://0073-sink-input-source-output-Add-hooks-for-volume-change.patch" +SRC_URI += "file://0074-sink-source-Add-hooks-for-mute-changes.patch" +SRC_URI += "file://0075-sink-input-source-output-Add-hooks-for-mute-changes.patch" +SRC_URI += "file://0076-sink-Link-monitor-source-before-activating-port.patch" +SRC_URI += "file://0077-context-extension-Add-the-pa_extension-class.patch" +SRC_URI += "file://0078-volume-api-Add-libvolume-api.so.patch.gz" +SRC_URI += "file://0079-Add-module-volume-api-and-the-related-client-API.patch.gz" +SRC_URI += "file://0080-pactl-Add-support-for-the-new-volume-API.patch" +SRC_URI += "file://0081-Add-module-audio-groups.patch" +SRC_URI += "file://0082-Add-module-main-volume-policy.patch" +SRC_URI += "file://0083-configuration-Add-default-IVI-audio-group-and-main-v.patch" +SRC_URI += "file://0084-sink-source-Initialize-port-before-fixate-hook-fixes.patch" +SRC_URI += "file://0085-configuration-pulseaudio-tizen-configuration-in-defa.patch" +SRC_URI += "file://0086-pactl-Fix-crash-in-pactl-list.patch" +SRC_URI += "file://0087-configuration-x-example-x-tizen-ivi-in-volume-config.patch" +SRC_URI += "file://0088-device-creator-stream-creator-Add-a-couple-of-assert.patch" +SRC_URI += "file://0089-main-volume-policy-Fix-a-memory-leak.patch" +SRC_URI += "file://0090-audio-groups-fix-issues-found-by-static-analysis.patch" +SRC_URI += "file://0091-core-util-Add-pa_append_to_home_dir.patch" +SRC_URI += "file://0092-core-util-Add-pa_get_config_home_dir.patch" +SRC_URI += "file://0093-core-util-Add-pa_append_to_config_home_dir.patch" +SRC_URI += "file://0094-core-Create-the-config-home-directory-on-startup.patch" +SRC_URI += "file://0095-alsa-Handle-unlinking-of-uninitialized-streams-grace.patch" +SRC_URI += "file://0096-role-cork-Handle-unlinking-of-uninitialized-streams-.patch" +SRC_URI += "file://0097-role-ducking-Handle-unlinking-of-uninitialized-strea.patch" +SRC_URI += "file://0098-core-util-Add-pa_boolean_to_string.patch" +SRC_URI += "file://0099-sink-input-source-output-Assign-to-reference_ratio-f.patch" +SRC_URI += "file://0100-sink-input-source-output-Add-hooks-for-reference-rat.patch" +SRC_URI += "file://0101-sink-input-source-output-Use-new_data.volume-only-fo.patch" +SRC_URI += "file://0102-sink-input-source-output-Add-the-real-object-pointer.patch" +SRC_URI += "file://0103-audio-groups-main-volume-policy-volume-api-Various-f.patch" \ No newline at end of file diff --git a/meta-tizen/recipes-multimedia/pulseaudio/pulseaudio_5.0/0001-volume-ramp-additions-to-the-low-level-infra.patch b/meta-tizen/recipes-multimedia/pulseaudio/pulseaudio_5.0/0001-volume-ramp-additions-to-the-low-level-infra.patch new file mode 100644 index 0000000..8b2693c --- /dev/null +++ b/meta-tizen/recipes-multimedia/pulseaudio/pulseaudio_5.0/0001-volume-ramp-additions-to-the-low-level-infra.patch @@ -0,0 +1,559 @@ +From: Jaska Uimonen +Date: Wed, 8 Aug 2012 11:14:37 +0300 +Subject: volume ramp: additions to the low level infra + +Change-Id: Ib2be01e0bea0856ebd0256066981fe34d05a4f86 +Signed-off-by: Jaska Uimonen +--- + src/map-file | 4 + + src/pulse/def.h | 13 ++- + src/pulse/volume.c | 74 ++++++++++++- + src/pulse/volume.h | 33 ++++++ + src/pulsecore/mix.c | 310 ++++++++++++++++++++++++++++++++++++++++++++++++++++ + src/pulsecore/mix.h | 27 +++++ + 6 files changed, 459 insertions(+), 2 deletions(-) + +diff --git a/src/map-file b/src/map-file +index fbad1a4..9903942 100644 +--- a/src/map-file ++++ b/src/map-file +@@ -136,6 +136,10 @@ pa_cvolume_max_mask; + pa_cvolume_merge; + pa_cvolume_min; + pa_cvolume_min_mask; ++pa_cvolume_ramp_equal; ++pa_cvolume_ramp_init; ++pa_cvolume_ramp_set; ++pa_cvolume_ramp_channel_ramp_set; + pa_cvolume_remap; + pa_cvolume_scale; + pa_cvolume_scale_mask; +diff --git a/src/pulse/def.h b/src/pulse/def.h +index d6fa912..b7854a3 100644 +--- a/src/pulse/def.h ++++ b/src/pulse/def.h +@@ -349,11 +349,15 @@ typedef enum pa_stream_flags { + * consider absolute when the sink is in flat volume mode, + * relative otherwise. \since 0.9.20 */ + +- PA_STREAM_PASSTHROUGH = 0x80000U ++ PA_STREAM_PASSTHROUGH = 0x80000U, + /**< Used to tag content that will be rendered by passthrough sinks. + * The data will be left as is and not reformatted, resampled. + * \since 1.0 */ + ++ PA_STREAM_START_RAMP_MUTED = 0x100000U ++ /**< Used to tag content that the stream will be started ramp volume ++ * muted so that you can nicely fade it in */ ++ + } pa_stream_flags_t; + + /** \cond fulldocs */ +@@ -382,6 +386,7 @@ typedef enum pa_stream_flags { + #define PA_STREAM_FAIL_ON_SUSPEND PA_STREAM_FAIL_ON_SUSPEND + #define PA_STREAM_RELATIVE_VOLUME PA_STREAM_RELATIVE_VOLUME + #define PA_STREAM_PASSTHROUGH PA_STREAM_PASSTHROUGH ++#define PA_STREAM_START_RAMP_MUTED PA_STREAM_START_RAMP_MUTED + + /** \endcond */ + +@@ -1049,6 +1054,12 @@ typedef enum pa_port_available { + /** \endcond */ + #endif + ++/** \cond fulldocs */ ++#define PA_VOLUMER_RAMP_TYPE_LINEAR PA_VOLUMER_RAMP_TYPE_LINEAR ++#define PA_VOLUMER_RAMP_TYPE_LOGARITHMIC PA_VOLUMER_RAMP_TYPE_LOGARITHMIC ++#define PA_VOLUMER_RAMP_TYPE_CUBIC PA_VOLUMER_RAMP_TYPE_CUBIC ++/** \endcond */ ++ + PA_C_DECL_END + + #endif +diff --git a/src/pulse/volume.c b/src/pulse/volume.c +index 6927392..b140cee 100644 +--- a/src/pulse/volume.c ++++ b/src/pulse/volume.c +@@ -447,7 +447,10 @@ int pa_cvolume_channels_equal_to(const pa_cvolume *a, pa_volume_t v) { + unsigned c; + pa_assert(a); + +- pa_return_val_if_fail(pa_cvolume_valid(a), 0); ++ if (pa_cvolume_valid(a) == 0) ++ abort(); ++ ++ /* pa_return_val_if_fail(pa_cvolume_valid(a), 0); */ + pa_return_val_if_fail(PA_VOLUME_IS_VALID(v), 0); + + for (c = 0; c < a->channels; c++) +@@ -988,3 +991,72 @@ pa_cvolume* pa_cvolume_dec(pa_cvolume *v, pa_volume_t dec) { + + return pa_cvolume_scale(v, m); + } ++ ++int pa_cvolume_ramp_equal(const pa_cvolume_ramp *a, const pa_cvolume_ramp *b) { ++ int i; ++ pa_assert(a); ++ pa_assert(b); ++ ++ if (PA_UNLIKELY(a == b)) ++ return 1; ++ ++ if (a->channels != b->channels) ++ return 0; ++ ++ for (i = 0; i < a->channels; i++) { ++ if (a->ramps[i].type != b->ramps[i].type || ++ a->ramps[i].length != b->ramps[i].length || ++ a->ramps[i].target != b->ramps[i].target) ++ return 0; ++ } ++ ++ return 1; ++} ++ ++pa_cvolume_ramp* pa_cvolume_ramp_init(pa_cvolume_ramp *ramp) { ++ unsigned c; ++ ++ pa_assert(ramp); ++ ++ ramp->channels = 0; ++ ++ for (c = 0; c < PA_CHANNELS_MAX; c++) { ++ ramp->ramps[c].type = PA_VOLUME_RAMP_TYPE_LINEAR; ++ ramp->ramps[c].length = 0; ++ ramp->ramps[c].target = PA_VOLUME_INVALID; ++ } ++ ++ return ramp; ++} ++ ++pa_cvolume_ramp* pa_cvolume_ramp_set(pa_cvolume_ramp *ramp, unsigned channels, pa_volume_ramp_type_t type, long time, pa_volume_t vol) { ++ int i; ++ ++ pa_assert(ramp); ++ pa_assert(channels > 0); ++ pa_assert(time >= 0); ++ pa_assert(channels <= PA_CHANNELS_MAX); ++ ++ ramp->channels = (uint8_t) channels; ++ ++ for (i = 0; i < ramp->channels; i++) { ++ ramp->ramps[i].type = type; ++ ramp->ramps[i].length = time; ++ ramp->ramps[i].target = PA_CLAMP_VOLUME(vol); ++ } ++ ++ return ramp; ++} ++ ++pa_cvolume_ramp* pa_cvolume_ramp_channel_ramp_set(pa_cvolume_ramp *ramp, unsigned channel, pa_volume_ramp_type_t type, long time, pa_volume_t vol) { ++ ++ pa_assert(ramp); ++ pa_assert(channel <= ramp->channels); ++ pa_assert(time >= 0); ++ ++ ramp->ramps[channel].type = type; ++ ramp->ramps[channel].length = time; ++ ramp->ramps[channel].target = PA_CLAMP_VOLUME(vol); ++ ++ return ramp; ++} +diff --git a/src/pulse/volume.h b/src/pulse/volume.h +index 7806954..3ffb573 100644 +--- a/src/pulse/volume.h ++++ b/src/pulse/volume.h +@@ -415,6 +415,39 @@ pa_cvolume* pa_cvolume_inc(pa_cvolume *v, pa_volume_t inc); + * the channels are kept. \since 0.9.16 */ + pa_cvolume* pa_cvolume_dec(pa_cvolume *v, pa_volume_t dec); + ++/** Volume ramp type ++*/ ++typedef enum pa_volume_ramp_type { ++ PA_VOLUME_RAMP_TYPE_LINEAR = 0, /**< linear */ ++ PA_VOLUME_RAMP_TYPE_LOGARITHMIC = 1, /**< logarithmic */ ++ PA_VOLUME_RAMP_TYPE_CUBIC = 2, ++} pa_volume_ramp_type_t; ++ ++/** A structure encapsulating a volume ramp */ ++typedef struct pa_volume_ramp_t { ++ pa_volume_ramp_type_t type; ++ long length; ++ pa_volume_t target; ++} pa_volume_ramp_t; ++ ++/** A structure encapsulating a multichannel volume ramp */ ++typedef struct pa_cvolume_ramp { ++ uint8_t channels; ++ pa_volume_ramp_t ramps[PA_CHANNELS_MAX]; ++} pa_cvolume_ramp; ++ ++/** Return non-zero when *a == *b */ ++int pa_cvolume_ramp_equal(const pa_cvolume_ramp *a, const pa_cvolume_ramp *b); ++ ++/** Init volume ramp struct */ ++pa_cvolume_ramp* pa_cvolume_ramp_init(pa_cvolume_ramp *ramp); ++ ++/** Set first n channels of ramp struct to certain value */ ++pa_cvolume_ramp* pa_cvolume_ramp_set(pa_cvolume_ramp *ramp, unsigned channel, pa_volume_ramp_type_t type, long time, pa_volume_t vol); ++ ++/** Set individual channel in the channel struct */ ++pa_cvolume_ramp* pa_cvolume_ramp_channel_ramp_set(pa_cvolume_ramp *ramp, unsigned channel, pa_volume_ramp_type_t type, long time, pa_volume_t vol); ++ + PA_C_DECL_END + + #endif +diff --git a/src/pulsecore/mix.c b/src/pulsecore/mix.c +index 4b789a6..c1e0c18 100644 +--- a/src/pulsecore/mix.c ++++ b/src/pulsecore/mix.c +@@ -721,3 +721,313 @@ void pa_volume_memchunk( + + pa_memblock_release(c->memblock); + } ++ ++static void calc_linear_integer_volume_no_mapping(int32_t linear[], float volume[], unsigned nchannels) { ++ unsigned channel, padding; ++ ++ pa_assert(linear); ++ pa_assert(volume); ++ ++ for (channel = 0; channel < nchannels; channel++) ++ linear[channel] = (int32_t) lrint(volume[channel] * 0x10000U); ++ ++ for (padding = 0; padding < VOLUME_PADDING; padding++, channel++) ++ linear[channel] = linear[padding]; ++} ++ ++static void calc_linear_float_volume_no_mapping(float linear[], float volume[], unsigned nchannels) { ++ unsigned channel, padding; ++ ++ pa_assert(linear); ++ pa_assert(volume); ++ ++ for (channel = 0; channel < nchannels; channel++) ++ linear[channel] = volume[channel]; ++ ++ for (padding = 0; padding < VOLUME_PADDING; padding++, channel++) ++ linear[channel] = linear[padding]; ++} ++ ++typedef void (*pa_calc_volume_no_mapping_func_t) (void *volumes, float *volume, int channels); ++ ++static const pa_calc_volume_no_mapping_func_t calc_volume_table_no_mapping[] = { ++ [PA_SAMPLE_U8] = (pa_calc_volume_no_mapping_func_t) calc_linear_integer_volume_no_mapping, ++ [PA_SAMPLE_ALAW] = (pa_calc_volume_no_mapping_func_t) calc_linear_integer_volume_no_mapping, ++ [PA_SAMPLE_ULAW] = (pa_calc_volume_no_mapping_func_t) calc_linear_integer_volume_no_mapping, ++ [PA_SAMPLE_S16LE] = (pa_calc_volume_no_mapping_func_t) calc_linear_integer_volume_no_mapping, ++ [PA_SAMPLE_S16BE] = (pa_calc_volume_no_mapping_func_t) calc_linear_integer_volume_no_mapping, ++ [PA_SAMPLE_FLOAT32LE] = (pa_calc_volume_no_mapping_func_t) calc_linear_float_volume_no_mapping, ++ [PA_SAMPLE_FLOAT32BE] = (pa_calc_volume_no_mapping_func_t) calc_linear_float_volume_no_mapping, ++ [PA_SAMPLE_S32LE] = (pa_calc_volume_no_mapping_func_t) calc_linear_integer_volume_no_mapping, ++ [PA_SAMPLE_S32BE] = (pa_calc_volume_no_mapping_func_t) calc_linear_integer_volume_no_mapping, ++ [PA_SAMPLE_S24LE] = (pa_calc_volume_no_mapping_func_t) calc_linear_integer_volume_no_mapping, ++ [PA_SAMPLE_S24BE] = (pa_calc_volume_no_mapping_func_t) calc_linear_integer_volume_no_mapping, ++ [PA_SAMPLE_S24_32LE] = (pa_calc_volume_no_mapping_func_t) calc_linear_integer_volume_no_mapping, ++ [PA_SAMPLE_S24_32BE] = (pa_calc_volume_no_mapping_func_t) calc_linear_integer_volume_no_mapping ++}; ++ ++static const unsigned format_sample_size_table[] = { ++ [PA_SAMPLE_U8] = 1, ++ [PA_SAMPLE_ALAW] = 1, ++ [PA_SAMPLE_ULAW] = 1, ++ [PA_SAMPLE_S16LE] = 2, ++ [PA_SAMPLE_S16BE] = 2, ++ [PA_SAMPLE_FLOAT32LE] = 4, ++ [PA_SAMPLE_FLOAT32BE] = 4, ++ [PA_SAMPLE_S32LE] = 4, ++ [PA_SAMPLE_S32BE] = 4, ++ [PA_SAMPLE_S24LE] = 3, ++ [PA_SAMPLE_S24BE] = 3, ++ [PA_SAMPLE_S24_32LE] = 4, ++ [PA_SAMPLE_S24_32BE] = 4 ++}; ++ ++static float calc_volume_ramp_linear(pa_volume_ramp_int_t *ramp) { ++ pa_assert(ramp); ++ pa_assert(ramp->length > 0); ++ ++ /* basic linear interpolation */ ++ return ramp->start + (ramp->length - ramp->left) * (ramp->end - ramp->start) / (float) ramp->length; ++} ++ ++static float calc_volume_ramp_logarithmic(pa_volume_ramp_int_t *ramp) { ++ float x_val, s, e; ++ long temp; ++ ++ pa_assert(ramp); ++ pa_assert(ramp->length > 0); ++ ++ if (ramp->end > ramp->start) { ++ temp = ramp->left; ++ s = ramp->end; ++ e = ramp->start; ++ } else { ++ temp = ramp->length - ramp->left; ++ s = ramp->start; ++ e = ramp->end; ++ } ++ ++ x_val = temp == 0 ? 0.0 : powf(temp, 10); ++ ++ /* base 10 logarithmic interpolation */ ++ return s + x_val * (e - s) / powf(ramp->length, 10); ++} ++ ++static float calc_volume_ramp_cubic(pa_volume_ramp_int_t *ramp) { ++ float x_val, s, e; ++ long temp; ++ ++ pa_assert(ramp); ++ pa_assert(ramp->length > 0); ++ ++ if (ramp->end > ramp->start) { ++ temp = ramp->left; ++ s = ramp->end; ++ e = ramp->start; ++ } else { ++ temp = ramp->length - ramp->left; ++ s = ramp->start; ++ e = ramp->end; ++ } ++ ++ x_val = temp == 0 ? 0.0 : cbrtf(temp); ++ ++ /* cubic interpolation */ ++ return s + x_val * (e - s) / cbrtf(ramp->length); ++} ++ ++typedef float (*pa_calc_volume_ramp_func_t) (pa_volume_ramp_int_t *); ++ ++static const pa_calc_volume_ramp_func_t calc_volume_ramp_table[] = { ++ [PA_VOLUME_RAMP_TYPE_LINEAR] = (pa_calc_volume_ramp_func_t) calc_volume_ramp_linear, ++ [PA_VOLUME_RAMP_TYPE_LOGARITHMIC] = (pa_calc_volume_ramp_func_t) calc_volume_ramp_logarithmic, ++ [PA_VOLUME_RAMP_TYPE_CUBIC] = (pa_calc_volume_ramp_func_t) calc_volume_ramp_cubic ++}; ++ ++static void calc_volume_ramps(pa_cvolume_ramp_int *ram, float *vol) ++{ ++ int i; ++ ++ for (i = 0; i < ram->channels; i++) { ++ if (ram->ramps[i].left <= 0) { ++ if (ram->ramps[i].target == PA_VOLUME_NORM) { ++ vol[i] = 1.0; ++ } ++ } else { ++ vol[i] = ram->ramps[i].curr = calc_volume_ramp_table[ram->ramps[i].type] (&ram->ramps[i]); ++ ram->ramps[i].left--; ++ } ++ } ++} ++ ++void pa_volume_ramp_memchunk( ++ pa_memchunk *c, ++ const pa_sample_spec *spec, ++ pa_cvolume_ramp_int *ramp) { ++ ++ void *ptr; ++ volume_val linear[PA_CHANNELS_MAX + VOLUME_PADDING]; ++ float vol[PA_CHANNELS_MAX + VOLUME_PADDING]; ++ pa_do_volume_func_t do_volume; ++ long length_in_frames; ++ int i; ++ ++ pa_assert(c); ++ pa_assert(spec); ++ pa_assert(pa_frame_aligned(c->length, spec)); ++ pa_assert(ramp); ++ ++ length_in_frames = c->length / format_sample_size_table[spec->format] / spec->channels; ++ ++ if (pa_memblock_is_silence(c->memblock)) { ++ for (i = 0; i < ramp->channels; i++) { ++ if (ramp->ramps[i].length > 0) ++ ramp->ramps[i].length -= length_in_frames; ++ } ++ return; ++ } ++ ++ if (spec->format < 0 || spec->format >= PA_SAMPLE_MAX) { ++ pa_log_warn("Unable to change volume of format"); ++ return; ++ } ++ ++ do_volume = pa_get_volume_func(spec->format); ++ pa_assert(do_volume); ++ ++ ptr = (uint8_t*) pa_memblock_acquire(c->memblock) + c->index; ++ ++ for (i = 0; i < length_in_frames; i++) { ++ calc_volume_ramps(ramp, vol); ++ calc_volume_table_no_mapping[spec->format] ((void *)linear, vol, spec->channels); ++ ++ /* we only process one frame per iteration */ ++ do_volume (ptr, (void *)linear, spec->channels, format_sample_size_table[spec->format] * spec->channels); ++ ++ /* pa_log_debug("1: %d 2: %d", linear[0], linear[1]); */ ++ ++ ptr = (uint8_t*)ptr + format_sample_size_table[spec->format] * spec->channels; ++ } ++ ++ pa_memblock_release(c->memblock); ++} ++ ++pa_cvolume_ramp_int* pa_cvolume_ramp_convert(const pa_cvolume_ramp *src, pa_cvolume_ramp_int *dst, int sample_rate) { ++ ++ int i, j, channels, remaining_channels; ++ float temp; ++ ++ if (dst->channels < src->channels) { ++ channels = dst->channels; ++ remaining_channels = 0; ++ } ++ else { ++ channels = src->channels; ++ remaining_channels = dst->channels; ++ } ++ ++ for (i = 0; i < channels; i++) { ++ dst->ramps[i].type = src->ramps[i].type; ++ /* ms to samples */ ++ dst->ramps[i].length = src->ramps[i].length * sample_rate / 1000; ++ dst->ramps[i].left = dst->ramps[i].length; ++ dst->ramps[i].start = dst->ramps[i].end; ++ dst->ramps[i].target = src->ramps[i].target; ++ /* scale to pulse internal mapping so that when ramp is over there's no glitch in volume */ ++ temp = src->ramps[i].target / (float)0x10000U; ++ dst->ramps[i].end = temp * temp * temp; ++ } ++ ++ j = i; ++ ++ for (i--; j < remaining_channels; j++) { ++ dst->ramps[j].type = dst->ramps[i].type; ++ dst->ramps[j].length = dst->ramps[i].length; ++ dst->ramps[j].left = dst->ramps[i].left; ++ dst->ramps[j].start = dst->ramps[i].start; ++ dst->ramps[j].target = dst->ramps[i].target; ++ dst->ramps[j].end = dst->ramps[i].end; ++ } ++ ++ return dst; ++} ++ ++bool pa_cvolume_ramp_active(pa_cvolume_ramp_int *ramp) { ++ int i; ++ ++ for (i = 0; i < ramp->channels; i++) { ++ if (ramp->ramps[i].left > 0) ++ return true; ++ } ++ ++ return false; ++} ++ ++bool pa_cvolume_ramp_target_active(pa_cvolume_ramp_int *ramp) { ++ int i; ++ ++ for (i = 0; i < ramp->channels; i++) { ++ if (ramp->ramps[i].target != PA_VOLUME_NORM) ++ return true; ++ } ++ ++ return false; ++} ++ ++pa_cvolume * pa_cvolume_ramp_get_targets(pa_cvolume_ramp_int *ramp, pa_cvolume *volume) { ++ int i = 0; ++ ++ volume->channels = ramp->channels; ++ ++ for (i = 0; i < ramp->channels; i++) ++ volume->values[i] = ramp->ramps[i].target; ++ ++ return volume; ++} ++ ++pa_cvolume_ramp_int* pa_cvolume_ramp_start_from(pa_cvolume_ramp_int *src, pa_cvolume_ramp_int *dst) { ++ int i; ++ ++ for (i = 0; i < src->channels; i++) { ++ /* if new vols are invalid, copy old ramp i.e. no effect */ ++ if (dst->ramps[i].target == PA_VOLUME_INVALID) ++ dst->ramps[i] = src->ramps[i]; ++ /* if there's some old ramp still left */ ++ else if (src->ramps[i].left > 0) ++ dst->ramps[i].start = src->ramps[i].curr; ++ } ++ ++ return dst; ++} ++ ++pa_cvolume_ramp_int* pa_cvolume_ramp_int_init(pa_cvolume_ramp_int *src, pa_volume_t vol, int channels) { ++ int i; ++ float temp; ++ ++ src->channels = channels; ++ ++ for (i = 0; i < channels; i++) { ++ src->ramps[i].type = PA_VOLUME_RAMP_TYPE_LINEAR; ++ src->ramps[i].length = 0; ++ src->ramps[i].left = 0; ++ if (vol == PA_VOLUME_NORM) { ++ src->ramps[i].start = 1.0; ++ src->ramps[i].end = 1.0; ++ src->ramps[i].curr = 1.0; ++ } ++ else if (vol == PA_VOLUME_MUTED) { ++ src->ramps[i].start = 0.0; ++ src->ramps[i].end = 0.0; ++ src->ramps[i].curr = 0.0; ++ } ++ else { ++ temp = vol / (float)0x10000U; ++ src->ramps[i].start = temp * temp * temp; ++ src->ramps[i].end = src->ramps[i].start; ++ src->ramps[i].curr = src->ramps[i].start; ++ } ++ src->ramps[i].target = vol; ++ } ++ ++ return src; ++} +diff --git a/src/pulsecore/mix.h b/src/pulsecore/mix.h +index a4cde01..b1ec74f 100644 +--- a/src/pulsecore/mix.h ++++ b/src/pulsecore/mix.h +@@ -61,4 +61,31 @@ void pa_volume_memchunk( + const pa_sample_spec *spec, + const pa_cvolume *volume); + ++typedef struct pa_volume_ramp_int_t { ++ pa_volume_ramp_type_t type; ++ long length; ++ long left; ++ float start; ++ float end; ++ float curr; ++ pa_volume_t target; ++} pa_volume_ramp_int_t; ++ ++typedef struct pa_cvolume_ramp_int { ++ uint8_t channels; ++ pa_volume_ramp_int_t ramps[PA_CHANNELS_MAX]; ++} pa_cvolume_ramp_int; ++ ++pa_cvolume_ramp_int* pa_cvolume_ramp_convert(const pa_cvolume_ramp *src, pa_cvolume_ramp_int *dst, int sample_rate); ++bool pa_cvolume_ramp_active(pa_cvolume_ramp_int *ramp); ++bool pa_cvolume_ramp_target_active(pa_cvolume_ramp_int *ramp); ++pa_cvolume_ramp_int* pa_cvolume_ramp_start_from(pa_cvolume_ramp_int *src, pa_cvolume_ramp_int *dst); ++pa_cvolume_ramp_int* pa_cvolume_ramp_int_init(pa_cvolume_ramp_int *src, pa_volume_t vol, int channels); ++pa_cvolume * pa_cvolume_ramp_get_targets(pa_cvolume_ramp_int *ramp, pa_cvolume *volume); ++ ++void pa_volume_ramp_memchunk( ++ pa_memchunk *c, ++ const pa_sample_spec *spec, ++ pa_cvolume_ramp_int *ramp); ++ + #endif diff --git a/meta-tizen/recipes-multimedia/pulseaudio/pulseaudio_5.0/0002-volume-ramp-add-volume-ramping-to-sink.patch b/meta-tizen/recipes-multimedia/pulseaudio/pulseaudio_5.0/0002-volume-ramp-add-volume-ramping-to-sink.patch new file mode 100644 index 0000000..4ea7c20 --- /dev/null +++ b/meta-tizen/recipes-multimedia/pulseaudio/pulseaudio_5.0/0002-volume-ramp-add-volume-ramping-to-sink.patch @@ -0,0 +1,243 @@ +From: Jaska Uimonen +Date: Wed, 8 Aug 2012 11:14:39 +0300 +Subject: volume ramp: add volume ramping to sink + +Change-Id: I72bd64b4e4ad161ae0526a7e40d4bf9e843ae98c +Signed-off-by: Jaska Uimonen +--- + src/pulsecore/sink.c | 92 +++++++++++++++++++++++++++++++++++++++++++++++++--- + src/pulsecore/sink.h | 9 +++++ + 2 files changed, 96 insertions(+), 5 deletions(-) + +diff --git a/src/pulsecore/sink.c b/src/pulsecore/sink.c +index f4647b8..61656ab 100644 +--- a/src/pulsecore/sink.c ++++ b/src/pulsecore/sink.c +@@ -333,6 +333,8 @@ pa_sink* pa_sink_new( + &s->sample_spec, + 0); + ++ pa_cvolume_ramp_int_init(&s->ramp, PA_VOLUME_NORM, data->sample_spec.channels); ++ + s->thread_info.rtpoll = NULL; + s->thread_info.inputs = pa_hashmap_new_full(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func, NULL, + (pa_free_cb_t) pa_sink_input_unref); +@@ -356,6 +358,8 @@ pa_sink* pa_sink_new( + s->thread_info.volume_change_extra_delay = core->deferred_volume_extra_delay_usec; + s->thread_info.latency_offset = s->latency_offset; + ++ s->thread_info.ramp = s->ramp; ++ + /* FIXME: This should probably be moved to pa_sink_put() */ + pa_assert_se(pa_idxset_put(core->sinks, s, &s->index) >= 0); + +@@ -1189,6 +1193,7 @@ void pa_sink_render(pa_sink*s, size_t length, pa_memchunk *result) { + + } else if (n == 1) { + pa_cvolume volume; ++ pa_cvolume target; + + *result = info[0].chunk; + pa_memblock_ref(result->memblock); +@@ -1205,12 +1210,25 @@ void pa_sink_render(pa_sink*s, size_t length, pa_memchunk *result) { + result, + &s->sample_spec, + result->length); +- } else if (!pa_cvolume_is_norm(&volume)) { ++ } else if (!pa_cvolume_is_norm(&volume) || pa_cvolume_ramp_target_active(&s->thread_info.ramp) || pa_cvolume_ramp_active(&s->thread_info.ramp)) { + pa_memchunk_make_writable(result, 0); +- pa_volume_memchunk(result, &s->sample_spec, &volume); ++ if (pa_cvolume_ramp_active(&s->thread_info.ramp)) { ++ if (!pa_cvolume_is_norm(&volume)) ++ pa_volume_memchunk(result, &s->sample_spec, &volume); ++ pa_volume_ramp_memchunk(result, &s->sample_spec, &(s->thread_info.ramp)); ++ } ++ else { ++ if (pa_cvolume_ramp_target_active(&s->thread_info.ramp)) { ++ pa_cvolume_ramp_get_targets(&s->thread_info.ramp, &target); ++ pa_sw_cvolume_multiply(&volume, &volume, &target); ++ } ++ pa_volume_memchunk(result, &s->sample_spec, &volume); ++ } + } + } else { + void *ptr; ++ pa_cvolume target_vol; ++ + result->memblock = pa_memblock_new(s->core->mempool, length); + + ptr = pa_memblock_acquire(result->memblock); +@@ -1219,6 +1237,16 @@ void pa_sink_render(pa_sink*s, size_t length, pa_memchunk *result) { + &s->sample_spec, + &s->thread_info.soft_volume, + s->thread_info.soft_muted); ++ ++ if (pa_cvolume_ramp_target_active(&s->thread_info.ramp) || pa_cvolume_ramp_active(&s->thread_info.ramp)) { ++ if (pa_cvolume_ramp_active(&s->thread_info.ramp)) ++ pa_volume_ramp_memchunk(result, &s->sample_spec, &(s->thread_info.ramp)); ++ else { ++ pa_cvolume_ramp_get_targets(&s->thread_info.ramp, &target_vol); ++ pa_volume_memchunk(result, &s->sample_spec, &target_vol); ++ } ++ } ++ + pa_memblock_release(result->memblock); + + result->index = 0; +@@ -1279,6 +1307,7 @@ void pa_sink_render_into(pa_sink*s, pa_memchunk *target) { + pa_silence_memchunk(target, &s->sample_spec); + else { + pa_memchunk vchunk; ++ pa_cvolume target_vol; + + vchunk = info[0].chunk; + pa_memblock_ref(vchunk.memblock); +@@ -1286,9 +1315,20 @@ void pa_sink_render_into(pa_sink*s, pa_memchunk *target) { + if (vchunk.length > length) + vchunk.length = length; + +- if (!pa_cvolume_is_norm(&volume)) { ++ if (!pa_cvolume_is_norm(&volume) || pa_cvolume_ramp_target_active(&s->thread_info.ramp) || pa_cvolume_ramp_active(&s->thread_info.ramp)) { + pa_memchunk_make_writable(&vchunk, 0); +- pa_volume_memchunk(&vchunk, &s->sample_spec, &volume); ++ if (pa_cvolume_ramp_active(&s->thread_info.ramp)) { ++ if (!pa_cvolume_is_norm(&volume)) ++ pa_volume_memchunk(&vchunk, &s->sample_spec, &volume); ++ pa_volume_ramp_memchunk(&vchunk, &s->sample_spec, &(s->thread_info.ramp)); ++ } ++ else { ++ if (pa_cvolume_ramp_target_active(&s->thread_info.ramp)) { ++ pa_cvolume_ramp_get_targets(&s->thread_info.ramp, &target_vol); ++ pa_sw_cvolume_multiply(&volume, &volume, &target_vol); ++ } ++ pa_volume_memchunk(&vchunk, &s->sample_spec, &volume); ++ } + } + + pa_memchunk_memcpy(target, &vchunk); +@@ -1297,6 +1337,7 @@ void pa_sink_render_into(pa_sink*s, pa_memchunk *target) { + + } else { + void *ptr; ++ pa_cvolume target_vol; + + ptr = pa_memblock_acquire(target->memblock); + +@@ -1306,6 +1347,15 @@ void pa_sink_render_into(pa_sink*s, pa_memchunk *target) { + &s->thread_info.soft_volume, + s->thread_info.soft_muted); + ++ if (pa_cvolume_ramp_target_active(&s->thread_info.ramp) || pa_cvolume_ramp_active(&s->thread_info.ramp)) { ++ if (pa_cvolume_ramp_active(&s->thread_info.ramp)) ++ pa_volume_ramp_memchunk(target, &s->sample_spec, &(s->thread_info.ramp)); ++ else { ++ pa_cvolume_ramp_get_targets(&s->thread_info.ramp, &target_vol); ++ pa_volume_memchunk(target, &s->sample_spec, &target_vol); ++ } ++ } ++ + pa_memblock_release(target->memblock); + } + +@@ -2085,6 +2135,32 @@ void pa_sink_set_volume( + pa_assert_se(pa_asyncmsgq_send(root_sink->asyncmsgq, PA_MSGOBJECT(root_sink), PA_SINK_MESSAGE_SET_SHARED_VOLUME, NULL, 0, NULL) == 0); + } + ++/* Called from main thread */ ++void pa_sink_set_volume_ramp( ++ pa_sink *s, ++ const pa_cvolume_ramp *ramp, ++ bool send_msg, ++ bool save) { ++ ++ pa_sink_assert_ref(s); ++ pa_assert_ctl_context(); ++ pa_assert(PA_SINK_IS_LINKED(s->state)); ++ pa_assert(ramp); ++ ++ /* make sure we don't change the volume when a PASSTHROUGH input is connected ... ++ * ... *except* if we're being invoked to reset the volume to ensure 0 dB gain */ ++ if (pa_sink_is_passthrough(s)) { ++ pa_log_warn("Cannot do volume ramp, Sink is connected to PASSTHROUGH input"); ++ return; ++ } ++ ++ pa_cvolume_ramp_convert(ramp, &s->ramp, s->sample_spec.rate); ++ ++ /* This tells the sink that volume ramp changed */ ++ if (send_msg) ++ pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_SET_VOLUME_RAMP, NULL, 0, NULL) == 0); ++} ++ + /* Called from the io thread if sync volume is used, otherwise from the main thread. + * Only to be called by sink implementor */ + void pa_sink_set_soft_volume(pa_sink *s, const pa_cvolume *volume) { +@@ -2737,13 +2813,19 @@ int pa_sink_process_msg(pa_msgobject *o, int code, void *userdata, int64_t offse + s->thread_info.soft_volume = s->soft_volume; + pa_sink_request_rewind(s, (size_t) -1); + } +- + /* Fall through ... */ + + case PA_SINK_MESSAGE_SYNC_VOLUMES: + sync_input_volumes_within_thread(s); + return 0; + ++ case PA_SINK_MESSAGE_SET_VOLUME_RAMP: ++ /* if we have ongoing ramp where we take current start values */ ++ pa_cvolume_ramp_start_from(&s->thread_info.ramp, &s->ramp); ++ s->thread_info.ramp = s->ramp; ++ pa_sink_request_rewind(s, (size_t) -1); ++ return 0; ++ + case PA_SINK_MESSAGE_GET_VOLUME: + + if ((s->flags & PA_SINK_DEFERRED_VOLUME) && s->get_volume) { +diff --git a/src/pulsecore/sink.h b/src/pulsecore/sink.h +index c7cb5f8..576f34b 100644 +--- a/src/pulsecore/sink.h ++++ b/src/pulsecore/sink.h +@@ -37,6 +37,7 @@ typedef struct pa_sink_volume_change pa_sink_volume_change; + #include + #include + #include ++#include + #include + #include + #include +@@ -105,6 +106,9 @@ struct pa_sink { + pa_cvolume saved_volume; + bool saved_save_volume:1; + ++ /* for volume ramps */ ++ pa_cvolume_ramp_int ramp; ++ + pa_asyncmsgq *asyncmsgq; + + pa_memchunk silence; +@@ -288,6 +292,8 @@ struct pa_sink { + uint32_t volume_change_safety_margin; + /* Usec delay added to all volume change events, may be negative. */ + int32_t volume_change_extra_delay; ++ ++ pa_cvolume_ramp_int ramp; + } thread_info; + + void *userdata; +@@ -322,6 +328,7 @@ typedef enum pa_sink_message { + PA_SINK_MESSAGE_SET_PORT, + PA_SINK_MESSAGE_UPDATE_VOLUME_AND_MUTE, + PA_SINK_MESSAGE_SET_LATENCY_OFFSET, ++ PA_SINK_MESSAGE_SET_VOLUME_RAMP, + PA_SINK_MESSAGE_MAX + } pa_sink_message_t; + +@@ -443,6 +450,8 @@ bool pa_sink_get_mute(pa_sink *sink, bool force_refresh); + + bool pa_sink_update_proplist(pa_sink *s, pa_update_mode_t mode, pa_proplist *p); + ++void pa_sink_set_volume_ramp(pa_sink *s, const pa_cvolume_ramp *ramp, bool send_msg, bool save); ++ + int pa_sink_set_port(pa_sink *s, const char *name, bool save); + void pa_sink_set_mixer_dirty(pa_sink *s, bool is_dirty); + diff --git a/meta-tizen/recipes-multimedia/pulseaudio/pulseaudio_5.0/0003-volume-ramp-adding-volume-ramping-to-sink-input.patch b/meta-tizen/recipes-multimedia/pulseaudio/pulseaudio_5.0/0003-volume-ramp-adding-volume-ramping-to-sink-input.patch new file mode 100644 index 0000000..9557dc8 --- /dev/null +++ b/meta-tizen/recipes-multimedia/pulseaudio/pulseaudio_5.0/0003-volume-ramp-adding-volume-ramping-to-sink-input.patch @@ -0,0 +1,181 @@ +From: Jaska Uimonen +Date: Wed, 8 Aug 2012 11:14:38 +0300 +Subject: volume ramp: adding volume ramping to sink-input + +Change-Id: I9376f531f3585c9ba5fb6d1b384589a8d123b292 +Signed-off-by: Jaska Uimonen +--- + src/pulsecore/sink-input.c | 59 ++++++++++++++++++++++++++++++++++++++++++++++ + src/pulsecore/sink-input.h | 12 +++++++++- + 2 files changed, 70 insertions(+), 1 deletion(-) + +diff --git a/src/pulsecore/sink-input.c b/src/pulsecore/sink-input.c +index f85b2c7..05ab25d 100644 +--- a/src/pulsecore/sink-input.c ++++ b/src/pulsecore/sink-input.c +@@ -527,6 +527,11 @@ int pa_sink_input_new( + reset_callbacks(i); + i->userdata = NULL; + ++ if (data->flags & PA_SINK_INPUT_START_RAMP_MUTED) ++ pa_cvolume_ramp_int_init(&i->ramp, PA_VOLUME_MUTED, data->sink->sample_spec.channels); ++ else ++ pa_cvolume_ramp_int_init(&i->ramp, PA_VOLUME_NORM, data->sink->sample_spec.channels); ++ + i->thread_info.state = i->state; + i->thread_info.attached = false; + pa_atomic_store(&i->thread_info.drained, 1); +@@ -543,6 +548,8 @@ int pa_sink_input_new( + i->thread_info.playing_for = 0; + i->thread_info.direct_outputs = pa_hashmap_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func); + ++ i->thread_info.ramp = i->ramp; ++ + pa_assert_se(pa_idxset_put(core->sink_inputs, i, &i->index) == 0); + pa_assert_se(pa_idxset_put(i->sink->inputs, pa_sink_input_ref(i), NULL) == 0); + +@@ -924,6 +931,8 @@ void pa_sink_input_peek(pa_sink_input *i, size_t slength /* in sink bytes */, pa + while (tchunk.length > 0) { + pa_memchunk wchunk; + bool nvfs = need_volume_factor_sink; ++ pa_cvolume target; ++ bool tmp; + + wchunk = tchunk; + pa_memblock_ref(wchunk.memblock); +@@ -960,6 +969,16 @@ void pa_sink_input_peek(pa_sink_input *i, size_t slength /* in sink bytes */, pa + pa_volume_memchunk(&wchunk, &i->sink->sample_spec, &i->volume_factor_sink); + } + ++ /* check for possible volume ramp */ ++ if (pa_cvolume_ramp_active(&i->thread_info.ramp)) { ++ pa_memchunk_make_writable(&wchunk, 0); ++ pa_volume_ramp_memchunk(&wchunk, &i->sink->sample_spec, &(i->thread_info.ramp)); ++ } else if ((tmp = pa_cvolume_ramp_target_active(&(i->thread_info.ramp)))) { ++ pa_memchunk_make_writable(&wchunk, 0); ++ pa_cvolume_ramp_get_targets(&i->thread_info.ramp, &target); ++ pa_volume_memchunk(&wchunk, &i->sink->sample_spec, &target); ++ } ++ + pa_memblockq_push_align(i->thread_info.render_memblockq, &wchunk); + } else { + pa_memchunk rchunk; +@@ -976,6 +995,16 @@ void pa_sink_input_peek(pa_sink_input *i, size_t slength /* in sink bytes */, pa + pa_volume_memchunk(&rchunk, &i->sink->sample_spec, &i->volume_factor_sink); + } + ++ /* check for possible volume ramp */ ++ if (pa_cvolume_ramp_active(&(i->thread_info.ramp))) { ++ pa_memchunk_make_writable(&rchunk, 0); ++ pa_volume_ramp_memchunk(&rchunk, &i->sink->sample_spec, &(i->thread_info.ramp)); ++ } else if (pa_cvolume_ramp_target_active(&(i->thread_info.ramp))) { ++ pa_memchunk_make_writable(&rchunk, 0); ++ pa_cvolume_ramp_get_targets(&i->thread_info.ramp, &target); ++ pa_volume_memchunk(&rchunk, &i->sink->sample_spec, &target); ++ } ++ + pa_memblockq_push_align(i->thread_info.render_memblockq, &rchunk); + pa_memblock_unref(rchunk.memblock); + } +@@ -1351,6 +1380,29 @@ int pa_sink_input_remove_volume_factor(pa_sink_input *i, const char *key) { + return 0; + } + ++/* Called from main thread */ ++void pa_sink_input_set_volume_ramp( ++ pa_sink_input *i, ++ const pa_cvolume_ramp *ramp, ++ bool send_msg, ++ bool save) { ++ ++ pa_sink_input_assert_ref(i); ++ pa_assert_ctl_context(); ++ pa_assert(PA_SINK_INPUT_IS_LINKED(i->state)); ++ pa_assert(ramp); ++ ++ pa_cvolume_ramp_convert(ramp, &i->ramp, i->sample_spec.rate); ++ ++ pa_log_debug("setting volume ramp with target vol:%d and length:%ld", ++ i->ramp.ramps[0].target, ++ i->ramp.ramps[0].length); ++ ++ /* This tells the sink that volume ramp changed */ ++ if (send_msg) ++ pa_assert_se(pa_asyncmsgq_send(i->sink->asyncmsgq, PA_MSGOBJECT(i), PA_SINK_INPUT_MESSAGE_SET_VOLUME_RAMP, NULL, 0, NULL) == 0); ++} ++ + /* Called from main context */ + static void set_real_ratio(pa_sink_input *i, const pa_cvolume *v) { + pa_sink_input_assert_ref(i); +@@ -1968,6 +2020,13 @@ int pa_sink_input_process_msg(pa_msgobject *o, int code, void *userdata, int64_t + } + return 0; + ++ case PA_SINK_INPUT_MESSAGE_SET_VOLUME_RAMP: ++ /* we have ongoing ramp where we take current start values */ ++ pa_cvolume_ramp_start_from(&i->thread_info.ramp, &i->ramp); ++ i->thread_info.ramp = i->ramp; ++ pa_sink_input_request_rewind(i, 0, TRUE, FALSE, FALSE); ++ return 0; ++ + case PA_SINK_INPUT_MESSAGE_SET_SOFT_MUTE: + if (i->thread_info.muted != i->muted) { + i->thread_info.muted = i->muted; +diff --git a/src/pulsecore/sink-input.h b/src/pulsecore/sink-input.h +index da33717..3f74054 100644 +--- a/src/pulsecore/sink-input.h ++++ b/src/pulsecore/sink-input.h +@@ -35,6 +35,7 @@ typedef struct pa_sink_input pa_sink_input; + #include + #include + #include ++#include + + typedef enum pa_sink_input_state { + PA_SINK_INPUT_INIT, /*< The stream is not active yet, because pa_sink_input_put() has not been called yet */ +@@ -61,7 +62,8 @@ typedef enum pa_sink_input_flags { + PA_SINK_INPUT_DONT_INHIBIT_AUTO_SUSPEND = 256, + PA_SINK_INPUT_NO_CREATE_ON_SUSPEND = 512, + PA_SINK_INPUT_KILL_ON_SUSPEND = 1024, +- PA_SINK_INPUT_PASSTHROUGH = 2048 ++ PA_SINK_INPUT_PASSTHROUGH = 2048, ++ PA_SINK_INPUT_START_RAMP_MUTED = 4096, + } pa_sink_input_flags_t; + + struct pa_sink_input { +@@ -124,6 +126,9 @@ struct pa_sink_input { + * this.*/ + bool save_sink:1, save_volume:1, save_muted:1; + ++ /* for volume ramps */ ++ pa_cvolume_ramp_int ramp; ++ + pa_resample_method_t requested_resample_method, actual_resample_method; + + /* Returns the chunk of audio data and drops it from the +@@ -252,6 +257,8 @@ struct pa_sink_input { + pa_usec_t requested_sink_latency; + + pa_hashmap *direct_outputs; ++ ++ pa_cvolume_ramp_int ramp; + } thread_info; + + void *userdata; +@@ -268,6 +275,7 @@ enum { + PA_SINK_INPUT_MESSAGE_SET_STATE, + PA_SINK_INPUT_MESSAGE_SET_REQUESTED_LATENCY, + PA_SINK_INPUT_MESSAGE_GET_REQUESTED_LATENCY, ++ PA_SINK_INPUT_MESSAGE_SET_VOLUME_RAMP, + PA_SINK_INPUT_MESSAGE_MAX + }; + +@@ -377,6 +385,8 @@ pa_cvolume *pa_sink_input_get_volume(pa_sink_input *i, pa_cvolume *volume, bool + void pa_sink_input_set_mute(pa_sink_input *i, bool mute, bool save); + bool pa_sink_input_get_mute(pa_sink_input *i); + ++void pa_sink_input_set_volume_ramp(pa_sink_input *i, const pa_cvolume_ramp *ramp, bool send_msg, bool save); ++ + void pa_sink_input_update_proplist(pa_sink_input *i, pa_update_mode_t mode, pa_proplist *p); + + pa_resample_method_t pa_sink_input_get_resample_method(pa_sink_input *i); diff --git a/meta-tizen/recipes-multimedia/pulseaudio/pulseaudio_5.0/0004-build-sys-install-files-for-a-module-development.patch b/meta-tizen/recipes-multimedia/pulseaudio/pulseaudio_5.0/0004-build-sys-install-files-for-a-module-development.patch new file mode 100644 index 0000000..8b43385 --- /dev/null +++ b/meta-tizen/recipes-multimedia/pulseaudio/pulseaudio_5.0/0004-build-sys-install-files-for-a-module-development.patch @@ -0,0 +1,72 @@ +From: Jaska Uimonen +Date: Tue, 5 Jun 2012 11:36:13 +0300 +Subject: build-sys: install files for a module development. + +Change-Id: Ib68b292e1f6bc82bb5c148ef53acf51cc571406e +Signed-off-by: Jaska Uimonen +--- + Makefile.am | 11 ++++++++++- + configure.ac | 1 + + pulseaudio-module-devel.pc.in | 12 ++++++++++++ + 3 files changed, 23 insertions(+), 1 deletion(-) + create mode 100644 pulseaudio-module-devel.pc.in + +diff --git a/Makefile.am b/Makefile.am +index b0b2553..a6a0b40 100644 +--- a/Makefile.am ++++ b/Makefile.am +@@ -50,7 +50,13 @@ dist_vapi_DATA = \ + vala/libpulse-mainloop-glib.deps vala/libpulse-mainloop-glib.vapi + + pkgconfigdir = $(libdir)/pkgconfig +-pkgconfig_DATA = libpulse.pc libpulse-simple.pc ++pkgconfig_DATA = libpulse.pc libpulse-simple.pc pulseaudio-module-devel.pc ++ ++moduledev_DATA = pulsecore-config.h src/pulsecore/*.h ++moduledevdir = $(includedir)/pulsemodule/pulsecore ++ ++moduledevinternal_DATA = src/pulse/internal.h src/pulse/client-conf.h src/pulse/fork-detect.h ++moduledevinternaldir = $(includedir)/pulsemodule/pulse + + if HAVE_GLIB20 + pkgconfig_DATA += \ +@@ -89,6 +95,9 @@ dist-hook: + check-daemon: + $(MAKE) -C src check-daemon + ++pulsecore-config.h: config.h ++ cp $< $@ ++ + .PHONY: homepage distcleancheck doxygen + + # see git-version-gen +diff --git a/configure.ac b/configure.ac +index 4854711..4e9f97e 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -1384,6 +1384,7 @@ man/default.pa.5.xml + man/pulse-cli-syntax.5.xml + man/start-pulseaudio-kde.1.xml + man/start-pulseaudio-x11.1.xml ++pulseaudio-module-devel.pc + ]) + + AC_CONFIG_FILES([src/esdcompat:src/daemon/esdcompat.in], [chmod +x src/esdcompat]) +diff --git a/pulseaudio-module-devel.pc.in b/pulseaudio-module-devel.pc.in +new file mode 100644 +index 0000000..85aadbc +--- /dev/null ++++ b/pulseaudio-module-devel.pc.in +@@ -0,0 +1,12 @@ ++prefix=@prefix@ ++exec_prefix=@exec_prefix@ ++libdir=@libdir@ ++includedir=@includedir@ ++modlibexecdir=@modlibexecdir@ ++ ++Name: pulseaudio-module-devel ++Description: PulseAudio Module Development Interface ++Version: @PACKAGE_VERSION@ ++Libs: -L${libdir} -L${libdir}/pulseaudio -L${modlibexecdir} -lpulsecommon-@PA_MAJORMINOR@ -lpulsecore-@PA_MAJORMINOR@ -lprotocol-native ++Libs.private: ++Cflags: -I${includedir}/pulsemodule -D_REENTRANT diff --git a/meta-tizen/recipes-multimedia/pulseaudio/pulseaudio_5.0/0005-jack-detection-fix-for-wired-headset.patch b/meta-tizen/recipes-multimedia/pulseaudio/pulseaudio_5.0/0005-jack-detection-fix-for-wired-headset.patch new file mode 100644 index 0000000..002e4ec --- /dev/null +++ b/meta-tizen/recipes-multimedia/pulseaudio/pulseaudio_5.0/0005-jack-detection-fix-for-wired-headset.patch @@ -0,0 +1,23 @@ +From: Jaska Uimonen +Date: Sun, 10 Jun 2012 15:13:11 +0300 +Subject: jack detection fix for wired headset + +Change-Id: I53d465bf56adc2e3e5551b43d59ff99b63bc76cc +Signed-off-by: Jaska Uimonen +--- + src/modules/alsa/mixer/paths/analog-output-headphones.conf | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/src/modules/alsa/mixer/paths/analog-output-headphones.conf b/src/modules/alsa/mixer/paths/analog-output-headphones.conf +index 89d794f..4771bc6 100644 +--- a/src/modules/alsa/mixer/paths/analog-output-headphones.conf ++++ b/src/modules/alsa/mixer/paths/analog-output-headphones.conf +@@ -95,7 +95,7 @@ volume = off + + ; On some machines Front is actually a part of the Headphone path + [Element Front] +-switch = mute ++switch = off + volume = zero + + [Element Rear] diff --git a/meta-tizen/recipes-multimedia/pulseaudio/pulseaudio_5.0/0006-make-pa_thread_mq_done-safe-for-subsequent-calls.patch b/meta-tizen/recipes-multimedia/pulseaudio/pulseaudio_5.0/0006-make-pa_thread_mq_done-safe-for-subsequent-calls.patch new file mode 100644 index 0000000..bfbf3d9 --- /dev/null +++ b/meta-tizen/recipes-multimedia/pulseaudio/pulseaudio_5.0/0006-make-pa_thread_mq_done-safe-for-subsequent-calls.patch @@ -0,0 +1,29 @@ +From: Janos Kovacs +Date: Thu, 16 Aug 2012 03:47:48 +0300 +Subject: make pa_thread_mq_done() safe for subsequent calls + +Change-Id: I71ab9efa72c38a2200a56b7a4d30116767bc104f +Signed-off-by: Jaska Uimonen +--- + src/pulsecore/thread-mq.c | 8 ++++++++ + 1 file changed, 8 insertions(+) + +diff --git a/src/pulsecore/thread-mq.c b/src/pulsecore/thread-mq.c +index d34b22b..f3c5b6c 100644 +--- a/src/pulsecore/thread-mq.c ++++ b/src/pulsecore/thread-mq.c +@@ -144,6 +144,14 @@ void pa_thread_mq_init(pa_thread_mq *q, pa_mainloop_api *mainloop, pa_rtpoll *rt + void pa_thread_mq_done(pa_thread_mq *q) { + pa_assert(q); + ++ if (!q->main_mainloop && !q->inq && !q->outq && ++ !q->read_main_event && !q->write_main_event) ++ return; ++ ++ pa_assert(q->main_mainloop); ++ pa_assert(q->inq && q->outq); ++ pa_assert(q->read_main_event && q->write_main_event); ++ + /* Since we are called from main context we can be sure that the + * inq is empty. However, the outq might still contain messages + * for the main loop, which we need to dispatch (e.g. release diff --git a/meta-tizen/recipes-multimedia/pulseaudio/pulseaudio_5.0/0007-node-manager-adding-external-node-manager-API.patch b/meta-tizen/recipes-multimedia/pulseaudio/pulseaudio_5.0/0007-node-manager-adding-external-node-manager-API.patch new file mode 100644 index 0000000..5deba2b --- /dev/null +++ b/meta-tizen/recipes-multimedia/pulseaudio/pulseaudio_5.0/0007-node-manager-adding-external-node-manager-API.patch @@ -0,0 +1,568 @@ +From: Ismo Puustinen +Date: Wed, 28 May 2014 11:11:56 +0300 +Subject: node manager: adding external node manager API + +Change-Id: I4f220f05b9de513a7003b3cea761c3540fa74685 +Signed-off-by: Jaska Uimonen +--- + src/Makefile.am | 4 + + src/map-file | 11 ++ + src/pulse/context.c | 5 + + src/pulse/ext-node-manager.c | 348 +++++++++++++++++++++++++++++++++++++++++++ + src/pulse/ext-node-manager.h | 93 ++++++++++++ + src/pulse/internal.h | 6 + + 6 files changed, 467 insertions(+) + create mode 100644 src/pulse/ext-node-manager.c + create mode 100644 src/pulse/ext-node-manager.h + +diff --git a/src/Makefile.am b/src/Makefile.am +index 857fda3..e1808e6 100644 +--- a/src/Makefile.am ++++ b/src/Makefile.am +@@ -753,6 +753,8 @@ pulseinclude_HEADERS = \ + pulse/ext-device-manager.h \ + pulse/ext-device-restore.h \ + pulse/ext-stream-restore.h \ ++ pulse/ext-echo-cancel.h \ ++ pulse/ext-node-manager.h \ + pulse/format.h \ + pulse/gccmacro.h \ + pulse/introspect.h \ +@@ -798,6 +800,8 @@ libpulse_la_SOURCES = \ + pulse/ext-device-manager.c pulse/ext-device-manager.h \ + pulse/ext-device-restore.c pulse/ext-device-restore.h \ + pulse/ext-stream-restore.c pulse/ext-stream-restore.h \ ++ pulse/ext-echo-cancel.c pulse/ext-echo-cancel.h \ ++ pulse/ext-node-manager.c pulse/ext-node-manager.h \ + pulse/format.c pulse/format.h \ + pulse/gccmacro.h \ + pulse/internal.h \ +diff --git a/src/map-file b/src/map-file +index 9903942..7dbcd00 100644 +--- a/src/map-file ++++ b/src/map-file +@@ -171,6 +171,17 @@ pa_ext_stream_restore_set_subscribe_cb; + pa_ext_stream_restore_subscribe; + pa_ext_stream_restore_test; + pa_ext_stream_restore_write; ++pa_ext_policy_test; ++pa_ext_policy_set_mono; ++pa_ext_policy_set_balance; ++pa_ext_echo_cancel_set_volume; ++pa_ext_echo_cancel_set_device; ++pa_ext_node_manager_test; ++pa_ext_node_manager_read_nodes; ++pa_ext_node_manager_connect_nodes; ++pa_ext_node_manager_disconnect_nodes; ++pa_ext_node_manager_subscribe; ++pa_ext_node_manager_set_subscribe_cb; + pa_format_info_copy; + pa_format_info_free; + pa_format_info_from_string; +diff --git a/src/pulse/context.c b/src/pulse/context.c +index b78df27..b8688f2 100644 +--- a/src/pulse/context.c ++++ b/src/pulse/context.c +@@ -122,6 +122,9 @@ static void reset_callbacks(pa_context *c) { + + c->ext_stream_restore.callback = NULL; + c->ext_stream_restore.userdata = NULL; ++ ++ c->ext_node_manager.callback = NULL; ++ c->ext_node_manager.userdata = NULL; + } + + pa_context *pa_context_new_with_proplist(pa_mainloop_api *mainloop, const char *name, pa_proplist *p) { +@@ -1361,6 +1364,8 @@ void pa_command_extension(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_t + pa_ext_device_restore_command(c, tag, t); + else if (pa_streq(name, "module-stream-restore")) + pa_ext_stream_restore_command(c, tag, t); ++ else if (pa_streq(name, "module-node-manager")) ++ pa_ext_node_manager_command(c, tag, t); + else + pa_log(_("Received message for unknown extension '%s'"), name); + +diff --git a/src/pulse/ext-node-manager.c b/src/pulse/ext-node-manager.c +new file mode 100644 +index 0000000..5cb3feb +--- /dev/null ++++ b/src/pulse/ext-node-manager.c +@@ -0,0 +1,348 @@ ++/*** ++ This file is part of PulseAudio. ++ ++ Copyright 2012 Jaska Uimonen ++ Copyright 2012 Janos Kovacs ++ ++ PulseAudio is free software; you can redistribute it and/or modify ++ it under the terms of the GNU Lesser General Public License as published ++ by the Free Software Foundation; either version 2.1 of the License, ++ or (at your option) any later version. ++ ++ PulseAudio is distributed in the hope that it will be useful, but ++ WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ General Public License for more details. ++ ++ You should have received a copy of the GNU Lesser General Public License ++ along with PulseAudio; if not, write to the Free Software ++ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 ++ USA. ++***/ ++ ++#ifdef HAVE_CONFIG_H ++#include ++#endif ++ ++#include ++#include ++#include ++#include ++ ++#include ++#include ++ ++#include "internal.h" ++#include "ext-node-manager.h" ++ ++enum { ++ SUBCOMMAND_TEST, ++ SUBCOMMAND_READ, ++ SUBCOMMAND_CONNECT, ++ SUBCOMMAND_DISCONNECT, ++ SUBCOMMAND_SUBSCRIBE, ++ SUBCOMMAND_EVENT ++}; ++ ++static void ext_node_manager_test_cb(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) { ++ pa_operation *o = userdata; ++ uint32_t version = PA_INVALID_INDEX; ++ ++ pa_assert(pd); ++ pa_assert(o); ++ pa_assert(PA_REFCNT_VALUE(o) >= 1); ++ ++ if (!o->context) ++ goto finish; ++ ++ if (command != PA_COMMAND_REPLY) { ++ if (pa_context_handle_error(o->context, command, t, FALSE) < 0) ++ goto finish; ++ ++ } else if (pa_tagstruct_getu32(t, &version) < 0 || ++ !pa_tagstruct_eof(t)) { ++ ++ pa_context_fail(o->context, PA_ERR_PROTOCOL); ++ goto finish; ++ } ++ ++ if (o->callback) { ++ pa_ext_node_manager_test_cb_t cb = (pa_ext_node_manager_test_cb_t) o->callback; ++ cb(o->context, version, o->userdata); ++ } ++ ++finish: ++ pa_operation_done(o); ++ pa_operation_unref(o); ++} ++ ++pa_operation *pa_ext_node_manager_test( ++ pa_context *c, ++ pa_ext_node_manager_test_cb_t cb, ++ void *userdata) { ++ ++ uint32_t tag; ++ pa_operation *o; ++ pa_tagstruct *t; ++ ++ pa_assert(c); ++ pa_assert(PA_REFCNT_VALUE(c) >= 1); ++ ++ PA_CHECK_VALIDITY_RETURN_NULL(c, !pa_detect_fork(), PA_ERR_FORKED); ++ PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE); ++ PA_CHECK_VALIDITY_RETURN_NULL(c, c->version >= 14, PA_ERR_NOTSUPPORTED); ++ ++ o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata); ++ ++ t = pa_tagstruct_command(c, PA_COMMAND_EXTENSION, &tag); ++ pa_tagstruct_putu32(t, PA_INVALID_INDEX); ++ pa_tagstruct_puts(t, "module-murphy-ivi"); ++ pa_tagstruct_putu32(t, SUBCOMMAND_TEST); ++ pa_pstream_send_tagstruct(c->pstream, t); ++ pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, ext_node_manager_test_cb, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref); ++ ++ return o; ++} ++ ++static void ext_node_manager_read_cb(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) { ++ pa_operation *o = userdata; ++ pa_ext_node_manager_info i; ++ ++ pa_assert(pd); ++ pa_assert(o); ++ pa_assert(PA_REFCNT_VALUE(o) >= 1); ++ ++ if (!o->context) ++ goto finish; ++ ++ if (command != PA_COMMAND_REPLY) { ++ if (pa_context_handle_error(o->context, command, t, FALSE) < 0) ++ goto finish; ++ } else { ++ ++ while (!pa_tagstruct_eof(t)) { ++ ++ memset(&i, 0, sizeof(i)); ++ ++ i.props = pa_proplist_new(); ++ ++ if (pa_tagstruct_gets(t, &i.name) < 0 || ++ pa_tagstruct_get_proplist(t, i.props) < 0) { ++ ++ pa_context_fail(o->context, PA_ERR_PROTOCOL); ++ goto finish; ++ } ++ ++ if (o->callback) { ++ pa_ext_node_manager_read_cb_t cb = (pa_ext_node_manager_read_cb_t) o->callback; ++ cb(o->context, &i, 0, o->userdata); ++ } ++ ++ pa_proplist_free(i.props); ++ } ++ ++ /* let's send end marker */ ++ if (o->callback) { ++ pa_ext_node_manager_read_cb_t cb = (pa_ext_node_manager_read_cb_t) o->callback; ++ cb(o->context, &i, 1, o->userdata); ++ } ++ } ++ ++finish: ++ pa_operation_done(o); ++ pa_operation_unref(o); ++} ++ ++pa_operation *pa_ext_node_manager_read_nodes( ++ pa_context *c, ++ pa_ext_node_manager_read_cb_t cb, ++ void *userdata) { ++ ++ uint32_t tag; ++ pa_operation *o; ++ pa_tagstruct *t; ++ ++ pa_assert(c); ++ pa_assert(PA_REFCNT_VALUE(c) >= 1); ++ ++ PA_CHECK_VALIDITY_RETURN_NULL(c, !pa_detect_fork(), PA_ERR_FORKED); ++ PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE); ++ PA_CHECK_VALIDITY_RETURN_NULL(c, c->version >= 14, PA_ERR_NOTSUPPORTED); ++ ++ o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata); ++ ++ t = pa_tagstruct_command(c, PA_COMMAND_EXTENSION, &tag); ++ pa_tagstruct_putu32(t, PA_INVALID_INDEX); ++ pa_tagstruct_puts(t, "module-murphy-ivi"); ++ pa_tagstruct_putu32(t, SUBCOMMAND_READ); ++ pa_pstream_send_tagstruct(c->pstream, t); ++ pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, ext_node_manager_read_cb, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref); ++ ++ return o; ++} ++ ++static void ext_node_manager_connect_cb(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) { ++ pa_operation *o = userdata; ++ uint32_t connection = PA_INVALID_INDEX; ++ ++ pa_assert(pd); ++ pa_assert(o); ++ pa_assert(PA_REFCNT_VALUE(o) >= 1); ++ ++ if (!o->context) ++ goto finish; ++ ++ if (command != PA_COMMAND_REPLY) { ++ if (pa_context_handle_error(o->context, command, t, FALSE) < 0) ++ goto finish; ++ ++ } else if (pa_tagstruct_getu32(t, &connection) < 0 || ++ !pa_tagstruct_eof(t)) { ++ ++ pa_context_fail(o->context, PA_ERR_PROTOCOL); ++ goto finish; ++ } ++ ++ if (o->callback) { ++ pa_ext_node_manager_connect_cb_t cb = (pa_ext_node_manager_connect_cb_t) o->callback; ++ cb(o->context, connection, o->userdata); ++ } ++ ++finish: ++ pa_operation_done(o); ++ pa_operation_unref(o); ++} ++ ++pa_operation *pa_ext_node_manager_connect_nodes( ++ pa_context *c, ++ uint32_t source_node_id, ++ uint32_t sink_node_id, ++ pa_ext_node_manager_connect_cb_t cb, ++ void *userdata) { ++ ++ uint32_t tag; ++ pa_operation *o = NULL; ++ pa_tagstruct *t = NULL; ++ ++ pa_assert(c); ++ pa_assert(PA_REFCNT_VALUE(c) >= 1); ++ ++ PA_CHECK_VALIDITY_RETURN_NULL(c, !pa_detect_fork(), PA_ERR_FORKED); ++ PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE); ++ PA_CHECK_VALIDITY_RETURN_NULL(c, c->version >= 14, PA_ERR_NOTSUPPORTED); ++ ++ o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata); ++ ++ t = pa_tagstruct_command(c, PA_COMMAND_EXTENSION, &tag); ++ pa_tagstruct_putu32(t, PA_INVALID_INDEX); ++ pa_tagstruct_puts(t, "module-murphy-ivi"); ++ pa_tagstruct_putu32(t, SUBCOMMAND_CONNECT); ++ ++ pa_tagstruct_putu32(t, source_node_id); ++ pa_tagstruct_putu32(t, sink_node_id); ++ ++ pa_pstream_send_tagstruct(c->pstream, t); ++ pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, ext_node_manager_connect_cb, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref); ++ ++ return o; ++} ++ ++pa_operation *pa_ext_node_manager_disconnect_nodes( ++ pa_context *c, ++ uint32_t conn_id, ++ pa_context_success_cb_t cb, ++ void *userdata) { ++ ++ uint32_t tag; ++ pa_operation *o = NULL; ++ pa_tagstruct *t = NULL; ++ ++ pa_assert(c); ++ pa_assert(PA_REFCNT_VALUE(c) >= 1); ++ ++ PA_CHECK_VALIDITY_RETURN_NULL(c, !pa_detect_fork(), PA_ERR_FORKED); ++ PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE); ++ PA_CHECK_VALIDITY_RETURN_NULL(c, c->version >= 14, PA_ERR_NOTSUPPORTED); ++ ++ o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata); ++ ++ t = pa_tagstruct_command(c, PA_COMMAND_EXTENSION, &tag); ++ pa_tagstruct_putu32(t, PA_INVALID_INDEX); ++ pa_tagstruct_puts(t, "module-murphy-ivi"); ++ pa_tagstruct_putu32(t, SUBCOMMAND_DISCONNECT); ++ ++ pa_tagstruct_putu32(t, conn_id); ++ ++ pa_pstream_send_tagstruct(c->pstream, t); ++ pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, pa_context_simple_ack_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref); ++ ++ return o; ++} ++ ++pa_operation *pa_ext_node_manager_subscribe( ++ pa_context *c, ++ int enable, ++ pa_context_success_cb_t cb, ++ void *userdata) { ++ ++ uint32_t tag; ++ pa_operation *o; ++ pa_tagstruct *t; ++ ++ pa_assert(c); ++ pa_assert(PA_REFCNT_VALUE(c) >= 1); ++ ++ PA_CHECK_VALIDITY_RETURN_NULL(c, !pa_detect_fork(), PA_ERR_FORKED); ++ PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE); ++ PA_CHECK_VALIDITY_RETURN_NULL(c, c->version >= 14, PA_ERR_NOTSUPPORTED); ++ ++ o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata); ++ ++ t = pa_tagstruct_command(c, PA_COMMAND_EXTENSION, &tag); ++ pa_tagstruct_putu32(t, PA_INVALID_INDEX); ++ pa_tagstruct_puts(t, "module-murphy-ivi"); ++ pa_tagstruct_putu32(t, SUBCOMMAND_SUBSCRIBE); ++ pa_tagstruct_put_boolean(t, enable); ++ pa_pstream_send_tagstruct(c->pstream, t); ++ pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, pa_context_simple_ack_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref); ++ ++ return o; ++} ++ ++void pa_ext_node_manager_set_subscribe_cb( ++ pa_context *c, ++ pa_ext_node_manager_subscribe_cb_t cb, ++ void *userdata) { ++ ++ pa_assert(c); ++ pa_assert(PA_REFCNT_VALUE(c) >= 1); ++ ++ if (pa_detect_fork()) ++ return; ++ ++ c->ext_node_manager.callback = cb; ++ c->ext_node_manager.userdata = userdata; ++} ++ ++void pa_ext_node_manager_command(pa_context *c, uint32_t tag, pa_tagstruct *t) { ++ uint32_t subcommand; ++ ++ pa_assert(c); ++ pa_assert(PA_REFCNT_VALUE(c) >= 1); ++ pa_assert(t); ++ ++ if (pa_tagstruct_getu32(t, &subcommand) < 0 || ++ !pa_tagstruct_eof(t)) { ++ ++ pa_context_fail(c, PA_ERR_PROTOCOL); ++ return; ++ } ++ ++ if (subcommand != SUBCOMMAND_EVENT) { ++ pa_context_fail(c, PA_ERR_PROTOCOL); ++ return; ++ } ++ ++ if (c->ext_node_manager.callback) ++ c->ext_node_manager.callback(c, c->ext_node_manager.userdata); ++} +diff --git a/src/pulse/ext-node-manager.h b/src/pulse/ext-node-manager.h +new file mode 100644 +index 0000000..57b9f01 +--- /dev/null ++++ b/src/pulse/ext-node-manager.h +@@ -0,0 +1,93 @@ ++#ifndef foopulseextnodemanagerhfoo ++#define foopulseextnodemanagerhfoo ++ ++/*** ++ This file is part of PulseAudio. ++ ++ Copyright 2012 Jaska Uimonen ++ Copyright 2012 Janos Kovacs ++ ++ PulseAudio is free software; you can redistribute it and/or modify ++ it under the terms of the GNU Lesser General Public License as published ++ by the Free Software Foundation; either version 2.1 of the License, ++ or (at your option) any later version. ++ ++ PulseAudio is distributed in the hope that it will be useful, but ++ WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ General Public License for more details. ++ ++ You should have received a copy of the GNU Lesser General Public License ++ along with PulseAudio; if not, write to the Free Software ++ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 ++ USA. ++***/ ++ ++#include ++#include ++#include ++ ++PA_C_DECL_BEGIN ++ ++typedef struct pa_ext_node_manager_info { ++ const char *name; ++ pa_proplist *props; ++} pa_ext_node_manager_info; ++ ++typedef void (*pa_ext_node_manager_test_cb_t)( ++ pa_context *c, ++ uint32_t version, ++ void *userdata); ++ ++pa_operation *pa_ext_node_manager_test( ++ pa_context *c, ++ pa_ext_node_manager_test_cb_t cb, ++ void *userdata); ++ ++typedef void (*pa_ext_node_manager_read_cb_t)( ++ pa_context *c, ++ const pa_ext_node_manager_info *info, ++ int eol, ++ void *userdata); ++ ++pa_operation *pa_ext_node_manager_read_nodes( ++ pa_context *c, ++ pa_ext_node_manager_read_cb_t cb, ++ void *userdata); ++ ++typedef void (*pa_ext_node_manager_connect_cb_t)( ++ pa_context *c, ++ uint32_t connection, ++ void *userdata); ++ ++pa_operation *pa_ext_node_manager_connect_nodes( ++ pa_context *c, ++ uint32_t src, ++ uint32_t dst, ++ pa_ext_node_manager_connect_cb_t cb, ++ void *userdata); ++ ++pa_operation *pa_ext_node_manager_disconnect_nodes( ++ pa_context *c, ++ uint32_t conn, ++ pa_context_success_cb_t cb, ++ void *userdata); ++ ++pa_operation *pa_ext_node_manager_subscribe( ++ pa_context *c, ++ int enable, ++ pa_context_success_cb_t cb, ++ void *userdata); ++ ++typedef void (*pa_ext_node_manager_subscribe_cb_t)( ++ pa_context *c, ++ void *userdata); ++ ++void pa_ext_node_manager_set_subscribe_cb( ++ pa_context *c, ++ pa_ext_node_manager_subscribe_cb_t cb, ++ void *userdata); ++ ++PA_C_DECL_END ++ ++#endif +diff --git a/src/pulse/internal.h b/src/pulse/internal.h +index c5084d5..61095d0 100644 +--- a/src/pulse/internal.h ++++ b/src/pulse/internal.h +@@ -31,6 +31,7 @@ + #include + #include + #include ++#include + + #include + #include +@@ -115,6 +116,10 @@ struct pa_context { + pa_ext_stream_restore_subscribe_cb_t callback; + void *userdata; + } ext_stream_restore; ++ struct { ++ pa_ext_node_manager_subscribe_cb_t callback; ++ void *userdata; ++ } ext_node_manager; + }; + + #define PA_MAX_WRITE_INDEX_CORRECTIONS 32 +@@ -303,6 +308,7 @@ pa_tagstruct *pa_tagstruct_command(pa_context *c, uint32_t command, uint32_t *ta + void pa_ext_device_manager_command(pa_context *c, uint32_t tag, pa_tagstruct *t); + void pa_ext_device_restore_command(pa_context *c, uint32_t tag, pa_tagstruct *t); + void pa_ext_stream_restore_command(pa_context *c, uint32_t tag, pa_tagstruct *t); ++void pa_ext_node_manager_command(pa_context *c, uint32_t tag, pa_tagstruct *t); + + bool pa_mainloop_is_our_api(pa_mainloop_api*m); + diff --git a/meta-tizen/recipes-multimedia/pulseaudio/pulseaudio_5.0/0008-node-manager-adding-node-support-for-pactl.patch b/meta-tizen/recipes-multimedia/pulseaudio/pulseaudio_5.0/0008-node-manager-adding-node-support-for-pactl.patch new file mode 100644 index 0000000..64cc9c3 --- /dev/null +++ b/meta-tizen/recipes-multimedia/pulseaudio/pulseaudio_5.0/0008-node-manager-adding-node-support-for-pactl.patch @@ -0,0 +1,152 @@ +From: Jaska Uimonen +Date: Wed, 5 Dec 2012 09:53:12 +0200 +Subject: node-manager: adding node support for pactl + +Change-Id: Id6badeba2181ef4afa9842307e4c6c60f72c472f +Signed-off-by: Jaska Uimonen +--- + src/utils/pactl.c | 75 ++++++++++++++++++++++++++++++++++++++++++++++++++++--- + 1 file changed, 72 insertions(+), 3 deletions(-) + +diff --git a/src/utils/pactl.c b/src/utils/pactl.c +index 40e6689..1d8faa4 100644 +--- a/src/utils/pactl.c ++++ b/src/utils/pactl.c +@@ -38,6 +38,7 @@ + + #include + #include ++#include + + #include + #include +@@ -97,6 +98,10 @@ static int actions = 1; + + static bool nl = false; + ++static uint32_t src_node_id; ++static uint32_t dst_node_id; ++static uint32_t conn_id; ++ + static enum { + NONE, + EXIT, +@@ -127,7 +132,9 @@ static enum { + SET_SOURCE_OUTPUT_MUTE, + SET_SINK_FORMATS, + SET_PORT_LATENCY_OFFSET, +- SUBSCRIBE ++ SUBSCRIBE, ++ NODE_CONNECT, ++ NODE_DISCONNECT + } action = NONE; + + static void quit(int ret) { +@@ -1007,6 +1014,30 @@ static void source_output_toggle_mute_callback(pa_context *c, const pa_source_ou + pa_operation_unref(pa_context_set_source_output_mute(c, o->index, !o->mute, simple_callback, NULL)); + } + ++static void node_list_callback(pa_context *c, ++ const pa_ext_node_manager_info *info, ++ int eol, ++ void *userdata) { ++ ++ if (!eol) { ++ const char *node_id = pa_proplist_gets(info->props, "index"); ++ if (node_id != NULL) { ++ printf("Node #%s (%s)\n", node_id, info->name); ++ printf("%s\n", pa_proplist_to_string(info->props)); ++ } ++ } else ++ complete_action(); ++} ++ ++static void node_connect_callback(pa_context *c, ++ uint32_t conne_id, ++ void *userdata) { ++ ++ printf("New connection id: %d\n", conne_id); ++ ++ complete_action(); ++} ++ + /* PA_MAX_FORMATS is defined in internal.h so we just define a sane value here */ + #define MAX_FORMATS 256 + +@@ -1212,6 +1243,8 @@ static void context_state_callback(pa_context *c, void *userdata) { + pa_operation_unref(pa_context_get_sample_info_list(c, get_sample_info_callback, NULL)); + else if (pa_streq(list_type, "cards")) + pa_operation_unref(pa_context_get_card_info_list(c, get_card_info_callback, NULL)); ++ else if (pa_streq(list_type, "nodes")) ++ pa_operation_unref(pa_ext_node_manager_read_nodes(c, node_list_callback, NULL)); + else + pa_assert_not_reached(); + } else { +@@ -1373,6 +1406,18 @@ static void context_state_callback(pa_context *c, void *userdata) { + NULL, + NULL)); + break; ++ case NODE_CONNECT: ++ pa_operation_unref(pa_ext_node_manager_connect_nodes(c, ++ src_node_id, ++ dst_node_id, ++ node_connect_callback, ++ NULL)); ++ break; ++ case NODE_DISCONNECT: ++ pa_operation_unref(pa_ext_node_manager_disconnect_nodes(c, conn_id, ++ simple_callback, ++ NULL)); ++ break; + + default: + pa_assert_not_reached(); +@@ -1494,6 +1539,9 @@ static void help(const char *argv0) { + printf("%s %s %s\n", argv0, _("[options]"), "subscribe"); + printf(_("\nThe special names @DEFAULT_SINK@, @DEFAULT_SOURCE@ and @DEFAULT_MONITOR@\n" + "can be used to specify the default sink, source and monitor.\n")); ++ printf("%s %s %s\n", argv0, _("[options]"), "node-list "); ++ printf("%s %s %s %s %s\n", argv0, _("[options]"), "node-connect ", _("#N"), _("#N")); ++ printf("%s %s %s %s\n", argv0, _("[options]"), "node-disconnect ", _("#N")); + + printf(_("\n" + " -h, --help Show this help\n" +@@ -1590,7 +1638,7 @@ int main(int argc, char *argv[]) { + if (pa_streq(argv[i], "modules") || pa_streq(argv[i], "clients") || + pa_streq(argv[i], "sinks") || pa_streq(argv[i], "sink-inputs") || + pa_streq(argv[i], "sources") || pa_streq(argv[i], "source-outputs") || +- pa_streq(argv[i], "samples") || pa_streq(argv[i], "cards")) { ++ pa_streq(argv[i], "samples") || pa_streq(argv[i], "cards") || pa_streq(argv[i], "nodes")) { + list_type = pa_xstrdup(argv[i]); + } else if (pa_streq(argv[i], "short")) { + short_list_format = true; +@@ -1960,7 +2008,28 @@ int main(int argc, char *argv[]) { + goto quit; + } + +- } else if (pa_streq(argv[optind], "help")) { ++ } else if (pa_streq(argv[optind], "node-connect")) { ++ action = NODE_CONNECT; ++ ++ if (argc != optind+3) { ++ pa_log(_("You have to specify a source and destination node indexes")); ++ goto quit; ++ } ++ ++ src_node_id = (uint32_t) atoi(argv[optind+1]); ++ dst_node_id = (uint32_t) atoi(argv[optind+2]); ++ ++ } else if (pa_streq(argv[optind], "node-disconnect")) { ++ action = NODE_DISCONNECT; ++ ++ if (argc != optind+2) { ++ pa_log(_("You have to specify a connection id")); ++ goto quit; ++ } ++ ++ conn_id = (uint32_t) atoi(argv[optind+1]); ++ ++ } else if (pa_streq(argv[optind], "help")) { + help(bn); + ret = 0; + goto quit; diff --git a/meta-tizen/recipes-multimedia/pulseaudio/pulseaudio_5.0/0009-add-internal-corking-state-for-sink-input.patch b/meta-tizen/recipes-multimedia/pulseaudio/pulseaudio_5.0/0009-add-internal-corking-state-for-sink-input.patch new file mode 100644 index 0000000..3ff665d --- /dev/null +++ b/meta-tizen/recipes-multimedia/pulseaudio/pulseaudio_5.0/0009-add-internal-corking-state-for-sink-input.patch @@ -0,0 +1,87 @@ +From: Jaska Uimonen +Date: Thu, 7 Mar 2013 13:41:44 +0200 +Subject: add internal corking state for sink-input + +Change-Id: Iaa7ef16c798e06c0fdf11097690c6cf49215773d +Signed-off-by: Jaska Uimonen +--- + src/pulsecore/sink-input.c | 30 +++++++++++++++++++++++++++++- + src/pulsecore/sink-input.h | 4 ++++ + 2 files changed, 33 insertions(+), 1 deletion(-) + +diff --git a/src/pulsecore/sink-input.c b/src/pulsecore/sink-input.c +index 05ab25d..dff6324 100644 +--- a/src/pulsecore/sink-input.c ++++ b/src/pulsecore/sink-input.c +@@ -788,6 +788,9 @@ void pa_sink_input_put(pa_sink_input *i) { + update_n_corked(i, state); + i->state = state; + ++ i->corked = FALSE; ++ i->corked_internal = FALSE; ++ + /* We might need to update the sink's volume if we are in flat volume mode. */ + if (pa_sink_flat_volume_enabled(i->sink)) + pa_sink_set_volume(i->sink, NULL, false, i->save_volume); +@@ -1506,13 +1509,38 @@ void pa_sink_input_update_proplist(pa_sink_input *i, pa_update_mode_t mode, pa_p + } + } + ++static void pa_sink_input_cork_really(pa_sink_input *i, bool b) { ++ pa_sink_input_assert_ref(i); ++ pa_assert_ctl_context(); ++ pa_assert(PA_SINK_INPUT_IS_LINKED(i->state)); ++ ++ if (i->corked_internal == false && i->corked == false) ++ b = false; ++ else ++ b = true; ++ ++ sink_input_set_state(i, b ? PA_SINK_INPUT_CORKED : PA_SINK_INPUT_RUNNING); ++} ++ + /* Called from main context */ + void pa_sink_input_cork(pa_sink_input *i, bool b) { + pa_sink_input_assert_ref(i); + pa_assert_ctl_context(); + pa_assert(PA_SINK_INPUT_IS_LINKED(i->state)); + +- sink_input_set_state(i, b ? PA_SINK_INPUT_CORKED : PA_SINK_INPUT_RUNNING); ++ i->corked = b; ++ ++ pa_sink_input_cork_really(i, b); ++} ++ ++void pa_sink_input_cork_internal(pa_sink_input *i, bool b) { ++ pa_sink_input_assert_ref(i); ++ pa_assert_ctl_context(); ++ pa_assert(PA_SINK_INPUT_IS_LINKED(i->state)); ++ ++ i->corked_internal = b; ++ ++ pa_sink_input_cork_really(i, b); + } + + /* Called from main context */ +diff --git a/src/pulsecore/sink-input.h b/src/pulsecore/sink-input.h +index 3f74054..deea348 100644 +--- a/src/pulsecore/sink-input.h ++++ b/src/pulsecore/sink-input.h +@@ -129,6 +129,9 @@ struct pa_sink_input { + /* for volume ramps */ + pa_cvolume_ramp_int ramp; + ++ bool corked; ++ bool corked_internal; ++ + pa_resample_method_t requested_resample_method, actual_resample_method; + + /* Returns the chunk of audio data and drops it from the +@@ -360,6 +363,7 @@ implementing the "zero latency" write-through functionality. */ + void pa_sink_input_request_rewind(pa_sink_input *i, size_t nbytes, bool rewrite, bool flush, bool dont_rewind_render); + + void pa_sink_input_cork(pa_sink_input *i, bool b); ++void pa_sink_input_cork_internal(pa_sink_input *i, bool b); + + int pa_sink_input_set_rate(pa_sink_input *i, uint32_t rate); + int pa_sink_input_update_rate(pa_sink_input *i); diff --git a/meta-tizen/recipes-multimedia/pulseaudio/pulseaudio_5.0/0010-bluetooth-Add-basic-support-for-HEADSET-profiles.patch b/meta-tizen/recipes-multimedia/pulseaudio/pulseaudio_5.0/0010-bluetooth-Add-basic-support-for-HEADSET-profiles.patch new file mode 100644 index 0000000..3249f6f --- /dev/null +++ b/meta-tizen/recipes-multimedia/pulseaudio/pulseaudio_5.0/0010-bluetooth-Add-basic-support-for-HEADSET-profiles.patch @@ -0,0 +1,634 @@ +From: =?utf-8?q?Jo=C3=A3o_Paulo_Rechi_Vita?= +Date: Wed, 10 Jul 2013 09:45:01 -0300 +Subject: bluetooth: Add basic support for HEADSET profiles + +This commit adds basic support for devices implementing HSP Headset +Unit, HSP Audio Gateway, HFP Handsfree Unit, HFP Audio Gateway to the +BlueZ 5 bluetooth audio devices driver module (module-bluez5-device). + +Change-Id: I6be5bcf310a327012f382d408a70e7fdc65caab1 +--- + src/modules/bluetooth/bluez5-util.c | 4 + + src/modules/bluetooth/bluez5-util.h | 6 + + src/modules/bluetooth/module-bluez5-device.c | 443 ++++++++++++++++++++------- + 3 files changed, 345 insertions(+), 108 deletions(-) + +diff --git a/src/modules/bluetooth/bluez5-util.c b/src/modules/bluetooth/bluez5-util.c +index 7eecc18..ae219ea 100644 +--- a/src/modules/bluetooth/bluez5-util.c ++++ b/src/modules/bluetooth/bluez5-util.c +@@ -1093,6 +1093,10 @@ const char *pa_bluetooth_profile_to_string(pa_bluetooth_profile_t profile) { + return "a2dp_sink"; + case PA_BLUETOOTH_PROFILE_A2DP_SOURCE: + return "a2dp_source"; ++ case PA_BLUETOOTH_PROFILE_HEADSET_HEAD_UNIT: ++ return "hsp"; ++ case PA_BLUETOOTH_PROFILE_HEADSET_AUDIO_GATEWAY: ++ return "hfgw"; + case PA_BLUETOOTH_PROFILE_OFF: + return "off"; + } +diff --git a/src/modules/bluetooth/bluez5-util.h b/src/modules/bluetooth/bluez5-util.h +index bbc5b71..841c3c6 100644 +--- a/src/modules/bluetooth/bluez5-util.h ++++ b/src/modules/bluetooth/bluez5-util.h +@@ -26,6 +26,10 @@ + + #define PA_BLUETOOTH_UUID_A2DP_SOURCE "0000110a-0000-1000-8000-00805f9b34fb" + #define PA_BLUETOOTH_UUID_A2DP_SINK "0000110b-0000-1000-8000-00805f9b34fb" ++#define PA_BLUETOOTH_UUID_HSP_HS "00001108-0000-1000-8000-00805f9b34fb" ++#define PA_BLUETOOTH_UUID_HSP_AG "00001112-0000-1000-8000-00805f9b34fb" ++#define PA_BLUETOOTH_UUID_HFP_HF "0000111e-0000-1000-8000-00805f9b34fb" ++#define PA_BLUETOOTH_UUID_HFP_AG "0000111f-0000-1000-8000-00805f9b34fb" + + typedef struct pa_bluetooth_transport pa_bluetooth_transport; + typedef struct pa_bluetooth_device pa_bluetooth_device; +@@ -41,6 +45,8 @@ typedef enum pa_bluetooth_hook { + typedef enum profile { + PA_BLUETOOTH_PROFILE_A2DP_SINK, + PA_BLUETOOTH_PROFILE_A2DP_SOURCE, ++ PA_BLUETOOTH_PROFILE_HEADSET_HEAD_UNIT, ++ PA_BLUETOOTH_PROFILE_HEADSET_AUDIO_GATEWAY, + PA_BLUETOOTH_PROFILE_OFF + } pa_bluetooth_profile_t; + #define PA_BLUETOOTH_PROFILE_COUNT PA_BLUETOOTH_PROFILE_OFF +diff --git a/src/modules/bluetooth/module-bluez5-device.c b/src/modules/bluetooth/module-bluez5-device.c +index 287e763..b511b92 100644 +--- a/src/modules/bluetooth/module-bluez5-device.c ++++ b/src/modules/bluetooth/module-bluez5-device.c +@@ -33,6 +33,7 @@ + #include + + #include ++#include + #include + #include + #include +@@ -59,7 +60,9 @@ PA_MODULE_USAGE("path="); + + #define MAX_PLAYBACK_CATCH_UP_USEC (100 * PA_USEC_PER_MSEC) + #define FIXED_LATENCY_PLAYBACK_A2DP (25 * PA_USEC_PER_MSEC) ++#define FIXED_LATENCY_PLAYBACK_SCO (125 * PA_USEC_PER_MSEC) + #define FIXED_LATENCY_RECORD_A2DP (25 * PA_USEC_PER_MSEC) ++#define FIXED_LATENCY_RECORD_SCO (25 * PA_USEC_PER_MSEC) + + #define BITPOOL_DEC_LIMIT 32 + #define BITPOOL_DEC_STEP 5 +@@ -235,6 +238,167 @@ static void connect_ports(struct userdata *u, void *new_data, pa_direction_t dir + } + + /* Run from IO thread */ ++static int sco_process_render(struct userdata *u) { ++ int ret = 0; ++ ++ pa_assert(u); ++ pa_assert(u->profile == PA_BLUETOOTH_PROFILE_HEADSET_HEAD_UNIT || u->profile == PA_BLUETOOTH_PROFILE_HEADSET_AUDIO_GATEWAY); ++ pa_assert(u->sink); ++ ++ /* First, render some data */ ++ if (!u->write_memchunk.memblock) ++ pa_sink_render_full(u->sink, u->write_block_size, &u->write_memchunk); ++ ++ pa_assert(u->write_memchunk.length == u->write_block_size); ++ ++ for (;;) { ++ ssize_t l; ++ const void *p; ++ ++ /* Now write that data to the socket. The socket is of type ++ * SEQPACKET, and we generated the data of the MTU size, so this ++ * should just work. */ ++ ++ p = (const uint8_t *) pa_memblock_acquire_chunk(&u->write_memchunk); ++ l = pa_write(u->stream_fd, p, u->write_memchunk.length, &u->stream_write_type); ++ pa_memblock_release(u->write_memchunk.memblock); ++ ++ pa_assert(l != 0); ++ ++ if (l < 0) { ++ ++ if (errno == EINTR) ++ /* Retry right away if we got interrupted */ ++ continue; ++ ++ else if (errno == EAGAIN) ++ /* Hmm, apparently the socket was not writable, give up for now */ ++ break; ++ ++ pa_log_error("Failed to write data to SCO socket: %s", pa_cstrerror(errno)); ++ ret = -1; ++ break; ++ } ++ ++ pa_assert((size_t) l <= u->write_memchunk.length); ++ ++ if ((size_t) l != u->write_memchunk.length) { ++ pa_log_error("Wrote memory block to socket only partially! %llu written, wanted to write %llu.", ++ (unsigned long long) l, ++ (unsigned long long) u->write_memchunk.length); ++ ret = -1; ++ break; ++ } ++ ++ u->write_index += (uint64_t) u->write_memchunk.length; ++ pa_memblock_unref(u->write_memchunk.memblock); ++ pa_memchunk_reset(&u->write_memchunk); ++ ++ ret = 1; ++ break; ++ } ++ ++ return ret; ++} ++ ++/* Run from IO thread */ ++static int sco_process_push(struct userdata *u) { ++ int ret = 0; ++ pa_memchunk memchunk; ++ ++ pa_assert(u); ++ pa_assert(u->profile == PA_BLUETOOTH_PROFILE_HEADSET_HEAD_UNIT || u->profile == PA_BLUETOOTH_PROFILE_HEADSET_AUDIO_GATEWAY); ++ pa_assert(u->source); ++ pa_assert(u->read_smoother); ++ ++ memchunk.memblock = pa_memblock_new(u->core->mempool, u->read_block_size); ++ memchunk.index = memchunk.length = 0; ++ ++ for (;;) { ++ ssize_t l; ++ void *p; ++ struct msghdr m; ++ struct cmsghdr *cm; ++ uint8_t aux[1024]; ++ struct iovec iov; ++ bool found_tstamp = false; ++ pa_usec_t tstamp; ++ ++ memset(&m, 0, sizeof(m)); ++ memset(&aux, 0, sizeof(aux)); ++ memset(&iov, 0, sizeof(iov)); ++ ++ m.msg_iov = &iov; ++ m.msg_iovlen = 1; ++ m.msg_control = aux; ++ m.msg_controllen = sizeof(aux); ++ ++ p = pa_memblock_acquire(memchunk.memblock); ++ iov.iov_base = p; ++ iov.iov_len = pa_memblock_get_length(memchunk.memblock); ++ l = recvmsg(u->stream_fd, &m, 0); ++ pa_memblock_release(memchunk.memblock); ++ ++ if (l <= 0) { ++ ++ if (l < 0 && errno == EINTR) ++ /* Retry right away if we got interrupted */ ++ continue; ++ ++ else if (l < 0 && errno == EAGAIN) ++ /* Hmm, apparently the socket was not readable, give up for now. */ ++ break; ++ ++ pa_log_error("Failed to read data from SCO socket: %s", l < 0 ? pa_cstrerror(errno) : "EOF"); ++ ret = -1; ++ break; ++ } ++ ++ pa_assert((size_t) l <= pa_memblock_get_length(memchunk.memblock)); ++ ++ /* In some rare occasions, we might receive packets of a very strange ++ * size. This could potentially be possible if the SCO packet was ++ * received partially over-the-air, or more probably due to hardware ++ * issues in our Bluetooth adapter. In these cases, in order to avoid ++ * an assertion failure due to unaligned data, just discard the whole ++ * packet */ ++ if (!pa_frame_aligned(l, &u->sample_spec)) { ++ pa_log_warn("SCO packet received of unaligned size: %zu", l); ++ break; ++ } ++ ++ memchunk.length = (size_t) l; ++ u->read_index += (uint64_t) l; ++ ++ for (cm = CMSG_FIRSTHDR(&m); cm; cm = CMSG_NXTHDR(&m, cm)) ++ if (cm->cmsg_level == SOL_SOCKET && cm->cmsg_type == SO_TIMESTAMP) { ++ struct timeval *tv = (struct timeval*) CMSG_DATA(cm); ++ pa_rtclock_from_wallclock(tv); ++ tstamp = pa_timeval_load(tv); ++ found_tstamp = true; ++ break; ++ } ++ ++ if (!found_tstamp) { ++ pa_log_warn("Couldn't find SO_TIMESTAMP data in auxiliary recvmsg() data!"); ++ tstamp = pa_rtclock_now(); ++ } ++ ++ pa_smoother_put(u->read_smoother, tstamp, pa_bytes_to_usec(u->read_index, &u->sample_spec)); ++ pa_smoother_resume(u->read_smoother, tstamp, true); ++ ++ pa_source_post(u->source, &memchunk); ++ ++ ret = l; ++ break; ++ } ++ ++ pa_memblock_unref(memchunk.memblock); ++ ++ return ret; ++} ++ ++/* Run from IO thread */ + static void a2dp_prepare_buffer(struct userdata *u) { + size_t min_buffer_size = PA_MAX(u->read_link_mtu, u->write_link_mtu); + +@@ -608,24 +772,31 @@ static void transport_release(struct userdata *u) { + + /* Run from I/O thread */ + static void transport_config_mtu(struct userdata *u) { +- u->read_block_size = +- (u->read_link_mtu - sizeof(struct rtp_header) - sizeof(struct rtp_payload)) +- / u->sbc_info.frame_length * u->sbc_info.codesize; ++ if (u->profile == PA_BLUETOOTH_PROFILE_HEADSET_HEAD_UNIT || u->profile == PA_BLUETOOTH_PROFILE_HEADSET_AUDIO_GATEWAY) { ++ u->read_block_size = u->read_link_mtu; ++ u->write_block_size = u->write_link_mtu; ++ } else { ++ u->read_block_size = ++ (u->read_link_mtu - sizeof(struct rtp_header) - sizeof(struct rtp_payload)) ++ / u->sbc_info.frame_length * u->sbc_info.codesize; + +- u->write_block_size = +- (u->write_link_mtu - sizeof(struct rtp_header) - sizeof(struct rtp_payload)) +- / u->sbc_info.frame_length * u->sbc_info.codesize; ++ u->write_block_size = ++ (u->write_link_mtu - sizeof(struct rtp_header) - sizeof(struct rtp_payload)) ++ / u->sbc_info.frame_length * u->sbc_info.codesize; ++ } + + if (u->sink) { + pa_sink_set_max_request_within_thread(u->sink, u->write_block_size); + pa_sink_set_fixed_latency_within_thread(u->sink, +- FIXED_LATENCY_PLAYBACK_A2DP + ++ (u->profile == PA_BLUETOOTH_PROFILE_A2DP_SINK ? ++ FIXED_LATENCY_PLAYBACK_A2DP : FIXED_LATENCY_PLAYBACK_SCO) + + pa_bytes_to_usec(u->write_block_size, &u->sample_spec)); + } + + if (u->source) + pa_source_set_fixed_latency_within_thread(u->source, +- FIXED_LATENCY_RECORD_A2DP + ++ (u->profile == PA_BLUETOOTH_PROFILE_A2DP_SOURCE ? ++ FIXED_LATENCY_RECORD_A2DP : FIXED_LATENCY_RECORD_SCO) + + pa_bytes_to_usec(u->read_block_size, &u->sample_spec)); + } + +@@ -752,15 +923,19 @@ static int add_source(struct userdata *u) { + data.namereg_fail = false; + pa_proplist_sets(data.proplist, "bluetooth.protocol", pa_bluetooth_profile_to_string(u->profile)); + pa_source_new_data_set_sample_spec(&data, &u->sample_spec); ++ if (u->profile == PA_BLUETOOTH_PROFILE_HEADSET_HEAD_UNIT) ++ pa_proplist_sets(data.proplist, PA_PROP_DEVICE_INTENDED_ROLES, "phone"); + + connect_ports(u, &data, PA_DIRECTION_INPUT); + + if (!u->transport_acquired) + switch (u->profile) { + case PA_BLUETOOTH_PROFILE_A2DP_SOURCE: ++ case PA_BLUETOOTH_PROFILE_HEADSET_AUDIO_GATEWAY: + data.suspend_cause = PA_SUSPEND_USER; + break; + case PA_BLUETOOTH_PROFILE_A2DP_SINK: ++ case PA_BLUETOOTH_PROFILE_HEADSET_HEAD_UNIT: + case PA_BLUETOOTH_PROFILE_OFF: + pa_assert_not_reached(); + break; +@@ -867,14 +1042,20 @@ static int add_sink(struct userdata *u) { + data.namereg_fail = false; + pa_proplist_sets(data.proplist, "bluetooth.protocol", pa_bluetooth_profile_to_string(u->profile)); + pa_sink_new_data_set_sample_spec(&data, &u->sample_spec); ++ if (u->profile == PA_BLUETOOTH_PROFILE_HEADSET_HEAD_UNIT) ++ pa_proplist_sets(data.proplist, PA_PROP_DEVICE_INTENDED_ROLES, "phone"); + + connect_ports(u, &data, PA_DIRECTION_OUTPUT); + + if (!u->transport_acquired) + switch (u->profile) { ++ case PA_BLUETOOTH_PROFILE_HEADSET_AUDIO_GATEWAY: ++ data.suspend_cause = PA_SUSPEND_USER; ++ break; + case PA_BLUETOOTH_PROFILE_A2DP_SINK: + /* Profile switch should have failed */ + case PA_BLUETOOTH_PROFILE_A2DP_SOURCE: ++ case PA_BLUETOOTH_PROFILE_HEADSET_HEAD_UNIT: + case PA_BLUETOOTH_PROFILE_OFF: + pa_assert_not_reached(); + break; +@@ -895,111 +1076,117 @@ static int add_sink(struct userdata *u) { + + /* Run from main thread */ + static void transport_config(struct userdata *u) { +- sbc_info_t *sbc_info = &u->sbc_info; +- a2dp_sbc_t *config; ++ if (u->profile == PA_BLUETOOTH_PROFILE_HEADSET_HEAD_UNIT || u->profile == PA_BLUETOOTH_PROFILE_HEADSET_AUDIO_GATEWAY) { ++ u->sample_spec.format = PA_SAMPLE_S16LE; ++ u->sample_spec.channels = 1; ++ u->sample_spec.rate = 8000; ++ } else { ++ sbc_info_t *sbc_info = &u->sbc_info; ++ a2dp_sbc_t *config; + +- pa_assert(u->transport); ++ pa_assert(u->transport); + +- u->sample_spec.format = PA_SAMPLE_S16LE; +- config = (a2dp_sbc_t *) u->transport->config; ++ u->sample_spec.format = PA_SAMPLE_S16LE; ++ config = (a2dp_sbc_t *) u->transport->config; + +- if (sbc_info->sbc_initialized) +- sbc_reinit(&sbc_info->sbc, 0); +- else +- sbc_init(&sbc_info->sbc, 0); +- sbc_info->sbc_initialized = true; ++ if (sbc_info->sbc_initialized) ++ sbc_reinit(&sbc_info->sbc, 0); ++ else ++ sbc_init(&sbc_info->sbc, 0); ++ sbc_info->sbc_initialized = true; + +- switch (config->frequency) { +- case SBC_SAMPLING_FREQ_16000: +- sbc_info->sbc.frequency = SBC_FREQ_16000; +- u->sample_spec.rate = 16000U; +- break; +- case SBC_SAMPLING_FREQ_32000: +- sbc_info->sbc.frequency = SBC_FREQ_32000; +- u->sample_spec.rate = 32000U; +- break; +- case SBC_SAMPLING_FREQ_44100: +- sbc_info->sbc.frequency = SBC_FREQ_44100; +- u->sample_spec.rate = 44100U; +- break; +- case SBC_SAMPLING_FREQ_48000: +- sbc_info->sbc.frequency = SBC_FREQ_48000; +- u->sample_spec.rate = 48000U; +- break; +- default: +- pa_assert_not_reached(); +- } ++ switch (config->frequency) { ++ case SBC_SAMPLING_FREQ_16000: ++ sbc_info->sbc.frequency = SBC_FREQ_16000; ++ u->sample_spec.rate = 16000U; ++ break; ++ case SBC_SAMPLING_FREQ_32000: ++ sbc_info->sbc.frequency = SBC_FREQ_32000; ++ u->sample_spec.rate = 32000U; ++ break; ++ case SBC_SAMPLING_FREQ_44100: ++ sbc_info->sbc.frequency = SBC_FREQ_44100; ++ u->sample_spec.rate = 44100U; ++ break; ++ case SBC_SAMPLING_FREQ_48000: ++ sbc_info->sbc.frequency = SBC_FREQ_48000; ++ u->sample_spec.rate = 48000U; ++ break; ++ default: ++ pa_assert_not_reached(); ++ } + +- switch (config->channel_mode) { +- case SBC_CHANNEL_MODE_MONO: +- sbc_info->sbc.mode = SBC_MODE_MONO; +- u->sample_spec.channels = 1; +- break; +- case SBC_CHANNEL_MODE_DUAL_CHANNEL: +- sbc_info->sbc.mode = SBC_MODE_DUAL_CHANNEL; +- u->sample_spec.channels = 2; +- break; +- case SBC_CHANNEL_MODE_STEREO: +- sbc_info->sbc.mode = SBC_MODE_STEREO; +- u->sample_spec.channels = 2; +- break; +- case SBC_CHANNEL_MODE_JOINT_STEREO: +- sbc_info->sbc.mode = SBC_MODE_JOINT_STEREO; +- u->sample_spec.channels = 2; +- break; +- default: +- pa_assert_not_reached(); +- } ++ switch (config->channel_mode) { ++ case SBC_CHANNEL_MODE_MONO: ++ sbc_info->sbc.mode = SBC_MODE_MONO; ++ u->sample_spec.channels = 1; ++ break; ++ case SBC_CHANNEL_MODE_DUAL_CHANNEL: ++ sbc_info->sbc.mode = SBC_MODE_DUAL_CHANNEL; ++ u->sample_spec.channels = 2; ++ break; ++ case SBC_CHANNEL_MODE_STEREO: ++ sbc_info->sbc.mode = SBC_MODE_STEREO; ++ u->sample_spec.channels = 2; ++ break; ++ case SBC_CHANNEL_MODE_JOINT_STEREO: ++ sbc_info->sbc.mode = SBC_MODE_JOINT_STEREO; ++ u->sample_spec.channels = 2; ++ break; ++ default: ++ pa_assert_not_reached(); ++ } + +- switch (config->allocation_method) { +- case SBC_ALLOCATION_SNR: +- sbc_info->sbc.allocation = SBC_AM_SNR; +- break; +- case SBC_ALLOCATION_LOUDNESS: +- sbc_info->sbc.allocation = SBC_AM_LOUDNESS; +- break; +- default: +- pa_assert_not_reached(); +- } ++ switch (config->allocation_method) { ++ case SBC_ALLOCATION_SNR: ++ sbc_info->sbc.allocation = SBC_AM_SNR; ++ break; ++ case SBC_ALLOCATION_LOUDNESS: ++ sbc_info->sbc.allocation = SBC_AM_LOUDNESS; ++ break; ++ default: ++ pa_assert_not_reached(); ++ } + +- switch (config->subbands) { +- case SBC_SUBBANDS_4: +- sbc_info->sbc.subbands = SBC_SB_4; +- break; +- case SBC_SUBBANDS_8: +- sbc_info->sbc.subbands = SBC_SB_8; +- break; +- default: +- pa_assert_not_reached(); +- } ++ switch (config->subbands) { ++ case SBC_SUBBANDS_4: ++ sbc_info->sbc.subbands = SBC_SB_4; ++ break; ++ case SBC_SUBBANDS_8: ++ sbc_info->sbc.subbands = SBC_SB_8; ++ break; ++ default: ++ pa_assert_not_reached(); ++ } + +- switch (config->block_length) { +- case SBC_BLOCK_LENGTH_4: +- sbc_info->sbc.blocks = SBC_BLK_4; +- break; +- case SBC_BLOCK_LENGTH_8: +- sbc_info->sbc.blocks = SBC_BLK_8; +- break; +- case SBC_BLOCK_LENGTH_12: +- sbc_info->sbc.blocks = SBC_BLK_12; +- break; +- case SBC_BLOCK_LENGTH_16: +- sbc_info->sbc.blocks = SBC_BLK_16; +- break; +- default: +- pa_assert_not_reached(); +- } ++ switch (config->block_length) { ++ case SBC_BLOCK_LENGTH_4: ++ sbc_info->sbc.blocks = SBC_BLK_4; ++ break; ++ case SBC_BLOCK_LENGTH_8: ++ sbc_info->sbc.blocks = SBC_BLK_8; ++ break; ++ case SBC_BLOCK_LENGTH_12: ++ sbc_info->sbc.blocks = SBC_BLK_12; ++ break; ++ case SBC_BLOCK_LENGTH_16: ++ sbc_info->sbc.blocks = SBC_BLK_16; ++ break; ++ default: ++ pa_assert_not_reached(); ++ } + +- sbc_info->min_bitpool = config->min_bitpool; +- sbc_info->max_bitpool = config->max_bitpool; ++ sbc_info->min_bitpool = config->min_bitpool; ++ sbc_info->max_bitpool = config->max_bitpool; + +- /* Set minimum bitpool for source to get the maximum possible block_size */ +- sbc_info->sbc.bitpool = u->profile == PA_BLUETOOTH_PROFILE_A2DP_SINK ? sbc_info->max_bitpool : sbc_info->min_bitpool; +- sbc_info->codesize = sbc_get_codesize(&sbc_info->sbc); +- sbc_info->frame_length = sbc_get_frame_length(&sbc_info->sbc); ++ /* Set minimum bitpool for source to get the maximum possible block_size */ ++ sbc_info->sbc.bitpool = u->profile == PA_BLUETOOTH_PROFILE_A2DP_SINK ? sbc_info->max_bitpool : sbc_info->min_bitpool; ++ sbc_info->codesize = sbc_get_codesize(&sbc_info->sbc); ++ sbc_info->frame_length = sbc_get_frame_length(&sbc_info->sbc); + +- pa_log_info("SBC parameters: allocation=%u, subbands=%u, blocks=%u, bitpool=%u", +- sbc_info->sbc.allocation, sbc_info->sbc.subbands, sbc_info->sbc.blocks, sbc_info->sbc.bitpool); ++ pa_log_info("SBC parameters: allocation=%u, subbands=%u, blocks=%u, bitpool=%u", ++ sbc_info->sbc.allocation, sbc_info->sbc.subbands, sbc_info->sbc.blocks, sbc_info->sbc.bitpool); ++ } + } + + /* Run from main thread */ +@@ -1019,7 +1206,7 @@ static int setup_transport(struct userdata *u) { + + u->transport = t; + +- if (u->profile == PA_BLUETOOTH_PROFILE_A2DP_SOURCE) ++ if (u->profile == PA_BLUETOOTH_PROFILE_A2DP_SOURCE || u->profile == PA_BLUETOOTH_PROFILE_HEADSET_AUDIO_GATEWAY) + transport_acquire(u, true); /* In case of error, the sink/sources will be created suspended */ + else if (transport_acquire(u, false) < 0) + return -1; /* We need to fail here until the interactions with module-suspend-on-idle and alike get improved */ +@@ -1040,11 +1227,13 @@ static int init_profile(struct userdata *u) { + + pa_assert(u->transport); + +- if (u->profile == PA_BLUETOOTH_PROFILE_A2DP_SINK) ++ if (u->profile == PA_BLUETOOTH_PROFILE_A2DP_SINK || u->profile == PA_BLUETOOTH_PROFILE_HEADSET_HEAD_UNIT || ++ u->profile == PA_BLUETOOTH_PROFILE_HEADSET_AUDIO_GATEWAY) + if (add_sink(u) < 0) + r = -1; + +- if (u->profile == PA_BLUETOOTH_PROFILE_A2DP_SOURCE) ++ if (u->profile == PA_BLUETOOTH_PROFILE_A2DP_SOURCE || u->profile == PA_BLUETOOTH_PROFILE_HEADSET_HEAD_UNIT || ++ u->profile == PA_BLUETOOTH_PROFILE_HEADSET_AUDIO_GATEWAY) + if (add_source(u) < 0) + r = -1; + +@@ -1090,7 +1279,10 @@ static void thread_func(void *userdata) { + if (pollfd && (pollfd->revents & POLLIN)) { + int n_read; + +- n_read = a2dp_process_push(u); ++ if (u->profile == PA_BLUETOOTH_PROFILE_HEADSET_HEAD_UNIT || u->profile == PA_BLUETOOTH_PROFILE_HEADSET_AUDIO_GATEWAY) ++ n_read = sco_process_push(u); ++ else ++ n_read = a2dp_process_push(u); + + if (n_read < 0) + goto io_fail; +@@ -1159,8 +1351,13 @@ static void thread_func(void *userdata) { + if (u->write_index <= 0) + u->started_at = pa_rtclock_now(); + +- if ((n_written = a2dp_process_render(u)) < 0) +- goto io_fail; ++ if (u->profile == PA_BLUETOOTH_PROFILE_A2DP_SINK) { ++ if ((n_written = a2dp_process_render(u)) < 0) ++ goto io_fail; ++ } else { ++ if ((n_written = sco_process_render(u)) < 0) ++ goto io_fail; ++ } + + if (n_written == 0) + pa_log("Broken kernel: we got EAGAIN on write() after POLLOUT!"); +@@ -1364,6 +1561,8 @@ static pa_direction_t get_profile_direction(pa_bluetooth_profile_t p) { + static const pa_direction_t profile_direction[] = { + [PA_BLUETOOTH_PROFILE_A2DP_SINK] = PA_DIRECTION_OUTPUT, + [PA_BLUETOOTH_PROFILE_A2DP_SOURCE] = PA_DIRECTION_INPUT, ++ [PA_BLUETOOTH_PROFILE_HEADSET_HEAD_UNIT] = PA_DIRECTION_INPUT | PA_DIRECTION_OUTPUT, ++ [PA_BLUETOOTH_PROFILE_HEADSET_AUDIO_GATEWAY] = PA_DIRECTION_INPUT | PA_DIRECTION_OUTPUT, + [PA_BLUETOOTH_PROFILE_OFF] = 0 + }; + +@@ -1543,6 +1742,34 @@ static pa_card_profile *create_card_profile(struct userdata *u, const char *uuid + + p = PA_CARD_PROFILE_DATA(cp); + *p = PA_BLUETOOTH_PROFILE_A2DP_SOURCE; ++ } else if (pa_streq(uuid, PA_BLUETOOTH_UUID_HSP_HS) || pa_streq(uuid, PA_BLUETOOTH_UUID_HFP_HF)) { ++ /* TODO: Change this profile's name to headset_head_unit, to reflect the remote ++ * device's role and be consistent with the other profiles */ ++ cp = pa_card_profile_new("hsp", _("Headset Head Unit (HSP/HFP)"), sizeof(enum profile)); ++ cp->priority = 20; ++ cp->n_sinks = 1; ++ cp->n_sources = 1; ++ cp->max_sink_channels = 1; ++ cp->max_source_channels = 1; ++ pa_hashmap_put(input_port->profiles, cp->name, cp); ++ pa_hashmap_put(output_port->profiles, cp->name, cp); ++ ++ p = PA_CARD_PROFILE_DATA(cp); ++ *p = PA_BLUETOOTH_PROFILE_HEADSET_HEAD_UNIT; ++ } else if (pa_streq(uuid, PA_BLUETOOTH_UUID_HSP_AG) || pa_streq(uuid, PA_BLUETOOTH_UUID_HFP_AG)) { ++ /* TODO: Change this profile's name to headset_audio_gateway, to reflect the remote ++ * device's role and be consistent with the other profiles */ ++ cp = pa_card_profile_new("hfgw", _("Headset Audio Gateway (HSP/HFP)"), sizeof(enum profile)); ++ cp->priority = 20; ++ cp->n_sinks = 1; ++ cp->n_sources = 1; ++ cp->max_sink_channels = 1; ++ cp->max_source_channels = 1; ++ pa_hashmap_put(input_port->profiles, cp->name, cp); ++ pa_hashmap_put(output_port->profiles, cp->name, cp); ++ ++ p = PA_CARD_PROFILE_DATA(cp); ++ *p = PA_BLUETOOTH_PROFILE_HEADSET_AUDIO_GATEWAY; + } + + if (cp && u->device->transports[*p]) diff --git a/meta-tizen/recipes-multimedia/pulseaudio/pulseaudio_5.0/0011-bluetooth-Create-Handsfree-Audio-Agent-NULL-backend.patch b/meta-tizen/recipes-multimedia/pulseaudio/pulseaudio_5.0/0011-bluetooth-Create-Handsfree-Audio-Agent-NULL-backend.patch new file mode 100644 index 0000000..0ead373 --- /dev/null +++ b/meta-tizen/recipes-multimedia/pulseaudio/pulseaudio_5.0/0011-bluetooth-Create-Handsfree-Audio-Agent-NULL-backend.patch @@ -0,0 +1,176 @@ +From: =?utf-8?q?Jo=C3=A3o_Paulo_Rechi_Vita?= +Date: Tue, 9 Jul 2013 19:08:15 -0300 +Subject: bluetooth: Create Handsfree Audio Agent NULL backend + +Change-Id: I1f562a336f2cd2d0ab91c25fd5128da3cc7fe6df +--- + configure.ac | 10 +++++++++ + src/Makefile.am | 4 +++- + src/modules/bluetooth/bluez5-util.c | 6 +++++ + src/modules/bluetooth/hfaudioagent-null.c | 37 +++++++++++++++++++++++++++++++ + src/modules/bluetooth/hfaudioagent.h | 31 ++++++++++++++++++++++++++ + 5 files changed, 87 insertions(+), 1 deletion(-) + create mode 100644 src/modules/bluetooth/hfaudioagent-null.c + create mode 100644 src/modules/bluetooth/hfaudioagent.h + +diff --git a/configure.ac b/configure.ac +index 4e9f97e..a35918c 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -1022,6 +1022,15 @@ AS_IF([test "x$HAVE_BLUEZ_4" = "x1" || test "x$HAVE_BLUEZ_5" = "x1"], HAVE_BLUEZ + AC_SUBST(HAVE_BLUEZ) + AM_CONDITIONAL([HAVE_BLUEZ], [test "x$HAVE_BLUEZ" = x1]) + ++## Headset profiles backend ## ++AC_ARG_WITH(bluetooth_headset_backend, AS_HELP_STRING([--with-bluetooth-headset-backend=],[Backend for Bluetooth headset profiles (null)])) ++if test -z "$with_bluetooth_headset_backend" ; then ++ BLUETOOTH_HEADSET_BACKEND=null ++else ++ BLUETOOTH_HEADSET_BACKEND=$with_bluetooth_headset_backend ++fi ++AC_SUBST(BLUETOOTH_HEADSET_BACKEND) ++ + #### UDEV support (optional) #### + + AC_ARG_ENABLE([udev], +@@ -1486,6 +1495,7 @@ echo " + Enable D-Bus: ${ENABLE_DBUS} + Enable BlueZ 4: ${ENABLE_BLUEZ_4} + Enable BlueZ 5: ${ENABLE_BLUEZ_5} ++ headset backed: ${BLUETOOTH_HEADSET_BACKEND} + Enable udev: ${ENABLE_UDEV} + Enable HAL->udev compat: ${ENABLE_HAL_COMPAT} + Enable systemd login: ${ENABLE_SYSTEMD} +diff --git a/src/Makefile.am b/src/Makefile.am +index e1808e6..2edce5f 100644 +--- a/src/Makefile.am ++++ b/src/Makefile.am +@@ -2074,7 +2074,9 @@ module_bluez4_device_la_CFLAGS = $(AM_CFLAGS) $(DBUS_CFLAGS) $(SBC_CFLAGS) + libbluez5_util_la_SOURCES = \ + modules/bluetooth/bluez5-util.c \ + modules/bluetooth/bluez5-util.h \ +- modules/bluetooth/a2dp-codecs.h ++ modules/bluetooth/a2dp-codecs.h \ ++ modules/bluetooth/hfaudioagent.h \ ++ modules/bluetooth/hfaudioagent-@BLUETOOTH_HEADSET_BACKEND@.c + libbluez5_util_la_LDFLAGS = -avoid-version + libbluez5_util_la_LIBADD = $(MODULE_LIBADD) $(DBUS_LIBS) + libbluez5_util_la_CFLAGS = $(AM_CFLAGS) $(DBUS_CFLAGS) +diff --git a/src/modules/bluetooth/bluez5-util.c b/src/modules/bluetooth/bluez5-util.c +index ae219ea..35c8adf 100644 +--- a/src/modules/bluetooth/bluez5-util.c ++++ b/src/modules/bluetooth/bluez5-util.c +@@ -34,6 +34,7 @@ + #include + + #include "a2dp-codecs.h" ++#include "hfaudioagent.h" + + #include "bluez5-util.h" + +@@ -87,6 +88,7 @@ struct pa_bluetooth_discovery { + pa_hashmap *devices; + pa_hashmap *transports; + ++ hf_audio_agent_data *hf_audio_agent; + PA_LLIST_HEAD(pa_dbus_pending, pending); + }; + +@@ -1574,6 +1576,7 @@ pa_bluetooth_discovery* pa_bluetooth_discovery_get(pa_core *c) { + + endpoint_init(y, PA_BLUETOOTH_PROFILE_A2DP_SINK); + endpoint_init(y, PA_BLUETOOTH_PROFILE_A2DP_SOURCE); ++ y->hf_audio_agent = hf_audio_agent_init(c); + + get_managed_objects(y); + +@@ -1615,6 +1618,9 @@ void pa_bluetooth_discovery_unref(pa_bluetooth_discovery *y) { + pa_hashmap_free(y->transports); + } + ++ if (y->hf_audio_agent) ++ hf_audio_agent_done(y->hf_audio_agent); ++ + if (y->connection) { + + if (y->matches_added) +diff --git a/src/modules/bluetooth/hfaudioagent-null.c b/src/modules/bluetooth/hfaudioagent-null.c +new file mode 100644 +index 0000000..96fca06 +--- /dev/null ++++ b/src/modules/bluetooth/hfaudioagent-null.c +@@ -0,0 +1,37 @@ ++/*** ++ This file is part of PulseAudio. ++ ++ Copyright 2013 João Paulo Rechi Vita ++ ++ PulseAudio is free software; you can redistribute it and/or modify ++ it under the terms of the GNU Lesser General Public License as ++ published by the Free Software Foundation; either version 2.1 of the ++ License, or (at your option) any later version. ++ ++ PulseAudio is distributed in the hope that it will be useful, but ++ WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ General Public License for more details. ++ ++ You should have received a copy of the GNU Lesser General Public ++ License along with PulseAudio; if not, write to the Free Software ++ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 ++ USA. ++***/ ++ ++#ifdef HAVE_CONFIG_H ++#include ++#endif ++ ++#include ++ ++#include "hfaudioagent.h" ++ ++hf_audio_agent_data *hf_audio_agent_init(pa_core *c) { ++ pa_log_debug("HandsfreeAudioAgent API support disabled"); ++ return NULL; ++} ++ ++void hf_audio_agent_done(hf_audio_agent_data *data) { ++ /* Nothing to do here */ ++} +diff --git a/src/modules/bluetooth/hfaudioagent.h b/src/modules/bluetooth/hfaudioagent.h +new file mode 100644 +index 0000000..2982034 +--- /dev/null ++++ b/src/modules/bluetooth/hfaudioagent.h +@@ -0,0 +1,31 @@ ++#ifndef foohfagenthfoo ++#define foohfagenthfoo ++ ++/*** ++ This file is part of PulseAudio. ++ ++ Copyright 2013 João Paulo Rechi Vita ++ ++ PulseAudio is free software; you can redistribute it and/or modify ++ it under the terms of the GNU Lesser General Public License as ++ published by the Free Software Foundation; either version 2.1 of the ++ License, or (at your option) any later version. ++ ++ PulseAudio is distributed in the hope that it will be useful, but ++ WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ General Public License for more details. ++ ++ You should have received a copy of the GNU Lesser General Public ++ License along with PulseAudio; if not, write to the Free Software ++ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 ++ USA. ++***/ ++ ++#include ++ ++typedef struct hf_audio_agent_data hf_audio_agent_data; ++ ++hf_audio_agent_data *hf_audio_agent_init(pa_core *c); ++void hf_audio_agent_done(hf_audio_agent_data *data); ++#endif diff --git a/meta-tizen/recipes-multimedia/pulseaudio/pulseaudio_5.0/0012-bluetooth-Create-Handsfree-Audio-Agent-oFono-backend.patch b/meta-tizen/recipes-multimedia/pulseaudio/pulseaudio_5.0/0012-bluetooth-Create-Handsfree-Audio-Agent-oFono-backend.patch new file mode 100644 index 0000000..39b938b --- /dev/null +++ b/meta-tizen/recipes-multimedia/pulseaudio/pulseaudio_5.0/0012-bluetooth-Create-Handsfree-Audio-Agent-oFono-backend.patch @@ -0,0 +1,161 @@ +From: =?utf-8?q?Jo=C3=A3o_Paulo_Rechi_Vita?= +Date: Tue, 9 Jul 2013 20:22:17 -0300 +Subject: bluetooth: Create Handsfree Audio Agent oFono backend + +Change-Id: I2d5793e997205a04b37b9389ab75fc8643adcece +--- + src/modules/bluetooth/hfaudioagent-ofono.c | 145 +++++++++++++++++++++++++++++ + 1 file changed, 145 insertions(+) + create mode 100644 src/modules/bluetooth/hfaudioagent-ofono.c + +diff --git a/src/modules/bluetooth/hfaudioagent-ofono.c b/src/modules/bluetooth/hfaudioagent-ofono.c +new file mode 100644 +index 0000000..af78d4d +--- /dev/null ++++ b/src/modules/bluetooth/hfaudioagent-ofono.c +@@ -0,0 +1,145 @@ ++/*** ++ This file is part of PulseAudio. ++ ++ Copyright 2013 João Paulo Rechi Vita ++ ++ PulseAudio is free software; you can redistribute it and/or modify ++ it under the terms of the GNU Lesser General Public License as ++ published by the Free Software Foundation; either version 2.1 of the ++ License, or (at your option) any later version. ++ ++ PulseAudio is distributed in the hope that it will be useful, but ++ WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ General Public License for more details. ++ ++ You should have received a copy of the GNU Lesser General Public ++ License along with PulseAudio; if not, write to the Free Software ++ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 ++ USA. ++***/ ++ ++#ifdef HAVE_CONFIG_H ++#include ++#endif ++ ++#include ++#include ++ ++#include "hfaudioagent.h" ++ ++#define OFONO_SERVICE "org.ofono" ++#define HF_AUDIO_AGENT_INTERFACE OFONO_SERVICE ".HandsfreeAudioAgent" ++ ++#define HF_AUDIO_AGENT_PATH "/HandsfreeAudioAgent" ++ ++#define HF_AUDIO_AGENT_XML \ ++ DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE \ ++ "" \ ++ " " \ ++ " " \ ++ " " \ ++ " " \ ++ " " \ ++ " " \ ++ " " \ ++ " " \ ++ " " \ ++ " " \ ++ " " \ ++ " " \ ++ " " \ ++ " " \ ++ "" ++ ++struct hf_audio_agent_data { ++ pa_core *core; ++ pa_dbus_connection *connection; ++}; ++ ++static DBusMessage *hf_audio_agent_release(DBusConnection *c, DBusMessage *m, void *data) { ++ DBusMessage *r = dbus_message_new_error(m, "org.ofono.Error.NotImplemented", "Operation is not implemented"); ++ return r; ++} ++ ++static DBusMessage *hf_audio_agent_new_connection(DBusConnection *c, DBusMessage *m, void *data) { ++ DBusMessage *r = dbus_message_new_error(m, "org.ofono.Error.NotImplemented", "Operation is not implemented"); ++ return r; ++} ++ ++static DBusHandlerResult hf_audio_agent_handler(DBusConnection *c, DBusMessage *m, void *data) { ++ hf_audio_agent_data *hfdata = data; ++ DBusMessage *r = NULL; ++ const char *path, *interface, *member; ++ ++ pa_assert(hfdata); ++ ++ path = dbus_message_get_path(m); ++ interface = dbus_message_get_interface(m); ++ member = dbus_message_get_member(m); ++ ++ if (!pa_streq(path, HF_AUDIO_AGENT_PATH)) ++ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; ++ ++ pa_log_debug("dbus: path=%s, interface=%s, member=%s", path, interface, member); ++ ++ if (dbus_message_is_method_call(m, "org.freedesktop.DBus.Introspectable", "Introspect")) { ++ const char *xml = HF_AUDIO_AGENT_XML; ++ ++ pa_assert_se(r = dbus_message_new_method_return(m)); ++ pa_assert_se(dbus_message_append_args(r, DBUS_TYPE_STRING, &xml, DBUS_TYPE_INVALID)); ++ ++ } else if (dbus_message_is_method_call(m, HF_AUDIO_AGENT_INTERFACE, "NewConnection")) ++ r = hf_audio_agent_new_connection(c, m, data); ++ else if (dbus_message_is_method_call(m, HF_AUDIO_AGENT_INTERFACE, "Release")) ++ r = hf_audio_agent_release(c, m, data); ++ else ++ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; ++ ++ if (r) { ++ pa_assert_se(dbus_connection_send(pa_dbus_connection_get(hfdata->connection), r, NULL)); ++ dbus_message_unref(r); ++ } ++ ++ return DBUS_HANDLER_RESULT_HANDLED; ++} ++ ++hf_audio_agent_data *hf_audio_agent_init(pa_core *c) { ++ hf_audio_agent_data *hfdata; ++ DBusError err; ++ static const DBusObjectPathVTable vtable_hf_audio_agent = { ++ .message_function = hf_audio_agent_handler, ++ }; ++ ++ pa_assert(c); ++ ++ hfdata = pa_xnew0(hf_audio_agent_data, 1); ++ hfdata->core = c; ++ ++ dbus_error_init(&err); ++ ++ if (!(hfdata->connection = pa_dbus_bus_get(c, DBUS_BUS_SYSTEM, &err))) { ++ pa_log("Failed to get D-Bus connection: %s", err.message); ++ dbus_error_free(&err); ++ return NULL; ++ } ++ ++ pa_assert_se(dbus_connection_register_object_path(pa_dbus_connection_get(hfdata->connection), HF_AUDIO_AGENT_PATH, ++ &vtable_hf_audio_agent, hfdata)); ++ ++ return hfdata; ++} ++ ++void hf_audio_agent_done(hf_audio_agent_data *data) { ++ hf_audio_agent_data *hfdata = data; ++ ++ pa_assert(hfdata); ++ ++ if (hfdata->connection) { ++ dbus_connection_unregister_object_path(pa_dbus_connection_get(hfdata->connection), HF_AUDIO_AGENT_PATH); ++ ++ pa_dbus_connection_unref(hfdata->connection); ++ } ++ ++ pa_xfree(hfdata); ++} diff --git a/meta-tizen/recipes-multimedia/pulseaudio/pulseaudio_5.0/0013-bluetooth-Monitor-D-Bus-signals.patch b/meta-tizen/recipes-multimedia/pulseaudio/pulseaudio_5.0/0013-bluetooth-Monitor-D-Bus-signals.patch new file mode 100644 index 0000000..0a93d5c --- /dev/null +++ b/meta-tizen/recipes-multimedia/pulseaudio/pulseaudio_5.0/0013-bluetooth-Monitor-D-Bus-signals.patch @@ -0,0 +1,99 @@ +From: =?utf-8?q?Jo=C3=A3o_Paulo_Rechi_Vita?= +Date: Wed, 10 Jul 2013 12:18:07 -0300 +Subject: bluetooth: Monitor D-Bus signals + +Change-Id: Ibfd4ab9acaf49df0fdfaff55609c560ab31f4df4 +--- + src/modules/bluetooth/hfaudioagent-ofono.c | 48 ++++++++++++++++++++++++++++++ + 1 file changed, 48 insertions(+) + +diff --git a/src/modules/bluetooth/hfaudioagent-ofono.c b/src/modules/bluetooth/hfaudioagent-ofono.c +index af78d4d..0e3ffc4 100644 +--- a/src/modules/bluetooth/hfaudioagent-ofono.c ++++ b/src/modules/bluetooth/hfaudioagent-ofono.c +@@ -30,6 +30,7 @@ + + #define OFONO_SERVICE "org.ofono" + #define HF_AUDIO_AGENT_INTERFACE OFONO_SERVICE ".HandsfreeAudioAgent" ++#define HF_AUDIO_MANAGER_INTERFACE OFONO_SERVICE ".HandsfreeAudioManager" + + #define HF_AUDIO_AGENT_PATH "/HandsfreeAudioAgent" + +@@ -55,8 +56,18 @@ + struct hf_audio_agent_data { + pa_core *core; + pa_dbus_connection *connection; ++ ++ bool filter_added; ++ pa_hashmap *hf_audio_cards; + }; + ++static DBusHandlerResult filter_cb(DBusConnection *bus, DBusMessage *m, void *data) { ++ pa_assert(bus); ++ pa_assert(m); ++ ++ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; ++} ++ + static DBusMessage *hf_audio_agent_release(DBusConnection *c, DBusMessage *m, void *data) { + DBusMessage *r = dbus_message_new_error(m, "org.ofono.Error.NotImplemented", "Operation is not implemented"); + return r; +@@ -115,6 +126,7 @@ hf_audio_agent_data *hf_audio_agent_init(pa_core *c) { + + hfdata = pa_xnew0(hf_audio_agent_data, 1); + hfdata->core = c; ++ hfdata->hf_audio_cards = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func); + + dbus_error_init(&err); + +@@ -124,6 +136,25 @@ hf_audio_agent_data *hf_audio_agent_init(pa_core *c) { + return NULL; + } + ++ /* dynamic detection of handsfree audio cards */ ++ if (!dbus_connection_add_filter(pa_dbus_connection_get(hfdata->connection), filter_cb, hfdata, NULL)) { ++ pa_log_error("Failed to add filter function"); ++ hf_audio_agent_done(hfdata); ++ return NULL; ++ } ++ hfdata->filter_added = true; ++ ++ if (pa_dbus_add_matches(pa_dbus_connection_get(hfdata->connection), &err, ++ "type='signal',sender='org.freedesktop.DBus',interface='org.freedesktop.DBus',member='NameOwnerChanged'," ++ "arg0='" OFONO_SERVICE "'", ++ "type='signal',sender='" OFONO_SERVICE "',interface='" HF_AUDIO_MANAGER_INTERFACE "',member='CardAdded'", ++ "type='signal',sender='" OFONO_SERVICE "',interface='" HF_AUDIO_MANAGER_INTERFACE "',member='CardRemoved'", ++ NULL) < 0) { ++ pa_log("Failed to add oFono D-Bus matches: %s", err.message); ++ hf_audio_agent_done(hfdata); ++ return NULL; ++ } ++ + pa_assert_se(dbus_connection_register_object_path(pa_dbus_connection_get(hfdata->connection), HF_AUDIO_AGENT_PATH, + &vtable_hf_audio_agent, hfdata)); + +@@ -135,7 +166,24 @@ void hf_audio_agent_done(hf_audio_agent_data *data) { + + pa_assert(hfdata); + ++ if (hfdata->hf_audio_cards) { ++ pa_hashmap_free(hfdata->hf_audio_cards, NULL); ++ hfdata->hf_audio_cards = NULL; ++ } ++ + if (hfdata->connection) { ++ ++ pa_dbus_remove_matches( ++ pa_dbus_connection_get(hfdata->connection), ++ "type='signal',sender='org.freedesktop.DBus',interface='org.freedesktop.DBus',member='NameOwnerChanged'," ++ "arg0='" OFONO_SERVICE "'", ++ "type='signal',sender='" OFONO_SERVICE "',interface='" HF_AUDIO_MANAGER_INTERFACE "',member='CardAdded'", ++ "type='signal',sender='" OFONO_SERVICE "',interface='" HF_AUDIO_MANAGER_INTERFACE "',member='CardRemoved'", ++ NULL); ++ ++ if (hfdata->filter_added) ++ dbus_connection_remove_filter(pa_dbus_connection_get(hfdata->connection), filter_cb, hfdata); ++ + dbus_connection_unregister_object_path(pa_dbus_connection_get(hfdata->connection), HF_AUDIO_AGENT_PATH); + + pa_dbus_connection_unref(hfdata->connection); diff --git a/meta-tizen/recipes-multimedia/pulseaudio/pulseaudio_5.0/0014-bluetooth-Create-pa_bluetooth_dbus_send_and_add_to_p.patch b/meta-tizen/recipes-multimedia/pulseaudio/pulseaudio_5.0/0014-bluetooth-Create-pa_bluetooth_dbus_send_and_add_to_p.patch new file mode 100644 index 0000000..c9d893c --- /dev/null +++ b/meta-tizen/recipes-multimedia/pulseaudio/pulseaudio_5.0/0014-bluetooth-Create-pa_bluetooth_dbus_send_and_add_to_p.patch @@ -0,0 +1,51 @@ +From: =?utf-8?q?Jo=C3=A3o_Paulo_Rechi_Vita?= +Date: Wed, 10 Jul 2013 13:00:16 -0300 +Subject: bluetooth: Create pa_bluetooth_dbus_send_and_add_to_pending() for + oFono backend + +Change-Id: I9b6ea22d6bd1059e5d7b3a12ec13b2768cacce0d +--- + src/modules/bluetooth/hfaudioagent-ofono.c | 21 +++++++++++++++++++++ + 1 file changed, 21 insertions(+) + +diff --git a/src/modules/bluetooth/hfaudioagent-ofono.c b/src/modules/bluetooth/hfaudioagent-ofono.c +index 0e3ffc4..38975b2 100644 +--- a/src/modules/bluetooth/hfaudioagent-ofono.c ++++ b/src/modules/bluetooth/hfaudioagent-ofono.c +@@ -59,8 +59,27 @@ struct hf_audio_agent_data { + + bool filter_added; + pa_hashmap *hf_audio_cards; ++ ++ PA_LLIST_HEAD(pa_dbus_pending, pending); + }; + ++static pa_dbus_pending* pa_bluetooth_dbus_send_and_add_to_pending(hf_audio_agent_data *hfdata, DBusMessage *m, ++ DBusPendingCallNotifyFunction func, void *call_data) { ++ pa_dbus_pending *p; ++ DBusPendingCall *call; ++ ++ pa_assert(hfdata); ++ pa_assert(m); ++ ++ pa_assert_se(dbus_connection_send_with_reply(pa_dbus_connection_get(hfdata->connection), m, &call, -1)); ++ ++ p = pa_dbus_pending_new(pa_dbus_connection_get(hfdata->connection), m, call, hfdata, call_data); ++ PA_LLIST_PREPEND(pa_dbus_pending, hfdata->pending, p); ++ dbus_pending_call_set_notify(call, func, p, NULL); ++ ++ return p; ++} ++ + static DBusHandlerResult filter_cb(DBusConnection *bus, DBusMessage *m, void *data) { + pa_assert(bus); + pa_assert(m); +@@ -166,6 +185,8 @@ void hf_audio_agent_done(hf_audio_agent_data *data) { + + pa_assert(hfdata); + ++ pa_dbus_free_pending_list(&hfdata->pending); ++ + if (hfdata->hf_audio_cards) { + pa_hashmap_free(hfdata->hf_audio_cards, NULL); + hfdata->hf_audio_cards = NULL; diff --git a/meta-tizen/recipes-multimedia/pulseaudio/pulseaudio_5.0/0015-bluetooth-Register-Unregister-Handsfree-Audio-Agent-.patch b/meta-tizen/recipes-multimedia/pulseaudio/pulseaudio_5.0/0015-bluetooth-Register-Unregister-Handsfree-Audio-Agent-.patch new file mode 100644 index 0000000..931069c --- /dev/null +++ b/meta-tizen/recipes-multimedia/pulseaudio/pulseaudio_5.0/0015-bluetooth-Register-Unregister-Handsfree-Audio-Agent-.patch @@ -0,0 +1,172 @@ +From: =?utf-8?q?Jo=C3=A3o_Paulo_Rechi_Vita?= +Date: Tue, 9 Jul 2013 20:59:12 -0300 +Subject: bluetooth: Register/Unregister Handsfree Audio Agent with oFono + +Register as a HandsfreeAudioAgent with oFono during backend +initialization and unregiter during backend finalization. This commit +also adds a check when receiving method calls or signals to make sure +the sender matches with the D-Bus service we're registered with. + +Change-Id: I0a49935702ffb52d6d2ba1baded8eb568262c5b1 +--- + src/modules/bluetooth/hfaudioagent-ofono.c | 107 ++++++++++++++++++++++++++++- + 1 file changed, 105 insertions(+), 2 deletions(-) + +diff --git a/src/modules/bluetooth/hfaudioagent-ofono.c b/src/modules/bluetooth/hfaudioagent-ofono.c +index 38975b2..de58e7d 100644 +--- a/src/modules/bluetooth/hfaudioagent-ofono.c ++++ b/src/modules/bluetooth/hfaudioagent-ofono.c +@@ -28,6 +28,9 @@ + + #include "hfaudioagent.h" + ++#define HFP_AUDIO_CODEC_CVSD 0x01 ++#define HFP_AUDIO_CODEC_MSBC 0x02 ++ + #define OFONO_SERVICE "org.ofono" + #define HF_AUDIO_AGENT_INTERFACE OFONO_SERVICE ".HandsfreeAudioAgent" + #define HF_AUDIO_MANAGER_INTERFACE OFONO_SERVICE ".HandsfreeAudioManager" +@@ -58,6 +61,7 @@ struct hf_audio_agent_data { + pa_dbus_connection *connection; + + bool filter_added; ++ char *ofono_bus_id; + pa_hashmap *hf_audio_cards; + + PA_LLIST_HEAD(pa_dbus_pending, pending); +@@ -80,20 +84,115 @@ static pa_dbus_pending* pa_bluetooth_dbus_send_and_add_to_pending(hf_audio_agent + return p; + } + ++static void hf_audio_agent_register_reply(DBusPendingCall *pending, void *userdata) { ++ DBusMessage *r; ++ pa_dbus_pending *p; ++ hf_audio_agent_data *hfdata; ++ ++ pa_assert_se(p = userdata); ++ pa_assert_se(hfdata = p->context_data); ++ pa_assert_se(r = dbus_pending_call_steal_reply(pending)); ++ ++ if (dbus_message_get_type(r) == DBUS_MESSAGE_TYPE_ERROR) { ++ pa_log_error("Failed to register as a handsfree audio agent with ofono: %s: %s", ++ dbus_message_get_error_name(r), pa_dbus_get_error_message(r)); ++ goto finish; ++ } ++ ++ hfdata->ofono_bus_id = pa_xstrdup(dbus_message_get_sender(r)); ++ ++ /* TODO: List all HandsfreeAudioCard objects */ ++ ++finish: ++ dbus_message_unref(r); ++ ++ PA_LLIST_REMOVE(pa_dbus_pending, hfdata->pending, p); ++ pa_dbus_pending_free(p); ++} ++ ++static void hf_audio_agent_register(hf_audio_agent_data *hfdata) { ++ DBusMessage *m; ++ unsigned char codecs[2]; ++ const unsigned char *pcodecs = codecs; ++ int ncodecs = 0; ++ const char *path = HF_AUDIO_AGENT_PATH; ++ ++ pa_assert(hfdata); ++ ++ pa_assert_se(m = dbus_message_new_method_call(OFONO_SERVICE, "/", HF_AUDIO_MANAGER_INTERFACE, "Register")); ++ ++ codecs[ncodecs++] = HFP_AUDIO_CODEC_CVSD; ++ codecs[ncodecs++] = HFP_AUDIO_CODEC_MSBC; ++ ++ pa_assert_se(dbus_message_append_args(m, DBUS_TYPE_OBJECT_PATH, &path, DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE, &pcodecs, ncodecs, ++ DBUS_TYPE_INVALID)); ++ ++ pa_bluetooth_dbus_send_and_add_to_pending(hfdata, m, hf_audio_agent_register_reply, NULL); ++} ++ ++static void hf_audio_agent_unregister(hf_audio_agent_data *hfdata) { ++ DBusMessage *m; ++ const char *path = HF_AUDIO_AGENT_PATH; ++ ++ pa_assert(hfdata); ++ pa_assert(hfdata->connection); ++ ++ if (hfdata->ofono_bus_id) { ++ pa_assert_se(m = dbus_message_new_method_call(hfdata->ofono_bus_id, "/", HF_AUDIO_MANAGER_INTERFACE, "Unregister")); ++ pa_assert_se(dbus_message_append_args(m, DBUS_TYPE_OBJECT_PATH, &path, DBUS_TYPE_INVALID)); ++ pa_assert_se(dbus_connection_send(pa_dbus_connection_get(hfdata->connection), m, NULL)); ++ ++ pa_xfree(hfdata->ofono_bus_id); ++ hfdata->ofono_bus_id = NULL; ++ } ++} ++ + static DBusHandlerResult filter_cb(DBusConnection *bus, DBusMessage *m, void *data) { ++ const char *sender; ++ hf_audio_agent_data *hfdata = data; ++ + pa_assert(bus); + pa_assert(m); ++ pa_assert(hfdata); ++ ++ sender = dbus_message_get_sender(m); ++ if (!pa_safe_streq(hfdata->ofono_bus_id, sender) && !pa_streq("org.freedesktop.DBus", sender)) ++ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + } + + static DBusMessage *hf_audio_agent_release(DBusConnection *c, DBusMessage *m, void *data) { +- DBusMessage *r = dbus_message_new_error(m, "org.ofono.Error.NotImplemented", "Operation is not implemented"); ++ DBusMessage *r; ++ const char *sender; ++ hf_audio_agent_data *hfdata = data; ++ ++ pa_assert(hfdata); ++ ++ sender = dbus_message_get_sender(m); ++ if (!pa_streq(hfdata->ofono_bus_id, sender)) { ++ pa_assert_se(r = dbus_message_new_error(m, "org.ofono.Error.NotAllowed", "Operation is not allowed by this sender")); ++ return r; ++ } ++ ++ r = dbus_message_new_error(m, "org.ofono.Error.NotImplemented", "Operation is not implemented"); + return r; + } + + static DBusMessage *hf_audio_agent_new_connection(DBusConnection *c, DBusMessage *m, void *data) { +- DBusMessage *r = dbus_message_new_error(m, "org.ofono.Error.NotImplemented", "Operation is not implemented"); ++ DBusMessage *r; ++ const char *sender; ++ hf_audio_agent_data *hfdata = data; ++ ++ pa_assert(hfdata); ++ ++ sender = dbus_message_get_sender(m); ++ if (!pa_streq(hfdata->ofono_bus_id, sender)) { ++ pa_assert_se(r = dbus_message_new_error(m, "org.ofono.Error.NotAllowed", "Operation is not allowed by this sender")); ++ return r; ++ } ++ ++ r = dbus_message_new_error(m, "org.ofono.Error.NotImplemented", "Operation is not implemented"); + return r; + } + +@@ -177,6 +276,8 @@ hf_audio_agent_data *hf_audio_agent_init(pa_core *c) { + pa_assert_se(dbus_connection_register_object_path(pa_dbus_connection_get(hfdata->connection), HF_AUDIO_AGENT_PATH, + &vtable_hf_audio_agent, hfdata)); + ++ hf_audio_agent_register(hfdata); ++ + return hfdata; + } + +@@ -205,6 +306,8 @@ void hf_audio_agent_done(hf_audio_agent_data *data) { + if (hfdata->filter_added) + dbus_connection_remove_filter(pa_dbus_connection_get(hfdata->connection), filter_cb, hfdata); + ++ hf_audio_agent_unregister(hfdata); ++ + dbus_connection_unregister_object_path(pa_dbus_connection_get(hfdata->connection), HF_AUDIO_AGENT_PATH); + + pa_dbus_connection_unref(hfdata->connection); diff --git a/meta-tizen/recipes-multimedia/pulseaudio/pulseaudio_5.0/0016-bluetooth-List-HandsfreeAudioCard-objects-from-oFono.patch b/meta-tizen/recipes-multimedia/pulseaudio/pulseaudio_5.0/0016-bluetooth-List-HandsfreeAudioCard-objects-from-oFono.patch new file mode 100644 index 0000000..d943ce4 --- /dev/null +++ b/meta-tizen/recipes-multimedia/pulseaudio/pulseaudio_5.0/0016-bluetooth-List-HandsfreeAudioCard-objects-from-oFono.patch @@ -0,0 +1,98 @@ +From: =?utf-8?q?Jo=C3=A3o_Paulo_Rechi_Vita?= +Date: Tue, 9 Jul 2013 21:03:28 -0300 +Subject: bluetooth: List HandsfreeAudioCard objects from oFono + +Change-Id: Idb2423d8b5640486976df5347e2f7d05a9e6dd71 +--- + src/modules/bluetooth/hfaudioagent-ofono.c | 71 +++++++++++++++++++++++++++++- + 1 file changed, 70 insertions(+), 1 deletion(-) + +diff --git a/src/modules/bluetooth/hfaudioagent-ofono.c b/src/modules/bluetooth/hfaudioagent-ofono.c +index de58e7d..6b27f80 100644 +--- a/src/modules/bluetooth/hfaudioagent-ofono.c ++++ b/src/modules/bluetooth/hfaudioagent-ofono.c +@@ -84,6 +84,75 @@ static pa_dbus_pending* pa_bluetooth_dbus_send_and_add_to_pending(hf_audio_agent + return p; + } + ++static void hf_audio_agent_get_cards_reply(DBusPendingCall *pending, void *userdata) { ++ DBusMessage *r; ++ pa_dbus_pending *p; ++ hf_audio_agent_data *hfdata; ++ DBusMessageIter i, array_i, struct_i, props_i; ++ char c; ++ ++ pa_assert_se(p = userdata); ++ pa_assert_se(hfdata = p->context_data); ++ pa_assert_se(r = dbus_pending_call_steal_reply(pending)); ++ ++ if (dbus_message_get_type(r) == DBUS_MESSAGE_TYPE_ERROR) { ++ pa_log_error("Failed to get a list of handsfree audio cards from ofono: %s: %s", ++ dbus_message_get_error_name(r), pa_dbus_get_error_message(r)); ++ goto finish; ++ } ++ ++ dbus_message_iter_init(r, &i); ++ if ((c = dbus_message_iter_get_arg_type(&i)) != DBUS_TYPE_ARRAY) { ++ pa_log_error("Invalid arguments in GetCards() reply: expected \'a\', received \'%c\'", c); ++ goto finish; ++ } ++ ++ dbus_message_iter_recurse(&i, &array_i); ++ while (dbus_message_iter_get_arg_type(&array_i) != DBUS_TYPE_INVALID) { ++ const char *path; ++ ++ if ((c = dbus_message_iter_get_arg_type(&array_i)) != DBUS_TYPE_STRUCT) { ++ pa_log_error("Invalid arguments in GetCards() reply: expected \'r\', received \'%c\'", c); ++ goto finish; ++ } ++ ++ dbus_message_iter_recurse(&array_i, &struct_i); ++ if ((c = dbus_message_iter_get_arg_type(&struct_i)) != DBUS_TYPE_OBJECT_PATH) { ++ pa_log_error("Invalid arguments in GetCards() reply: expected \'o\', received \'%c\'", c); ++ goto finish; ++ } ++ ++ dbus_message_iter_get_basic(&struct_i, &path); ++ ++ dbus_message_iter_next(&struct_i); ++ if ((c = dbus_message_iter_get_arg_type(&struct_i)) != DBUS_TYPE_ARRAY) { ++ pa_log_error("Invalid arguments in GetCards() reply: expected \'a\', received \'%c\'", c); ++ goto finish; ++ } ++ ++ dbus_message_iter_recurse(&struct_i, &props_i); ++ ++ /* TODO: Parse HandsfreeAudioCard properties */ ++ ++ dbus_message_iter_next(&array_i); ++ } ++ ++finish: ++ dbus_message_unref(r); ++ ++ PA_LLIST_REMOVE(pa_dbus_pending, hfdata->pending, p); ++ pa_dbus_pending_free(p); ++} ++ ++static void hf_audio_agent_get_cards(hf_audio_agent_data *hfdata) { ++ DBusMessage *m; ++ ++ pa_assert(hfdata); ++ ++ pa_assert_se(m = dbus_message_new_method_call(OFONO_SERVICE, "/", HF_AUDIO_MANAGER_INTERFACE, "GetCards")); ++ pa_bluetooth_dbus_send_and_add_to_pending(hfdata, m, hf_audio_agent_get_cards_reply, NULL); ++} ++ + static void hf_audio_agent_register_reply(DBusPendingCall *pending, void *userdata) { + DBusMessage *r; + pa_dbus_pending *p; +@@ -101,7 +170,7 @@ static void hf_audio_agent_register_reply(DBusPendingCall *pending, void *userda + + hfdata->ofono_bus_id = pa_xstrdup(dbus_message_get_sender(r)); + +- /* TODO: List all HandsfreeAudioCard objects */ ++ hf_audio_agent_get_cards(hfdata); + + finish: + dbus_message_unref(r); diff --git a/meta-tizen/recipes-multimedia/pulseaudio/pulseaudio_5.0/0017-bluetooth-Parse-HandsfreeAudioCard-properties.patch b/meta-tizen/recipes-multimedia/pulseaudio/pulseaudio_5.0/0017-bluetooth-Parse-HandsfreeAudioCard-properties.patch new file mode 100644 index 0000000..f7a2174 --- /dev/null +++ b/meta-tizen/recipes-multimedia/pulseaudio/pulseaudio_5.0/0017-bluetooth-Parse-HandsfreeAudioCard-properties.patch @@ -0,0 +1,192 @@ +From: =?utf-8?q?Jo=C3=A3o_Paulo_Rechi_Vita?= +Date: Tue, 9 Jul 2013 21:37:26 -0300 +Subject: bluetooth: Parse HandsfreeAudioCard properties + +Change-Id: I37600ca3ab60d1b82608f4e395fa427fdaf62164 +--- + src/modules/bluetooth/hfaudioagent-ofono.c | 128 ++++++++++++++++++++++++++++- + 1 file changed, 126 insertions(+), 2 deletions(-) + +diff --git a/src/modules/bluetooth/hfaudioagent-ofono.c b/src/modules/bluetooth/hfaudioagent-ofono.c +index 6b27f80..2e16a48 100644 +--- a/src/modules/bluetooth/hfaudioagent-ofono.c ++++ b/src/modules/bluetooth/hfaudioagent-ofono.c +@@ -25,6 +25,9 @@ + + #include + #include ++#include ++ ++#include "bluez5-util.h" + + #include "hfaudioagent.h" + +@@ -56,9 +59,21 @@ + " " \ + "" + ++typedef struct hf_audio_card { ++ char *path; ++ char *remote; ++ char *local; ++ ++ int fd; ++ uint8_t codec; ++ ++ pa_bluetooth_transport *transport; ++} hf_audio_card; ++ + struct hf_audio_agent_data { + pa_core *core; + pa_dbus_connection *connection; ++ pa_bluetooth_discovery *discovery; + + bool filter_added; + char *ofono_bus_id; +@@ -84,6 +99,111 @@ static pa_dbus_pending* pa_bluetooth_dbus_send_and_add_to_pending(hf_audio_agent + return p; + } + ++static hf_audio_card *hf_audio_card_new(hf_audio_agent_data *hfdata, const char *path) { ++ hf_audio_card *hfac = pa_xnew0(hf_audio_card, 1); ++ ++ hfac->path = pa_xstrdup(path); ++ hfac->fd = -1; ++ ++ return hfac; ++} ++ ++static void hf_audio_card_free(void *data) { ++ hf_audio_card *hfac = data; ++ ++ pa_assert(hfac); ++ ++ pa_bluetooth_transport_free(hfac->transport); ++ pa_xfree(hfac->path); ++ pa_xfree(hfac->remote); ++ pa_xfree(hfac->local); ++ pa_xfree(hfac); ++} ++ ++static int hf_audio_agent_transport_acquire(pa_bluetooth_transport *t, bool optional, size_t *imtu, size_t *omtu) { ++ return -1; ++} ++ ++static void hf_audio_agent_transport_release(pa_bluetooth_transport *t) { ++} ++ ++static void hf_audio_agent_card_found(hf_audio_agent_data *hfdata, const char *path, DBusMessageIter *props_i) { ++ DBusMessageIter i, value_i; ++ const char *key, *value; ++ hf_audio_card *hfac; ++ pa_bluetooth_device *d; ++ ++ pa_assert(hfdata); ++ pa_assert(path); ++ pa_assert(props_i); ++ ++ pa_log_debug("New HF card found: %s", path); ++ ++ hfac = hf_audio_card_new(hfdata, path); ++ ++ while (dbus_message_iter_get_arg_type(props_i) != DBUS_TYPE_INVALID) { ++ char c; ++ ++ if ((c = dbus_message_iter_get_arg_type(props_i)) != DBUS_TYPE_DICT_ENTRY) { ++ pa_log_error("Invalid properties for %s: expected \'e\', received \'%c\'", path, c); ++ goto fail; ++ } ++ ++ dbus_message_iter_recurse(props_i, &i); ++ ++ if ((c = dbus_message_iter_get_arg_type(&i)) != DBUS_TYPE_STRING) { ++ pa_log_error("Invalid properties for %s: expected \'s\', received \'%c\'", path, c); ++ goto fail; ++ } ++ ++ dbus_message_iter_get_basic(&i, &key); ++ dbus_message_iter_next(&i); ++ ++ if ((c = dbus_message_iter_get_arg_type(&i)) != DBUS_TYPE_VARIANT) { ++ pa_log_error("Invalid properties for %s: expected \'v\', received \'%c\'", path, c); ++ goto fail; ++ } ++ ++ dbus_message_iter_recurse(&i, &value_i); ++ ++ if ((c = dbus_message_iter_get_arg_type(&value_i)) != DBUS_TYPE_STRING) { ++ pa_log_error("Invalid properties for %s: expected \'s\', received \'%c\'", path, c); ++ goto fail; ++ } ++ ++ dbus_message_iter_get_basic(&value_i, &value); ++ ++ if (pa_streq(key, "RemoteAddress")) ++ hfac->remote = pa_xstrdup(value); ++ else if (pa_streq(key, "LocalAddress")) ++ hfac->local = pa_xstrdup(value); ++ ++ pa_log_debug("%s: %s", key, value); ++ ++ dbus_message_iter_next(props_i); ++ } ++ ++ pa_hashmap_put(hfdata->hf_audio_cards, hfac->path, hfac); ++ ++ d = pa_bluetooth_discovery_get_device_by_address(hfdata->discovery, hfac->remote, hfac->local); ++ if (d) { ++ hfac->transport = pa_bluetooth_transport_new(d, hfdata->ofono_bus_id, path, PA_BLUETOOTH_PROFILE_HEADSET_AUDIO_GATEWAY, NULL, 0); ++ hfac->transport->acquire = hf_audio_agent_transport_acquire; ++ hfac->transport->release = hf_audio_agent_transport_release; ++ hfac->transport->userdata = hfdata; ++ ++ d->transports[PA_BLUETOOTH_PROFILE_HEADSET_AUDIO_GATEWAY] = hfac->transport; ++ ++ pa_bluetooth_transport_put(hfac->transport); ++ } else ++ pa_log_error("Device doesnt exist for %s", path); ++ ++ return; ++ ++fail: ++ pa_xfree(hfac); ++} ++ + static void hf_audio_agent_get_cards_reply(DBusPendingCall *pending, void *userdata) { + DBusMessage *r; + pa_dbus_pending *p; +@@ -132,7 +252,7 @@ static void hf_audio_agent_get_cards_reply(DBusPendingCall *pending, void *userd + + dbus_message_iter_recurse(&struct_i, &props_i); + +- /* TODO: Parse HandsfreeAudioCard properties */ ++ hf_audio_agent_card_found(hfdata, path, &props_i); + + dbus_message_iter_next(&array_i); + } +@@ -314,6 +434,7 @@ hf_audio_agent_data *hf_audio_agent_init(pa_core *c) { + hfdata = pa_xnew0(hf_audio_agent_data, 1); + hfdata->core = c; + hfdata->hf_audio_cards = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func); ++ hfdata->discovery = pa_shared_get(c, "bluetooth-discovery"); + + dbus_error_init(&err); + +@@ -358,7 +479,7 @@ void hf_audio_agent_done(hf_audio_agent_data *data) { + pa_dbus_free_pending_list(&hfdata->pending); + + if (hfdata->hf_audio_cards) { +- pa_hashmap_free(hfdata->hf_audio_cards, NULL); ++ pa_hashmap_free(hfdata->hf_audio_cards, hf_audio_card_free); + hfdata->hf_audio_cards = NULL; + } + +@@ -382,5 +503,8 @@ void hf_audio_agent_done(hf_audio_agent_data *data) { + pa_dbus_connection_unref(hfdata->connection); + } + ++ if (hfdata->discovery) ++ hfdata->discovery = NULL; ++ + pa_xfree(hfdata); + } diff --git a/meta-tizen/recipes-multimedia/pulseaudio/pulseaudio_5.0/0018-bluetooth-Implement-transport-acquire-for-hf_audio_a.patch b/meta-tizen/recipes-multimedia/pulseaudio/pulseaudio_5.0/0018-bluetooth-Implement-transport-acquire-for-hf_audio_a.patch new file mode 100644 index 0000000..c3815cc --- /dev/null +++ b/meta-tizen/recipes-multimedia/pulseaudio/pulseaudio_5.0/0018-bluetooth-Implement-transport-acquire-for-hf_audio_a.patch @@ -0,0 +1,48 @@ +From: =?utf-8?q?Jo=C3=A3o_Paulo_Rechi_Vita?= +Date: Tue, 9 Jul 2013 22:22:28 -0300 +Subject: bluetooth: Implement transport acquire for hf_audio_agent transports + +Change-Id: Ia092e75c51f79d1a594ba6dacda55ec03a5dae93 +--- + src/modules/bluetooth/hfaudioagent-ofono.c | 28 +++++++++++++++++++++++++++- + 1 file changed, 27 insertions(+), 1 deletion(-) + +diff --git a/src/modules/bluetooth/hfaudioagent-ofono.c b/src/modules/bluetooth/hfaudioagent-ofono.c +index 2e16a48..3684bed 100644 +--- a/src/modules/bluetooth/hfaudioagent-ofono.c ++++ b/src/modules/bluetooth/hfaudioagent-ofono.c +@@ -121,7 +121,33 @@ static void hf_audio_card_free(void *data) { + } + + static int hf_audio_agent_transport_acquire(pa_bluetooth_transport *t, bool optional, size_t *imtu, size_t *omtu) { +- return -1; ++ hf_audio_agent_data *hfdata = t->userdata; ++ hf_audio_card *hfac = pa_hashmap_get(hfdata->hf_audio_cards, t->path); ++ ++ if (!optional) { ++ DBusMessage *m; ++ ++ pa_assert_se(m = dbus_message_new_method_call(t->owner, t->path, "org.ofono.HandsfreeAudioCard", "Connect")); ++ pa_assert_se(dbus_connection_send(pa_dbus_connection_get(hfdata->connection), m, NULL)); ++ ++ return -1; ++ } ++ ++ /* The correct block size should take into account the SCO MTU from ++ * the Bluetooth adapter and (for adapters in the USB bus) the MxPS ++ * value from the Isoc USB endpoint in use by btusb and should be ++ * made available to userspace by the Bluetooth kernel subsystem. ++ * Meanwhile the empiric value 48 will be used. */ ++ if (imtu) ++ *imtu = 48; ++ if (omtu) ++ *omtu = 48; ++ ++ if (hfac) { ++ t->codec = hfac->codec; ++ return hfac->fd; ++ } else ++ return -1; + } + + static void hf_audio_agent_transport_release(pa_bluetooth_transport *t) { diff --git a/meta-tizen/recipes-multimedia/pulseaudio/pulseaudio_5.0/0019-bluetooth-Track-oFono-service.patch b/meta-tizen/recipes-multimedia/pulseaudio/pulseaudio_5.0/0019-bluetooth-Track-oFono-service.patch new file mode 100644 index 0000000..5f8fa21 --- /dev/null +++ b/meta-tizen/recipes-multimedia/pulseaudio/pulseaudio_5.0/0019-bluetooth-Track-oFono-service.patch @@ -0,0 +1,109 @@ +From: =?utf-8?q?Jo=C3=A3o_Paulo_Rechi_Vita?= +Date: Thu, 11 Jul 2013 11:43:48 -0300 +Subject: bluetooth: Track oFono service + +Change-Id: I6752f21b848757dbea20c92c68b581141cefbc67 +--- + src/modules/bluetooth/hfaudioagent-ofono.c | 61 ++++++++++++++++++++++++++++-- + 1 file changed, 58 insertions(+), 3 deletions(-) + +diff --git a/src/modules/bluetooth/hfaudioagent-ofono.c b/src/modules/bluetooth/hfaudioagent-ofono.c +index 3684bed..97e0fa8 100644 +--- a/src/modules/bluetooth/hfaudioagent-ofono.c ++++ b/src/modules/bluetooth/hfaudioagent-ofono.c +@@ -364,6 +364,7 @@ static void hf_audio_agent_unregister(hf_audio_agent_data *hfdata) { + + static DBusHandlerResult filter_cb(DBusConnection *bus, DBusMessage *m, void *data) { + const char *sender; ++ DBusError err; + hf_audio_agent_data *hfdata = data; + + pa_assert(bus); +@@ -374,6 +375,46 @@ static DBusHandlerResult filter_cb(DBusConnection *bus, DBusMessage *m, void *da + if (!pa_safe_streq(hfdata->ofono_bus_id, sender) && !pa_streq("org.freedesktop.DBus", sender)) + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + ++ dbus_error_init(&err); ++ ++ if (dbus_message_is_signal(m, "org.freedesktop.DBus", "NameOwnerChanged")) { ++ const char *name, *old_owner, *new_owner; ++ ++ if (!dbus_message_get_args(m, &err, ++ DBUS_TYPE_STRING, &name, ++ DBUS_TYPE_STRING, &old_owner, ++ DBUS_TYPE_STRING, &new_owner, ++ DBUS_TYPE_INVALID)) { ++ pa_log_error("Failed to parse org.freedesktop.DBus.NameOwnerChanged: %s", err.message); ++ goto fail; ++ } ++ ++ if (pa_streq(name, OFONO_SERVICE)) { ++ ++ if (old_owner && *old_owner) { ++ pa_log_debug("oFono disappeared"); ++ ++ if (hfdata->hf_audio_cards) { ++ pa_hashmap_free(hfdata->hf_audio_cards); ++ hfdata->hf_audio_cards = NULL; ++ } ++ ++ if(hfdata->ofono_bus_id) { ++ pa_xfree(hfdata->ofono_bus_id); ++ hfdata->ofono_bus_id = NULL; ++ } ++ } ++ ++ if (new_owner && *new_owner) { ++ pa_log_debug("oFono appeared"); ++ hf_audio_agent_register(hfdata); ++ } ++ } ++ ++ } ++ ++fail: ++ dbus_error_free(&err); + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + } + +@@ -390,7 +431,20 @@ static DBusMessage *hf_audio_agent_release(DBusConnection *c, DBusMessage *m, vo + return r; + } + +- r = dbus_message_new_error(m, "org.ofono.Error.NotImplemented", "Operation is not implemented"); ++ pa_log_debug("HF audio agent has been unregistered by oFono (%s)", hfdata->ofono_bus_id); ++ ++ if (hfdata->hf_audio_cards) { ++ pa_hashmap_free(hfdata->hf_audio_cards); ++ hfdata->hf_audio_cards = NULL; ++ } ++ ++ if(hfdata->ofono_bus_id) { ++ pa_xfree(hfdata->ofono_bus_id); ++ hfdata->ofono_bus_id = NULL; ++ } ++ ++ pa_assert_se(r = dbus_message_new_method_return(m)); ++ + return r; + } + +@@ -459,7 +513,8 @@ hf_audio_agent_data *hf_audio_agent_init(pa_core *c) { + + hfdata = pa_xnew0(hf_audio_agent_data, 1); + hfdata->core = c; +- hfdata->hf_audio_cards = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func); ++ hfdata->hf_audio_cards = pa_hashmap_new_full(pa_idxset_string_hash_func, pa_idxset_string_compare_func, ++ NULL, hf_audio_card_free); + hfdata->discovery = pa_shared_get(c, "bluetooth-discovery"); + + dbus_error_init(&err); +@@ -505,7 +560,7 @@ void hf_audio_agent_done(hf_audio_agent_data *data) { + pa_dbus_free_pending_list(&hfdata->pending); + + if (hfdata->hf_audio_cards) { +- pa_hashmap_free(hfdata->hf_audio_cards, hf_audio_card_free); ++ pa_hashmap_free(hfdata->hf_audio_cards); + hfdata->hf_audio_cards = NULL; + } + diff --git a/meta-tizen/recipes-multimedia/pulseaudio/pulseaudio_5.0/0020-bluetooth-Handle-CardAdded-signal.patch b/meta-tizen/recipes-multimedia/pulseaudio/pulseaudio_5.0/0020-bluetooth-Handle-CardAdded-signal.patch new file mode 100644 index 0000000..f3aaf33 --- /dev/null +++ b/meta-tizen/recipes-multimedia/pulseaudio/pulseaudio_5.0/0020-bluetooth-Handle-CardAdded-signal.patch @@ -0,0 +1,38 @@ +From: =?utf-8?q?Jo=C3=A3o_Paulo_Rechi_Vita?= +Date: Thu, 11 Jul 2013 11:47:37 -0300 +Subject: bluetooth: Handle CardAdded signal + +Change-Id: I695b97e26ce369d76503980c73fc3849252b4c91 +--- + src/modules/bluetooth/hfaudioagent-ofono.c | 18 ++++++++++++++++++ + 1 file changed, 18 insertions(+) + +diff --git a/src/modules/bluetooth/hfaudioagent-ofono.c b/src/modules/bluetooth/hfaudioagent-ofono.c +index 97e0fa8..be20257 100644 +--- a/src/modules/bluetooth/hfaudioagent-ofono.c ++++ b/src/modules/bluetooth/hfaudioagent-ofono.c +@@ -411,6 +411,24 @@ static DBusHandlerResult filter_cb(DBusConnection *bus, DBusMessage *m, void *da + } + } + ++ } else if (dbus_message_is_signal(m, "org.ofono.HandsfreeAudioManager", "CardAdded")) { ++ const char *p; ++ DBusMessageIter arg_i, props_i; ++ ++ if (!dbus_message_iter_init(m, &arg_i) || !pa_streq(dbus_message_get_signature(m), "oa{sv}")) { ++ pa_log_error("Failed to parse org.ofono.HandsfreeAudioManager.CardAdded"); ++ goto fail; ++ } ++ ++ dbus_message_iter_get_basic(&arg_i, &p); ++ ++ pa_assert_se(dbus_message_iter_next(&arg_i)); ++ pa_assert(dbus_message_iter_get_arg_type(&arg_i) == DBUS_TYPE_ARRAY); ++ ++ dbus_message_iter_recurse(&arg_i, &props_i); ++ ++ hf_audio_agent_card_found(hfdata, p, &props_i); ++ + } + + fail: diff --git a/meta-tizen/recipes-multimedia/pulseaudio/pulseaudio_5.0/0021-bluetooth-Handle-CardRemoved-signal.patch b/meta-tizen/recipes-multimedia/pulseaudio/pulseaudio_5.0/0021-bluetooth-Handle-CardRemoved-signal.patch new file mode 100644 index 0000000..fbd766e --- /dev/null +++ b/meta-tizen/recipes-multimedia/pulseaudio/pulseaudio_5.0/0021-bluetooth-Handle-CardRemoved-signal.patch @@ -0,0 +1,43 @@ +From: =?utf-8?q?Jo=C3=A3o_Paulo_Rechi_Vita?= +Date: Thu, 11 Jul 2013 12:53:10 -0300 +Subject: bluetooth: Handle CardRemoved signal + +Change-Id: I41083b3b5d8a318927dcdb373e871032c3f52748 +--- + src/modules/bluetooth/hfaudioagent-ofono.c | 23 +++++++++++++++++++++++ + 1 file changed, 23 insertions(+) + +diff --git a/src/modules/bluetooth/hfaudioagent-ofono.c b/src/modules/bluetooth/hfaudioagent-ofono.c +index be20257..ee158af 100644 +--- a/src/modules/bluetooth/hfaudioagent-ofono.c ++++ b/src/modules/bluetooth/hfaudioagent-ofono.c +@@ -429,6 +429,29 @@ static DBusHandlerResult filter_cb(DBusConnection *bus, DBusMessage *m, void *da + + hf_audio_agent_card_found(hfdata, p, &props_i); + ++ } else if (dbus_message_is_signal(m, "org.ofono.HandsfreeAudioManager", "CardRemoved")) { ++ const char *p; ++ hf_audio_card *hfac; ++ bool old_any_connected; ++ ++ if (!dbus_message_get_args(m, &err, DBUS_TYPE_OBJECT_PATH, &p, DBUS_TYPE_INVALID)) { ++ pa_log_error("Failed to parse org.ofono.HandsfreeAudioManager.CardRemoved: %s", err.message); ++ goto fail; ++ } ++ ++ if ((hfac = pa_hashmap_remove(hfdata->hf_audio_cards, p)) != NULL) { ++ old_any_connected = pa_bluetooth_device_any_transport_connected(hfac->transport->device); ++ ++ hfac->transport->state = PA_BLUETOOTH_TRANSPORT_STATE_DISCONNECTED; ++ hfac->transport->device->transports[hfac->transport->profile] = NULL; ++ pa_hook_fire(pa_bluetooth_discovery_hook(hfdata->discovery, PA_BLUETOOTH_HOOK_TRANSPORT_STATE_CHANGED), hfac->transport); ++ ++ if (old_any_connected != pa_bluetooth_device_any_transport_connected(hfac->transport->device)) { ++ pa_hook_fire(pa_bluetooth_discovery_hook(hfdata->discovery, PA_BLUETOOTH_HOOK_DEVICE_CONNECTION_CHANGED), hfac->transport->device); ++ } ++ ++ hf_audio_card_free(hfac); ++ } + } + + fail: diff --git a/meta-tizen/recipes-multimedia/pulseaudio/pulseaudio_5.0/0022-bluetooth-Implement-org.ofono.HandsfreeAudioAgent.Ne.patch b/meta-tizen/recipes-multimedia/pulseaudio/pulseaudio_5.0/0022-bluetooth-Implement-org.ofono.HandsfreeAudioAgent.Ne.patch new file mode 100644 index 0000000..4ad62dc --- /dev/null +++ b/meta-tizen/recipes-multimedia/pulseaudio/pulseaudio_5.0/0022-bluetooth-Implement-org.ofono.HandsfreeAudioAgent.Ne.patch @@ -0,0 +1,56 @@ +From: =?utf-8?q?Jo=C3=A3o_Paulo_Rechi_Vita?= +Date: Thu, 11 Jul 2013 13:23:31 -0300 +Subject: bluetooth: Implement org.ofono.HandsfreeAudioAgent.NewConnection() + +Change-Id: Idaff2e3a96ce83e7ee6f961543273429b044be8e +--- + src/modules/bluetooth/hfaudioagent-ofono.c | 29 +++++++++++++++++++++++++++-- + 1 file changed, 27 insertions(+), 2 deletions(-) + +diff --git a/src/modules/bluetooth/hfaudioagent-ofono.c b/src/modules/bluetooth/hfaudioagent-ofono.c +index ee158af..ba0acaf 100644 +--- a/src/modules/bluetooth/hfaudioagent-ofono.c ++++ b/src/modules/bluetooth/hfaudioagent-ofono.c +@@ -491,7 +491,10 @@ static DBusMessage *hf_audio_agent_release(DBusConnection *c, DBusMessage *m, vo + + static DBusMessage *hf_audio_agent_new_connection(DBusConnection *c, DBusMessage *m, void *data) { + DBusMessage *r; +- const char *sender; ++ const char *sender, *card; ++ int fd; ++ uint8_t codec; ++ hf_audio_card *hfac; + hf_audio_agent_data *hfdata = data; + + pa_assert(hfdata); +@@ -502,7 +505,29 @@ static DBusMessage *hf_audio_agent_new_connection(DBusConnection *c, DBusMessage + return r; + } + +- r = dbus_message_new_error(m, "org.ofono.Error.NotImplemented", "Operation is not implemented"); ++ if (dbus_message_get_args(m, NULL, ++ DBUS_TYPE_OBJECT_PATH, &card, ++ DBUS_TYPE_UNIX_FD, &fd, ++ DBUS_TYPE_BYTE, &codec, ++ DBUS_TYPE_INVALID) == FALSE) { ++ pa_assert_se(r = dbus_message_new_error(m, "org.ofono.Error.InvalidArguments", "Invalid arguments in method call")); ++ return r; ++ } ++ ++ if ( !(hfac = pa_hashmap_get(hfdata->hf_audio_cards, card)) ) { ++ pa_log_warn("New audio connection on unknown card %s (fd=%d, codec=%d)", card, fd, codec); ++ pa_assert_se(r = dbus_message_new_error(m, "org.ofono.Error.InvalidArguments", "Unknown card")); ++ return r; ++ } else ++ pa_log_debug("New audio connection on card %s (fd=%d, codec=%d)", card, fd, codec); ++ ++ hfac->fd = fd; ++ hfac->codec = codec; ++ hfac->transport->state = PA_BLUETOOTH_TRANSPORT_STATE_PLAYING; ++ pa_hook_fire(pa_bluetooth_discovery_hook(hfdata->discovery, PA_BLUETOOTH_HOOK_TRANSPORT_STATE_CHANGED), hfac->transport); ++ ++ pa_assert_se(r = dbus_message_new_method_return(m)); ++ + return r; + } + diff --git a/meta-tizen/recipes-multimedia/pulseaudio/pulseaudio_5.0/0023-bluetooth-Fix-not-handle-fd-in-DEFER_SETUP-state.patch b/meta-tizen/recipes-multimedia/pulseaudio/pulseaudio_5.0/0023-bluetooth-Fix-not-handle-fd-in-DEFER_SETUP-state.patch new file mode 100644 index 0000000..587fce6 --- /dev/null +++ b/meta-tizen/recipes-multimedia/pulseaudio/pulseaudio_5.0/0023-bluetooth-Fix-not-handle-fd-in-DEFER_SETUP-state.patch @@ -0,0 +1,44 @@ +From: Luiz Augusto von Dentz +Date: Fri, 23 Aug 2013 12:56:40 +0300 +Subject: bluetooth: Fix not handle fd in DEFER_SETUP state + +The fd passed over NewConnection is in DEFER_SETUP and need to be read to +be accept otherwise it wont work. + +Change-Id: I2f6df033d3c1602064a39bb40a5bbd60e014c8f7 +--- + src/modules/bluetooth/hfaudioagent-ofono.c | 14 ++++++++++++-- + 1 file changed, 12 insertions(+), 2 deletions(-) + +diff --git a/src/modules/bluetooth/hfaudioagent-ofono.c b/src/modules/bluetooth/hfaudioagent-ofono.c +index ba0acaf..90d1348 100644 +--- a/src/modules/bluetooth/hfaudioagent-ofono.c ++++ b/src/modules/bluetooth/hfaudioagent-ofono.c +@@ -23,6 +23,8 @@ + #include + #endif + ++#include ++ + #include + #include + #include +@@ -518,8 +520,16 @@ static DBusMessage *hf_audio_agent_new_connection(DBusConnection *c, DBusMessage + pa_log_warn("New audio connection on unknown card %s (fd=%d, codec=%d)", card, fd, codec); + pa_assert_se(r = dbus_message_new_error(m, "org.ofono.Error.InvalidArguments", "Unknown card")); + return r; +- } else +- pa_log_debug("New audio connection on card %s (fd=%d, codec=%d)", card, fd, codec); ++ } ++ ++ pa_log_debug("New audio connection on card %s (fd=%d, codec=%d)", card, fd, codec); ++ ++ /* Do the socket defered setup */ ++ if (recv(fd, NULL, 0, 0) < 0) { ++ const char *strerr = strerror(errno); ++ pa_log_warn("Defered setup failed: %d (%s)", errno, strerr); ++ pa_assert_se(r = dbus_message_new_error(m, "org.ofono.Error.InvalidArguments", strerr)); ++ } + + hfac->fd = fd; + hfac->codec = codec; diff --git a/meta-tizen/recipes-multimedia/pulseaudio/pulseaudio_5.0/0024-bluetooth-Suspend-sink-source-on-HFP-s-stream-HUP.patch b/meta-tizen/recipes-multimedia/pulseaudio/pulseaudio_5.0/0024-bluetooth-Suspend-sink-source-on-HFP-s-stream-HUP.patch new file mode 100644 index 0000000..5858d67 --- /dev/null +++ b/meta-tizen/recipes-multimedia/pulseaudio/pulseaudio_5.0/0024-bluetooth-Suspend-sink-source-on-HFP-s-stream-HUP.patch @@ -0,0 +1,60 @@ +From: =?utf-8?q?Jo=C3=A3o_Paulo_Rechi_Vita?= +Date: Wed, 27 Mar 2013 01:43:42 -0300 +Subject: bluetooth: Suspend sink/source on HFP's stream HUP + +When the Audio Connection is disconnected the sink and source should be +suspended. + +Change-Id: Ifedd5e7fe70ee74e509b82270ce84aba762f2412 +--- + src/modules/bluetooth/module-bluez5-device.c | 17 ++++++++++++++--- + 1 file changed, 14 insertions(+), 3 deletions(-) + +diff --git a/src/modules/bluetooth/module-bluez5-device.c b/src/modules/bluetooth/module-bluez5-device.c +index b511b92..ae5ec1d 100644 +--- a/src/modules/bluetooth/module-bluez5-device.c ++++ b/src/modules/bluetooth/module-bluez5-device.c +@@ -74,6 +74,7 @@ static const char* const valid_modargs[] = { + + enum { + BLUETOOTH_MESSAGE_IO_THREAD_FAILED, ++ BLUETOOTH_MESSAGE_TRANSPORT_STATE_CHANGED, + BLUETOOTH_MESSAGE_MAX + }; + +@@ -1427,6 +1428,12 @@ io_fail: + pending_read_bytes = 0; + writable = false; + ++ if (u->profile == PA_BLUETOOTH_PROFILE_HEADSET_AUDIO_GATEWAY) { ++ u->transport->state = PA_BLUETOOTH_TRANSPORT_STATE_IDLE; ++ pa_asyncmsgq_post(pa_thread_mq_get()->outq, PA_MSGOBJECT(u->msg), BLUETOOTH_MESSAGE_TRANSPORT_STATE_CHANGED, u, 0, ++ NULL, NULL); ++ } ++ + teardown_stream(u); + } + +@@ -1994,15 +2001,19 @@ static pa_hook_result_t transport_state_changed_cb(pa_bluetooth_discovery *y, pa + + /* Run from main thread context */ + static int device_process_msg(pa_msgobject *obj, int code, void *data, int64_t offset, pa_memchunk *chunk) { +- struct bluetooth_msg *u = BLUETOOTH_MSG(obj); ++ struct bluetooth_msg *b = BLUETOOTH_MSG(obj); ++ struct userdata *u = data; + + switch (code) { ++ case BLUETOOTH_MESSAGE_TRANSPORT_STATE_CHANGED: ++ handle_transport_state_change(u, u->transport); ++ break; + case BLUETOOTH_MESSAGE_IO_THREAD_FAILED: +- if (u->card->module->unload_requested) ++ if (b->card->module->unload_requested) + break; + + pa_log_debug("Switching the profile to off due to IO thread failure."); +- pa_assert_se(pa_card_set_profile(u->card, pa_hashmap_get(u->card->profiles, "off"), false) >= 0); ++ pa_assert_se(pa_card_set_profile(b->card, "off", false) >= 0); + break; + } + diff --git a/meta-tizen/recipes-multimedia/pulseaudio/pulseaudio_5.0/0025-bluetooth-Implement-transport-release-for-hf_audio_a.patch b/meta-tizen/recipes-multimedia/pulseaudio/pulseaudio_5.0/0025-bluetooth-Implement-transport-release-for-hf_audio_a.patch new file mode 100644 index 0000000..b557ea5 --- /dev/null +++ b/meta-tizen/recipes-multimedia/pulseaudio/pulseaudio_5.0/0025-bluetooth-Implement-transport-release-for-hf_audio_a.patch @@ -0,0 +1,27 @@ +From: Luiz Augusto von Dentz +Date: Fri, 23 Aug 2013 19:19:34 +0300 +Subject: bluetooth: Implement transport release for hf_audio_agent transports + +Change-Id: I496b3ab1c2f8e347c41262818ec3b9a35ed7262e +--- + src/modules/bluetooth/hfaudioagent-ofono.c | 7 +++++++ + 1 file changed, 7 insertions(+) + +diff --git a/src/modules/bluetooth/hfaudioagent-ofono.c b/src/modules/bluetooth/hfaudioagent-ofono.c +index 90d1348..a0474df 100644 +--- a/src/modules/bluetooth/hfaudioagent-ofono.c ++++ b/src/modules/bluetooth/hfaudioagent-ofono.c +@@ -153,6 +153,13 @@ static int hf_audio_agent_transport_acquire(pa_bluetooth_transport *t, bool opti + } + + static void hf_audio_agent_transport_release(pa_bluetooth_transport *t) { ++ hf_audio_agent_data *hfdata = t->userdata; ++ hf_audio_card *hfac = pa_hashmap_get(hfdata->hf_audio_cards, t->path); ++ ++ if (hfac) { ++ shutdown(hfac->fd, SHUT_RDWR); ++ hfac->fd = -1; ++ } + } + + static void hf_audio_agent_card_found(hf_audio_agent_data *hfdata, const char *path, DBusMessageIter *props_i) { diff --git a/meta-tizen/recipes-multimedia/pulseaudio/pulseaudio_5.0/0026-bluetooth-Fixes-HFP-audio-transfer-when-initiator.patch b/meta-tizen/recipes-multimedia/pulseaudio/pulseaudio_5.0/0026-bluetooth-Fixes-HFP-audio-transfer-when-initiator.patch new file mode 100644 index 0000000..4dd9742 --- /dev/null +++ b/meta-tizen/recipes-multimedia/pulseaudio/pulseaudio_5.0/0026-bluetooth-Fixes-HFP-audio-transfer-when-initiator.patch @@ -0,0 +1,59 @@ +From: Luiz Augusto von Dentz +Date: Fri, 23 Aug 2013 19:44:23 +0300 +Subject: bluetooth: Fixes HFP audio transfer when initiator + +This makes sure org.ofono.HandsfreeAudioCard.Connect is called regardless +of the optional flag and also makes sure to update the profile state +whenever SCO is disconnected. + +Change-Id: I4400753333f14a2381eb75d5b62d2ea51d1c7139 +--- + src/modules/bluetooth/hfaudioagent-ofono.c | 2 +- + src/modules/bluetooth/module-bluez5-device.c | 14 +++++++------- + 2 files changed, 8 insertions(+), 8 deletions(-) + +diff --git a/src/modules/bluetooth/hfaudioagent-ofono.c b/src/modules/bluetooth/hfaudioagent-ofono.c +index a0474df..59eafdb 100644 +--- a/src/modules/bluetooth/hfaudioagent-ofono.c ++++ b/src/modules/bluetooth/hfaudioagent-ofono.c +@@ -126,7 +126,7 @@ static int hf_audio_agent_transport_acquire(pa_bluetooth_transport *t, bool opti + hf_audio_agent_data *hfdata = t->userdata; + hf_audio_card *hfac = pa_hashmap_get(hfdata->hf_audio_cards, t->path); + +- if (!optional) { ++ if (hfac->fd < 0) { + DBusMessage *m; + + pa_assert_se(m = dbus_message_new_method_call(t->owner, t->path, "org.ofono.HandsfreeAudioCard", "Connect")); +diff --git a/src/modules/bluetooth/module-bluez5-device.c b/src/modules/bluetooth/module-bluez5-device.c +index ae5ec1d..39fa5d0 100644 +--- a/src/modules/bluetooth/module-bluez5-device.c ++++ b/src/modules/bluetooth/module-bluez5-device.c +@@ -768,6 +768,12 @@ static void transport_release(struct userdata *u) { + + u->transport_acquired = false; + ++ if (u->profile == PA_BLUETOOTH_PROFILE_HEADSET_AUDIO_GATEWAY) { ++ u->transport->state = PA_BLUETOOTH_TRANSPORT_STATE_IDLE; ++ pa_asyncmsgq_post(pa_thread_mq_get()->outq, PA_MSGOBJECT(u->msg), BLUETOOTH_MESSAGE_TRANSPORT_STATE_CHANGED, u, 0, ++ NULL, NULL); ++ } ++ + teardown_stream(u); + } + +@@ -1428,13 +1434,7 @@ io_fail: + pending_read_bytes = 0; + writable = false; + +- if (u->profile == PA_BLUETOOTH_PROFILE_HEADSET_AUDIO_GATEWAY) { +- u->transport->state = PA_BLUETOOTH_TRANSPORT_STATE_IDLE; +- pa_asyncmsgq_post(pa_thread_mq_get()->outq, PA_MSGOBJECT(u->msg), BLUETOOTH_MESSAGE_TRANSPORT_STATE_CHANGED, u, 0, +- NULL, NULL); +- } +- +- teardown_stream(u); ++ transport_release(u); + } + + fail: diff --git a/meta-tizen/recipes-multimedia/pulseaudio/pulseaudio_5.0/0027-bluetooth-Set-off-profile-as-default-for-newly-creat.patch b/meta-tizen/recipes-multimedia/pulseaudio/pulseaudio_5.0/0027-bluetooth-Set-off-profile-as-default-for-newly-creat.patch new file mode 100644 index 0000000..cce0497 --- /dev/null +++ b/meta-tizen/recipes-multimedia/pulseaudio/pulseaudio_5.0/0027-bluetooth-Set-off-profile-as-default-for-newly-creat.patch @@ -0,0 +1,37 @@ +From: Luiz Augusto von Dentz +Date: Mon, 16 Sep 2013 15:10:51 +0300 +Subject: bluetooth: Set 'off' profile as default for newly create cards + +This makes sure that pa_card_new doesn't select one profile based on +priority which may conflict with audio policy. + +Change-Id: Ifa5ad111d0c9f58701f93bcfd85b70d1f096efac +--- + src/modules/bluetooth/module-bluez4-device.c | 1 + + src/modules/bluetooth/module-bluez5-device.c | 1 + + 2 files changed, 2 insertions(+) + +diff --git a/src/modules/bluetooth/module-bluez4-device.c b/src/modules/bluetooth/module-bluez4-device.c +index 83e603f..b0b12f8 100644 +--- a/src/modules/bluetooth/module-bluez4-device.c ++++ b/src/modules/bluetooth/module-bluez4-device.c +@@ -2295,6 +2295,7 @@ static int add_card(struct userdata *u) { + d = PA_CARD_PROFILE_DATA(p); + *d = PA_BLUEZ4_PROFILE_OFF; + pa_hashmap_put(data.profiles, p->name, p); ++ pa_card_new_data_set_profile(&data, "off"); + + if ((default_profile = pa_modargs_get_value(u->modargs, "profile", NULL))) { + if (pa_hashmap_get(data.profiles, default_profile)) +diff --git a/src/modules/bluetooth/module-bluez5-device.c b/src/modules/bluetooth/module-bluez5-device.c +index 39fa5d0..a0776c8 100644 +--- a/src/modules/bluetooth/module-bluez5-device.c ++++ b/src/modules/bluetooth/module-bluez5-device.c +@@ -1888,6 +1888,7 @@ static int add_card(struct userdata *u) { + p = PA_CARD_PROFILE_DATA(cp); + *p = PA_BLUETOOTH_PROFILE_OFF; + pa_hashmap_put(data.profiles, cp->name, cp); ++ pa_card_new_data_set_profile(&data, "off"); + + u->card = pa_card_new(u->core, &data); + pa_card_new_data_done(&data); diff --git a/meta-tizen/recipes-multimedia/pulseaudio/pulseaudio_5.0/0028-fix-ofono-and-pulseaudio-starting-order-assert.patch b/meta-tizen/recipes-multimedia/pulseaudio/pulseaudio_5.0/0028-fix-ofono-and-pulseaudio-starting-order-assert.patch new file mode 100644 index 0000000..db44a2e --- /dev/null +++ b/meta-tizen/recipes-multimedia/pulseaudio/pulseaudio_5.0/0028-fix-ofono-and-pulseaudio-starting-order-assert.patch @@ -0,0 +1,27 @@ +From: Jaska Uimonen +Date: Thu, 2 Jan 2014 15:37:02 +0200 +Subject: fix ofono and pulseaudio starting order assert + +Change-Id: I743c6e1fb5c65cc0702f073d6c2beb4e6868d7bb +Signed-off-by: Jaska Uimonen +--- + src/modules/bluetooth/hfaudioagent-ofono.c | 6 ++---- + 1 file changed, 2 insertions(+), 4 deletions(-) + +diff --git a/src/modules/bluetooth/hfaudioagent-ofono.c b/src/modules/bluetooth/hfaudioagent-ofono.c +index 59eafdb..7f93c6b 100644 +--- a/src/modules/bluetooth/hfaudioagent-ofono.c ++++ b/src/modules/bluetooth/hfaudioagent-ofono.c +@@ -403,10 +403,8 @@ static DBusHandlerResult filter_cb(DBusConnection *bus, DBusMessage *m, void *da + if (old_owner && *old_owner) { + pa_log_debug("oFono disappeared"); + +- if (hfdata->hf_audio_cards) { +- pa_hashmap_free(hfdata->hf_audio_cards); +- hfdata->hf_audio_cards = NULL; +- } ++ if (hfdata->hf_audio_cards) ++ pa_hashmap_remove_all(hfdata->hf_audio_cards); + + if(hfdata->ofono_bus_id) { + pa_xfree(hfdata->ofono_bus_id); diff --git a/meta-tizen/recipes-multimedia/pulseaudio/pulseaudio_5.0/0029-hfp-do-safe-strcmp-in-dbus-handler.patch b/meta-tizen/recipes-multimedia/pulseaudio/pulseaudio_5.0/0029-hfp-do-safe-strcmp-in-dbus-handler.patch new file mode 100644 index 0000000..8402ffa --- /dev/null +++ b/meta-tizen/recipes-multimedia/pulseaudio/pulseaudio_5.0/0029-hfp-do-safe-strcmp-in-dbus-handler.patch @@ -0,0 +1,23 @@ +From: Jaska Uimonen +Date: Mon, 30 Dec 2013 18:03:18 +0200 +Subject: hfp do safe strcmp in dbus handler + +Change-Id: I4ba64d22b2b807530263b5f274cd89f208c675ac +Signed-off-by: Jaska Uimonen +--- + src/modules/bluetooth/hfaudioagent-ofono.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/src/modules/bluetooth/hfaudioagent-ofono.c b/src/modules/bluetooth/hfaudioagent-ofono.c +index 7f93c6b..2ac8a82 100644 +--- a/src/modules/bluetooth/hfaudioagent-ofono.c ++++ b/src/modules/bluetooth/hfaudioagent-ofono.c +@@ -381,7 +381,7 @@ static DBusHandlerResult filter_cb(DBusConnection *bus, DBusMessage *m, void *da + pa_assert(hfdata); + + sender = dbus_message_get_sender(m); +- if (!pa_safe_streq(hfdata->ofono_bus_id, sender) && !pa_streq("org.freedesktop.DBus", sender)) ++ if (!pa_safe_streq(hfdata->ofono_bus_id, sender) && !pa_safe_streq("org.freedesktop.DBus", sender)) + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + + dbus_error_init(&err); diff --git a/meta-tizen/recipes-multimedia/pulseaudio/pulseaudio_5.0/0030-add-parameter-to-define-key-used-in-stream-restore.patch b/meta-tizen/recipes-multimedia/pulseaudio/pulseaudio_5.0/0030-add-parameter-to-define-key-used-in-stream-restore.patch new file mode 100644 index 0000000..0773d40 --- /dev/null +++ b/meta-tizen/recipes-multimedia/pulseaudio/pulseaudio_5.0/0030-add-parameter-to-define-key-used-in-stream-restore.patch @@ -0,0 +1,258 @@ +From: Jaska Uimonen +Date: Thu, 5 Sep 2013 12:21:19 +0300 +Subject: add parameter to define key used in stream restore. + +It is possible now to use preferred_stream_group +command line parameter when loading module stream +restore. This key will be searched from the stream's +proplist and if found it is used as key when +restoring the volumes and other values. There's also +special value media.role.within.appication.name you +can use to enable restoring stream roles within +application. So different streams with different +roles within application will save their volumes. +If the preferred stream group parameter is left out +module stream restore will fallback to old default +functionality. + +Change-Id: I636f47b43476f3d4cd6c14244eafcd050683bb69 +Signed-off-by: Jaska Uimonen +--- + src/modules/module-stream-restore.c | 34 +++++++++++++--------- + src/pulsecore/proplist-util.c | 58 +++++++++++++++++++++++++++++++++++++ + src/pulsecore/proplist-util.h | 1 + + 3 files changed, 80 insertions(+), 13 deletions(-) + +diff --git a/src/modules/module-stream-restore.c b/src/modules/module-stream-restore.c +index 9689e79..68968c9 100644 +--- a/src/modules/module-stream-restore.c ++++ b/src/modules/module-stream-restore.c +@@ -70,7 +70,8 @@ PA_MODULE_USAGE( + "restore_muted= " + "on_hotplug= " + "on_rescue= " +- "fallback_table="); ++ "fallback_table=" ++ "preferred_stream_group= "); + + #define SAVE_INTERVAL (10 * PA_USEC_PER_SEC) + #define IDENTIFICATION_PROPERTY "module-stream-restore.id" +@@ -87,6 +88,7 @@ static const char* const valid_modargs[] = { + "on_hotplug", + "on_rescue", + "fallback_table", ++ "preferred_stream_group", + NULL + }; + +@@ -112,6 +114,7 @@ struct userdata { + bool restore_muted:1; + bool on_hotplug:1; + bool on_rescue:1; ++ char *preferred_stream_group; + + pa_native_protocol *protocol; + pa_idxset *subscribed; +@@ -1277,7 +1280,7 @@ static void subscribe_callback(pa_core *c, pa_subscription_event_type_t t, uint3 + if (!(sink_input = pa_idxset_get_by_index(c->sink_inputs, idx))) + return; + +- if (!(name = pa_proplist_get_stream_group(sink_input->proplist, "sink-input", IDENTIFICATION_PROPERTY))) ++ if (!(name = pa_proplist_get_stream_group_extended(sink_input->proplist, "sink-input", IDENTIFICATION_PROPERTY, u->preferred_stream_group))) + return; + + if ((old = entry_read(u, name))) { +@@ -1327,7 +1330,7 @@ static void subscribe_callback(pa_core *c, pa_subscription_event_type_t t, uint3 + if (!(source_output = pa_idxset_get_by_index(c->source_outputs, idx))) + return; + +- if (!(name = pa_proplist_get_stream_group(source_output->proplist, "source-output", IDENTIFICATION_PROPERTY))) ++ if (!(name = pa_proplist_get_stream_group_extended(source_output->proplist, "source-output", IDENTIFICATION_PROPERTY, u->preferred_stream_group))) + return; + + if ((old = entry_read(u, name))) { +@@ -1420,7 +1423,7 @@ static pa_hook_result_t sink_input_new_hook_callback(pa_core *c, pa_sink_input_n + pa_assert(u); + pa_assert(u->restore_device); + +- if (!(name = pa_proplist_get_stream_group(new_data->proplist, "sink-input", IDENTIFICATION_PROPERTY))) ++ if (!(name = pa_proplist_get_stream_group_extended(new_data->proplist, "sink-input", IDENTIFICATION_PROPERTY, u->preferred_stream_group))) + return PA_HOOK_OK; + + if (new_data->sink) +@@ -1462,7 +1465,7 @@ static pa_hook_result_t sink_input_fixate_hook_callback(pa_core *c, pa_sink_inpu + pa_assert(u); + pa_assert(u->restore_volume || u->restore_muted); + +- if (!(name = pa_proplist_get_stream_group(new_data->proplist, "sink-input", IDENTIFICATION_PROPERTY))) ++ if (!(name = pa_proplist_get_stream_group_extended(new_data->proplist, "sink-input", IDENTIFICATION_PROPERTY, u->preferred_stream_group))) + return PA_HOOK_OK; + + if ((e = entry_read(u, name))) { +@@ -1516,7 +1519,7 @@ static pa_hook_result_t source_output_new_hook_callback(pa_core *c, pa_source_ou + if (new_data->direct_on_input) + return PA_HOOK_OK; + +- if (!(name = pa_proplist_get_stream_group(new_data->proplist, "source-output", IDENTIFICATION_PROPERTY))) ++ if (!(name = pa_proplist_get_stream_group_extended(new_data->proplist, "source-output", IDENTIFICATION_PROPERTY, u->preferred_stream_group))) + return PA_HOOK_OK; + + if (new_data->source) +@@ -1559,7 +1562,7 @@ static pa_hook_result_t source_output_fixate_hook_callback(pa_core *c, pa_source + pa_assert(u); + pa_assert(u->restore_volume || u->restore_muted); + +- if (!(name = pa_proplist_get_stream_group(new_data->proplist, "source-output", IDENTIFICATION_PROPERTY))) ++ if (!(name = pa_proplist_get_stream_group_extended(new_data->proplist, "source-output", IDENTIFICATION_PROPERTY, u->preferred_stream_group))) + return PA_HOOK_OK; + + if ((e = entry_read(u, name))) { +@@ -1631,7 +1634,7 @@ static pa_hook_result_t sink_put_hook_callback(pa_core *c, pa_sink *sink, struct + if (!PA_SINK_INPUT_IS_LINKED(pa_sink_input_get_state(si))) + continue; + +- if (!(name = pa_proplist_get_stream_group(si->proplist, "sink-input", IDENTIFICATION_PROPERTY))) ++ if (!(name = pa_proplist_get_stream_group_extended(si->proplist, "sink-input", IDENTIFICATION_PROPERTY, u->preferred_stream_group))) + continue; + + if ((e = entry_read(u, name))) { +@@ -1679,7 +1682,7 @@ static pa_hook_result_t source_put_hook_callback(pa_core *c, pa_source *source, + if (!PA_SOURCE_OUTPUT_IS_LINKED(pa_source_output_get_state(so))) + continue; + +- if (!(name = pa_proplist_get_stream_group(so->proplist, "source-output", IDENTIFICATION_PROPERTY))) ++ if (!(name = pa_proplist_get_stream_group_extended(so->proplist, "source-output", IDENTIFICATION_PROPERTY, u->preferred_stream_group))) + continue; + + if ((e = entry_read(u, name))) { +@@ -1715,7 +1718,7 @@ static pa_hook_result_t sink_unlink_hook_callback(pa_core *c, pa_sink *sink, str + if (!si->sink) + continue; + +- if (!(name = pa_proplist_get_stream_group(si->proplist, "sink-input", IDENTIFICATION_PROPERTY))) ++ if (!(name = pa_proplist_get_stream_group_extended(si->proplist, "sink-input", IDENTIFICATION_PROPERTY, u->preferred_stream_group))) + continue; + + if ((e = entry_read(u, name))) { +@@ -1761,7 +1764,7 @@ static pa_hook_result_t source_unlink_hook_callback(pa_core *c, pa_source *sourc + if (!so->source) + continue; + +- if (!(name = pa_proplist_get_stream_group(so->proplist, "source-output", IDENTIFICATION_PROPERTY))) ++ if (!(name = pa_proplist_get_stream_group_extended(so->proplist, "source-output", IDENTIFICATION_PROPERTY, u->preferred_stream_group))) + continue; + + if ((e = entry_read(u, name))) { +@@ -1880,7 +1883,7 @@ static void entry_apply(struct userdata *u, const char *name, struct entry *e) { + char *n; + pa_sink *s; + +- if (!(n = pa_proplist_get_stream_group(si->proplist, "sink-input", IDENTIFICATION_PROPERTY))) ++ if (!(n = pa_proplist_get_stream_group_extended(si->proplist, "sink-input", IDENTIFICATION_PROPERTY, u->preferred_stream_group))) + continue; + + if (!pa_streq(name, n)) { +@@ -1928,7 +1931,7 @@ static void entry_apply(struct userdata *u, const char *name, struct entry *e) { + char *n; + pa_source *s; + +- if (!(n = pa_proplist_get_stream_group(so->proplist, "source-output", IDENTIFICATION_PROPERTY))) ++ if (!(n = pa_proplist_get_stream_group_extended(so->proplist, "source-output", IDENTIFICATION_PROPERTY, u->preferred_stream_group))) + continue; + + if (!pa_streq(name, n)) { +@@ -2411,6 +2414,8 @@ int pa__init(pa_module*m) { + + u->subscription = pa_subscription_new(m->core, PA_SUBSCRIPTION_MASK_SINK_INPUT|PA_SUBSCRIPTION_MASK_SOURCE_OUTPUT, subscribe_callback, u); + ++ u->preferred_stream_group = pa_xstrdup(pa_modargs_get_value(ma, "preferred_stream_group", NULL)); ++ + if (restore_device) { + /* A little bit earlier than module-intended-roles ... */ + u->sink_input_new_hook_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_INPUT_NEW], PA_HOOK_EARLY, (pa_hook_cb_t) sink_input_new_hook_callback, u); +@@ -2554,5 +2559,8 @@ void pa__done(pa_module*m) { + if (u->subscribed) + pa_idxset_free(u->subscribed, NULL); + ++ if (u->preferred_stream_group) ++ pa_xfree(u->preferred_stream_group); ++ + pa_xfree(u); + } +diff --git a/src/pulsecore/proplist-util.c b/src/pulsecore/proplist-util.c +index 473290f..0a8dbca 100644 +--- a/src/pulsecore/proplist-util.c ++++ b/src/pulsecore/proplist-util.c +@@ -274,3 +274,61 @@ char *pa_proplist_get_stream_group(pa_proplist *p, const char *prefix, const cha + + return t; + } ++ ++char *pa_proplist_get_stream_group_extended(pa_proplist *p, const char *prefix, const char *cache, const char *preferred_stream_group) { ++ const char *r = NULL; ++ const char *q = NULL; ++ char *t; ++ ++ if (!p) ++ return NULL; ++ ++ if (cache && (r = pa_proplist_gets(p, cache))) ++ return pa_xstrdup(r); ++ ++ if (!prefix) ++ prefix = "stream"; ++ ++ /* try first to get the preferred stream group, then fallback to hard coded order */ ++ if (preferred_stream_group) { ++ if (!strcmp(preferred_stream_group, "media.role.within.application.name")) { ++ if ((r = pa_proplist_gets(p, PA_PROP_APPLICATION_NAME)) && ++ (q = pa_proplist_gets(p, PA_PROP_MEDIA_ROLE))) { ++ t = pa_sprintf_malloc("%s-by-media-role-within-application-name:%s-%s", prefix, q, r); ++ } else { ++ /* make r NULL to be able to fallback to "standard" stream restore code */ ++ r = NULL; ++ } ++ } ++ else if ((r = pa_proplist_gets(p, preferred_stream_group))) { ++ if (!strcmp(preferred_stream_group, PA_PROP_MEDIA_ROLE)) ++ t = pa_sprintf_malloc("%s-by-media-role:%s", prefix, r); ++ else if (!strcmp(preferred_stream_group, PA_PROP_APPLICATION_ID)) ++ t = pa_sprintf_malloc("%s-by-application-id:%s", prefix, r); ++ else if (!strcmp(preferred_stream_group, PA_PROP_APPLICATION_NAME)) ++ t = pa_sprintf_malloc("%s-by-application-name:%s", prefix, r); ++ else if (!strcmp(preferred_stream_group, PA_PROP_MEDIA_NAME)) ++ t = pa_sprintf_malloc("%s-by-media-name:%s", prefix, r); ++ else ++ t = pa_sprintf_malloc("%s-fallback:%s", prefix, r); ++ } ++ } ++ ++ if (!r) { ++ if ((r = pa_proplist_gets(p, PA_PROP_MEDIA_ROLE))) ++ t = pa_sprintf_malloc("%s-by-media-role:%s", prefix, r); ++ else if ((r = pa_proplist_gets(p, PA_PROP_APPLICATION_ID))) ++ t = pa_sprintf_malloc("%s-by-application-id:%s", prefix, r); ++ else if ((r = pa_proplist_gets(p, PA_PROP_APPLICATION_NAME))) ++ t = pa_sprintf_malloc("%s-by-application-name:%s", prefix, r); ++ else if ((r = pa_proplist_gets(p, PA_PROP_MEDIA_NAME))) ++ t = pa_sprintf_malloc("%s-by-media-name:%s", prefix, r); ++ else ++ t = pa_sprintf_malloc("%s-fallback:%s", prefix, r); ++ } ++ ++ if (cache) ++ pa_proplist_sets(p, cache, t); ++ ++ return t; ++} +diff --git a/src/pulsecore/proplist-util.h b/src/pulsecore/proplist-util.h +index 3d08776..47bdb57 100644 +--- a/src/pulsecore/proplist-util.h ++++ b/src/pulsecore/proplist-util.h +@@ -26,5 +26,6 @@ + + void pa_init_proplist(pa_proplist *p); + char *pa_proplist_get_stream_group(pa_proplist *pl, const char *prefix, const char *cache); ++char *pa_proplist_get_stream_group_extended(pa_proplist *p, const char *prefix, const char *cache, const char *preferred_stream_group); + + #endif diff --git a/meta-tizen/recipes-multimedia/pulseaudio/pulseaudio_5.0/0031-increase-alsa-rewind-safeguard.patch b/meta-tizen/recipes-multimedia/pulseaudio/pulseaudio_5.0/0031-increase-alsa-rewind-safeguard.patch new file mode 100644 index 0000000..eac1811 --- /dev/null +++ b/meta-tizen/recipes-multimedia/pulseaudio/pulseaudio_5.0/0031-increase-alsa-rewind-safeguard.patch @@ -0,0 +1,39 @@ +From: Jaska Uimonen +Date: Mon, 28 Apr 2014 11:26:15 +0300 +Subject: increase alsa rewind safeguard + +In some devices alsa drivers behaves badly +if pulseaudio rewind safeguard is too small. +This is not fixing the driver issues, but is +a workaround to give time (1.33ms->5ms) for +user space processing so that alsa is not +getting into this weird state. This could be +also helped by running pulseaudio in realtime. +This is of course increasing the volume setting +latency, but should leave it still quite +responsive. + +Change-Id: Iecdf879bf8ba58e991808d2dc382def05de36ec9 +Signed-off-by: Jaska Uimonen +--- + src/modules/alsa/alsa-sink.c | 5 +++++ + 1 file changed, 5 insertions(+) + +diff --git a/src/modules/alsa/alsa-sink.c b/src/modules/alsa/alsa-sink.c +index ccf1137..5a41cf6 100644 +--- a/src/modules/alsa/alsa-sink.c ++++ b/src/modules/alsa/alsa-sink.c +@@ -87,8 +87,13 @@ + + #define VOLUME_ACCURACY (PA_VOLUME_NORM/100) /* don't require volume adjustments to be perfectly correct. don't necessarily extend granularity in software unless the differences get greater than this level */ + ++#if 0 + #define DEFAULT_REWIND_SAFEGUARD_BYTES (256U) /* 1.33ms @48kHz, we'll never rewind less than this */ + #define DEFAULT_REWIND_SAFEGUARD_USEC (1330) /* 1.33ms, depending on channels/rate/sample we may rewind more than 256 above */ ++#endif ++ ++#define DEFAULT_REWIND_SAFEGUARD_BYTES (1024U) /* increase safeguard 4x */ ++#define DEFAULT_REWIND_SAFEGUARD_USEC (5000) /* increase safeguard ~4x */ + + struct userdata { + pa_core *core; diff --git a/meta-tizen/recipes-multimedia/pulseaudio/pulseaudio_5.0/0032-fix-for-profile-change-prototype-in-bluez5-patch.patch b/meta-tizen/recipes-multimedia/pulseaudio/pulseaudio_5.0/0032-fix-for-profile-change-prototype-in-bluez5-patch.patch new file mode 100644 index 0000000..646694a --- /dev/null +++ b/meta-tizen/recipes-multimedia/pulseaudio/pulseaudio_5.0/0032-fix-for-profile-change-prototype-in-bluez5-patch.patch @@ -0,0 +1,23 @@ +From: Jaska Uimonen +Date: Tue, 11 Mar 2014 12:23:09 +0200 +Subject: fix for profile change prototype in bluez5 patch + +Change-Id: I2361c4eca82e6ac4a8f94e9f9c97f09cb6648049 +Signed-off-by: Jaska Uimonen +--- + src/modules/bluetooth/module-bluez5-device.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/src/modules/bluetooth/module-bluez5-device.c b/src/modules/bluetooth/module-bluez5-device.c +index a0776c8..790dcf1 100644 +--- a/src/modules/bluetooth/module-bluez5-device.c ++++ b/src/modules/bluetooth/module-bluez5-device.c +@@ -2014,7 +2014,7 @@ static int device_process_msg(pa_msgobject *obj, int code, void *data, int64_t o + break; + + pa_log_debug("Switching the profile to off due to IO thread failure."); +- pa_assert_se(pa_card_set_profile(b->card, "off", false) >= 0); ++ pa_assert_se(pa_card_set_profile(b->card, pa_hashmap_get(u->card->profiles, "off"), false) >= 0); + break; + } + diff --git a/meta-tizen/recipes-multimedia/pulseaudio/pulseaudio_5.0/0033-changes-to-pa-simple-api-samsung.patch b/meta-tizen/recipes-multimedia/pulseaudio/pulseaudio_5.0/0033-changes-to-pa-simple-api-samsung.patch new file mode 100644 index 0000000..e041892 --- /dev/null +++ b/meta-tizen/recipes-multimedia/pulseaudio/pulseaudio_5.0/0033-changes-to-pa-simple-api-samsung.patch @@ -0,0 +1,438 @@ +From: "vivian,zhang" +Date: Tue, 18 Jun 2013 16:10:15 +0800 +Subject: changes to pa simple api - samsung + +Change-Id: I997c02217a8dc14524480164aa0baeea901c7b4e +Signed-off-by: Jaska Uimonen +--- + src/Makefile.am | 4 +- + src/map-file | 6 ++ + src/pulse/simple.c | 288 +++++++++++++++++++++++++++++++++++++++++++++++++++++ + src/pulse/simple.h | 28 ++++++ + 4 files changed, 324 insertions(+), 2 deletions(-) + +diff --git a/src/Makefile.am b/src/Makefile.am +index 2edce5f..5ec8609 100644 +--- a/src/Makefile.am ++++ b/src/Makefile.am +@@ -824,7 +824,7 @@ libpulse_la_SOURCES = \ + pulse/volume.c pulse/volume.h \ + pulse/xmalloc.c pulse/xmalloc.h + +-libpulse_la_CFLAGS = $(AM_CFLAGS) $(LIBJSON_CFLAGS) ++libpulse_la_CFLAGS = $(AM_CFLAGS) $(DBUS_CFLAGS) $(LIBJSON_CFLAGS) + libpulse_la_LIBADD = $(AM_LIBADD) $(WINSOCK_LIBS) $(LTLIBICONV) $(LIBJSON_LIBS) libpulsecommon-@PA_MAJORMINOR@.la + libpulse_la_LDFLAGS = $(AM_LDFLAGS) $(VERSIONING_LDFLAGS) -version-info $(LIBPULSE_VERSION_INFO) + +@@ -834,7 +834,7 @@ libpulse_la_LIBADD += $(DBUS_LIBS) + endif + + libpulse_simple_la_SOURCES = pulse/simple.c pulse/simple.h +-libpulse_simple_la_CFLAGS = $(AM_CFLAGS) ++libpulse_simple_la_CFLAGS = $(AM_CFLAGS) $(DBUS_CFLAGS) + libpulse_simple_la_LIBADD = $(AM_LIBADD) libpulse.la libpulsecommon-@PA_MAJORMINOR@.la + libpulse_simple_la_LDFLAGS = $(AM_LDFLAGS) $(VERSIONING_LDFLAGS) -version-info $(LIBPULSE_SIMPLE_VERSION_INFO) + +diff --git a/src/map-file b/src/map-file +index 7dbcd00..95364ae 100644 +--- a/src/map-file ++++ b/src/map-file +@@ -282,11 +282,17 @@ pa_signal_new; + pa_signal_set_destroy; + pa_simple_drain; + pa_simple_flush; ++pa_simple_mute; + pa_simple_free; + pa_simple_get_latency; + pa_simple_new; ++pa_simple_new_proplist; + pa_simple_read; + pa_simple_write; ++pa_simple_set_volume; ++pa_simple_get_stream_index; ++pa_simple_cork; ++pa_simple_is_corked; + pa_stream_begin_write; + pa_stream_cancel_write; + pa_stream_connect_playback; +diff --git a/src/pulse/simple.c b/src/pulse/simple.c +index 1891131..9109ffa 100644 +--- a/src/pulse/simple.c ++++ b/src/pulse/simple.c +@@ -32,10 +32,12 @@ + #include + #include + ++#include + #include + #include + + #include "simple.h" ++#include "internal.h" + + struct pa_simple { + pa_threaded_mainloop *mainloop; +@@ -102,6 +104,24 @@ static void context_state_cb(pa_context *c, void *userdata) { + } + } + ++static void stream_success_context_cb(pa_stream *s, int success, void *userdata) { ++ pa_simple *p = userdata; ++ pa_assert(s); ++ pa_assert(p); ++ ++ p->operation_success = success; ++ pa_threaded_mainloop_signal(p->mainloop, 0); ++} ++ ++static void success_context_cb(pa_context *c, int success, void *userdata) { ++ pa_simple *p = userdata; ++ pa_assert(c); ++ pa_assert(p); ++ ++ p->operation_success = success; ++ pa_threaded_mainloop_signal(p->mainloop, 0); ++} ++ + static void stream_state_cb(pa_stream *s, void * userdata) { + pa_simple *p = userdata; + pa_assert(s); +@@ -251,6 +271,122 @@ fail: + return NULL; + } + ++pa_simple* pa_simple_new_proplist( ++ const char *server, ++ const char *name, ++ pa_stream_direction_t dir, ++ const char *dev, ++ const char *stream_name, ++ const pa_sample_spec *ss, ++ const pa_channel_map *map, ++ const pa_buffer_attr *attr, ++ pa_proplist *proplist, ++ int *rerror) { ++ ++ pa_simple *p; ++ int error = PA_ERR_INTERNAL, r; ++ ++ CHECK_VALIDITY_RETURN_ANY(rerror, !server || *server, PA_ERR_INVALID, NULL); ++ CHECK_VALIDITY_RETURN_ANY(rerror, dir == PA_STREAM_PLAYBACK || dir == PA_STREAM_RECORD, PA_ERR_INVALID, NULL); ++ CHECK_VALIDITY_RETURN_ANY(rerror, !dev || *dev, PA_ERR_INVALID, NULL); ++ CHECK_VALIDITY_RETURN_ANY(rerror, ss && pa_sample_spec_valid(ss), PA_ERR_INVALID, NULL); ++ CHECK_VALIDITY_RETURN_ANY(rerror, !map || (pa_channel_map_valid(map) && map->channels == ss->channels), PA_ERR_INVALID, NULL) ++ ++ p = pa_xnew0(pa_simple, 1); ++ p->direction = dir; ++ ++ if (!(p->mainloop = pa_threaded_mainloop_new())) ++ goto fail; ++ ++ if (!(p->context = pa_context_new(pa_threaded_mainloop_get_api(p->mainloop), name))) ++ goto fail; ++ ++ pa_context_set_state_callback(p->context, context_state_cb, p); ++ ++ if (pa_context_connect(p->context, server, 0, NULL) < 0) { ++ error = pa_context_errno(p->context); ++ goto fail; ++ } ++ ++ pa_threaded_mainloop_lock(p->mainloop); ++ ++ if (pa_threaded_mainloop_start(p->mainloop) < 0) ++ goto unlock_and_fail; ++ ++ for (;;) { ++ pa_context_state_t state; ++ ++ state = pa_context_get_state(p->context); ++ ++ if (state == PA_CONTEXT_READY) ++ break; ++ ++ if (!PA_CONTEXT_IS_GOOD(state)) { ++ error = pa_context_errno(p->context); ++ goto unlock_and_fail; ++ } ++ ++ /* Wait until the context is ready */ ++ pa_threaded_mainloop_wait(p->mainloop); ++ } ++ ++ if (!(p->stream = pa_stream_new_with_proplist(p->context, stream_name, ss, map, proplist))) { ++ error = pa_context_errno(p->context); ++ goto unlock_and_fail; ++ } ++ ++ pa_stream_set_state_callback(p->stream, stream_state_cb, p); ++ pa_stream_set_read_callback(p->stream, stream_request_cb, p); ++ pa_stream_set_write_callback(p->stream, stream_request_cb, p); ++ pa_stream_set_latency_update_callback(p->stream, stream_latency_update_cb, p); ++ ++ if (dir == PA_STREAM_PLAYBACK) ++ r = pa_stream_connect_playback(p->stream, dev, attr, ++ PA_STREAM_INTERPOLATE_TIMING ++ |PA_STREAM_ADJUST_LATENCY ++ |PA_STREAM_AUTO_TIMING_UPDATE, NULL, NULL); ++ else ++ r = pa_stream_connect_record(p->stream, dev, attr, ++ PA_STREAM_INTERPOLATE_TIMING ++ |PA_STREAM_ADJUST_LATENCY ++ |PA_STREAM_AUTO_TIMING_UPDATE ++ |PA_STREAM_START_CORKED); ++ ++ if (r < 0) { ++ error = pa_context_errno(p->context); ++ goto unlock_and_fail; ++ } ++ ++ for (;;) { ++ pa_stream_state_t state; ++ ++ state = pa_stream_get_state(p->stream); ++ ++ if (state == PA_STREAM_READY) ++ break; ++ ++ if (!PA_STREAM_IS_GOOD(state)) { ++ error = pa_context_errno(p->context); ++ goto unlock_and_fail; ++ } ++ ++ /* Wait until the stream is ready */ ++ pa_threaded_mainloop_wait(p->mainloop); ++ } ++ ++ pa_threaded_mainloop_unlock(p->mainloop); ++ ++ return p; ++ ++unlock_and_fail: ++ pa_threaded_mainloop_unlock(p->mainloop); ++ ++fail: ++ if (rerror) ++ *rerror = error; ++ pa_simple_free(p); ++ return NULL; ++} + void pa_simple_free(pa_simple *s) { + pa_assert(s); + +@@ -452,6 +588,111 @@ unlock_and_fail: + return -1; + } + ++int pa_simple_mute(pa_simple *p, int mute, int *rerror) { ++ pa_operation *o = NULL; ++ uint32_t idx; ++ ++ pa_assert(p); ++ ++ CHECK_VALIDITY_RETURN_ANY(rerror, p->direction == PA_STREAM_PLAYBACK, PA_ERR_BADSTATE, -1); ++ ++ pa_threaded_mainloop_lock(p->mainloop); ++ CHECK_DEAD_GOTO(p, rerror, unlock_and_fail); ++ ++ CHECK_SUCCESS_GOTO(p, rerror, ((idx = pa_stream_get_index (p->stream)) != PA_INVALID_INDEX), unlock_and_fail); ++ ++ ++ o = pa_context_set_sink_input_mute (p->context, idx, mute, success_context_cb, p); ++ CHECK_SUCCESS_GOTO(p, rerror, o, unlock_and_fail); ++ ++ p->operation_success = 0; ++ while (pa_operation_get_state(o) == PA_OPERATION_RUNNING) { ++ pa_threaded_mainloop_wait(p->mainloop); ++ CHECK_DEAD_GOTO(p, rerror, unlock_and_fail); ++ } ++ CHECK_SUCCESS_GOTO(p, rerror, p->operation_success, unlock_and_fail); ++ ++ pa_operation_unref(o); ++ pa_threaded_mainloop_unlock(p->mainloop); ++ ++ return 0; ++ ++unlock_and_fail: ++ ++ if (o) { ++ pa_operation_cancel(o); ++ pa_operation_unref(o); ++ } ++ ++ pa_threaded_mainloop_unlock(p->mainloop); ++ return -1; ++} ++ ++int pa_simple_get_stream_index(pa_simple *p, unsigned int *idx, int *rerror) { ++ pa_assert(p); ++ CHECK_VALIDITY_RETURN_ANY(rerror, idx != NULL, PA_ERR_INVALID, -1); ++ ++ pa_threaded_mainloop_lock(p->mainloop); ++ ++ CHECK_DEAD_GOTO(p, rerror, unlock_and_fail); ++ ++ *idx = pa_stream_get_index(p->stream); ++ ++ pa_threaded_mainloop_unlock(p->mainloop); ++ return 0; ++ ++unlock_and_fail: ++ pa_threaded_mainloop_unlock(p->mainloop); ++ return -1; ++} ++ ++int pa_simple_set_volume(pa_simple *p, int volume, int *rerror) { ++ pa_operation *o = NULL; ++ pa_stream *s = NULL; ++ uint32_t idx; ++ pa_cvolume cv; ++ ++ ++ pa_assert(p); ++ ++ CHECK_VALIDITY_RETURN_ANY(rerror, p->direction == PA_STREAM_PLAYBACK, PA_ERR_BADSTATE, -1); ++ CHECK_VALIDITY_RETURN_ANY(rerror, volume >= 0, PA_ERR_INVALID, -1); ++ CHECK_VALIDITY_RETURN_ANY(rerror, volume <= 65535, PA_ERR_INVALID, -1); ++ ++ pa_threaded_mainloop_lock(p->mainloop); ++ CHECK_DEAD_GOTO(p, rerror, unlock_and_fail); ++ ++ CHECK_SUCCESS_GOTO(p, rerror, ((idx = pa_stream_get_index (p->stream)) != PA_INVALID_INDEX), unlock_and_fail); ++ ++ s = p->stream; ++ pa_assert(s); ++ pa_cvolume_set(&cv, s->sample_spec.channels, volume); ++ ++ o = pa_context_set_sink_input_volume (p->context, idx, &cv, success_context_cb, p); ++ CHECK_SUCCESS_GOTO(p, rerror, o, unlock_and_fail); ++ ++ p->operation_success = 0; ++ while (pa_operation_get_state(o) == PA_OPERATION_RUNNING) { ++ pa_threaded_mainloop_wait(p->mainloop); ++ CHECK_DEAD_GOTO(p, rerror, unlock_and_fail); ++ } ++ CHECK_SUCCESS_GOTO(p, rerror, p->operation_success, unlock_and_fail); ++ ++ pa_operation_unref(o); ++ pa_threaded_mainloop_unlock(p->mainloop); ++ ++ return 0; ++ ++unlock_and_fail: ++ ++ if (o) { ++ pa_operation_cancel(o); ++ pa_operation_unref(o); ++ } ++ ++ pa_threaded_mainloop_unlock(p->mainloop); ++ return -1; ++} + pa_usec_t pa_simple_get_latency(pa_simple *p, int *rerror) { + pa_usec_t t; + int negative; +@@ -481,3 +722,50 @@ unlock_and_fail: + pa_threaded_mainloop_unlock(p->mainloop); + return (pa_usec_t) -1; + } ++ ++int pa_simple_cork(pa_simple *p, int cork, int *rerror) { ++ pa_operation *o = NULL; ++ ++ pa_assert(p); ++ ++ pa_threaded_mainloop_lock(p->mainloop); ++ CHECK_DEAD_GOTO(p, rerror, unlock_and_fail); ++ ++ o = pa_stream_cork(p->stream, cork, stream_success_context_cb, p); ++ CHECK_SUCCESS_GOTO(p, rerror, o, unlock_and_fail); ++ ++ p->operation_success = 0; ++ while (pa_operation_get_state(o) == PA_OPERATION_RUNNING) { ++ pa_threaded_mainloop_wait(p->mainloop); ++ CHECK_DEAD_GOTO(p, rerror, unlock_and_fail); ++ } ++ CHECK_SUCCESS_GOTO(p, rerror, p->operation_success, unlock_and_fail); ++ ++ pa_operation_unref(o); ++ pa_threaded_mainloop_unlock(p->mainloop); ++ ++ return 0; ++ ++unlock_and_fail: ++ ++ if (o) { ++ pa_operation_cancel(o); ++ pa_operation_unref(o); ++ } ++ ++ pa_threaded_mainloop_unlock(p->mainloop); ++ return -1; ++} ++ ++int pa_simple_is_corked(pa_simple *p) { ++ int is_cork; ++ pa_assert(p); ++ ++ pa_threaded_mainloop_lock(p->mainloop); ++ ++ is_cork = pa_stream_is_corked(p->stream); ++ ++ pa_threaded_mainloop_unlock(p->mainloop); ++ ++ return is_cork; ++} +diff --git a/src/pulse/simple.h b/src/pulse/simple.h +index 54003ff..4f4a988 100644 +--- a/src/pulse/simple.h ++++ b/src/pulse/simple.h +@@ -31,6 +31,7 @@ + #include + #include + ++#include + /** \page simple Simple API + * + * \section overv_sec Overview +@@ -129,6 +130,19 @@ pa_simple* pa_simple_new( + int *error /**< A pointer where the error code is stored when the routine returns NULL. It is OK to pass NULL here. */ + ); + ++/** Create a new connection to the server with proplist */ ++pa_simple* pa_simple_new_proplist( ++ const char *server, /**< Server name, or NULL for default */ ++ const char *name, /**< A descriptive name for this client (application name, ...) */ ++ pa_stream_direction_t dir, /**< Open this stream for recording or playback? */ ++ const char *dev, /**< Sink (resp. source) name, or NULL for default */ ++ const char *stream_name, /**< A descriptive name for this client (application name, song title, ...) */ ++ const pa_sample_spec *ss, /**< The sample type to use */ ++ const pa_channel_map *map, /**< The channel map to use, or NULL for default */ ++ const pa_buffer_attr *attr, /**< Buffering attributes, or NULL for default */ ++ pa_proplist *proplist, /**< Properties, or NULL for default */ ++ int *error /**< A pointer where the error code is stored when the routine returns NULL. It is OK to pass NULL here. */ ++ ); + /** Close and free the connection to the server. The connection object becomes invalid when this is called. */ + void pa_simple_free(pa_simple *s); + +@@ -155,6 +169,20 @@ pa_usec_t pa_simple_get_latency(pa_simple *s, int *error); + + /** Flush the playback or record buffer. This discards any audio in the buffer. */ + int pa_simple_flush(pa_simple *s, int *error); ++/** Mute the playback stream */ ++int pa_simple_mute(pa_simple *p, int mute, int *rerror); ++ ++/** Volume control the playback stream */ ++int pa_simple_set_volume(pa_simple *p, int volume, int *rerror); ++ ++/** Get stream index */ ++int pa_simple_get_stream_index(pa_simple *p, unsigned int *idx, int *rerror); ++ ++/** Cork on=1/off=0 stream */ ++int pa_simple_cork(pa_simple *p, int cork, int *rerror); ++ ++/** Check whether stream is corked or not */ ++int pa_simple_is_corked(pa_simple *p); + + PA_C_DECL_END + diff --git a/meta-tizen/recipes-multimedia/pulseaudio/pulseaudio_5.0/0034-add-support-for-samsung-power-management-samsung.patch b/meta-tizen/recipes-multimedia/pulseaudio/pulseaudio_5.0/0034-add-support-for-samsung-power-management-samsung.patch new file mode 100644 index 0000000..5d2380b --- /dev/null +++ b/meta-tizen/recipes-multimedia/pulseaudio/pulseaudio_5.0/0034-add-support-for-samsung-power-management-samsung.patch @@ -0,0 +1,301 @@ +From: "vivian,zhang" +Date: Tue, 18 Jun 2013 16:11:16 +0800 +Subject: add support for samsung power management - samsung + +Change-Id: Id76a3971e36c08773848fdf1ac1cc9a9200d7330 +Signed-off-by: Jaska Uimonen +--- + configure.ac | 12 +++ + src/Makefile.am | 3 + + src/modules/module-suspend-on-idle.c | 179 ++++++++++++++++++++++++++++++++++- + 3 files changed, 192 insertions(+), 2 deletions(-) + +diff --git a/configure.ac b/configure.ac +index a35918c..9a79b36 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -673,6 +673,18 @@ AS_IF([test "x$enable_samplerate" = "xyes" && test "x$HAVE_LIBSAMPLERATE" = "x0" + AM_CONDITIONAL([HAVE_LIBSAMPLERATE], [test "x$HAVE_LIBSAMPLERATE" = x1]) + AS_IF([test "x$HAVE_LIBSAMPLERATE" = "x1"], AC_DEFINE([HAVE_LIBSAMPLERATE], 1, [Have libsamplerate?])) + ++#### samsung PM API support #### ++ ++AC_ARG_ENABLE(pmlock, AC_HELP_STRING([--enable-pmlock], [using Samsung power management api]), ++[ ++ case "${enableval}" in ++ yes) USE_PM_LOCK=yes ;; ++ no) USE_PM_LOCK=no ;; ++ *) AC_MSG_ERROR(bad value ${enableval} for --enable-pmlock) ;; ++ esac ++ ],[USE_PM_LOCK=no]) ++AM_CONDITIONAL(USE_PM_LOCK, test "x$USE_PM_LOCK" = "xyes") ++ + #### Database support #### + + AC_ARG_WITH([database], +diff --git a/src/Makefile.am b/src/Makefile.am +index 5ec8609..12bb73e 100644 +--- a/src/Makefile.am ++++ b/src/Makefile.am +@@ -1940,6 +1940,9 @@ module_suspend_on_idle_la_SOURCES = modules/module-suspend-on-idle.c + module_suspend_on_idle_la_LDFLAGS = $(MODULE_LDFLAGS) + module_suspend_on_idle_la_LIBADD = $(MODULE_LIBADD) + module_suspend_on_idle_la_CFLAGS = $(AM_CFLAGS) ++if USE_PM_LOCK ++module_suspend_on_idle_la_CFLAGS += -DUSE_PM_LOCK ++endif + + # echo-cancel module + module_echo_cancel_la_SOURCES = \ +diff --git a/src/modules/module-suspend-on-idle.c b/src/modules/module-suspend-on-idle.c +index 15cbf95..8350917 100644 +--- a/src/modules/module-suspend-on-idle.c ++++ b/src/modules/module-suspend-on-idle.c +@@ -34,7 +34,53 @@ + #include + #include + ++#include + #include "module-suspend-on-idle-symdef.h" ++//move to configure.ac ++//#define USE_PM_LOCK /* Enable as default */ ++#ifdef USE_PM_LOCK ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#define SOCK_PATH "/tmp/pm_sock" ++#define SHIFT_UNLOCK 4 ++#define SHIFT_UNLOCK_PARAMETER 12 ++#define SHIFT_CHANGE_STATE 8 ++#define SHIFT_HOLD_KEY_BLOCK 16 ++#define SHIFT_CHANGE_TIMEOUT 20 ++#define TIMEOUT_RESET_BIT 0x80 ++ ++#define LCD_NORMAL 0x1 /**< NORMAL state */ ++#define LCD_DIM 0x2 /**< LCD dimming state */ ++#define LCD_OFF 0x4 /**< LCD off state */ ++#define SUSPEND 0x8 /**< Sleep state */ ++#define POWER_OFF 0x16 /**< Sleep state */ ++#define SETALL (LCD_DIM | LCD_OFF | LCD_NORMAL) /*< select all state - not supported yet */ ++ ++/* parameters for pm_lock_state() */ ++#define STAY_CUR_STATE 0x0 ++#define GOTO_STATE_NOW 0x1 ++#define HOLD_KEY_BLOCK 0x2 ++ ++/* paramters for pm_unlcok_state() - details are described at 162 line */ ++#define PM_SLEEP_MARGIN 0x0 /**< keep guard time for unlock */ ++#define PM_RESET_TIMER 0x1 /**< reset timer for unlock */ ++#define PM_KEEP_TIMER 0x2 /**< keep timer for unlock */ ++ ++struct pwr_msg { ++ pid_t pid; ++ unsigned int cond; ++ unsigned int timeout; ++ unsigned int timeout2; ++}; ++ ++#endif /* USE_PM_LOCK */ + + PA_MODULE_AUTHOR("Lennart Poettering"); + PA_MODULE_DESCRIPTION("When a sink/source is idle for too long, suspend it"); +@@ -47,6 +93,13 @@ static const char* const valid_modargs[] = { + NULL, + }; + ++#ifdef USE_PM_LOCK ++#define PM_TYPE_SINK 0x01 ++#define PM_TYPE_SOURCE 0x02 ++ ++#define UPDATE_PM_LOCK(current,type) (current |= type) ++#define UPDATE_PM_UNLOCK(current,type) (current &= ~type) ++#endif /* USE_PM_LOCK */ + struct userdata { + pa_core *core; + pa_usec_t timeout; +@@ -70,6 +123,9 @@ struct userdata { + *source_output_move_finish_slot, + *sink_input_state_changed_slot, + *source_output_state_changed_slot; ++#ifdef USE_PM_LOCK ++ uint32_t pm_state; ++#endif /* USE_PM_LOCK */ + }; + + struct device_info { +@@ -80,10 +136,83 @@ struct device_info { + pa_time_event *time_event; + pa_usec_t timeout; + }; ++#ifdef USE_PM_LOCK ++ ++static int send_msg(unsigned int s_bits, unsigned int timeout, unsigned int timeout2) ++{ ++ int rc = 0; ++ int sock; ++ struct pwr_msg p; ++ struct sockaddr_un remote; ++ ++ p.pid = getpid(); ++ p.cond = s_bits; ++ p.timeout = timeout; ++ p.timeout2 = timeout2; ++ ++ sock = socket(AF_UNIX, SOCK_DGRAM, 0); ++ if (sock == -1) { ++ return -1; ++ } ++ ++ remote.sun_family = AF_UNIX; ++ if(strlen(SOCK_PATH) >= sizeof(remote.sun_path)) { ++ return -1; ++ } ++ strncpy(remote.sun_path, SOCK_PATH, sizeof(remote.sun_path)); ++ ++ rc = sendto(sock, (void *)&p, sizeof(p), 0, (struct sockaddr *)&remote, ++ sizeof(struct sockaddr_un)); ++ ++ close(sock); ++ return rc; ++} ++ ++static int pm_lock_state(unsigned int s_bits, unsigned int flag, ++ unsigned int timeout) ++{ ++ switch (s_bits) { ++ case LCD_NORMAL: ++ case LCD_DIM: ++ case LCD_OFF: ++ break; ++ default: ++ return -1; ++ } ++ if (flag & GOTO_STATE_NOW) ++ /* if the flag is true, go to the locking state directly */ ++ s_bits = s_bits | (s_bits << SHIFT_CHANGE_STATE); ++ if (flag & HOLD_KEY_BLOCK) ++ s_bits = s_bits | (1 << SHIFT_HOLD_KEY_BLOCK); ++ ++ return send_msg(s_bits, timeout, 0); ++} ++ ++static int pm_unlock_state(unsigned int s_bits, unsigned int flag) ++{ ++ switch (s_bits) { ++ case LCD_NORMAL: ++ case LCD_DIM: ++ case LCD_OFF: ++ break; ++ default: ++ return -1; ++ } ++ ++ s_bits = (s_bits << SHIFT_UNLOCK); ++ s_bits = (s_bits | (flag << SHIFT_UNLOCK_PARAMETER)); ++ return send_msg(s_bits, 0, 0); ++} ++ ++#endif + + static void timeout_cb(pa_mainloop_api*a, pa_time_event* e, const struct timeval *t, void *userdata) { + struct device_info *d = userdata; + ++#ifdef USE_PM_LOCK ++ int ret = -1; ++#endif ++ + pa_assert(d); + + d->userdata->core->mainloop->time_restart(d->time_event, NULL); +@@ -92,12 +221,33 @@ static void timeout_cb(pa_mainloop_api*a, pa_time_event* e, const struct timeval + pa_log_info("Sink %s idle for too long, suspending ...", d->sink->name); + pa_sink_suspend(d->sink, true, PA_SUSPEND_IDLE); + pa_core_maybe_vacuum(d->userdata->core); ++#ifdef USE_PM_LOCK ++ UPDATE_PM_UNLOCK(d->userdata->pm_state, PM_TYPE_SINK); ++ if(!(d->userdata->pm_state)) { ++ ret = pm_unlock_state(LCD_OFF, PM_SLEEP_MARGIN); ++ if(ret != -1) ++ pa_log_info("sink pm_unlock_state success [%d]", ret); ++ else ++ pa_log_error("sink pm_unlock_state failed [%d]", ret); ++ } ++#endif /* USE_PM_LOCK */ + } + + if (d->source && pa_source_check_suspend(d->source) <= 0 && !(d->source->suspend_cause & PA_SUSPEND_IDLE)) { + pa_log_info("Source %s idle for too long, suspending ...", d->source->name); + pa_source_suspend(d->source, true, PA_SUSPEND_IDLE); + pa_core_maybe_vacuum(d->userdata->core); ++#ifdef USE_PM_LOCK ++ ++ UPDATE_PM_UNLOCK(d->userdata->pm_state, PM_TYPE_SOURCE); ++ if(!(d->userdata->pm_state)) { ++ ret = pm_unlock_state(LCD_OFF, PM_SLEEP_MARGIN); ++ if(ret != -1) ++ pa_log_info("source pm_unlock_state success [%d]", ret); ++ else ++ pa_log_error("source pm_unlock_state failed [%d]", ret); ++ } ++#endif /* USE_PM_LOCK */ + } + } + +@@ -117,17 +267,39 @@ static void restart(struct device_info *d) { + } + + static void resume(struct device_info *d) { ++#ifdef USE_PM_LOCK ++ int ret = -1; ++#endif ++ + pa_assert(d); + + d->userdata->core->mainloop->time_restart(d->time_event, NULL); + + if (d->sink) { +- pa_log_debug("Sink %s becomes busy, resuming.", d->sink->name); ++#ifdef USE_PM_LOCK ++ UPDATE_PM_LOCK(d->userdata->pm_state, PM_TYPE_SINK); ++ ret = pm_lock_state(LCD_OFF, STAY_CUR_STATE, 0); ++ if(ret != -1) { ++ pa_log_info("sink pm_lock_state success [%d]", ret); ++ } else { ++ pa_log_error("sink pm_lock_state failed [%d]", ret); ++ } ++#endif /* USE_PM_LOCK */ ++ pa_log_debug("Sink %s becomes busy.", d->sink->name); + pa_sink_suspend(d->sink, false, PA_SUSPEND_IDLE); + } + + if (d->source) { +- pa_log_debug("Source %s becomes busy, resuming.", d->source->name); ++#ifdef USE_PM_LOCK ++ UPDATE_PM_LOCK(d->userdata->pm_state, PM_TYPE_SOURCE); ++ ret = pm_lock_state(LCD_OFF, STAY_CUR_STATE, 0); ++ if(ret != -1) { ++ pa_log_info("source pm_lock_state success [%d]", ret); ++ } else { ++ pa_log_error("source pm_lock_state failed [%d]", ret); ++ } ++#endif /* USE_PM_LOCK */ ++ pa_log_debug("Source %s becomes busy.", d->source->name); + pa_source_suspend(d->source, false, PA_SUSPEND_IDLE); + } + } +@@ -462,6 +634,9 @@ int pa__init(pa_module*m) { + u->core = m->core; + u->timeout = timeout * PA_USEC_PER_SEC; + u->device_infos = pa_hashmap_new_full(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func, NULL, (pa_free_cb_t) device_info_free); ++#ifdef USE_PM_LOCK ++ u->pm_state = 0x00; ++#endif /* USE_PM_LOCK */ + + PA_IDXSET_FOREACH(sink, m->core->sinks, idx) + device_new_hook_cb(m->core, PA_OBJECT(sink), u); diff --git a/meta-tizen/recipes-multimedia/pulseaudio/pulseaudio_5.0/0035-Add-preload-fileter-for-resample-samsung.patch.gz b/meta-tizen/recipes-multimedia/pulseaudio/pulseaudio_5.0/0035-Add-preload-fileter-for-resample-samsung.patch.gz new file mode 100644 index 0000000..a8f29d7 Binary files /dev/null and b/meta-tizen/recipes-multimedia/pulseaudio/pulseaudio_5.0/0035-Add-preload-fileter-for-resample-samsung.patch.gz differ diff --git a/meta-tizen/recipes-multimedia/pulseaudio/pulseaudio_5.0/0036-Enhance-for-echo-cancel-samsung.patch b/meta-tizen/recipes-multimedia/pulseaudio/pulseaudio_5.0/0036-Enhance-for-echo-cancel-samsung.patch new file mode 100644 index 0000000..19bbb16 --- /dev/null +++ b/meta-tizen/recipes-multimedia/pulseaudio/pulseaudio_5.0/0036-Enhance-for-echo-cancel-samsung.patch @@ -0,0 +1,304 @@ +From: "vivian,zhang" +Date: Tue, 18 Jun 2013 16:18:58 +0800 +Subject: Enhance for echo cancel - samsung + +Change-Id: Ibd59e7e033d5a6789ddc7d5ef39e23f26dcf55cc +Signed-off-by: Jaska Uimonen +--- + src/Makefile.am | 2 +- + src/map-file | 2 + + src/modules/echo-cancel/module-echo-cancel.c | 55 +++++++++++++++ + src/pulse/ext-echo-cancel.c | 100 +++++++++++++++++++++++++++ + src/pulse/ext-echo-cancel.h | 49 +++++++++++++ + 5 files changed, 207 insertions(+), 1 deletion(-) + create mode 100644 src/pulse/ext-echo-cancel.c + create mode 100644 src/pulse/ext-echo-cancel.h + +diff --git a/src/Makefile.am b/src/Makefile.am +index 12bb73e..ae841e3 100644 +--- a/src/Makefile.am ++++ b/src/Makefile.am +@@ -1950,7 +1950,7 @@ module_echo_cancel_la_SOURCES = \ + modules/echo-cancel/null.c \ + modules/echo-cancel/echo-cancel.h + module_echo_cancel_la_LDFLAGS = $(MODULE_LDFLAGS) +-module_echo_cancel_la_LIBADD = $(MODULE_LIBADD) $(LIBSPEEX_LIBS) ++module_echo_cancel_la_LIBADD = libprotocol-native.la $(MODULE_LIBADD) $(LIBSPEEX_LIBS) + module_echo_cancel_la_CFLAGS = $(AM_CFLAGS) $(SERVER_CFLAGS) $(LIBSPEEX_CFLAGS) + if HAVE_ADRIAN_EC + module_echo_cancel_la_SOURCES += \ +diff --git a/src/map-file b/src/map-file +index 95364ae..d51596c 100644 +--- a/src/map-file ++++ b/src/map-file +@@ -182,6 +182,8 @@ pa_ext_node_manager_connect_nodes; + pa_ext_node_manager_disconnect_nodes; + pa_ext_node_manager_subscribe; + pa_ext_node_manager_set_subscribe_cb; ++pa_ext_echo_cancel_set_volume; ++pa_ext_echo_cancel_set_device; + pa_format_info_copy; + pa_format_info_free; + pa_format_info_from_string; +diff --git a/src/modules/echo-cancel/module-echo-cancel.c b/src/modules/echo-cancel/module-echo-cancel.c +index fbdb3b3..29eed13 100644 +--- a/src/modules/echo-cancel/module-echo-cancel.c ++++ b/src/modules/echo-cancel/module-echo-cancel.c +@@ -53,6 +53,9 @@ + #include + #include + ++#include ++#include ++ + #include "module-echo-cancel-symdef.h" + + PA_MODULE_AUTHOR("Wim Taymans"); +@@ -94,6 +97,11 @@ typedef enum { + #endif + } pa_echo_canceller_method_t; + ++enum { ++ AEC_SET_VOLUME, ++ AEC_SET_DEVICE, ++}; ++ + #ifdef HAVE_WEBRTC + #define DEFAULT_ECHO_CANCELLER "webrtc" + #else +@@ -255,6 +263,8 @@ struct userdata { + struct { + pa_cvolume current_volume; + } thread_info; ++ ++ pa_native_protocol *protocol; + }; + + static void source_output_snapshot_within_thread(struct userdata *u, struct snapshot *snapshot); +@@ -1602,6 +1612,43 @@ static pa_echo_canceller_method_t get_ec_method_from_string(const char *method) + return PA_ECHO_CANCELLER_INVALID; + } + ++static int extension_cb(pa_native_protocol *p, pa_module *m, pa_native_connection *c, uint32_t tag, pa_tagstruct *t) { ++ uint32_t command; ++ uint32_t value; ++ pa_tagstruct *reply = NULL; ++ pa_assert(p); ++ pa_assert(m); ++ pa_assert(c); ++ pa_assert(t); ++ ++ if (pa_tagstruct_getu32(t, &command) < 0) ++ goto fail; ++ ++ reply = pa_tagstruct_new(NULL, 0); ++ pa_tagstruct_putu32(reply, PA_COMMAND_REPLY); ++ pa_tagstruct_putu32(reply, tag); ++ ++ switch (command) { ++ case AEC_SET_VOLUME: { ++ pa_tagstruct_getu32(t,&value); ++ pa_log_debug("AEC_SET_VOLUME in echo cancel = %d",value); ++ break; ++ } ++ case AEC_SET_DEVICE: { ++ pa_tagstruct_getu32(t,&value); ++ pa_log_debug("AEC_SET_DEVICE in echo cancel = %d",value); ++ break; ++ } ++ default: ++ goto fail; ++ } ++ pa_pstream_send_tagstruct(pa_native_connection_get_pstream(c), reply); ++ return 0; ++ ++fail: ++ return -1; ++} ++ + /* Common initialisation bits between module-echo-cancel and the standalone + * test program. + * +@@ -1992,6 +2039,9 @@ int pa__init(pa_module*m) { + + u->thread_info.current_volume = u->source->reference_volume; + ++ u->protocol = pa_native_protocol_get(m->core); ++ pa_native_protocol_install_ext(u->protocol, m, extension_cb); ++ + pa_sink_put(u->sink); + pa_source_put(u->source); + +@@ -2069,6 +2119,11 @@ void pa__done(pa_module*m) { + pa_xfree(u->ec); + } + ++ if (u->protocol) { ++ pa_native_protocol_remove_ext(u->protocol, m); ++ pa_native_protocol_unref(u->protocol); ++ } ++ + if (u->asyncmsgq) + pa_asyncmsgq_unref(u->asyncmsgq); + +diff --git a/src/pulse/ext-echo-cancel.c b/src/pulse/ext-echo-cancel.c +new file mode 100644 +index 0000000..9339939 +--- /dev/null ++++ b/src/pulse/ext-echo-cancel.c +@@ -0,0 +1,100 @@ ++/*** ++ This file is part of PulseAudio. ++ ++ PulseAudio is free software; you can redistribute it and/or modify ++ it under the terms of the GNU Lesser General Public License as published ++ by the Free Software Foundation; either version 2.1 of the License, ++ or (at your option) any later version. ++ ++ PulseAudio is distributed in the hope that it will be useful, but ++ WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ General Public License for more details. ++ ++ You should have received a copy of the GNU Lesser General Public License ++ along with PulseAudio; if not, write to the Free Software ++ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 ++ USA. ++***/ ++ ++#ifdef HAVE_CONFIG_H ++#include ++#endif ++ ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include "internal.h" ++#include "operation.h" ++#include "fork-detect.h" ++ ++#include "ext-echo-cancel.h" ++ ++enum { ++ AEC_SET_VOLUME, ++ AEC_SET_DEVICE, ++}; ++ ++pa_operation *pa_ext_echo_cancel_set_device ( ++ pa_context *c, ++ int device, ++ pa_context_success_cb_t cb, ++ void *userdata) { ++ ++ uint32_t tag; ++ pa_operation *o = NULL; ++ pa_tagstruct *t = NULL; ++ ++ pa_assert(c); ++ pa_assert(PA_REFCNT_VALUE(c) >= 1); ++ PA_CHECK_VALIDITY_RETURN_NULL(c, !pa_detect_fork(), PA_ERR_FORKED); ++ PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE); ++ PA_CHECK_VALIDITY_RETURN_NULL(c, c->version >= 14, PA_ERR_NOTSUPPORTED); ++ ++ o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata); ++ ++ t = pa_tagstruct_command(c, PA_COMMAND_EXTENSION, &tag); ++ pa_tagstruct_putu32(t, PA_INVALID_INDEX); ++ pa_tagstruct_puts(t, "module-echo-cancel"); ++ pa_tagstruct_putu32(t, AEC_SET_DEVICE); ++ pa_tagstruct_putu32(t, device); ++ ++ pa_pstream_send_tagstruct(c->pstream, t); ++ pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, pa_context_simple_ack_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref); ++ return o; ++} ++ ++ ++pa_operation *pa_ext_echo_cancel_set_volume ( ++ pa_context *c, ++ int volume, ++ pa_context_success_cb_t cb, ++ void *userdata) { ++ ++ uint32_t tag; ++ pa_operation *o = NULL; ++ pa_tagstruct *t = NULL; ++ ++ pa_assert(c); ++ pa_assert(PA_REFCNT_VALUE(c) >= 1); ++ PA_CHECK_VALIDITY_RETURN_NULL(c, !pa_detect_fork(), PA_ERR_FORKED); ++ PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE); ++ PA_CHECK_VALIDITY_RETURN_NULL(c, c->version >= 14, PA_ERR_NOTSUPPORTED); ++ ++ o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata); ++ ++ t = pa_tagstruct_command(c, PA_COMMAND_EXTENSION, &tag); ++ pa_tagstruct_putu32(t, PA_INVALID_INDEX); ++ pa_tagstruct_puts(t, "module-echo-cancel"); ++ pa_tagstruct_putu32(t, AEC_SET_VOLUME); ++ pa_tagstruct_putu32(t, volume); ++ ++ pa_pstream_send_tagstruct(c->pstream, t); ++ pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, pa_context_simple_ack_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref); ++ ++ return o; ++} +diff --git a/src/pulse/ext-echo-cancel.h b/src/pulse/ext-echo-cancel.h +new file mode 100644 +index 0000000..12e4eeb +--- /dev/null ++++ b/src/pulse/ext-echo-cancel.h +@@ -0,0 +1,49 @@ ++#ifndef foopulseechocancelfoo ++#define foopulseechocancelfoo ++ ++/*** ++ This file is part of PulseAudio. ++ ++ PulseAudio is free software; you can redistribute it and/or modify ++ it under the terms of the GNU Lesser General Public License as published ++ by the Free Software Foundation; either version 2.1 of the License, ++ or (at your option) any later version. ++ ++ PulseAudio is distributed in the hope that it will be useful, but ++ WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ General Public License for more details. ++ ++ You should have received a copy of the GNU Lesser General Public License ++ along with PulseAudio; if not, write to the Free Software ++ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 ++ USA. ++***/ ++ ++#include ++#include ++ ++/** \file ++ * ++ * Routines for controlling module-echo-cancel ++ */ ++ ++PA_C_DECL_BEGIN ++ ++/** Set volume to AEC module */ ++pa_operation *pa_ext_echo_cancel_set_volume ( ++ pa_context *c, ++ int volume, ++ pa_context_success_cb_t cb, ++ void *userdata); ++ ++pa_operation *pa_ext_echo_cancel_set_device ( ++ pa_context *c, ++ int device, ++ pa_context_success_cb_t cb, ++ void *userdata); ++ ++ ++PA_C_DECL_END ++ ++#endif diff --git a/meta-tizen/recipes-multimedia/pulseaudio/pulseaudio_5.0/0037-add-support-for-dlog-samsung.patch b/meta-tizen/recipes-multimedia/pulseaudio/pulseaudio_5.0/0037-add-support-for-dlog-samsung.patch new file mode 100644 index 0000000..33c38c3 --- /dev/null +++ b/meta-tizen/recipes-multimedia/pulseaudio/pulseaudio_5.0/0037-add-support-for-dlog-samsung.patch @@ -0,0 +1,285 @@ +From: "vivian,zhang" +Date: Tue, 18 Jun 2013 16:20:04 +0800 +Subject: add support for dlog - samsung + +Change-Id: Ieddf2f3bdab50926372e9e2b5cedb2756b6cfd5c +Signed-off-by: Jaska Uimonen +--- + configure.ac | 18 +++++++++ + src/Makefile.am | 9 +++++ + src/daemon/cmdline.c | 8 +++- + src/daemon/daemon-conf.c | 10 ++++- + src/pulsecore/cli-command.c | 8 ++++ + src/pulsecore/log.c | 95 +++++++++++++++++++++++++++++++++++++++++++++ + src/pulsecore/log.h | 4 ++ + 7 files changed, 150 insertions(+), 2 deletions(-) + +diff --git a/configure.ac b/configure.ac +index 9a79b36..c0beac0 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -643,6 +643,24 @@ PKG_CHECK_MODULES(LIBJSON, [ json-c >= 0.11 ], [], + + PKG_CHECK_MODULES(LIBSNDFILE, [ sndfile >= 1.0.20 ]) + ++dnl use dlog -------------------------------------------------------------------------- ++AC_ARG_ENABLE(dlog, AC_HELP_STRING([--enable-dlog], [using dlog]), ++[ ++ case "${enableval}" in ++ yes) USE_DLOG=yes ;; ++ no) USE_DLOG=no ;; ++ *) AC_MSG_ERROR(bad value ${enableval} for --enable-dlog) ;; ++ esac ++ ],[USE_DLOG=no]) ++ ++if test "x$USE_DLOG" = "xyes"; then ++ PKG_CHECK_MODULES(DLOG, dlog) ++ AC_SUBST(DLOG_CFLAGS) ++ AC_SUBST(DLOG_LIBS) ++fi ++AM_CONDITIONAL(USE_DLOG, test "x$USE_DLOG" = "xyes") ++dnl end -------------------------------------------------------------------- ++ + #### atomic-ops #### + + AC_MSG_CHECKING([whether we need libatomic_ops]) +diff --git a/src/Makefile.am b/src/Makefile.am +index ae841e3..3e41300 100644 +--- a/src/Makefile.am ++++ b/src/Makefile.am +@@ -172,6 +172,10 @@ else + pulseaudio_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS) $(IMMEDIATE_LDFLAGS) -dlopen force $(foreach f,$(PREOPEN_LIBS),-dlopen $(f)) + endif + ++if USE_DLOG ++pulseaudio_CFLAGS += -DUSE_DLOG ++endif ++ + ################################### + # Utility programs # + ################################### +@@ -740,6 +744,11 @@ libpulsecommon_@PA_MAJORMINOR@_la_CFLAGS += $(DBUS_CFLAGS) + libpulsecommon_@PA_MAJORMINOR@_la_LIBADD += $(DBUS_LIBS) + endif + ++if USE_DLOG ++libpulsecommon_@PA_MAJORMINOR@_la_CFLAGS += $(DLOG_CFLAGS) -DUSE_DLOG ++libpulsecommon_@PA_MAJORMINOR@_la_LIBADD += $(DLOG_LIBS) ++endif ++ + ################################### + # Client library # + ################################### +diff --git a/src/daemon/cmdline.c b/src/daemon/cmdline.c +index 68579c5..5bb1a0a 100644 +--- a/src/daemon/cmdline.c ++++ b/src/daemon/cmdline.c +@@ -140,8 +140,12 @@ void pa_cmdline_help(const char *argv0) { + " --scache-idle-time=SECS Unload autoloaded samples when idle and\n" + " this time passed\n" + " --log-level[=LEVEL] Increase or set verbosity level\n" +- " -v --verbose Increase the verbosity level\n" ++ " -v Increase the verbosity level\n" ++#ifdef USE_DLOG ++ " --log-target={auto,syslog,stderr,file:PATH,newfile:PATH,dlog,dlog-color}\n" ++#else + " --log-target={auto,syslog,stderr,file:PATH,newfile:PATH}\n" ++#endif + " Specify the log target\n" + " --log-meta[=BOOL] Include code location in log messages\n" + " --log-time[=BOOL] Include timestamps in log messages\n" +@@ -325,6 +329,8 @@ int pa_cmdline_parse(pa_daemon_conf *conf, int argc, char *const argv [], int *d + if (pa_daemon_conf_set_log_target(conf, optarg) < 0) { + #ifdef HAVE_JOURNAL + pa_log(_("Invalid log target: use either 'syslog', 'journal','stderr' or 'auto' or a valid file name 'file:', 'newfile:'.")); ++#elif defined(USE_DLOG) ++ pa_log(_("Invalid log target: use either 'syslog', 'stderr' or 'auto' or a valid file name 'file:', 'newfile:' or 'dlog' or 'dlog-color'.")); + #else + pa_log(_("Invalid log target: use either 'syslog', 'stderr' or 'auto' or a valid file name 'file:', 'newfile:'.")); + #endif +diff --git a/src/daemon/daemon-conf.c b/src/daemon/daemon-conf.c +index ce777a6..1dde213 100644 +--- a/src/daemon/daemon-conf.c ++++ b/src/daemon/daemon-conf.c +@@ -188,9 +188,17 @@ int pa_daemon_conf_set_log_target(pa_daemon_conf *c, const char *string) { + + if (!log_target) + return -1; ++ ++ c->log_target = log_target; + } + +- c->log_target = log_target; ++#ifdef USE_DLOG ++ else if (!strcmp(string, "dlog")) { ++ c->log_target = PA_LOG_DLOG; ++ } else if (!strcmp(string, "dlog-color")) { ++ c->log_target = PA_LOG_DLOG_COLOR; ++ } ++#endif + + return 0; + } +diff --git a/src/pulsecore/cli-command.c b/src/pulsecore/cli-command.c +index 8c956ac..2497b41 100644 +--- a/src/pulsecore/cli-command.c ++++ b/src/pulsecore/cli-command.c +@@ -188,7 +188,11 @@ static const struct command commands[] = { + { "kill-client", pa_cli_command_kill_client, "Kill a client (args: index)", 2}, + { "kill-sink-input", pa_cli_command_kill_sink_input, "Kill a sink input (args: index)", 2}, + { "kill-source-output", pa_cli_command_kill_source_output, "Kill a source output (args: index)", 2}, ++#ifdef USE_DLOG ++ { "set-log-target", pa_cli_command_log_target, "Change the log target (args: null|auto|syslog|stderr|file:PATH|newfile:PATH|dlog|dlog-color)", 2}, ++#else + { "set-log-target", pa_cli_command_log_target, "Change the log target (args: null|auto|syslog|stderr|file:PATH|newfile:PATH)", 2}, ++#endif + { "set-log-level", pa_cli_command_log_level, "Change the log level (args: numeric level)", 2}, + { "set-log-meta", pa_cli_command_log_meta, "Show source code location in log messages (args: bool)", 2}, + { "set-log-time", pa_cli_command_log_time, "Show timestamps in log messages (args: bool)", 2}, +@@ -1508,7 +1512,11 @@ static int pa_cli_command_log_target(pa_core *c, pa_tokenizer *t, pa_strbuf *buf + pa_assert(fail); + + if (!(m = pa_tokenizer_get(t, 1))) { ++#ifdef USE_DLOG ++ pa_strbuf_puts(buf, "You need to specify a log target (null|auto|syslog|stderr|file:PATH|newfile:PATH|dlog|dlog-color).\n"); ++#else + pa_strbuf_puts(buf, "You need to specify a log target (null|auto|syslog|stderr|file:PATH|newfile:PATH).\n"); ++#endif + return -1; + } + +diff --git a/src/pulsecore/log.c b/src/pulsecore/log.c +index cf96dce..dfb1d0f 100644 +--- a/src/pulsecore/log.c ++++ b/src/pulsecore/log.c +@@ -61,6 +61,27 @@ + + #include "log.h" + ++#ifdef USE_DLOG ++#include ++#define DLOG_TAG "PULSEAUDIO" ++ ++#define COLOR_BLACK 30 ++#define COLOR_RED 31 ++#define COLOR_GREEN 32 ++#define COLOR_BLUE 34 ++#define COLOR_MAGENTA 35 ++#define COLOR_CYAN 36 ++#define COLOR_WHITE 97 ++#define COLOR_B_GRAY 100 ++#define COLOR_B_RED 101 ++#define COLOR_B_GREEN 102 ++#define COLOR_B_YELLOW 103 ++#define COLOR_B_BLUE 104 ++#define COLOR_B_MAGENTA 105 ++#define COLOR_B_CYAN 106 ++#define COLOR_REVERSE 7 ++ ++#endif + #define ENV_LOG_SYSLOG "PULSE_LOG_SYSLOG" + #define ENV_LOG_LEVEL "PULSE_LOG" + #define ENV_LOG_COLORS "PULSE_LOG_COLORS" +@@ -545,6 +566,74 @@ void pa_log_levelv_meta( + + break; + } ++ ++#ifdef USE_DLOG ++ case PA_LOG_DLOG: { ++ char *local_t; ++ ++ openlog(ident, LOG_PID, LOG_USER); ++ ++ if ((local_t = pa_utf8_to_locale(t))) ++ t = local_t; ++ ++ switch (level) ++ { ++ case PA_LOG_DEBUG: ++ SLOG (LOG_DEBUG, DLOG_TAG, "%s%s%s%s", timestamp, location, t, pa_strempty(bt)); ++ break; ++ case PA_LOG_INFO: ++ case PA_LOG_NOTICE: // no notice category in dlog, use info instead. ++ SLOG (LOG_INFO, DLOG_TAG, "%s%s%s%s", timestamp, location, t, pa_strempty(bt)); ++ break; ++ case PA_LOG_WARN: ++ SLOG (LOG_WARN, DLOG_TAG, "%s%s%s%s", timestamp, location, t, pa_strempty(bt)); ++ break; ++ case PA_LOG_ERROR: ++ SLOG (LOG_ERROR, DLOG_TAG, "%s%s%s%s", timestamp, location, t, pa_strempty(bt)); ++ break; ++ default: ++ SLOG (LOG_DEBUG, DLOG_TAG, "%s%s%s%s", timestamp, location, t, pa_strempty(bt)); ++ break; ++ } ++ ++ pa_xfree(local_t); ++ ++ break; ++ } ++ case PA_LOG_DLOG_COLOR: { ++ char *local_t; ++ ++ openlog(ident, LOG_PID, LOG_USER); ++ ++ if ((local_t = pa_utf8_to_locale(t))) ++ t = local_t; ++ ++ switch (level) ++ { ++ case PA_LOG_DEBUG: ++ SLOG (LOG_DEBUG, DLOG_TAG, "\033[%dm%s%s%s%s\033[0m", COLOR_GREEN, timestamp, location, t, pa_strempty(bt)); ++ break; ++ case PA_LOG_INFO: ++ case PA_LOG_NOTICE: // no notice category in dlog, use info instead. ++ SLOG (LOG_INFO, DLOG_TAG, "\033[%dm%s%s%s%s\033[0m", COLOR_BLUE, timestamp, location, t, pa_strempty(bt)); ++ break; ++ case PA_LOG_WARN: ++ SLOG (LOG_WARN, DLOG_TAG, "\033[%dm%s%s%s%s\033[0m", COLOR_MAGENTA, timestamp, location, t, pa_strempty(bt)); ++ break; ++ case PA_LOG_ERROR: ++ SLOG (LOG_ERROR, DLOG_TAG, "\033[%dm%s%s%s%s\033[0m", COLOR_RED, timestamp, location, t, pa_strempty(bt)); ++ break; ++ default: ++ SLOG (LOG_DEBUG, DLOG_TAG, "%s%s%s%s", timestamp, location, t, pa_strempty(bt)); ++ break; ++ } ++ ++ pa_xfree(local_t); ++ ++ break; ++ } ++ ++#endif + case PA_LOG_NULL: + default: + break; +@@ -629,6 +718,12 @@ pa_log_target *pa_log_parse_target(const char *string) { + t = pa_log_target_new(PA_LOG_FILE, string + 5); + else if (pa_startswith(string, "newfile:")) + t = pa_log_target_new(PA_LOG_NEWFILE, string + 8); ++#ifdef USE_DLOG ++ else if (pa_streq(string, "dlog")) ++ t = pa_log_target_new(PA_LOG_DLOG, NULL); ++ else if (pa_streq(string, "dlog-color")) ++ t = pa_log_target_new(PA_LOG_DLOG_COLOR, NULL); ++#endif + else + pa_log(_("Invalid log target.")); + +diff --git a/src/pulsecore/log.h b/src/pulsecore/log.h +index 5e9611d..031040f 100644 +--- a/src/pulsecore/log.h ++++ b/src/pulsecore/log.h +@@ -41,6 +41,10 @@ typedef enum pa_log_target_type { + PA_LOG_NULL, /* to /dev/null */ + PA_LOG_FILE, /* to a user specified file */ + PA_LOG_NEWFILE, /* with an automatic suffix to avoid overwriting anything */ ++#ifdef USE_DLOG ++ PA_LOG_DLOG, ++ PA_LOG_DLOG_COLOR, ++#endif + } pa_log_target_type_t; + + typedef enum pa_log_level { diff --git a/meta-tizen/recipes-multimedia/pulseaudio/pulseaudio_5.0/0038-add-policy-module-samsung.patch b/meta-tizen/recipes-multimedia/pulseaudio/pulseaudio_5.0/0038-add-policy-module-samsung.patch new file mode 100644 index 0000000..fb976b1 --- /dev/null +++ b/meta-tizen/recipes-multimedia/pulseaudio/pulseaudio_5.0/0038-add-policy-module-samsung.patch @@ -0,0 +1,1317 @@ +From: "vivian,zhang" +Date: Tue, 18 Jun 2013 16:21:32 +0800 +Subject: add policy module - samsung + +Change-Id: I2111a9c4dc0a371dbea5b347cf77adbe8f930528 +Signed-off-by: Jaska Uimonen +--- + configure.ac | 18 + + src/Makefile.am | 28 +- + src/modules/module-policy.c | 926 ++++++++++++++++++++++++++++++++++++++++++++ + src/pulse/ext-policy.c | 177 +++++++++ + src/pulse/ext-policy.h | 61 +++ + src/pulse/proplist.h | 3 + + 6 files changed, 1211 insertions(+), 2 deletions(-) + create mode 100644 src/modules/module-policy.c + create mode 100644 src/pulse/ext-policy.c + create mode 100644 src/pulse/ext-policy.h + +diff --git a/configure.ac b/configure.ac +index c0beac0..0e205d3 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -643,6 +643,24 @@ PKG_CHECK_MODULES(LIBJSON, [ json-c >= 0.11 ], [], + + PKG_CHECK_MODULES(LIBSNDFILE, [ sndfile >= 1.0.20 ]) + ++dnl use samsung policy module -------------------------------------------------------- ++AC_ARG_ENABLE(samsung-policy, AC_HELP_STRING([--enable-samsung-policy], [using samsung-policy]), ++[ ++ case "${enableval}" in ++ yes) USE_SAMSUNG_POLICY=yes ;; ++ no) USE_SAMSUNG_POLICY=no ;; ++ *) AC_MSG_ERROR(bad value ${enableval} for --enable-samsung_policy) ;; ++ esac ++ ],[USE_SAMSUNG_POLICY=no]) ++ ++if test "x$USE_SAMSUNG_POLICY" = "xyes"; then ++ PKG_CHECK_MODULES(VCONF, vconf) ++ AC_SUBST(VCONF_CFLAGS) ++ AC_SUBST(VCONF_LIBS) ++fi ++AM_CONDITIONAL(USE_SAMSUNG_POLICY, test "x$USE_SAMSUNG_POLICY" = "xyes") ++dnl end -------------------------------------------------------------------- ++ + dnl use dlog -------------------------------------------------------------------------- + AC_ARG_ENABLE(dlog, AC_HELP_STRING([--enable-dlog], [using dlog]), + [ +diff --git a/src/Makefile.am b/src/Makefile.am +index 3e41300..4872dfd 100644 +--- a/src/Makefile.am ++++ b/src/Makefile.am +@@ -149,7 +149,7 @@ pulseaudio_SOURCES = \ + daemon/ltdl-bind-now.c daemon/ltdl-bind-now.h \ + daemon/main.c + +-pulseaudio_CFLAGS = $(AM_CFLAGS) $(CAP_CFLAGS) ++pulseaudio_CFLAGS = $(AM_CFLAGS) $(LIBSNDFILE_CFLAGS) $(CAP_CFLAGS) $(DBUS_CFLAGS) + pulseaudio_LDADD = $(AM_LDADD) libpulsecore-@PA_MAJORMINOR@.la libpulsecommon-@PA_MAJORMINOR@.la libpulse.la $(LIBLTDL) $(CAP_LIBS) + # This is needed because automake doesn't properly expand the foreach below + pulseaudio_DEPENDENCIES = libpulsecore-@PA_MAJORMINOR@.la libpulsecommon-@PA_MAJORMINOR@.la libpulse.la $(PREOPEN_LIBS) +@@ -787,6 +787,11 @@ pulseinclude_HEADERS = \ + pulse/volume.h \ + pulse/xmalloc.h + ++if USE_SAMSUNG_POLICY ++pulseinclude_HEADERS += \ ++ pulse/ext-policy.h ++endif ++ + lib_LTLIBRARIES = \ + libpulse.la \ + libpulse-simple.la +@@ -833,6 +838,11 @@ libpulse_la_SOURCES = \ + pulse/volume.c pulse/volume.h \ + pulse/xmalloc.c pulse/xmalloc.h + ++if USE_SAMSUNG_POLICY ++libpulse_la_SOURCES += \ ++ pulse/ext-policy.c pulse/ext-policy.h ++endif ++ + libpulse_la_CFLAGS = $(AM_CFLAGS) $(DBUS_CFLAGS) $(LIBJSON_CFLAGS) + libpulse_la_LIBADD = $(AM_LIBADD) $(WINSOCK_LIBS) $(LTLIBICONV) $(LIBJSON_LIBS) libpulsecommon-@PA_MAJORMINOR@.la + libpulse_la_LDFLAGS = $(AM_LDFLAGS) $(VERSIONING_LDFLAGS) -version-info $(LIBPULSE_VERSION_INFO) +@@ -1088,6 +1098,10 @@ if HAVE_DBUS + # Serveral module (e.g. libalsa-util.la) + modlibexec_LTLIBRARIES += \ + module-console-kit.la ++if USE_SAMSUNG_POLICY ++modlibexec_LTLIBRARIES += \ ++ module-policy.la ++endif + endif + + modlibexec_LTLIBRARIES += \ +@@ -1470,7 +1484,10 @@ SYMDEF_FILES = \ + module-switch-on-port-available-symdef.h \ + module-filter-apply-symdef.h \ + module-filter-heuristics-symdef.h +- ++if USE_SAMSUNG_POLICY ++SYMDEF_FILES += \ ++ module-policy-symdef.h ++endif + if HAVE_ESOUND + SYMDEF_FILES += \ + module-esound-protocol-tcp-symdef.h \ +@@ -2120,6 +2137,13 @@ module_rygel_media_server_la_LDFLAGS = $(MODULE_LDFLAGS) + module_rygel_media_server_la_LIBADD = $(MODULE_LIBADD) $(DBUS_LIBS) libprotocol-http.la + module_rygel_media_server_la_CFLAGS = $(AM_CFLAGS) $(DBUS_CFLAGS) + ++if USE_SAMSUNG_POLICY ++module_policy_la_SOURCES = modules/module-policy.c ++module_policy_la_LDFLAGS = $(MODULE_LDFLAGS) ++module_policy_la_LIBADD = $(AM_LIBADD) $(DBUS_LIBS) $(VCONF_LIBS) libprotocol-native.la libpulsecore-@PA_MAJORMINOR@.la libpulsecommon-@PA_MAJORMINOR@.la libpulse.la ++module_policy_la_CFLAGS = $(AM_CFLAGS) $(DBUS_CFLAGS) $(VCONF_CFLAGS) ++endif ++ + ################################### + # Some minor stuff # + ################################### +diff --git a/src/modules/module-policy.c b/src/modules/module-policy.c +new file mode 100644 +index 0000000..2172018 +--- /dev/null ++++ b/src/modules/module-policy.c +@@ -0,0 +1,926 @@ ++#ifdef HAVE_CONFIG_H ++#include ++#endif ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include // for mono ++ ++#include "module-policy-symdef.h" ++ ++PA_MODULE_AUTHOR("Seungbae Shin"); ++PA_MODULE_DESCRIPTION("Media Policy module"); ++PA_MODULE_VERSION(PACKAGE_VERSION); ++PA_MODULE_LOAD_ONCE(true); ++PA_MODULE_USAGE( ++ "on_hotplug= "); ++ ++static const char* const valid_modargs[] = { ++ "on_hotplug", ++ NULL ++}; ++ ++struct userdata { ++ pa_core *core; ++ pa_module *module; ++ ++ pa_hook_slot *sink_input_new_hook_slot,*sink_put_hook_slot; ++ ++ pa_hook_slot *sink_input_unlink_slot,*sink_unlink_slot; ++ pa_hook_slot *sink_input_unlink_post_slot, *sink_unlink_post_slot; ++ pa_hook_slot *sink_input_move_start_slot,*sink_input_move_finish_slot; ++ pa_subscription *subscription; ++ ++ bool on_hotplug:1; ++ int bt_off_idx; ++ ++ int is_mono; ++ float balance; ++ pa_module* module_mono_bt; ++ pa_module* module_combined; ++ pa_module* module_mono_combined; ++ pa_native_protocol *protocol; ++ pa_hook_slot *source_output_new_hook_slot; ++}; ++ ++enum { ++ SUBCOMMAND_TEST, ++ SUBCOMMAND_MONO, ++ SUBCOMMAND_BALANCE, ++}; ++ ++/* DEFINEs */ ++#define AEC_SINK "alsa_output.0.analog-stereo.echo-cancel" ++#define AEC_SOURCE "alsa_input.0.analog-stereo.echo-cancel" ++#define SINK_ALSA "alsa_output.0.analog-stereo" ++#define SINK_MONO_ALSA "mono_alsa" ++#define SINK_MONO_BT "mono_bt" ++#define SINK_COMBINED "combined" ++#define SINK_MONO_COMBINED "mono_combined" ++#define POLICY_AUTO "auto" ++#define POLICY_PHONE "phone" ++#define POLICY_ALL "all" ++#define POLICY_VOIP "voip" ++#define BLUEZ_API "bluez" ++#define ALSA_API "alsa" ++#define MONO_KEY VCONFKEY_SETAPPL_ACCESSIBILITY_MONO_AUDIO ++ ++/* check if this sink is bluez */ ++static bool policy_is_bluez (pa_sink* sink) ++{ ++ const char* api_name = NULL; ++ ++ if (sink == NULL) { ++ pa_log_warn ("input param sink is null"); ++ return false; ++ } ++ ++ api_name = pa_proplist_gets(sink->proplist, PA_PROP_DEVICE_API); ++ if (api_name) { ++ if (pa_streq (api_name, BLUEZ_API)) { ++#ifdef DEBUG_DETAIL ++ pa_log_debug("[POLICY][%s] [%s] exists and it is [%s]...true !!", __func__, PA_PROP_DEVICE_API, api_name); ++#endif ++ return true; ++ } else { ++#ifdef DEBUG_DETAIL ++ pa_log_debug("[POLICY][%s] [%s] exists, but not bluez...false !!", __func__, PA_PROP_DEVICE_API); ++#endif ++ } ++ } else { ++#ifdef DEBUG_DETAIL ++ pa_log_debug("[POLICY][%s] No [%s] exists...false!!", __func__, PA_PROP_DEVICE_API); ++#endif ++ } ++ ++ return false; ++} ++ ++/* check if this sink is bluez */ ++static bool policy_is_usb_alsa (pa_sink* sink) ++{ ++ const char* api_name = NULL; ++ const char* device_bus_name = NULL; ++ ++ if (sink == NULL) { ++ pa_log_warn ("input param sink is null"); ++ return false; ++ } ++ ++ api_name = pa_proplist_gets(sink->proplist, PA_PROP_DEVICE_API); ++ if (api_name) { ++ if (pa_streq (api_name, ALSA_API)) { ++#ifdef DEBUG_DETAIL ++ pa_log_debug("[POLICY][%s] [%s] exists and it is [%s]...true !!", __func__, PA_PROP_DEVICE_API, api_name); ++#endif ++ device_bus_name = pa_proplist_gets(sink->proplist, PA_PROP_DEVICE_BUS); ++ if (device_bus_name) { ++ if (pa_streq (device_bus_name, "usb")) { ++ return true; ++ } ++ } ++ } else { ++#ifdef DEBUG_DETAIL ++ pa_log_debug("[POLICY][%s] [%s] exists, but not alsa...false !!", __func__, PA_PROP_DEVICE_API); ++#endif ++ } ++ } else { ++#ifdef DEBUG_DETAIL ++ pa_log_debug("[POLICY][%s] No [%s] exists...false!!", __func__, PA_PROP_DEVICE_API); ++#endif ++ } ++ ++ return false; ++} ++ ++/* Get sink by name */ ++static pa_sink* policy_get_sink_by_name (pa_core *c, const char* sink_name) ++{ ++ pa_sink *s = NULL; ++ uint32_t idx; ++ ++ if (c == NULL || sink_name == NULL) { ++ pa_log_warn ("input param is null"); ++ return NULL; ++ } ++ ++ PA_IDXSET_FOREACH(s, c->sinks, idx) { ++ if (pa_streq (s->name, sink_name)) { ++ pa_log_debug ("[POLICY][%s] return [%p] for [%s]\n", __func__, s, sink_name); ++ return s; ++ } ++ } ++ return NULL; ++} ++ ++/* Get bt sink if available */ ++static pa_sink* policy_get_bt_sink (pa_core *c) ++{ ++ pa_sink *s = NULL; ++ uint32_t idx; ++ ++ if (c == NULL) { ++ pa_log_warn ("input param is null"); ++ return NULL; ++ } ++ ++ PA_IDXSET_FOREACH(s, c->sinks, idx) { ++ if (policy_is_bluez (s)) { ++ pa_log_debug ("[POLICY][%s] return [%p] for [%s]\n", __func__, s, s->name); ++ return s; ++ } ++ } ++ return NULL; ++} ++ ++/* Select sink for given condition */ ++static pa_sink* policy_select_proper_sink (pa_core *c, const char* policy, int is_mono) ++{ ++ pa_sink* sink = NULL; ++ pa_sink* bt_sink = NULL; ++ pa_sink* def = NULL; ++ ++ if (c == NULL || policy == NULL) { ++ pa_log_warn ("input param is null"); ++ return NULL; ++ } ++ ++ pa_assert (c); ++ ++ bt_sink = policy_get_bt_sink(c); ++ def = pa_namereg_get_default_sink(c); ++ if (def == NULL) { ++ pa_log_warn ("POLICY][%s] pa_namereg_get_default_sink() returns null", __func__); ++ return NULL; ++ } ++ ++ pa_log_debug ("[POLICY][%s] policy[%s], is_mono[%d], current default[%s], bt sink[%s]\n", ++ __func__, policy, is_mono, def->name, (bt_sink)? bt_sink->name:"null"); ++ ++ /* Select sink to */ ++ if (pa_streq(policy, POLICY_ALL)) { ++ /* all */ ++ if (bt_sink) { ++ sink = policy_get_sink_by_name(c, (is_mono)? SINK_MONO_COMBINED : SINK_COMBINED); ++ } else { ++ sink = policy_get_sink_by_name (c, (is_mono)? SINK_MONO_ALSA : SINK_ALSA); ++ } ++ ++ } else if (pa_streq(policy, POLICY_PHONE)) { ++ /* phone */ ++ sink = policy_get_sink_by_name (c, (is_mono)? SINK_MONO_ALSA : SINK_ALSA); ++ } else if (pa_streq(policy, POLICY_VOIP)) { ++ /* VOIP */ ++ sink = policy_get_sink_by_name (c,AEC_SINK); ++ } else { ++ /* auto */ ++ if (policy_is_bluez(def)) { ++ sink = (is_mono)? policy_get_sink_by_name (c, SINK_MONO_BT) : def; ++ } else if (policy_is_usb_alsa(def)) { ++ sink = def; ++ } else { ++ sink = (is_mono)? policy_get_sink_by_name (c, SINK_MONO_ALSA) : def; ++ } ++ } ++ ++ pa_log_debug ("[POLICY][%s] selected sink : [%s]\n", __func__, (sink)? sink->name : "null"); ++ return sink; ++} ++ ++static bool policy_is_filter (pa_sink_input* si) ++{ ++ const char* role = NULL; ++ ++ if (si == NULL) { ++ pa_log_warn ("input param sink-input is null"); ++ return false; ++ } ++ ++ if ((role = pa_proplist_gets(si->proplist, PA_PROP_MEDIA_ROLE))) { ++#ifdef DEBUG_DETAIL ++ pa_log_debug("[POLICY][%s] Role of sink input [%d] = %s", __func__, si->index, role); ++#endif ++ if (pa_streq(role, "filter")) { ++#ifdef DEBUG_DETAIL ++ pa_log_debug("[POLICY] no need to change of sink for %s", role); ++#endif ++ return true; ++ } ++ } ++ ++ return false; ++} ++ ++ ++ ++#define EXT_VERSION 1 ++ ++static int extension_cb(pa_native_protocol *p, pa_module *m, pa_native_connection *c, uint32_t tag, pa_tagstruct *t) { ++ struct userdata *u = NULL; ++ uint32_t command; ++ pa_tagstruct *reply = NULL; ++ ++ pa_sink_input *si = NULL; ++ pa_sink *s = NULL; ++ uint32_t idx; ++ pa_sink* sink_to_move = NULL; ++ ++ pa_assert(p); ++ pa_assert(m); ++ pa_assert(c); ++ pa_assert(t); ++ ++ u = m->userdata; ++ ++ if (pa_tagstruct_getu32(t, &command) < 0) ++ goto fail; ++ ++ reply = pa_tagstruct_new(NULL, 0); ++ pa_tagstruct_putu32(reply, PA_COMMAND_REPLY); ++ pa_tagstruct_putu32(reply, tag); ++ ++ switch (command) { ++ case SUBCOMMAND_TEST: { ++ if (!pa_tagstruct_eof(t)) ++ goto fail; ++ ++ pa_tagstruct_putu32(reply, EXT_VERSION); ++ break; ++ } ++ ++ case SUBCOMMAND_MONO: { ++ ++ bool enable; ++ ++ if (pa_tagstruct_get_boolean(t, &enable) < 0) ++ goto fail; ++ ++ pa_log_debug ("[POLICY][%s] new mono value = %d\n", __func__, enable); ++ if (enable == u->is_mono) { ++ pa_log_debug ("[POLICY][%s] No changes in mono value = %d", __func__, u->is_mono); ++ break; ++ } ++ ++ u->is_mono = enable; ++ ++ /* Move current sink-input to proper mono sink */ ++ PA_IDXSET_FOREACH(si, u->core->sink_inputs, idx) { ++ const char *policy = NULL; ++ ++ /* Skip this if it is already in the process of being moved ++ * anyway */ ++ if (!si->sink) ++ continue; ++ ++ /* It might happen that a stream and a sink are set up at the ++ same time, in which case we want to make sure we don't ++ interfere with that */ ++ if (!PA_SINK_INPUT_IS_LINKED(pa_sink_input_get_state(si))) ++ continue; ++ ++ /* Get role (if role is filter, skip it) */ ++ if (policy_is_filter(si)) ++ continue; ++ ++ /* Check policy, if no policy exists, treat as AUTO */ ++ if (!(policy = pa_proplist_gets(si->proplist, PA_PROP_MEDIA_POLICY))) { ++ pa_log_debug("[POLICY] set policy of sink-input[%d] from [%s] to [auto]", si->index, "null"); ++ policy = POLICY_AUTO; ++ } ++ pa_log_debug("[POLICY] Policy of sink input [%d] = %s", si->index, policy); ++ ++ /* Select sink to move and move to it */ ++ sink_to_move = policy_select_proper_sink (u->core, policy, u->is_mono); ++ if (sink_to_move) { ++ pa_log_debug("[POLICY][%s] Moving sink-input[%d] from [%s] to [%s]", __func__, si->index, si->sink->name, sink_to_move->name); ++ pa_sink_input_move_to(si, sink_to_move, false); ++ } else { ++ pa_log_debug("[POLICY][%s] Can't move sink-input....", __func__); ++ } ++ } ++ break; ++ } ++ ++ case SUBCOMMAND_BALANCE: { ++ float balance; ++ pa_cvolume cvol; ++ pa_channel_map map; ++ ++ if (pa_tagstruct_get_cvolume(t, &cvol) < 0) ++ goto fail; ++ ++ pa_channel_map_init_stereo(&map); ++ balance = pa_cvolume_get_balance(&cvol, &map); ++ ++ pa_log_debug ("[POLICY][%s] new balance value = [%f]\n", __func__, balance); ++ ++ if (balance == u->balance) { ++ pa_log_debug ("[POLICY][%s] No changes in balance value = [%f]", __func__, u->balance); ++ break; ++ } ++ ++ u->balance = balance; ++ ++ /* Apply balance value to each Sinks */ ++ PA_IDXSET_FOREACH(s, u->core->sinks, idx) { ++ pa_cvolume* cvol = pa_sink_get_volume (s, false); ++ pa_cvolume_set_balance (cvol, &s->channel_map, u->balance); ++ pa_sink_set_volume(s, cvol, true, true); ++ } ++ break; ++ } ++ ++ default: ++ goto fail; ++ } ++ ++ pa_pstream_send_tagstruct(pa_native_connection_get_pstream(c), reply); ++ return 0; ++ ++ fail: ++ ++ if (reply) ++ pa_tagstruct_free(reply); ++ ++ return -1; ++} ++ ++/* Called when new sink-input is creating */ ++static pa_hook_result_t sink_input_new_hook_callback(pa_core *c, pa_sink_input_new_data *new_data, struct userdata *u) ++{ ++ const char *policy = NULL; ++ ++ pa_assert(c); ++ pa_assert(new_data); ++ pa_assert(u); ++ ++ if (!new_data->proplist) { ++ pa_log_debug("[POLICY] New stream lacks property data."); ++ return PA_HOOK_OK; ++ } ++ ++ /* If sink-input has already sink, skip */ ++ if (new_data->sink) { ++ /* sink-input with filter role will be also here because sink is already set */ ++#ifdef DEBUG_DETAIL ++ pa_log_debug("[POLICY] Not setting device for stream [%s], because already set.", ++ pa_strnull(pa_proplist_gets(new_data->proplist, PA_PROP_MEDIA_NAME))); ++#endif ++ return PA_HOOK_OK; ++ } ++ ++ /* If no policy exists, skip */ ++ if (!(policy = pa_proplist_gets(new_data->proplist, PA_PROP_MEDIA_POLICY))) { ++ pa_log_debug("[POLICY][%s] Not setting device for stream [%s], because it lacks policy.", ++ __func__, pa_strnull(pa_proplist_gets(new_data->proplist, PA_PROP_MEDIA_NAME))); ++ return PA_HOOK_OK; ++ } ++ pa_log_debug("[POLICY][%s] Policy for stream [%s] = [%s]", ++ __func__, pa_strnull(pa_proplist_gets(new_data->proplist, PA_PROP_MEDIA_NAME)), policy); ++ ++ /* Set proper sink to sink-input */ ++ pa_sink* new_sink = policy_select_proper_sink(c, policy, u->is_mono); ++ if(new_sink != new_data->sink) ++ { ++ pa_sink_input_new_data_set_sink(new_data, new_sink, false); ++ } ++ /*new_data->save_sink = false; ++ new_data->sink = policy_select_proper_sink (c, policy, u->is_mono);*/ ++ pa_log_debug("[POLICY][%s] set sink of sink-input to [%s]", __func__, (new_data->sink)? new_data->sink->name : "null"); ++ ++ return PA_HOOK_OK; ++} ++ ++/* Called when new sink is added while sink-input is existing */ ++static pa_hook_result_t sink_put_hook_callback(pa_core *c, pa_sink *sink, struct userdata *u) ++{ ++ pa_sink_input *si; ++ pa_sink *sink_to_move; ++ uint32_t idx; ++ char *args = NULL; ++ ++ bool is_bt; ++ bool is_usb_alsa; ++ ++ pa_assert(c); ++ pa_assert(sink); ++ pa_assert(u); ++ pa_assert(u->on_hotplug); ++ ++ /* If connected sink is BLUETOOTH, set as default */ ++ /* we are checking with device.api property */ ++ is_bt = policy_is_bluez(sink); ++ is_usb_alsa = policy_is_usb_alsa(sink); ++ ++ if (is_bt || is_usb_alsa) { ++ pa_log_debug("[POLICY][%s] set default sink to sink[%s][%d]", __func__, sink->name, sink->index); ++ pa_namereg_set_default_sink (c,sink); ++ } else { ++ pa_log_debug("[POLICY][%s] this sink [%s][%d] is not a bluez....return", __func__, sink->name, sink->index); ++ return PA_HOOK_OK; ++ } ++ ++ if (is_bt) { ++ /* Load mono_bt sink */ ++ args = pa_sprintf_malloc("sink_name=%s master=%s channels=1", SINK_MONO_BT, sink->name); ++ u->module_mono_bt = pa_module_load(u->module->core, "module-remap-sink", args); ++ pa_xfree(args); ++ ++ /* load combine sink */ ++ args = pa_sprintf_malloc("sink_name=%s slaves=\"%s,%s\"", SINK_COMBINED, sink->name, SINK_ALSA); ++ u->module_combined = pa_module_load(u->module->core, "module-combine", args); ++ pa_xfree(args); ++ ++ /* load mono_combine sink */ ++ args = pa_sprintf_malloc("sink_name=%s master=%s channels=1", SINK_MONO_COMBINED, SINK_COMBINED); ++ u->module_mono_combined = pa_module_load(u->module->core, "module-remap-sink", args); ++ pa_xfree(args); ++ } ++ ++ /* Iterate each sink inputs to decide whether we should move to new sink */ ++ PA_IDXSET_FOREACH(si, c->sink_inputs, idx) { ++ const char *policy = NULL; ++ ++ if (si->sink == sink) ++ continue; ++ ++ /* Skip this if it is already in the process of being moved ++ * anyway */ ++ if (!si->sink) ++ continue; ++ ++ /* It might happen that a stream and a sink are set up at the ++ same time, in which case we want to make sure we don't ++ interfere with that */ ++ if (!PA_SINK_INPUT_IS_LINKED(pa_sink_input_get_state(si))) ++ continue; ++ ++ /* Get role (if role is filter, skip it) */ ++ if (policy_is_filter(si)) ++ continue; ++ ++ /* Check policy */ ++ if (!(policy = pa_proplist_gets(si->proplist, PA_PROP_MEDIA_POLICY))) { ++ /* No policy exists, this means auto */ ++ pa_log_debug("[POLICY][%s] set policy of sink-input[%d] from [%s] to [auto]", __func__, si->index, "null"); ++ policy = POLICY_AUTO; ++ } ++ ++ sink_to_move = policy_select_proper_sink (c, policy, u->is_mono); ++ if (sink_to_move) { ++ pa_log_debug("[POLICY][%s] Moving sink-input[%d] from [%s] to [%s]", __func__, si->index, si->sink->name, sink_to_move->name); ++ pa_sink_input_move_to(si, sink_to_move, false); ++ } else { ++ pa_log_debug("[POLICY][%s] Can't move sink-input....",__func__); ++ } ++ } ++ ++ /* Reset sink volume with balance from userdata */ ++ pa_cvolume* cvol = pa_sink_get_volume(sink, false); ++ pa_cvolume_set_balance(cvol, &sink->channel_map, u->balance); ++ pa_sink_set_volume(sink, cvol, true, true); ++ ++ return PA_HOOK_OK; ++} ++ ++static void subscribe_cb(pa_core *c, pa_subscription_event_type_t t, uint32_t idx, void *userdata) ++{ ++ struct userdata *u = userdata; ++ pa_sink *def; ++ pa_sink_input *si; ++ uint32_t idx2; ++ pa_sink *sink_to_move = NULL; ++ pa_assert(u); ++ ++ pa_log_debug("[POLICY][%s] subscribe_cb() t=[0x%x], idx=[%d]", __func__, t, idx); ++ ++ /* We only handle server changes */ ++ if (t == (PA_SUBSCRIPTION_EVENT_SERVER|PA_SUBSCRIPTION_EVENT_CHANGE)) { ++ ++ def = pa_namereg_get_default_sink(c); ++ if (def == NULL) { ++ pa_log_warn("[POLICY][%s] pa_namereg_get_default_sink() returns null", __func__); ++ return; ++ } ++ pa_log_debug("[POLICY][%s] trying to move stream to current default sink = [%s]", __func__, def->name); ++ ++ /* Iterate each sink inputs to decide whether we should move to new DEFAULT sink */ ++ PA_IDXSET_FOREACH(si, c->sink_inputs, idx2) { ++ const char *policy = NULL; ++ ++ if (!si->sink) ++ continue; ++ ++ /* Get role (if role is filter, skip it) */ ++ if (policy_is_filter(si)) ++ continue; ++ ++ /* Get policy */ ++ if (!(policy = pa_proplist_gets(si->proplist, PA_PROP_MEDIA_POLICY))) { ++ /* No policy exists, this means auto */ ++ pa_log_debug("[POLICY][%s] set policy of sink-input[%d] from [%s] to [auto]", __func__, si->index, "null"); ++ policy = POLICY_AUTO; ++ } ++ ++ sink_to_move = policy_select_proper_sink (c, policy, u->is_mono); ++ if (sink_to_move) { ++ /* Move sink-input to new DEFAULT sink */ ++ pa_log_debug("[POLICY][%s] Moving sink-input[%d] from [%s] to [%s]", __func__, si->index, si->sink->name, sink_to_move->name); ++ pa_sink_input_move_to(si, sink_to_move, false); ++ } ++ } ++ } ++} ++ ++static pa_hook_result_t sink_unlink_hook_callback(pa_core *c, pa_sink *sink, void* userdata) { ++ struct userdata *u = userdata; ++ uint32_t idx; ++ pa_sink *sink_to_move; ++ pa_sink_input *si; ++ ++ pa_assert(c); ++ pa_assert(sink); ++ pa_assert(u); ++ ++ /* There's no point in doing anything if the core is shut down anyway */ ++ if (c->state == PA_CORE_SHUTDOWN) ++ return PA_HOOK_OK; ++ ++ /* if unloading sink is not bt, just return */ ++ if (!policy_is_bluez (sink)) { ++ pa_log_debug("[POLICY][%s] sink[%s][%d] unlinked but not a bluez....return\n", __func__, sink->name, sink->index); ++ return PA_HOOK_OK; ++ } ++ ++ pa_log_debug ("[POLICY][%s] SINK unlinked ================================ sink [%s][%d], bt_off_idx was [%d]", ++ __func__, sink->name, sink->index,u->bt_off_idx); ++ ++ u->bt_off_idx = sink->index; ++ pa_log_debug ("[POLICY][%s] bt_off_idx is set to [%d]", __func__, u->bt_off_idx); ++ ++ /* BT sink is unloading, move sink-input to proper sink */ ++ PA_IDXSET_FOREACH(si, c->sink_inputs, idx) { ++ ++ if (!si->sink) ++ continue; ++ ++ /* Get role (if role is filter, skip it) */ ++ if (policy_is_filter(si)) ++ continue; ++ ++ /* Find who were using bt sink or bt related sink and move them to proper sink (alsa/mono_alsa) */ ++ if (pa_streq (si->sink->name, SINK_MONO_BT) || ++ pa_streq (si->sink->name, SINK_MONO_COMBINED) || ++ pa_streq (si->sink->name, SINK_COMBINED) || ++ policy_is_bluez (si->sink)) { ++ ++ /* Move sink-input to proper sink : only alsa related sink is available now */ ++ sink_to_move = policy_get_sink_by_name (c, (u->is_mono)? SINK_MONO_ALSA : SINK_ALSA); ++ if (sink_to_move) { ++ pa_log_debug("[POLICY][%s] Moving sink-input[%d] from [%s] to [%s]", __func__, si->index, si->sink->name, sink_to_move->name); ++ pa_sink_input_move_to(si, sink_to_move, false); ++ } else { ++ pa_log_warn("[POLICY][%s] No sink to move", __func__); ++ } ++ } ++ } ++ ++ pa_log_debug ("[POLICY][%s] unload sink in dependencies", __func__); ++ ++ /* Unload mono_combine sink */ ++ if (u->module_mono_combined) { ++ pa_module_unload(u->module->core, u->module_mono_combined, true); ++ u->module_mono_combined = NULL; ++ } ++ ++ /* Unload combine sink */ ++ if (u->module_combined) { ++ pa_module_unload(u->module->core, u->module_combined, true); ++ u->module_combined = NULL; ++ } ++ ++ /* Unload mono_bt sink */ ++ if (u->module_mono_bt) { ++ pa_module_unload(u->module->core, u->module_mono_bt, true); ++ u->module_mono_bt = NULL; ++ } ++ ++ return PA_HOOK_OK; ++} ++ ++static pa_hook_result_t sink_unlink_post_hook_callback(pa_core *c, pa_sink *sink, void* userdata) { ++ struct userdata *u = userdata; ++ ++ pa_assert(c); ++ pa_assert(sink); ++ pa_assert(u); ++ ++ pa_log_debug("[POLICY][%s] SINK unlinked POST ================================ sink [%s][%d]", __func__, sink->name, sink->index); ++ ++ /* There's no point in doing anything if the core is shut down anyway */ ++ if (c->state == PA_CORE_SHUTDOWN) ++ return PA_HOOK_OK; ++ ++ /* if unloading sink is not bt, just return */ ++ if (!policy_is_bluez (sink)) { ++ pa_log_debug("[POLICY][%s] not a bluez....return\n", __func__); ++ return PA_HOOK_OK; ++ } ++ ++ u->bt_off_idx = -1; ++ pa_log_debug ("[POLICY][%s] bt_off_idx is cleared to [%d]", __func__, u->bt_off_idx); ++ ++ return PA_HOOK_OK; ++} ++ ++static pa_hook_result_t sink_input_move_start_cb(pa_core *core, pa_sink_input *i, struct userdata *u) { ++ pa_core_assert_ref(core); ++ pa_sink_input_assert_ref(i); ++ ++ /* There's no point in doing anything if the core is shut down anyway */ ++ if (core->state == PA_CORE_SHUTDOWN) ++ return PA_HOOK_OK; ++ ++ pa_log_debug ("[POLICY][%s] sink_input_move_start_cb -------------------------------------- sink-input [%d] was sink [%s][%d] : Trying to mute!!!", ++ __func__, i->index, i->sink->name, i->sink->index); ++ pa_sink_input_set_mute(i, true, false); ++ ++ return PA_HOOK_OK; ++} ++ ++static pa_hook_result_t sink_input_move_finish_cb(pa_core *core, pa_sink_input *i, struct userdata *u) { ++ pa_core_assert_ref(core); ++ pa_sink_input_assert_ref(i); ++ ++ /* There's no point in doing anything if the core is shut down anyway */ ++ if (core->state == PA_CORE_SHUTDOWN) ++ return PA_HOOK_OK; ++ ++ pa_log_debug("[POLICY][%s] sink_input_move_finish_cb -------------------------------------- sink-input [%d], sink [%s][%d], bt_off_idx [%d] : %s", ++ __func__, i->index, i->sink->name, i->sink->index, u->bt_off_idx, ++ (u->bt_off_idx == -1)? "Trying to un-mute!!!!" : "skip un-mute..."); ++ ++ /* If sink input move is caused by bt sink unlink, then skip un-mute operation */ ++ if (u->bt_off_idx == -1) { ++ pa_sink_input_set_mute(i, false, false); ++ } ++ ++ return PA_HOOK_OK; ++} ++ ++static pa_source* policy_get_source_by_name (pa_core *c, const char* source_name) ++{ ++ pa_source *s = NULL; ++ uint32_t idx; ++ ++ if (c == NULL || source_name == NULL) { ++ pa_log_warn ("input param is null"); ++ return NULL; ++ } ++ ++ PA_IDXSET_FOREACH(s, c->sources, idx) { ++ if (pa_streq (s->name, source_name)) { ++ pa_log_debug ("[POLICY][%s] return [%p] for [%s]\n", __func__, s, source_name); ++ return s; ++ } ++ } ++ return NULL; ++} ++ ++/* Select source for given condition */ ++static pa_source* policy_select_proper_source (pa_core *c, const char* policy) ++{ ++ pa_source* source = NULL; ++ pa_source* def = NULL; ++ ++ if (c == NULL || policy == NULL) { ++ pa_log_warn ("input param is null"); ++ return NULL; ++ } ++ ++ pa_assert (c); ++ def = pa_namereg_get_default_source(c); ++ if (def == NULL) { ++ pa_log_warn ("POLICY][%s] pa_namereg_get_default_source() returns null", __func__); ++ return NULL; ++ } ++ ++ /* Select source to */ ++ if (pa_streq(policy, POLICY_VOIP)) { ++ source = policy_get_source_by_name (c, AEC_SOURCE); ++ ++ } else { ++ source = def; ++ } ++ ++ pa_log_debug ("[POLICY][%s] selected source : [%s]\n", __func__, (source)? source->name : "null"); ++ return source; ++} ++ ++ ++/* Called when new source-output is creating */ ++static pa_hook_result_t source_output_new_hook_callback(pa_core *c, pa_source_output_new_data *new_data, struct userdata *u) { ++ const char *policy = NULL; ++ pa_assert(c); ++ pa_assert(new_data); ++ pa_assert(u); ++ ++ if (!new_data->proplist) { ++ pa_log_debug("New stream lacks property data."); ++ return PA_HOOK_OK; ++ } ++ ++ if (new_data->source) { ++ pa_log_debug("Not setting device for stream %s, because already set.", pa_strnull(pa_proplist_gets(new_data->proplist, PA_PROP_MEDIA_NAME))); ++ return PA_HOOK_OK; ++ } ++ ++ /* If no policy exists, skip */ ++ if (!(policy = pa_proplist_gets(new_data->proplist, PA_PROP_MEDIA_POLICY))) { ++ pa_log_debug("[POLICY][%s] Not setting device for stream [%s], because it lacks policy.", ++ __func__, pa_strnull(pa_proplist_gets(new_data->proplist, PA_PROP_MEDIA_NAME))); ++ return PA_HOOK_OK; ++ } ++ pa_log_debug("[POLICY][%s] Policy for stream [%s] = [%s]", ++ __func__, pa_strnull(pa_proplist_gets(new_data->proplist, PA_PROP_MEDIA_NAME)), policy); ++ ++ /* Set proper source to source-output */ ++ pa_source* new_source = policy_select_proper_source(c, policy); ++ if(new_source != new_data->source) ++ { ++ pa_source_output_new_data_set_source(new_data, new_source, false); ++ } ++ /*new_data->save_source= false; ++ new_data->source= policy_select_proper_source (c, policy);*/ ++ pa_log_debug("[POLICY][%s] set source of source-input to [%s]", __func__, (new_data->source)? new_data->source->name : "null"); ++ ++ return PA_HOOK_OK; ++} ++ ++int pa__init(pa_module *m) ++{ ++ pa_modargs *ma = NULL; ++ struct userdata *u; ++ bool on_hotplug = true, on_rescue = true; ++ ++ pa_assert(m); ++ ++ if (!(ma = pa_modargs_new(m->argument, valid_modargs))) { ++ pa_log("Failed to parse module arguments"); ++ goto fail; ++ } ++ ++ if (pa_modargs_get_value_boolean(ma, "on_hotplug", &on_hotplug) < 0 || ++ pa_modargs_get_value_boolean(ma, "on_rescue", &on_rescue) < 0) { ++ pa_log("on_hotplug= and on_rescue= expect boolean arguments"); ++ goto fail; ++ } ++ ++ m->userdata = u = pa_xnew0(struct userdata, 1); ++ u->core = m->core; ++ u->module = m; ++ u->on_hotplug = on_hotplug; ++ ++ ++ /* A little bit later than module-stream-restore */ ++ u->sink_input_new_hook_slot = ++ pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_INPUT_NEW], PA_HOOK_EARLY+10, (pa_hook_cb_t) sink_input_new_hook_callback, u); ++ ++ u->source_output_new_hook_slot = ++ pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_NEW], PA_HOOK_EARLY+10, (pa_hook_cb_t) source_output_new_hook_callback, u); ++ ++ if (on_hotplug) { ++ /* A little bit later than module-stream-restore */ ++ u->sink_put_hook_slot = ++ pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_PUT], PA_HOOK_LATE+10, (pa_hook_cb_t) sink_put_hook_callback, u); ++ } ++ ++ /* sink unlink comes before sink-input unlink */ ++ u->sink_unlink_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_UNLINK], PA_HOOK_EARLY, (pa_hook_cb_t) sink_unlink_hook_callback, u); ++ u->sink_unlink_post_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_UNLINK_POST], PA_HOOK_EARLY, (pa_hook_cb_t) sink_unlink_post_hook_callback, u); ++ ++ u->sink_input_move_start_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_INPUT_MOVE_START], PA_HOOK_LATE, (pa_hook_cb_t) sink_input_move_start_cb, u); ++ u->sink_input_move_finish_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_INPUT_MOVE_FINISH], PA_HOOK_LATE, (pa_hook_cb_t) sink_input_move_finish_cb, u); ++ ++ u->subscription = pa_subscription_new(u->core, PA_SUBSCRIPTION_MASK_SERVER, subscribe_cb, u); ++ ++ ++ u->bt_off_idx = -1; /* initial bt off sink index */ ++ ++ u->module_mono_bt = NULL; ++ u->module_combined = NULL; ++ u->module_mono_combined = NULL; ++ ++ u->protocol = pa_native_protocol_get(m->core); ++ pa_native_protocol_install_ext(u->protocol, m, extension_cb); ++ ++ /* Get mono key value for init */ ++ vconf_get_bool(MONO_KEY, &u->is_mono); ++ ++ pa_log_info("policy module is loaded\n"); ++ ++ if (ma) ++ pa_modargs_free(ma); ++ ++ return 0; ++ ++fail: ++ if (ma) ++ pa_modargs_free(ma); ++ ++ pa__done(m); ++ ++ return -1; ++} ++ ++void pa__done(pa_module *m) ++{ ++ struct userdata* u; ++ ++ pa_assert(m); ++ ++ if (!(u = m->userdata)) ++ return; ++ ++ if (u->sink_input_new_hook_slot) ++ pa_hook_slot_free(u->sink_input_new_hook_slot); ++ if (u->sink_put_hook_slot) ++ pa_hook_slot_free(u->sink_put_hook_slot); ++ if (u->sink_unlink_slot) ++ pa_hook_slot_free(u->sink_unlink_slot); ++ if (u->sink_unlink_post_slot) ++ pa_hook_slot_free(u->sink_unlink_post_slot); ++ if (u->sink_input_move_start_slot) ++ pa_hook_slot_free(u->sink_input_move_start_slot); ++ if (u->sink_input_move_finish_slot) ++ pa_hook_slot_free(u->sink_input_move_finish_slot); ++ if (u->subscription) ++ pa_subscription_free(u->subscription); ++ if (u->protocol) { ++ pa_native_protocol_remove_ext(u->protocol, m); ++ pa_native_protocol_unref(u->protocol); ++ } ++ if (u->source_output_new_hook_slot) ++ pa_hook_slot_free(u->source_output_new_hook_slot); ++ ++ pa_xfree(u); ++ ++ ++ pa_log_info("policy module is unloaded\n"); ++} +diff --git a/src/pulse/ext-policy.c b/src/pulse/ext-policy.c +new file mode 100644 +index 0000000..f3a3a8c +--- /dev/null ++++ b/src/pulse/ext-policy.c +@@ -0,0 +1,177 @@ ++/*** ++ This file is part of PulseAudio. ++ ++ PulseAudio is free software; you can redistribute it and/or modify ++ it under the terms of the GNU Lesser General Public License as published ++ by the Free Software Foundation; either version 2.1 of the License, ++ or (at your option) any later version. ++ ++ PulseAudio is distributed in the hope that it will be useful, but ++ WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ General Public License for more details. ++ ++ You should have received a copy of the GNU Lesser General Public License ++ along with PulseAudio; if not, write to the Free Software ++ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 ++ USA. ++***/ ++ ++#ifdef HAVE_CONFIG_H ++#include ++#endif ++ ++#include ++#include ++#include ++ ++#include ++#include ++ ++#include "internal.h" ++#include "operation.h" ++#include "fork-detect.h" ++ ++#include "ext-policy.h" ++ ++enum { ++ SUBCOMMAND_TEST, ++ SUBCOMMAND_MONO, ++ SUBCOMMAND_BALANCE, ++}; ++ ++static void ext_policy_test_cb(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) { ++ pa_operation *o = userdata; ++ uint32_t version = PA_INVALID_INDEX; ++ ++ pa_assert(pd); ++ pa_assert(o); ++ pa_assert(PA_REFCNT_VALUE(o) >= 1); ++ ++ if (!o->context) ++ goto finish; ++ ++ if (command != PA_COMMAND_REPLY) { ++ if (pa_context_handle_error(o->context, command, t, FALSE) < 0) ++ goto finish; ++ ++ } else if (pa_tagstruct_getu32(t, &version) < 0 || ++ !pa_tagstruct_eof(t)) { ++ ++ pa_context_fail(o->context, PA_ERR_PROTOCOL); ++ goto finish; ++ } ++ ++ if (o->callback) { ++ pa_ext_policy_test_cb_t cb = (pa_ext_policy_test_cb_t) o->callback; ++ cb(o->context, version, o->userdata); ++ } ++ ++finish: ++ pa_operation_done(o); ++ pa_operation_unref(o); ++} ++ ++pa_operation *pa_ext_policy_test( ++ pa_context *c, ++ pa_ext_device_manager_test_cb_t cb, ++ void *userdata) { ++ ++ uint32_t tag; ++ pa_operation *o; ++ pa_tagstruct *t; ++ ++ pa_assert(c); ++ pa_assert(PA_REFCNT_VALUE(c) >= 1); ++ ++ PA_CHECK_VALIDITY_RETURN_NULL(c, !pa_detect_fork(), PA_ERR_FORKED); ++ PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE); ++ PA_CHECK_VALIDITY_RETURN_NULL(c, c->version >= 14, PA_ERR_NOTSUPPORTED); ++ ++ o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata); ++ ++ t = pa_tagstruct_command(c, PA_COMMAND_EXTENSION, &tag); ++ pa_tagstruct_putu32(t, PA_INVALID_INDEX); ++ pa_tagstruct_puts(t, "module-policy"); ++ pa_tagstruct_putu32(t, SUBCOMMAND_TEST); ++ pa_pstream_send_tagstruct(c->pstream, t); ++ pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, ext_policy_test_cb, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref); ++ ++ return o; ++} ++ ++pa_operation *pa_ext_policy_set_mono ( ++ pa_context *c, ++ int enable, ++ pa_context_success_cb_t cb, ++ void *userdata) { ++ ++ uint32_t tag; ++ pa_operation *o = NULL; ++ pa_tagstruct *t = NULL; ++ ++ pa_assert(c); ++ pa_assert(PA_REFCNT_VALUE(c) >= 1); ++ ++ PA_CHECK_VALIDITY_RETURN_NULL(c, !pa_detect_fork(), PA_ERR_FORKED); ++ PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE); ++ PA_CHECK_VALIDITY_RETURN_NULL(c, c->version >= 14, PA_ERR_NOTSUPPORTED); ++ ++ o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata); ++ ++ t = pa_tagstruct_command(c, PA_COMMAND_EXTENSION, &tag); ++ pa_tagstruct_putu32(t, PA_INVALID_INDEX); ++ pa_tagstruct_puts(t, "module-policy"); ++ pa_tagstruct_putu32(t, SUBCOMMAND_MONO); ++ pa_tagstruct_put_boolean(t, !!enable); ++ ++ pa_pstream_send_tagstruct(c->pstream, t); ++ pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, pa_context_simple_ack_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref); ++ ++ return o; ++} ++ ++pa_operation *pa_ext_policy_set_balance ( ++ pa_context *c, ++ double *balance, ++ pa_context_success_cb_t cb, ++ void *userdata) { ++ ++ uint32_t tag; ++ pa_operation *o = NULL; ++ pa_tagstruct *t = NULL; ++ pa_cvolume cvol; ++ pa_channel_map map; ++ ++ pa_assert(c); ++ pa_assert(PA_REFCNT_VALUE(c) >= 1); ++ ++ PA_CHECK_VALIDITY_RETURN_NULL(c, !pa_detect_fork(), PA_ERR_FORKED); ++ PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE); ++ PA_CHECK_VALIDITY_RETURN_NULL(c, c->version >= 14, PA_ERR_NOTSUPPORTED); ++ ++ o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata); ++ ++ t = pa_tagstruct_command(c, PA_COMMAND_EXTENSION, &tag); ++ pa_tagstruct_putu32(t, PA_INVALID_INDEX); ++ pa_tagstruct_puts(t, "module-policy"); ++ pa_tagstruct_putu32(t, SUBCOMMAND_BALANCE); ++ ++ /* Prepare cvolume for transfer */ ++ pa_channel_map_init_stereo(&map); ++ pa_cvolume_set(&cvol, map.channels, 65535); ++ ++ pa_log_error ("balance = %f", *balance); ++ ++ pa_cvolume_set_balance(&cvol, &map, *balance); ++ ++ pa_log_error ("balance get = %f", pa_cvolume_get_balance(&cvol, &map)); ++ ++ pa_tagstruct_put_cvolume(t, &cvol); ++ ++ pa_pstream_send_tagstruct(c->pstream, t); ++ pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, pa_context_simple_ack_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref); ++ ++ return o; ++} ++ +diff --git a/src/pulse/ext-policy.h b/src/pulse/ext-policy.h +new file mode 100644 +index 0000000..ec62ead +--- /dev/null ++++ b/src/pulse/ext-policy.h +@@ -0,0 +1,61 @@ ++#ifndef foopulseextpolicyhfoo ++#define foopulseextpolicyhfoo ++ ++/*** ++ This file is part of PulseAudio. ++ ++ PulseAudio is free software; you can redistribute it and/or modify ++ it under the terms of the GNU Lesser General Public License as published ++ by the Free Software Foundation; either version 2.1 of the License, ++ or (at your option) any later version. ++ ++ PulseAudio is distributed in the hope that it will be useful, but ++ WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ General Public License for more details. ++ ++ You should have received a copy of the GNU Lesser General Public License ++ along with PulseAudio; if not, write to the Free Software ++ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 ++ USA. ++***/ ++ ++#include ++#include ++ ++/** \file ++ * ++ * Routines for controlling module-policy ++ */ ++ ++PA_C_DECL_BEGIN ++ ++/** Callback prototype for pa_ext_policy_test(). \since 0.9.21 */ ++typedef void (*pa_ext_policy_test_cb_t)( ++ pa_context *c, ++ uint32_t version, ++ void *userdata); ++ ++/** Test if this extension module is available in the server. \since 0.9.21 */ ++pa_operation *pa_ext_policy_test( ++ pa_context *c, ++ pa_ext_policy_test_cb_t cb, ++ void *userdata); ++ ++/** Enable the mono mode. \since 0.9.21 */ ++pa_operation *pa_ext_policy_set_mono ( ++ pa_context *c, ++ int enable, ++ pa_context_success_cb_t cb, ++ void *userdata); ++ ++/** Enable the balance mode. \since 0.9.21 */ ++pa_operation *pa_ext_policy_set_balance ( ++ pa_context *c, ++ double *balance, ++ pa_context_success_cb_t cb, ++ void *userdata); ++ ++PA_C_DECL_END ++ ++#endif +diff --git a/src/pulse/proplist.h b/src/pulse/proplist.h +index dc3cddc..341abaa 100644 +--- a/src/pulse/proplist.h ++++ b/src/pulse/proplist.h +@@ -65,6 +65,9 @@ PA_C_DECL_BEGIN + /** For streams: logic role of this media. One of the strings "video", "music", "game", "event", "phone", "animation", "production", "a11y", "test" */ + #define PA_PROP_MEDIA_ROLE "media.role" + ++/** For streams: logic role of this media. One of the strings "auto", "phone" */ ++#define PA_PROP_MEDIA_POLICY "media.policy" ++ + /** For streams: the name of a filter that is desired, e.g.\ "echo-cancel" or "equalizer-sink". PulseAudio may choose to not apply the filter if it does not make sense (for example, applying echo-cancellation on a Bluetooth headset probably does not make sense. \since 1.0 */ + #define PA_PROP_FILTER_WANT "filter.want" + diff --git a/meta-tizen/recipes-multimedia/pulseaudio/pulseaudio_5.0/0039-add-bluetooth-a2dp-aptx-codec-support-samsung.patch b/meta-tizen/recipes-multimedia/pulseaudio/pulseaudio_5.0/0039-add-bluetooth-a2dp-aptx-codec-support-samsung.patch new file mode 100644 index 0000000..1355048 --- /dev/null +++ b/meta-tizen/recipes-multimedia/pulseaudio/pulseaudio_5.0/0039-add-bluetooth-a2dp-aptx-codec-support-samsung.patch @@ -0,0 +1,1024 @@ +From: "vivian,zhang" +Date: Tue, 18 Jun 2013 16:23:45 +0800 +Subject: add bluetooth a2dp aptx codec support - samsung + +Change-Id: I2c90198774c1e7d3e2ecb99f2dd365d56308f157 +Signed-off-by: Jaska Uimonen +--- + configure.ac | 20 ++ + src/Makefile.am | 27 +++ + src/modules/bluetooth/a2dp-codecs.h | 39 ++++ + src/modules/bluetooth/bluetooth-util.h | 183 +++++++++++++++++ + src/modules/bluetooth/bluez4-util.c | 184 ++++++++++++++++- + src/modules/bluetooth/module-bluetooth-discover.c | 37 ++++ + src/modules/bluetooth/module-bluez4-device.c | 236 ++++++++++++++++++++++ + 7 files changed, 724 insertions(+), 2 deletions(-) + create mode 100644 src/modules/bluetooth/bluetooth-util.h + +diff --git a/configure.ac b/configure.ac +index 0e205d3..ebff16c 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -1079,6 +1079,26 @@ else + fi + AC_SUBST(BLUETOOTH_HEADSET_BACKEND) + ++#### Bluetooth A2DP aptx codec support(optional) #### ++AC_ARG_ENABLE([bt_a2dp_aptx], ++ AS_HELP_STRING([--enable-bt-a2dp-aptx],[Enable optional Bluetooth A2DP aptx codec support(arm only)]), ++ [ ++ case "${enableval}" in ++ yes) bt_a2dp_aptx=yes ;; ++ no) bt_a2dp_aptx=no ;; ++ *) AC_MSG_ERROR(bad value ${enableval} for --enable-bt-a2dp-aptx) ;; ++ esac ++ ], ++ [bt_a2dp_aptx=false]) ++if test "x${bt_a2dp_aptx}" == xyes ; then ++ HAVE_BT_A2DP_APTX=1 ++else ++ HAVE_BT_A2DP_APTX=0 ++fi ++ ++AC_SUBST(HAVE_BT_A2DP_APTX) ++AM_CONDITIONAL([HAVE_BT_A2DP_APTX], [test "x$HAVE_BT_A2DP_APTX" = x1]) ++ + #### UDEV support (optional) #### + + AC_ARG_ENABLE([udev], +diff --git a/src/Makefile.am b/src/Makefile.am +index 4872dfd..3c062eb 100644 +--- a/src/Makefile.am ++++ b/src/Makefile.am +@@ -2078,7 +2078,11 @@ module_bluetooth_policy_la_CFLAGS = $(AM_CFLAGS) $(DBUS_CFLAGS) + module_bluetooth_discover_la_SOURCES = modules/bluetooth/module-bluetooth-discover.c + module_bluetooth_discover_la_LDFLAGS = $(MODULE_LDFLAGS) + module_bluetooth_discover_la_LIBADD = $(MODULE_LIBADD) ++if HAVE_BT_A2DP_APTX ++module_bluetooth_discover_la_CFLAGS = $(AM_CFLAGS) -DBLUETOOTH_APTX_SUPPORT ++else + module_bluetooth_discover_la_CFLAGS = $(AM_CFLAGS) ++endif + + # Bluetooth BlueZ 4 sink / source + module_bluez4_discover_la_SOURCES = modules/bluetooth/module-bluez4-discover.c +@@ -2092,12 +2096,23 @@ libbluez4_util_la_SOURCES = \ + modules/bluetooth/bluez4-util.h + libbluez4_util_la_LDFLAGS = -avoid-version + libbluez4_util_la_LIBADD = $(MODULE_LIBADD) $(DBUS_LIBS) ++ ++if HAVE_BT_A2DP_APTX ++libbluez4_util_la_CFLAGS = $(AM_CFLAGS) $(DBUS_CFLAGS) -DBLUETOOTH_APTX_SUPPORT ++else + libbluez4_util_la_CFLAGS = $(AM_CFLAGS) $(DBUS_CFLAGS) ++endif + + module_bluez4_device_la_SOURCES = modules/bluetooth/module-bluez4-device.c modules/bluetooth/rtp.h + module_bluez4_device_la_LDFLAGS = $(MODULE_LDFLAGS) + module_bluez4_device_la_LIBADD = $(MODULE_LIBADD) $(DBUS_LIBS) $(SBC_LIBS) libbluez4-util.la ++ ++if HAVE_BT_A2DP_APTX ++module_bluez4_device_la_CFLAGS = $(AM_CFLAGS) $(DBUS_CFLAGS) $(SBC_CFLAGS) \ ++ -DBLUETOOTH_APTX_SUPPORT ++else + module_bluez4_device_la_CFLAGS = $(AM_CFLAGS) $(DBUS_CFLAGS) $(SBC_CFLAGS) ++endif + + # Bluetooth BlueZ 5 sink / source + libbluez5_util_la_SOURCES = \ +@@ -2108,7 +2123,12 @@ libbluez5_util_la_SOURCES = \ + modules/bluetooth/hfaudioagent-@BLUETOOTH_HEADSET_BACKEND@.c + libbluez5_util_la_LDFLAGS = -avoid-version + libbluez5_util_la_LIBADD = $(MODULE_LIBADD) $(DBUS_LIBS) ++ ++if HAVE_BT_A2DP_APTX ++libbluez5_util_la_CFLAGS = $(AM_CFLAGS) $(DBUS_CFLAGS) -DBLUETOOTH_APTX_SUPPORT ++else + libbluez5_util_la_CFLAGS = $(AM_CFLAGS) $(DBUS_CFLAGS) ++endif + + module_bluez5_discover_la_SOURCES = modules/bluetooth/module-bluez5-discover.c + module_bluez5_discover_la_LDFLAGS = $(MODULE_LDFLAGS) +@@ -2120,6 +2140,13 @@ module_bluez5_device_la_LDFLAGS = $(MODULE_LDFLAGS) + module_bluez5_device_la_LIBADD = $(MODULE_LIBADD) $(SBC_LIBS) libbluez5-util.la + module_bluez5_device_la_CFLAGS = $(AM_CFLAGS) $(SBC_CFLAGS) + ++if HAVE_BT_A2DP_APTX ++module_bluez5_device_la_CFLAGS = $(AM_CFLAGS) $(SBC_CFLAGS) \ ++ -DBLUETOOTH_APTX_SUPPORT ++else ++module_bluez5_device_la_CFLAGS = $(AM_CFLAGS) $(SBC_CFLAGS) ++endif ++ + # Apple Airtunes/RAOP + module_raop_sink_la_SOURCES = modules/raop/module-raop-sink.c + module_raop_sink_la_LDFLAGS = $(MODULE_LDFLAGS) +diff --git a/src/modules/bluetooth/a2dp-codecs.h b/src/modules/bluetooth/a2dp-codecs.h +index 51c796a..c94812b 100644 +--- a/src/modules/bluetooth/a2dp-codecs.h ++++ b/src/modules/bluetooth/a2dp-codecs.h +@@ -27,6 +27,7 @@ + #define A2DP_CODEC_MPEG24 0x02 + #define A2DP_CODEC_ATRAC 0x03 + ++#define A2DP_CODEC_NON_A2DP 0xFF + #define SBC_SAMPLING_FREQ_16000 (1 << 3) + #define SBC_SAMPLING_FREQ_32000 (1 << 2) + #define SBC_SAMPLING_FREQ_44100 (1 << 1) +@@ -67,6 +68,32 @@ + #define MAX_BITPOOL 64 + #define MIN_BITPOOL 2 + ++/*#define APTX_CHANNEL_MODE_STEREO 2 */ ++/* ++ * aptX codec for Bluetooth only supports stereo mode with value 2 ++ * But we do have sink devices programmed to send capabilities with other channel mode support. ++ * So to handle the case and keeping codec symmetry with SBC etc., we do define other channel mode, ++ * and we always make sure to set configuration with APTX_CHANNEL_MODE_STEREO only. ++ * ++ * */ ++ ++#define APTX_CHANNEL_MODE_MONO (1 << 3) ++#define APTX_CHANNEL_MODE_DUAL_CHANNEL (1 << 2) ++#define APTX_CHANNEL_MODE_STEREO (1 << 1) ++#define APTX_CHANNEL_MODE_JOINT_STEREO 1 ++ ++#define APTX_VENDOR_ID0 0x4F /*APTX codec ID 79*/ ++#define APTX_VENDOR_ID1 0x0 ++#define APTX_VENDOR_ID2 0x0 ++#define APTX_VENDOR_ID3 0x0 ++ ++#define APTX_CODEC_ID0 0x1 ++#define APTX_CODEC_ID1 0x0 ++ ++#define APTX_SAMPLING_FREQ_16000 (1 << 3) ++#define APTX_SAMPLING_FREQ_32000 (1 << 2) ++#define APTX_SAMPLING_FREQ_44100 (1 << 1) ++#define APTX_SAMPLING_FREQ_48000 1 + #if __BYTE_ORDER == __LITTLE_ENDIAN + + typedef struct { +@@ -89,6 +116,12 @@ typedef struct { + uint16_t bitrate; + } __attribute__ ((packed)) a2dp_mpeg_t; + ++typedef struct { ++ uint8_t vendor_id[4]; ++ uint8_t codec_id[2]; ++ uint8_t channel_mode:4; ++ uint8_t frequency:4; ++} __attribute__ ((packed)) a2dp_aptx_t; + #elif __BYTE_ORDER == __BIG_ENDIAN + + typedef struct { +@@ -110,6 +143,12 @@ typedef struct { + uint8_t frequency:6; + uint16_t bitrate; + } __attribute__ ((packed)) a2dp_mpeg_t; ++typedef struct { ++ uint8_t vendor_id[4]; ++ uint8_t codec_id[2]; ++ uint8_t frequency:4; ++ uint8_t channel_mode:4; ++} __attribute__ ((packed)) a2dp_aptx_t; + + #else + #error "Unknown byte order" +diff --git a/src/modules/bluetooth/bluetooth-util.h b/src/modules/bluetooth/bluetooth-util.h +new file mode 100644 +index 0000000..859ad2d +--- /dev/null ++++ b/src/modules/bluetooth/bluetooth-util.h +@@ -0,0 +1,183 @@ ++#ifndef foobluetoothutilhfoo ++#define foobluetoothutilhfoo ++ ++/*** ++ This file is part of PulseAudio. ++ ++ Copyright 2008-2009 Joao Paulo Rechi Vita ++ ++ PulseAudio is free software; you can redistribute it and/or modify ++ it under the terms of the GNU Lesser General Public License as ++ published by the Free Software Foundation; either version 2.1 of the ++ License, or (at your option) any later version. ++ ++ PulseAudio is distributed in the hope that it will be useful, but ++ WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ General Public License for more details. ++ ++ You should have received a copy of the GNU Lesser General Public ++ License along with PulseAudio; if not, write to the Free Software ++ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 ++ USA. ++***/ ++ ++#include ++ ++#include ++#include ++ ++#define PA_BLUETOOTH_ERROR_NOT_SUPPORTED "org.bluez.Error.NotSupported" ++ ++/* UUID copied from bluez/audio/device.h */ ++#define GENERIC_AUDIO_UUID "00001203-0000-1000-8000-00805f9b34fb" ++ ++#define HSP_HS_UUID "00001108-0000-1000-8000-00805f9b34fb" ++#define HSP_AG_UUID "00001112-0000-1000-8000-00805f9b34fb" ++ ++#define HFP_HS_UUID "0000111e-0000-1000-8000-00805f9b34fb" ++#define HFP_AG_UUID "0000111f-0000-1000-8000-00805f9b34fb" ++ ++#define ADVANCED_AUDIO_UUID "0000110d-0000-1000-8000-00805f9b34fb" ++ ++#define A2DP_SOURCE_UUID "0000110a-0000-1000-8000-00805f9b34fb" ++#define A2DP_SINK_UUID "0000110b-0000-1000-8000-00805f9b34fb" ++ ++#define HSP_MAX_GAIN 15 ++ ++typedef struct pa_bluetooth_uuid pa_bluetooth_uuid; ++typedef struct pa_bluetooth_device pa_bluetooth_device; ++typedef struct pa_bluetooth_discovery pa_bluetooth_discovery; ++typedef struct pa_bluetooth_transport pa_bluetooth_transport; ++ ++struct userdata; ++ ++struct pa_bluetooth_uuid { ++ char *uuid; ++ PA_LLIST_FIELDS(pa_bluetooth_uuid); ++}; ++ ++enum profile { ++ PROFILE_A2DP, ++ PROFILE_A2DP_SOURCE, ++ PROFILE_HSP, ++ PROFILE_HFGW, ++ PROFILE_OFF ++}; ++ ++#define PA_BLUETOOTH_PROFILE_COUNT PROFILE_OFF ++ ++struct pa_bluetooth_hook_uuid_data { ++ pa_bluetooth_device *device; ++ const char *uuid; ++}; ++ ++/* Hook data: pa_bluetooth_discovery pointer. */ ++typedef enum pa_bluetooth_hook { ++ PA_BLUETOOTH_HOOK_DEVICE_CONNECTION_CHANGED, /* Call data: pa_bluetooth_device */ ++ PA_BLUETOOTH_HOOK_DEVICE_UUID_ADDED, /* Call data: pa_bluetooth_hook_uuid_data */ ++ PA_BLUETOOTH_HOOK_TRANSPORT_STATE_CHANGED, /* Call data: pa_bluetooth_transport */ ++ PA_BLUETOOTH_HOOK_TRANSPORT_NREC_CHANGED, /* Call data: pa_bluetooth_transport */ ++ PA_BLUETOOTH_HOOK_TRANSPORT_MICROPHONE_GAIN_CHANGED, /* Call data: pa_bluetooth_transport */ ++ PA_BLUETOOTH_HOOK_TRANSPORT_SPEAKER_GAIN_CHANGED, /* Call data: pa_bluetooth_transport */ ++ PA_BLUETOOTH_HOOK_MAX ++} pa_bluetooth_hook_t; ++ ++typedef enum pa_bluetooth_transport_state { ++ PA_BLUETOOTH_TRANSPORT_STATE_DISCONNECTED, ++ PA_BLUETOOTH_TRANSPORT_STATE_IDLE, /* Connected but not playing */ ++ PA_BLUETOOTH_TRANSPORT_STATE_PLAYING ++} pa_bluetooth_transport_state_t; ++ ++struct pa_bluetooth_transport { ++ pa_bluetooth_device *device; ++ char *owner; ++ char *path; ++ enum profile profile; ++ uint8_t codec; ++ uint8_t *config; ++ int config_size; ++ ++ pa_bluetooth_transport_state_t state; ++ bool nrec; ++ uint16_t microphone_gain; /* Used for HSP/HFP */ ++ uint16_t speaker_gain; /* Used for HSP/HFP */ ++}; ++ ++/* This enum is shared among Audio, Headset, AudioSink, and AudioSource, although not all values are acceptable in all profiles */ ++typedef enum pa_bt_audio_state { ++ PA_BT_AUDIO_STATE_INVALID = -1, ++ PA_BT_AUDIO_STATE_DISCONNECTED, ++ PA_BT_AUDIO_STATE_CONNECTING, ++ PA_BT_AUDIO_STATE_CONNECTED, ++ PA_BT_AUDIO_STATE_PLAYING ++} pa_bt_audio_state_t; ++ ++struct pa_bluetooth_device { ++ pa_bluetooth_discovery *discovery; ++ bool dead; ++ ++ int device_info_valid; /* 0: no results yet; 1: good results; -1: bad results ... */ ++ ++ /* Device information */ ++ char *name; ++ char *path; ++ pa_bluetooth_transport *transports[PA_BLUETOOTH_PROFILE_COUNT]; ++ int paired; ++ char *alias; ++ PA_LLIST_HEAD(pa_bluetooth_uuid, uuids); ++ char *address; ++ int class; ++ int trusted; ++ ++ /* Audio state */ ++ pa_bt_audio_state_t audio_state; ++ ++ /* AudioSink, AudioSource, Headset and HandsfreeGateway states */ ++ pa_bt_audio_state_t profile_state[PA_BLUETOOTH_PROFILE_COUNT]; ++}; ++ ++pa_bluetooth_discovery* pa_bluetooth_discovery_get(pa_core *core); ++pa_bluetooth_discovery* pa_bluetooth_discovery_ref(pa_bluetooth_discovery *y); ++void pa_bluetooth_discovery_unref(pa_bluetooth_discovery *d); ++ ++pa_bluetooth_device* pa_bluetooth_discovery_get_by_path(pa_bluetooth_discovery *d, const char* path); ++pa_bluetooth_device* pa_bluetooth_discovery_get_by_address(pa_bluetooth_discovery *d, const char* address); ++ ++bool pa_bluetooth_device_any_audio_connected(const pa_bluetooth_device *d); ++ ++int pa_bluetooth_transport_acquire(pa_bluetooth_transport *t, bool optional, size_t *imtu, size_t *omtu); ++void pa_bluetooth_transport_release(pa_bluetooth_transport *t); ++ ++void pa_bluetooth_transport_set_microphone_gain(pa_bluetooth_transport *t, uint16_t value); ++void pa_bluetooth_transport_set_speaker_gain(pa_bluetooth_transport *t, uint16_t value); ++ ++pa_hook* pa_bluetooth_discovery_hook(pa_bluetooth_discovery *y, pa_bluetooth_hook_t hook); ++ ++typedef enum pa_bt_form_factor { ++ PA_BT_FORM_FACTOR_UNKNOWN, ++ PA_BT_FORM_FACTOR_HEADSET, ++ PA_BT_FORM_FACTOR_HANDSFREE, ++ PA_BT_FORM_FACTOR_MICROPHONE, ++ PA_BT_FORM_FACTOR_SPEAKER, ++ PA_BT_FORM_FACTOR_HEADPHONE, ++ PA_BT_FORM_FACTOR_PORTABLE, ++ PA_BT_FORM_FACTOR_CAR, ++ PA_BT_FORM_FACTOR_HIFI, ++ PA_BT_FORM_FACTOR_PHONE, ++} pa_bt_form_factor_t; ++ ++pa_bt_form_factor_t pa_bluetooth_get_form_factor(uint32_t class); ++const char *pa_bt_form_factor_to_string(pa_bt_form_factor_t ff); ++ ++char *pa_bluetooth_cleanup_name(const char *name); ++ ++bool pa_bluetooth_uuid_has(pa_bluetooth_uuid *uuids, const char *uuid); ++const char *pa_bt_profile_to_string(enum profile profile); ++ ++#ifdef BLUETOOTH_APTX_SUPPORT ++int pa_load_aptx(const char *aptx_lib_name); ++int pa_unload_aptx(void); ++void* pa_aptx_get_handle(void); ++#endif ++#endif +diff --git a/src/modules/bluetooth/bluez4-util.c b/src/modules/bluetooth/bluez4-util.c +index f047b73..7e7aed0 100644 +--- a/src/modules/bluetooth/bluez4-util.c ++++ b/src/modules/bluetooth/bluez4-util.c +@@ -22,6 +22,9 @@ + #ifdef HAVE_CONFIG_H + #include + #endif ++#ifdef BLUETOOTH_APTX_SUPPORT ++#include ++#endif + + #include + +@@ -36,6 +39,9 @@ + #define ENDPOINT_PATH_HFP_HS "/MediaEndpoint/BlueZ4/HFPHS" + #define ENDPOINT_PATH_A2DP_SOURCE "/MediaEndpoint/BlueZ4/A2DPSource" + #define ENDPOINT_PATH_A2DP_SINK "/MediaEndpoint/BlueZ4/A2DPSink" ++#ifdef BLUETOOTH_APTX_SUPPORT ++#define ENDPOINT_PATH_A2DP_APTX_SOURCE "/MediaEndpoint/Bluez4/A2DPSource_aptx" ++#endif + + #define ENDPOINT_INTROSPECT_XML \ + DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE \ +@@ -80,6 +86,56 @@ static pa_dbus_pending* send_and_add_to_pending(pa_bluez4_discovery *y, DBusMess + static void found_adapter(pa_bluez4_discovery *y, const char *path); + static pa_bluez4_device *found_device(pa_bluez4_discovery *y, const char* path); + ++#ifdef BLUETOOTH_APTX_SUPPORT ++static void *aptx_handle = NULL; ++ ++int pa_unload_aptx(void) ++{ ++ if (aptx_handle == NULL) { ++ pa_log_warn("Unable to unload apt-X library"); ++ return -1; ++ } ++ ++ dlclose(aptx_handle); ++ aptx_handle = NULL; ++ ++ pa_log_debug("unloaded apt-X library successfully"); ++ return 0; ++} ++ ++int pa_load_aptx(const char *aptx_lib_name) ++{ ++ char* lib_path = NULL ; ++ ++ if(aptx_lib_name == NULL) ++ return -1; ++ ++ lib_path = pa_sprintf_malloc("%s/%s", PA_DLSEARCHPATH, aptx_lib_name); ++ ++ if (!lib_path) ++ return -1; ++ ++ pa_log_info("aptx_lib_path = [%s]", lib_path); ++ ++ aptx_handle = dlopen(lib_path, RTLD_LAZY); ++ if (aptx_handle == NULL) { ++ pa_log_warn("Unable to load apt-X library [%s]", dlerror()); ++ pa_xfree(lib_path); ++ return -1; ++ } ++ ++ pa_log_debug("loaded apt-X library successfully"); ++ pa_xfree(lib_path); ++ ++ return 0; ++} ++ ++void* pa_aptx_get_handle(void) ++{ ++ return aptx_handle; ++} ++#endif ++ + static pa_bluez4_audio_state_t audio_state_from_string(const char* value) { + pa_assert(value); + +@@ -835,6 +891,8 @@ static void register_endpoint(pa_bluez4_discovery *y, const char *path, const ch + uint8_t capability = 0; + pa_dbus_append_basic_array_variant_dict_entry(&d, "Capabilities", DBUS_TYPE_BYTE, &capability, 1); + } else { ++ pa_log_debug("register_endpoint: codec=%d[%s]", codec, codec==A2DP_CODEC_SBC ? "A2DP_CODEC_SBC" : codec==A2DP_CODEC_NON_A2DP ? "A2DP_CODEC_NON_A2DP" : "unknown"); ++ if (codec == A2DP_CODEC_SBC) { + a2dp_sbc_t capabilities; + + capabilities.channel_mode = SBC_CHANNEL_MODE_MONO | SBC_CHANNEL_MODE_DUAL_CHANNEL | +@@ -849,6 +907,23 @@ static void register_endpoint(pa_bluez4_discovery *y, const char *path, const ch + capabilities.max_bitpool = MAX_BITPOOL; + + pa_dbus_append_basic_array_variant_dict_entry(&d, "Capabilities", DBUS_TYPE_BYTE, &capabilities, sizeof(capabilities)); ++ } else if (codec == A2DP_CODEC_NON_A2DP ) { ++ /* aptx */ ++ a2dp_aptx_t capabilities; ++ ++ capabilities.vendor_id[0] = APTX_VENDOR_ID0; ++ capabilities.vendor_id[1] = APTX_VENDOR_ID1; ++ capabilities.vendor_id[2] = APTX_VENDOR_ID2; ++ capabilities.vendor_id[3] = APTX_VENDOR_ID3; ++ ++ capabilities.codec_id[0] = APTX_CODEC_ID0; ++ capabilities.codec_id[1] = APTX_CODEC_ID1; ++ ++ capabilities.channel_mode= APTX_CHANNEL_MODE_STEREO; ++ capabilities.frequency= APTX_SAMPLING_FREQ_44100; ++ ++ pa_dbus_append_basic_array_variant_dict_entry(&d, "Capabilities", DBUS_TYPE_BYTE, &capabilities, sizeof(capabilities)); ++ } + } + + dbus_message_iter_close_container(&i, &d); +@@ -857,6 +932,7 @@ static void register_endpoint(pa_bluez4_discovery *y, const char *path, const ch + } + + static void found_adapter(pa_bluez4_discovery *y, const char *path) { ++ + DBusMessage *m; + + pa_assert_se(m = dbus_message_new_method_call("org.bluez", path, "org.bluez.Adapter", "GetProperties")); +@@ -866,6 +942,10 @@ static void found_adapter(pa_bluez4_discovery *y, const char *path) { + register_endpoint(y, path, ENDPOINT_PATH_HFP_HS, HFP_HS_UUID); + register_endpoint(y, path, ENDPOINT_PATH_A2DP_SOURCE, A2DP_SOURCE_UUID); + register_endpoint(y, path, ENDPOINT_PATH_A2DP_SINK, A2DP_SINK_UUID); ++#ifdef BLUETOOTH_APTX_SUPPORT ++ if (aptx_handle) ++ register_endpoint(y, path, ENDPOINT_PATH_A2DP_APTX_SOURCE, A2DP_SOURCE_UUID); ++#endif + } + + static void list_adapters(pa_bluez4_discovery *y) { +@@ -1349,7 +1429,11 @@ static DBusMessage *endpoint_set_configuration(DBusConnection *conn, DBusMessage + p = PA_BLUEZ4_PROFILE_HSP; + else if (dbus_message_has_path(m, ENDPOINT_PATH_HFP_HS)) + p = PA_BLUEZ4_PROFILE_HFGW; ++#ifdef BLUETOOTH_APTX_SUPPORT ++ else if (dbus_message_has_path(m, ENDPOINT_PATH_A2DP_SOURCE) || dbus_message_has_path(m, ENDPOINT_PATH_A2DP_APTX_SOURCE)) ++#else + else if (dbus_message_has_path(m, ENDPOINT_PATH_A2DP_SOURCE)) ++#endif + p = PA_BLUEZ4_PROFILE_A2DP; + else + p = PA_BLUEZ4_PROFILE_A2DP_SOURCE; +@@ -1475,6 +1559,84 @@ static uint8_t a2dp_default_bitpool(uint8_t freq, uint8_t mode) { + } + } + ++#ifdef BLUETOOTH_APTX_SUPPORT ++static DBusMessage *endpoint_select_configuration_for_aptx(DBusConnection *c, DBusMessage *m, void *userdata) { ++ a2dp_aptx_t *cap; ++ a2dp_aptx_t config; ++ uint8_t *pconf = (uint8_t *) &config; ++ int size; ++ DBusMessage *r; ++ DBusError e; ++ ++ dbus_error_init(&e); ++ ++ if (!dbus_message_get_args(m, &e, DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE, &cap, &size, DBUS_TYPE_INVALID)) { ++ pa_log("org.bluez.MediaEndpoint.SelectConfiguration: %s", e.message); ++ dbus_error_free(&e); ++ goto fail; ++ } ++ ++ pa_assert(size == sizeof(config)); ++ ++ memset(&config, 0, sizeof(config)); ++ ++ if (cap->vendor_id[0] == APTX_VENDOR_ID0 && ++ cap->vendor_id[1] == APTX_VENDOR_ID1 && ++ cap->vendor_id[2] == APTX_VENDOR_ID2 && ++ cap->vendor_id[3] == APTX_VENDOR_ID3 && ++ cap->codec_id[0] == APTX_CODEC_ID0 && ++ cap->codec_id[1] == APTX_CODEC_ID1 ) ++ pa_log_debug("A2DP_CODEC_NON_A2DP and this is APTX Codec"); ++ else { ++ pa_log_debug("A2DP_CODEC_NON_A2DP but this is not APTX Codec"); ++ goto fail; ++ } ++ ++ memcpy(&config,cap, sizeof(config)); ++ ++/* The below code shuld be re-written by aptx */ ++/* And we should configure pulseaudio freq */ ++ ++ if (cap->frequency & APTX_SAMPLING_FREQ_44100) ++ config.frequency = APTX_SAMPLING_FREQ_44100; ++ else if (cap->frequency & APTX_SAMPLING_FREQ_48000) ++ config.frequency = APTX_SAMPLING_FREQ_48000; ++ else if (cap->frequency & APTX_SAMPLING_FREQ_32000) ++ config.frequency = APTX_SAMPLING_FREQ_32000; ++ else if (cap->frequency & APTX_SAMPLING_FREQ_16000) ++ config.frequency = APTX_SAMPLING_FREQ_16000; ++ else { ++ pa_log_error("No aptx supported frequencies"); ++ goto fail; ++ } ++ ++ if (cap->channel_mode & APTX_CHANNEL_MODE_JOINT_STEREO) ++ config.channel_mode = APTX_CHANNEL_MODE_STEREO; ++ else if (cap->channel_mode & APTX_CHANNEL_MODE_STEREO) ++ config.channel_mode = APTX_CHANNEL_MODE_STEREO; ++ else if (cap->channel_mode & APTX_CHANNEL_MODE_DUAL_CHANNEL) ++ config.channel_mode = APTX_CHANNEL_MODE_STEREO; ++ else { ++ pa_log_error("No aptx supported channel modes"); ++ goto fail; ++ } ++ ++ pa_assert_se(r = dbus_message_new_method_return(m)); ++ ++ pa_assert_se(dbus_message_append_args( ++ r, ++ DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE, &pconf, size, ++ DBUS_TYPE_INVALID)); ++ ++ return r; ++ ++fail: ++ pa_assert_se(r = (dbus_message_new_error(m, "org.bluez.MediaEndpoint.Error.InvalidArguments", ++ "Unable to select configuration"))); ++ return r; ++} ++#endif ++ + static DBusMessage *endpoint_select_configuration(DBusConnection *c, DBusMessage *m, void *userdata) { + pa_bluez4_discovery *y = userdata; + a2dp_sbc_t *cap, config; +@@ -1493,6 +1655,10 @@ static DBusMessage *endpoint_select_configuration(DBusConnection *c, DBusMessage + { 48000U, SBC_SAMPLING_FREQ_48000 } + }; + ++#ifdef BLUETOOTH_APTX_SUPPORT ++ if (dbus_message_has_path(m, A2DP_APTX_SOURCE_ENDPOINT)) ++ return endpoint_select_configuration_for_aptx(c ,m ,userdata); ++#endif + dbus_error_init(&e); + + if (!dbus_message_get_args(m, &e, DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE, &cap, &size, DBUS_TYPE_INVALID)) { +@@ -1614,8 +1780,13 @@ static DBusHandlerResult endpoint_handler(DBusConnection *c, DBusMessage *m, voi + + dbus_error_init(&e); + +- if (!pa_streq(path, ENDPOINT_PATH_A2DP_SOURCE) && !pa_streq(path, ENDPOINT_PATH_A2DP_SINK) +- && !pa_streq(path, ENDPOINT_PATH_HFP_AG) && !pa_streq(path, ENDPOINT_PATH_HFP_HS)) ++ if (!pa_streq(path, ENDPOINT_PATH_A2DP_SOURCE) && ++ !pa_streq(path, ENDPOINT_PATH_A2DP_SINK) && ++ !pa_streq(path, ENDPOINT_PATH_HFP_AG) && ++#ifdef BLUETOOTH_APTX_SUPPORT ++ !pa_streq(path, ENDPOINT_PATH_A2DP_APTX_SOURCE) && ++#endif ++ !pa_streq(path, ENDPOINT_PATH_HFP_HS)) + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + + if (dbus_message_is_method_call(m, "org.freedesktop.DBus.Introspectable", "Introspect")) { +@@ -1706,6 +1877,11 @@ pa_bluez4_discovery* pa_bluez4_discovery_get(pa_core *c) { + pa_assert_se(dbus_connection_register_object_path(conn, ENDPOINT_PATH_A2DP_SOURCE, &vtable_endpoint, y)); + pa_assert_se(dbus_connection_register_object_path(conn, ENDPOINT_PATH_A2DP_SINK, &vtable_endpoint, y)); + ++#ifdef BLUETOOTH_APTX_SUPPORT ++ if (aptx_handle) ++ pa_assert_se(dbus_connection_register_object_path(conn, ENDPOINT_PATH_A2DP_APTX_SOURCE, &vtable_endpoint, y)); ++#endif ++ + list_adapters(y); + + return y; +@@ -1754,6 +1930,10 @@ void pa_bluez4_discovery_unref(pa_bluez4_discovery *y) { + dbus_connection_unregister_object_path(pa_dbus_connection_get(y->connection), ENDPOINT_PATH_HFP_HS); + dbus_connection_unregister_object_path(pa_dbus_connection_get(y->connection), ENDPOINT_PATH_A2DP_SOURCE); + dbus_connection_unregister_object_path(pa_dbus_connection_get(y->connection), ENDPOINT_PATH_A2DP_SINK); ++#ifdef BLUETOOTH_APTX_SUPPORT ++ if (aptx_handle) ++ dbus_connection_unregister_object_path(pa_dbus_connection_get(y->connection), ENDPOINT_PATH_A2DP_APTX_SOURCE); ++#endif + pa_dbus_remove_matches( + pa_dbus_connection_get(y->connection), + "type='signal',sender='org.freedesktop.DBus',interface='org.freedesktop.DBus',member='NameOwnerChanged'" +diff --git a/src/modules/bluetooth/module-bluetooth-discover.c b/src/modules/bluetooth/module-bluetooth-discover.c +index 0bcfcf9..e1dbec5 100644 +--- a/src/modules/bluetooth/module-bluetooth-discover.c ++++ b/src/modules/bluetooth/module-bluetooth-discover.c +@@ -34,6 +34,17 @@ PA_MODULE_DESCRIPTION("Detect available Bluetooth daemon and load the correspond + PA_MODULE_VERSION(PACKAGE_VERSION); + PA_MODULE_LOAD_ONCE(true); + ++#ifdef BLUETOOTH_APTX_SUPPORT ++PA_MODULE_USAGE("aptx_lib_name="); ++#endif ++ ++#ifdef BLUETOOTH_APTX_SUPPORT ++static const char* const valid_modargs[] = { ++ "aptx_lib_name", ++ NULL ++}; ++#endif ++ + struct userdata { + uint32_t bluez5_module_idx; + uint32_t bluez4_module_idx; +@@ -43,8 +54,30 @@ int pa__init(pa_module* m) { + struct userdata *u; + pa_module *mm; + ++#ifdef BLUETOOTH_APTX_SUPPORT ++ pa_modargs *ma = NULL; ++ const char *aptx_lib_name = NULL; ++#endif ++ + pa_assert(m); + ++#ifdef BLUETOOTH_APTX_SUPPORT ++ if (!(ma = pa_modargs_new(m->argument, valid_modargs))) { ++ pa_log("Failed to parse module arguments"); ++ goto fail; ++ } ++ ++ if (pa_modargs_get_value(ma, "async", NULL)) ++ pa_log_warn("The 'async' argument is deprecated and does nothing."); ++ ++ ++ aptx_lib_name = pa_modargs_get_value(ma, "aptx_lib_name", NULL); ++ if (aptx_lib_name) ++ pa_load_aptx(aptx_lib_name); ++ else ++ pa_log("Failed to parse aptx_lib_name argument."); ++#endif ++ + m->userdata = u = pa_xnew0(struct userdata, 1); + u->bluez5_module_idx = PA_INVALID_INDEX; + u->bluez4_module_idx = PA_INVALID_INDEX; +@@ -83,5 +116,9 @@ void pa__done(pa_module* m) { + if (u->bluez4_module_idx != PA_INVALID_INDEX) + pa_module_unload_by_index(m->core, u->bluez4_module_idx, true); + ++#ifdef BLUETOOTH_APTX_SUPPORT ++ pa_unload_aptx(); ++#endif ++ + pa_xfree(u); + } +diff --git a/src/modules/bluetooth/module-bluez4-device.c b/src/modules/bluetooth/module-bluez4-device.c +index b0b12f8..eba92c6 100644 +--- a/src/modules/bluetooth/module-bluez4-device.c ++++ b/src/modules/bluetooth/module-bluez4-device.c +@@ -29,6 +29,9 @@ + #include + #include + #include ++#ifdef BLUETOOTH_APTX_SUPPORT ++#include ++#endif + + #include + #include +@@ -107,6 +110,10 @@ struct a2dp_info { + bool sbc_initialized; /* Keep track if the encoder is initialized */ + size_t codesize, frame_length; /* SBC Codesize, frame_length. We simply cache those values here */ + ++#ifdef BLUETOOTH_APTX_SUPPORT ++ pa_bool_t aptx_initialized; /* Keep track if the encoder is initialized */ ++ void *aptx; /* Codec data */ ++#endif + void* buffer; /* Codec transfer buffer */ + size_t buffer_size; /* Size of the buffer */ + +@@ -207,6 +214,42 @@ enum { + + static int init_profile(struct userdata *u); + ++#ifdef BLUETOOTH_APTX_SUPPORT ++void* (*aptx_new)(short endian); ++int (*aptx_encode)(void* _state, void* _pcmL, void* _pcmR, void* _buffer); ++ ++const char *aptx_new_name = "NewAptxEnc"; ++const char *aptx_encode_name = "aptxbtenc_encodestereo"; ++ ++static pa_bool_t pa_load_aptx_sym(void *handle ) ++{ ++ if (!handle) ++ return FALSE; ++ ++ aptx_new = (void* (*)(short endian))dlsym(handle, aptx_new_name); ++ ++ if (aptx_new) { ++ pa_log_debug("Load Symbol(%s)", aptx_new_name); ++ } else { ++ pa_log_debug("Fail to Load Symbol(%s)", aptx_new_name); ++ return FALSE; ++ } ++ ++ aptx_encode = (int (*)(void* _state, void* _pcmL, void* _pcmR, ++ void* _buffer)) ++ dlsym(handle, "aptxbtenc_encodestereo"); ++ ++ if (aptx_encode) { ++ pa_log_debug("Load Symbol(%s)", aptx_encode_name); ++ } else { ++ pa_log_debug("Fail to Load Symbol(%s)", aptx_encode_name); ++ return FALSE; ++ } ++ ++ return TRUE; ++} ++#endif ++ + /* from IO thread */ + static void a2dp_set_bitpool(struct userdata *u, uint8_t bitpool) { + struct a2dp_info *a2dp; +@@ -859,6 +902,123 @@ static int a2dp_process_render(struct userdata *u) { + return ret; + } + ++#ifdef BLUETOOTH_APTX_SUPPORT ++/* Run from IO thread */ ++static int a2dp_aptx_process_render(struct userdata *u) { ++ struct a2dp_info *a2dp; ++ size_t nbytes; ++ void *d; ++ const void *p; ++ size_t to_write, to_encode; ++ int ret = 0; ++ ++ int pcmL[4],pcmR[4]; ++ int i=0; ++ const short *mybuffer; ++ ++ pa_assert(u); ++ pa_assert(u->profile == PROFILE_A2DP); ++ pa_assert(u->sink); ++ ++ /* First, render some data */ ++ if (!u->write_memchunk.memblock) ++ pa_sink_render_full(u->sink, u->write_block_size, &u->write_memchunk); ++ ++ pa_assert(u->write_memchunk.length == u->write_block_size); ++ ++ a2dp_prepare_buffer(u); ++ ++ a2dp = &u->a2dp; ++ ++ /* Try to create a packet of the full MTU */ ++ p = (const uint8_t*) pa_memblock_acquire(u->write_memchunk.memblock) + u->write_memchunk.index; ++ to_encode = u->write_memchunk.length; ++ ++ d = (uint8_t*) a2dp->buffer ; ++ to_write = a2dp->buffer_size; ++ ++ while (PA_LIKELY(to_encode > 0 && to_write > 0)) { ++ size_t written; ++ ssize_t encoded; ++ ++ mybuffer=(uint8_t *)p; ++ ++ for (i = 0; i < 4; i += 1) { ++ pcmL[i] = mybuffer[2*i]; ++ pcmR[i] = mybuffer[2*i+1]; ++ } ++ /*(8 audio samples)16 bytes of audo data encoded to 4 bytes*/ ++ aptx_encode(a2dp->aptx, pcmL, pcmR, (short*)d); ++ ++ encoded=16; ++ written=4; ++ ++ pa_assert_fp((size_t) encoded <= to_encode); ++ pa_assert_fp((size_t) written <= to_write); ++ ++ p = (const uint8_t*) p + encoded; ++ to_encode -= encoded; ++ ++ d = (uint8_t*) d + written; ++ to_write -= written; ++ ++ } ++ ++ pa_memblock_release(u->write_memchunk.memblock); ++ ++ pa_assert(to_encode == 0); ++ ++ PA_ONCE_BEGIN { ++ pa_log_debug("Using APTX encoder implementation"); ++ } PA_ONCE_END; ++ ++ nbytes = (uint8_t*) d - (uint8_t*) a2dp->buffer; ++ ++ for (;;) { ++ ssize_t l; ++ ++ l = pa_write(u->stream_fd, a2dp->buffer, nbytes, &u->stream_write_type); ++ ++ pa_assert(l != 0); ++ ++ if (l < 0) { ++ ++ if (errno == EINTR) ++ /* Retry right away if we got interrupted */ ++ continue; ++ ++ else if (errno == EAGAIN) ++ /* Hmm, apparently the socket was not writable, give up for now */ ++ break; ++ ++ pa_log_error("Failed to write data to socket: %s", pa_cstrerror(errno)); ++ ret = -1; ++ break; ++ } ++ ++ pa_assert((size_t) l <= nbytes); ++ ++ if ((size_t) l != nbytes) { ++ pa_log_warn("Wrote memory block to socket only partially! %llu written, wanted to write %llu.", ++ (unsigned long long) l, ++ (unsigned long long) nbytes); ++ ret = -1; ++ break; ++ } ++ ++ u->write_index += (uint64_t) u->write_memchunk.length; ++ pa_memblock_unref(u->write_memchunk.memblock); ++ pa_memchunk_reset(&u->write_memchunk); ++ ++ ret = 1; ++ ++ break; ++ } ++ ++ return ret; ++} ++#endif ++ + static int a2dp_process_push(struct userdata *u) { + int ret = 0; + pa_memchunk memchunk; +@@ -1104,8 +1264,13 @@ static void thread_func(void *userdata) { + u->started_at = pa_rtclock_now(); + + if (u->profile == PA_BLUEZ4_PROFILE_A2DP) { ++#ifdef BLUETOOTH_APTX_SUPPORT ++ if ((n_written = a2dp_aptx_process_render(u)) < 0) ++ goto io_fail; ++#else + if ((n_written = a2dp_process_render(u)) < 0) + goto io_fail; ++#endif + } else { + if ((n_written = hsp_process_render(u)) < 0) + goto io_fail; +@@ -1689,14 +1854,73 @@ static int add_source(struct userdata *u) { + return 0; + } + ++#ifdef BLUETOOTH_APTX_SUPPORT ++/* should be implemeted */ ++static int bt_transport_config_a2dp_for_aptx(struct userdata *u) { ++ //const pa_bluetooth_transport *t; ++ struct a2dp_info *a2dp = &u->a2dp; ++ //a2dp_sbc_t *config; ++ ++ //t = pa_bluetooth_discovery_get_transport(u->discovery, u->transport); ++ //pa_assert(t); ++ ++ //config = (a2dp_sbc_t *) t->config; ++ ++ u->sample_spec.format = PA_SAMPLE_S16LE; ++ ++ if (!a2dp->aptx_initialized){ ++ #if __BYTE_ORDER==__LITTLE_ENDIAN ++ a2dp->aptx = aptx_new(1); ++ #elif __BYTE_ORDER==__BIG_ENDIAN ++ a2dp->aptx = aptx_new(0); ++ #else ++ #error "Unknown byte order" ++ #endif ++ a2dp->aptx_initialized = TRUE; ++ } ++ ++ pa_log_debug("aptx Encoder is intialized !!"); ++ ++ u->write_block_size =(size_t)(u->write_link_mtu/(size_t)16) *16*4 ; ++ u->read_block_size =(size_t)(u->read_link_mtu/(size_t)16) *16*4 ; ++ ++ pa_log_info("APTX parameters write_block_size(%d),write_link_mtu(%d)",u->write_block_size,u->write_link_mtu); ++ pa_log_info("APTX parameters read_block_size(%d),read_link_mtu(%d)",u->read_block_size,u->read_link_mtu); ++ ++ return 0; ++} ++#endif ++ + static void bt_transport_config_a2dp(struct userdata *u) { + const pa_bluez4_transport *t; + struct a2dp_info *a2dp = &u->a2dp; + a2dp_sbc_t *config; ++#ifdef BLUETOOTH_APTX_SUPPORT ++ a2dp_aptx_t *aptx_config; ++#endif + + t = u->transport; + pa_assert(t); + ++#ifdef BLUETOOTH_APTX_SUPPORT ++ if (t->codec == A2DP_CODEC_NON_A2DP) { ++ aptx_config = (a2dp_aptx_t *) t->config; ++ if (aptx_config->vendor_id[0] == APTX_VENDOR_ID0 && ++ aptx_config->vendor_id[1] == APTX_VENDOR_ID1 && ++ aptx_config->vendor_id[2] == APTX_VENDOR_ID2 && ++ aptx_config->vendor_id[3] == APTX_VENDOR_ID3 && ++ aptx_config->codec_id[0] == APTX_CODEC_ID0 && ++ aptx_config->codec_id[1] == APTX_CODEC_ID1 ){ ++ pa_log("A2DP_CODEC_NON_A2DP and this is APTX Codec"); ++ ++ return bt_transport_config_a2dp_for_aptx(u); ++ } else { ++ pa_log("A2DP_CODEC_NON_A2DP but this is not APTX Codec"); ++ return -1; ++ } ++ } ++#endif ++ + config = (a2dp_sbc_t *) t->config; + + u->sample_spec.format = PA_SAMPLE_S16LE; +@@ -2424,6 +2648,9 @@ int pa__init(pa_module *m) { + struct userdata *u; + const char *address, *path; + pa_bluez4_device *device; ++#ifdef BLUETOOTH_APTX_SUPPORT ++ void *handle; ++#endif + + pa_assert(m); + +@@ -2524,6 +2751,15 @@ int pa__init(pa_module *m) { + u->msg->parent.process_msg = device_process_msg; + u->msg->card = u->card; + ++#ifdef BLUETOOTH_APTX_SUPPORT ++ handle = pa_aptx_get_handle(); ++ ++ if (handle) { ++ pa_log_debug("Aptx Library loaded\n"); ++ pa_load_aptx_sym(handle); ++ } ++#endif ++ + if (u->profile != PA_BLUEZ4_PROFILE_OFF) + if (init_profile(u) < 0) + goto off; diff --git a/meta-tizen/recipes-multimedia/pulseaudio/pulseaudio_5.0/0040-create-pa_ready-file-samsung.patch b/meta-tizen/recipes-multimedia/pulseaudio/pulseaudio_5.0/0040-create-pa_ready-file-samsung.patch new file mode 100644 index 0000000..191e70e --- /dev/null +++ b/meta-tizen/recipes-multimedia/pulseaudio/pulseaudio_5.0/0040-create-pa_ready-file-samsung.patch @@ -0,0 +1,40 @@ +From: Jaska Uimonen +Date: Thu, 8 Aug 2013 11:23:38 +0300 +Subject: create pa_ready file - samsung + +Change-Id: I2146599f2e814be064864f8ca76879b761642f11 +Signed-off-by: Jaska Uimonen +--- + src/daemon/main.c | 5 +++++ + 1 file changed, 5 insertions(+) + +diff --git a/src/daemon/main.c b/src/daemon/main.c +index e01371e..74cb45f 100644 +--- a/src/daemon/main.c ++++ b/src/daemon/main.c +@@ -38,6 +38,8 @@ + #include + #include + ++#include ++ + #ifdef HAVE_SYS_MMAN_H + #include + #endif +@@ -101,6 +103,7 @@ + #include "ltdl-bind-now.h" + #include "server-lookup.h" + ++#define PA_READY "/tmp/.pa_ready" + #ifdef HAVE_LIBWRAP + /* Only one instance of these variables */ + int allow_severity = LOG_INFO; +@@ -1145,6 +1148,8 @@ int main(int argc, char *argv[]) { + #endif + + pa_log_info(_("Daemon startup complete.")); ++ /* broadcast if we're ready */ ++ creat(PA_READY, 0644); + + retval = 0; + if (pa_mainloop_run(mainloop, &retval) < 0) diff --git a/meta-tizen/recipes-multimedia/pulseaudio/pulseaudio_5.0/0041-set-alsa-suspend-timeout-to-zero-samsung.patch b/meta-tizen/recipes-multimedia/pulseaudio/pulseaudio_5.0/0041-set-alsa-suspend-timeout-to-zero-samsung.patch new file mode 100644 index 0000000..9c6c9a4 --- /dev/null +++ b/meta-tizen/recipes-multimedia/pulseaudio/pulseaudio_5.0/0041-set-alsa-suspend-timeout-to-zero-samsung.patch @@ -0,0 +1,32 @@ +From: Jaska Uimonen +Date: Thu, 8 Aug 2013 11:24:25 +0300 +Subject: set alsa suspend timeout to zero - samsung + +Change-Id: Ie7c93c727d878226189f751efbd6e088ece7f36f +Signed-off-by: Jaska Uimonen +--- + src/modules/alsa/alsa-sink.c | 4 ++++ + 1 file changed, 4 insertions(+) + +diff --git a/src/modules/alsa/alsa-sink.c b/src/modules/alsa/alsa-sink.c +index 5a41cf6..03babb3 100644 +--- a/src/modules/alsa/alsa-sink.c ++++ b/src/modules/alsa/alsa-sink.c +@@ -60,6 +60,7 @@ + #include "alsa-util.h" + #include "alsa-sink.h" + ++#define ALSA_SUSPEND_ON_IDLE_TIMEOUT "0" + /* #define DEBUG_TIMING */ + + #define DEFAULT_DEVICE "default" +@@ -2275,6 +2276,9 @@ pa_sink *pa_alsa_sink_new(pa_module *m, pa_modargs *ma, const char*driver, pa_ca + pa_proplist_setf(data.proplist, PA_PROP_DEVICE_BUFFERING_FRAGMENT_SIZE, "%lu", (unsigned long) (period_frames * frame_size)); + pa_proplist_sets(data.proplist, PA_PROP_DEVICE_ACCESS_MODE, u->use_tsched ? "mmap+timer" : (u->use_mmap ? "mmap" : "serial")); + ++ /* Set Suspend timeout to ZERO to avoid noise */ ++ pa_log_info("Set suspend-on-idle timeout to ZERO to avoid noise"); ++ pa_proplist_sets(data.proplist, "module-suspend-on-idle.timeout", ALSA_SUSPEND_ON_IDLE_TIMEOUT); + if (mapping) { + pa_proplist_sets(data.proplist, PA_PROP_DEVICE_PROFILE_NAME, mapping->name); + pa_proplist_sets(data.proplist, PA_PROP_DEVICE_PROFILE_DESCRIPTION, mapping->description); diff --git a/meta-tizen/recipes-multimedia/pulseaudio/pulseaudio_5.0/0042-cope-with-possible-infinite-waiting-in-startup-samsu.patch b/meta-tizen/recipes-multimedia/pulseaudio/pulseaudio_5.0/0042-cope-with-possible-infinite-waiting-in-startup-samsu.patch new file mode 100644 index 0000000..4e91da4 --- /dev/null +++ b/meta-tizen/recipes-multimedia/pulseaudio/pulseaudio_5.0/0042-cope-with-possible-infinite-waiting-in-startup-samsu.patch @@ -0,0 +1,61 @@ +From: Jaska Uimonen +Date: Thu, 8 Aug 2013 11:27:44 +0300 +Subject: cope with possible infinite waiting in startup - samsung + +Change-Id: Ie7c74131e267f44324f031a953c15f81b0c31a07 +Signed-off-by: Jaska Uimonen +--- + src/pulsecore/core-util.c | 19 ++++++++++++++++++- + 1 file changed, 18 insertions(+), 1 deletion(-) + +diff --git a/src/pulsecore/core-util.c b/src/pulsecore/core-util.c +index 0d9e354..1256a1e 100644 +--- a/src/pulsecore/core-util.c ++++ b/src/pulsecore/core-util.c +@@ -1698,6 +1698,7 @@ static char* make_random_dir(mode_t m) { + char *fn; + size_t pathlen; + ++ srand (time(NULL)); + fn = pa_sprintf_malloc("%s" PA_PATH_SEP "pulse-XXXXXXXXXXXX", pa_get_temp_dir()); + pathlen = strlen(fn); + +@@ -1763,6 +1764,7 @@ static int make_random_dir_and_link(mode_t m, const char *k) { + char *pa_get_runtime_dir(void) { + char *d, *k = NULL, *p = NULL, *t = NULL, *mid; + mode_t m; ++ int retry_count = 100; + + /* The runtime directory shall contain dynamic data that needs NOT + * to be kept across reboots and is usually private to the user, +@@ -1823,6 +1825,21 @@ char *pa_get_runtime_dir(void) { + for (;;) { + /* OK, first let's check if the "runtime" symlink already exists */ + ++ /* FIXME: This is recovery routine for infinite waiting issue such as below situation. ++ * eg. 50f64052a5dbbe087c11dfac4effb63c-runtime -> /tmp/pulse-LDK8gTL6Dh9N ++ 50f64052a5dbbe087c11dfac4effb63c-runtime.tmp -> /tmp/pulse-cDM1bQhObZ7O */ ++ if (retry_count-- == 0) { ++ pa_log_error ("retry is over....do cleanup"); ++ ++ /* Remove original file */ ++ unlink (k); ++ ++ /* Remove original.tmp file */ ++ t = pa_sprintf_malloc("%s.tmp", k); ++ unlink (t); ++ pa_xfree(t); ++ t = NULL; ++ } + p = pa_readlink(k); + if (!p) { + +@@ -3297,7 +3314,7 @@ const char *pa_get_temp_dir(void) { + pa_is_path_absolute(t)) + return t; + +- return "/tmp"; ++ return "/tmp/pulseaudio"; + } + + int pa_open_cloexec(const char *fn, int flags, mode_t mode) { diff --git a/meta-tizen/recipes-multimedia/pulseaudio/pulseaudio_5.0/0043-use-udev-only-for-usb-devices-samsung.patch b/meta-tizen/recipes-multimedia/pulseaudio/pulseaudio_5.0/0043-use-udev-only-for-usb-devices-samsung.patch new file mode 100644 index 0000000..d989b3f --- /dev/null +++ b/meta-tizen/recipes-multimedia/pulseaudio/pulseaudio_5.0/0043-use-udev-only-for-usb-devices-samsung.patch @@ -0,0 +1,98 @@ +From: Jaska Uimonen +Date: Thu, 8 Aug 2013 11:28:39 +0300 +Subject: use udev only for usb devices - samsung + +Change-Id: Ia8cd2f5eb5ebe5248af11906c67d572ede133b33 +Signed-off-by: Jaska Uimonen +--- + configure.ac | 14 +++++++++++++- + src/modules/alsa/module-alsa-card.c | 7 +++++++ + src/modules/module-udev-detect.c | 17 +++++++++++++++++ + 3 files changed, 37 insertions(+), 1 deletion(-) + +diff --git a/configure.ac b/configure.ac +index ebff16c..13c8f6c 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -1115,8 +1115,18 @@ AC_SUBST(HAVE_UDEV) + AM_CONDITIONAL([HAVE_UDEV], [test "x$HAVE_UDEV" = x1]) + AS_IF([test "x$HAVE_UDEV" = "x1"], AC_DEFINE([HAVE_UDEV], 1, [Have UDEV.])) + +-#### HAL compat support (optional, dependent on UDEV) #### ++#### udev for usb _only_ support (optional, dependant on UDEV) #### ++AC_ARG_ENABLE([udev_with_usb_only], ++ AS_HELP_STRING([--enable-udev-with-usb-only],[Enable UDEV with only USB support])) ++ ++AS_IF([test "x$enable_udev_with_usb_only" != "xyes"], ++ [AS_IF([test "x$HAVE_UDEV" = "x1"], HAVE_UDEV_ONLY_USB=0, HAVE_UDEV_ONLY_USB=1)], ++ HAVE_UDEV_ONLY_USB=1) + ++AM_CONDITIONAL([HAVE_UDEV_ONLY_USB], [test "x$HAVE_UDEV_ONLY_USB" = x1]) ++AS_IF([test "x$HAVE_UDEV_ONLY_USB" = "x1"], AC_DEFINE([HAVE_UDEV_ONLY_USB], 1, [Have usb only with udev.])) ++ ++#### HAL compat support (optional, dependent on UDEV) #### + AC_ARG_ENABLE([hal-compat], + AS_HELP_STRING([--disable-hal-compat],[Disable optional HAL->udev transition compatibility support])) + +@@ -1511,6 +1521,7 @@ AS_IF([test "x$HAVE_SYSTEMD_JOURNAL" = "x1"], ENABLE_SYSTEMD_JOURNAL=yes, ENABLE + AS_IF([test "x$HAVE_BLUEZ_4" = "x1"], ENABLE_BLUEZ_4=yes, ENABLE_BLUEZ_4=no) + AS_IF([test "x$HAVE_BLUEZ_5" = "x1"], ENABLE_BLUEZ_5=yes, ENABLE_BLUEZ_5=no) + AS_IF([test "x$HAVE_HAL_COMPAT" = "x1"], ENABLE_HAL_COMPAT=yes, ENABLE_HAL_COMPAT=no) ++AS_IF([test "x$HAVE_UDEV_ONLY_USB" = "x1"], ENABLE_UDEV_ONLY_USB=yes, ENABLE_UDEV_ONLY_USB=no) + AS_IF([test "x$HAVE_TCPWRAP" = "x1"], ENABLE_TCPWRAP=yes, ENABLE_TCPWRAP=no) + AS_IF([test "x$HAVE_LIBSAMPLERATE" = "x1"], ENABLE_LIBSAMPLERATE=yes, ENABLE_LIBSAMPLERATE=no) + AS_IF([test "x$HAVE_IPV6" = "x1"], ENABLE_IPV6=yes, ENABLE_IPV6=no) +@@ -1566,6 +1577,7 @@ echo " + headset backed: ${BLUETOOTH_HEADSET_BACKEND} + Enable udev: ${ENABLE_UDEV} + Enable HAL->udev compat: ${ENABLE_HAL_COMPAT} ++ Enable udev usb only: ${ENABLE_UDEV_ONLY_USB} + Enable systemd login: ${ENABLE_SYSTEMD} + Enable systemd journal: ${ENABLE_SYSTEMD_JOURNAL} + Enable TCP Wrappers: ${ENABLE_TCPWRAP} +diff --git a/src/modules/alsa/module-alsa-card.c b/src/modules/alsa/module-alsa-card.c +index cf54c0f..1e63230 100644 +--- a/src/modules/alsa/module-alsa-card.c ++++ b/src/modules/alsa/module-alsa-card.c +@@ -678,6 +678,13 @@ int pa__init(pa_module *m) { + u->use_ucm = false; + #ifdef HAVE_UDEV + fn = pa_udev_get_property(u->alsa_card_index, "PULSE_PROFILE_SET"); ++#ifdef ENABLE_UDEV_ONLY_USB ++ pa_log("PULSE_PROFILE_SET = %s", fn); ++ if (fn == NULL) { ++ fn = strdup ("tizen_usb.conf"); ++ pa_log("(new) PULSE_PROFILE_SET = %s", fn); ++ } ++#endif + #endif + + if (pa_modargs_get_value(u->modargs, "profile_set", NULL)) { +diff --git a/src/modules/module-udev-detect.c b/src/modules/module-udev-detect.c +index c28c867..1e8fb06 100644 +--- a/src/modules/module-udev-detect.c ++++ b/src/modules/module-udev-detect.c +@@ -464,6 +464,23 @@ static void process_device(struct userdata *u, struct udev_device *dev) { + return; + } + ++ pa_log_debug ("devpath = %s", udev_device_get_devpath(dev)); ++ pa_log_debug ("subsystem = %s", udev_device_get_subsystem(dev)); ++ pa_log_debug ("devtype = %s", udev_device_get_devtype(dev)); ++ pa_log_debug ("syspath = %s", udev_device_get_syspath(dev)); ++ pa_log_debug ("sysname = %s", udev_device_get_sysname(dev)); ++ pa_log_debug ("sysnum = %s", udev_device_get_sysnum(dev)); ++ pa_log_debug ("devnode = %s", udev_device_get_devnode(dev)); ++ pa_log_debug ("parent subsystem = %s", udev_device_get_subsystem(udev_device_get_parent(dev))); ++ ++#ifdef ENABLE_UDEV_ONLY_USB ++ /* If parent's subsystem is not USB, return */ ++ if (!pa_streq(udev_device_get_subsystem(udev_device_get_parent(dev)), "usb")) { ++ pa_log_debug("Ignoring %s, because it's parent subsystem is not a USB.", udev_device_get_devpath(dev)); ++ return; ++ } ++#endif ++ + action = udev_device_get_action(dev); + + if (action && pa_streq(action, "remove")) diff --git a/meta-tizen/recipes-multimedia/pulseaudio/pulseaudio_5.0/0044-fixes-and-improvements-to-makefile-and-configure-in-.patch b/meta-tizen/recipes-multimedia/pulseaudio/pulseaudio_5.0/0044-fixes-and-improvements-to-makefile-and-configure-in-.patch new file mode 100644 index 0000000..0cb735b --- /dev/null +++ b/meta-tizen/recipes-multimedia/pulseaudio/pulseaudio_5.0/0044-fixes-and-improvements-to-makefile-and-configure-in-.patch @@ -0,0 +1,120 @@ +From: Jaska Uimonen +Date: Fri, 7 Mar 2014 17:49:42 +0200 +Subject: fixes and improvements to makefile and configure in - samsung + +Change-Id: Ic2338a8382fe45f9d509537950592c9c4aa83606 +Signed-off-by: Jaska Uimonen +--- + configure.ac | 14 +++++++++++++- + src/Makefile.am | 28 +++++++++------------------- + 2 files changed, 22 insertions(+), 20 deletions(-) + +diff --git a/configure.ac b/configure.ac +index 13c8f6c..870375f 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -1539,6 +1539,11 @@ AS_IF([test "x$HAVE_ESOUND" = "x1" -a "x$USE_PER_USER_ESOUND_SOCKET" = "x1"], EN + AS_IF([test "x$HAVE_GCOV" = "x1"], ENABLE_GCOV=yes, ENABLE_GCOV=no) + AS_IF([test "x$HAVE_LIBCHECK" = "x1"], ENABLE_TESTS=yes, ENABLE_TESTS=no) + AS_IF([test "x$enable_legacy_database_entry_format" != "xno"], ENABLE_LEGACY_DATABASE_ENTRY_FORMAT=yes, ENABLE_LEGACY_DATABASE_ENTRY_FORMAT=no) ++AS_IF([test "x$USE_SAMSUNG_POLICY" = "x1"], ENABLE_SAMSUNG_POLICY=yes, ENABLE_SAMSUNG_POLICY=no) ++AS_IF([test "x$USE_DLOG" = "x1"], ENABLE_DLOG=yes, ENABLE_DLOG=no) ++AS_IF([test "x$USE_PM_LOCK" = "x1"], ENABLE_PM_LOCK=yes, ENABLE_PM_LOCK=no) ++AS_IF([test "x$USE_BT_PROFILE_SET" = "x1"], ENABLE_BT_PROFILE_SET=yes, ENABLE_BT_PROFILE_SET=no) ++AS_IF([test "x$HAVE_BT_A2DP_APTX" = "x1"], HAVE_BT_A2DP_APTX=yes, HAVE_BT_A2DP_APTX=no) + + echo " + ---{ $PACKAGE_NAME $VERSION }--- +@@ -1577,7 +1582,6 @@ echo " + headset backed: ${BLUETOOTH_HEADSET_BACKEND} + Enable udev: ${ENABLE_UDEV} + Enable HAL->udev compat: ${ENABLE_HAL_COMPAT} +- Enable udev usb only: ${ENABLE_UDEV_ONLY_USB} + Enable systemd login: ${ENABLE_SYSTEMD} + Enable systemd journal: ${ENABLE_SYSTEMD_JOURNAL} + Enable TCP Wrappers: ${ENABLE_TCPWRAP} +@@ -1604,6 +1608,14 @@ echo " + Preopened modules: ${PREOPEN_MODS} + + Legacy Database Entry Support: ${ENABLE_LEGACY_DATABASE_ENTRY_FORMAT} ++ ++ Tizen ++ samsung policy: ${ENABLE_SAMSUNG_POLICY} ++ dlog: ${ENABLE_DLOG} ++ pmapi: ${ENABLE_PM_LOCK} ++ bluetooth profile set: ${ENABLE_BT_PROFILE_SET} ++ bluetooth aptx codec: ${HAVE_BT_A2DP_APTX} ++ udev with usb only: ${ENABLE_UDEV_ONLY_USB} + " + + if test "${ENABLE_SPEEX}" = "no" && test "${ENABLE_WEBRTC}" = "no" && test "${ENABLE_ADRIAN_EC}" = "no" ; then +diff --git a/src/Makefile.am b/src/Makefile.am +index 3c062eb..a60b5bf 100644 +--- a/src/Makefile.am ++++ b/src/Makefile.am +@@ -2078,10 +2078,9 @@ module_bluetooth_policy_la_CFLAGS = $(AM_CFLAGS) $(DBUS_CFLAGS) + module_bluetooth_discover_la_SOURCES = modules/bluetooth/module-bluetooth-discover.c + module_bluetooth_discover_la_LDFLAGS = $(MODULE_LDFLAGS) + module_bluetooth_discover_la_LIBADD = $(MODULE_LIBADD) +-if HAVE_BT_A2DP_APTX +-module_bluetooth_discover_la_CFLAGS = $(AM_CFLAGS) -DBLUETOOTH_APTX_SUPPORT +-else + module_bluetooth_discover_la_CFLAGS = $(AM_CFLAGS) ++if HAVE_BT_A2DP_APTX ++module_bluetooth_discover_la_CFLAGS += -DBLUETOOTH_APTX_SUPPORT + endif + + # Bluetooth BlueZ 4 sink / source +@@ -2096,22 +2095,17 @@ libbluez4_util_la_SOURCES = \ + modules/bluetooth/bluez4-util.h + libbluez4_util_la_LDFLAGS = -avoid-version + libbluez4_util_la_LIBADD = $(MODULE_LIBADD) $(DBUS_LIBS) +- +-if HAVE_BT_A2DP_APTX +-libbluez4_util_la_CFLAGS = $(AM_CFLAGS) $(DBUS_CFLAGS) -DBLUETOOTH_APTX_SUPPORT +-else + libbluez4_util_la_CFLAGS = $(AM_CFLAGS) $(DBUS_CFLAGS) ++if HAVE_BT_A2DP_APTX ++libbluez4_util_la_CFLAGS += -DBLUETOOTH_APTX_SUPPORT + endif + + module_bluez4_device_la_SOURCES = modules/bluetooth/module-bluez4-device.c modules/bluetooth/rtp.h + module_bluez4_device_la_LDFLAGS = $(MODULE_LDFLAGS) + module_bluez4_device_la_LIBADD = $(MODULE_LIBADD) $(DBUS_LIBS) $(SBC_LIBS) libbluez4-util.la +- +-if HAVE_BT_A2DP_APTX +-module_bluez4_device_la_CFLAGS = $(AM_CFLAGS) $(DBUS_CFLAGS) $(SBC_CFLAGS) \ +- -DBLUETOOTH_APTX_SUPPORT +-else + module_bluez4_device_la_CFLAGS = $(AM_CFLAGS) $(DBUS_CFLAGS) $(SBC_CFLAGS) ++if HAVE_BT_A2DP_APTX ++module_bluez4_device_la_CFLAGS += -DBLUETOOTH_APTX_SUPPORT + endif + + # Bluetooth BlueZ 5 sink / source +@@ -2123,11 +2117,10 @@ libbluez5_util_la_SOURCES = \ + modules/bluetooth/hfaudioagent-@BLUETOOTH_HEADSET_BACKEND@.c + libbluez5_util_la_LDFLAGS = -avoid-version + libbluez5_util_la_LIBADD = $(MODULE_LIBADD) $(DBUS_LIBS) ++libbluez5_util_la_CFLAGS = $(AM_CFLAGS) $(DBUS_CFLAGS) + + if HAVE_BT_A2DP_APTX +-libbluez5_util_la_CFLAGS = $(AM_CFLAGS) $(DBUS_CFLAGS) -DBLUETOOTH_APTX_SUPPORT +-else +-libbluez5_util_la_CFLAGS = $(AM_CFLAGS) $(DBUS_CFLAGS) ++libbluez5_util_la_CFLAGS += -DBLUETOOTH_APTX_SUPPORT + endif + + module_bluez5_discover_la_SOURCES = modules/bluetooth/module-bluez5-discover.c +@@ -2141,10 +2134,7 @@ module_bluez5_device_la_LIBADD = $(MODULE_LIBADD) $(SBC_LIBS) libbluez5-util.la + module_bluez5_device_la_CFLAGS = $(AM_CFLAGS) $(SBC_CFLAGS) + + if HAVE_BT_A2DP_APTX +-module_bluez5_device_la_CFLAGS = $(AM_CFLAGS) $(SBC_CFLAGS) \ +- -DBLUETOOTH_APTX_SUPPORT +-else +-module_bluez5_device_la_CFLAGS = $(AM_CFLAGS) $(SBC_CFLAGS) ++module_bluez5_device_la_CFLAGS += -DBLUETOOTH_APTX_SUPPORT + endif + + # Apple Airtunes/RAOP diff --git a/meta-tizen/recipes-multimedia/pulseaudio/pulseaudio_5.0/0045-fix-warning-in-gconf-helper.patch b/meta-tizen/recipes-multimedia/pulseaudio/pulseaudio_5.0/0045-fix-warning-in-gconf-helper.patch new file mode 100644 index 0000000..b89ec5a --- /dev/null +++ b/meta-tizen/recipes-multimedia/pulseaudio/pulseaudio_5.0/0045-fix-warning-in-gconf-helper.patch @@ -0,0 +1,24 @@ +From: Jaska Uimonen +Date: Tue, 11 Mar 2014 12:47:52 +0200 +Subject: fix warning in gconf helper + +Change-Id: Id75cd24cfd1c0d62d4c227b6715dc0d9d5ea6b1f +Signed-off-by: Jaska Uimonen +--- + src/modules/gconf/gconf-helper.c | 2 ++ + 1 file changed, 2 insertions(+) + +diff --git a/src/modules/gconf/gconf-helper.c b/src/modules/gconf/gconf-helper.c +index fbd8cfd..4681748 100644 +--- a/src/modules/gconf/gconf-helper.c ++++ b/src/modules/gconf/gconf-helper.c +@@ -99,7 +99,9 @@ int main(int argc, char *argv[]) { + GConfClient *client; + GSList *modules, *m; + ++#if !defined(GLIB_VERSION_2_36) + g_type_init(); ++#endif + + if (!(client = gconf_client_get_default())) + goto fail; diff --git a/meta-tizen/recipes-multimedia/pulseaudio/pulseaudio_5.0/0046-volume-ramp-add-client-api-support-for-volume-rampin.patch b/meta-tizen/recipes-multimedia/pulseaudio/pulseaudio_5.0/0046-volume-ramp-add-client-api-support-for-volume-rampin.patch new file mode 100644 index 0000000..9cc07b6 --- /dev/null +++ b/meta-tizen/recipes-multimedia/pulseaudio/pulseaudio_5.0/0046-volume-ramp-add-client-api-support-for-volume-rampin.patch @@ -0,0 +1,461 @@ +From: Jaska Uimonen +Date: Wed, 8 Aug 2012 11:14:40 +0300 +Subject: volume ramp: add client api support for volume ramping + +Change-Id: I6afc7540af68400db54eec258bdf4a80c311bb69 +Signed-off-by: Jaska Uimonen +--- + src/map-file | 3 ++ + src/pulse/introspect.c | 74 +++++++++++++++++++++++++++++++++++++ + src/pulse/introspect.h | 9 +++++ + src/pulse/stream.c | 6 ++- + src/pulsecore/native-common.h | 3 ++ + src/pulsecore/pdispatch.c | 2 + + src/pulsecore/protocol-native.c | 81 ++++++++++++++++++++++++++++++++++++++++- + src/pulsecore/tagstruct.c | 74 +++++++++++++++++++++++++++++++++++++ + src/pulsecore/tagstruct.h | 3 ++ + 9 files changed, 252 insertions(+), 3 deletions(-) + +diff --git a/src/map-file b/src/map-file +index d51596c..20e577a 100644 +--- a/src/map-file ++++ b/src/map-file +@@ -94,12 +94,15 @@ pa_context_set_event_callback; + pa_context_set_name; + pa_context_set_sink_input_mute; + pa_context_set_sink_input_volume; ++pa_context_set_sink_input_volume_ramp; + pa_context_set_sink_mute_by_index; + pa_context_set_sink_mute_by_name; + pa_context_set_sink_port_by_index; + pa_context_set_sink_port_by_name; + pa_context_set_sink_volume_by_index; + pa_context_set_sink_volume_by_name; ++pa_context_set_sink_volume_ramp_by_index; ++pa_context_set_sink_volume_ramp_by_name; + pa_context_set_source_output_mute; + pa_context_set_source_output_volume; + pa_context_set_source_mute_by_index; +diff --git a/src/pulse/introspect.c b/src/pulse/introspect.c +index 2d54fdb..a72020a 100644 +--- a/src/pulse/introspect.c ++++ b/src/pulse/introspect.c +@@ -1446,6 +1446,56 @@ pa_operation* pa_context_set_sink_volume_by_name(pa_context *c, const char *name + return o; + } + ++pa_operation* pa_context_set_sink_volume_ramp_by_index(pa_context *c, uint32_t idx, const pa_cvolume_ramp *ramp, pa_context_success_cb_t cb, void *userdata) { ++ pa_operation *o; ++ pa_tagstruct *t; ++ uint32_t tag; ++ ++ pa_assert(c); ++ pa_assert(PA_REFCNT_VALUE(c) >= 1); ++ pa_assert(ramp); ++ ++ PA_CHECK_VALIDITY_RETURN_NULL(c, !pa_detect_fork(), PA_ERR_FORKED); ++ PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE); ++ ++ o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata); ++ ++ t = pa_tagstruct_command(c, PA_COMMAND_SET_SINK_VOLUME_RAMP, &tag); ++ pa_tagstruct_putu32(t, idx); ++ pa_tagstruct_puts(t, NULL); ++ pa_tagstruct_put_cvolume_ramp(t, ramp); ++ pa_pstream_send_tagstruct(c->pstream, t); ++ pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, pa_context_simple_ack_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref); ++ ++ return o; ++} ++ ++pa_operation* pa_context_set_sink_volume_ramp_by_name(pa_context *c, const char *name, const pa_cvolume_ramp *ramp, pa_context_success_cb_t cb, void *userdata) { ++ pa_operation *o; ++ pa_tagstruct *t; ++ uint32_t tag; ++ ++ pa_assert(c); ++ pa_assert(PA_REFCNT_VALUE(c) >= 1); ++ pa_assert(name); ++ pa_assert(ramp); ++ ++ PA_CHECK_VALIDITY_RETURN_NULL(c, !pa_detect_fork(), PA_ERR_FORKED); ++ PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE); ++ PA_CHECK_VALIDITY_RETURN_NULL(c, !name || *name, PA_ERR_INVALID); ++ ++ o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata); ++ ++ t = pa_tagstruct_command(c, PA_COMMAND_SET_SINK_VOLUME_RAMP, &tag); ++ pa_tagstruct_putu32(t, PA_INVALID_INDEX); ++ pa_tagstruct_puts(t, name); ++ pa_tagstruct_put_cvolume_ramp(t, ramp); ++ pa_pstream_send_tagstruct(c->pstream, t); ++ pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, pa_context_simple_ack_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref); ++ ++ return o; ++} ++ + pa_operation* pa_context_set_sink_mute_by_index(pa_context *c, uint32_t idx, int mute, pa_context_success_cb_t cb, void *userdata) { + pa_operation *o; + pa_tagstruct *t; +@@ -1543,6 +1593,30 @@ pa_operation* pa_context_set_sink_input_mute(pa_context *c, uint32_t idx, int mu + return o; + } + ++pa_operation* pa_context_set_sink_input_volume_ramp(pa_context *c, uint32_t idx, const pa_cvolume_ramp *ramp, pa_context_success_cb_t cb, void *userdata) { ++ pa_operation *o; ++ pa_tagstruct *t; ++ uint32_t tag; ++ ++ pa_assert(c); ++ pa_assert(PA_REFCNT_VALUE(c) >= 1); ++ pa_assert(ramp); ++ ++ PA_CHECK_VALIDITY_RETURN_NULL(c, !pa_detect_fork(), PA_ERR_FORKED); ++ PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE); ++ ++ o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata); ++ ++ t = pa_tagstruct_command(c, PA_COMMAND_SET_SINK_INPUT_VOLUME_RAMP, &tag); ++ pa_tagstruct_putu32(t, idx); ++ pa_tagstruct_puts(t, NULL); ++ pa_tagstruct_put_cvolume_ramp(t, ramp); ++ pa_pstream_send_tagstruct(c->pstream, t); ++ pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, pa_context_simple_ack_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref); ++ ++ return o; ++} ++ + pa_operation* pa_context_set_source_volume_by_index(pa_context *c, uint32_t idx, const pa_cvolume *volume, pa_context_success_cb_t cb, void *userdata) { + pa_operation *o; + pa_tagstruct *t; +diff --git a/src/pulse/introspect.h b/src/pulse/introspect.h +index 22fefed..127cade 100644 +--- a/src/pulse/introspect.h ++++ b/src/pulse/introspect.h +@@ -287,6 +287,12 @@ pa_operation* pa_context_set_sink_mute_by_index(pa_context *c, uint32_t idx, int + /** Set the mute switch of a sink device specified by its name */ + pa_operation* pa_context_set_sink_mute_by_name(pa_context *c, const char *name, int mute, pa_context_success_cb_t cb, void *userdata); + ++/** Set the volume ramp of a sink device specified by its index */ ++pa_operation* pa_context_set_sink_volume_ramp_by_index(pa_context *c, uint32_t idx, const pa_cvolume_ramp *ramp, pa_context_success_cb_t cb, void *userdata); ++ ++/** Set the volume ramp of a sink device specified by its name */ ++pa_operation* pa_context_set_sink_volume_ramp_by_name(pa_context *c, const char *name, const pa_cvolume_ramp *ramp, pa_context_success_cb_t cb, void *userdata); ++ + /** Suspend/Resume a sink. \since 0.9.7 */ + pa_operation* pa_context_suspend_sink_by_name(pa_context *c, const char *sink_name, int suspend, pa_context_success_cb_t cb, void* userdata); + +@@ -604,6 +610,9 @@ pa_operation* pa_context_set_sink_input_mute(pa_context *c, uint32_t idx, int mu + /** Kill a sink input. */ + pa_operation* pa_context_kill_sink_input(pa_context *c, uint32_t idx, pa_context_success_cb_t cb, void *userdata); + ++/** Set the volume ramp of a sink input specified by its index */ ++pa_operation* pa_context_set_sink_input_volume_ramp(pa_context *c, uint32_t idx, const pa_cvolume_ramp *ramp, pa_context_success_cb_t cb, void *userdata); ++ + /** @} */ + + /** @{ \name Source Outputs */ +diff --git a/src/pulse/stream.c b/src/pulse/stream.c +index 8e35c29..501b5b6 100644 +--- a/src/pulse/stream.c ++++ b/src/pulse/stream.c +@@ -1213,7 +1213,8 @@ static int create_stream( + PA_STREAM_START_UNMUTED| + PA_STREAM_FAIL_ON_SUSPEND| + PA_STREAM_RELATIVE_VOLUME| +- PA_STREAM_PASSTHROUGH)), PA_ERR_INVALID); ++ PA_STREAM_PASSTHROUGH| ++ PA_STREAM_START_RAMP_MUTED)), PA_ERR_INVALID); + + PA_CHECK_VALIDITY(s->context, s->context->version >= 12 || !(flags & PA_STREAM_VARIABLE_RATE), PA_ERR_NOTSUPPORTED); + PA_CHECK_VALIDITY(s->context, s->context->version >= 13 || !(flags & PA_STREAM_PEAK_DETECT), PA_ERR_NOTSUPPORTED); +@@ -1372,6 +1373,9 @@ static int create_stream( + pa_tagstruct_put_boolean(t, flags & (PA_STREAM_PASSTHROUGH)); + } + ++ if (s->context->version >= 22 && s->direction == PA_STREAM_PLAYBACK) ++ pa_tagstruct_put_boolean(t, flags & (PA_STREAM_START_RAMP_MUTED)); ++ + pa_pstream_send_tagstruct(s->context->pstream, t); + pa_pdispatch_register_reply(s->context->pdispatch, tag, DEFAULT_TIMEOUT, pa_create_stream_callback, s, NULL); + +diff --git a/src/pulsecore/native-common.h b/src/pulsecore/native-common.h +index dad82e0..301e2e7 100644 +--- a/src/pulsecore/native-common.h ++++ b/src/pulsecore/native-common.h +@@ -176,6 +176,9 @@ enum { + /* Supported since protocol v27 (3.0) */ + PA_COMMAND_SET_PORT_LATENCY_OFFSET, + ++ PA_COMMAND_SET_SINK_VOLUME_RAMP, ++ PA_COMMAND_SET_SINK_INPUT_VOLUME_RAMP, ++ + PA_COMMAND_MAX + }; + +diff --git a/src/pulsecore/pdispatch.c b/src/pulsecore/pdispatch.c +index 4033240..8643378 100644 +--- a/src/pulsecore/pdispatch.c ++++ b/src/pulsecore/pdispatch.c +@@ -190,6 +190,8 @@ static const char *command_names[PA_COMMAND_MAX] = { + [PA_COMMAND_SET_SOURCE_OUTPUT_VOLUME] = "SET_SOURCE_OUTPUT_VOLUME", + [PA_COMMAND_SET_SOURCE_OUTPUT_MUTE] = "SET_SOURCE_OUTPUT_MUTE", + ++ [PA_COMMAND_SET_SINK_VOLUME_RAMP] = "SET_SINK_VOLUME_RAMP", ++ [PA_COMMAND_SET_SINK_INPUT_VOLUME_RAMP] = "SET_SINK_INPUT_VOLUME_RAMP", + }; + + #endif +diff --git a/src/pulsecore/protocol-native.c b/src/pulsecore/protocol-native.c +index 41b4b50..4304cd4 100644 +--- a/src/pulsecore/protocol-native.c ++++ b/src/pulsecore/protocol-native.c +@@ -294,6 +294,7 @@ static void command_extension(pa_pdispatch *pd, uint32_t command, uint32_t tag, + static void command_set_card_profile(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata); + static void command_set_sink_or_source_port(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata); + static void command_set_port_latency_offset(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata); ++static void command_set_volume_ramp(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata); + + static const pa_pdispatch_cb_t command_table[PA_COMMAND_MAX] = { + [PA_COMMAND_ERROR] = NULL, +@@ -397,6 +398,9 @@ static const pa_pdispatch_cb_t command_table[PA_COMMAND_MAX] = { + + [PA_COMMAND_SET_PORT_LATENCY_OFFSET] = command_set_port_latency_offset, + ++ [PA_COMMAND_SET_SINK_VOLUME_RAMP] = command_set_volume_ramp, ++ [PA_COMMAND_SET_SINK_INPUT_VOLUME_RAMP] = command_set_volume_ramp, ++ + [PA_COMMAND_EXTENSION] = command_extension + }; + +@@ -1994,7 +1998,8 @@ static void command_create_playback_stream(pa_pdispatch *pd, uint32_t command, u + muted_set = false, + fail_on_suspend = false, + relative_volume = false, +- passthrough = false; ++ passthrough = false, ++ ramp_muted = false; + + pa_sink_input_flags_t flags = 0; + pa_proplist *p = NULL; +@@ -2122,6 +2127,7 @@ static void command_create_playback_stream(pa_pdispatch *pd, uint32_t command, u + } + pa_idxset_put(formats, format, NULL); + } ++ + } + + if (n_formats == 0) { +@@ -2134,6 +2140,11 @@ static void command_create_playback_stream(pa_pdispatch *pd, uint32_t command, u + } + } + ++ if (pa_tagstruct_get_boolean(t, &ramp_muted) < 0 ) { ++ protocol_error(c); ++ goto finish; ++ } ++ + if (!pa_tagstruct_eof(t)) { + protocol_error(c); + goto finish; +@@ -2165,7 +2176,8 @@ static void command_create_playback_stream(pa_pdispatch *pd, uint32_t command, u + (variable_rate ? PA_SINK_INPUT_VARIABLE_RATE : 0) | + (dont_inhibit_auto_suspend ? PA_SINK_INPUT_DONT_INHIBIT_AUTO_SUSPEND : 0) | + (fail_on_suspend ? PA_SINK_INPUT_NO_CREATE_ON_SUSPEND|PA_SINK_INPUT_KILL_ON_SUSPEND : 0) | +- (passthrough ? PA_SINK_INPUT_PASSTHROUGH : 0); ++ (passthrough ? PA_SINK_INPUT_PASSTHROUGH : 0) | ++ (ramp_muted ? PA_SINK_INPUT_START_RAMP_MUTED : 0); + + /* Only since protocol version 15 there's a separate muted_set + * flag. For older versions we synthesize it here */ +@@ -3797,6 +3809,71 @@ static void command_set_volume( + pa_pstream_send_simple_ack(c->pstream, tag); + } + ++static void command_set_volume_ramp( ++ pa_pdispatch *pd, ++ uint32_t command, ++ uint32_t tag, ++ pa_tagstruct *t, ++ void *userdata) { ++ ++ pa_native_connection *c = PA_NATIVE_CONNECTION(userdata); ++ uint32_t idx; ++ pa_cvolume_ramp ramp; ++ pa_sink *sink = NULL; ++ pa_sink_input *si = NULL; ++ const char *name = NULL; ++ const char *client_name; ++ ++ pa_native_connection_assert_ref(c); ++ pa_assert(t); ++ ++ if (pa_tagstruct_getu32(t, &idx) < 0 || ++ (command == PA_COMMAND_SET_SINK_VOLUME_RAMP && pa_tagstruct_gets(t, &name) < 0) || ++ (command == PA_COMMAND_SET_SINK_INPUT_VOLUME_RAMP && pa_tagstruct_gets(t, &name) < 0) || ++ pa_tagstruct_get_cvolume_ramp(t, &ramp) || ++ !pa_tagstruct_eof(t)) { ++ protocol_error(c); ++ return; ++ } ++ ++ CHECK_VALIDITY(c->pstream, c->authorized, tag, PA_ERR_ACCESS); ++ CHECK_VALIDITY(c->pstream, !name || pa_namereg_is_valid_name_or_wildcard(name, command == PA_COMMAND_SET_SINK_VOLUME ? PA_NAMEREG_SINK : PA_NAMEREG_SOURCE), tag, PA_ERR_INVALID); ++ CHECK_VALIDITY(c->pstream, idx != PA_INVALID_INDEX || name, tag, PA_ERR_INVALID); ++ CHECK_VALIDITY(c->pstream, idx == PA_INVALID_INDEX || !name, tag, PA_ERR_INVALID); ++ CHECK_VALIDITY(c->pstream, !name || idx == PA_INVALID_INDEX, tag, PA_ERR_INVALID); ++ ++ switch (command) { ++ ++ case PA_COMMAND_SET_SINK_VOLUME_RAMP: ++ if (idx != PA_INVALID_INDEX) ++ sink = pa_idxset_get_by_index(c->protocol->core->sinks, idx); ++ else ++ sink = pa_namereg_get(c->protocol->core, name, PA_NAMEREG_SINK); ++ break; ++ ++ case PA_COMMAND_SET_SINK_INPUT_VOLUME_RAMP: ++ si = pa_idxset_get_by_index(c->protocol->core->sink_inputs, idx); ++ break; ++ ++ default: ++ pa_assert_not_reached(); ++ } ++ ++ CHECK_VALIDITY(c->pstream, sink || si, tag, PA_ERR_NOENTITY); ++ ++ client_name = pa_strnull(pa_proplist_gets(c->client->proplist, PA_PROP_APPLICATION_PROCESS_BINARY)); ++ ++ if (sink) { ++ pa_log_debug("Client %s changes volume ramp of sink %s.", client_name, sink->name); ++ pa_sink_set_volume_ramp(sink, &ramp, TRUE, TRUE); ++ } else if (si) { ++ pa_log_debug("Client %s changes volume ramp of sink input %s.", client_name, pa_strnull(pa_proplist_gets(si->proplist, PA_PROP_MEDIA_NAME))); ++ pa_sink_input_set_volume_ramp(si, &ramp, TRUE, TRUE); ++ } ++ ++ pa_pstream_send_simple_ack(c->pstream, tag); ++} ++ + static void command_set_mute( + pa_pdispatch *pd, + uint32_t command, +diff --git a/src/pulsecore/tagstruct.c b/src/pulsecore/tagstruct.c +index e51fcf2..aba0178 100644 +--- a/src/pulsecore/tagstruct.c ++++ b/src/pulsecore/tagstruct.c +@@ -256,6 +256,35 @@ void pa_tagstruct_put_cvolume(pa_tagstruct *t, const pa_cvolume *cvolume) { + } + } + ++void pa_tagstruct_put_cvolume_ramp(pa_tagstruct *t, const pa_cvolume_ramp *ramp) { ++ unsigned i; ++ pa_volume_ramp_type_t type; ++ long length; ++ pa_volume_t target; ++ ++ pa_assert(t); ++ pa_assert(ramp); ++ extend(t, 2 + ramp->channels * (sizeof(pa_volume_ramp_type_t) + sizeof(long) + sizeof(pa_volume_t))); ++ ++ t->data[t->length++] = PA_TAG_CVOLUME_RAMP; ++ t->data[t->length++] = ramp->channels; ++ ++ for (i = 0; i < ramp->channels; i++) { ++ type = htonl(ramp->ramps[i].type); ++ target = htonl(ramp->ramps[i].target); ++ length = htonl(ramp->ramps[i].length); ++ ++ memcpy(t->data + t->length, &type, sizeof(pa_volume_ramp_type_t)); ++ t->length += sizeof(pa_volume_ramp_type_t); ++ ++ memcpy(t->data + t->length, &length, sizeof(long)); ++ t->length += sizeof(long); ++ ++ memcpy(t->data + t->length, &target, sizeof(pa_volume_t)); ++ t->length += sizeof(pa_volume_t); ++ } ++} ++ + void pa_tagstruct_put_volume(pa_tagstruct *t, pa_volume_t vol) { + uint32_t u; + pa_assert(t); +@@ -579,6 +608,51 @@ int pa_tagstruct_get_cvolume(pa_tagstruct *t, pa_cvolume *cvolume) { + return 0; + } + ++int pa_tagstruct_get_cvolume_ramp(pa_tagstruct *t, pa_cvolume_ramp *ramp) { ++ unsigned i; ++ pa_volume_ramp_type_t type; ++ long length; ++ pa_volume_t target; ++ uint8_t *read_ptr; ++ ++ pa_assert(t); ++ pa_assert(ramp); ++ ++ if (t->rindex+2 > t->length) ++ return -1; ++ ++ if (t->data[t->rindex] != PA_TAG_CVOLUME_RAMP) ++ return -1; ++ ++ if ((ramp->channels = t->data[t->rindex+1]) > PA_CHANNELS_MAX) ++ return -1; ++ ++ if (t->rindex+2+ramp->channels*(sizeof(pa_volume_ramp_type_t)+sizeof(long)+sizeof(pa_volume_t)) > t->length) ++ return -1; ++ ++ read_ptr = t->data + t->rindex + 2; ++ ++ for (i = 0; i < ramp->channels; i++) { ++ memcpy(&type, read_ptr, sizeof(pa_volume_ramp_type_t)); ++ ramp->ramps[i].type = (pa_volume_ramp_type_t) ntohl(type); ++ read_ptr += sizeof(pa_volume_ramp_type_t); ++ ++ ++ memcpy(&length, read_ptr, sizeof(long)); ++ ramp->ramps[i].length = (long) ntohl(length); ++ read_ptr += sizeof(long); ++ ++ ++ memcpy(&target, read_ptr, sizeof(pa_volume_t)); ++ ramp->ramps[i].target = (pa_volume_t) ntohl(target); ++ read_ptr += sizeof(pa_volume_t); ++ } ++ ++ t->rindex = read_ptr - t->data; ++ ++ return 0; ++} ++ + int pa_tagstruct_get_volume(pa_tagstruct*t, pa_volume_t *vol) { + uint32_t u; + +diff --git a/src/pulsecore/tagstruct.h b/src/pulsecore/tagstruct.h +index 9fef255..4634bd6 100644 +--- a/src/pulsecore/tagstruct.h ++++ b/src/pulsecore/tagstruct.h +@@ -60,6 +60,7 @@ enum { + PA_TAG_PROPLIST = 'P', + PA_TAG_VOLUME = 'V', + PA_TAG_FORMAT_INFO = 'f', ++ PA_TAG_CVOLUME_RAMP = 'J' + }; + + pa_tagstruct *pa_tagstruct_new(const uint8_t* data, size_t length); +@@ -86,6 +87,7 @@ void pa_tagstruct_put_cvolume(pa_tagstruct *t, const pa_cvolume *cvolume); + void pa_tagstruct_put_proplist(pa_tagstruct *t, pa_proplist *p); + void pa_tagstruct_put_volume(pa_tagstruct *t, pa_volume_t volume); + void pa_tagstruct_put_format_info(pa_tagstruct *t, pa_format_info *f); ++void pa_tagstruct_put_cvolume_ramp(pa_tagstruct *t, const pa_cvolume_ramp *ramp); + + int pa_tagstruct_get(pa_tagstruct *t, ...); + +@@ -104,5 +106,6 @@ int pa_tagstruct_get_cvolume(pa_tagstruct *t, pa_cvolume *v); + int pa_tagstruct_get_proplist(pa_tagstruct *t, pa_proplist *p); + int pa_tagstruct_get_volume(pa_tagstruct *t, pa_volume_t *v); + int pa_tagstruct_get_format_info(pa_tagstruct *t, pa_format_info *f); ++int pa_tagstruct_get_cvolume_ramp(pa_tagstruct *t, pa_cvolume_ramp *ramp); + + #endif diff --git a/meta-tizen/recipes-multimedia/pulseaudio/pulseaudio_5.0/0047-adjust-default-bluetooth-profile-to-off.patch b/meta-tizen/recipes-multimedia/pulseaudio/pulseaudio_5.0/0047-adjust-default-bluetooth-profile-to-off.patch new file mode 100644 index 0000000..4f20de2 --- /dev/null +++ b/meta-tizen/recipes-multimedia/pulseaudio/pulseaudio_5.0/0047-adjust-default-bluetooth-profile-to-off.patch @@ -0,0 +1,23 @@ +From: Jaska Uimonen +Date: Wed, 14 Aug 2013 15:00:52 +0300 +Subject: adjust default bluetooth profile to off + +Change-Id: I95ca525b0d20c1a864a0c66060767bb4c2160400 +Signed-off-by: Jaska Uimonen +--- + src/modules/bluetooth/module-bluez4-device.c | 2 ++ + 1 file changed, 2 insertions(+) + +diff --git a/src/modules/bluetooth/module-bluez4-device.c b/src/modules/bluetooth/module-bluez4-device.c +index eba92c6..6b775b7 100644 +--- a/src/modules/bluetooth/module-bluez4-device.c ++++ b/src/modules/bluetooth/module-bluez4-device.c +@@ -2527,6 +2527,8 @@ static int add_card(struct userdata *u) { + else + pa_log_warn("Profile '%s' not valid or not supported by device.", default_profile); + } ++ else ++ pa_card_new_data_set_profile(&data, p->name); + + u->card = pa_card_new(u->core, &data); + pa_card_new_data_done(&data); diff --git a/meta-tizen/recipes-multimedia/pulseaudio/pulseaudio_5.0/0048-Add-bt_profile_set-patch-which-fixed-bt-a2dp-hsp-pro.patch b/meta-tizen/recipes-multimedia/pulseaudio/pulseaudio_5.0/0048-Add-bt_profile_set-patch-which-fixed-bt-a2dp-hsp-pro.patch new file mode 100644 index 0000000..83227f7 --- /dev/null +++ b/meta-tizen/recipes-multimedia/pulseaudio/pulseaudio_5.0/0048-Add-bt_profile_set-patch-which-fixed-bt-a2dp-hsp-pro.patch @@ -0,0 +1,83 @@ +From: "vivian,zhang" +Date: Wed, 17 Jul 2013 11:17:40 +0800 +Subject: Add bt_profile_set patch which fixed bt a2dp&hsp profile setting + issues in mobile + +Change-Id: I9bc3649b02ab7ac56584211789a3ea18ff17fbb7 +Signed-off-by: Jaska Uimonen +--- + configure.ac | 13 +++++++++++++ + src/Makefile.am | 3 +++ + src/modules/bluetooth/module-bluez4-discover.c | 18 ++++++++++++++++++ + 3 files changed, 34 insertions(+) + +diff --git a/configure.ac b/configure.ac +index 870375f..6a6bc83 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -679,6 +679,19 @@ fi + AM_CONDITIONAL(USE_DLOG, test "x$USE_DLOG" = "xyes") + dnl end -------------------------------------------------------------------- + ++dnl use bt-profile-set -------------------------------------------------------------------------- ++AC_ARG_ENABLE(bt-profile-set, AC_HELP_STRING([--enable-bt-profile-set], [enable bt profile param]), ++[ ++ case "${enableval}" in ++ yes) USE_BT_PROFILE_SET=yes ;; ++ no) USE_BT_PROFILE_SET=no ;; ++ *) AC_MSG_ERROR(bad value ${enableval} for --enable-bt-profile-set) ;; ++ esac ++ ],[USE_BT_PROFILE_SET=no]) ++ ++AM_CONDITIONAL(USE_BT_PROFILE_SET, test "x$USE_BT_PROFILE_SET" = "xyes") ++dnl end -------------------------------------------------------------------- ++ + #### atomic-ops #### + + AC_MSG_CHECKING([whether we need libatomic_ops]) +diff --git a/src/Makefile.am b/src/Makefile.am +index a60b5bf..b09903f 100644 +--- a/src/Makefile.am ++++ b/src/Makefile.am +@@ -2082,6 +2082,9 @@ module_bluetooth_discover_la_CFLAGS = $(AM_CFLAGS) + if HAVE_BT_A2DP_APTX + module_bluetooth_discover_la_CFLAGS += -DBLUETOOTH_APTX_SUPPORT + endif ++if USE_BT_PROFILE_SET ++module_bluetooth_discover_la_CFLAGS += -DBLUETOOTH_PROFILE_SET ++endif + + # Bluetooth BlueZ 4 sink / source + module_bluez4_discover_la_SOURCES = modules/bluetooth/module-bluez4-discover.c +diff --git a/src/modules/bluetooth/module-bluez4-discover.c b/src/modules/bluetooth/module-bluez4-discover.c +index 7673ba7..aaaa095 100644 +--- a/src/modules/bluetooth/module-bluez4-discover.c ++++ b/src/modules/bluetooth/module-bluez4-discover.c +@@ -79,9 +79,27 @@ static pa_hook_result_t load_module_for_device(pa_bluez4_discovery *y, const pa_ + pa_module *m = NULL; + char *args; + ++#ifdef BLUETOOTH_PROFILE_SET ++ const char *profile = NULL; ++ ++ if ((d->transports[PROFILE_A2DP] && d->transports[PROFILE_A2DP]->state != PA_BLUETOOTH_TRANSPORT_STATE_DISCONNECTED)) { ++ profile = "a2dp"; ++ } if ((d->transports[PROFILE_A2DP_SOURCE] && d->transports[PROFILE_A2DP_SOURCE]->state != PA_BLUETOOTH_TRANSPORT_STATE_DISCONNECTED)) { ++ profile = "a2dp_source"; ++ } else if ((d->transports[PROFILE_HFGW] && d->transports[PROFILE_HFGW]->state != PA_BLUETOOTH_TRANSPORT_STATE_DISCONNECTED)) { ++ profile = "hfgw"; ++ } ++ if (!profile) ++ return PA_HOOK_OK; ++#endif ++ + /* Oh, awesome, a new device has shown up and been connected! */ + ++#ifdef BLUETOOTH_PROFILE_SET ++ args = pa_sprintf_malloc("address=\"%s\" path=\"%s\" profile=\"%s\"", d->address, d->path, profile); ++#else + args = pa_sprintf_malloc("address=\"%s\" path=\"%s\"", d->address, d->path); ++#endif + + if (pa_modargs_get_value(u->modargs, "sco_sink", NULL) && + pa_modargs_get_value(u->modargs, "sco_source", NULL)) { diff --git a/meta-tizen/recipes-multimedia/pulseaudio/pulseaudio_5.0/0049-added-pulseaudio.service-file.patch b/meta-tizen/recipes-multimedia/pulseaudio/pulseaudio_5.0/0049-added-pulseaudio.service-file.patch new file mode 100644 index 0000000..c4c125c --- /dev/null +++ b/meta-tizen/recipes-multimedia/pulseaudio/pulseaudio_5.0/0049-added-pulseaudio.service-file.patch @@ -0,0 +1,27 @@ +From: Jaska Uimonen +Date: Tue, 11 Jun 2013 17:03:27 +0300 +Subject: added pulseaudio.service file + +Change-Id: I8efef41060189f116be49b3455c588d67f045f82 +Signed-off-by: Jaska Uimonen +--- + pulseaudio.service | 10 ++++++++++ + 1 file changed, 10 insertions(+) + create mode 100644 pulseaudio.service + +diff --git a/pulseaudio.service b/pulseaudio.service +new file mode 100644 +index 0000000..f9d8592 +--- /dev/null ++++ b/pulseaudio.service +@@ -0,0 +1,10 @@ ++[Unit] ++Description=pulseaudio service ++After=syslog.target dbus.service ++ ++[Service] ++Type=simple ++ExecStart=/usr/bin/pulseaudio --system ++ ++[Install] ++WantedBy=multi-user.target diff --git a/meta-tizen/recipes-multimedia/pulseaudio/pulseaudio_5.0/0050-.gitignore-Add-pulsecore-config.h.patch b/meta-tizen/recipes-multimedia/pulseaudio/pulseaudio_5.0/0050-.gitignore-Add-pulsecore-config.h.patch new file mode 100644 index 0000000..c5c1d3b --- /dev/null +++ b/meta-tizen/recipes-multimedia/pulseaudio/pulseaudio_5.0/0050-.gitignore-Add-pulsecore-config.h.patch @@ -0,0 +1,19 @@ +From: Tanu Kaskinen +Date: Mon, 24 Feb 2014 12:38:31 +0200 +Subject: .gitignore: Add pulsecore-config.h + +Change-Id: I8409f1964e65e79669eaeb4930c6d536417a0b05 +Signed-off-by: Jaska Uimonen +--- + .gitignore | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/.gitignore b/.gitignore +index f41ee72..b4a465e 100644 +--- a/.gitignore ++++ b/.gitignore +@@ -34,3 +34,4 @@ missing + mkinstalldirs + stamp-* + .dirstamp ++pulsecore-config.h diff --git a/meta-tizen/recipes-multimedia/pulseaudio/pulseaudio_5.0/0051-pactl-Fix-crash-with-older-servers.patch b/meta-tizen/recipes-multimedia/pulseaudio/pulseaudio_5.0/0051-pactl-Fix-crash-with-older-servers.patch new file mode 100644 index 0000000..cd4736c --- /dev/null +++ b/meta-tizen/recipes-multimedia/pulseaudio/pulseaudio_5.0/0051-pactl-Fix-crash-with-older-servers.patch @@ -0,0 +1,405 @@ +From: Tanu Kaskinen +Date: Sun, 16 Feb 2014 14:30:38 +0200 +Subject: pactl: Fix crash with older servers + +Servers older than 0.9.15 don't know anything about cards, and card +operations will return a NULL pa_operation object when connected to +that old server. We must check the pa_operation pointer before passing +it to pa_operation_unref(), otherwise a NULL operation will result in +a crash. + +Change-Id: Idc258479c077aafaff6254b76acb18034a158107 +Signed-off-by: Jaska Uimonen +--- + src/utils/pactl.c | 203 +++++++++++++++++++++++++++++++++++------------------- + 1 file changed, 133 insertions(+), 70 deletions(-) + +diff --git a/src/utils/pactl.c b/src/utils/pactl.c +index 1d8faa4..958d700 100644 +--- a/src/utils/pactl.c ++++ b/src/utils/pactl.c +@@ -94,7 +94,7 @@ static pa_stream *sample_stream = NULL; + static pa_sample_spec sample_spec; + static pa_channel_map channel_map; + static size_t sample_length = 0; +-static int actions = 1; ++static int actions = 0; + + static bool nl = false; + +@@ -1046,6 +1046,7 @@ static void set_sink_formats(pa_context *c, uint32_t sink, const char *str) { + char *format = NULL; + const char *state = NULL; + int i = 0; ++ pa_operation *o = NULL; + + while ((format = pa_split(str, ";", &state))) { + pa_format_info *f = pa_format_info_from_string(pa_strip(format)); +@@ -1059,7 +1060,11 @@ static void set_sink_formats(pa_context *c, uint32_t sink, const char *str) { + pa_xfree(format); + } + +- pa_operation_unref(pa_ext_device_restore_save_formats(c, PA_DEVICE_TYPE_SINK, sink, i, f_arr, simple_callback, NULL)); ++ o = pa_ext_device_restore_save_formats(c, PA_DEVICE_TYPE_SINK, sink, i, f_arr, simple_callback, NULL); ++ if (o) { ++ pa_operation_unref(o); ++ actions++; ++ } + + done: + if (format) +@@ -1185,7 +1190,10 @@ static void context_subscribe_callback(pa_context *c, pa_subscription_event_type + } + + static void context_state_callback(pa_context *c, void *userdata) { ++ pa_operation *o = NULL; ++ + pa_assert(c); ++ + switch (pa_context_get_state(c)) { + case PA_CONTEXT_CONNECTING: + case PA_CONTEXT_AUTHORIZING: +@@ -1195,21 +1203,26 @@ static void context_state_callback(pa_context *c, void *userdata) { + case PA_CONTEXT_READY: + switch (action) { + case STAT: +- pa_operation_unref(pa_context_stat(c, stat_callback, NULL)); ++ o = pa_context_stat(c, stat_callback, NULL); + if (short_list_format) + break; +- actions++; ++ ++ if (o) { ++ pa_operation_unref(o); ++ actions++; ++ } ++ /* Fall through */ + + case INFO: +- pa_operation_unref(pa_context_get_server_info(c, get_server_info_callback, NULL)); ++ o = pa_context_get_server_info(c, get_server_info_callback, NULL); + break; + + case PLAY_SAMPLE: +- pa_operation_unref(pa_context_play_sample(c, sample_name, sink_name, PA_VOLUME_NORM, simple_callback, NULL)); ++ o = pa_context_play_sample(c, sample_name, sink_name, PA_VOLUME_NORM, simple_callback, NULL); + break; + + case REMOVE_SAMPLE: +- pa_operation_unref(pa_context_remove_sample(c, sample_name, simple_callback, NULL)); ++ o = pa_context_remove_sample(c, sample_name, simple_callback, NULL); + break; + + case UPLOAD_SAMPLE: +@@ -1219,165 +1232,205 @@ static void context_state_callback(pa_context *c, void *userdata) { + pa_stream_set_state_callback(sample_stream, stream_state_callback, NULL); + pa_stream_set_write_callback(sample_stream, stream_write_callback, NULL); + pa_stream_connect_upload(sample_stream, sample_length); ++ actions++; + break; + + case EXIT: +- pa_operation_unref(pa_context_exit_daemon(c, simple_callback, NULL)); ++ o = pa_context_exit_daemon(c, simple_callback, NULL); + break; + + case LIST: + if (list_type) { + if (pa_streq(list_type, "modules")) +- pa_operation_unref(pa_context_get_module_info_list(c, get_module_info_callback, NULL)); ++ o = pa_context_get_module_info_list(c, get_module_info_callback, NULL); + else if (pa_streq(list_type, "sinks")) +- pa_operation_unref(pa_context_get_sink_info_list(c, get_sink_info_callback, NULL)); ++ o = pa_context_get_sink_info_list(c, get_sink_info_callback, NULL); + else if (pa_streq(list_type, "sources")) +- pa_operation_unref(pa_context_get_source_info_list(c, get_source_info_callback, NULL)); ++ o = pa_context_get_source_info_list(c, get_source_info_callback, NULL); + else if (pa_streq(list_type, "sink-inputs")) +- pa_operation_unref(pa_context_get_sink_input_info_list(c, get_sink_input_info_callback, NULL)); ++ o = pa_context_get_sink_input_info_list(c, get_sink_input_info_callback, NULL); + else if (pa_streq(list_type, "source-outputs")) +- pa_operation_unref(pa_context_get_source_output_info_list(c, get_source_output_info_callback, NULL)); ++ o = pa_context_get_source_output_info_list(c, get_source_output_info_callback, NULL); + else if (pa_streq(list_type, "clients")) +- pa_operation_unref(pa_context_get_client_info_list(c, get_client_info_callback, NULL)); ++ o = pa_context_get_client_info_list(c, get_client_info_callback, NULL); + else if (pa_streq(list_type, "samples")) +- pa_operation_unref(pa_context_get_sample_info_list(c, get_sample_info_callback, NULL)); ++ o = pa_context_get_sample_info_list(c, get_sample_info_callback, NULL); + else if (pa_streq(list_type, "cards")) +- pa_operation_unref(pa_context_get_card_info_list(c, get_card_info_callback, NULL)); ++ o = pa_context_get_card_info_list(c, get_card_info_callback, NULL); + else if (pa_streq(list_type, "nodes")) +- pa_operation_unref(pa_ext_node_manager_read_nodes(c, node_list_callback, NULL)); ++ o = pa_ext_node_manager_read_nodes(c, node_list_callback, NULL); + else + pa_assert_not_reached(); + } else { +- actions = 8; +- pa_operation_unref(pa_context_get_module_info_list(c, get_module_info_callback, NULL)); +- pa_operation_unref(pa_context_get_sink_info_list(c, get_sink_info_callback, NULL)); +- pa_operation_unref(pa_context_get_source_info_list(c, get_source_info_callback, NULL)); +- pa_operation_unref(pa_context_get_sink_input_info_list(c, get_sink_input_info_callback, NULL)); +- pa_operation_unref(pa_context_get_source_output_info_list(c, get_source_output_info_callback, NULL)); +- pa_operation_unref(pa_context_get_client_info_list(c, get_client_info_callback, NULL)); +- pa_operation_unref(pa_context_get_sample_info_list(c, get_sample_info_callback, NULL)); +- pa_operation_unref(pa_context_get_card_info_list(c, get_card_info_callback, NULL)); ++ o = pa_context_get_module_info_list(c, get_module_info_callback, NULL); ++ if (o) { ++ pa_operation_unref(o); ++ actions++; ++ } ++ ++ o = pa_context_get_sink_info_list(c, get_sink_info_callback, NULL); ++ if (o) { ++ pa_operation_unref(o); ++ actions++; ++ } ++ ++ o = pa_context_get_source_info_list(c, get_source_info_callback, NULL); ++ if (o) { ++ pa_operation_unref(o); ++ actions++; ++ } ++ o = pa_context_get_sink_input_info_list(c, get_sink_input_info_callback, NULL); ++ if (o) { ++ pa_operation_unref(o); ++ actions++; ++ } ++ ++ o = pa_context_get_source_output_info_list(c, get_source_output_info_callback, NULL); ++ if (o) { ++ pa_operation_unref(o); ++ actions++; ++ } ++ ++ o = pa_context_get_client_info_list(c, get_client_info_callback, NULL); ++ if (o) { ++ pa_operation_unref(o); ++ actions++; ++ } ++ ++ o = pa_context_get_sample_info_list(c, get_sample_info_callback, NULL); ++ if (o) { ++ pa_operation_unref(o); ++ actions++; ++ } ++ ++ o = pa_context_get_card_info_list(c, get_card_info_callback, NULL); ++ if (o) { ++ pa_operation_unref(o); ++ actions++; ++ } ++ ++ o = NULL; + } + break; + + case MOVE_SINK_INPUT: +- pa_operation_unref(pa_context_move_sink_input_by_name(c, sink_input_idx, sink_name, simple_callback, NULL)); ++ o = pa_context_move_sink_input_by_name(c, sink_input_idx, sink_name, simple_callback, NULL); + break; + + case MOVE_SOURCE_OUTPUT: +- pa_operation_unref(pa_context_move_source_output_by_name(c, source_output_idx, source_name, simple_callback, NULL)); ++ o = pa_context_move_source_output_by_name(c, source_output_idx, source_name, simple_callback, NULL); + break; + + case LOAD_MODULE: +- pa_operation_unref(pa_context_load_module(c, module_name, module_args, index_callback, NULL)); ++ o = pa_context_load_module(c, module_name, module_args, index_callback, NULL); + break; + + case UNLOAD_MODULE: + if (module_name) +- pa_operation_unref(pa_context_get_module_info_list(c, unload_module_by_name_callback, NULL)); ++ o = pa_context_get_module_info_list(c, unload_module_by_name_callback, NULL); + else +- pa_operation_unref(pa_context_unload_module(c, module_index, simple_callback, NULL)); ++ o = pa_context_unload_module(c, module_index, simple_callback, NULL); + break; + + case SUSPEND_SINK: + if (sink_name) +- pa_operation_unref(pa_context_suspend_sink_by_name(c, sink_name, suspend, simple_callback, NULL)); ++ o = pa_context_suspend_sink_by_name(c, sink_name, suspend, simple_callback, NULL); + else +- pa_operation_unref(pa_context_suspend_sink_by_index(c, PA_INVALID_INDEX, suspend, simple_callback, NULL)); ++ o = pa_context_suspend_sink_by_index(c, PA_INVALID_INDEX, suspend, simple_callback, NULL); + break; + + case SUSPEND_SOURCE: + if (source_name) +- pa_operation_unref(pa_context_suspend_source_by_name(c, source_name, suspend, simple_callback, NULL)); ++ o = pa_context_suspend_source_by_name(c, source_name, suspend, simple_callback, NULL); + else +- pa_operation_unref(pa_context_suspend_source_by_index(c, PA_INVALID_INDEX, suspend, simple_callback, NULL)); ++ o = pa_context_suspend_source_by_index(c, PA_INVALID_INDEX, suspend, simple_callback, NULL); + break; + + case SET_CARD_PROFILE: +- pa_operation_unref(pa_context_set_card_profile_by_name(c, card_name, profile_name, simple_callback, NULL)); ++ o = pa_context_set_card_profile_by_name(c, card_name, profile_name, simple_callback, NULL); + break; + + case SET_SINK_PORT: +- pa_operation_unref(pa_context_set_sink_port_by_name(c, sink_name, port_name, simple_callback, NULL)); ++ o = pa_context_set_sink_port_by_name(c, sink_name, port_name, simple_callback, NULL); + break; + + case SET_DEFAULT_SINK: +- pa_operation_unref(pa_context_set_default_sink(c, sink_name, simple_callback, NULL)); ++ o = pa_context_set_default_sink(c, sink_name, simple_callback, NULL); + break; + + case SET_SOURCE_PORT: +- pa_operation_unref(pa_context_set_source_port_by_name(c, source_name, port_name, simple_callback, NULL)); ++ o = pa_context_set_source_port_by_name(c, source_name, port_name, simple_callback, NULL); + break; + + case SET_DEFAULT_SOURCE: +- pa_operation_unref(pa_context_set_default_source(c, source_name, simple_callback, NULL)); ++ o = pa_context_set_default_source(c, source_name, simple_callback, NULL); + break; + + case SET_SINK_MUTE: + if (mute == TOGGLE_MUTE) +- pa_operation_unref(pa_context_get_sink_info_by_name(c, sink_name, sink_toggle_mute_callback, NULL)); ++ o = pa_context_get_sink_info_by_name(c, sink_name, sink_toggle_mute_callback, NULL); + else +- pa_operation_unref(pa_context_set_sink_mute_by_name(c, sink_name, mute, simple_callback, NULL)); ++ o = pa_context_set_sink_mute_by_name(c, sink_name, mute, simple_callback, NULL); + break; + + case SET_SOURCE_MUTE: + if (mute == TOGGLE_MUTE) +- pa_operation_unref(pa_context_get_source_info_by_name(c, source_name, source_toggle_mute_callback, NULL)); ++ o = pa_context_get_source_info_by_name(c, source_name, source_toggle_mute_callback, NULL); + else +- pa_operation_unref(pa_context_set_source_mute_by_name(c, source_name, mute, simple_callback, NULL)); ++ o = pa_context_set_source_mute_by_name(c, source_name, mute, simple_callback, NULL); + break; + + case SET_SINK_INPUT_MUTE: + if (mute == TOGGLE_MUTE) +- pa_operation_unref(pa_context_get_sink_input_info(c, sink_input_idx, sink_input_toggle_mute_callback, NULL)); ++ o = pa_context_get_sink_input_info(c, sink_input_idx, sink_input_toggle_mute_callback, NULL); + else +- pa_operation_unref(pa_context_set_sink_input_mute(c, sink_input_idx, mute, simple_callback, NULL)); ++ o = pa_context_set_sink_input_mute(c, sink_input_idx, mute, simple_callback, NULL); + break; + + case SET_SOURCE_OUTPUT_MUTE: + if (mute == TOGGLE_MUTE) +- pa_operation_unref(pa_context_get_source_output_info(c, source_output_idx, source_output_toggle_mute_callback, NULL)); ++ o = pa_context_get_source_output_info(c, source_output_idx, source_output_toggle_mute_callback, NULL); + else +- pa_operation_unref(pa_context_set_source_output_mute(c, source_output_idx, mute, simple_callback, NULL)); ++ o = pa_context_set_source_output_mute(c, source_output_idx, mute, simple_callback, NULL); + break; + + case SET_SINK_VOLUME: + if ((volume_flags & VOL_RELATIVE) == VOL_RELATIVE) { +- pa_operation_unref(pa_context_get_sink_info_by_name(c, sink_name, get_sink_volume_callback, NULL)); ++ o = pa_context_get_sink_info_by_name(c, sink_name, get_sink_volume_callback, NULL); + } else { + pa_cvolume v; + pa_cvolume_set(&v, 1, volume); +- pa_operation_unref(pa_context_set_sink_volume_by_name(c, sink_name, &v, simple_callback, NULL)); ++ o = pa_context_set_sink_volume_by_name(c, sink_name, &v, simple_callback, NULL); + } + break; + + case SET_SOURCE_VOLUME: + if ((volume_flags & VOL_RELATIVE) == VOL_RELATIVE) { +- pa_operation_unref(pa_context_get_source_info_by_name(c, source_name, get_source_volume_callback, NULL)); ++ o = pa_context_get_source_info_by_name(c, source_name, get_source_volume_callback, NULL); + } else { + pa_cvolume v; + pa_cvolume_set(&v, 1, volume); +- pa_operation_unref(pa_context_set_source_volume_by_name(c, source_name, &v, simple_callback, NULL)); ++ o = pa_context_set_source_volume_by_name(c, source_name, &v, simple_callback, NULL); + } + break; + + case SET_SINK_INPUT_VOLUME: + if ((volume_flags & VOL_RELATIVE) == VOL_RELATIVE) { +- pa_operation_unref(pa_context_get_sink_input_info(c, sink_input_idx, get_sink_input_volume_callback, NULL)); ++ o = pa_context_get_sink_input_info(c, sink_input_idx, get_sink_input_volume_callback, NULL); + } else { + pa_cvolume v; + pa_cvolume_set(&v, 1, volume); +- pa_operation_unref(pa_context_set_sink_input_volume(c, sink_input_idx, &v, simple_callback, NULL)); ++ o = pa_context_set_sink_input_volume(c, sink_input_idx, &v, simple_callback, NULL); + } + break; + + case SET_SOURCE_OUTPUT_VOLUME: + if ((volume_flags & VOL_RELATIVE) == VOL_RELATIVE) { +- pa_operation_unref(pa_context_get_source_output_info(c, source_output_idx, get_source_output_volume_callback, NULL)); ++ o = pa_context_get_source_output_info(c, source_output_idx, get_source_output_volume_callback, NULL); + } else { + pa_cvolume v; + pa_cvolume_set(&v, 1, volume); +- pa_operation_unref(pa_context_set_source_output_volume(c, source_output_idx, &v, simple_callback, NULL)); ++ o = pa_context_set_source_output_volume(c, source_output_idx, &v, simple_callback, NULL); + } + break; + +@@ -1386,25 +1439,24 @@ static void context_state_callback(pa_context *c, void *userdata) { + break; + + case SET_PORT_LATENCY_OFFSET: +- pa_operation_unref(pa_context_set_port_latency_offset(c, card_name, port_name, latency_offset, simple_callback, NULL)); ++ o = pa_context_set_port_latency_offset(c, card_name, port_name, latency_offset, simple_callback, NULL); + break; + + case SUBSCRIBE: + pa_context_set_subscribe_callback(c, context_subscribe_callback, NULL); + +- pa_operation_unref(pa_context_subscribe( +- c, +- PA_SUBSCRIPTION_MASK_SINK| +- PA_SUBSCRIPTION_MASK_SOURCE| +- PA_SUBSCRIPTION_MASK_SINK_INPUT| +- PA_SUBSCRIPTION_MASK_SOURCE_OUTPUT| +- PA_SUBSCRIPTION_MASK_MODULE| +- PA_SUBSCRIPTION_MASK_CLIENT| +- PA_SUBSCRIPTION_MASK_SAMPLE_CACHE| +- PA_SUBSCRIPTION_MASK_SERVER| +- PA_SUBSCRIPTION_MASK_CARD, +- NULL, +- NULL)); ++ o = pa_context_subscribe(c, ++ PA_SUBSCRIPTION_MASK_SINK| ++ PA_SUBSCRIPTION_MASK_SOURCE| ++ PA_SUBSCRIPTION_MASK_SINK_INPUT| ++ PA_SUBSCRIPTION_MASK_SOURCE_OUTPUT| ++ PA_SUBSCRIPTION_MASK_MODULE| ++ PA_SUBSCRIPTION_MASK_CLIENT| ++ PA_SUBSCRIPTION_MASK_SAMPLE_CACHE| ++ PA_SUBSCRIPTION_MASK_SERVER| ++ PA_SUBSCRIPTION_MASK_CARD, ++ NULL, ++ NULL); + break; + case NODE_CONNECT: + pa_operation_unref(pa_ext_node_manager_connect_nodes(c, +@@ -1422,6 +1474,17 @@ static void context_state_callback(pa_context *c, void *userdata) { + default: + pa_assert_not_reached(); + } ++ ++ if (o) { ++ pa_operation_unref(o); ++ actions++; ++ } ++ ++ if (actions == 0) { ++ pa_log("Operation failed: %s", pa_strerror(pa_context_errno(c))); ++ quit(1); ++ } ++ + break; + + case PA_CONTEXT_TERMINATED: diff --git a/meta-tizen/recipes-multimedia/pulseaudio/pulseaudio_5.0/0052-volume-Increase-PA_SW_VOLUME_SNPRINT_DB_MAX.patch b/meta-tizen/recipes-multimedia/pulseaudio/pulseaudio_5.0/0052-volume-Increase-PA_SW_VOLUME_SNPRINT_DB_MAX.patch new file mode 100644 index 0000000..32330d6 --- /dev/null +++ b/meta-tizen/recipes-multimedia/pulseaudio/pulseaudio_5.0/0052-volume-Increase-PA_SW_VOLUME_SNPRINT_DB_MAX.patch @@ -0,0 +1,26 @@ +From: Tanu Kaskinen +Date: Sun, 9 Mar 2014 10:50:23 +0200 +Subject: volume: Increase PA_SW_VOLUME_SNPRINT_DB_MAX + +10 bytes isn't enough for "-123.45 dB", including the terminating null +byte. + +Change-Id: I865060befd01d3dde69556c1f90b7de55350501a +Signed-off-by: Jaska Uimonen +--- + src/pulse/volume.h | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/src/pulse/volume.h b/src/pulse/volume.h +index 3ffb573..cfdc85c 100644 +--- a/src/pulse/volume.h ++++ b/src/pulse/volume.h +@@ -216,7 +216,7 @@ char *pa_volume_snprint(char *s, size_t l, pa_volume_t v); + * any release without warning and without being considered API or ABI + * breakage. You should not use this definition anywhere where it + * might become part of an ABI. \since 0.9.15 */ +-#define PA_SW_VOLUME_SNPRINT_DB_MAX 10 ++#define PA_SW_VOLUME_SNPRINT_DB_MAX 11 + + /** Pretty print a volume but show dB values. \since 0.9.15 */ + char *pa_sw_volume_snprint_dB(char *s, size_t l, pa_volume_t v); diff --git a/meta-tizen/recipes-multimedia/pulseaudio/pulseaudio_5.0/0053-sink-input-source-output-Fix-mute-saving.patch b/meta-tizen/recipes-multimedia/pulseaudio/pulseaudio_5.0/0053-sink-input-source-output-Fix-mute-saving.patch new file mode 100644 index 0000000..fc389f6 --- /dev/null +++ b/meta-tizen/recipes-multimedia/pulseaudio/pulseaudio_5.0/0053-sink-input-source-output-Fix-mute-saving.patch @@ -0,0 +1,37 @@ +From: Tanu Kaskinen +Date: Mon, 14 Apr 2014 14:29:48 +0300 +Subject: sink-input, source-output: Fix mute saving + +Change-Id: I2298ab51a384c3ddfa33da3e941e03f5027b4d77 +Signed-off-by: Jaska Uimonen +--- + src/pulsecore/sink-input.c | 2 +- + src/pulsecore/source-output.c | 2 +- + 2 files changed, 2 insertions(+), 2 deletions(-) + +diff --git a/src/pulsecore/sink-input.c b/src/pulsecore/sink-input.c +index dff6324..d0509b3 100644 +--- a/src/pulsecore/sink-input.c ++++ b/src/pulsecore/sink-input.c +@@ -1470,7 +1470,7 @@ void pa_sink_input_set_mute(pa_sink_input *i, bool mute, bool save) { + pa_assert(PA_SINK_INPUT_IS_LINKED(i->state)); + + if (!i->muted == !mute) { +- i->save_muted = i->save_muted || mute; ++ i->save_muted |= save; + return; + } + +diff --git a/src/pulsecore/source-output.c b/src/pulsecore/source-output.c +index 4e4b7e9..b12758a 100644 +--- a/src/pulsecore/source-output.c ++++ b/src/pulsecore/source-output.c +@@ -1062,7 +1062,7 @@ void pa_source_output_set_mute(pa_source_output *o, bool mute, bool save) { + pa_assert(PA_SOURCE_OUTPUT_IS_LINKED(o->state)); + + if (!o->muted == !mute) { +- o->save_muted = o->save_muted || mute; ++ o->save_muted |= save; + return; + } + diff --git a/meta-tizen/recipes-multimedia/pulseaudio/pulseaudio_5.0/0054-direction-Add-a-couple-of-direction-helper-functions.patch b/meta-tizen/recipes-multimedia/pulseaudio/pulseaudio_5.0/0054-direction-Add-a-couple-of-direction-helper-functions.patch new file mode 100644 index 0000000..3282596 --- /dev/null +++ b/meta-tizen/recipes-multimedia/pulseaudio/pulseaudio_5.0/0054-direction-Add-a-couple-of-direction-helper-functions.patch @@ -0,0 +1,180 @@ +From: Tanu Kaskinen +Date: Wed, 26 Mar 2014 10:25:17 +0200 +Subject: direction: Add a couple of direction helper functions + +Change-Id: I365acd7ce3e7abcbcb8a532c79016fca558238e8 +Signed-off-by: Jaska Uimonen +--- + doxygen/doxygen.conf.in | 1 + + src/Makefile.am | 2 ++ + src/map-file | 2 ++ + src/pulse/direction.c | 46 ++++++++++++++++++++++++++++++++++++++++++++++ + src/pulse/direction.h | 37 +++++++++++++++++++++++++++++++++++++ + src/pulse/pulseaudio.h | 5 +++-- + 6 files changed, 91 insertions(+), 2 deletions(-) + create mode 100644 src/pulse/direction.c + create mode 100644 src/pulse/direction.h + +diff --git a/doxygen/doxygen.conf.in b/doxygen/doxygen.conf.in +index a078e27..40cea8b 100644 +--- a/doxygen/doxygen.conf.in ++++ b/doxygen/doxygen.conf.in +@@ -421,6 +421,7 @@ INPUT = \ + @srcdir@/../src/pulse/channelmap.h \ + @srcdir@/../src/pulse/context.h \ + @srcdir@/../src/pulse/def.h \ ++ @srcdir@/../src/pulse/direction.h \ + @srcdir@/../src/pulse/error.h \ + @srcdir@/../src/pulse/ext-stream-restore.h \ + @srcdir@/../src/pulse/ext-device-manager.h \ +diff --git a/src/Makefile.am b/src/Makefile.am +index b09903f..fe6cc53 100644 +--- a/src/Makefile.am ++++ b/src/Makefile.am +@@ -758,6 +758,7 @@ pulseinclude_HEADERS = \ + pulse/channelmap.h \ + pulse/context.h \ + pulse/def.h \ ++ pulse/direction.h \ + pulse/error.h \ + pulse/ext-device-manager.h \ + pulse/ext-device-restore.h \ +@@ -810,6 +811,7 @@ libpulse_la_SOURCES = \ + pulse/channelmap.c pulse/channelmap.h \ + pulse/context.c pulse/context.h \ + pulse/def.h \ ++ pulse/direction.c pulse/direction.h \ + pulse/error.c pulse/error.h \ + pulse/ext-device-manager.c pulse/ext-device-manager.h \ + pulse/ext-device-restore.c pulse/ext-device-restore.h \ +diff --git a/src/map-file b/src/map-file +index 20e577a..fbf3f22 100644 +--- a/src/map-file ++++ b/src/map-file +@@ -153,6 +153,8 @@ pa_cvolume_set_position; + pa_cvolume_snprint; + pa_cvolume_snprint_verbose; + pa_cvolume_valid; ++pa_direction_to_string; ++pa_direction_valid; + pa_encoding_to_string; + pa_ext_device_manager_delete; + pa_ext_device_manager_enable_role_device_priority_routing; +diff --git a/src/pulse/direction.c b/src/pulse/direction.c +new file mode 100644 +index 0000000..95f5e00 +--- /dev/null ++++ b/src/pulse/direction.c +@@ -0,0 +1,46 @@ ++/*** ++ This file is part of PulseAudio. ++ ++ Copyright 2014 Intel Corporation ++ ++ PulseAudio is free software; you can redistribute it and/or modify ++ it under the terms of the GNU Lesser General Public License as published ++ by the Free Software Foundation; either version 2.1 of the License, ++ or (at your option) any later version. ++ ++ PulseAudio is distributed in the hope that it will be useful, but ++ WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ General Public License for more details. ++ ++ You should have received a copy of the GNU Lesser General Public License ++ along with PulseAudio; if not, write to the Free Software ++ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 ++ USA. ++***/ ++ ++#include "direction.h" ++ ++#include ++ ++int pa_direction_valid(pa_direction_t direction) { ++ if (direction != PA_DIRECTION_INPUT ++ && direction != PA_DIRECTION_OUTPUT ++ && direction != (PA_DIRECTION_INPUT | PA_DIRECTION_OUTPUT)) ++ return 0; ++ ++ return 1; ++} ++ ++const char *pa_direction_to_string(pa_direction_t direction) { ++ pa_init_i18n(); ++ ++ if (direction == PA_DIRECTION_INPUT) ++ return _("input"); ++ if (direction == PA_DIRECTION_OUTPUT) ++ return _("output"); ++ if (direction == (PA_DIRECTION_INPUT | PA_DIRECTION_OUTPUT)) ++ return _("bidirectional"); ++ ++ return _("invalid"); ++} +diff --git a/src/pulse/direction.h b/src/pulse/direction.h +new file mode 100644 +index 0000000..127f07a +--- /dev/null ++++ b/src/pulse/direction.h +@@ -0,0 +1,37 @@ ++#ifndef foodirectionhfoo ++#define foodirectionhfoo ++ ++/*** ++ This file is part of PulseAudio. ++ ++ Copyright 2014 Intel Corporation ++ ++ PulseAudio is free software; you can redistribute it and/or modify ++ it under the terms of the GNU Lesser General Public License as published ++ by the Free Software Foundation; either version 2.1 of the License, ++ or (at your option) any later version. ++ ++ PulseAudio is distributed in the hope that it will be useful, but ++ WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ General Public License for more details. ++ ++ You should have received a copy of the GNU Lesser General Public License ++ along with PulseAudio; if not, write to the Free Software ++ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 ++ USA. ++***/ ++ ++#include ++ ++/** \file ++ * Utility functions for \ref pa_direction_t. */ ++ ++/** Return non-zero if the given value is a valid direction (either input, ++ * output or bidirectional). \since 6.0 */ ++int pa_direction_valid(pa_direction_t direction) PA_GCC_CONST; ++ ++/** Return a textual representation of the direction. \since 6.0 */ ++const char *pa_direction_to_string(pa_direction_t direction); ++ ++#endif +diff --git a/src/pulse/pulseaudio.h b/src/pulse/pulseaudio.h +index 21b7213..2e270dd 100644 +--- a/src/pulse/pulseaudio.h ++++ b/src/pulse/pulseaudio.h +@@ -23,6 +23,7 @@ + USA. + ***/ + ++#include + #include + #include + #include +@@ -49,8 +50,8 @@ + + /** \file + * Include all libpulse header files at once. The following files are +- * included: \ref mainloop-api.h, \ref sample.h, \ref def.h, \ref +- * context.h, \ref stream.h, \ref introspect.h, \ref subscribe.h, \ref ++ * included: \ref direction.h, \ref mainloop-api.h, \ref sample.h, \ref def.h, ++ * \ref context.h, \ref stream.h, \ref introspect.h, \ref subscribe.h, \ref + * scache.h, \ref version.h, \ref error.h, \ref channelmap.h, \ref + * operation.h,\ref volume.h, \ref xmalloc.h, \ref utf8.h, \ref + * thread-mainloop.h, \ref mainloop.h, \ref util.h, \ref proplist.h, diff --git a/meta-tizen/recipes-multimedia/pulseaudio/pulseaudio_5.0/0055-core-util-Add-pa_join.patch b/meta-tizen/recipes-multimedia/pulseaudio/pulseaudio_5.0/0055-core-util-Add-pa_join.patch new file mode 100644 index 0000000..d510f44 --- /dev/null +++ b/meta-tizen/recipes-multimedia/pulseaudio/pulseaudio_5.0/0055-core-util-Add-pa_join.patch @@ -0,0 +1,52 @@ +From: Tanu Kaskinen +Date: Wed, 26 Mar 2014 13:15:12 +0200 +Subject: core-util: Add pa_join() + +Change-Id: I84ac0ee7a3097fce8ed9bad26b210fc97db9e9a7 +Signed-off-by: Jaska Uimonen +--- + src/pulsecore/core-util.c | 18 ++++++++++++++++++ + src/pulsecore/core-util.h | 1 + + 2 files changed, 19 insertions(+) + +diff --git a/src/pulsecore/core-util.c b/src/pulsecore/core-util.c +index 1256a1e..508b3d3 100644 +--- a/src/pulsecore/core-util.c ++++ b/src/pulsecore/core-util.c +@@ -1120,6 +1120,24 @@ char *pa_split_spaces(const char *c, const char **state) { + return pa_xstrndup(current, l); + } + ++char *pa_join(const char * const *strings, unsigned n_strings, const char *delimiter) { ++ pa_strbuf *buf; ++ unsigned i; ++ ++ pa_assert(strings || n_strings == 0); ++ ++ buf = pa_strbuf_new(); ++ ++ for (i = 0; i < n_strings; i++) { ++ if (i > 0 && delimiter) ++ pa_strbuf_puts(buf, delimiter); ++ ++ pa_strbuf_puts(buf, strings[i]); ++ } ++ ++ return pa_strbuf_tostring_free(buf); ++} ++ + PA_STATIC_TLS_DECLARE(signame, pa_xfree); + + /* Return the name of an UNIX signal. Similar to Solaris sig2str() */ +diff --git a/src/pulsecore/core-util.h b/src/pulsecore/core-util.h +index e6cb261..aba1863 100644 +--- a/src/pulsecore/core-util.h ++++ b/src/pulsecore/core-util.h +@@ -108,6 +108,7 @@ static inline const char *pa_strna(const char *x) { + char *pa_split(const char *c, const char*delimiters, const char **state); + const char *pa_split_in_place(const char *c, const char*delimiters, int *n, const char **state); + char *pa_split_spaces(const char *c, const char **state); ++char *pa_join(const char * const *strings, unsigned n_strings, const char *delimiter); + + char *pa_strip_nl(char *s); + char *pa_strip(char *s); diff --git a/meta-tizen/recipes-multimedia/pulseaudio/pulseaudio_5.0/0056-dynarray-Add-pa_dynarray_get_raw_array.patch b/meta-tizen/recipes-multimedia/pulseaudio/pulseaudio_5.0/0056-dynarray-Add-pa_dynarray_get_raw_array.patch new file mode 100644 index 0000000..cf2c694 --- /dev/null +++ b/meta-tizen/recipes-multimedia/pulseaudio/pulseaudio_5.0/0056-dynarray-Add-pa_dynarray_get_raw_array.patch @@ -0,0 +1,36 @@ +From: Tanu Kaskinen +Date: Wed, 26 Mar 2014 13:33:31 +0200 +Subject: dynarray: Add pa_dynarray_get_raw_array() + +Change-Id: I6e40c2a20586d13c99c9d98059e4dbb1d0e9e562 +Signed-off-by: Jaska Uimonen +--- + src/pulsecore/dynarray.c | 6 ++++++ + src/pulsecore/dynarray.h | 1 + + 2 files changed, 7 insertions(+) + +diff --git a/src/pulsecore/dynarray.c b/src/pulsecore/dynarray.c +index b207eca..b65fb62 100644 +--- a/src/pulsecore/dynarray.c ++++ b/src/pulsecore/dynarray.c +@@ -93,3 +93,9 @@ unsigned pa_dynarray_size(pa_dynarray *array) { + + return array->n_entries; + } ++ ++void * const *pa_dynarray_get_raw_array(pa_dynarray *array) { ++ pa_assert(array); ++ ++ return array->data; ++} +diff --git a/src/pulsecore/dynarray.h b/src/pulsecore/dynarray.h +index 04dd2d2..078acec 100644 +--- a/src/pulsecore/dynarray.h ++++ b/src/pulsecore/dynarray.h +@@ -54,5 +54,6 @@ void *pa_dynarray_get(pa_dynarray *array, unsigned i); + void *pa_dynarray_steal_last(pa_dynarray *array); + + unsigned pa_dynarray_size(pa_dynarray *array); ++void * const *pa_dynarray_get_raw_array(pa_dynarray *array); + + #endif diff --git a/meta-tizen/recipes-multimedia/pulseaudio/pulseaudio_5.0/0057-dynarray-Add-PA_DYNARRAY_FOREACH.patch b/meta-tizen/recipes-multimedia/pulseaudio/pulseaudio_5.0/0057-dynarray-Add-PA_DYNARRAY_FOREACH.patch new file mode 100644 index 0000000..64d8ce5 --- /dev/null +++ b/meta-tizen/recipes-multimedia/pulseaudio/pulseaudio_5.0/0057-dynarray-Add-PA_DYNARRAY_FOREACH.patch @@ -0,0 +1,64 @@ +From: Tanu Kaskinen +Date: Thu, 19 Dec 2013 21:29:50 +0200 +Subject: dynarray: Add PA_DYNARRAY_FOREACH + +The PA_DYNARRAY_FOREACH macro requires that pa_dynarray_get() returns +NULL if the index is out of bounds. + +Change-Id: If9db312516fbb079e8b67d94d35a44783ab3395a +Signed-off-by: Jaska Uimonen +--- + src/pulsecore/dynarray.c | 4 +++- + src/pulsecore/dynarray.h | 5 +++++ + src/pulsecore/tokenizer.c | 3 --- + 3 files changed, 8 insertions(+), 4 deletions(-) + +diff --git a/src/pulsecore/dynarray.c b/src/pulsecore/dynarray.c +index b65fb62..8dd8fab 100644 +--- a/src/pulsecore/dynarray.c ++++ b/src/pulsecore/dynarray.c +@@ -74,7 +74,9 @@ void pa_dynarray_append(pa_dynarray *array, void *p) { + + void *pa_dynarray_get(pa_dynarray *array, unsigned i) { + pa_assert(array); +- pa_assert(i < array->n_entries); ++ ++ if (i >= array->n_entries) ++ return NULL; + + return array->data[i]; + } +diff --git a/src/pulsecore/dynarray.h b/src/pulsecore/dynarray.h +index 078acec..65030f2 100644 +--- a/src/pulsecore/dynarray.h ++++ b/src/pulsecore/dynarray.h +@@ -48,6 +48,8 @@ pa_dynarray* pa_dynarray_new(pa_free_cb_t free_cb); + void pa_dynarray_free(pa_dynarray *array); + + void pa_dynarray_append(pa_dynarray *array, void *p); ++ ++/* Returns NULL if i is out of bounds. */ + void *pa_dynarray_get(pa_dynarray *array, unsigned i); + + /* Returns the removed item, or NULL if the array is empty. */ +@@ -56,4 +58,7 @@ void *pa_dynarray_steal_last(pa_dynarray *array); + unsigned pa_dynarray_size(pa_dynarray *array); + void * const *pa_dynarray_get_raw_array(pa_dynarray *array); + ++#define PA_DYNARRAY_FOREACH(elem, array, idx) \ ++ for ((idx) = 0; ((elem) = pa_dynarray_get(array, idx)); (idx)++) ++ + #endif +diff --git a/src/pulsecore/tokenizer.c b/src/pulsecore/tokenizer.c +index 4c610e8..d71a7da 100644 +--- a/src/pulsecore/tokenizer.c ++++ b/src/pulsecore/tokenizer.c +@@ -80,8 +80,5 @@ const char *pa_tokenizer_get(pa_tokenizer *t, unsigned i) { + + pa_assert(a); + +- if (i >= pa_dynarray_size(a)) +- return NULL; +- + return pa_dynarray_get(a, i); + } diff --git a/meta-tizen/recipes-multimedia/pulseaudio/pulseaudio_5.0/0058-dynarray-Add-pa_dynarray_remove_last.patch b/meta-tizen/recipes-multimedia/pulseaudio/pulseaudio_5.0/0058-dynarray-Add-pa_dynarray_remove_last.patch new file mode 100644 index 0000000..a6e0f56 --- /dev/null +++ b/meta-tizen/recipes-multimedia/pulseaudio/pulseaudio_5.0/0058-dynarray-Add-pa_dynarray_remove_last.patch @@ -0,0 +1,51 @@ +From: Tanu Kaskinen +Date: Wed, 21 May 2014 11:32:09 +0300 +Subject: dynarray: Add pa_dynarray_remove_last() + +Change-Id: I9098df96aac57a3ee2084061aa174f7ee02b1588 +Signed-off-by: Jaska Uimonen +--- + src/pulsecore/dynarray.c | 15 +++++++++++++++ + src/pulsecore/dynarray.h | 3 +++ + 2 files changed, 18 insertions(+) + +diff --git a/src/pulsecore/dynarray.c b/src/pulsecore/dynarray.c +index 8dd8fab..c0b5fb0 100644 +--- a/src/pulsecore/dynarray.c ++++ b/src/pulsecore/dynarray.c +@@ -81,6 +81,21 @@ void *pa_dynarray_get(pa_dynarray *array, unsigned i) { + return array->data[i]; + } + ++int pa_dynarray_remove_last(pa_dynarray *array) { ++ void *entry; ++ ++ pa_assert(array); ++ ++ entry = pa_dynarray_steal_last(array); ++ if (!entry) ++ return -PA_ERR_NOENTITY; ++ ++ if (array->free_cb) ++ array->free_cb(entry); ++ ++ return 0; ++} ++ + void *pa_dynarray_steal_last(pa_dynarray *array) { + pa_assert(array); + +diff --git a/src/pulsecore/dynarray.h b/src/pulsecore/dynarray.h +index 65030f2..fd91433 100644 +--- a/src/pulsecore/dynarray.h ++++ b/src/pulsecore/dynarray.h +@@ -52,6 +52,9 @@ void pa_dynarray_append(pa_dynarray *array, void *p); + /* Returns NULL if i is out of bounds. */ + void *pa_dynarray_get(pa_dynarray *array, unsigned i); + ++/* Returns -PA_ERR_NOENTITY if the array is empty, and zero otherwise. */ ++int pa_dynarray_remove_last(pa_dynarray *array); ++ + /* Returns the removed item, or NULL if the array is empty. */ + void *pa_dynarray_steal_last(pa_dynarray *array); + diff --git a/meta-tizen/recipes-multimedia/pulseaudio/pulseaudio_5.0/0059-dynarray-Add-pa_dynarray_remove_all.patch b/meta-tizen/recipes-multimedia/pulseaudio/pulseaudio_5.0/0059-dynarray-Add-pa_dynarray_remove_all.patch new file mode 100644 index 0000000..d2e66b5 --- /dev/null +++ b/meta-tizen/recipes-multimedia/pulseaudio/pulseaudio_5.0/0059-dynarray-Add-pa_dynarray_remove_all.patch @@ -0,0 +1,57 @@ +From: Tanu Kaskinen +Date: Wed, 21 May 2014 11:36:19 +0300 +Subject: dynarray: Add pa_dynarray_remove_all() + +Change-Id: I35079f8fe4b361221a1bdc1fececbe318bf3ee0e +Signed-off-by: Jaska Uimonen +--- + src/pulsecore/dynarray.c | 13 ++++++++----- + src/pulsecore/dynarray.h | 2 ++ + 2 files changed, 10 insertions(+), 5 deletions(-) + +diff --git a/src/pulsecore/dynarray.c b/src/pulsecore/dynarray.c +index c0b5fb0..6dba743 100644 +--- a/src/pulsecore/dynarray.c ++++ b/src/pulsecore/dynarray.c +@@ -47,13 +47,9 @@ pa_dynarray* pa_dynarray_new(pa_free_cb_t free_cb) { + } + + void pa_dynarray_free(pa_dynarray *array) { +- unsigned i; + pa_assert(array); + +- if (array->free_cb) +- for (i = 0; i < array->n_entries; i++) +- array->free_cb(array->data[i]); +- ++ pa_dynarray_remove_all(array); + pa_xfree(array->data); + pa_xfree(array); + } +@@ -105,6 +101,13 @@ void *pa_dynarray_steal_last(pa_dynarray *array) { + return NULL; + } + ++void pa_dynarray_remove_all(pa_dynarray *array) { ++ pa_assert(array); ++ ++ while (array->n_entries > 0) ++ pa_dynarray_remove_last(array); ++} ++ + unsigned pa_dynarray_size(pa_dynarray *array) { + pa_assert(array); + +diff --git a/src/pulsecore/dynarray.h b/src/pulsecore/dynarray.h +index fd91433..439f181 100644 +--- a/src/pulsecore/dynarray.h ++++ b/src/pulsecore/dynarray.h +@@ -58,6 +58,8 @@ int pa_dynarray_remove_last(pa_dynarray *array); + /* Returns the removed item, or NULL if the array is empty. */ + void *pa_dynarray_steal_last(pa_dynarray *array); + ++void pa_dynarray_remove_all(pa_dynarray *array); ++ + unsigned pa_dynarray_size(pa_dynarray *array); + void * const *pa_dynarray_get_raw_array(pa_dynarray *array); + diff --git a/meta-tizen/recipes-multimedia/pulseaudio/pulseaudio_5.0/0060-hashmap-Add-pa_hashmap_remove_and_free.patch b/meta-tizen/recipes-multimedia/pulseaudio/pulseaudio_5.0/0060-hashmap-Add-pa_hashmap_remove_and_free.patch new file mode 100644 index 0000000..09ab108 --- /dev/null +++ b/meta-tizen/recipes-multimedia/pulseaudio/pulseaudio_5.0/0060-hashmap-Add-pa_hashmap_remove_and_free.patch @@ -0,0 +1,53 @@ +From: Tanu Kaskinen +Date: Wed, 26 Mar 2014 13:58:40 +0200 +Subject: hashmap: Add pa_hashmap_remove_and_free() + +Change-Id: Ia3530c29cecf8964989aec4f0527fc982e80e34a +Signed-off-by: Jaska Uimonen +--- + src/pulsecore/hashmap.c | 13 +++++++++++++ + src/pulsecore/hashmap.h | 7 +++++++ + 2 files changed, 20 insertions(+) + +diff --git a/src/pulsecore/hashmap.c b/src/pulsecore/hashmap.c +index acac1e0..2cc03cb 100644 +--- a/src/pulsecore/hashmap.c ++++ b/src/pulsecore/hashmap.c +@@ -207,6 +207,19 @@ void* pa_hashmap_remove(pa_hashmap *h, const void *key) { + return data; + } + ++int pa_hashmap_remove_and_free(pa_hashmap *h, const void *key) { ++ void *data; ++ ++ pa_assert(h); ++ ++ data = pa_hashmap_remove(h, key); ++ ++ if (data && h->value_free_func) ++ h->value_free_func(data); ++ ++ return data ? 0 : -1; ++} ++ + void pa_hashmap_remove_all(pa_hashmap *h) { + pa_assert(h); + +diff --git a/src/pulsecore/hashmap.h b/src/pulsecore/hashmap.h +index e42732a..8042f7b 100644 +--- a/src/pulsecore/hashmap.h ++++ b/src/pulsecore/hashmap.h +@@ -52,6 +52,13 @@ void* pa_hashmap_get(pa_hashmap *h, const void *key); + /* Returns the data of the entry while removing */ + void* pa_hashmap_remove(pa_hashmap *h, const void *key); + ++/* Removes the entry and frees the entry data. Returns a negative value if the ++ * entry is not found. FIXME: This function shouldn't be needed. ++ * pa_hashmap_remove() should free the entry data, and the current semantics of ++ * pa_hashmap_remove() should be implemented by a function called ++ * pa_hashmap_steal(). */ ++int pa_hashmap_remove_and_free(pa_hashmap *h, const void *key); ++ + /* Remove all entries but don't free the hashmap */ + void pa_hashmap_remove_all(pa_hashmap *h); + diff --git a/meta-tizen/recipes-multimedia/pulseaudio/pulseaudio_5.0/0061-device-port-Add-pa_device_port.active.patch b/meta-tizen/recipes-multimedia/pulseaudio/pulseaudio_5.0/0061-device-port-Add-pa_device_port.active.patch new file mode 100644 index 0000000..ef71747 --- /dev/null +++ b/meta-tizen/recipes-multimedia/pulseaudio/pulseaudio_5.0/0061-device-port-Add-pa_device_port.active.patch @@ -0,0 +1,236 @@ +From: Tanu Kaskinen +Date: Wed, 26 Mar 2014 13:41:42 +0200 +Subject: device-port: Add pa_device_port.active + +In the Tizen volume API, I create and delete volume control objects +for ports based on their state (only active ports have volume +controls). Having the pa_device_port.active flag directly accessible +is much nicer than figuring out the state by iterating through sinks +and checking what their active port is. It's also safer to get a +notification for a deactivated port before the port switch is +executed, compared to using the SINK_PORT_CHANGED hook that is fired +only after the port switch is complete. + +Change-Id: I3f7f8855721c8dc3a643708a72f6e35341ff7117 +Signed-off-by: Jaska Uimonen +--- + src/pulsecore/core.h | 1 + + src/pulsecore/device-port.c | 15 +++++++++++++++ + src/pulsecore/device-port.h | 4 ++++ + src/pulsecore/sink.c | 28 ++++++++++++++++++++++++---- + src/pulsecore/source.c | 29 +++++++++++++++++++++++++---- + 5 files changed, 69 insertions(+), 8 deletions(-) + +diff --git a/src/pulsecore/core.h b/src/pulsecore/core.h +index f268e42..e1cd18f 100644 +--- a/src/pulsecore/core.h ++++ b/src/pulsecore/core.h +@@ -119,6 +119,7 @@ typedef enum pa_core_hook { + PA_CORE_HOOK_CARD_PROFILE_ADDED, + PA_CORE_HOOK_CARD_PROFILE_AVAILABLE_CHANGED, + PA_CORE_HOOK_PORT_AVAILABLE_CHANGED, ++ PA_CORE_HOOK_PORT_ACTIVE_CHANGED, + PA_CORE_HOOK_PORT_LATENCY_OFFSET_CHANGED, + PA_CORE_HOOK_MAX + } pa_core_hook_t; +diff --git a/src/pulsecore/device-port.c b/src/pulsecore/device-port.c +index 0b65d5c..c183990 100644 +--- a/src/pulsecore/device-port.c ++++ b/src/pulsecore/device-port.c +@@ -176,3 +176,18 @@ void pa_device_port_set_latency_offset(pa_device_port *p, int64_t offset) { + pa_subscription_post(core, PA_SUBSCRIPTION_EVENT_CARD|PA_SUBSCRIPTION_EVENT_CHANGE, p->card->index); + pa_hook_fire(&core->hooks[PA_CORE_HOOK_PORT_LATENCY_OFFSET_CHANGED], p); + } ++ ++void pa_device_port_active_changed(pa_device_port *port, bool new_active) { ++ bool old_active; ++ ++ pa_assert(port); ++ ++ old_active = port->active; ++ ++ if (new_active == old_active) ++ return; ++ ++ port->active = new_active; ++ pa_log_debug("Port %s %s.", port->name, new_active ? "activated" : "deactivated"); ++ pa_hook_fire(&port->core->hooks[PA_CORE_HOOK_PORT_ACTIVE_CHANGED], port); ++} +diff --git a/src/pulsecore/device-port.h b/src/pulsecore/device-port.h +index b10d554..2964900 100644 +--- a/src/pulsecore/device-port.h ++++ b/src/pulsecore/device-port.h +@@ -48,6 +48,7 @@ struct pa_device_port { + + unsigned priority; + pa_available_t available; /* PA_AVAILABLE_UNKNOWN, PA_AVAILABLE_NO or PA_AVAILABLE_YES */ ++ bool active; + + pa_proplist *proplist; + pa_hashmap *profiles; /* Does not own the profiles */ +@@ -83,4 +84,7 @@ void pa_device_port_set_available(pa_device_port *p, pa_available_t available); + + void pa_device_port_set_latency_offset(pa_device_port *p, int64_t offset); + ++/* Called from sink.c and source.c only. */ ++void pa_device_port_active_changed(pa_device_port *port, bool new_active); ++ + #endif +diff --git a/src/pulsecore/sink.c b/src/pulsecore/sink.c +index 61656ab..11a6e77 100644 +--- a/src/pulsecore/sink.c ++++ b/src/pulsecore/sink.c +@@ -669,6 +669,9 @@ void pa_sink_put(pa_sink* s) { + else + pa_assert_se(sink_set_state(s, PA_SINK_IDLE) == 0); + ++ if (s->active_port) ++ pa_device_port_active_changed(s->active_port, true); ++ + pa_source_put(s->monitor_source); + + pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK | PA_SUBSCRIPTION_EVENT_NEW, s->index); +@@ -696,6 +699,9 @@ void pa_sink_unlink(pa_sink* s) { + if (linked) + pa_hook_fire(&s->core->hooks[PA_CORE_HOOK_SINK_UNLINK], s); + ++ if (s->active_port) ++ pa_device_port_active_changed(s->active_port, false); ++ + if (s->state != PA_SINK_UNLINKED) + pa_namereg_unregister(s->core, s->name); + pa_idxset_remove_by_data(s->core->sinks, s, NULL); +@@ -3410,6 +3416,7 @@ size_t pa_sink_get_max_request(pa_sink *s) { + /* Called from main context */ + int pa_sink_set_port(pa_sink *s, const char *name, bool save) { + pa_device_port *port; ++ pa_device_port *old_port; + int ret; + + pa_sink_assert_ref(s); +@@ -3426,11 +3433,15 @@ int pa_sink_set_port(pa_sink *s, const char *name, bool save) { + if (!(port = pa_hashmap_get(s->ports, name))) + return -PA_ERR_NOENTITY; + +- if (s->active_port == port) { ++ old_port = s->active_port; ++ ++ if (port == old_port) { + s->save_port = s->save_port || save; + return 0; + } + ++ pa_device_port_active_changed(old_port, false); ++ + if (s->flags & PA_SINK_DEFERRED_VOLUME) { + struct sink_message_set_port msg = { .port = port, .ret = 0 }; + pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_SET_PORT, &msg, 0, NULL) == 0); +@@ -3439,17 +3450,26 @@ int pa_sink_set_port(pa_sink *s, const char *name, bool save) { + else + ret = s->set_port(s, port); + +- if (ret < 0) +- return -PA_ERR_NOENTITY; ++ if (ret < 0) { ++ pa_log("Failed to set the port of sink %s from %s to %s.", s->name, old_port->name, port->name); ++ ++ /* We don't know the real state of the device, but let's assume that ++ * the old port is still active, because s->active_port is left to ++ * point to the old port anyway. */ ++ pa_device_port_active_changed(old_port, true); ++ ++ return ret; ++ } + + pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE, s->index); + +- pa_log_info("Changed port of sink %u \"%s\" to %s", s->index, s->name, port->name); ++ pa_log_info("Changed port of sink %u \"%s\" from %s to %s", s->index, s->name, old_port->name, port->name); + + s->active_port = port; + s->save_port = save; + + pa_sink_set_latency_offset(s, s->active_port->latency_offset); ++ pa_device_port_active_changed(port, true); + + pa_hook_fire(&s->core->hooks[PA_CORE_HOOK_SINK_PORT_CHANGED], s); + +diff --git a/src/pulsecore/source.c b/src/pulsecore/source.c +index af4c6ec..d39193f 100644 +--- a/src/pulsecore/source.c ++++ b/src/pulsecore/source.c +@@ -611,6 +611,9 @@ void pa_source_put(pa_source *s) { + else + pa_assert_se(source_set_state(s, PA_SOURCE_IDLE) == 0); + ++ if (s->active_port) ++ pa_device_port_active_changed(s->active_port, true); ++ + pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SOURCE | PA_SUBSCRIPTION_EVENT_NEW, s->index); + pa_hook_fire(&s->core->hooks[PA_CORE_HOOK_SOURCE_PUT], s); + } +@@ -631,6 +634,9 @@ void pa_source_unlink(pa_source *s) { + if (linked) + pa_hook_fire(&s->core->hooks[PA_CORE_HOOK_SOURCE_UNLINK], s); + ++ if (s->active_port) ++ pa_device_port_active_changed(s->active_port, false); ++ + if (s->state != PA_SOURCE_UNLINKED) + pa_namereg_unregister(s->core, s->name); + pa_idxset_remove_by_data(s->core->sources, s, NULL); +@@ -2608,6 +2614,7 @@ size_t pa_source_get_max_rewind(pa_source *s) { + /* Called from main context */ + int pa_source_set_port(pa_source *s, const char *name, bool save) { + pa_device_port *port; ++ pa_device_port *old_port; + int ret; + + pa_source_assert_ref(s); +@@ -2624,11 +2631,15 @@ int pa_source_set_port(pa_source *s, const char *name, bool save) { + if (!(port = pa_hashmap_get(s->ports, name))) + return -PA_ERR_NOENTITY; + +- if (s->active_port == port) { ++ old_port = s->active_port; ++ ++ if (port == old_port) { + s->save_port = s->save_port || save; + return 0; + } + ++ pa_device_port_active_changed(old_port, false); ++ + if (s->flags & PA_SOURCE_DEFERRED_VOLUME) { + struct source_message_set_port msg = { .port = port, .ret = 0 }; + pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SOURCE_MESSAGE_SET_PORT, &msg, 0, NULL) == 0); +@@ -2637,16 +2648,26 @@ int pa_source_set_port(pa_source *s, const char *name, bool save) { + else + ret = s->set_port(s, port); + +- if (ret < 0) +- return -PA_ERR_NOENTITY; ++ if (ret < 0) { ++ pa_log("Failed to set the port of sink %s from %s to %s.", s->name, old_port->name, port->name); ++ ++ /* We don't know the real state of the device, but let's assume that ++ * the old port is still active, because s->active_port is left to ++ * point to the old port anyway. */ ++ pa_device_port_active_changed(old_port, true); ++ ++ return ret; ++ } + + pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_CHANGE, s->index); + +- pa_log_info("Changed port of source %u \"%s\" to %s", s->index, s->name, port->name); ++ pa_log_info("Changed port of source %u \"%s\" from %s to %s", s->index, s->name, old_port->name, port->name); + + s->active_port = port; + s->save_port = save; + ++ pa_device_port_active_changed(port, true); ++ + pa_hook_fire(&s->core->hooks[PA_CORE_HOOK_SOURCE_PORT_CHANGED], s); + + return 0; diff --git a/meta-tizen/recipes-multimedia/pulseaudio/pulseaudio_5.0/0062-sink-source-Assign-to-reference_volume-from-only-one.patch b/meta-tizen/recipes-multimedia/pulseaudio/pulseaudio_5.0/0062-sink-source-Assign-to-reference_volume-from-only-one.patch new file mode 100644 index 0000000..2f5a35c --- /dev/null +++ b/meta-tizen/recipes-multimedia/pulseaudio/pulseaudio_5.0/0062-sink-source-Assign-to-reference_volume-from-only-one.patch @@ -0,0 +1,246 @@ +From: Tanu Kaskinen +Date: Mon, 7 Apr 2014 12:20:58 +0300 +Subject: sink, source: Assign to reference_volume from only one place + +Forcing all reference volume changes to go through +set_reference_volume_direct() makes it easier to check where the +reference volume is changed, and it also allows us to have only one +place where notifications for changed reference volume are sent. + +Change-Id: I2e769b8a2b0d7031a02446dead8ca2e0c3402751 +Signed-off-by: Jaska Uimonen +--- + src/pulsecore/sink-input.c | 23 ++++++++++------------- + src/pulsecore/sink.c | 30 ++++++++++++++++++++++++++---- + src/pulsecore/sink.h | 7 +++++++ + src/pulsecore/source-output.c | 23 ++++++++++------------- + src/pulsecore/source.c | 30 ++++++++++++++++++++++++++---- + src/pulsecore/source.h | 7 +++++++ + 6 files changed, 86 insertions(+), 34 deletions(-) + +diff --git a/src/pulsecore/sink-input.c b/src/pulsecore/sink-input.c +index d0509b3..6596eeb 100644 +--- a/src/pulsecore/sink-input.c ++++ b/src/pulsecore/sink-input.c +@@ -1716,6 +1716,7 @@ int pa_sink_input_start_move(pa_sink_input *i) { + * their volume - this function does all that by using recursion. */ + static void update_volume_due_to_moving(pa_sink_input *i, pa_sink *dest) { + pa_cvolume old_volume; ++ pa_cvolume new_volume; + + pa_assert(i); + pa_assert(dest); +@@ -1787,25 +1788,21 @@ static void update_volume_due_to_moving(pa_sink_input *i, pa_sink *dest) { + * (sinks that use volume sharing should always have + * soft_volume of 0 dB) */ + +- old_volume = i->origin_sink->reference_volume; +- +- i->origin_sink->reference_volume = root_sink->reference_volume; +- pa_cvolume_remap(&i->origin_sink->reference_volume, &root_sink->channel_map, &i->origin_sink->channel_map); ++ new_volume = root_sink->reference_volume; ++ pa_cvolume_remap(&new_volume, &root_sink->channel_map, &i->origin_sink->channel_map); ++ pa_sink_set_reference_volume_direct(i->origin_sink, &new_volume); + + i->origin_sink->real_volume = root_sink->real_volume; + pa_cvolume_remap(&i->origin_sink->real_volume, &root_sink->channel_map, &i->origin_sink->channel_map); + + pa_assert(pa_cvolume_is_norm(&i->origin_sink->soft_volume)); + +- /* Notify others about the changed sink volume. If you wonder whether +- * i->origin_sink->set_volume() should be called somewhere, that's not +- * the case, because sinks that use volume sharing shouldn't have any +- * internal volume that set_volume() would update. If you wonder +- * whether the thread_info variables should be synced, yes, they +- * should, and it's done by the PA_SINK_MESSAGE_FINISH_MOVE message +- * handler. */ +- if (!pa_cvolume_equal(&i->origin_sink->reference_volume, &old_volume)) +- pa_subscription_post(i->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE, i->origin_sink->index); ++ /* If you wonder whether i->origin_sink->set_volume() should be called ++ * somewhere, that's not the case, because sinks that use volume ++ * sharing shouldn't have any internal volume that set_volume() would ++ * update. If you wonder whether the thread_info variables should be ++ * synced, yes, they should, and it's done by the ++ * PA_SINK_MESSAGE_FINISH_MOVE message handler. */ + + /* Recursively update origin sink inputs. */ + PA_IDXSET_FOREACH(origin_sink_input, i->origin_sink->inputs, idx) +diff --git a/src/pulsecore/sink.c b/src/pulsecore/sink.c +index 11a6e77..e00dce5 100644 +--- a/src/pulsecore/sink.c ++++ b/src/pulsecore/sink.c +@@ -2017,13 +2017,11 @@ static bool update_reference_volume(pa_sink *s, const pa_cvolume *v, const pa_ch + pa_cvolume_remap(&volume, channel_map, &s->channel_map); + + reference_volume_changed = !pa_cvolume_equal(&volume, &s->reference_volume); +- s->reference_volume = volume; ++ pa_sink_set_reference_volume_direct(s, &volume); + + s->save_volume = (!reference_volume_changed && s->save_volume) || save; + +- if (reference_volume_changed) +- pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE, s->index); +- else if (!(s->flags & PA_SINK_SHARE_VOLUME_WITH_MASTER)) ++ if (!reference_volume_changed && !(s->flags & PA_SINK_SHARE_VOLUME_WITH_MASTER)) + /* If the root sink's volume doesn't change, then there can't be any + * changes in the other sinks in the sink tree either. + * +@@ -3905,3 +3903,27 @@ done: + + return out_formats; + } ++ ++/* Called from the main thread. */ ++void pa_sink_set_reference_volume_direct(pa_sink *s, const pa_cvolume *volume) { ++ pa_cvolume old_volume; ++ char old_volume_str[PA_CVOLUME_SNPRINT_VERBOSE_MAX]; ++ char new_volume_str[PA_CVOLUME_SNPRINT_VERBOSE_MAX]; ++ ++ pa_assert(s); ++ pa_assert(volume); ++ ++ old_volume = s->reference_volume; ++ ++ if (pa_cvolume_equal(volume, &old_volume)) ++ return; ++ ++ s->reference_volume = *volume; ++ pa_log_debug("The reference volume of sink %s changed from %s to %s.", s->name, ++ pa_cvolume_snprint_verbose(old_volume_str, sizeof(old_volume_str), &old_volume, &s->channel_map, ++ s->flags & PA_SINK_DECIBEL_VOLUME), ++ pa_cvolume_snprint_verbose(new_volume_str, sizeof(new_volume_str), volume, &s->channel_map, ++ s->flags & PA_SINK_DECIBEL_VOLUME)); ++ ++ pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE, s->index); ++} +diff --git a/src/pulsecore/sink.h b/src/pulsecore/sink.h +index 576f34b..41a439b 100644 +--- a/src/pulsecore/sink.h ++++ b/src/pulsecore/sink.h +@@ -512,6 +512,13 @@ void pa_sink_invalidate_requested_latency(pa_sink *s, bool dynamic); + + pa_usec_t pa_sink_get_latency_within_thread(pa_sink *s); + ++/* Called from the main thread, from sink-input.c only. The normal way to set ++ * the sink reference volume is to call pa_sink_set_volume(), but the flat ++ * volume logic in sink-input.c needs also a function that doesn't do all the ++ * extra stuff that pa_sink_set_volume() does. This function simply sets ++ * s->reference_volume and fires change notifications. */ ++void pa_sink_set_reference_volume_direct(pa_sink *s, const pa_cvolume *volume); ++ + /* Verify that we called in IO context (aka 'thread context), or that + * the sink is not yet set up, i.e. the thread not set up yet. See + * pa_assert_io_context() in thread-mq.h for more information. */ +diff --git a/src/pulsecore/source-output.c b/src/pulsecore/source-output.c +index b12758a..169d98d 100644 +--- a/src/pulsecore/source-output.c ++++ b/src/pulsecore/source-output.c +@@ -1263,6 +1263,7 @@ int pa_source_output_start_move(pa_source_output *o) { + * their volume - this function does all that by using recursion. */ + static void update_volume_due_to_moving(pa_source_output *o, pa_source *dest) { + pa_cvolume old_volume; ++ pa_cvolume new_volume; + + pa_assert(o); + pa_assert(dest); +@@ -1336,25 +1337,21 @@ static void update_volume_due_to_moving(pa_source_output *o, pa_source *dest) { + * (sources that use volume sharing should always have + * soft_volume of 0 dB) */ + +- old_volume = o->destination_source->reference_volume; +- +- o->destination_source->reference_volume = root_source->reference_volume; +- pa_cvolume_remap(&o->destination_source->reference_volume, &root_source->channel_map, &o->destination_source->channel_map); ++ new_volume = root_source->reference_volume; ++ pa_cvolume_remap(&new_volume, &root_source->channel_map, &o->destination_source->channel_map); ++ pa_source_set_reference_volume_direct(o->destination_source, &new_volume); + + o->destination_source->real_volume = root_source->real_volume; + pa_cvolume_remap(&o->destination_source->real_volume, &root_source->channel_map, &o->destination_source->channel_map); + + pa_assert(pa_cvolume_is_norm(&o->destination_source->soft_volume)); + +- /* Notify others about the changed source volume. If you wonder whether +- * o->destination_source->set_volume() should be called somewhere, that's not +- * the case, because sources that use volume sharing shouldn't have any +- * internal volume that set_volume() would update. If you wonder +- * whether the thread_info variables should be synced, yes, they +- * should, and it's done by the PA_SOURCE_MESSAGE_FINISH_MOVE message +- * handler. */ +- if (!pa_cvolume_equal(&o->destination_source->reference_volume, &old_volume)) +- pa_subscription_post(o->core, PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_CHANGE, o->destination_source->index); ++ /* If you wonder whether o->destination_source->set_volume() should be ++ * called somewhere, that's not the case, because sources that use ++ * volume sharing shouldn't have any internal volume that set_volume() ++ * would update. If you wonder whether the thread_info variables should ++ * be synced, yes, they should, and it's done by the ++ * PA_SOURCE_MESSAGE_FINISH_MOVE message handler. */ + + /* Recursively update origin source outputs. */ + PA_IDXSET_FOREACH(destination_source_output, o->destination_source->outputs, idx) +diff --git a/src/pulsecore/source.c b/src/pulsecore/source.c +index d39193f..f58bb44 100644 +--- a/src/pulsecore/source.c ++++ b/src/pulsecore/source.c +@@ -1562,13 +1562,11 @@ static bool update_reference_volume(pa_source *s, const pa_cvolume *v, const pa_ + pa_cvolume_remap(&volume, channel_map, &s->channel_map); + + reference_volume_changed = !pa_cvolume_equal(&volume, &s->reference_volume); +- s->reference_volume = volume; ++ pa_source_set_reference_volume_direct(s, &volume); + + s->save_volume = (!reference_volume_changed && s->save_volume) || save; + +- if (reference_volume_changed) +- pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_CHANGE, s->index); +- else if (!(s->flags & PA_SOURCE_SHARE_VOLUME_WITH_MASTER)) ++ if (!reference_volume_changed && !(s->flags & PA_SOURCE_SHARE_VOLUME_WITH_MASTER)) + /* If the root source's volume doesn't change, then there can't be any + * changes in the other source in the source tree either. + * +@@ -2899,3 +2897,27 @@ done: + + return out_formats; + } ++ ++/* Called from the main thread. */ ++void pa_source_set_reference_volume_direct(pa_source *s, const pa_cvolume *volume) { ++ pa_cvolume old_volume; ++ char old_volume_str[PA_CVOLUME_SNPRINT_VERBOSE_MAX]; ++ char new_volume_str[PA_CVOLUME_SNPRINT_VERBOSE_MAX]; ++ ++ pa_assert(s); ++ pa_assert(volume); ++ ++ old_volume = s->reference_volume; ++ ++ if (pa_cvolume_equal(volume, &old_volume)) ++ return; ++ ++ s->reference_volume = *volume; ++ pa_log_debug("The reference volume of source %s changed from %s to %s.", s->name, ++ pa_cvolume_snprint_verbose(old_volume_str, sizeof(old_volume_str), &old_volume, &s->channel_map, ++ s->flags & PA_SOURCE_DECIBEL_VOLUME), ++ pa_cvolume_snprint_verbose(new_volume_str, sizeof(new_volume_str), volume, &s->channel_map, ++ s->flags & PA_SOURCE_DECIBEL_VOLUME)); ++ ++ pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_CHANGE, s->index); ++} +diff --git a/src/pulsecore/source.h b/src/pulsecore/source.h +index 5c74a51..6318595 100644 +--- a/src/pulsecore/source.h ++++ b/src/pulsecore/source.h +@@ -426,6 +426,13 @@ bool pa_source_volume_change_apply(pa_source *s, pa_usec_t *usec_to_next); + void pa_source_invalidate_requested_latency(pa_source *s, bool dynamic); + pa_usec_t pa_source_get_latency_within_thread(pa_source *s); + ++/* Called from the main thread, from source-output.c only. The normal way to ++ * set the source reference volume is to call pa_source_set_volume(), but the ++ * flat volume logic in source-output.c needs also a function that doesn't do ++ * all the extra stuff that pa_source_set_volume() does. This function simply ++ * sets s->reference_volume and fires change notifications. */ ++void pa_source_set_reference_volume_direct(pa_source *s, const pa_cvolume *volume); ++ + #define pa_source_assert_io_context(s) \ + pa_assert(pa_thread_mq_get() || !PA_SOURCE_IS_LINKED((s)->state)) + diff --git a/meta-tizen/recipes-multimedia/pulseaudio/pulseaudio_5.0/0063-sink-input-source-output-Assign-to-volume-from-only-.patch b/meta-tizen/recipes-multimedia/pulseaudio/pulseaudio_5.0/0063-sink-input-source-output-Assign-to-volume-from-only-.patch new file mode 100644 index 0000000..38b3293 --- /dev/null +++ b/meta-tizen/recipes-multimedia/pulseaudio/pulseaudio_5.0/0063-sink-input-source-output-Assign-to-volume-from-only-.patch @@ -0,0 +1,506 @@ +From: Tanu Kaskinen +Date: Mon, 7 Apr 2014 12:48:15 +0300 +Subject: sink-input, source-output: Assign to volume from only one place + +Forcing all volume changes to go through set_volume_direct() makes +it easier to check where the stream volume is changed, and it also +allows us to have only one place where notifications for changed +volume are sent. + +Change-Id: Ie61bcc5747b419bb83c19a3ed78fd9f4d8a73cce +Signed-off-by: Jaska Uimonen +--- + src/pulsecore/sink-input.c | 69 +++++++++++++++++++++---------------------- + src/pulsecore/sink-input.h | 7 +++++ + src/pulsecore/sink.c | 53 +++++++++------------------------ + src/pulsecore/source-output.c | 62 ++++++++++++++++++++------------------ + src/pulsecore/source-output.h | 7 +++++ + src/pulsecore/source.c | 54 +++++++++------------------------ + 6 files changed, 108 insertions(+), 144 deletions(-) + +diff --git a/src/pulsecore/sink-input.c b/src/pulsecore/sink-input.c +index 6596eeb..83a0493 100644 +--- a/src/pulsecore/sink-input.c ++++ b/src/pulsecore/sink-input.c +@@ -1292,7 +1292,7 @@ void pa_sink_input_set_volume(pa_sink_input *i, const pa_cvolume *volume, bool s + return; + } + +- i->volume = *volume; ++ pa_sink_input_set_volume_direct(i, volume); + i->save_volume = save; + + if (pa_sink_flat_volume_enabled(i->sink)) { +@@ -1310,13 +1310,6 @@ void pa_sink_input_set_volume(pa_sink_input *i, const pa_cvolume *volume, bool s + /* Copy the new soft_volume to the thread_info struct */ + pa_assert_se(pa_asyncmsgq_send(i->sink->asyncmsgq, PA_MSGOBJECT(i), PA_SINK_INPUT_MESSAGE_SET_SOFT_VOLUME, NULL, 0, NULL) == 0); + } +- +- /* The volume changed, let's tell people so */ +- if (i->volume_changed) +- i->volume_changed(i); +- +- /* The virtual volume changed, let's tell people so */ +- pa_subscription_post(i->core, PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_CHANGE, i->index); + } + + void pa_sink_input_add_volume_factor(pa_sink_input *i, const char *key, const pa_cvolume *volume_factor) { +@@ -1715,7 +1708,6 @@ int pa_sink_input_start_move(pa_sink_input *i) { + * then also the origin sink and all streams connected to it need to update + * their volume - this function does all that by using recursion. */ + static void update_volume_due_to_moving(pa_sink_input *i, pa_sink *dest) { +- pa_cvolume old_volume; + pa_cvolume new_volume; + + pa_assert(i); +@@ -1765,19 +1757,11 @@ static void update_volume_due_to_moving(pa_sink_input *i, pa_sink *dest) { + * always have volume_factor as soft_volume, so no change + * should be needed) */ + +- old_volume = i->volume; +- pa_cvolume_reset(&i->volume, i->volume.channels); ++ pa_cvolume_reset(&new_volume, i->volume.channels); ++ pa_sink_input_set_volume_direct(i, &new_volume); + pa_cvolume_reset(&i->reference_ratio, i->reference_ratio.channels); + pa_assert(pa_cvolume_is_norm(&i->real_ratio)); + pa_assert(pa_cvolume_equal(&i->soft_volume, &i->volume_factor)); +- +- /* Notify others about the changed sink input volume. */ +- if (!pa_cvolume_equal(&i->volume, &old_volume)) { +- if (i->volume_changed) +- i->volume_changed(i); +- +- pa_subscription_post(i->core, PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_CHANGE, i->index); +- } + } + + /* Additionally, the origin sink volume needs updating: +@@ -1809,8 +1793,6 @@ static void update_volume_due_to_moving(pa_sink_input *i, pa_sink *dest) { + update_volume_due_to_moving(origin_sink_input, dest); + + } else { +- old_volume = i->volume; +- + if (pa_sink_flat_volume_enabled(i->sink)) { + /* Ok, so this is a regular stream, and flat volume is enabled. The + * volume will have to be updated as follows: +@@ -1822,9 +1804,10 @@ static void update_volume_due_to_moving(pa_sink_input *i, pa_sink *dest) { + * i->soft_volume := i->real_ratio * i->volume_factor + * (handled later by pa_sink_set_volume) */ + +- i->volume = i->sink->reference_volume; +- pa_cvolume_remap(&i->volume, &i->sink->channel_map, &i->channel_map); +- pa_sw_cvolume_multiply(&i->volume, &i->volume, &i->reference_ratio); ++ new_volume = i->sink->reference_volume; ++ pa_cvolume_remap(&new_volume, &i->sink->channel_map, &i->channel_map); ++ pa_sw_cvolume_multiply(&new_volume, &new_volume, &i->reference_ratio); ++ pa_sink_input_set_volume_direct(i, &new_volume); + + } else { + /* Ok, so this is a regular stream, and flat volume is disabled. +@@ -1835,21 +1818,10 @@ static void update_volume_due_to_moving(pa_sink_input *i, pa_sink *dest) { + * i->real_ratio := i->reference_ratio + * i->soft_volume := i->real_ratio * i->volume_factor */ + +- i->volume = i->reference_ratio; ++ pa_sink_input_set_volume_direct(i, &i->reference_ratio); + i->real_ratio = i->reference_ratio; + pa_sw_cvolume_multiply(&i->soft_volume, &i->real_ratio, &i->volume_factor); + } +- +- /* Notify others about the changed sink input volume. */ +- if (!pa_cvolume_equal(&i->volume, &old_volume)) { +- /* XXX: In case i->sink has flat volume enabled, then real_ratio +- * and soft_volume are not updated yet. Let's hope that the +- * callback implementation doesn't care about those variables... */ +- if (i->volume_changed) +- i->volume_changed(i); +- +- pa_subscription_post(i->core, PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_CHANGE, i->index); +- } + } + + /* If i->sink == dest, then recursion has finished, and we can finally call +@@ -2333,3 +2305,28 @@ int pa_sink_input_update_rate(pa_sink_input *i) { + + return 0; + } ++ ++/* Called from the main thread. */ ++void pa_sink_input_set_volume_direct(pa_sink_input *i, const pa_cvolume *volume) { ++ pa_cvolume old_volume; ++ char old_volume_str[PA_CVOLUME_SNPRINT_VERBOSE_MAX]; ++ char new_volume_str[PA_CVOLUME_SNPRINT_VERBOSE_MAX]; ++ ++ pa_assert(i); ++ pa_assert(volume); ++ ++ old_volume = i->volume; ++ ++ if (pa_cvolume_equal(volume, &old_volume)) ++ return; ++ ++ i->volume = *volume; ++ pa_log_debug("The volume of sink input %u changed from %s to %s.", i->index, ++ pa_cvolume_snprint_verbose(old_volume_str, sizeof(old_volume_str), &old_volume, &i->channel_map, true), ++ pa_cvolume_snprint_verbose(new_volume_str, sizeof(new_volume_str), volume, &i->channel_map, true)); ++ ++ if (i->volume_changed) ++ i->volume_changed(i); ++ ++ pa_subscription_post(i->core, PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_CHANGE, i->index); ++} +diff --git a/src/pulsecore/sink-input.h b/src/pulsecore/sink-input.h +index deea348..4e7b229 100644 +--- a/src/pulsecore/sink-input.h ++++ b/src/pulsecore/sink-input.h +@@ -431,6 +431,13 @@ bool pa_sink_input_process_underrun(pa_sink_input *i); + + pa_memchunk* pa_sink_input_get_silence(pa_sink_input *i, pa_memchunk *ret); + ++/* Called from the main thread, from sink.c only. The normal way to set the ++ * sink input volume is to call pa_sink_input_set_volume(), but the flat volume ++ * logic in sink.c needs also a function that doesn't do all the extra stuff ++ * that pa_sink_input_set_volume() does. This function simply sets i->volume ++ * and fires change notifications. */ ++void pa_sink_input_set_volume_direct(pa_sink_input *i, const pa_cvolume *volume); ++ + #define pa_sink_input_assert_io_context(s) \ + pa_assert(pa_thread_mq_get() || !PA_SINK_INPUT_IS_LINKED((s)->state)) + +diff --git a/src/pulsecore/sink.c b/src/pulsecore/sink.c +index e00dce5..94046b1 100644 +--- a/src/pulsecore/sink.c ++++ b/src/pulsecore/sink.c +@@ -1898,20 +1898,13 @@ static void update_real_volume(pa_sink *s, const pa_cvolume *new_volume, pa_chan + PA_IDXSET_FOREACH(i, s->inputs, idx) { + if (i->origin_sink && (i->origin_sink->flags & PA_SINK_SHARE_VOLUME_WITH_MASTER)) { + if (pa_sink_flat_volume_enabled(s)) { +- pa_cvolume old_volume = i->volume; ++ pa_cvolume new_input_volume; + + /* Follow the root sink's real volume. */ +- i->volume = *new_volume; +- pa_cvolume_remap(&i->volume, channel_map, &i->channel_map); ++ new_input_volume = *new_volume; ++ pa_cvolume_remap(&new_input_volume, channel_map, &i->channel_map); ++ pa_sink_input_set_volume_direct(i, &new_input_volume); + compute_reference_ratio(i); +- +- /* The volume changed, let's tell people so */ +- if (!pa_cvolume_equal(&old_volume, &i->volume)) { +- if (i->volume_changed) +- i->volume_changed(i); +- +- pa_subscription_post(i->core, PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_CHANGE, i->index); +- } + } + + update_real_volume(i->origin_sink, new_volume, channel_map); +@@ -1966,7 +1959,7 @@ static void propagate_reference_volume(pa_sink *s) { + * sink input volumes accordingly */ + + PA_IDXSET_FOREACH(i, s->inputs, idx) { +- pa_cvolume old_volume; ++ pa_cvolume new_volume; + + if (i->origin_sink && (i->origin_sink->flags & PA_SINK_SHARE_VOLUME_WITH_MASTER)) { + propagate_reference_volume(i->origin_sink); +@@ -1977,24 +1970,14 @@ static void propagate_reference_volume(pa_sink *s) { + continue; + } + +- old_volume = i->volume; +- + /* This basically calculates: + * + * i->volume := s->reference_volume * i->reference_ratio */ + +- i->volume = s->reference_volume; +- pa_cvolume_remap(&i->volume, &s->channel_map, &i->channel_map); +- pa_sw_cvolume_multiply(&i->volume, &i->volume, &i->reference_ratio); +- +- /* The volume changed, let's tell people so */ +- if (!pa_cvolume_equal(&old_volume, &i->volume)) { +- +- if (i->volume_changed) +- i->volume_changed(i); +- +- pa_subscription_post(i->core, PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_CHANGE, i->index); +- } ++ new_volume = s->reference_volume; ++ pa_cvolume_remap(&new_volume, &s->channel_map, &i->channel_map); ++ pa_sw_cvolume_multiply(&new_volume, &new_volume, &i->reference_ratio); ++ pa_sink_input_set_volume_direct(i, &new_volume); + } + } + +@@ -2215,7 +2198,7 @@ static void propagate_real_volume(pa_sink *s, const pa_cvolume *old_real_volume) + if (pa_sink_flat_volume_enabled(s)) { + + PA_IDXSET_FOREACH(i, s->inputs, idx) { +- pa_cvolume old_volume = i->volume; ++ pa_cvolume new_volume; + + /* 2. Since the sink's reference and real volumes are equal + * now our ratios should be too. */ +@@ -2229,18 +2212,10 @@ static void propagate_real_volume(pa_sink *s, const pa_cvolume *old_real_volume) + * i->volume = s->reference_volume * i->reference_ratio + * + * This is identical to propagate_reference_volume() */ +- i->volume = s->reference_volume; +- pa_cvolume_remap(&i->volume, &s->channel_map, &i->channel_map); +- pa_sw_cvolume_multiply(&i->volume, &i->volume, &i->reference_ratio); +- +- /* Notify if something changed */ +- if (!pa_cvolume_equal(&old_volume, &i->volume)) { +- +- if (i->volume_changed) +- i->volume_changed(i); +- +- pa_subscription_post(i->core, PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_CHANGE, i->index); +- } ++ new_volume = s->reference_volume; ++ pa_cvolume_remap(&new_volume, &s->channel_map, &i->channel_map); ++ pa_sw_cvolume_multiply(&new_volume, &new_volume, &i->reference_ratio); ++ pa_sink_input_set_volume_direct(i, &new_volume); + + if (i->origin_sink && (i->origin_sink->flags & PA_SINK_SHARE_VOLUME_WITH_MASTER)) + propagate_real_volume(i->origin_sink, old_real_volume); +diff --git a/src/pulsecore/source-output.c b/src/pulsecore/source-output.c +index 169d98d..761323e 100644 +--- a/src/pulsecore/source-output.c ++++ b/src/pulsecore/source-output.c +@@ -972,7 +972,7 @@ void pa_source_output_set_volume(pa_source_output *o, const pa_cvolume *volume, + return; + } + +- o->volume = *volume; ++ pa_source_output_set_volume_direct(o, volume); + o->save_volume = save; + + if (pa_source_flat_volume_enabled(o->source)) { +@@ -1262,7 +1262,6 @@ int pa_source_output_start_move(pa_source_output *o) { + * then also the origin source and all streams connected to it need to update + * their volume - this function does all that by using recursion. */ + static void update_volume_due_to_moving(pa_source_output *o, pa_source *dest) { +- pa_cvolume old_volume; + pa_cvolume new_volume; + + pa_assert(o); +@@ -1314,19 +1313,11 @@ static void update_volume_due_to_moving(pa_source_output *o, pa_source *dest) { + * always have volume_factor as soft_volume, so no change + * should be needed) */ + +- old_volume = o->volume; +- pa_cvolume_reset(&o->volume, o->volume.channels); ++ pa_cvolume_reset(&new_volume, o->volume.channels); ++ pa_source_output_set_volume_direct(o, &new_volume); + pa_cvolume_reset(&o->reference_ratio, o->reference_ratio.channels); + pa_assert(pa_cvolume_is_norm(&o->real_ratio)); + pa_assert(pa_cvolume_equal(&o->soft_volume, &o->volume_factor)); +- +- /* Notify others about the changed source output volume. */ +- if (!pa_cvolume_equal(&o->volume, &old_volume)) { +- if (o->volume_changed) +- o->volume_changed(o); +- +- pa_subscription_post(o->core, PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT|PA_SUBSCRIPTION_EVENT_CHANGE, o->index); +- } + } + + /* Additionally, the origin source volume needs updating: +@@ -1358,8 +1349,6 @@ static void update_volume_due_to_moving(pa_source_output *o, pa_source *dest) { + update_volume_due_to_moving(destination_source_output, dest); + + } else { +- old_volume = o->volume; +- + if (pa_source_flat_volume_enabled(o->source)) { + /* Ok, so this is a regular stream, and flat volume is enabled. The + * volume will have to be updated as follows: +@@ -1371,9 +1360,10 @@ static void update_volume_due_to_moving(pa_source_output *o, pa_source *dest) { + * o->soft_volume := o->real_ratio * o->volume_factor + * (handled later by pa_source_set_volume) */ + +- o->volume = o->source->reference_volume; +- pa_cvolume_remap(&o->volume, &o->source->channel_map, &o->channel_map); +- pa_sw_cvolume_multiply(&o->volume, &o->volume, &o->reference_ratio); ++ new_volume = o->source->reference_volume; ++ pa_cvolume_remap(&new_volume, &o->source->channel_map, &o->channel_map); ++ pa_sw_cvolume_multiply(&new_volume, &new_volume, &o->reference_ratio); ++ pa_source_output_set_volume_direct(o, &new_volume); + + } else { + /* Ok, so this is a regular stream, and flat volume is disabled. +@@ -1384,21 +1374,10 @@ static void update_volume_due_to_moving(pa_source_output *o, pa_source *dest) { + * o->real_ratio := o->reference_ratio + * o->soft_volume := o->real_ratio * o->volume_factor */ + +- o->volume = o->reference_ratio; ++ pa_source_output_set_volume_direct(o, &o->reference_ratio); + o->real_ratio = o->reference_ratio; + pa_sw_cvolume_multiply(&o->soft_volume, &o->real_ratio, &o->volume_factor); + } +- +- /* Notify others about the changed source output volume. */ +- if (!pa_cvolume_equal(&o->volume, &old_volume)) { +- /* XXX: In case o->source has flat volume enabled, then real_ratio +- * and soft_volume are not updated yet. Let's hope that the +- * callback implementation doesn't care about those variables... */ +- if (o->volume_changed) +- o->volume_changed(o); +- +- pa_subscription_post(o->core, PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT|PA_SUBSCRIPTION_EVENT_CHANGE, o->index); +- } + } + + /* If o->source == dest, then recursion has finished, and we can finally call +@@ -1692,3 +1671,28 @@ int pa_source_output_update_rate(pa_source_output *o) { + + return 0; + } ++ ++/* Called from the main thread. */ ++void pa_source_output_set_volume_direct(pa_source_output *o, const pa_cvolume *volume) { ++ pa_cvolume old_volume; ++ char old_volume_str[PA_CVOLUME_SNPRINT_VERBOSE_MAX]; ++ char new_volume_str[PA_CVOLUME_SNPRINT_VERBOSE_MAX]; ++ ++ pa_assert(o); ++ pa_assert(volume); ++ ++ old_volume = o->volume; ++ ++ if (pa_cvolume_equal(volume, &old_volume)) ++ return; ++ ++ o->volume = *volume; ++ pa_log_debug("The volume of source output %u changed from %s to %s.", o->index, ++ pa_cvolume_snprint_verbose(old_volume_str, sizeof(old_volume_str), &old_volume, &o->channel_map, true), ++ pa_cvolume_snprint_verbose(new_volume_str, sizeof(new_volume_str), volume, &o->channel_map, true)); ++ ++ if (o->volume_changed) ++ o->volume_changed(o); ++ ++ pa_subscription_post(o->core, PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT|PA_SUBSCRIPTION_EVENT_CHANGE, o->index); ++} +diff --git a/src/pulsecore/source-output.h b/src/pulsecore/source-output.h +index 102fb8b..27d6fd4 100644 +--- a/src/pulsecore/source-output.h ++++ b/src/pulsecore/source-output.h +@@ -353,6 +353,13 @@ int pa_source_output_process_msg(pa_msgobject *mo, int code, void *userdata, int + + pa_usec_t pa_source_output_set_requested_latency_within_thread(pa_source_output *o, pa_usec_t usec); + ++/* Called from the main thread, from source.c only. The normal way to set the ++ * source output volume is to call pa_source_output_set_volume(), but the flat ++ * volume logic in source.c needs also a function that doesn't do all the extra ++ * stuff that pa_source_output_set_volume() does. This function simply sets ++ * o->volume and fires change notifications. */ ++void pa_source_output_set_volume_direct(pa_source_output *o, const pa_cvolume *volume); ++ + #define pa_source_output_assert_io_context(s) \ + pa_assert(pa_thread_mq_get() || !PA_SOURCE_OUTPUT_IS_LINKED((s)->state)) + +diff --git a/src/pulsecore/source.c b/src/pulsecore/source.c +index f58bb44..c8165e6 100644 +--- a/src/pulsecore/source.c ++++ b/src/pulsecore/source.c +@@ -1443,20 +1443,13 @@ static void update_real_volume(pa_source *s, const pa_cvolume *new_volume, pa_ch + PA_IDXSET_FOREACH(o, s->outputs, idx) { + if (o->destination_source && (o->destination_source->flags & PA_SOURCE_SHARE_VOLUME_WITH_MASTER)) { + if (pa_source_flat_volume_enabled(s)) { +- pa_cvolume old_volume = o->volume; ++ pa_cvolume new_output_volume; + + /* Follow the root source's real volume. */ +- o->volume = *new_volume; +- pa_cvolume_remap(&o->volume, channel_map, &o->channel_map); ++ new_output_volume = *new_volume; ++ pa_cvolume_remap(&new_output_volume, channel_map, &o->channel_map); ++ pa_source_output_set_volume_direct(o, &new_output_volume); + compute_reference_ratio(o); +- +- /* The volume changed, let's tell people so */ +- if (!pa_cvolume_equal(&old_volume, &o->volume)) { +- if (o->volume_changed) +- o->volume_changed(o); +- +- pa_subscription_post(o->core, PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT|PA_SUBSCRIPTION_EVENT_CHANGE, o->index); +- } + } + + update_real_volume(o->destination_source, new_volume, channel_map); +@@ -1511,7 +1504,7 @@ static void propagate_reference_volume(pa_source *s) { + * source output volumes accordingly */ + + PA_IDXSET_FOREACH(o, s->outputs, idx) { +- pa_cvolume old_volume; ++ pa_cvolume new_volume; + + if (o->destination_source && (o->destination_source->flags & PA_SOURCE_SHARE_VOLUME_WITH_MASTER)) { + propagate_reference_volume(o->destination_source); +@@ -1522,24 +1515,14 @@ static void propagate_reference_volume(pa_source *s) { + continue; + } + +- old_volume = o->volume; +- + /* This basically calculates: + * + * o->volume := o->reference_volume * o->reference_ratio */ + +- o->volume = s->reference_volume; +- pa_cvolume_remap(&o->volume, &s->channel_map, &o->channel_map); +- pa_sw_cvolume_multiply(&o->volume, &o->volume, &o->reference_ratio); +- +- /* The volume changed, let's tell people so */ +- if (!pa_cvolume_equal(&old_volume, &o->volume)) { +- +- if (o->volume_changed) +- o->volume_changed(o); +- +- pa_subscription_post(o->core, PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT|PA_SUBSCRIPTION_EVENT_CHANGE, o->index); +- } ++ new_volume = s->reference_volume; ++ pa_cvolume_remap(&new_volume, &s->channel_map, &o->channel_map); ++ pa_sw_cvolume_multiply(&new_volume, &new_volume, &o->reference_ratio); ++ pa_source_output_set_volume_direct(o, &new_volume); + } + } + +@@ -1732,9 +1715,8 @@ static void propagate_real_volume(pa_source *s, const pa_cvolume *old_real_volum + } + + if (pa_source_flat_volume_enabled(s)) { +- + PA_IDXSET_FOREACH(o, s->outputs, idx) { +- pa_cvolume old_volume = o->volume; ++ pa_cvolume new_volume; + + /* 2. Since the source's reference and real volumes are equal + * now our ratios should be too. */ +@@ -1748,18 +1730,10 @@ static void propagate_real_volume(pa_source *s, const pa_cvolume *old_real_volum + * o->volume = s->reference_volume * o->reference_ratio + * + * This is identical to propagate_reference_volume() */ +- o->volume = s->reference_volume; +- pa_cvolume_remap(&o->volume, &s->channel_map, &o->channel_map); +- pa_sw_cvolume_multiply(&o->volume, &o->volume, &o->reference_ratio); +- +- /* Notify if something changed */ +- if (!pa_cvolume_equal(&old_volume, &o->volume)) { +- +- if (o->volume_changed) +- o->volume_changed(o); +- +- pa_subscription_post(o->core, PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT|PA_SUBSCRIPTION_EVENT_CHANGE, o->index); +- } ++ new_volume = s->reference_volume; ++ pa_cvolume_remap(&new_volume, &s->channel_map, &o->channel_map); ++ pa_sw_cvolume_multiply(&new_volume, &new_volume, &o->reference_ratio); ++ pa_source_output_set_volume_direct(o, &new_volume); + + if (o->destination_source && (o->destination_source->flags & PA_SOURCE_SHARE_VOLUME_WITH_MASTER)) + propagate_real_volume(o->destination_source, old_real_volume); diff --git a/meta-tizen/recipes-multimedia/pulseaudio/pulseaudio_5.0/0064-sink-source-Return-early-from-set_mute.patch b/meta-tizen/recipes-multimedia/pulseaudio/pulseaudio_5.0/0064-sink-source-Return-early-from-set_mute.patch new file mode 100644 index 0000000..4cd0fdb --- /dev/null +++ b/meta-tizen/recipes-multimedia/pulseaudio/pulseaudio_5.0/0064-sink-source-Return-early-from-set_mute.patch @@ -0,0 +1,75 @@ +From: Tanu Kaskinen +Date: Mon, 14 Apr 2014 14:03:24 +0300 +Subject: sink, source: Return early from set_mute() + +This avoids redundant set_mute() callback calls. + +Some logging was added too. + +Change-Id: I10188c3b43d61fe751abe0f9940015af35c4a137 +Signed-off-by: Jaska Uimonen +--- + src/pulsecore/sink.c | 13 +++++++++---- + src/pulsecore/source.c | 13 +++++++++---- + 2 files changed, 18 insertions(+), 8 deletions(-) + +diff --git a/src/pulsecore/sink.c b/src/pulsecore/sink.c +index 94046b1..3eed550 100644 +--- a/src/pulsecore/sink.c ++++ b/src/pulsecore/sink.c +@@ -2288,16 +2288,21 @@ void pa_sink_set_mute(pa_sink *s, bool mute, bool save) { + pa_assert(PA_SINK_IS_LINKED(s->state)); + + old_muted = s->muted; ++ ++ if (mute == old_muted) { ++ s->save_muted |= save; ++ return; ++ } ++ + s->muted = mute; +- s->save_muted = (old_muted == s->muted && s->save_muted) || save; ++ s->save_muted = save; + + if (!(s->flags & PA_SINK_DEFERRED_VOLUME) && s->set_mute) + s->set_mute(s); + ++ pa_log_debug("The mute of sink %s changed from %s to %s.", s->name, pa_yes_no(old_muted), pa_yes_no(mute)); + pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_SET_MUTE, NULL, 0, NULL) == 0); +- +- if (old_muted != s->muted) +- pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE, s->index); ++ pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE, s->index); + } + + /* Called from main thread */ +diff --git a/src/pulsecore/source.c b/src/pulsecore/source.c +index c8165e6..4f4aea3 100644 +--- a/src/pulsecore/source.c ++++ b/src/pulsecore/source.c +@@ -1806,16 +1806,21 @@ void pa_source_set_mute(pa_source *s, bool mute, bool save) { + pa_assert(PA_SOURCE_IS_LINKED(s->state)); + + old_muted = s->muted; ++ ++ if (mute == old_muted) { ++ s->save_muted |= save; ++ return; ++ } ++ + s->muted = mute; +- s->save_muted = (old_muted == s->muted && s->save_muted) || save; ++ s->save_muted = save; + + if (!(s->flags & PA_SOURCE_DEFERRED_VOLUME) && s->set_mute) + s->set_mute(s); + ++ pa_log_debug("The mute of source %s changed from %s to %s.", s->name, pa_yes_no(old_muted), pa_yes_no(mute)); + pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SOURCE_MESSAGE_SET_MUTE, NULL, 0, NULL) == 0); +- +- if (old_muted != s->muted) +- pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_CHANGE, s->index); ++ pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_CHANGE, s->index); + } + + /* Called from main thread */ diff --git a/meta-tizen/recipes-multimedia/pulseaudio/pulseaudio_5.0/0065-sink-input-source-output-Add-logging-to-set_mute.patch b/meta-tizen/recipes-multimedia/pulseaudio/pulseaudio_5.0/0065-sink-input-source-output-Add-logging-to-set_mute.patch new file mode 100644 index 0000000..a60d1d2 --- /dev/null +++ b/meta-tizen/recipes-multimedia/pulseaudio/pulseaudio_5.0/0065-sink-input-source-output-Add-logging-to-set_mute.patch @@ -0,0 +1,67 @@ +From: Tanu Kaskinen +Date: Mon, 14 Apr 2014 14:13:56 +0300 +Subject: sink-input, source-output: Add logging to set_mute() + +Change-Id: Ie10aa76cae75c7b6a52ea4a9039b8e3e37a748b2 +Signed-off-by: Jaska Uimonen +--- + src/pulsecore/sink-input.c | 8 +++++++- + src/pulsecore/source-output.c | 8 +++++++- + 2 files changed, 14 insertions(+), 2 deletions(-) + +diff --git a/src/pulsecore/sink-input.c b/src/pulsecore/sink-input.c +index 83a0493..f706acc 100644 +--- a/src/pulsecore/sink-input.c ++++ b/src/pulsecore/sink-input.c +@@ -1458,16 +1458,22 @@ pa_cvolume *pa_sink_input_get_volume(pa_sink_input *i, pa_cvolume *volume, bool + + /* Called from main context */ + void pa_sink_input_set_mute(pa_sink_input *i, bool mute, bool save) { ++ bool old_mute; ++ + pa_sink_input_assert_ref(i); + pa_assert_ctl_context(); + pa_assert(PA_SINK_INPUT_IS_LINKED(i->state)); + +- if (!i->muted == !mute) { ++ old_mute = i->muted; ++ ++ if (mute == old_mute) { + i->save_muted |= save; + return; + } + + i->muted = mute; ++ pa_log_debug("The mute of sink input %u changed from %s to %s.", i->index, pa_yes_no(old_mute), pa_yes_no(mute)); ++ + i->save_muted = save; + + pa_assert_se(pa_asyncmsgq_send(i->sink->asyncmsgq, PA_MSGOBJECT(i), PA_SINK_INPUT_MESSAGE_SET_SOFT_MUTE, NULL, 0, NULL) == 0); +diff --git a/src/pulsecore/source-output.c b/src/pulsecore/source-output.c +index 761323e..bb89384 100644 +--- a/src/pulsecore/source-output.c ++++ b/src/pulsecore/source-output.c +@@ -1057,16 +1057,22 @@ pa_cvolume *pa_source_output_get_volume(pa_source_output *o, pa_cvolume *volume, + + /* Called from main context */ + void pa_source_output_set_mute(pa_source_output *o, bool mute, bool save) { ++ bool old_mute; ++ + pa_source_output_assert_ref(o); + pa_assert_ctl_context(); + pa_assert(PA_SOURCE_OUTPUT_IS_LINKED(o->state)); + +- if (!o->muted == !mute) { ++ old_mute = o->muted; ++ ++ if (mute == old_mute) { + o->save_muted |= save; + return; + } + + o->muted = mute; ++ pa_log_debug("The mute of source output %u changed from %s to %s.", o->index, pa_yes_no(old_mute), pa_yes_no(mute)); ++ + o->save_muted = save; + + pa_assert_se(pa_asyncmsgq_send(o->source->asyncmsgq, PA_MSGOBJECT(o), PA_SOURCE_OUTPUT_MESSAGE_SET_SOFT_MUTE, NULL, 0, NULL) == 0); diff --git a/meta-tizen/recipes-multimedia/pulseaudio/pulseaudio_5.0/0066-sink-source-Allow-calling-set_mute-during-initializa.patch b/meta-tizen/recipes-multimedia/pulseaudio/pulseaudio_5.0/0066-sink-source-Allow-calling-set_mute-during-initializa.patch new file mode 100644 index 0000000..02ac7b5 --- /dev/null +++ b/meta-tizen/recipes-multimedia/pulseaudio/pulseaudio_5.0/0066-sink-source-Allow-calling-set_mute-during-initializa.patch @@ -0,0 +1,61 @@ +From: Tanu Kaskinen +Date: Mon, 14 Apr 2014 14:43:23 +0300 +Subject: sink, source: Allow calling set_mute() during initialization + +Currently the alsa sink and source write directly to s->muted during +initialization, but I think it's better to avoid direct writes, and +use the set_mute() function instead, because that makes it easier to +figure out where s->muted is modified. This patch prevents the +set_mute() call from crashing in the state assertion. + +Change-Id: I12220fb2668723931bebbe1484f115016f1edf25 +Signed-off-by: Jaska Uimonen Signed-off-by: Jaska Uimonen +--- + src/pulsecore/sink.c | 4 +++- + src/pulsecore/source.c | 4 +++- + 2 files changed, 6 insertions(+), 2 deletions(-) + +diff --git a/src/pulsecore/sink.c b/src/pulsecore/sink.c +index 3eed550..7eb4cb4 100644 +--- a/src/pulsecore/sink.c ++++ b/src/pulsecore/sink.c +@@ -2285,7 +2285,6 @@ void pa_sink_set_mute(pa_sink *s, bool mute, bool save) { + + pa_sink_assert_ref(s); + pa_assert_ctl_context(); +- pa_assert(PA_SINK_IS_LINKED(s->state)); + + old_muted = s->muted; + +@@ -2300,6 +2299,9 @@ void pa_sink_set_mute(pa_sink *s, bool mute, bool save) { + if (!(s->flags & PA_SINK_DEFERRED_VOLUME) && s->set_mute) + s->set_mute(s); + ++ if (!PA_SINK_IS_LINKED(s->state)) ++ return; ++ + pa_log_debug("The mute of sink %s changed from %s to %s.", s->name, pa_yes_no(old_muted), pa_yes_no(mute)); + pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_SET_MUTE, NULL, 0, NULL) == 0); + pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE, s->index); +diff --git a/src/pulsecore/source.c b/src/pulsecore/source.c +index 4f4aea3..3b6ad44 100644 +--- a/src/pulsecore/source.c ++++ b/src/pulsecore/source.c +@@ -1803,7 +1803,6 @@ void pa_source_set_mute(pa_source *s, bool mute, bool save) { + + pa_source_assert_ref(s); + pa_assert_ctl_context(); +- pa_assert(PA_SOURCE_IS_LINKED(s->state)); + + old_muted = s->muted; + +@@ -1818,6 +1817,9 @@ void pa_source_set_mute(pa_source *s, bool mute, bool save) { + if (!(s->flags & PA_SOURCE_DEFERRED_VOLUME) && s->set_mute) + s->set_mute(s); + ++ if (!PA_SOURCE_IS_LINKED(s->state)) ++ return; ++ + pa_log_debug("The mute of source %s changed from %s to %s.", s->name, pa_yes_no(old_muted), pa_yes_no(mute)); + pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SOURCE_MESSAGE_SET_MUTE, NULL, 0, NULL) == 0); + pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_CHANGE, s->index); diff --git a/meta-tizen/recipes-multimedia/pulseaudio/pulseaudio_5.0/0067-echo-cancel-Remove-redundant-get_mute-callback.patch b/meta-tizen/recipes-multimedia/pulseaudio/pulseaudio_5.0/0067-echo-cancel-Remove-redundant-get_mute-callback.patch new file mode 100644 index 0000000..4962b8d --- /dev/null +++ b/meta-tizen/recipes-multimedia/pulseaudio/pulseaudio_5.0/0067-echo-cancel-Remove-redundant-get_mute-callback.patch @@ -0,0 +1,47 @@ +From: Tanu Kaskinen +Date: Mon, 14 Apr 2014 15:13:08 +0300 +Subject: echo-cancel: Remove redundant get_mute() callback + +The callback just called pa_source_output_get_mute(), which doesn't +have any side effects, and the return value wasn't used either, so +the callback was essentially a no-op. + +Change-Id: Ic16824b06393f59b55087842da64c7d09035b9e8 +Signed-off-by: Jaska Uimonen +--- + src/modules/echo-cancel/module-echo-cancel.c | 15 --------------- + 1 file changed, 15 deletions(-) + +diff --git a/src/modules/echo-cancel/module-echo-cancel.c b/src/modules/echo-cancel/module-echo-cancel.c +index 29eed13..8f791ce 100644 +--- a/src/modules/echo-cancel/module-echo-cancel.c ++++ b/src/modules/echo-cancel/module-echo-cancel.c +@@ -656,20 +656,6 @@ static void sink_set_mute_cb(pa_sink *s) { + pa_sink_input_set_mute(u->sink_input, s->muted, s->save_muted); + } + +-/* Called from main context */ +-static void source_get_mute_cb(pa_source *s) { +- struct userdata *u; +- +- pa_source_assert_ref(s); +- pa_assert_se(u = s->userdata); +- +- if (!PA_SOURCE_IS_LINKED(pa_source_get_state(s)) || +- !PA_SOURCE_OUTPUT_IS_LINKED(pa_source_output_get_state(u->source_output))) +- return; +- +- pa_source_output_get_mute(u->source_output); +-} +- + /* Called from source I/O thread context. */ + static void apply_diff_time(struct userdata *u, int64_t diff_time) { + int64_t diff; +@@ -1857,7 +1843,6 @@ int pa__init(pa_module*m) { + u->source->parent.process_msg = source_process_msg_cb; + u->source->set_state = source_set_state_cb; + u->source->update_requested_latency = source_update_requested_latency_cb; +- pa_source_set_get_mute_callback(u->source, source_get_mute_cb); + pa_source_set_set_mute_callback(u->source, source_set_mute_cb); + if (!u->use_volume_sharing) { + pa_source_set_get_volume_callback(u->source, source_get_volume_cb); diff --git a/meta-tizen/recipes-multimedia/pulseaudio/pulseaudio_5.0/0068-sink-source-Call-set_mute-from-mute_changed.patch b/meta-tizen/recipes-multimedia/pulseaudio/pulseaudio_5.0/0068-sink-source-Call-set_mute-from-mute_changed.patch new file mode 100644 index 0000000..42422ea --- /dev/null +++ b/meta-tizen/recipes-multimedia/pulseaudio/pulseaudio_5.0/0068-sink-source-Call-set_mute-from-mute_changed.patch @@ -0,0 +1,133 @@ +From: Tanu Kaskinen +Date: Tue, 15 Apr 2014 09:39:49 +0300 +Subject: sink, source: Call set_mute() from mute_changed() + +This refactoring reduces duplication, as mute_changed() used to do the +same things as set_mute(). Other benefits are improved logging +(set_mute() logs the mute change, mute_changed() used to not do that) +and the soft mute state is kept up to date, because set_mute() sends +the SET_MUTE message to the IO thread. + +The set_mute_in_progress flag is an extra precaution for preventing +recursion in case a sink/source implementation's set_mute() callback +causes mute_changed() to be called. Currently there are no such +implementations, but I think that would be a valid thing to do, so +some day there might be such implementation. + +Change-Id: I33c81f4034001f777c4533c2c63eada67548c683 +Signed-off-by: Jaska Uimonen +--- + src/pulsecore/sink.c | 19 ++++++++++++------- + src/pulsecore/sink.h | 2 ++ + src/pulsecore/source.c | 19 ++++++++++++------- + src/pulsecore/source.h | 2 ++ + 4 files changed, 28 insertions(+), 14 deletions(-) + +diff --git a/src/pulsecore/sink.c b/src/pulsecore/sink.c +index 7eb4cb4..4c348b5 100644 +--- a/src/pulsecore/sink.c ++++ b/src/pulsecore/sink.c +@@ -2296,8 +2296,11 @@ void pa_sink_set_mute(pa_sink *s, bool mute, bool save) { + s->muted = mute; + s->save_muted = save; + +- if (!(s->flags & PA_SINK_DEFERRED_VOLUME) && s->set_mute) ++ if (!(s->flags & PA_SINK_DEFERRED_VOLUME) && s->set_mute) { ++ s->set_mute_in_progress = true; + s->set_mute(s); ++ s->set_mute_in_progress = false; ++ } + + if (!PA_SINK_IS_LINKED(s->state)) + return; +@@ -2341,15 +2344,17 @@ void pa_sink_mute_changed(pa_sink *s, bool new_muted) { + pa_assert_ctl_context(); + pa_assert(PA_SINK_IS_LINKED(s->state)); + +- /* The sink implementor may call this if the volume changed to make sure everyone is notified */ +- +- if (s->muted == new_muted) ++ if (s->set_mute_in_progress) + return; + +- s->muted = new_muted; +- s->save_muted = true; ++ /* pa_sink_set_mute() does this same check, so this may appear redundant, ++ * but we must have this here also, because the save parameter of ++ * pa_sink_set_mute() would otherwise have unintended side effects (saving ++ * the mute state when it shouldn't be saved). */ ++ if (new_muted == s->muted) ++ return; + +- pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE, s->index); ++ pa_sink_set_mute(s, new_muted, true); + } + + /* Called from main thread */ +diff --git a/src/pulsecore/sink.h b/src/pulsecore/sink.h +index 41a439b..72437a4 100644 +--- a/src/pulsecore/sink.h ++++ b/src/pulsecore/sink.h +@@ -122,6 +122,8 @@ struct pa_sink { + + unsigned priority; + ++ bool set_mute_in_progress; ++ + /* Called when the main loop requests a state change. Called from + * main loop context. If returns -1 the state change will be + * inhibited */ +diff --git a/src/pulsecore/source.c b/src/pulsecore/source.c +index 3b6ad44..2f6aaad 100644 +--- a/src/pulsecore/source.c ++++ b/src/pulsecore/source.c +@@ -1814,8 +1814,11 @@ void pa_source_set_mute(pa_source *s, bool mute, bool save) { + s->muted = mute; + s->save_muted = save; + +- if (!(s->flags & PA_SOURCE_DEFERRED_VOLUME) && s->set_mute) ++ if (!(s->flags & PA_SOURCE_DEFERRED_VOLUME) && s->set_mute) { ++ s->set_mute_in_progress = true; + s->set_mute(s); ++ s->set_mute_in_progress = false; ++ } + + if (!PA_SOURCE_IS_LINKED(s->state)) + return; +@@ -1859,15 +1862,17 @@ void pa_source_mute_changed(pa_source *s, bool new_muted) { + pa_assert_ctl_context(); + pa_assert(PA_SOURCE_IS_LINKED(s->state)); + +- /* The source implementor may call this if the mute state changed to make sure everyone is notified */ +- +- if (s->muted == new_muted) ++ if (s->set_mute_in_progress) + return; + +- s->muted = new_muted; +- s->save_muted = true; ++ /* pa_source_set_mute() does this same check, so this may appear redundant, ++ * but we must have this here also, because the save parameter of ++ * pa_source_set_mute() would otherwise have unintended side effects ++ * (saving the mute state when it shouldn't be saved). */ ++ if (new_muted == s->muted) ++ return; + +- pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_CHANGE, s->index); ++ pa_source_set_mute(s, new_muted, true); + } + + /* Called from main thread */ +diff --git a/src/pulsecore/source.h b/src/pulsecore/source.h +index 6318595..ca2ed59 100644 +--- a/src/pulsecore/source.h ++++ b/src/pulsecore/source.h +@@ -118,6 +118,8 @@ struct pa_source { + + unsigned priority; + ++ bool set_mute_in_progress; ++ + /* Called when the main loop requests a state change. Called from + * main loop context. If returns -1 the state change will be + * inhibited */ diff --git a/meta-tizen/recipes-multimedia/pulseaudio/pulseaudio_5.0/0069-sink-source-Assign-to-s-muted-from-only-one-place.patch b/meta-tizen/recipes-multimedia/pulseaudio/pulseaudio_5.0/0069-sink-source-Assign-to-s-muted-from-only-one-place.patch new file mode 100644 index 0000000..c7332fd --- /dev/null +++ b/meta-tizen/recipes-multimedia/pulseaudio/pulseaudio_5.0/0069-sink-source-Assign-to-s-muted-from-only-one-place.patch @@ -0,0 +1,346 @@ +From: Tanu Kaskinen +Date: Mon, 14 Apr 2014 14:52:16 +0300 +Subject: sink, source: Assign to s->muted from only one place + +Forcing all mute changes to go through set_mute() makes it easier to +check where the muted field is changed, and it also allows us to have +only one place where notifications for changed mute are sent. + +Change-Id: Idb1bd6ef923a165e249d42265ebedc30a6c8fca4 +Signed-off-by: Jaska Uimonen +--- + src/modules/alsa/alsa-sink.c | 17 ++++++++++------- + src/modules/alsa/alsa-source.c | 17 ++++++++++------- + src/modules/module-solaris.c | 17 +++++++++++------ + src/pulsecore/sink.c | 26 ++++++++++---------------- + src/pulsecore/sink.h | 24 ++++++++++++++++++------ + src/pulsecore/source.c | 26 ++++++++++---------------- + src/pulsecore/source.h | 24 ++++++++++++++++++------ + 7 files changed, 87 insertions(+), 64 deletions(-) + +diff --git a/src/modules/alsa/alsa-sink.c b/src/modules/alsa/alsa-sink.c +index 03babb3..2bc27ff 100644 +--- a/src/modules/alsa/alsa-sink.c ++++ b/src/modules/alsa/alsa-sink.c +@@ -1401,18 +1401,17 @@ static void sink_write_volume_cb(pa_sink *s) { + } + } + +-static void sink_get_mute_cb(pa_sink *s) { ++static int sink_get_mute_cb(pa_sink *s, bool *mute) { + struct userdata *u = s->userdata; +- bool b; + + pa_assert(u); + pa_assert(u->mixer_path); + pa_assert(u->mixer_handle); + +- if (pa_alsa_path_get_mute(u->mixer_path, u->mixer_handle, &b) < 0) +- return; ++ if (pa_alsa_path_get_mute(u->mixer_path, u->mixer_handle, mute) < 0) ++ return -1; + +- s->muted = b; ++ return 0; + } + + static void sink_set_mute_cb(pa_sink *s) { +@@ -2399,8 +2398,12 @@ pa_sink *pa_alsa_sink_new(pa_module *m, pa_modargs *ma, const char*driver, pa_ca + if (u->sink->set_mute) + u->sink->set_mute(u->sink); + } else { +- if (u->sink->get_mute) +- u->sink->get_mute(u->sink); ++ if (u->sink->get_mute) { ++ bool mute; ++ ++ if (u->sink->get_mute(u->sink, &mute) >= 0) ++ pa_sink_set_mute(u->sink, mute, false); ++ } + } + + if ((data.volume_is_set || data.muted_is_set) && u->sink->write_volume) +diff --git a/src/modules/alsa/alsa-source.c b/src/modules/alsa/alsa-source.c +index 2e93e0f..e181a30 100644 +--- a/src/modules/alsa/alsa-source.c ++++ b/src/modules/alsa/alsa-source.c +@@ -1277,18 +1277,17 @@ static void source_write_volume_cb(pa_source *s) { + } + } + +-static void source_get_mute_cb(pa_source *s) { ++static int source_get_mute_cb(pa_source *s, bool *mute) { + struct userdata *u = s->userdata; +- bool b; + + pa_assert(u); + pa_assert(u->mixer_path); + pa_assert(u->mixer_handle); + +- if (pa_alsa_path_get_mute(u->mixer_path, u->mixer_handle, &b) < 0) +- return; ++ if (pa_alsa_path_get_mute(u->mixer_path, u->mixer_handle, mute) < 0) ++ return -1; + +- s->muted = b; ++ return 0; + } + + static void source_set_mute_cb(pa_source *s) { +@@ -2088,8 +2087,12 @@ pa_source *pa_alsa_source_new(pa_module *m, pa_modargs *ma, const char*driver, p + if (u->source->set_mute) + u->source->set_mute(u->source); + } else { +- if (u->source->get_mute) +- u->source->get_mute(u->source); ++ if (u->source->get_mute) { ++ bool mute; ++ ++ if (u->source->get_mute(u->source, &mute) >= 0) ++ pa_source_set_mute(u->source, mute, false); ++ } + } + + if ((data.volume_is_set || data.muted_is_set) && u->source->write_volume) +diff --git a/src/modules/module-solaris.c b/src/modules/module-solaris.c +index b4fa734..71a98e9 100644 +--- a/src/modules/module-solaris.c ++++ b/src/modules/module-solaris.c +@@ -571,18 +571,23 @@ static void sink_set_mute(pa_sink *s) { + } + } + +-static void sink_get_mute(pa_sink *s) { ++static int sink_get_mute(pa_sink *s, bool *mute) { + struct userdata *u = s->userdata; + audio_info_t info; + + pa_assert(u); + +- if (u->fd >= 0) { +- if (ioctl(u->fd, AUDIO_GETINFO, &info) < 0) +- pa_log("AUDIO_SETINFO: %s", pa_cstrerror(errno)); +- else +- s->muted = !!info.output_muted; ++ if (u->fd < 0) ++ return -1; ++ ++ if (ioctl(u->fd, AUDIO_GETINFO, &info) < 0) { ++ pa_log("AUDIO_GETINFO: %s", pa_cstrerror(errno)); ++ return -1; + } ++ ++ *mute = info.output_muted; ++ ++ return 0; + } + + static void process_rewind(struct userdata *u) { +diff --git a/src/pulsecore/sink.c b/src/pulsecore/sink.c +index 4c348b5..b64001b 100644 +--- a/src/pulsecore/sink.c ++++ b/src/pulsecore/sink.c +@@ -519,7 +519,7 @@ void pa_sink_set_write_volume_callback(pa_sink *s, pa_sink_cb_t cb) { + pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE, s->index); + } + +-void pa_sink_set_get_mute_callback(pa_sink *s, pa_sink_cb_t cb) { ++void pa_sink_set_get_mute_callback(pa_sink *s, pa_sink_get_mute_cb_t cb) { + pa_assert(s); + + s->get_mute = cb; +@@ -2317,21 +2317,15 @@ bool pa_sink_get_mute(pa_sink *s, bool force_refresh) { + pa_assert_ctl_context(); + pa_assert(PA_SINK_IS_LINKED(s->state)); + +- if (s->refresh_muted || force_refresh) { +- bool old_muted = s->muted; ++ if ((s->refresh_muted || force_refresh) && s->get_mute) { ++ bool mute; + +- if (!(s->flags & PA_SINK_DEFERRED_VOLUME) && s->get_mute) +- s->get_mute(s); +- +- pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_GET_MUTE, NULL, 0, NULL) == 0); +- +- if (old_muted != s->muted) { +- s->save_muted = true; +- +- pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE, s->index); +- +- /* Make sure the soft mute status stays in sync */ +- pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_SET_MUTE, NULL, 0, NULL) == 0); ++ if (s->flags & PA_SINK_DEFERRED_VOLUME) { ++ if (pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_GET_MUTE, &mute, 0, NULL) >= 0) ++ pa_sink_mute_changed(s, mute); ++ } else { ++ if (s->get_mute(s, &mute) >= 0) ++ pa_sink_mute_changed(s, mute); + } + } + +@@ -2848,7 +2842,7 @@ int pa_sink_process_msg(pa_msgobject *o, int code, void *userdata, int64_t offse + case PA_SINK_MESSAGE_GET_MUTE: + + if (s->flags & PA_SINK_DEFERRED_VOLUME && s->get_mute) +- s->get_mute(s); ++ return s->get_mute(s, userdata); + + return 0; + +diff --git a/src/pulsecore/sink.h b/src/pulsecore/sink.h +index 72437a4..e069d02 100644 +--- a/src/pulsecore/sink.h ++++ b/src/pulsecore/sink.h +@@ -59,6 +59,8 @@ static inline bool PA_SINK_IS_LINKED(pa_sink_state_t x) { + /* A generic definition for void callback functions */ + typedef void(*pa_sink_cb_t)(pa_sink *s); + ++typedef int (*pa_sink_get_mute_cb_t)(pa_sink *s, bool *mute); ++ + struct pa_sink { + pa_msgobject parent; + +@@ -195,14 +197,24 @@ struct pa_sink { + * set this callback. */ + pa_sink_cb_t write_volume; /* may be NULL */ + +- /* Called when the mute setting is queried. A PA_SINK_MESSAGE_GET_MUTE +- * message will also be sent. Called from IO thread if PA_SINK_DEFERRED_VOLUME +- * flag is set otherwise from main loop context. If refresh_mute is false +- * neither this function is called nor a message is sent. ++ /* If the sink mute can change "spontaneously" (i.e. initiated by the sink ++ * implementation, not by someone else calling pa_sink_set_mute()), then ++ * the sink implementation can notify about changed mute either by calling ++ * pa_sink_mute_changed() or by calling pa_sink_get_mute() with ++ * force_refresh=true. If the implementation chooses the latter approach, ++ * it should implement the get_mute callback. Otherwise get_mute can be ++ * NULL. ++ * ++ * This is called when pa_sink_get_mute() is called with ++ * force_refresh=true. This is called from the IO thread if the ++ * PA_SINK_DEFERRED_VOLUME flag is set, otherwise this is called from the ++ * main thread. On success, the implementation is expected to return 0 and ++ * set the mute parameter that is passed as a reference. On failure, the ++ * implementation is expected to return -1. + * + * You must use the function pa_sink_set_get_mute_callback() to + * set this callback. */ +- pa_sink_cb_t get_mute; /* may be NULL */ ++ pa_sink_get_mute_cb_t get_mute; + + /* Called when the mute setting shall be changed. A PA_SINK_MESSAGE_SET_MUTE + * message will also be sent. Called from IO thread if PA_SINK_DEFERRED_VOLUME +@@ -386,7 +398,7 @@ pa_sink* pa_sink_new( + void pa_sink_set_get_volume_callback(pa_sink *s, pa_sink_cb_t cb); + void pa_sink_set_set_volume_callback(pa_sink *s, pa_sink_cb_t cb); + void pa_sink_set_write_volume_callback(pa_sink *s, pa_sink_cb_t cb); +-void pa_sink_set_get_mute_callback(pa_sink *s, pa_sink_cb_t cb); ++void pa_sink_set_get_mute_callback(pa_sink *s, pa_sink_get_mute_cb_t cb); + void pa_sink_set_set_mute_callback(pa_sink *s, pa_sink_cb_t cb); + void pa_sink_enable_decibel_volume(pa_sink *s, bool enable); + +diff --git a/src/pulsecore/source.c b/src/pulsecore/source.c +index 2f6aaad..24e8be9 100644 +--- a/src/pulsecore/source.c ++++ b/src/pulsecore/source.c +@@ -467,7 +467,7 @@ void pa_source_set_write_volume_callback(pa_source *s, pa_source_cb_t cb) { + pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_CHANGE, s->index); + } + +-void pa_source_set_get_mute_callback(pa_source *s, pa_source_cb_t cb) { ++void pa_source_set_get_mute_callback(pa_source *s, pa_source_get_mute_cb_t cb) { + pa_assert(s); + + s->get_mute = cb; +@@ -1835,21 +1835,15 @@ bool pa_source_get_mute(pa_source *s, bool force_refresh) { + pa_assert_ctl_context(); + pa_assert(PA_SOURCE_IS_LINKED(s->state)); + +- if (s->refresh_muted || force_refresh) { +- bool old_muted = s->muted; ++ if ((s->refresh_muted || force_refresh) && s->get_mute) { ++ bool mute; + +- if (!(s->flags & PA_SOURCE_DEFERRED_VOLUME) && s->get_mute) +- s->get_mute(s); +- +- pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SOURCE_MESSAGE_GET_MUTE, NULL, 0, NULL) == 0); +- +- if (old_muted != s->muted) { +- s->save_muted = true; +- +- pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_CHANGE, s->index); +- +- /* Make sure the soft mute status stays in sync */ +- pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SOURCE_MESSAGE_SET_MUTE, NULL, 0, NULL) == 0); ++ if (s->flags & PA_SOURCE_DEFERRED_VOLUME) { ++ if (pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SOURCE_MESSAGE_GET_MUTE, &mute, 0, NULL) >= 0) ++ pa_source_mute_changed(s, mute); ++ } else { ++ if (s->get_mute(s, &mute) >= 0) ++ pa_source_mute_changed(s, mute); + } + } + +@@ -2136,7 +2130,7 @@ int pa_source_process_msg(pa_msgobject *object, int code, void *userdata, int64_ + case PA_SOURCE_MESSAGE_GET_MUTE: + + if (s->flags & PA_SOURCE_DEFERRED_VOLUME && s->get_mute) +- s->get_mute(s); ++ return s->get_mute(s, userdata); + + return 0; + +diff --git a/src/pulsecore/source.h b/src/pulsecore/source.h +index ca2ed59..83bc2dd 100644 +--- a/src/pulsecore/source.h ++++ b/src/pulsecore/source.h +@@ -58,6 +58,8 @@ static inline bool PA_SOURCE_IS_LINKED(pa_source_state_t x) { + /* A generic definition for void callback functions */ + typedef void(*pa_source_cb_t)(pa_source *s); + ++typedef int (*pa_source_get_mute_cb_t)(pa_source *s, bool *mute); ++ + struct pa_source { + pa_msgobject parent; + +@@ -158,14 +160,24 @@ struct pa_source { + * set this callback. */ + pa_source_cb_t write_volume; /* may be NULL */ + +- /* Called when the mute setting is queried. Called from main loop +- * context. If this is NULL a PA_SOURCE_MESSAGE_GET_MUTE message +- * will be sent to the IO thread instead. If refresh_mute is +- * false neither this function is called nor a message is sent. ++ /* If the source mute can change "spontaneously" (i.e. initiated by the ++ * source implementation, not by someone else calling ++ * pa_source_set_mute()), then the source implementation can notify about ++ * changed mute either by calling pa_source_mute_changed() or by calling ++ * pa_source_get_mute() with force_refresh=true. If the implementation ++ * chooses the latter approach, it should implement the get_mute callback. ++ * Otherwise get_mute can be NULL. ++ * ++ * This is called when pa_source_get_mute() is called with ++ * force_refresh=true. This is called from the IO thread if the ++ * PA_SOURCE_DEFERRED_VOLUME flag is set, otherwise this is called from the ++ * main thread. On success, the implementation is expected to return 0 and ++ * set the mute parameter that is passed as a reference. On failure, the ++ * implementation is expected to return -1. + * + * You must use the function pa_source_set_get_mute_callback() to + * set this callback. */ +- pa_source_cb_t get_mute; /* may be NULL */ ++ pa_source_get_mute_cb_t get_mute; + + /* Called when the mute setting shall be changed. Called from main + * loop context. If this is NULL a PA_SOURCE_MESSAGE_SET_MUTE +@@ -316,7 +328,7 @@ pa_source* pa_source_new( + void pa_source_set_get_volume_callback(pa_source *s, pa_source_cb_t cb); + void pa_source_set_set_volume_callback(pa_source *s, pa_source_cb_t cb); + void pa_source_set_write_volume_callback(pa_source *s, pa_source_cb_t cb); +-void pa_source_set_get_mute_callback(pa_source *s, pa_source_cb_t cb); ++void pa_source_set_get_mute_callback(pa_source *s, pa_source_get_mute_cb_t cb); + void pa_source_set_set_mute_callback(pa_source *s, pa_source_cb_t cb); + void pa_source_enable_decibel_volume(pa_source *s, bool enable); + diff --git a/meta-tizen/recipes-multimedia/pulseaudio/pulseaudio_5.0/0070-sink-input-source-output-Remove-redundant-get_mute-f.patch b/meta-tizen/recipes-multimedia/pulseaudio/pulseaudio_5.0/0070-sink-input-source-output-Remove-redundant-get_mute-f.patch new file mode 100644 index 0000000..9b25a3d --- /dev/null +++ b/meta-tizen/recipes-multimedia/pulseaudio/pulseaudio_5.0/0070-sink-input-source-output-Remove-redundant-get_mute-f.patch @@ -0,0 +1,210 @@ +From: Tanu Kaskinen +Date: Mon, 14 Apr 2014 15:24:31 +0300 +Subject: sink-input, source-output: Remove redundant get_mute() functions + +The functions just return the muted value. Callers can as well read +the struct field directly, it's simpler that way. + +Change-Id: I368f7e09cdf522039a6573e5002f7544b4e840d3 +Signed-off-by: Jaska Uimonen +--- + src/modules/dbus/iface-stream.c | 4 ++-- + src/modules/module-role-cork.c | 9 ++++----- + src/modules/module-stream-restore.c | 4 ++-- + src/pulsecore/cli-text.c | 4 ++-- + src/pulsecore/protocol-native.c | 4 ++-- + src/pulsecore/sink-input.c | 9 --------- + src/pulsecore/sink-input.h | 1 - + src/pulsecore/source-output.c | 9 --------- + src/pulsecore/source-output.h | 1 - + 9 files changed, 12 insertions(+), 33 deletions(-) + +diff --git a/src/modules/dbus/iface-stream.c b/src/modules/dbus/iface-stream.c +index 1cff95e..4cbcd74 100644 +--- a/src/modules/dbus/iface-stream.c ++++ b/src/modules/dbus/iface-stream.c +@@ -765,7 +765,7 @@ static void subscription_cb(pa_core *c, pa_subscription_event_type_t t, uint32_t + } + } + +- new_mute = pa_sink_input_get_mute(s->sink_input); ++ new_mute = s->sink_input->muted; + + if (s->mute != new_mute) { + s->mute = new_mute; +@@ -861,7 +861,7 @@ pa_dbusiface_stream *pa_dbusiface_stream_new_playback(pa_dbusiface_core *core, p + else + pa_cvolume_init(&s->volume); + +- s->mute = pa_sink_input_get_mute(sink_input); ++ s->mute = sink_input->muted; + s->proplist = pa_proplist_copy(sink_input->proplist); + s->dbus_protocol = pa_dbus_protocol_get(sink_input->core); + s->subscription = pa_subscription_new(sink_input->core, PA_SUBSCRIPTION_MASK_SINK_INPUT, subscription_cb, s); +diff --git a/src/modules/module-role-cork.c b/src/modules/module-role-cork.c +index 6573cd6..8ca2109 100644 +--- a/src/modules/module-role-cork.c ++++ b/src/modules/module-role-cork.c +@@ -102,7 +102,7 @@ static inline void apply_cork_to_sink(struct userdata *u, pa_sink *s, pa_sink_in + pa_sink_assert_ref(s); + + for (j = PA_SINK_INPUT(pa_idxset_first(s->inputs, &idx)); j; j = PA_SINK_INPUT(pa_idxset_next(s->inputs, &idx))) { +- bool corked, muted, corked_here; ++ bool corked, corked_here; + const char *role; + + if (j == ignore) +@@ -119,10 +119,9 @@ static inline void apply_cork_to_sink(struct userdata *u, pa_sink *s, pa_sink_in + continue; + + corked = (pa_sink_input_get_state(j) == PA_SINK_INPUT_CORKED); +- muted = pa_sink_input_get_mute(j); + corked_here = !!pa_hashmap_get(u->cork_state, j); + +- if (cork && !corked && !muted) { ++ if (cork && !corked && !j->muted) { + pa_log_debug("Found a '%s' stream that should be corked/muted.", cork_role); + if (!corked_here) + pa_hashmap_put(u->cork_state, j, PA_INT_TO_PTR(1)); +@@ -131,9 +130,9 @@ static inline void apply_cork_to_sink(struct userdata *u, pa_sink *s, pa_sink_in + } else if (!cork) { + pa_hashmap_remove(u->cork_state, j); + +- if (corked_here && (corked || muted)) { ++ if (corked_here && (corked || j->muted)) { + pa_log_debug("Found a '%s' stream that should be uncorked/unmuted.", cork_role); +- if (muted) ++ if (j->muted) + pa_sink_input_set_mute(j, false, false); + if (corked) + pa_sink_input_send_event(j, PA_STREAM_EVENT_REQUEST_UNCORK, NULL); +diff --git a/src/modules/module-stream-restore.c b/src/modules/module-stream-restore.c +index 68968c9..4fc5645 100644 +--- a/src/modules/module-stream-restore.c ++++ b/src/modules/module-stream-restore.c +@@ -1303,7 +1303,7 @@ static void subscribe_callback(pa_core *c, pa_subscription_event_type_t t, uint3 + } + + if (sink_input->save_muted) { +- entry->muted = pa_sink_input_get_mute(sink_input); ++ entry->muted = sink_input->muted; + entry->muted_valid = true; + + mute_updated = !created_new_entry && (!old->muted_valid || entry->muted != old->muted); +@@ -1353,7 +1353,7 @@ static void subscribe_callback(pa_core *c, pa_subscription_event_type_t t, uint3 + } + + if (source_output->save_muted) { +- entry->muted = pa_source_output_get_mute(source_output); ++ entry->muted = source_output->muted; + entry->muted_valid = true; + + mute_updated = !created_new_entry && (!old->muted_valid || entry->muted != old->muted); +diff --git a/src/pulsecore/cli-text.c b/src/pulsecore/cli-text.c +index c7db0a6..2992ae8 100644 +--- a/src/pulsecore/cli-text.c ++++ b/src/pulsecore/cli-text.c +@@ -536,7 +536,7 @@ char *pa_source_output_list_to_string(pa_core *c) { + state_table[pa_source_output_get_state(o)], + o->source->index, o->source->name, + volume_str, +- pa_yes_no(pa_source_output_get_mute(o)), ++ pa_yes_no(o->muted), + (double) pa_source_output_get_latency(o, NULL) / PA_USEC_PER_MSEC, + clt, + pa_sample_spec_snprint(ss, sizeof(ss), &o->sample_spec), +@@ -634,7 +634,7 @@ char *pa_sink_input_list_to_string(pa_core *c) { + state_table[pa_sink_input_get_state(i)], + i->sink->index, i->sink->name, + volume_str, +- pa_yes_no(pa_sink_input_get_mute(i)), ++ pa_yes_no(i->muted), + (double) pa_sink_input_get_latency(i, NULL) / PA_USEC_PER_MSEC, + clt, + pa_sample_spec_snprint(ss, sizeof(ss), &i->sample_spec), +diff --git a/src/pulsecore/protocol-native.c b/src/pulsecore/protocol-native.c +index 4304cd4..21e02fe 100644 +--- a/src/pulsecore/protocol-native.c ++++ b/src/pulsecore/protocol-native.c +@@ -3378,7 +3378,7 @@ static void sink_input_fill_tagstruct(pa_native_connection *c, pa_tagstruct *t, + pa_tagstruct_puts(t, pa_resample_method_to_string(pa_sink_input_get_resample_method(s))); + pa_tagstruct_puts(t, s->driver); + if (c->version >= 11) +- pa_tagstruct_put_boolean(t, pa_sink_input_get_mute(s)); ++ pa_tagstruct_put_boolean(t, s->muted); + if (c->version >= 13) + pa_tagstruct_put_proplist(t, s->proplist); + if (c->version >= 19) +@@ -3425,7 +3425,7 @@ static void source_output_fill_tagstruct(pa_native_connection *c, pa_tagstruct * + pa_tagstruct_put_boolean(t, (pa_source_output_get_state(s) == PA_SOURCE_OUTPUT_CORKED)); + if (c->version >= 22) { + pa_tagstruct_put_cvolume(t, &v); +- pa_tagstruct_put_boolean(t, pa_source_output_get_mute(s)); ++ pa_tagstruct_put_boolean(t, s->muted); + pa_tagstruct_put_boolean(t, has_volume); + pa_tagstruct_put_boolean(t, s->volume_writable); + pa_tagstruct_put_format_info(t, s->format); +diff --git a/src/pulsecore/sink-input.c b/src/pulsecore/sink-input.c +index f706acc..a274620 100644 +--- a/src/pulsecore/sink-input.c ++++ b/src/pulsecore/sink-input.c +@@ -1485,15 +1485,6 @@ void pa_sink_input_set_mute(pa_sink_input *i, bool mute, bool save) { + pa_subscription_post(i->core, PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_CHANGE, i->index); + } + +-/* Called from main context */ +-bool pa_sink_input_get_mute(pa_sink_input *i) { +- pa_sink_input_assert_ref(i); +- pa_assert_ctl_context(); +- pa_assert(PA_SINK_INPUT_IS_LINKED(i->state)); +- +- return i->muted; +-} +- + /* Called from main thread */ + void pa_sink_input_update_proplist(pa_sink_input *i, pa_update_mode_t mode, pa_proplist *p) { + pa_sink_input_assert_ref(i); +diff --git a/src/pulsecore/sink-input.h b/src/pulsecore/sink-input.h +index 4e7b229..1bd3eee 100644 +--- a/src/pulsecore/sink-input.h ++++ b/src/pulsecore/sink-input.h +@@ -387,7 +387,6 @@ int pa_sink_input_remove_volume_factor(pa_sink_input *i, const char *key); + pa_cvolume *pa_sink_input_get_volume(pa_sink_input *i, pa_cvolume *volume, bool absolute); + + void pa_sink_input_set_mute(pa_sink_input *i, bool mute, bool save); +-bool pa_sink_input_get_mute(pa_sink_input *i); + + void pa_sink_input_set_volume_ramp(pa_sink_input *i, const pa_cvolume_ramp *ramp, bool send_msg, bool save); + +diff --git a/src/pulsecore/source-output.c b/src/pulsecore/source-output.c +index bb89384..d3888df 100644 +--- a/src/pulsecore/source-output.c ++++ b/src/pulsecore/source-output.c +@@ -1084,15 +1084,6 @@ void pa_source_output_set_mute(pa_source_output *o, bool mute, bool save) { + pa_subscription_post(o->core, PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT|PA_SUBSCRIPTION_EVENT_CHANGE, o->index); + } + +-/* Called from main context */ +-bool pa_source_output_get_mute(pa_source_output *o) { +- pa_source_output_assert_ref(o); +- pa_assert_ctl_context(); +- pa_assert(PA_SOURCE_OUTPUT_IS_LINKED(o->state)); +- +- return o->muted; +-} +- + /* Called from main thread */ + void pa_source_output_update_proplist(pa_source_output *o, pa_update_mode_t mode, pa_proplist *p) { + pa_source_output_assert_ref(o); +diff --git a/src/pulsecore/source-output.h b/src/pulsecore/source-output.h +index 27d6fd4..73170d3 100644 +--- a/src/pulsecore/source-output.h ++++ b/src/pulsecore/source-output.h +@@ -318,7 +318,6 @@ void pa_source_output_set_volume(pa_source_output *o, const pa_cvolume *volume, + pa_cvolume *pa_source_output_get_volume(pa_source_output *o, pa_cvolume *volume, bool absolute); + + void pa_source_output_set_mute(pa_source_output *o, bool mute, bool save); +-bool pa_source_output_get_mute(pa_source_output *o); + + void pa_source_output_update_proplist(pa_source_output *o, pa_update_mode_t mode, pa_proplist *p); + diff --git a/meta-tizen/recipes-multimedia/pulseaudio/pulseaudio_5.0/0071-solaris-tunnel-Remove-some-redundant-boolean-convers.patch b/meta-tizen/recipes-multimedia/pulseaudio/pulseaudio_5.0/0071-solaris-tunnel-Remove-some-redundant-boolean-convers.patch new file mode 100644 index 0000000..4b4acfe --- /dev/null +++ b/meta-tizen/recipes-multimedia/pulseaudio/pulseaudio_5.0/0071-solaris-tunnel-Remove-some-redundant-boolean-convers.patch @@ -0,0 +1,37 @@ +From: Tanu Kaskinen +Date: Mon, 14 Apr 2014 15:34:57 +0300 +Subject: solaris, tunnel: Remove some redundant boolean conversions + +Change-Id: Ibc922f8455a3ccb6c71289e70cd474464930643e +Signed-off-by: Jaska Uimonen +--- + src/modules/module-solaris.c | 2 +- + src/modules/module-tunnel.c | 2 +- + 2 files changed, 2 insertions(+), 2 deletions(-) + +diff --git a/src/modules/module-solaris.c b/src/modules/module-solaris.c +index 71a98e9..4f11000 100644 +--- a/src/modules/module-solaris.c ++++ b/src/modules/module-solaris.c +@@ -564,7 +564,7 @@ static void sink_set_mute(pa_sink *s) { + if (u->fd >= 0) { + AUDIO_INITINFO(&info); + +- info.output_muted = !!s->muted; ++ info.output_muted = s->muted; + + if (ioctl(u->fd, AUDIO_SETINFO, &info) < 0) + pa_log("AUDIO_SETINFO: %s", pa_cstrerror(errno)); +diff --git a/src/modules/module-tunnel.c b/src/modules/module-tunnel.c +index 1ddfd25..0fc3d73 100644 +--- a/src/modules/module-tunnel.c ++++ b/src/modules/module-tunnel.c +@@ -1906,7 +1906,7 @@ static void sink_set_mute(pa_sink *sink) { + pa_tagstruct_putu32(t, PA_COMMAND_SET_SINK_INPUT_MUTE); + pa_tagstruct_putu32(t, u->ctag++); + pa_tagstruct_putu32(t, u->device_index); +- pa_tagstruct_put_boolean(t, !!sink->muted); ++ pa_tagstruct_put_boolean(t, sink->muted); + pa_pstream_send_tagstruct(u->pstream, t); + } + diff --git a/meta-tizen/recipes-multimedia/pulseaudio/pulseaudio_5.0/0072-sink-source-Add-hooks-for-volume-changes.patch b/meta-tizen/recipes-multimedia/pulseaudio/pulseaudio_5.0/0072-sink-source-Add-hooks-for-volume-changes.patch new file mode 100644 index 0000000..08fbc83 --- /dev/null +++ b/meta-tizen/recipes-multimedia/pulseaudio/pulseaudio_5.0/0072-sink-source-Add-hooks-for-volume-changes.patch @@ -0,0 +1,52 @@ +From: Tanu Kaskinen +Date: Sun, 9 Mar 2014 13:36:04 +0200 +Subject: sink, source: Add hooks for volume changes + +Change-Id: Id4389a38e601dee3f84d7fad0583a7dc87108e87 +Signed-off-by: Jaska Uimonen +--- + src/pulsecore/core.h | 2 ++ + src/pulsecore/sink.c | 1 + + src/pulsecore/source.c | 1 + + 3 files changed, 4 insertions(+) + +diff --git a/src/pulsecore/core.h b/src/pulsecore/core.h +index e1cd18f..85f1b81 100644 +--- a/src/pulsecore/core.h ++++ b/src/pulsecore/core.h +@@ -76,6 +76,7 @@ typedef enum pa_core_hook { + PA_CORE_HOOK_SINK_PROPLIST_CHANGED, + PA_CORE_HOOK_SINK_PORT_CHANGED, + PA_CORE_HOOK_SINK_FLAGS_CHANGED, ++ PA_CORE_HOOK_SINK_VOLUME_CHANGED, + PA_CORE_HOOK_SOURCE_NEW, + PA_CORE_HOOK_SOURCE_FIXATE, + PA_CORE_HOOK_SOURCE_PUT, +@@ -85,6 +86,7 @@ typedef enum pa_core_hook { + PA_CORE_HOOK_SOURCE_PROPLIST_CHANGED, + PA_CORE_HOOK_SOURCE_PORT_CHANGED, + PA_CORE_HOOK_SOURCE_FLAGS_CHANGED, ++ PA_CORE_HOOK_SOURCE_VOLUME_CHANGED, + PA_CORE_HOOK_SINK_INPUT_NEW, + PA_CORE_HOOK_SINK_INPUT_FIXATE, + PA_CORE_HOOK_SINK_INPUT_PUT, +diff --git a/src/pulsecore/sink.c b/src/pulsecore/sink.c +index b64001b..f8e5449 100644 +--- a/src/pulsecore/sink.c ++++ b/src/pulsecore/sink.c +@@ -3907,4 +3907,5 @@ void pa_sink_set_reference_volume_direct(pa_sink *s, const pa_cvolume *volume) { + s->flags & PA_SINK_DECIBEL_VOLUME)); + + pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE, s->index); ++ pa_hook_fire(&s->core->hooks[PA_CORE_HOOK_SINK_VOLUME_CHANGED], s); + } +diff --git a/src/pulsecore/source.c b/src/pulsecore/source.c +index 24e8be9..8a43708 100644 +--- a/src/pulsecore/source.c ++++ b/src/pulsecore/source.c +@@ -2900,4 +2900,5 @@ void pa_source_set_reference_volume_direct(pa_source *s, const pa_cvolume *volum + s->flags & PA_SOURCE_DECIBEL_VOLUME)); + + pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_CHANGE, s->index); ++ pa_hook_fire(&s->core->hooks[PA_CORE_HOOK_SOURCE_VOLUME_CHANGED], s); + } diff --git a/meta-tizen/recipes-multimedia/pulseaudio/pulseaudio_5.0/0073-sink-input-source-output-Add-hooks-for-volume-change.patch b/meta-tizen/recipes-multimedia/pulseaudio/pulseaudio_5.0/0073-sink-input-source-output-Add-hooks-for-volume-change.patch new file mode 100644 index 0000000..9cf1c67 --- /dev/null +++ b/meta-tizen/recipes-multimedia/pulseaudio/pulseaudio_5.0/0073-sink-input-source-output-Add-hooks-for-volume-change.patch @@ -0,0 +1,52 @@ +From: Tanu Kaskinen +Date: Mon, 7 Apr 2014 14:22:43 +0300 +Subject: sink-input, source-output: Add hooks for volume changes + +Change-Id: I89c6f2934762caa2c49c70c0446c14d0de58a10e +Signed-off-by: Jaska Uimonen +--- + src/pulsecore/core.h | 2 ++ + src/pulsecore/sink-input.c | 1 + + src/pulsecore/source-output.c | 1 + + 3 files changed, 4 insertions(+) + +diff --git a/src/pulsecore/core.h b/src/pulsecore/core.h +index 85f1b81..a839898 100644 +--- a/src/pulsecore/core.h ++++ b/src/pulsecore/core.h +@@ -97,6 +97,7 @@ typedef enum pa_core_hook { + PA_CORE_HOOK_SINK_INPUT_MOVE_FAIL, + PA_CORE_HOOK_SINK_INPUT_STATE_CHANGED, + PA_CORE_HOOK_SINK_INPUT_PROPLIST_CHANGED, ++ PA_CORE_HOOK_SINK_INPUT_VOLUME_CHANGED, + PA_CORE_HOOK_SINK_INPUT_SEND_EVENT, + PA_CORE_HOOK_SOURCE_OUTPUT_NEW, + PA_CORE_HOOK_SOURCE_OUTPUT_FIXATE, +@@ -108,6 +109,7 @@ typedef enum pa_core_hook { + PA_CORE_HOOK_SOURCE_OUTPUT_MOVE_FAIL, + PA_CORE_HOOK_SOURCE_OUTPUT_STATE_CHANGED, + PA_CORE_HOOK_SOURCE_OUTPUT_PROPLIST_CHANGED, ++ PA_CORE_HOOK_SOURCE_OUTPUT_VOLUME_CHANGED, + PA_CORE_HOOK_SOURCE_OUTPUT_SEND_EVENT, + PA_CORE_HOOK_CLIENT_NEW, + PA_CORE_HOOK_CLIENT_PUT, +diff --git a/src/pulsecore/sink-input.c b/src/pulsecore/sink-input.c +index a274620..3024539 100644 +--- a/src/pulsecore/sink-input.c ++++ b/src/pulsecore/sink-input.c +@@ -2326,4 +2326,5 @@ void pa_sink_input_set_volume_direct(pa_sink_input *i, const pa_cvolume *volume) + i->volume_changed(i); + + pa_subscription_post(i->core, PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_CHANGE, i->index); ++ pa_hook_fire(&i->core->hooks[PA_CORE_HOOK_SINK_INPUT_VOLUME_CHANGED], i); + } +diff --git a/src/pulsecore/source-output.c b/src/pulsecore/source-output.c +index d3888df..18a1478 100644 +--- a/src/pulsecore/source-output.c ++++ b/src/pulsecore/source-output.c +@@ -1692,4 +1692,5 @@ void pa_source_output_set_volume_direct(pa_source_output *o, const pa_cvolume *v + o->volume_changed(o); + + pa_subscription_post(o->core, PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT|PA_SUBSCRIPTION_EVENT_CHANGE, o->index); ++ pa_hook_fire(&o->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_VOLUME_CHANGED], o); + } diff --git a/meta-tizen/recipes-multimedia/pulseaudio/pulseaudio_5.0/0074-sink-source-Add-hooks-for-mute-changes.patch b/meta-tizen/recipes-multimedia/pulseaudio/pulseaudio_5.0/0074-sink-source-Add-hooks-for-mute-changes.patch new file mode 100644 index 0000000..ddd8514 --- /dev/null +++ b/meta-tizen/recipes-multimedia/pulseaudio/pulseaudio_5.0/0074-sink-source-Add-hooks-for-mute-changes.patch @@ -0,0 +1,56 @@ +From: Tanu Kaskinen +Date: Tue, 15 Apr 2014 11:10:24 +0300 +Subject: sink, source: Add hooks for mute changes + +Change-Id: I1203c1199fea0e93f1a61391695b12ab8af66180 +Signed-off-by: Jaska Uimonen +--- + src/pulsecore/core.h | 2 ++ + src/pulsecore/sink.c | 1 + + src/pulsecore/source.c | 1 + + 3 files changed, 4 insertions(+) + +diff --git a/src/pulsecore/core.h b/src/pulsecore/core.h +index a839898..af7fa50 100644 +--- a/src/pulsecore/core.h ++++ b/src/pulsecore/core.h +@@ -77,6 +77,7 @@ typedef enum pa_core_hook { + PA_CORE_HOOK_SINK_PORT_CHANGED, + PA_CORE_HOOK_SINK_FLAGS_CHANGED, + PA_CORE_HOOK_SINK_VOLUME_CHANGED, ++ PA_CORE_HOOK_SINK_MUTE_CHANGED, + PA_CORE_HOOK_SOURCE_NEW, + PA_CORE_HOOK_SOURCE_FIXATE, + PA_CORE_HOOK_SOURCE_PUT, +@@ -87,6 +88,7 @@ typedef enum pa_core_hook { + PA_CORE_HOOK_SOURCE_PORT_CHANGED, + PA_CORE_HOOK_SOURCE_FLAGS_CHANGED, + PA_CORE_HOOK_SOURCE_VOLUME_CHANGED, ++ PA_CORE_HOOK_SOURCE_MUTE_CHANGED, + PA_CORE_HOOK_SINK_INPUT_NEW, + PA_CORE_HOOK_SINK_INPUT_FIXATE, + PA_CORE_HOOK_SINK_INPUT_PUT, +diff --git a/src/pulsecore/sink.c b/src/pulsecore/sink.c +index f8e5449..695e471 100644 +--- a/src/pulsecore/sink.c ++++ b/src/pulsecore/sink.c +@@ -2308,6 +2308,7 @@ void pa_sink_set_mute(pa_sink *s, bool mute, bool save) { + pa_log_debug("The mute of sink %s changed from %s to %s.", s->name, pa_yes_no(old_muted), pa_yes_no(mute)); + pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_SET_MUTE, NULL, 0, NULL) == 0); + pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE, s->index); ++ pa_hook_fire(&s->core->hooks[PA_CORE_HOOK_SINK_MUTE_CHANGED], s); + } + + /* Called from main thread */ +diff --git a/src/pulsecore/source.c b/src/pulsecore/source.c +index 8a43708..0fddfaa 100644 +--- a/src/pulsecore/source.c ++++ b/src/pulsecore/source.c +@@ -1826,6 +1826,7 @@ void pa_source_set_mute(pa_source *s, bool mute, bool save) { + pa_log_debug("The mute of source %s changed from %s to %s.", s->name, pa_yes_no(old_muted), pa_yes_no(mute)); + pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SOURCE_MESSAGE_SET_MUTE, NULL, 0, NULL) == 0); + pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_CHANGE, s->index); ++ pa_hook_fire(&s->core->hooks[PA_CORE_HOOK_SOURCE_MUTE_CHANGED], s); + } + + /* Called from main thread */ diff --git a/meta-tizen/recipes-multimedia/pulseaudio/pulseaudio_5.0/0075-sink-input-source-output-Add-hooks-for-mute-changes.patch b/meta-tizen/recipes-multimedia/pulseaudio/pulseaudio_5.0/0075-sink-input-source-output-Add-hooks-for-mute-changes.patch new file mode 100644 index 0000000..2607c14 --- /dev/null +++ b/meta-tizen/recipes-multimedia/pulseaudio/pulseaudio_5.0/0075-sink-input-source-output-Add-hooks-for-mute-changes.patch @@ -0,0 +1,56 @@ +From: Tanu Kaskinen +Date: Tue, 15 Apr 2014 11:27:53 +0300 +Subject: sink-input, source-output: Add hooks for mute changes + +Change-Id: I256cfa27ffa6addb35640266b73f1fe07a483203 +Signed-off-by: Jaska Uimonen +--- + src/pulsecore/core.h | 2 ++ + src/pulsecore/sink-input.c | 1 + + src/pulsecore/source-output.c | 1 + + 3 files changed, 4 insertions(+) + +diff --git a/src/pulsecore/core.h b/src/pulsecore/core.h +index af7fa50..1f042b9 100644 +--- a/src/pulsecore/core.h ++++ b/src/pulsecore/core.h +@@ -100,6 +100,7 @@ typedef enum pa_core_hook { + PA_CORE_HOOK_SINK_INPUT_STATE_CHANGED, + PA_CORE_HOOK_SINK_INPUT_PROPLIST_CHANGED, + PA_CORE_HOOK_SINK_INPUT_VOLUME_CHANGED, ++ PA_CORE_HOOK_SINK_INPUT_MUTE_CHANGED, + PA_CORE_HOOK_SINK_INPUT_SEND_EVENT, + PA_CORE_HOOK_SOURCE_OUTPUT_NEW, + PA_CORE_HOOK_SOURCE_OUTPUT_FIXATE, +@@ -112,6 +113,7 @@ typedef enum pa_core_hook { + PA_CORE_HOOK_SOURCE_OUTPUT_STATE_CHANGED, + PA_CORE_HOOK_SOURCE_OUTPUT_PROPLIST_CHANGED, + PA_CORE_HOOK_SOURCE_OUTPUT_VOLUME_CHANGED, ++ PA_CORE_HOOK_SOURCE_OUTPUT_MUTE_CHANGED, + PA_CORE_HOOK_SOURCE_OUTPUT_SEND_EVENT, + PA_CORE_HOOK_CLIENT_NEW, + PA_CORE_HOOK_CLIENT_PUT, +diff --git a/src/pulsecore/sink-input.c b/src/pulsecore/sink-input.c +index 3024539..9d13269 100644 +--- a/src/pulsecore/sink-input.c ++++ b/src/pulsecore/sink-input.c +@@ -1483,6 +1483,7 @@ void pa_sink_input_set_mute(pa_sink_input *i, bool mute, bool save) { + i->mute_changed(i); + + pa_subscription_post(i->core, PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_CHANGE, i->index); ++ pa_hook_fire(&i->core->hooks[PA_CORE_HOOK_SINK_INPUT_MUTE_CHANGED], i); + } + + /* Called from main thread */ +diff --git a/src/pulsecore/source-output.c b/src/pulsecore/source-output.c +index 18a1478..d3d15f1 100644 +--- a/src/pulsecore/source-output.c ++++ b/src/pulsecore/source-output.c +@@ -1082,6 +1082,7 @@ void pa_source_output_set_mute(pa_source_output *o, bool mute, bool save) { + o->mute_changed(o); + + pa_subscription_post(o->core, PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT|PA_SUBSCRIPTION_EVENT_CHANGE, o->index); ++ pa_hook_fire(&o->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_MUTE_CHANGED], o); + } + + /* Called from main thread */ diff --git a/meta-tizen/recipes-multimedia/pulseaudio/pulseaudio_5.0/0076-sink-Link-monitor-source-before-activating-port.patch b/meta-tizen/recipes-multimedia/pulseaudio/pulseaudio_5.0/0076-sink-Link-monitor-source-before-activating-port.patch new file mode 100644 index 0000000..e2de6d6 --- /dev/null +++ b/meta-tizen/recipes-multimedia/pulseaudio/pulseaudio_5.0/0076-sink-Link-monitor-source-before-activating-port.patch @@ -0,0 +1,30 @@ +From: Tanu Kaskinen +Date: Fri, 9 May 2014 11:25:28 +0300 +Subject: sink: Link monitor source before activating port + +The port activation hook callbacks may want to use the monitor source. + +Change-Id: I5d5c51171a78162dacb3286983cb560001e79ba1 +Signed-off-by: Jaska Uimonen +--- + src/pulsecore/sink.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/src/pulsecore/sink.c b/src/pulsecore/sink.c +index 695e471..191b560 100644 +--- a/src/pulsecore/sink.c ++++ b/src/pulsecore/sink.c +@@ -669,11 +669,11 @@ void pa_sink_put(pa_sink* s) { + else + pa_assert_se(sink_set_state(s, PA_SINK_IDLE) == 0); + ++ pa_source_put(s->monitor_source); ++ + if (s->active_port) + pa_device_port_active_changed(s->active_port, true); + +- pa_source_put(s->monitor_source); +- + pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK | PA_SUBSCRIPTION_EVENT_NEW, s->index); + pa_hook_fire(&s->core->hooks[PA_CORE_HOOK_SINK_PUT], s); + } diff --git a/meta-tizen/recipes-multimedia/pulseaudio/pulseaudio_5.0/0077-context-extension-Add-the-pa_extension-class.patch b/meta-tizen/recipes-multimedia/pulseaudio/pulseaudio_5.0/0077-context-extension-Add-the-pa_extension-class.patch new file mode 100644 index 0000000..a2a5141 --- /dev/null +++ b/meta-tizen/recipes-multimedia/pulseaudio/pulseaudio_5.0/0077-context-extension-Add-the-pa_extension-class.patch @@ -0,0 +1,338 @@ +From: Tanu Kaskinen +Date: Tue, 4 Mar 2014 15:03:05 +0200 +Subject: context, extension: Add the pa_extension class + +pa_extension is an abstraction layer that allows pa_context to manage +the extensions without needing any extension-specific code. This patch +only implements the pa_extension base class, the class isn't used yet +by any actual extensions. + +Change-Id: I457b3d0b674b4cfd1d38452d8f8cb51cf6b7b533 +Signed-off-by: Jaska Uimonen +--- + src/Makefile.am | 1 + + src/pulse/context.c | 48 +++++++++++++++++++++++- + src/pulse/extension.c | 100 ++++++++++++++++++++++++++++++++++++++++++++++++++ + src/pulse/extension.h | 63 +++++++++++++++++++++++++++++++ + src/pulse/internal.h | 6 +++ + 5 files changed, 217 insertions(+), 1 deletion(-) + create mode 100644 src/pulse/extension.c + create mode 100644 src/pulse/extension.h + +diff --git a/src/Makefile.am b/src/Makefile.am +index fe6cc53..22b9b81 100644 +--- a/src/Makefile.am ++++ b/src/Makefile.am +@@ -813,6 +813,7 @@ libpulse_la_SOURCES = \ + pulse/def.h \ + pulse/direction.c pulse/direction.h \ + pulse/error.c pulse/error.h \ ++ pulse/extension.c pulse/extension.h \ + pulse/ext-device-manager.c pulse/ext-device-manager.h \ + pulse/ext-device-restore.c pulse/ext-device-restore.h \ + pulse/ext-stream-restore.c pulse/ext-stream-restore.h \ +diff --git a/src/pulse/context.c b/src/pulse/context.c +index b8688f2..9c9c3d9 100644 +--- a/src/pulse/context.c ++++ b/src/pulse/context.c +@@ -186,14 +186,20 @@ pa_context *pa_context_new_with_proplist(pa_mainloop_api *mainloop, const char * + } + } + ++ c->extensions = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func); ++ + return c; + } + + static void context_unlink(pa_context *c) { ++ pa_extension *extension; + pa_stream *s; + + pa_assert(c); + ++ while ((extension = pa_hashmap_first(c->extensions))) ++ pa_extension_kill(extension); ++ + s = c->streams ? pa_stream_ref(c->streams) : NULL; + while (s) { + pa_stream *n = s->next ? pa_stream_ref(s->next) : NULL; +@@ -280,6 +286,9 @@ void pa_context_unref(pa_context *c) { + } + + void pa_context_set_state(pa_context *c, pa_context_state_t st) { ++ pa_extension *extension; ++ void *state; ++ + pa_assert(c); + pa_assert(PA_REFCNT_VALUE(c) >= 1); + +@@ -290,6 +299,12 @@ void pa_context_set_state(pa_context *c, pa_context_state_t st) { + + c->state = st; + ++ PA_HASHMAP_FOREACH(extension, c->extensions, state) ++ pa_extension_context_state_changed(extension, 1); ++ ++ PA_HASHMAP_FOREACH(extension, c->extensions, state) ++ pa_extension_context_state_changed(extension, 2); ++ + if (c->state_callback) + c->state_callback(c, c->state_userdata); + +@@ -1338,6 +1353,7 @@ void pa_command_extension(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_t + pa_context *c = userdata; + uint32_t idx; + const char *name; ++ pa_extension *extension; + + pa_assert(pd); + pa_assert(command == PA_COMMAND_EXTENSION); +@@ -1366,7 +1382,16 @@ void pa_command_extension(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_t + pa_ext_stream_restore_command(c, tag, t); + else if (pa_streq(name, "module-node-manager")) + pa_ext_node_manager_command(c, tag, t); +- else ++ else if ((extension = pa_context_get_extension(c, name))) { ++ uint32_t subcommand; ++ ++ if (pa_tagstruct_getu32(t, &subcommand) < 0) { ++ pa_context_fail(c, PA_ERR_PROTOCOL); ++ goto finish; ++ } ++ ++ pa_extension_process_command(extension, subcommand, tag, t); ++ } else + pa_log(_("Received message for unknown extension '%s'"), name); + + finish: +@@ -1464,3 +1489,24 @@ int pa_context_load_cookie_from_file(pa_context *c, const char *cookie_file_path + + return pa_client_conf_load_cookie_from_file(c->conf, cookie_file_path); + } ++ ++pa_extension *pa_context_get_extension(pa_context *context, const char *name) { ++ pa_assert(context); ++ pa_assert(name); ++ ++ return pa_hashmap_get(context->extensions, name); ++} ++ ++void pa_context_add_extension(pa_context *context, pa_extension *extension) { ++ pa_assert(context); ++ pa_assert(extension); ++ ++ pa_assert_se(pa_hashmap_put(context->extensions, extension->name, extension) >= 0); ++} ++ ++int pa_context_remove_extension(pa_context *context, pa_extension *extension) { ++ pa_assert(context); ++ pa_assert(extension); ++ ++ return pa_hashmap_remove(context->extensions, extension->name) ? 0 : -1; ++} +diff --git a/src/pulse/extension.c b/src/pulse/extension.c +new file mode 100644 +index 0000000..17e7e6c +--- /dev/null ++++ b/src/pulse/extension.c +@@ -0,0 +1,100 @@ ++/*** ++ This file is part of PulseAudio. ++ ++ Copyright 2014 Intel Corporation ++ ++ PulseAudio is free software; you can redistribute it and/or modify ++ it under the terms of the GNU Lesser General Public License as published ++ by the Free Software Foundation; either version 2.1 of the License, ++ or (at your option) any later version. ++ ++ PulseAudio is distributed in the hope that it will be useful, but ++ WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ General Public License for more details. ++ ++ You should have received a copy of the GNU Lesser General Public License ++ along with PulseAudio; if not, write to the Free Software ++ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 ++ USA. ++***/ ++ ++#ifdef HAVE_CONFIG_H ++#include ++#endif ++ ++#include "extension.h" ++ ++#include ++ ++#include ++#include ++ ++pa_extension *pa_extension_new(pa_context *context, const char *name) { ++ pa_extension *extension = NULL; ++ ++ pa_assert(context); ++ pa_assert(name); ++ ++ extension = pa_xnew0(pa_extension, 1); ++ extension->context = context; ++ extension->name = pa_xstrdup(name); ++ ++ return extension; ++} ++ ++void pa_extension_put(pa_extension *extension) { ++ pa_assert(extension); ++ pa_assert(extension->kill); ++ ++ pa_context_add_extension(extension->context, extension); ++} ++ ++static void extension_unlink(pa_extension *extension) { ++ pa_assert(extension); ++ ++ if (extension->unlinked) ++ return; ++ ++ extension->unlinked = true; ++ ++ pa_context_remove_extension(extension->context, extension); ++} ++ ++void pa_extension_free(pa_extension *extension) { ++ pa_assert(extension); ++ ++ extension_unlink(extension); ++ ++ pa_xfree(extension->name); ++ pa_xfree(extension); ++} ++ ++void pa_extension_context_state_changed(pa_extension *extension, unsigned phase) { ++ pa_assert(extension); ++ pa_assert(phase == 1 || phase == 2); ++ ++ if (extension->context_state_changed) ++ extension->context_state_changed(extension, phase); ++} ++ ++void pa_extension_kill(pa_extension *extension) { ++ pa_assert(extension); ++ ++ if (extension->unlinked) ++ return; ++ ++ extension->kill(extension); ++} ++ ++void pa_extension_process_command(pa_extension *extension, uint32_t command, uint32_t tag, pa_tagstruct *tagstruct) { ++ pa_assert(extension); ++ pa_assert(tagstruct); ++ ++ if (extension->process_command) ++ extension->process_command(extension, command, tag, tagstruct); ++ else { ++ pa_log("Unexpected command for extension %s: %u", extension->name, command); ++ pa_context_fail(extension->context, PA_ERR_PROTOCOL); ++ } ++} +diff --git a/src/pulse/extension.h b/src/pulse/extension.h +new file mode 100644 +index 0000000..cadc267 +--- /dev/null ++++ b/src/pulse/extension.h +@@ -0,0 +1,63 @@ ++#ifndef fooextensionhfoo ++#define fooextensionhfoo ++ ++/*** ++ This file is part of PulseAudio. ++ ++ Copyright 2014 Intel Corporation ++ ++ PulseAudio is free software; you can redistribute it and/or modify ++ it under the terms of the GNU Lesser General Public License as published ++ by the Free Software Foundation; either version 2.1 of the License, ++ or (at your option) any later version. ++ ++ PulseAudio is distributed in the hope that it will be useful, but ++ WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ General Public License for more details. ++ ++ You should have received a copy of the GNU Lesser General Public License ++ along with PulseAudio; if not, write to the Free Software ++ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 ++ USA. ++***/ ++ ++#include ++ ++#include ++ ++#include ++ ++typedef struct pa_extension pa_extension; ++ ++struct pa_extension { ++ pa_context *context; ++ char *name; ++ bool unlinked; ++ ++ /* This is called when the context state changes. The callback is called ++ * twice for each state change, first with phase = 1 and then with ++ * phase = 2. In the first phase the extension should update its internal ++ * state without calling any application callbacks. In the second phase it ++ * should call the application callbacks (if any). May be NULL. */ ++ void (*context_state_changed)(pa_extension *extension, unsigned phase); ++ ++ /* Called from pa_extension_kill(). May not be NULL. */ ++ void (*kill)(pa_extension *extension); ++ ++ /* Called from pa_extension_process_command(). May be NULL, if the ++ * extension doesn't expect any commands from the server. */ ++ void (*process_command)(pa_extension *extension, uint32_t command, uint32_t tag, pa_tagstruct *tagstruct); ++ ++ void *userdata; ++}; ++ ++pa_extension *pa_extension_new(pa_context *context, const char *name); ++void pa_extension_put(pa_extension *extension); ++void pa_extension_free(pa_extension *extension); ++ ++void pa_extension_context_state_changed(pa_extension *extension, unsigned phase); ++void pa_extension_kill(pa_extension *extension); ++void pa_extension_process_command(pa_extension *extension, uint32_t command, uint32_t tag, pa_tagstruct *tagstruct); ++ ++#endif +diff --git a/src/pulse/internal.h b/src/pulse/internal.h +index 61095d0..1428fb8 100644 +--- a/src/pulse/internal.h ++++ b/src/pulse/internal.h +@@ -23,6 +23,7 @@ + USA. + ***/ + ++#include + #include + #include + #include +@@ -103,6 +104,8 @@ struct pa_context { + + uint32_t client_index; + ++ pa_hashmap *extensions; /* extension name -> pa_extension */ ++ + /* Extension specific data */ + struct { + pa_ext_device_manager_subscribe_cb_t callback; +@@ -269,6 +272,9 @@ int pa_context_set_error(pa_context *c, int error); + void pa_context_set_state(pa_context *c, pa_context_state_t st); + int pa_context_handle_error(pa_context *c, uint32_t command, pa_tagstruct *t, bool fail); + pa_operation* pa_context_send_simple_command(pa_context *c, uint32_t command, void (*internal_callback)(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata), void (*cb)(void), void *userdata); ++pa_extension *pa_context_get_extension(pa_context *context, const char *name); ++void pa_context_add_extension(pa_context *context, pa_extension *extension); ++int pa_context_remove_extension(pa_context *context, pa_extension *extension); + + void pa_stream_set_state(pa_stream *s, pa_stream_state_t st); + diff --git a/meta-tizen/recipes-multimedia/pulseaudio/pulseaudio_5.0/0078-volume-api-Add-libvolume-api.so.patch.gz b/meta-tizen/recipes-multimedia/pulseaudio/pulseaudio_5.0/0078-volume-api-Add-libvolume-api.so.patch.gz new file mode 100644 index 0000000..465ef38 Binary files /dev/null and b/meta-tizen/recipes-multimedia/pulseaudio/pulseaudio_5.0/0078-volume-api-Add-libvolume-api.so.patch.gz differ diff --git a/meta-tizen/recipes-multimedia/pulseaudio/pulseaudio_5.0/0079-Add-module-volume-api-and-the-related-client-API.patch.gz b/meta-tizen/recipes-multimedia/pulseaudio/pulseaudio_5.0/0079-Add-module-volume-api-and-the-related-client-API.patch.gz new file mode 100644 index 0000000..d3e3ee7 Binary files /dev/null and b/meta-tizen/recipes-multimedia/pulseaudio/pulseaudio_5.0/0079-Add-module-volume-api-and-the-related-client-API.patch.gz differ diff --git a/meta-tizen/recipes-multimedia/pulseaudio/pulseaudio_5.0/0080-pactl-Add-support-for-the-new-volume-API.patch b/meta-tizen/recipes-multimedia/pulseaudio/pulseaudio_5.0/0080-pactl-Add-support-for-the-new-volume-API.patch new file mode 100644 index 0000000..3fd8311 --- /dev/null +++ b/meta-tizen/recipes-multimedia/pulseaudio/pulseaudio_5.0/0080-pactl-Add-support-for-the-new-volume-API.patch @@ -0,0 +1,963 @@ +From: Tanu Kaskinen +Date: Wed, 21 May 2014 14:05:47 +0300 +Subject: pactl: Add support for the new volume API + +Change-Id: I2bb6625c1cd575366388ec8dc3dd4fd2097c9a4a +Signed-off-by: Jaska Uimonen +--- + man/pactl.1.xml.in | 50 +++- + src/utils/pactl.c | 727 +++++++++++++++++++++++++++++++++++++++++++++++++++-- + 2 files changed, 759 insertions(+), 18 deletions(-) + +diff --git a/man/pactl.1.xml.in b/man/pactl.1.xml.in +index 29071b3..cd54e4c 100644 +--- a/man/pactl.1.xml.in ++++ b/man/pactl.1.xml.in +@@ -80,9 +80,12 @@ USA. + + + + ++

++ ++ ++ ++ ++ ++ ++ + +