AS_IF([test "x$HAVE_SYSTEMD_DAEMON" = "x1"], ENABLE_SYSTEMD_DAEMON=yes, ENABLE_SYSTEMD_DAEMON=no)
AS_IF([test "x$HAVE_SYSTEMD_LOGIN" = "x1"], ENABLE_SYSTEMD_LOGIN=yes, ENABLE_SYSTEMD_LOGIN=no)
AS_IF([test "x$HAVE_SYSTEMD_JOURNAL" = "x1"], ENABLE_SYSTEMD_JOURNAL=yes, ENABLE_SYSTEMD_JOURNAL=no)
- 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_BLUEZ_VENDOR_CODEC" = "x1"], HAVE_BLUEZ_VENDOR_CODEC=yes, HAVE_BLUEZ_VENDOR_CODEC=no)
AS_IF([test "x$HAVE_BLUEZ_5_OFONO_HEADSET" = "x1"], ENABLE_BLUEZ_5_OFONO_HEADSET=yes, ENABLE_BLUEZ_5_OFONO_HEADSET=no)
AS_IF([test "x$HAVE_BLUEZ_5_NATIVE_HEADSET" = "x1"], ENABLE_BLUEZ_5_NATIVE_HEADSET=yes, ENABLE_BLUEZ_5_NATIVE_HEADSET=no)
AS_IF([test "x$HAVE_HAL_COMPAT" = "x1"], ENABLE_HAL_COMPAT=yes, ENABLE_HAL_COMPAT=no)
--- /dev/null
- Version: 11.1
- Release: 81
+%define udev_dir %{_prefix}/lib/udev
+
+Name: pulseaudio
+Summary: Improved Linux sound server
++Version: 13.0
++Release: 1
+Group: Multimedia/Audio
+License: LGPL-2.1
+URL: http://pulseaudio.org
+Source0: http://www.freedesktop.org/software/pulseaudio/releases/%{name}-%{version}.tar.gz
+Source1001: pulseaudio.manifest
+BuildRequires: libtool-ltdl-devel
+BuildRequires: libtool
+BuildRequires: intltool
+BuildRequires: fdupes
+BuildRequires: pkgconfig(speexdsp)
+BuildRequires: pkgconfig(sndfile)
+BuildRequires: pkgconfig(alsa)
+BuildRequires: pkgconfig(glib-2.0)
+%if "%{tizen_profile_name}" != "tv"
+BuildRequires: pkgconfig(sbc)
+%endif
+BuildRequires: pkgconfig(dbus-1)
+BuildRequires: pkgconfig(libudev)
+BuildRequires: pkgconfig(openssl1.1)
+BuildRequires: pkgconfig(json-c)
+BuildRequires: pkgconfig(dlog)
+BuildRequires: pkgconfig(cynara-client)
+BuildRequires: pkgconfig(cynara-creds-socket)
+BuildRequires: pkgconfig(cynara-session)
+BuildRequires: systemd-devel
+BuildRequires: libcap-devel
+%if "%{tizen_profile_name}" == "tv"
+BuildRequires: pkgconfig(lwipc)
+%endif
+Requires: udev
+Requires(post): /sbin/ldconfig
+Requires(postun): /sbin/ldconfig
+
+%description
+PulseAudio is a sound server for Linux and other Unix like operating
+systems. It is intended to be an improved drop-in replacement for the
+Enlightened Sound Daemon (ESOUND).
+
+%package -n libpulse
+Summary: PulseAudio client libraries
+Group: Multimedia/Audio
+
+%description -n libpulse
+Client libraries used by applications that access a PulseAudio sound server
+via PulseAudio's native interface.
+
+%package -n libpulse-mainloop-glib
+Summary: GLIB 2
+Group: Multimedia/Audio
+
+%description -n libpulse-mainloop-glib
+pulseaudio is a networked sound server for Linux and other Unix like
+operating systems and Microsoft Windows. It is intended to be an
+improved drop-in replacement for the Enlightened Sound Daemon (ESOUND).
+
+This package contains the GLIB Main Loop bindings for the PulseAudio
+sound server.
+
+%package -n libpulse-devel
+Summary: PulseAudio client development headers and libraries
+Group: Multimedia/Development
+Requires: libpulse = %{version}
+Requires: libpulse-mainloop-glib = %{version}
+
+%description -n libpulse-devel
+Headers and libraries for developing applications that access a PulseAudio
+sound server via PulseAudio's native interface
+
+%package utils
+Summary: Command line tools for the PulseAudio sound server
+Group: Multimedia/Audio
+Requires: %{name} = %{version}-%{release}
+
+%description utils
+These tools provide command line access to various features of the
+PulseAudio sound server. Included tools are:
+pabrowse - Browse available PulseAudio servers on the local network.
+paplay - Playback a WAV file via a PulseAudio sink.
+pacat - Cat raw audio data to a PulseAudio sink.
+parec - Cat raw audio data from a PulseAudio source.
+pacmd - Connect to PulseAudio's built-in command line control interface.
+pactl - Send a control command to a PulseAudio server.
+padsp - /dev/dsp wrapper to transparently support OSS applications.
+pax11publish - Store/retrieve PulseAudio default server/sink/source
+settings in the X11 root window.
+
+%package module-bluetooth
+Summary: Bluetooth module for PulseAudio sound server
+Group: Multimedia/Audio
+Requires: %{name} = %{version}-%{release}
+
+%description module-bluetooth
+This module enables PulseAudio to work with bluetooth devices, like headset
+or audio gateway
+
+%package config
+Summary: PA default configuration
+Group: System Environment/Configuration
+
+%description config
+Default configuration for PulseAudio.
+
+%package module-raop
+Summary: PA module-raop
+Group: Multimedia/Audio
+
+%description module-raop
+PulseAudio module-raop.
+
+%package module-augment-properties
+Summary: PA module-augment-properties
+Group: Multimedia/Audio
+
+%description module-augment-properties
+PulseAudio module-augment-properties.
+
+%package module-dbus-protocol
+Summary: PA module-dbus-protocol
+Group: Multimedia/Audio
+
+%description module-dbus-protocol
+PulseAudio module-dbus-protocol.
+
+%package module-switch-on-connect
+Summary: PA module-swich-on-connect
+Group: Multimedia/Audio
+
+%description module-switch-on-connect
+PulseAudio module-swich-on-connect.
+
+%package vala-bindings
+Summary: PA Vala bindings
+Group: Multimedia/Audio
+Requires: %{name} = %{version}-%{release}
+
+%description vala-bindings
+PulseAudio Vala bindings.
+
+%package realtime-scheduling
+Summary: PA realtime scheduling
+Group: Multimedia/Audio
+Requires: %{name} = %{version}-%{release}
+Requires: libcap-tools
+
+%description realtime-scheduling
+PulseAudio realtime-scheduling.
+
+%prep
+%setup -q -T -b0
+echo "%{version}" > .tarball-version
+cp %{SOURCE1001} .
+%if "%{tizen_profile_name}" == "tv"
+cp src/daemon/systemd/system/pulseaudio-tv.service.in src/daemon/systemd/system/pulseaudio.service.in
+%endif
+
+%build
+export CFLAGS="%{optflags} -fno-strict-aliasing -D__TIZEN__ -DTIZEN_BT_A2DP_MULTISTREAM -D__TIZEN_BT__ -D__TIZEN_LOG__ %{?asan:-ldl -fPIC }"
+%if 0%{?sec_build_binary_debug_enable}
+export CFLAGS+=" -DTIZEN_DEBUG_ENABLE"
+export CXXFLAGS="$CXXFLAGS -DTIZEN_DEBUG_ENABLE"
+export FFLAGS="$FFLAGS -DTIZEN_DEBUG_ENABLE"
+%endif
+
+%if "%{tizen_profile_name}" == "mobile"
+echo "tizen profile mobile"
+export CFLAGS+=" -DTIZEN_PROFILE_MOBILE "
+%endif
+
+%if "%{tizen_profile_name}" == "wearable"
+echo "tizen profile wearable"
+export CFLAGS+=" -DTIZEN_PROFILE_WEARABLE"
+%endif
+
+%if "%{tizen_profile_name}" == "tv"
+echo "tizen profile tv"
+export CFLAGS+=" -DTIZEN_TV_PROD "
+%endif
+
+export LD_AS_NEEDED=0
+NOCONFIGURE=yes ./bootstrap.sh
+%configure --prefix=%{_prefix} \
+ --disable-static \
+ --enable-alsa \
+ --disable-ipv6 \
+ --disable-oss-output \
+ --disable-oss-wrapper \
+ --disable-esound \
+ --disable-per-user-esound-socket \
+ --disable-x11 \
+ --disable-hal-compat \
+ --disable-lirc \
+ --disable-avahi \
+ --disable-jack \
+ --without-fftw \
+ --disable-bluez4 \
+ --disable-systemd-login \
++ --disable-gconf \
++ --disable-gsettings \
+ --with-udev-rules-dir=%{udev_dir}/rules.d \
+ --with-system-user=pulse \
+ --with-system-group=pulse \
+ --with-access-group=pulse-access \
+ --enable-dlog \
+ --enable-buffer-attr \
+ --enable-pcm-dump \
+ --enable-security \
+ --enable-udev-with-usb-only \
+ --enable-paready \
+ --enable-pa-simple-ext \
+ --enable-empty-pop \
+%{?asan: --disable-neon-opt } \
+ --enable-filter-group \
+ --enable-volume-ramp \
+ --enable-individual-volume-ratio \
+%if "%{tizen_profile_name}" == "tv"
+ --enable-prelink \
+ --enable-lwipc \
+ --disable-pcm-dump \
+%endif
+
+%__make %{?_smp_mflags} V=0
+
+%install
+%make_install
+%find_lang %{name}
+
+rm -rf %{buildroot}%{_sysconfdir}/xdg/autostart/pulseaudio-kde.desktop
+rm -rf %{buildroot}%{_bindir}/start-pulseaudio-kde
+rm -rf %{buildroot}%{_bindir}/start-pulseaudio-x11
+rm -rf %{buildroot}%{_libdir}/pulse-%{version}/modules/module-device-manager.so
+mkdir -p %{buildroot}%{_includedir}/pulsecore/filter
+cp %{buildroot}%{_includedir}/pulsecore/lfe-filter.h %{buildroot}%{_includedir}/pulsecore/filter
+
+%if "%{tizen_profile_name}" == "tv"
+mkdir -p %{buildroot}%{_unitdir}/sysinit.target.wants/
+ln -s ../pulseaudio.service %{buildroot}%{_unitdir}/sysinit.target.wants/pulseaudio.service
+%else
+mkdir -p %{buildroot}%{_unitdir}/multi-user.target.wants/
+ln -s ../pulseaudio.service %{buildroot}%{_unitdir}/multi-user.target.wants/pulseaudio.service
+%endif
+
+fdupes %{buildroot}%{_datadir}
+fdupes %{buildroot}%{_includedir}
+
+# get rid of *.la files
+rm -f %{buildroot}%{_libdir}/*.la
+rm -f %{buildroot}%{_libdir}/pulseaudio/*.la
+
+%post
+/sbin/ldconfig
+
+%preun
+
+%postun -p /sbin/ldconfig
+
+%post -n libpulse -p /sbin/ldconfig
+%postun -n libpulse -p /sbin/ldconfig
+
+%post -n libpulse-mainloop-glib -p /sbin/ldconfig
+%postun -n libpulse-mainloop-glib -p /sbin/ldconfig
+
+%post realtime-scheduling
+setcap cap_sys_nice+ep /usr/bin/pulseaudio
+
+%postun realtime-scheduling
+setcap -r /usr/bin/pulseaudio
+
+%lang_package
+
+%files
+%manifest %{name}.manifest
+%defattr(-,root,root,-)
+%license LICENSE LGPL
+%exclude %{_bindir}/esdcompat
+%{_bindir}/pulseaudio
+%{_libdir}/pulseaudio/libpulsecore-%{version}.so
+%{udev_dir}/rules.d/90-pulseaudio.rules
+%config(noreplace) /etc/dbus-1/system.d/pulseaudio-system.conf
+# list all modules
+%{_libdir}/pulse-%{version}/modules/libalsa-util.so
+%{_libdir}/pulse-%{version}/modules/libcli.so
+%{_libdir}/pulse-%{version}/modules/libprotocol-cli.so
+%{_libdir}/pulse-%{version}/modules/libprotocol-http.so
+%{_libdir}/pulse-%{version}/modules/libprotocol-native.so
+%{_libdir}/pulse-%{version}/modules/libprotocol-simple.so
+%{_libdir}/pulse-%{version}/modules/librtp.so
+%{_libdir}/pulse-%{version}/modules/module-allow-passthrough.so
+%{_libdir}/pulse-%{version}/modules/module-alsa-card.so
+%{_libdir}/pulse-%{version}/modules/module-alsa-sink.so
+%{_libdir}/pulse-%{version}/modules/module-alsa-source.so
+%{_libdir}/pulse-%{version}/modules/module-always-sink.so
++%{_libdir}/pulse-%{version}/modules/module-always-source.so
+%{_libdir}/pulse-%{version}/modules/module-card-restore.so
+%{_libdir}/pulse-%{version}/modules/module-cli-protocol-tcp.so
+%{_libdir}/pulse-%{version}/modules/module-cli-protocol-unix.so
+%{_libdir}/pulse-%{version}/modules/module-cli.so
+%{_libdir}/pulse-%{version}/modules/module-combine.so
+%{_libdir}/pulse-%{version}/modules/module-combine-sink.so
+%{_libdir}/pulse-%{version}/modules/module-console-kit.so
+%{_libdir}/pulse-%{version}/modules/module-default-device-restore.so
+%{_libdir}/pulse-%{version}/modules/module-detect.so
+%{_libdir}/pulse-%{version}/modules/module-device-restore.so
+%{_libdir}/pulse-%{version}/modules/module-echo-cancel.so
+%{_libdir}/pulse-%{version}/modules/module-filter-apply.so
+%{_libdir}/pulse-%{version}/modules/module-filter-heuristics.so
+%{_libdir}/pulse-%{version}/modules/module-http-protocol-tcp.so
+%{_libdir}/pulse-%{version}/modules/module-http-protocol-unix.so
+%{_libdir}/pulse-%{version}/modules/module-intended-roles.so
+%{_libdir}/pulse-%{version}/modules/module-ladspa-sink.so
+%{_libdir}/pulse-%{version}/modules/module-loopback.so
+%{_libdir}/pulse-%{version}/modules/module-match.so
+%{_libdir}/pulse-%{version}/modules/module-mmkbd-evdev.so
+%{_libdir}/pulse-%{version}/modules/module-native-protocol-fd.so
+%{_libdir}/pulse-%{version}/modules/module-native-protocol-tcp.so
+%{_libdir}/pulse-%{version}/modules/module-native-protocol-unix.so
+%{_libdir}/pulse-%{version}/modules/module-null-sink.so
+%{_libdir}/pulse-%{version}/modules/module-null-source.so
+%{_libdir}/pulse-%{version}/modules/module-pipe-sink.so
+%{_libdir}/pulse-%{version}/modules/module-pipe-source.so
+%{_libdir}/pulse-%{version}/modules/module-position-event-sounds.so
+%{_libdir}/pulse-%{version}/modules/module-remap-sink.so
+%{_libdir}/pulse-%{version}/modules/module-remap-source.so
+%{_libdir}/pulse-%{version}/modules/module-rescue-streams.so
+%{_libdir}/pulse-%{version}/modules/module-role-ducking.so
+%{_libdir}/pulse-%{version}/modules/module-role-cork.so
+%{_libdir}/pulse-%{version}/modules/module-rtp-recv.so
+%{_libdir}/pulse-%{version}/modules/module-rtp-send.so
+%{_libdir}/pulse-%{version}/modules/module-rygel-media-server.so
+%{_libdir}/pulse-%{version}/modules/module-simple-protocol-tcp.so
+%{_libdir}/pulse-%{version}/modules/module-simple-protocol-unix.so
+%{_libdir}/pulse-%{version}/modules/module-sine.so
+%{_libdir}/pulse-%{version}/modules/module-sine-source.so
+%{_libdir}/pulse-%{version}/modules/module-stream-restore.so
+%{_libdir}/pulse-%{version}/modules/module-suspend-on-idle.so
+%{_libdir}/pulse-%{version}/modules/module-switch-on-port-available.so
+%{_libdir}/pulse-%{version}/modules/module-tunnel-sink.so
+%{_libdir}/pulse-%{version}/modules/module-tunnel-sink-new.so
+%{_libdir}/pulse-%{version}/modules/module-tunnel-source.so
+%{_libdir}/pulse-%{version}/modules/module-tunnel-source-new.so
+%{_libdir}/pulse-%{version}/modules/module-udev-detect.so
+%{_libdir}/pulse-%{version}/modules/module-virtual-sink.so
+%{_libdir}/pulse-%{version}/modules/module-virtual-source.so
+%{_libdir}/pulse-%{version}/modules/module-virtual-surround-sink.so
+%{_libdir}/pulse-%{version}/modules/module-volume-restore.so
+
+
+%exclude %{_unitdir_user}/pulseaudio.service
+%exclude %{_unitdir_user}/pulseaudio.socket
+%{_unitdir}/pulseaudio.service
+%if "%{tizen_profile_name}" == "tv"
+%{_unitdir}/sysinit.target.wants/pulseaudio.service
+%else
+%{_unitdir}/multi-user.target.wants/pulseaudio.service
+%endif
+%exclude /usr/share/bash-completion/completions/*
+%exclude /usr/share/zsh/site-functions/_pulseaudio
+
+
+%files -n libpulse
+%manifest %{name}.manifest
+%license LICENSE LGPL
+%defattr(-,root,root,-)
+%{_libdir}/libpulse.so.*
+%{_libdir}/libpulse-simple.so.*
+%{_libdir}/pulseaudio/libpulsecommon-*.so
+
+%files -n libpulse-devel
+%manifest %{name}.manifest
+%defattr(-,root,root,-)
+%{_includedir}/pulse/*
+%{_includedir}/pulsecore/*
+%{_includedir}/pulsecore/filter/*
+%{_libdir}/libpulse.so
+%{_libdir}/libpulse-simple.so
+%{_libdir}/libpulse-mainloop-glib.so
+%{_libdir}/pkgconfig/libpulse*.pc
+%{_libdir}/pkgconfig/pulsecore.pc
+%{_datadir}/vala/vapi/libpulse.vapi
+# cmake stuff
+%{_libdir}/cmake/PulseAudio/PulseAudioConfig.cmake
+%{_libdir}/cmake/PulseAudio/PulseAudioConfigVersion.cmake
+
+%files -n libpulse-mainloop-glib
+%manifest %{name}.manifest
+%defattr(-,root,root)
+%{_libdir}/libpulse-mainloop-glib.so.*
+
+%files utils
+%manifest %{name}.manifest
+%license LICENSE LGPL
+%defattr(-,root,root,-)
+%doc %{_mandir}/man1/*
+%doc %{_mandir}/man5/*
+%{_bindir}/pacat
+%{_bindir}/pacmd
+%{_bindir}/pactl
+%{_bindir}/paplay
+%{_bindir}/parec
+%{_bindir}/pamon
+%{_bindir}/parecord
+%{_bindir}/pasuspender
++%{_bindir}/pa-info
+
+%files module-bluetooth
+%manifest %{name}.manifest
+%license LICENSE LGPL
+%if "%{tizen_profile_name}" != "tv"
+%defattr(-,root,root,-)
+%{_libdir}/pulse-%{version}/modules/module-bluetooth-discover.so
+%{_libdir}/pulse-%{version}/modules/module-bluetooth-policy.so
+%{_libdir}/pulse-%{version}/modules/module-bluez5-discover.so
+%{_libdir}/pulse-%{version}/modules/module-bluez5-device.so
+%{_libdir}/pulse-%{version}/modules/module-a2dp-vendor-codec-aptx.so
+%{_libdir}/pulse-%{version}/modules/libbluez5-util.so
+%endif
+
+%files module-raop
+%manifest %{name}.manifest
+%defattr(-,root,root,-)
+%{_libdir}/pulse-%{version}/modules/libraop.so
+%{_libdir}/pulse-%{version}/modules/module-raop*.so
+
+%files module-augment-properties
+%manifest %{name}.manifest
+%defattr(-,root,root,-)
+%{_libdir}/pulse-%{version}/modules/module-augment-properties.so
+
+%files module-dbus-protocol
+%manifest %{name}.manifest
+%defattr(-,root,root,-)
+%{_libdir}/pulse-%{version}/modules/module-dbus-protocol.so
+
+%files module-switch-on-connect
+%manifest %{name}.manifest
+%defattr(-,root,root,-)
+%{_libdir}/pulse-%{version}/modules/module-switch-on-connect.so
+
+%files config
+%manifest %{name}.manifest
+%defattr(-,root,root,-)
+%config(noreplace) %{_sysconfdir}/pulse/daemon.conf
+%config(noreplace) %{_sysconfdir}/pulse/default.pa
+%config(noreplace) %{_sysconfdir}/pulse/client.conf
+%config(noreplace) %{_sysconfdir}/pulse/system.pa
+
+%{_datadir}/pulseaudio/alsa-mixer/paths/*
+%{_datadir}/pulseaudio/alsa-mixer/profile-sets/*
+
+%files vala-bindings
+%manifest %{name}.manifest
+%defattr(-,root,root,-)
+%{_datadir}/vala/vapi/*
+
+%files realtime-scheduling
+%defattr(-,root,root,-)
+
+%docs_package
module_filter_apply_la_SOURCES = modules/module-filter-apply.c
module_filter_apply_la_LDFLAGS = $(MODULE_LDFLAGS)
module_filter_apply_la_LIBADD = $(MODULE_LIBADD)
- module_filter_apply_la_CFLAGS = -DTIZEN_FILTER_GROUP
+ module_filter_apply_la_CFLAGS = $(AM_CFLAGS) -DPA_MODULE_NAME=module_filter_apply
+if TIZEN_FILTER_GROUP
++module_filter_apply_la_CFLAGS += -DTIZEN_FILTER_GROUP
+endif
module_filter_heuristics_la_SOURCES = modules/module-filter-heuristics.c
module_filter_heuristics_la_LDFLAGS = $(MODULE_LDFLAGS)
module_alsa_card_la_SOURCES = modules/alsa/module-alsa-card.c
module_alsa_card_la_LDFLAGS = $(MODULE_LDFLAGS)
module_alsa_card_la_LIBADD = $(MODULE_LIBADD) $(ASOUNDLIB_LIBS) libalsa-util.la
- module_alsa_card_la_CFLAGS = $(AM_CFLAGS) $(ASOUNDLIB_CFLAGS)
+ module_alsa_card_la_CFLAGS = $(AM_CFLAGS) $(ASOUNDLIB_CFLAGS) -DPA_MODULE_NAME=module_alsa_card
+if TIZEN_UDEV_USB_ONLY
+module_alsa_card_la_CFLAGS += -DTIZEN_UDEV_USB_ONLY
+endif
-
# Solaris
module_solaris_la_SOURCES = modules/module-solaris.c
module_udev_detect_la_SOURCES = modules/module-udev-detect.c
module_udev_detect_la_LDFLAGS = $(MODULE_LDFLAGS)
module_udev_detect_la_LIBADD = $(MODULE_LIBADD) $(UDEV_LIBS)
- module_udev_detect_la_CFLAGS = $(AM_CFLAGS) $(UDEV_CFLAGS)
+ module_udev_detect_la_CFLAGS = $(AM_CFLAGS) $(UDEV_CFLAGS) -DPA_MODULE_NAME=module_udev_detect
+if TIZEN_UDEV_USB_ONLY
+module_udev_detect_la_CFLAGS += -DTIZEN_UDEV_USB_ONLY
+endif
module_console_kit_la_SOURCES = modules/module-console-kit.c
module_console_kit_la_LDFLAGS = $(MODULE_LDFLAGS)
libbluez5_util_la_SOURCES = \
modules/bluetooth/bluez5-util.c \
modules/bluetooth/bluez5-util.h \
- modules/bluetooth/a2dp-codecs.h
- modules/bluetooth/a2dp-codec-api.h \
- modules/bluetooth/a2dp-codec-util.c \
- modules/bluetooth/a2dp-codec-util.h \
+ modules/bluetooth/a2dp-codecs.h \
+ modules/bluetooth/rtp.h
if HAVE_BLUEZ_5_OFONO_HEADSET
libbluez5_util_la_SOURCES += \
modules/bluetooth/backend-ofono.c
libbluez5_util_la_LDFLAGS = -avoid-version
libbluez5_util_la_LIBADD = $(MODULE_LIBADD) $(DBUS_LIBS)
libbluez5_util_la_CFLAGS = $(AM_CFLAGS) $(DBUS_CFLAGS)
-libbluez5_util_la_SOURCES += modules/bluetooth/a2dp-codec-sbc.c
+ libbluez5_util_la_CPPFLAGS = $(AM_CPPFLAGS)
+
++#libbluez5_util_la_SOURCES += modules/bluetooth/a2dp-codec-sbc.c
+ libbluez5_util_la_LIBADD += $(SBC_LIBS)
+ libbluez5_util_la_CFLAGS += $(SBC_CFLAGS)
+if HAVE_BLUEZ_VENDOR_CODEC
+module_a2dp_vendor_codec_aptx_la_SOURCES = \
+ modules/bluetooth/module-a2dp-vendor-codec-aptx.c \
+ modules/bluetooth/module-a2dp-vendor-codec.h \
+ modules/bluetooth/a2dp-codecs.h
+module_a2dp_vendor_codec_aptx_la_LDFLAGS = $(MODULE_LDFLAGS)
+module_a2dp_vendor_codec_aptx_la_LIBADD = $(MODULE_LIBADD) $(DBUS_LIBS) libbluez5-util.la
+module_a2dp_vendor_codec_aptx_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)
module_bluez5_discover_la_LIBADD = $(MODULE_LIBADD) $(DBUS_LIBS) libbluez5-util.la
case ARG_LOG_TARGET:
if (pa_daemon_conf_set_log_target(conf, optarg) < 0) {
#ifdef HAVE_SYSTEMD_JOURNAL
- pa_log(_("Invalid log target: use either 'syslog', 'journal','stderr' or 'auto' or a valid file name 'file:<path>', 'newfile:<path>'."));
++
+ pa_log(_("Invalid log target: use either 'syslog', 'journal', 'stderr' or 'auto' or a valid file name 'file:<path>', 'newfile:<path>'."));
+#elif defined(TIZEN_DLOG)
+ pa_log(_("Invalid log target: use either 'syslog', 'stderr' or 'auto' or a valid file name 'file:<path>', 'newfile:<path>' or 'dlog' or 'dlog-color'."));
#else
pa_log(_("Invalid log target: use either 'syslog', 'stderr' or 'auto' or a valid file name 'file:<path>', 'newfile:<path>'."));
#endif
#include "ltdl-bind-now.h"
#include "server-lookup.h"
+#ifdef TIZEN_PA_READY
+#include <fcntl.h>
+#define PA_READY "/tmp/.pa_ready"
+#endif
+
+#ifdef TIZEN_TV_PROD_LWIPC
+#include <lwipc.h>
+#define PULSEAUDIO_READY "/tmp/.pulseaudio_ready"
+#endif
+ #ifdef DISABLE_LIBTOOL_PRELOAD
+ /* FIXME: work around a libtool bug by making sure we have 2 elements. Bug has
+ * been reported: https://debbugs.gnu.org/cgi/bugreport.cgi?bug=29576 */
+ const lt_dlsymlist lt_preloaded_symbols[] = {
+ { "@PROGRAM@", NULL },
+ { NULL, NULL }
+ };
+ #endif
#ifdef HAVE_LIBWRAP
/* Only one instance of these variables */
ACTION!="change", GOTO="pulseaudio_end"
KERNEL!="card*", GOTO="pulseaudio_end"
SUBSYSTEMS=="usb", GOTO="pulseaudio_check_usb"
+ SUBSYSTEMS=="firewire", GOTO="pulseaudio_firewire_quirk"
SUBSYSTEMS=="platform", DRIVERS=="thinkpad_acpi", ENV{PULSE_IGNORE}="1"
+# For tizen platform, we only allow usb devices from udev
+SUBSYSTEMS!="usb", ENV{PULSE_IGNORE}="1", GOTO="pulseaudio_end"
# Force enable speaker and internal mic for some laptops
# This should only be necessary for kernels 3.3, 3.4 and 3.5 (as they are lacking the phantom jack kctls).
pa_assert(u->mixer_handle);
data = PA_DEVICE_PORT_DATA(p);
-
pa_assert_se(u->mixer_path = data->path);
- pa_alsa_path_select(u->mixer_path, data->setting, u->mixer_handle, s->muted);
-
mixer_volume_init(u);
- if (s->set_mute)
- s->set_mute(s);
- if (s->flags & PA_SINK_DEFERRED_VOLUME) {
- if (s->write_volume)
- s->write_volume(s);
- } else {
- if (s->set_volume)
- s->set_volume(s);
- }
+ if (s->flags & PA_SINK_DEFERRED_VOLUME)
+ pa_asyncmsgq_send(u->sink->asyncmsgq, PA_MSGOBJECT(u->sink), SINK_MESSAGE_SYNC_MIXER, p, 0, NULL);
+ else
+ sync_mixer(u, p);
+
+ if (data->suspend_when_unavailable && p->available == PA_AVAILABLE_NO)
+ pa_sink_suspend(s, true, PA_SUSPEND_UNAVAILABLE);
+ else
+ pa_sink_suspend(s, false, PA_SUSPEND_UNAVAILABLE);
+ if (data->suspend_when_unavailable && p->available == PA_AVAILABLE_NO)
+ pa_sink_suspend(s, true, PA_SUSPEND_UNAVAILABLE);
+ else
+ pa_sink_suspend(s, false, PA_SUSPEND_UNAVAILABLE);
+
return 0;
}
channel-map = left,right
paths-output = analog-output analog-output-lineout analog-output-speaker analog-output-headphones analog-output-headphones-2
paths-input = analog-input-front-mic analog-input-rear-mic analog-input-internal-mic analog-input-dock-mic analog-input analog-input-mic analog-input-linein analog-input-aux analog-input-video analog-input-tvtuner analog-input-fm analog-input-mic-line analog-input-headphone-mic analog-input-headset-mic
- priority = 10
+ priority = 15
+
+ # If everything else fails, try to use hw:0 as a stereo device...
+ [Mapping stereo-fallback]
+ device-strings = hw:%f
+ fallback = yes
+ channel-map = front-left,front-right
+ paths-output = analog-output analog-output-lineout analog-output-speaker analog-output-headphones analog-output-headphones-2
+ paths-input = analog-input-front-mic analog-input-rear-mic analog-input-internal-mic analog-input-dock-mic analog-input analog-input-mic analog-input-linein analog-input-aux analog-input-video analog-input-tvtuner analog-input-fm analog-input-mic-line analog-input-headphone-mic analog-input-headset-mic
+ priority = 1
+
+ # ...and if even that fails, try to use hw:0 as a mono device.
+ [Mapping mono-fallback]
+ device-strings = hw:%f
+ fallback = yes
+ channel-map = mono
+ paths-output = analog-output analog-output-lineout analog-output-speaker analog-output-headphones analog-output-headphones-2 analog-output-mono
+ paths-input = analog-input-front-mic analog-input-rear-mic analog-input-internal-mic analog-input-dock-mic analog-input analog-input-mic analog-input-linein analog-input-aux analog-input-video analog-input-tvtuner analog-input-fm analog-input-mic-line analog-input-headset-mic
+ priority = 1
+
+# If everything else fails, try to use hw:0 as a stereo device.
+[Mapping stereo-fallback]
+device-strings = hw:%f
+fallback = yes
+channel-map = front-left,front-right
+paths-output = analog-output analog-output-lineout analog-output-speaker analog-output-headphones analog-output-headphones-2
+paths-input = analog-input-front-mic analog-input-rear-mic analog-input-internal-mic analog-input-dock-mic analog-input analog-input-mic analog-input-linein analog-input-aux analog-input-video analog-input-tvtuner analog-input-fm analog-input-mic-line analog-input-headphone-mic analog-input-headset-mic
+priority = 1
[Mapping analog-surround-21]
device-strings = surround21:%f
#define A2DP_CODEC_SBC 0x00
#define A2DP_CODEC_MPEG12 0x01
#define A2DP_CODEC_MPEG24 0x02
++#ifdef __TIZEN_BT_6__
+ #define A2DP_CODEC_ATRAC 0x04
+ #define A2DP_CODEC_VENDOR 0xFF
++#else
+#define A2DP_CODEC_ATRAC 0x03
+#ifdef __TIZEN_BT__
+/* #define BLUETOOTH_APTX_SUPPORT 1 */
+#define A2DP_CODEC_VENDOR 0xFF
+#endif
+#define A2DP_CODEC_NON_A2DP 0xFF
++#endif
+
#define SBC_SAMPLING_FREQ_16000 (1 << 3)
#define SBC_SAMPLING_FREQ_32000 (1 << 2)
#define SBC_SAMPLING_FREQ_44100 (1 << 1)
#define SBC_ALLOCATION_SNR (1 << 1)
#define SBC_ALLOCATION_LOUDNESS 1
++#ifdef __TIZEN_BT_6__
+ #define SBC_MIN_BITPOOL 2
+ #define SBC_MAX_BITPOOL 250
+
+ /* Other settings:
+ * Block length = 16
+ * Allocation method = Loudness
+ * Subbands = 8
+ */
+ #define SBC_BITPOOL_MQ_MONO_44100 19
+ #define SBC_BITPOOL_MQ_MONO_48000 18
+ #define SBC_BITPOOL_MQ_JOINT_STEREO_44100 35
+ #define SBC_BITPOOL_MQ_JOINT_STEREO_48000 33
+ #define SBC_BITPOOL_HQ_MONO_44100 31
+ #define SBC_BITPOOL_HQ_MONO_48000 29
+ #define SBC_BITPOOL_HQ_JOINT_STEREO_44100 53
+ #define SBC_BITPOOL_HQ_JOINT_STEREO_48000 51
++#endif
+
#define MPEG_CHANNEL_MODE_MONO (1 << 3)
#define MPEG_CHANNEL_MODE_DUAL_CHANNEL (1 << 2)
#define MPEG_CHANNEL_MODE_STEREO (1 << 1)
#define MPEG_SAMPLING_FREQ_44100 (1 << 1)
#define MPEG_SAMPLING_FREQ_48000 1
-
++#ifndef __TIZEN_BT_6__
+#if defined(__TIZEN_BT__) && defined(ADJUST_ANDROID_BITPOOL)
+#define MAX_BITPOOL 35
+#else
+#define MAX_BITPOOL 64
+#endif
+#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
++#endif
++
+ #define MPEG_BIT_RATE_INDEX_0 (1 << 0)
+ #define MPEG_BIT_RATE_INDEX_1 (1 << 1)
+ #define MPEG_BIT_RATE_INDEX_2 (1 << 2)
+ #define MPEG_BIT_RATE_INDEX_3 (1 << 3)
+ #define MPEG_BIT_RATE_INDEX_4 (1 << 4)
+ #define MPEG_BIT_RATE_INDEX_5 (1 << 5)
+ #define MPEG_BIT_RATE_INDEX_6 (1 << 6)
+ #define MPEG_BIT_RATE_INDEX_7 (1 << 7)
+ #define MPEG_BIT_RATE_INDEX_8 (1 << 8)
+ #define MPEG_BIT_RATE_INDEX_9 (1 << 9)
+ #define MPEG_BIT_RATE_INDEX_10 (1 << 10)
+ #define MPEG_BIT_RATE_INDEX_11 (1 << 11)
+ #define MPEG_BIT_RATE_INDEX_12 (1 << 12)
+ #define MPEG_BIT_RATE_INDEX_13 (1 << 13)
+ #define MPEG_BIT_RATE_INDEX_14 (1 << 14)
+
+ #define MPEG_MP1_BIT_RATE_32000 MPEG_BIT_RATE_INDEX_1
+ #define MPEG_MP1_BIT_RATE_64000 MPEG_BIT_RATE_INDEX_2
+ #define MPEG_MP1_BIT_RATE_96000 MPEG_BIT_RATE_INDEX_3
+ #define MPEG_MP1_BIT_RATE_128000 MPEG_BIT_RATE_INDEX_4
+ #define MPEG_MP1_BIT_RATE_160000 MPEG_BIT_RATE_INDEX_5
+ #define MPEG_MP1_BIT_RATE_192000 MPEG_BIT_RATE_INDEX_6
+ #define MPEG_MP1_BIT_RATE_224000 MPEG_BIT_RATE_INDEX_7
+ #define MPEG_MP1_BIT_RATE_256000 MPEG_BIT_RATE_INDEX_8
+ #define MPEG_MP1_BIT_RATE_288000 MPEG_BIT_RATE_INDEX_9
+ #define MPEG_MP1_BIT_RATE_320000 MPEG_BIT_RATE_INDEX_10
+ #define MPEG_MP1_BIT_RATE_352000 MPEG_BIT_RATE_INDEX_11
+ #define MPEG_MP1_BIT_RATE_384000 MPEG_BIT_RATE_INDEX_12
+ #define MPEG_MP1_BIT_RATE_416000 MPEG_BIT_RATE_INDEX_13
+ #define MPEG_MP1_BIT_RATE_448000 MPEG_BIT_RATE_INDEX_14
+
+ #define MPEG_MP2_BIT_RATE_32000 MPEG_BIT_RATE_INDEX_1
+ #define MPEG_MP2_BIT_RATE_48000 MPEG_BIT_RATE_INDEX_2
+ #define MPEG_MP2_BIT_RATE_56000 MPEG_BIT_RATE_INDEX_3
+ #define MPEG_MP2_BIT_RATE_64000 MPEG_BIT_RATE_INDEX_4
+ #define MPEG_MP2_BIT_RATE_80000 MPEG_BIT_RATE_INDEX_5
+ #define MPEG_MP2_BIT_RATE_96000 MPEG_BIT_RATE_INDEX_6
+ #define MPEG_MP2_BIT_RATE_112000 MPEG_BIT_RATE_INDEX_7
+ #define MPEG_MP2_BIT_RATE_128000 MPEG_BIT_RATE_INDEX_8
+ #define MPEG_MP2_BIT_RATE_160000 MPEG_BIT_RATE_INDEX_9
+ #define MPEG_MP2_BIT_RATE_192000 MPEG_BIT_RATE_INDEX_10
+ #define MPEG_MP2_BIT_RATE_224000 MPEG_BIT_RATE_INDEX_11
+ #define MPEG_MP2_BIT_RATE_256000 MPEG_BIT_RATE_INDEX_12
+ #define MPEG_MP2_BIT_RATE_320000 MPEG_BIT_RATE_INDEX_13
+ #define MPEG_MP2_BIT_RATE_384000 MPEG_BIT_RATE_INDEX_14
+
+ #define MPEG_MP3_BIT_RATE_32000 MPEG_BIT_RATE_INDEX_1
+ #define MPEG_MP3_BIT_RATE_40000 MPEG_BIT_RATE_INDEX_2
+ #define MPEG_MP3_BIT_RATE_48000 MPEG_BIT_RATE_INDEX_3
+ #define MPEG_MP3_BIT_RATE_56000 MPEG_BIT_RATE_INDEX_4
+ #define MPEG_MP3_BIT_RATE_64000 MPEG_BIT_RATE_INDEX_5
+ #define MPEG_MP3_BIT_RATE_80000 MPEG_BIT_RATE_INDEX_6
+ #define MPEG_MP3_BIT_RATE_96000 MPEG_BIT_RATE_INDEX_7
+ #define MPEG_MP3_BIT_RATE_112000 MPEG_BIT_RATE_INDEX_8
+ #define MPEG_MP3_BIT_RATE_128000 MPEG_BIT_RATE_INDEX_9
+ #define MPEG_MP3_BIT_RATE_160000 MPEG_BIT_RATE_INDEX_10
+ #define MPEG_MP3_BIT_RATE_192000 MPEG_BIT_RATE_INDEX_11
+ #define MPEG_MP3_BIT_RATE_224000 MPEG_BIT_RATE_INDEX_12
+ #define MPEG_MP3_BIT_RATE_256000 MPEG_BIT_RATE_INDEX_13
+ #define MPEG_MP3_BIT_RATE_320000 MPEG_BIT_RATE_INDEX_14
+
+ #define MPEG_BIT_RATE_FREE MPEG_BIT_RATE_INDEX_0
+
+ #define MPEG_GET_BITRATE(a) ((uint16_t)(a).bitrate1 << 8 | (a).bitrate2)
+ #define MPEG_SET_BITRATE(a, b) \
+ do { \
+ (a).bitrate1 = ((b) >> 8) & 0x7f; \
+ (a).bitrate2 = (b) & 0xff; \
+ } while (0)
+
+ #define AAC_OBJECT_TYPE_MPEG2_AAC_LC 0x80
+ #define AAC_OBJECT_TYPE_MPEG4_AAC_LC 0x40
+ #define AAC_OBJECT_TYPE_MPEG4_AAC_LTP 0x20
+ #define AAC_OBJECT_TYPE_MPEG4_AAC_SCA 0x10
+
+ #define AAC_SAMPLING_FREQ_8000 0x0800
+ #define AAC_SAMPLING_FREQ_11025 0x0400
+ #define AAC_SAMPLING_FREQ_12000 0x0200
+ #define AAC_SAMPLING_FREQ_16000 0x0100
+ #define AAC_SAMPLING_FREQ_22050 0x0080
+ #define AAC_SAMPLING_FREQ_24000 0x0040
+ #define AAC_SAMPLING_FREQ_32000 0x0020
+ #define AAC_SAMPLING_FREQ_44100 0x0010
+ #define AAC_SAMPLING_FREQ_48000 0x0008
+ #define AAC_SAMPLING_FREQ_64000 0x0004
+ #define AAC_SAMPLING_FREQ_88200 0x0002
+ #define AAC_SAMPLING_FREQ_96000 0x0001
+
+ #define AAC_CHANNELS_1 0x02
+ #define AAC_CHANNELS_2 0x01
+
+ #define AAC_GET_BITRATE(a) ((a).bitrate1 << 16 | \
+ (a).bitrate2 << 8 | (a).bitrate3)
+ #define AAC_GET_FREQUENCY(a) ((a).frequency1 << 4 | (a).frequency2)
+
+ #define AAC_SET_BITRATE(a, b) \
+ do { \
+ (a).bitrate1 = (b >> 16) & 0x7f; \
+ (a).bitrate2 = (b >> 8) & 0xff; \
+ (a).bitrate3 = b & 0xff; \
+ } while (0)
+ #define AAC_SET_FREQUENCY(a, f) \
+ do { \
+ (a).frequency1 = (f >> 4) & 0xff; \
+ (a).frequency2 = f & 0x0f; \
+ } while (0)
+
+ #define AAC_INIT_BITRATE(b) \
+ .bitrate1 = (b >> 16) & 0x7f, \
+ .bitrate2 = (b >> 8) & 0xff, \
+ .bitrate3 = b & 0xff,
+ #define AAC_INIT_FREQUENCY(f) \
+ .frequency1 = (f >> 4) & 0xff, \
+ .frequency2 = f & 0x0f,
+
+ #define APTX_VENDOR_ID 0x0000004f
+ #define APTX_CODEC_ID 0x0001
+
+ #define APTX_CHANNEL_MODE_MONO 0x01
+ #define APTX_CHANNEL_MODE_STEREO 0x02
+
+ #define APTX_SAMPLING_FREQ_16000 0x08
+ #define APTX_SAMPLING_FREQ_32000 0x04
+ #define APTX_SAMPLING_FREQ_44100 0x02
+ #define APTX_SAMPLING_FREQ_48000 0x01
+
+ #define FASTSTREAM_VENDOR_ID 0x0000000a
+ #define FASTSTREAM_CODEC_ID 0x0001
+
+ #define FASTSTREAM_DIRECTION_SINK 0x1
+ #define FASTSTREAM_DIRECTION_SOURCE 0x2
+
+ #define FASTSTREAM_SINK_SAMPLING_FREQ_44100 0x2
+ #define FASTSTREAM_SINK_SAMPLING_FREQ_48000 0x1
+
+ #define FASTSTREAM_SOURCE_SAMPLING_FREQ_16000 0x2
+
+ #define APTX_LL_VENDOR_ID 0x0000000a
+ #define APTX_LL_CODEC_ID 0x0002
+
+ /* Default parameters for aptX Low Latency encoder */
+
+ /* Target codec buffer level = 180 */
+ #define APTX_LL_TARGET_LEVEL2 0xb4
+ #define APTX_LL_TARGET_LEVEL1 0x00
+
+ /* Initial codec buffer level = 360 */
+ #define APTX_LL_INITIAL_LEVEL2 0x68
+ #define APTX_LL_INITIAL_LEVEL1 0x01
+
+ /* SRA max rate 0.005 * 10000 = 50 */
+ #define APTX_LL_SRA_MAX_RATE 0x32
+
+ /* SRA averaging time = 1s */
+ #define APTX_LL_SRA_AVG_TIME 0x01
+
+ /* Good working codec buffer level = 180 */
+ #define APTX_LL_GOOD_WORKING_LEVEL2 0xB4
+ #define APTX_LL_GOOD_WORKING_LEVEL1 0x00
+
+ #define APTX_HD_VENDOR_ID 0x000000D7
+ #define APTX_HD_CODEC_ID 0x0024
+
+ #define LDAC_VENDOR_ID 0x0000012d
+ #define LDAC_CODEC_ID 0x00aa
+
+ #define LDAC_SAMPLING_FREQ_44100 0x20
+ #define LDAC_SAMPLING_FREQ_48000 0x10
+ #define LDAC_SAMPLING_FREQ_88200 0x08
+ #define LDAC_SAMPLING_FREQ_96000 0x04
+ #define LDAC_SAMPLING_FREQ_176400 0x02
+ #define LDAC_SAMPLING_FREQ_192000 0x01
+
+ #define LDAC_CHANNEL_MODE_MONO 0x04
+ #define LDAC_CHANNEL_MODE_DUAL 0x02
+ #define LDAC_CHANNEL_MODE_STEREO 0x01
+
+ typedef struct {
+ uint8_t vendor_id4;
+ uint8_t vendor_id3;
+ uint8_t vendor_id2;
+ uint8_t vendor_id1;
+ uint8_t codec_id2;
+ uint8_t codec_id1;
+ } __attribute__ ((packed)) a2dp_vendor_codec_t;
+
+ #define A2DP_GET_VENDOR_ID(a) ( \
+ (((uint32_t)(a).vendor_id4) << 0) | \
+ (((uint32_t)(a).vendor_id3) << 8) | \
+ (((uint32_t)(a).vendor_id2) << 16) | \
+ (((uint32_t)(a).vendor_id1) << 24) \
+ )
+ #define A2DP_GET_CODEC_ID(a) ((a).codec_id2 | (((uint16_t)(a).codec_id1) << 8))
+ #define A2DP_SET_VENDOR_ID_CODEC_ID(v, c) ((a2dp_vendor_codec_t){ \
+ .vendor_id4 = (((v) >> 0) & 0xff), \
+ .vendor_id3 = (((v) >> 8) & 0xff), \
+ .vendor_id2 = (((v) >> 16) & 0xff), \
+ .vendor_id1 = (((v) >> 24) & 0xff), \
+ .codec_id2 = (((c) >> 0) & 0xff), \
+ .codec_id1 = (((c) >> 8) & 0xff), \
+ })
+
+ typedef struct {
+ uint8_t reserved;
+ uint8_t target_level2;
+ uint8_t target_level1;
+ uint8_t initial_level2;
+ uint8_t initial_level1;
+ uint8_t sra_max_rate;
+ uint8_t sra_avg_time;
+ uint8_t good_working_level2;
+ uint8_t good_working_level1;
+ } __attribute__ ((packed)) a2dp_aptx_ll_new_caps_t;
+
+ typedef struct {
+ a2dp_vendor_codec_t info;
+ uint8_t frequency;
+ uint8_t channel_mode;
+ } __attribute__ ((packed)) a2dp_ldac_t;
+
+ #if defined(__BYTE_ORDER) && defined(__LITTLE_ENDIAN) && \
+ __BYTE_ORDER == __LITTLE_ENDIAN
typedef struct {
uint8_t channel_mode:4;
uint8_t frequency:6;
uint8_t mpf:1;
uint8_t rfa:1;
++#ifdef __TIZEN_BT_6__
+ uint8_t bitrate1:7;
+ uint8_t vbr:1;
+ uint8_t bitrate2;
++#else
+ uint16_t bitrate;
++#endif
} __attribute__ ((packed)) a2dp_mpeg_t;
typedef struct {
+ uint8_t object_type;
+ uint8_t frequency1;
+ uint8_t rfa:2;
+ uint8_t channels:2;
+ uint8_t frequency2:4;
+ uint8_t bitrate1:7;
+ uint8_t vbr:1;
+ uint8_t bitrate2;
+ uint8_t bitrate3;
+ } __attribute__ ((packed)) a2dp_aac_t;
+
+ typedef struct {
++#ifdef __TIZEN_BT_6__
+ a2dp_vendor_codec_t info;
+ uint8_t channel_mode:4;
+ uint8_t frequency:4;
++#else
+ uint8_t vendor_id[4];
+ uint8_t codec_id[2];
+ uint8_t channel_mode:4;
+ uint8_t frequency:4;
++#endif
} __attribute__ ((packed)) a2dp_aptx_t;
- #elif __BYTE_ORDER == __BIG_ENDIAN
+
++#ifdef __TIZEN_BT_6__
+ typedef struct {
+ a2dp_vendor_codec_t info;
+ uint8_t direction;
+ uint8_t sink_frequency:4;
+ uint8_t source_frequency:4;
+ } __attribute__ ((packed)) a2dp_faststream_t;
+
+ typedef struct {
+ a2dp_aptx_t aptx;
+ uint8_t bidirect_link:1;
+ uint8_t has_new_caps:1;
+ uint8_t reserved:6;
+ a2dp_aptx_ll_new_caps_t new_caps[0];
+ } __attribute__ ((packed)) a2dp_aptx_ll_t;
++#endif
+
+ #elif defined(__BYTE_ORDER) && defined(__BIG_ENDIAN) && \
+ __BYTE_ORDER == __BIG_ENDIAN
typedef struct {
uint8_t frequency:4;
uint8_t rfa:1;
uint8_t mpf:1;
uint8_t frequency:6;
++#ifdef __TIZEN_BT_6__
+ uint8_t vbr:1;
+ uint8_t bitrate1:7;
+ uint8_t bitrate2;
++#else
+ uint16_t bitrate;
++#endif
} __attribute__ ((packed)) a2dp_mpeg_t;
typedef struct {
+ uint8_t object_type;
+ uint8_t frequency1;
+ uint8_t frequency2:4;
+ uint8_t channels:2;
+ uint8_t rfa:2;
+ uint8_t vbr:1;
+ uint8_t bitrate1:7;
+ uint8_t bitrate2;
+ uint8_t bitrate3;
+ } __attribute__ ((packed)) a2dp_aac_t;
+
+ typedef struct {
++#ifdef __TIZEN_BT_6__
+ a2dp_vendor_codec_t info;
+ uint8_t frequency:4;
+ uint8_t channel_mode:4;
++#else
+ uint8_t vendor_id[4];
+ uint8_t codec_id[2];
+ uint8_t frequency:4;
+ uint8_t channel_mode:4;
++#endif
} __attribute__ ((packed)) a2dp_aptx_t;
+ typedef struct {
+ a2dp_vendor_codec_t info;
+ uint8_t direction;
+ uint8_t source_frequency:4;
+ uint8_t sink_frequency:4;
+ } __attribute__ ((packed)) a2dp_faststream_t;
+
+ typedef struct {
+ a2dp_aptx_t aptx;
+ uint8_t reserved:6;
+ uint8_t has_new_caps:1;
+ uint8_t bidirect_link:1;
+ a2dp_aptx_ll_new_caps_t new_caps[0];
+ } __attribute__ ((packed)) a2dp_aptx_ll_t;
+
#else
#error "Unknown byte order"
#endif
pa_xfree(endpoint);
}
++#ifdef __TIZEN_BT_6__
+ static void register_endpoint(pa_bluetooth_discovery *y, const pa_a2dp_codec *a2dp_codec, const char *path, const char *endpoint, const char *uuid) {
+ DBusMessage *m;
+ DBusMessageIter i, d;
+ uint8_t capabilities[MAX_A2DP_CAPS_SIZE];
+ size_t capabilities_size;
+ uint8_t codec_id;
+
+ pa_log_debug("Registering %s on adapter %s", endpoint, path);
+
+ codec_id = a2dp_codec->id.codec_id;
+ capabilities_size = a2dp_codec->fill_capabilities(capabilities);
+ pa_assert(capabilities_size != 0);
+
+ pa_assert_se(m = dbus_message_new_method_call(BLUEZ_SERVICE, path, BLUEZ_MEDIA_INTERFACE, "RegisterEndpoint"));
+
+ dbus_message_iter_init_append(m, &i);
+ pa_assert_se(dbus_message_iter_append_basic(&i, DBUS_TYPE_OBJECT_PATH, &endpoint));
+ dbus_message_iter_open_container(&i, DBUS_TYPE_ARRAY, DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING DBUS_TYPE_STRING_AS_STRING
+ DBUS_TYPE_VARIANT_AS_STRING DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &d);
+ pa_dbus_append_basic_variant_dict_entry(&d, "UUID", DBUS_TYPE_STRING, &uuid);
+ pa_dbus_append_basic_variant_dict_entry(&d, "Codec", DBUS_TYPE_BYTE, &codec_id);
+ pa_dbus_append_basic_array_variant_dict_entry(&d, "Capabilities", DBUS_TYPE_BYTE, &capabilities, capabilities_size);
+
+ dbus_message_iter_close_container(&i, &d);
+
+ send_and_add_to_pending(y, m, register_endpoint_reply, pa_xstrdup(endpoint));
+ }
++#else
+static void register_endpoint(pa_bluetooth_discovery *y, const char *path, const char *endpoint, const char *uuid) {
+ DBusMessage *m;
+ DBusMessageIter i, d;
+ uint8_t codec = 0;
+
+#ifdef BLUETOOTH_APTX_SUPPORT
+ if(pa_streq(endpoint,A2DP_APTX_SOURCE_ENDPOINT))
+ codec = A2DP_CODEC_VENDOR;
+#endif
+ pa_log_debug("Registering %s on adapter %s", endpoint, path);
+
+ pa_assert_se(m = dbus_message_new_method_call(BLUEZ_SERVICE, path, BLUEZ_MEDIA_INTERFACE, "RegisterEndpoint"));
+
+ dbus_message_iter_init_append(m, &i);
+ pa_assert_se(dbus_message_iter_append_basic(&i, DBUS_TYPE_OBJECT_PATH, &endpoint));
+ dbus_message_iter_open_container(&i, DBUS_TYPE_ARRAY, DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING DBUS_TYPE_STRING_AS_STRING
+ DBUS_TYPE_VARIANT_AS_STRING DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &d);
+ pa_dbus_append_basic_variant_dict_entry(&d, "UUID", DBUS_TYPE_STRING, &uuid);
+ pa_dbus_append_basic_variant_dict_entry(&d, "Codec", DBUS_TYPE_BYTE, &codec);
+
+ if (pa_streq(uuid, PA_BLUETOOTH_UUID_A2DP_SOURCE) || pa_streq(uuid, PA_BLUETOOTH_UUID_A2DP_SINK)) {
+#ifdef __TIZEN_BT__
+ if (codec == A2DP_CODEC_SBC) {
+ a2dp_sbc_t capabilities;
+ capabilities.channel_mode = SBC_CHANNEL_MODE_MONO | SBC_CHANNEL_MODE_DUAL_CHANNEL | SBC_CHANNEL_MODE_STEREO |
+ SBC_CHANNEL_MODE_JOINT_STEREO;
+ capabilities.frequency = SBC_SAMPLING_FREQ_16000 | SBC_SAMPLING_FREQ_32000 | SBC_SAMPLING_FREQ_44100 |
+ SBC_SAMPLING_FREQ_48000;
+ capabilities.allocation_method = SBC_ALLOCATION_SNR | SBC_ALLOCATION_LOUDNESS;
+ capabilities.subbands = SBC_SUBBANDS_4 | SBC_SUBBANDS_8;
+ capabilities.block_length = SBC_BLOCK_LENGTH_4 | SBC_BLOCK_LENGTH_8 | SBC_BLOCK_LENGTH_12 | SBC_BLOCK_LENGTH_16;
+ capabilities.min_bitpool = MIN_BITPOOL;
+ capabilities.max_bitpool = MAX_BITPOOL;
+ pa_dbus_append_basic_array_variant_dict_entry(&d, "Capabilities", DBUS_TYPE_BYTE, &capabilities, sizeof(capabilities));
+ }
+#ifdef BLUETOOTH_APTX_SUPPORT
+ if (codec == A2DP_CODEC_VENDOR ) {
+ /* 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));
+ }
+#endif /* BLUETOOTH_APTX_SUPPORT */
+#else
+ a2dp_sbc_t capabilities;
+ capabilities.channel_mode = SBC_CHANNEL_MODE_MONO | SBC_CHANNEL_MODE_DUAL_CHANNEL | SBC_CHANNEL_MODE_STEREO |
+ SBC_CHANNEL_MODE_JOINT_STEREO;
+ capabilities.frequency = SBC_SAMPLING_FREQ_16000 | SBC_SAMPLING_FREQ_32000 | SBC_SAMPLING_FREQ_44100 |
+ SBC_SAMPLING_FREQ_48000;
+ capabilities.allocation_method = SBC_ALLOCATION_SNR | SBC_ALLOCATION_LOUDNESS;
+ capabilities.subbands = SBC_SUBBANDS_4 | SBC_SUBBANDS_8;
+ capabilities.block_length = SBC_BLOCK_LENGTH_4 | SBC_BLOCK_LENGTH_8 | SBC_BLOCK_LENGTH_12 | SBC_BLOCK_LENGTH_16;
+ capabilities.min_bitpool = MIN_BITPOOL;
+ capabilities.max_bitpool = MAX_BITPOOL;
+ pa_dbus_append_basic_array_variant_dict_entry(&d, "Capabilities", DBUS_TYPE_BYTE, &capabilities, sizeof(capabilities));
+#endif /* __TIZEN_BT__ */
+ }
+
+ dbus_message_iter_close_container(&i, &d);
+
+ send_and_add_to_pending(y, m, register_endpoint_reply, pa_xstrdup(endpoint));
+}
++#endif
static void parse_interfaces_and_properties(pa_bluetooth_discovery *y, DBusMessageIter *dict_i) {
DBusMessageIter element_i;
if (pa_streq(interface, BLUEZ_ADAPTER_INTERFACE)) {
pa_bluetooth_adapter *a;
++#ifdef __TIZEN_BT_6__
+ unsigned a2dp_codec_i;
++#endif
if ((a = pa_hashmap_get(y->adapters, path))) {
pa_log_error("Found duplicated D-Bus path for adapter %s", path);
if (!a->valid)
return;
-
++#ifdef __TIZEN_BT_6__
+ /* Order is important. bluez prefers endpoints registered earlier.
+ * And codec with higher number has higher priority. So iterate in reverse order. */
+ for (a2dp_codec_i = pa_bluetooth_a2dp_codec_count(); a2dp_codec_i > 0; a2dp_codec_i--) {
+ const pa_a2dp_codec *a2dp_codec = pa_bluetooth_a2dp_codec_iter(a2dp_codec_i-1);
+ char *endpoint;
+
+ endpoint = pa_sprintf_malloc("%s/%s", A2DP_SINK_ENDPOINT, a2dp_codec->name);
+ register_endpoint(y, a2dp_codec, path, endpoint, PA_BLUETOOTH_UUID_A2DP_SINK);
+ pa_xfree(endpoint);
+
+ endpoint = pa_sprintf_malloc("%s/%s", A2DP_SOURCE_ENDPOINT, a2dp_codec->name);
+ register_endpoint(y, a2dp_codec, path, endpoint, PA_BLUETOOTH_UUID_A2DP_SOURCE);
+ pa_xfree(endpoint);
+ }
++#else
+ register_endpoint(y, path, A2DP_SOURCE_ENDPOINT, PA_BLUETOOTH_UUID_A2DP_SOURCE);
+ register_endpoint(y, path, A2DP_SINK_ENDPOINT, PA_BLUETOOTH_UUID_A2DP_SINK);
+#ifdef TIZEN_BT_A2DP_MULTISTREAM
+ register_endpoint(y, path, A2DP_SINK_ENDPOINT2, PA_BLUETOOTH_UUID_A2DP_SINK);
+#endif
+#ifdef BLUETOOTH_APTX_SUPPORT
+ register_endpoint(y, path, A2DP_APTX_SOURCE_ENDPOINT, PA_BLUETOOTH_UUID_A2DP_SOURCE);
+#endif
++#endif
+
} else if (pa_streq(interface, BLUEZ_DEVICE_INTERFACE)) {
if ((d = pa_hashmap_get(y->devices, path))) {
return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
}
++#ifndef __TIZEN_BT_6__
+static uint8_t a2dp_default_bitpool(uint8_t freq, uint8_t mode) {
+ /* These bitpool values were chosen based on the A2DP spec recommendation */
+ switch (freq) {
+ case SBC_SAMPLING_FREQ_16000:
+ case SBC_SAMPLING_FREQ_32000:
+ return 53;
+
+ case SBC_SAMPLING_FREQ_44100:
+
+ switch (mode) {
+ case SBC_CHANNEL_MODE_MONO:
+ case SBC_CHANNEL_MODE_DUAL_CHANNEL:
+ return 31;
+
+ case SBC_CHANNEL_MODE_STEREO:
+ case SBC_CHANNEL_MODE_JOINT_STEREO:
+#if defined(__TIZEN_BT__) && defined(ADJUST_ANDROID_BITPOOL)
+ return 35;
+#else
+ return 53;
+#endif
+ }
+
+ pa_log_warn("Invalid channel mode %u", mode);
+ return 53;
+
+ case SBC_SAMPLING_FREQ_48000:
+
+ switch (mode) {
+ case SBC_CHANNEL_MODE_MONO:
+ case SBC_CHANNEL_MODE_DUAL_CHANNEL:
+ return 29;
+
+ case SBC_CHANNEL_MODE_STEREO:
+ case SBC_CHANNEL_MODE_JOINT_STEREO:
+ return 51;
+ }
+
+ pa_log_warn("Invalid channel mode %u", mode);
+ return 51;
+ }
+
+ pa_log_warn("Invalid sampling freq %u", freq);
+ return 53;
+}
+
+#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
++#endif
+
const char *pa_bluetooth_profile_to_string(pa_bluetooth_profile_t profile) {
switch(profile) {
case PA_BLUETOOTH_PROFILE_A2DP_SINK:
return NULL;
}
++#ifdef __TIZEN_BT_6__
+ static const pa_a2dp_codec *a2dp_endpoint_to_a2dp_codec(const char *endpoint) {
+ const char *codec_name;
+
+ if (pa_startswith(endpoint, A2DP_SINK_ENDPOINT "/"))
+ codec_name = endpoint + strlen(A2DP_SINK_ENDPOINT "/");
+ else if (pa_startswith(endpoint, A2DP_SOURCE_ENDPOINT "/"))
+ codec_name = endpoint + strlen(A2DP_SOURCE_ENDPOINT "/");
+ else
+ return NULL;
+
+ return pa_bluetooth_get_a2dp_codec(codec_name);
+ }
++#endif
+
static DBusMessage *endpoint_set_configuration(DBusConnection *conn, DBusMessage *m, void *userdata) {
++#ifdef __TIZEN_BT_6__
+ pa_bluetooth_discovery *y = userdata;
+ pa_bluetooth_device *d;
+ pa_bluetooth_transport *t;
+ const pa_a2dp_codec *a2dp_codec = NULL;
+ const char *sender, *path, *endpoint_path, *dev_path = NULL, *uuid = NULL;
+ const uint8_t *config = NULL;
+ int size = 0;
+ pa_bluetooth_profile_t p = PA_BLUETOOTH_PROFILE_OFF;
+ DBusMessageIter args, props;
+ DBusMessage *r;
+
+ if (!dbus_message_iter_init(m, &args) || !pa_streq(dbus_message_get_signature(m), "oa{sv}")) {
+ pa_log_error("Invalid signature for method SetConfiguration()");
+ goto fail2;
+ }
+
+ dbus_message_iter_get_basic(&args, &path);
+
+ if (pa_hashmap_get(y->transports, path)) {
+ pa_log_error("Endpoint SetConfiguration(): Transport %s is already configured.", path);
+ goto fail2;
+ }
+
+ pa_assert_se(dbus_message_iter_next(&args));
+
+ dbus_message_iter_recurse(&args, &props);
+ if (dbus_message_iter_get_arg_type(&props) != DBUS_TYPE_DICT_ENTRY)
+ goto fail;
+
+ endpoint_path = dbus_message_get_path(m);
+
+ /* Read transport properties */
+ while (dbus_message_iter_get_arg_type(&props) == DBUS_TYPE_DICT_ENTRY) {
+ const char *key;
+ DBusMessageIter value, entry;
+ int var;
+
+ dbus_message_iter_recurse(&props, &entry);
+ dbus_message_iter_get_basic(&entry, &key);
+
+ dbus_message_iter_next(&entry);
+ dbus_message_iter_recurse(&entry, &value);
+
+ var = dbus_message_iter_get_arg_type(&value);
+
+ if (pa_streq(key, "UUID")) {
+ if (var != DBUS_TYPE_STRING) {
+ pa_log_error("Property %s of wrong type %c", key, (char)var);
+ goto fail;
+ }
+
+ dbus_message_iter_get_basic(&value, &uuid);
+
+ if (pa_startswith(endpoint_path, A2DP_SINK_ENDPOINT "/"))
+ p = PA_BLUETOOTH_PROFILE_A2DP_SOURCE;
+ else if (pa_startswith(endpoint_path, A2DP_SOURCE_ENDPOINT "/"))
+ p = PA_BLUETOOTH_PROFILE_A2DP_SINK;
+
+ if ((pa_streq(uuid, PA_BLUETOOTH_UUID_A2DP_SOURCE) && p != PA_BLUETOOTH_PROFILE_A2DP_SINK) ||
+ (pa_streq(uuid, PA_BLUETOOTH_UUID_A2DP_SINK) && p != PA_BLUETOOTH_PROFILE_A2DP_SOURCE)) {
+ pa_log_error("UUID %s of transport %s incompatible with endpoint %s", uuid, path, endpoint_path);
+ goto fail;
+ }
+ } else if (pa_streq(key, "Device")) {
+ if (var != DBUS_TYPE_OBJECT_PATH) {
+ pa_log_error("Property %s of wrong type %c", key, (char)var);
+ goto fail;
+ }
+
+ dbus_message_iter_get_basic(&value, &dev_path);
+ } else if (pa_streq(key, "Configuration")) {
+ DBusMessageIter array;
+
+ if (var != DBUS_TYPE_ARRAY) {
+ pa_log_error("Property %s of wrong type %c", key, (char)var);
+ goto fail;
+ }
+
+ dbus_message_iter_recurse(&value, &array);
+ var = dbus_message_iter_get_arg_type(&array);
+ if (var != DBUS_TYPE_BYTE) {
+ pa_log_error("%s is an array of wrong type %c", key, (char)var);
+ goto fail;
+ }
+
+ dbus_message_iter_get_fixed_array(&array, &config, &size);
+
+ a2dp_codec = a2dp_endpoint_to_a2dp_codec(endpoint_path);
+ pa_assert(a2dp_codec);
+
+ if (!a2dp_codec->is_configuration_valid(config, size))
+ goto fail;
+ }
+
+ dbus_message_iter_next(&props);
+ }
+
+ if (!a2dp_codec)
+ goto fail2;
+
+ if ((d = pa_hashmap_get(y->devices, dev_path))) {
+ if (!d->valid) {
+ pa_log_error("Information about device %s is invalid", dev_path);
+ goto fail2;
+ }
+ } else {
+ /* InterfacesAdded signal is probably on its way, device_info_valid is kept as 0. */
+ pa_log_warn("SetConfiguration() received for unknown device %s", dev_path);
+ d = device_create(y, dev_path);
+ }
+
+ if (d->transports[p] != NULL) {
+ pa_log_error("Cannot configure transport %s because profile %s is already used", path, pa_bluetooth_profile_to_string(p));
+ goto fail2;
+ }
+
+ sender = dbus_message_get_sender(m);
+
+ pa_assert_se(r = dbus_message_new_method_return(m));
+ pa_assert_se(dbus_connection_send(pa_dbus_connection_get(y->connection), r, NULL));
+ dbus_message_unref(r);
+
+ t = pa_bluetooth_transport_new(d, sender, path, p, config, size);
+ t->a2dp_codec = a2dp_codec;
+ t->acquire = bluez5_transport_acquire_cb;
+ t->release = bluez5_transport_release_cb;
+ pa_bluetooth_transport_put(t);
+
+ pa_log_debug("Transport %s available for profile %s", t->path, pa_bluetooth_profile_to_string(t->profile));
+
+ return NULL;
+
+ fail:
+ pa_log_error("Endpoint SetConfiguration(): invalid arguments");
+
+ fail2:
+ pa_assert_se(r = dbus_message_new_error(m, "org.bluez.Error.InvalidArguments", "Unable to set configuration"));
+ return r;
++#else
+ pa_bluetooth_discovery *y = userdata;
+ pa_bluetooth_device *d;
+ pa_bluetooth_transport *t;
+ const char *sender, *path, *endpoint_path, *dev_path = NULL, *uuid = NULL;
+#ifdef __TIZEN_BT__
+ uint8_t codec = 0;
+#endif
+ const uint8_t *config = NULL;
+ int size = 0;
+ pa_bluetooth_profile_t p = PA_BLUETOOTH_PROFILE_OFF;
+ DBusMessageIter args, props;
+ DBusMessage *r;
+
+ if (!dbus_message_iter_init(m, &args) || !pa_streq(dbus_message_get_signature(m), "oa{sv}")) {
+ pa_log_error("Invalid signature for method SetConfiguration()");
+ goto fail2;
+ }
+
+ dbus_message_iter_get_basic(&args, &path);
+
+ if (pa_hashmap_get(y->transports, path)) {
+ pa_log_error("Endpoint SetConfiguration(): Transport %s is already configured.", path);
+ goto fail2;
+ }
+
+ pa_assert_se(dbus_message_iter_next(&args));
+
+ dbus_message_iter_recurse(&args, &props);
+ if (dbus_message_iter_get_arg_type(&props) != DBUS_TYPE_DICT_ENTRY)
+ goto fail;
+
+ /* Read transport properties */
+ while (dbus_message_iter_get_arg_type(&props) == DBUS_TYPE_DICT_ENTRY) {
+ const char *key;
+ DBusMessageIter value, entry;
+ int var;
+
+ dbus_message_iter_recurse(&props, &entry);
+ dbus_message_iter_get_basic(&entry, &key);
+
+ dbus_message_iter_next(&entry);
+ dbus_message_iter_recurse(&entry, &value);
+
+ var = dbus_message_iter_get_arg_type(&value);
+
+ if (pa_streq(key, "UUID")) {
+ if (var != DBUS_TYPE_STRING) {
+ pa_log_error("Property %s of wrong type %c", key, (char)var);
+ goto fail;
+ }
+
+ dbus_message_iter_get_basic(&value, &uuid);
+
+ endpoint_path = dbus_message_get_path(m);
+#ifdef __TIZEN_BT__
+ pa_assert_se(endpoint_path);
+#endif
+
+#ifdef BLUETOOTH_APTX_SUPPORT
+ if (pa_streq(endpoint_path, A2DP_SOURCE_ENDPOINT) ||
+ pa_streq(endpoint_path, A2DP_APTX_SOURCE_ENDPOINT)) {
+ if (pa_streq(uuid, PA_BLUETOOTH_UUID_A2DP_SOURCE))
+ p = PA_BLUETOOTH_PROFILE_A2DP_SINK;
+ }
+#else
+ if (pa_streq(endpoint_path, A2DP_SOURCE_ENDPOINT)) {
+ if (pa_streq(uuid, PA_BLUETOOTH_UUID_A2DP_SOURCE))
+ p = PA_BLUETOOTH_PROFILE_A2DP_SINK;
+ }
+#endif
+ else if (pa_streq(endpoint_path, A2DP_SINK_ENDPOINT)) {
+ if (pa_streq(uuid, PA_BLUETOOTH_UUID_A2DP_SINK))
+ p = PA_BLUETOOTH_PROFILE_A2DP_SOURCE;
+ }
+#ifdef TIZEN_BT_A2DP_MULTISTREAM
+ else if (pa_streq(endpoint_path, A2DP_SINK_ENDPOINT2)) {
+ if (pa_streq(uuid, PA_BLUETOOTH_UUID_A2DP_SINK))
+ p = PA_BLUETOOTH_PROFILE_A2DP_SOURCE;
+ }
+#endif
+
+ if (p == PA_BLUETOOTH_PROFILE_OFF) {
+ pa_log_error("UUID %s of transport %s incompatible with endpoint %s", uuid, path, endpoint_path);
+ goto fail;
+ }
+#ifdef __TIZEN_BT__
+ } else if (pa_streq(key, "Codec")) {
+ if (var != DBUS_TYPE_BYTE) {
+ pa_log_error("Property %s of wrong type %c", key, (char)var);
+ goto fail;
+ }
+
+ dbus_message_iter_get_basic(&value, &codec);
+#endif
+ } else if (pa_streq(key, "Device")) {
+ if (var != DBUS_TYPE_OBJECT_PATH) {
+ pa_log_error("Property %s of wrong type %c", key, (char)var);
+ goto fail;
+ }
+
+ dbus_message_iter_get_basic(&value, &dev_path);
+ } else if (pa_streq(key, "Configuration")) {
+ DBusMessageIter array;
+#ifndef BLUETOOTH_APTX_SUPPORT
+ a2dp_sbc_t *c;
+#endif
+
+ if (var != DBUS_TYPE_ARRAY) {
+ pa_log_error("Property %s of wrong type %c", key, (char)var);
+ goto fail;
+ }
+
+ dbus_message_iter_recurse(&value, &array);
+ var = dbus_message_iter_get_arg_type(&array);
+ if (var != DBUS_TYPE_BYTE) {
+ pa_log_error("%s is an array of wrong type %c", key, (char)var);
+ goto fail;
+ }
+
+ dbus_message_iter_get_fixed_array(&array, &config, &size);
+#ifndef BLUETOOTH_APTX_SUPPORT
+ if (size != sizeof(a2dp_sbc_t)) {
+ pa_log_error("Configuration array of invalid size");
+ goto fail;
+ }
+
+ c = (a2dp_sbc_t *) config;
+
+ if (c->frequency != SBC_SAMPLING_FREQ_16000 && c->frequency != SBC_SAMPLING_FREQ_32000 &&
+ c->frequency != SBC_SAMPLING_FREQ_44100 && c->frequency != SBC_SAMPLING_FREQ_48000) {
+ pa_log_error("Invalid sampling frequency in configuration");
+ goto fail;
+ }
+
+ if (c->channel_mode != SBC_CHANNEL_MODE_MONO && c->channel_mode != SBC_CHANNEL_MODE_DUAL_CHANNEL &&
+ c->channel_mode != SBC_CHANNEL_MODE_STEREO && c->channel_mode != SBC_CHANNEL_MODE_JOINT_STEREO) {
+ pa_log_error("Invalid channel mode in configuration");
+ goto fail;
+ }
+
+ if (c->allocation_method != SBC_ALLOCATION_SNR && c->allocation_method != SBC_ALLOCATION_LOUDNESS) {
+ pa_log_error("Invalid allocation method in configuration");
+ goto fail;
+ }
+
+ if (c->subbands != SBC_SUBBANDS_4 && c->subbands != SBC_SUBBANDS_8) {
+ pa_log_error("Invalid SBC subbands in configuration");
+ goto fail;
+ }
+
+ if (c->block_length != SBC_BLOCK_LENGTH_4 && c->block_length != SBC_BLOCK_LENGTH_8 &&
+ c->block_length != SBC_BLOCK_LENGTH_12 && c->block_length != SBC_BLOCK_LENGTH_16) {
+ pa_log_error("Invalid block length in configuration");
+ goto fail;
+ }
+#endif
+ }
+
+ dbus_message_iter_next(&props);
+ }
+
+ if ((d = pa_hashmap_get(y->devices, dev_path))) {
+ if (!d->valid) {
+ pa_log_error("Information about device %s is invalid", dev_path);
+ goto fail2;
+ }
+ } else {
+ /* InterfacesAdded signal is probably on its way, device_info_valid is kept as 0. */
+ pa_log_warn("SetConfiguration() received for unknown device %s", dev_path);
+ d = device_create(y, dev_path);
+ }
+
+ if (d->transports[p] != NULL) {
+ pa_log_error("Cannot configure transport %s because profile %s is already used", path, pa_bluetooth_profile_to_string(p));
+ goto fail2;
+ }
+
+ sender = dbus_message_get_sender(m);
+
+ pa_assert_se(r = dbus_message_new_method_return(m));
+ pa_assert_se(dbus_connection_send(pa_dbus_connection_get(y->connection), r, NULL));
+ dbus_message_unref(r);
+
+ t = pa_bluetooth_transport_new(d, sender, path, p, config, size);
+#ifdef __TIZEN_BT__
+ t->codec = codec;
+ d->transports[p] = t;
+#endif
+ t->acquire = bluez5_transport_acquire_cb;
+ t->release = bluez5_transport_release_cb;
+ pa_bluetooth_transport_put(t);
+
+ pa_log_debug("Transport %s available for profile %s", t->path, pa_bluetooth_profile_to_string(t->profile));
+
+ return NULL;
+
+fail:
+ pa_log_error("Endpoint SetConfiguration(): invalid arguments");
+
+fail2:
+ pa_assert_se(r = dbus_message_new_error(m, "org.bluez.Error.InvalidArguments", "Unable to set configuration"));
+ return r;
++#endif
}
static DBusMessage *endpoint_select_configuration(DBusConnection *conn, DBusMessage *m, void *userdata) {
pa_bluetooth_discovery *y = userdata;
++#ifndef __TIZEN_BT_6__
+ a2dp_sbc_t *cap, config;
+ uint8_t *pconf = (uint8_t *) &config;
+ int i, size;
+ DBusMessage *r;
+ DBusError err;
+
+ static const struct {
+ uint32_t rate;
+ uint8_t cap;
+ } freq_table[] = {
+ { 16000U, SBC_SAMPLING_FREQ_16000 },
+ { 32000U, SBC_SAMPLING_FREQ_32000 },
+ { 44100U, SBC_SAMPLING_FREQ_44100 },
+ { 48000U, SBC_SAMPLING_FREQ_48000 }
+ };
++#else
+ const char *endpoint_path;
+ uint8_t *cap;
+ int size;
+ const pa_a2dp_codec *a2dp_codec;
+ uint8_t config[MAX_A2DP_CAPS_SIZE];
+ uint8_t *config_ptr = config;
+ size_t config_size;
+ DBusMessage *r;
+ DBusError err;
+
+ endpoint_path = dbus_message_get_path(m);
++#endif
+
++#ifndef __TIZEN_BT_6__
+#ifdef BLUETOOTH_APTX_SUPPORT
+ if (dbus_message_has_path(m, A2DP_APTX_SOURCE_ENDPOINT))
+ return endpoint_select_configuration_for_aptx(conn ,m ,userdata);
+#endif
++#endif
dbus_error_init(&err);
goto fail;
}
++#ifndef __TIZEN_BT_6__
+ if (size != sizeof(config)) {
+ pa_log_error("Capabilities array has invalid size");
+ goto fail;
+ }
+
+ pa_zero(config);
+
+ /* Find the lowest freq that is at least as high as the requested sampling rate */
+ for (i = 0; (unsigned) i < PA_ELEMENTSOF(freq_table); i++)
+ if (freq_table[i].rate >= y->core->default_sample_spec.rate && (cap->frequency & freq_table[i].cap)) {
+ config.frequency = freq_table[i].cap;
+ break;
+ }
+
+ if ((unsigned) i == PA_ELEMENTSOF(freq_table)) {
+ for (--i; i >= 0; i--) {
+ if (cap->frequency & freq_table[i].cap) {
+ config.frequency = freq_table[i].cap;
+ break;
+ }
+ }
+
+ if (i < 0) {
+ pa_log_error("Not suitable sample rate");
+ goto fail;
+ }
+ }
+
+ pa_assert((unsigned) i < PA_ELEMENTSOF(freq_table));
+
+ if (y->core->default_sample_spec.channels <= 1) {
+ if (cap->channel_mode & SBC_CHANNEL_MODE_MONO)
+ config.channel_mode = SBC_CHANNEL_MODE_MONO;
+ else if (cap->channel_mode & SBC_CHANNEL_MODE_JOINT_STEREO)
+ config.channel_mode = SBC_CHANNEL_MODE_JOINT_STEREO;
+ else if (cap->channel_mode & SBC_CHANNEL_MODE_STEREO)
+ config.channel_mode = SBC_CHANNEL_MODE_STEREO;
+ else if (cap->channel_mode & SBC_CHANNEL_MODE_DUAL_CHANNEL)
+ config.channel_mode = SBC_CHANNEL_MODE_DUAL_CHANNEL;
+ else {
+ pa_log_error("No supported channel modes");
+ goto fail;
+ }
+ }
+
+ if (y->core->default_sample_spec.channels >= 2) {
+ if (cap->channel_mode & SBC_CHANNEL_MODE_JOINT_STEREO)
+ config.channel_mode = SBC_CHANNEL_MODE_JOINT_STEREO;
+ else if (cap->channel_mode & SBC_CHANNEL_MODE_STEREO)
+ config.channel_mode = SBC_CHANNEL_MODE_STEREO;
+ else if (cap->channel_mode & SBC_CHANNEL_MODE_DUAL_CHANNEL)
+ config.channel_mode = SBC_CHANNEL_MODE_DUAL_CHANNEL;
+ else if (cap->channel_mode & SBC_CHANNEL_MODE_MONO)
+ config.channel_mode = SBC_CHANNEL_MODE_MONO;
+ else {
+ pa_log_error("No supported channel modes");
+ goto fail;
+ }
+ }
+
+ if (cap->block_length & SBC_BLOCK_LENGTH_16)
+ config.block_length = SBC_BLOCK_LENGTH_16;
+ else if (cap->block_length & SBC_BLOCK_LENGTH_12)
+ config.block_length = SBC_BLOCK_LENGTH_12;
+ else if (cap->block_length & SBC_BLOCK_LENGTH_8)
+ config.block_length = SBC_BLOCK_LENGTH_8;
+ else if (cap->block_length & SBC_BLOCK_LENGTH_4)
+ config.block_length = SBC_BLOCK_LENGTH_4;
+ else {
+ pa_log_error("No supported block lengths");
+ goto fail;
+ }
+
+ if (cap->subbands & SBC_SUBBANDS_8)
+ config.subbands = SBC_SUBBANDS_8;
+ else if (cap->subbands & SBC_SUBBANDS_4)
+ config.subbands = SBC_SUBBANDS_4;
+ else {
+ pa_log_error("No supported subbands");
+ goto fail;
+ }
+
+ if (cap->allocation_method & SBC_ALLOCATION_LOUDNESS)
+ config.allocation_method = SBC_ALLOCATION_LOUDNESS;
+ else if (cap->allocation_method & SBC_ALLOCATION_SNR)
+ config.allocation_method = SBC_ALLOCATION_SNR;
+
+ config.min_bitpool = (uint8_t) PA_MAX(MIN_BITPOOL, cap->min_bitpool);
+ config.max_bitpool = (uint8_t) PA_MIN(a2dp_default_bitpool(config.frequency, config.channel_mode), cap->max_bitpool);
+
+ if (config.min_bitpool > config.max_bitpool)
+ 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));
++#else
+ a2dp_codec = a2dp_endpoint_to_a2dp_codec(endpoint_path);
+ pa_assert(a2dp_codec);
+
+ config_size = a2dp_codec->fill_preferred_configuration(&y->core->default_sample_spec, cap, size, config);
+ if (config_size == 0)
+ 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, &config_ptr, config_size, DBUS_TYPE_INVALID));
++#endif
return r;
}
static DBusMessage *endpoint_release(DBusConnection *conn, DBusMessage *m, void *userdata) {
++#ifndef __TIZEN_BT_6__
+ DBusMessage *r;
+
+ pa_assert_se(r = dbus_message_new_error(m, BLUEZ_MEDIA_ENDPOINT_INTERFACE ".Error.NotImplemented",
+ "Method not implemented"));
++#else
+ DBusMessage *r = NULL;
+
+ /* From doc/media-api.txt in bluez:
+ *
+ * This method gets called when the service daemon
+ * unregisters the endpoint. An endpoint can use it to do
+ * cleanup tasks. There is no need to unregister the
+ * endpoint, because when this method gets called it has
+ * already been unregistered.
+ *
+ * We don't have any cleanup to do. */
+
+ /* Reply only if requested. Generally bluetoothd doesn't request a reply
+ * to the Release() call. Sending replies when not requested on the system
+ * bus tends to cause errors in syslog from dbus-daemon, because it
+ * doesn't let unexpected replies through, so it's important to have this
+ * check here. */
+ if (!dbus_message_get_no_reply(m))
+ pa_assert_se(r = dbus_message_new_method_return(m));
++#endif
return r;
}
++#ifndef __TIZEN_BT_6__
+#ifdef __TIZEN_BT__
+static DBusMessage *endpoint_suspend_media(DBusConnection *conn, DBusMessage *m, void *userdata) {
+ pa_bluetooth_discovery *y = userdata;
+ pa_bluetooth_transport *t;
+ DBusMessage *r;
+
+ pa_log_debug("dbus Call from to Suspend Media");
+ if (!(t = pa_hashmap_first(y->transports)))
+ goto fail;
+
+ pa_hook_fire(&t->device->discovery->hooks[PA_BLUETOOTH_HOOK_SCO_STATE_CHANGED], t);
+
+ pa_assert_se(r = dbus_message_new_method_return(m));
+
+ return r;
+
+fail:
+ pa_assert_se(r = dbus_message_new_error(m, "org.bluez.Error.InvalidArguments", "Unable to clear configuration"));
+ return r;
+}
+#endif
++#endif
+
static DBusHandlerResult endpoint_handler(DBusConnection *c, DBusMessage *m, void *userdata) {
struct pa_bluetooth_discovery *y = userdata;
DBusMessage *r = NULL;
pa_log_debug("dbus: path=%s, interface=%s, member=%s", path, interface, member);
++#ifndef __TIZEN_BT_6__
+#ifdef __TIZEN_BT__
+ pa_assert_se(path);
+#endif
+
+#ifdef BLUETOOTH_APTX_SUPPORT
+#ifdef TIZEN_BT_A2DP_MULTISTREAM
+ if (!pa_streq(path, A2DP_SOURCE_ENDPOINT) && !pa_streq(path, A2DP_SINK_ENDPOINT) && !pa_streq(path, A2DP_SINK_ENDPOINT2)
+ && !pa_streq(path,A2DP_APTX_SOURCE_ENDPOINT))
+ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+#else
+ if (!pa_streq(path, A2DP_SOURCE_ENDPOINT) && !pa_streq(path, A2DP_SINK_ENDPOINT) && !pa_streq(path,A2DP_APTX_SOURCE_ENDPOINT))
+ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+#endif
+#else
+#ifdef TIZEN_BT_A2DP_MULTISTREAM
+ if (!pa_streq(path, A2DP_SOURCE_ENDPOINT) && !pa_streq(path, A2DP_SINK_ENDPOINT) && !pa_streq(path, A2DP_SINK_ENDPOINT2))
+ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+#else
+ if (!pa_streq(path, A2DP_SOURCE_ENDPOINT) && !pa_streq(path, A2DP_SINK_ENDPOINT))
+ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+#endif
+#endif /* BLUETOOTH_APTX_SUPPORT */
++#endif
++
++#ifdef __TIZEN_BT_6__
+ if (!a2dp_endpoint_to_a2dp_codec(path))
+ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
++#endif
if (dbus_message_is_method_call(m, "org.freedesktop.DBus.Introspectable", "Introspect")) {
const char *xml = ENDPOINT_INTROSPECT_XML;
r = endpoint_clear_configuration(c, m, userdata);
else if (dbus_message_is_method_call(m, BLUEZ_MEDIA_ENDPOINT_INTERFACE, "Release"))
r = endpoint_release(c, m, userdata);
++#ifndef __TIZEN_BT_6__
+#ifdef __TIZEN_BT__
+ else if (dbus_message_is_method_call(m, BLUEZ_MEDIA_ENDPOINT_INTERFACE, "SuspendMedia"))
+ endpoint_suspend_media(c, m, userdata);
+#endif
++#endif
else
return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
return DBUS_HANDLER_RESULT_HANDLED;
}
++#ifndef __TIZEN_BT_6__
+static void endpoint_init(pa_bluetooth_discovery *y, pa_bluetooth_profile_t profile) {
+ static const DBusObjectPathVTable vtable_endpoint = {
+ .message_function = endpoint_handler,
+ };
+
+ pa_assert(y);
+
+ switch(profile) {
+ case PA_BLUETOOTH_PROFILE_A2DP_SINK:
+ pa_assert_se(dbus_connection_register_object_path(pa_dbus_connection_get(y->connection), A2DP_SOURCE_ENDPOINT,
+ &vtable_endpoint, y));
+#ifdef BLUETOOTH_APTX_SUPPORT
+ pa_assert_se(dbus_connection_register_object_path(pa_dbus_connection_get(y->connection), A2DP_APTX_SOURCE_ENDPOINT,
+ &vtable_endpoint, y));
+#endif
+ break;
+ case PA_BLUETOOTH_PROFILE_A2DP_SOURCE:
+ pa_assert_se(dbus_connection_register_object_path(pa_dbus_connection_get(y->connection), A2DP_SINK_ENDPOINT,
+ &vtable_endpoint, y));
+#ifdef TIZEN_BT_A2DP_MULTISTREAM
+ pa_assert_se(dbus_connection_register_object_path(pa_dbus_connection_get(y->connection), A2DP_SINK_ENDPOINT2,
+ &vtable_endpoint, y));
+#endif
+ break;
+ default:
+ pa_assert_not_reached();
+ break;
+ }
+}
++#else
+ static void endpoint_init(pa_bluetooth_discovery *y, const char *endpoint) {
+ static const DBusObjectPathVTable vtable_endpoint = {
+ .message_function = endpoint_handler,
+ };
+
+ pa_assert(y);
+ pa_assert(endpoint);
+
+ pa_assert_se(dbus_connection_register_object_path(pa_dbus_connection_get(y->connection), endpoint,
+ &vtable_endpoint, y));
+ }
++#endif
++#ifndef __TIZEN_BT_6__
+static void endpoint_done(pa_bluetooth_discovery *y, pa_bluetooth_profile_t profile) {
+ pa_assert(y);
+
+ switch(profile) {
+ case PA_BLUETOOTH_PROFILE_A2DP_SINK:
+ dbus_connection_unregister_object_path(pa_dbus_connection_get(y->connection), A2DP_SOURCE_ENDPOINT);
+#ifdef BLUETOOTH_APTX_SUPPORT
+ dbus_connection_unregister_object_path(pa_dbus_connection_get(y->connection), A2DP_APTX_SOURCE_ENDPOINT);
+#endif
+ break;
+ case PA_BLUETOOTH_PROFILE_A2DP_SOURCE:
+ dbus_connection_unregister_object_path(pa_dbus_connection_get(y->connection), A2DP_SINK_ENDPOINT);
+#ifdef TIZEN_BT_A2DP_MULTISTREAM
+ dbus_connection_unregister_object_path(pa_dbus_connection_get(y->connection), A2DP_SINK_ENDPOINT2);
+ break;
+#endif
+ default:
+ pa_assert_not_reached();
+ break;
+ }
+}
++#else
+ static void endpoint_done(pa_bluetooth_discovery *y, const char *endpoint) {
+ pa_assert(y);
+ pa_assert(endpoint);
+
+ dbus_connection_unregister_object_path(pa_dbus_connection_get(y->connection), endpoint);
+ }
++#endif
pa_bluetooth_discovery* pa_bluetooth_discovery_get(pa_core *c, int headset_backend) {
pa_bluetooth_discovery *y;
DBusError err;
DBusConnection *conn;
++#ifdef __TIZEN_BT_6__
+ unsigned i;
++#else
+ unsigned i, count;
+ const pa_a2dp_codec *a2dp_codec;
+ char *endpoint;
++#endif
y = pa_xnew0(pa_bluetooth_discovery, 1);
PA_REFCNT_INIT(y);
}
y->matches_added = true;
++#ifdef __TIZEN_BT_6__
+ count = pa_bluetooth_a2dp_codec_count();
+ for (i = 0; i < count; i++) {
+ a2dp_codec = pa_bluetooth_a2dp_codec_iter(i);
+
+ endpoint = pa_sprintf_malloc("%s/%s", A2DP_SINK_ENDPOINT, a2dp_codec->name);
+ endpoint_init(y, endpoint);
+ pa_xfree(endpoint);
+
+ endpoint = pa_sprintf_malloc("%s/%s", A2DP_SOURCE_ENDPOINT, a2dp_codec->name);
+ endpoint_init(y, endpoint);
+ pa_xfree(endpoint);
+ }
++#else
+ endpoint_init(y, PA_BLUETOOTH_PROFILE_A2DP_SINK);
+ endpoint_init(y, PA_BLUETOOTH_PROFILE_A2DP_SOURCE);
++#endif
+
+#ifndef __TIZEN_BT__
+ y->hf_audio_agent = hf_audio_agent_init(c);
+#endif
get_managed_objects(y);
if (y->filter_added)
dbus_connection_remove_filter(pa_dbus_connection_get(y->connection), filter_cb, y);
++#ifdef __TIZEN_BT_6__
+ count = pa_bluetooth_a2dp_codec_count();
+ for (i = 0; i < count; i++) {
+ a2dp_codec = pa_bluetooth_a2dp_codec_iter(i);
+
+ endpoint = pa_sprintf_malloc("%s/%s", A2DP_SINK_ENDPOINT, a2dp_codec->name);
+ endpoint_done(y, endpoint);
+ pa_xfree(endpoint);
+
+ endpoint = pa_sprintf_malloc("%s/%s", A2DP_SOURCE_ENDPOINT, a2dp_codec->name);
+ endpoint_done(y, endpoint);
+ pa_xfree(endpoint);
+ }
++#else
+ endpoint_done(y, PA_BLUETOOTH_PROFILE_A2DP_SINK);
+ endpoint_done(y, PA_BLUETOOTH_PROFILE_A2DP_SOURCE);
++#endif
pa_dbus_connection_unref(y->connection);
}
typedef enum pa_bluetooth_hook {
PA_BLUETOOTH_HOOK_DEVICE_CONNECTION_CHANGED, /* Call data: pa_bluetooth_device */
+ PA_BLUETOOTH_HOOK_DEVICE_UNLINK, /* Call data: pa_bluetooth_device */
PA_BLUETOOTH_HOOK_TRANSPORT_STATE_CHANGED, /* Call data: pa_bluetooth_transport */
+#ifdef __TIZEN_BT__
+ PA_BLUETOOTH_HOOK_SCO_STATE_CHANGED, /* Call data: pa_bluetooth_transport */
+#endif
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
return PA_HOOK_OK;
if (u->enable_a2dp_source && pa_streq(s, "a2dp_source"))
+#ifdef __TIZEN__
+ role = "media";
+#else
role = "music";
- /* TODO: remove hfgw when we remove BlueZ 4 support */
- else if (u->enable_ag && (pa_streq(s, "hfgw") || pa_streq(s, "headset_audio_gateway")))
+#endif
+ else if (u->enable_ag && pa_streq(s, "headset_audio_gateway"))
role = "phone";
else {
pa_log_debug("Profile %s cannot be selected for loopback", s);
#include <errno.h>
#include <arpa/inet.h>
++#ifndef __TIZEN_BT_6__
+#include <sbc/sbc.h>
++#endif
#include <pulse/rtclock.h>
#include <pulse/timeval.h>
#include <pulsecore/thread-mq.h>
#include <pulsecore/time-smoother.h>
+#ifdef __TIZEN_BT__
+#include <pulsecore/sink.h>
+#include <pulsecore/namereg.h>
+#include <dirent.h>
+#include <dlfcn.h>
+#endif
+
#include "a2dp-codecs.h"
+ #include "a2dp-codec-util.h"
#include "bluez5-util.h"
-
- #include "module-bluez5-device-symdef.h"
++#ifndef __TIZEN_BT_6__
+#include "rtp.h"
++#endif
+
+#ifdef BLUETOOTH_APTX_SUPPORT
+#include <dlfcn.h>
+#endif
PA_MODULE_AUTHOR("João Paulo Rechi Vita");
PA_MODULE_DESCRIPTION("BlueZ 5 Bluetooth audio sink and source");
PA_MODULE_USAGE("path=<device object path>"
"autodetect_mtu=<boolean>");
++#ifndef __TIZEN_BT_6__
+#define MAX_PLAYBACK_CATCH_UP_USEC (100 * PA_USEC_PER_MSEC)
++#endif
#define FIXED_LATENCY_PLAYBACK_A2DP (25 * PA_USEC_PER_MSEC)
++#ifdef __TIZEN_BT_6__
+ #define FIXED_LATENCY_PLAYBACK_SCO (25 * PA_USEC_PER_MSEC)
++#else
+#define FIXED_LATENCY_PLAYBACK_SCO (125 * PA_USEC_PER_MSEC)
++#endif
#define FIXED_LATENCY_RECORD_A2DP (25 * PA_USEC_PER_MSEC)
#define FIXED_LATENCY_RECORD_SCO (25 * PA_USEC_PER_MSEC)
++#ifndef __TIZEN_BT_6__
+#define BITPOOL_DEC_LIMIT 32
+#define BITPOOL_DEC_STEP 5
#define HSP_MAX_GAIN 15
++#endif
static const char* const valid_modargs[] = {
"path",
pa_usec_t started_at;
pa_smoother *read_smoother;
pa_memchunk write_memchunk;
+ pa_sample_spec sample_spec;
+ struct sbc_info sbc_info;
+
+#ifdef __TIZEN_BT__
+ pa_modargs *modargs;
+ struct pa_bluetooth_vendor_codec_ops *selected_ops;
+ pa_proplist *vendor_codec_list;
+ bool is_aptx_supported;
+#endif
+
+ const pa_a2dp_codec *a2dp_codec;
+
+ void *encoder_info;
+ pa_sample_spec encoder_sample_spec;
+ void *encoder_buffer; /* Codec transfer buffer */
+ size_t encoder_buffer_size; /* Size of the buffer */
+
+ void *decoder_info;
+ pa_sample_spec decoder_sample_spec;
+ void *decoder_buffer; /* Codec transfer buffer */
+ size_t decoder_buffer_size; /* Size of the buffer */
};
typedef enum pa_bluetooth_form_factor {
* issues in our Bluetooth adapter. In these cases, in order to avoid
* an assertion failure due to unaligned data, just discard the whole
* packet */
++#ifdef __TIZEN_BT_6__
+ if (!pa_frame_aligned(l, &u->decoder_sample_spec)) {
++#else
+ if (!pa_frame_aligned(l, &u->sample_spec)) {
++#endif
pa_log_warn("SCO packet received of unaligned size: %zu", l);
pa_memblock_unref(memchunk.memblock);
return -1;
tstamp = pa_rtclock_now();
}
++#ifdef __TIZEN_BT_6__
+ pa_smoother_put(u->read_smoother, tstamp, pa_bytes_to_usec(u->read_index, &u->decoder_sample_spec));
++#else
+ pa_smoother_put(u->read_smoother, tstamp, pa_bytes_to_usec(u->read_index, &u->sample_spec));
++#endif
pa_smoother_resume(u->read_smoother, tstamp, true);
pa_source_post(u->source, &memchunk);
}
/* Run from IO thread */
++#ifdef __TIZEN_BT_6__
+ static void a2dp_prepare_encoder_buffer(struct userdata *u) {
+ pa_assert(u);
+
+ if (u->encoder_buffer_size < u->write_link_mtu) {
+ pa_xfree(u->encoder_buffer);
+ u->encoder_buffer = pa_xmalloc(u->write_link_mtu);
+ }
+
+ /* Encoder buffer cannot be larger then link MTU, otherwise
+ * encode method would produce larger packets then link MTU */
+ u->encoder_buffer_size = u->write_link_mtu;
+ }
++#else
+static void a2dp_prepare_buffer(struct userdata *u) {
+ size_t min_buffer_size = PA_MAX(u->read_link_mtu, u->write_link_mtu);
+
+ pa_assert(u);
+
+ if (u->sbc_info.buffer_size >= min_buffer_size)
+ return;
+
+ u->sbc_info.buffer_size = 2 * min_buffer_size;
+ pa_xfree(u->sbc_info.buffer);
+ u->sbc_info.buffer = pa_xmalloc(u->sbc_info.buffer_size);
+}
++#endif
+
+ /* Run from IO thread */
+ static void a2dp_prepare_decoder_buffer(struct userdata *u) {
+ pa_assert(u);
+
+ if (u->decoder_buffer_size < u->read_link_mtu) {
+ pa_xfree(u->decoder_buffer);
+ u->decoder_buffer = pa_xmalloc(u->read_link_mtu);
+ }
+
+ /* Decoder buffer cannot be larger then link MTU, otherwise
+ * decode method would produce larger output then read_block_size */
+ u->decoder_buffer_size = u->read_link_mtu;
+ }
+
+ /* Run from IO thread */
+ static int a2dp_write_buffer(struct userdata *u, size_t nbytes) {
+ int ret = 0;
+
+ /* Encoder function of A2DP codec may provide empty buffer, in this case do
+ * not post any empty buffer via A2DP socket. It may be because of codec
+ * internal state, e.g. encoder is waiting for more samples so it can
+ * provide encoded data. */
+ if (PA_UNLIKELY(!nbytes)) {
+ u->write_index += (uint64_t) u->write_memchunk.length;
+ pa_memblock_unref(u->write_memchunk.memblock);
+ pa_memchunk_reset(&u->write_memchunk);
+ return 0;
+ }
+
+ for (;;) {
+ ssize_t l;
+
+ l = pa_write(u->stream_fd, u->encoder_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 */
+ pa_log_debug("Got EAGAIN on write() after POLLOUT, probably there is a temporary connection loss.");
+ 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;
+ }
/* Run from IO thread */
static int a2dp_process_render(struct userdata *u) {
- struct rtp_payload *payload;
++#ifdef __TIZEN_BT_6__
+ const uint8_t *ptr;
+ size_t processed;
+ size_t length;
+
+ pa_assert(u);
+ pa_assert(u->profile == PA_BLUETOOTH_PROFILE_A2DP_SINK);
+ pa_assert(u->sink);
+ pa_assert(u->a2dp_codec);
+
+ /* 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_encoder_buffer(u);
+
+ /* Try to create a packet of the full MTU */
+ ptr = (const uint8_t *) pa_memblock_acquire_chunk(&u->write_memchunk);
+
+ length = u->a2dp_codec->encode_buffer(u->encoder_info, u->write_index / pa_frame_size(&u->encoder_sample_spec), ptr, u->write_memchunk.length, u->encoder_buffer, u->encoder_buffer_size, &processed);
+
+ pa_memblock_release(u->write_memchunk.memblock);
+
+ if (processed != u->write_memchunk.length) {
+ pa_log_error("Encoding error");
+ return -1;
+ }
+
+ return a2dp_write_buffer(u, length);
++#else
+ struct sbc_info *sbc_info;
+ struct rtp_header *header;
- payload = (struct rtp_payload*) ((uint8_t*) sbc_info->buffer + sizeof(*header));
++ struct rtp_sbc_payload *payload;
+ size_t nbytes;
+ void *d;
+ const void *p;
+ size_t to_write, to_encode;
+ unsigned frame_count;
+ int ret = 0;
+
+ pa_assert(u);
+ pa_assert(u->profile == PA_BLUETOOTH_PROFILE_A2DP_SINK);
+ 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);
+
+ sbc_info = &u->sbc_info;
+ header = sbc_info->buffer;
++ payload = (struct rtp_sbc_payload*) ((uint8_t*) sbc_info->buffer + sizeof(*header));
+
+ frame_count = 0;
+
+ /* Try to create a packet of the full MTU */
+
+ p = (const uint8_t *) pa_memblock_acquire_chunk(&u->write_memchunk);
+ to_encode = u->write_memchunk.length;
+
+ d = (uint8_t*) sbc_info->buffer + sizeof(*header) + sizeof(*payload);
+ to_write = sbc_info->buffer_size - sizeof(*header) - sizeof(*payload);
+
+ while (PA_LIKELY(to_encode > 0 && to_write > 0)) {
+ ssize_t written;
+ ssize_t encoded;
+
+ encoded = sbc_encode(&sbc_info->sbc,
+ p, to_encode,
+ d, to_write,
+ &written);
+
+ if (PA_UNLIKELY(encoded <= 0)) {
+ pa_log_error("SBC encoding error (%li)", (long) encoded);
+ pa_memblock_release(u->write_memchunk.memblock);
+ return -1;
+ }
+
+ pa_assert_fp((size_t) encoded <= to_encode);
+ pa_assert_fp((size_t) encoded == sbc_info->codesize);
+
+ pa_assert_fp((size_t) written <= to_write);
+ pa_assert_fp((size_t) written == sbc_info->frame_length);
+
+ p = (const uint8_t*) p + encoded;
+ to_encode -= encoded;
+
+ d = (uint8_t*) d + written;
+ to_write -= written;
+
+ frame_count++;
+ }
+
+ pa_memblock_release(u->write_memchunk.memblock);
+
+ pa_assert(to_encode == 0);
+
+ PA_ONCE_BEGIN {
+ pa_log_debug("Using SBC encoder implementation: %s", pa_strnull(sbc_get_implementation_info(&sbc_info->sbc)));
+ } PA_ONCE_END;
+
+ /* write it to the fifo */
+ memset(sbc_info->buffer, 0, sizeof(*header) + sizeof(*payload));
+ header->v = 2;
+ header->pt = 1;
+ header->sequence_number = htons(sbc_info->seq_num++);
+ header->timestamp = htonl(u->write_index / pa_frame_size(&u->sample_spec));
+ header->ssrc = htonl(1);
+ payload->frame_count = frame_count;
+
+ nbytes = (uint8_t*) d - (uint8_t*) sbc_info->buffer;
+
+ for (;;) {
+ ssize_t l;
+
+ l = pa_write(u->stream_fd, sbc_info->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
}
+#ifdef __TIZEN_BT__
+/* Run from IO thread */
+static int a2dp_process_null_render(struct userdata *u) {
+ pa_assert(u);
+ pa_assert(u->profile == PA_BLUETOOTH_PROFILE_A2DP_SINK);
+ 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);
+
+ u->write_index += (uint64_t) u->write_memchunk.length;
+ pa_memblock_unref(u->write_memchunk.memblock);
+ pa_memchunk_reset(&u->write_memchunk);
+
+ return 1;
+}
+#endif
+
/* Run from IO thread */
static int a2dp_process_push(struct userdata *u) {
- struct rtp_payload *payload;
++#ifdef __TIZEN_BT_6__
+ int ret = 0;
+ pa_memchunk memchunk;
+
+ pa_assert(u);
+ pa_assert(u->profile == PA_BLUETOOTH_PROFILE_A2DP_SOURCE);
+ pa_assert(u->source);
+ pa_assert(u->read_smoother);
+ pa_assert(u->a2dp_codec);
+
+ memchunk.memblock = pa_memblock_new(u->core->mempool, u->read_block_size);
+ memchunk.index = memchunk.length = 0;
+
+ a2dp_prepare_decoder_buffer(u);
+
+ for (;;) {
+ bool found_tstamp = false;
+ pa_usec_t tstamp;
+ uint8_t *ptr;
+ ssize_t l;
+ size_t processed;
+
+ l = pa_read(u->stream_fd, u->decoder_buffer, u->decoder_buffer_size, &u->stream_write_type);
+
+ 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 socket: %s", l < 0 ? pa_cstrerror(errno) : "EOF");
+ ret = -1;
+ break;
+ }
+
+ pa_assert((size_t) l <= u->decoder_buffer_size);
+
+ /* TODO: get timestamp from rtp */
+ if (!found_tstamp) {
+ /* pa_log_warn("Couldn't find SO_TIMESTAMP data in auxiliary recvmsg() data!"); */
+ tstamp = pa_rtclock_now();
+ }
+
+ ptr = pa_memblock_acquire(memchunk.memblock);
+ memchunk.length = pa_memblock_get_length(memchunk.memblock);
+
+ memchunk.length = u->a2dp_codec->decode_buffer(u->decoder_info, u->decoder_buffer, l, ptr, memchunk.length, &processed);
+
+ pa_memblock_release(memchunk.memblock);
+
+ if (processed != (size_t) l) {
+ pa_log_error("Decoding error");
+ ret = -1;
+ break;
+ }
+
+ u->read_index += (uint64_t) memchunk.length;
+ pa_smoother_put(u->read_smoother, tstamp, pa_bytes_to_usec(u->read_index, &u->decoder_sample_spec));
+ pa_smoother_resume(u->read_smoother, tstamp, true);
+
+ /* Decoding of A2DP codec data may result in empty buffer, in this case
+ * do not post empty audio samples. It may happen due to algorithmic
+ * delay of audio codec. */
+ if (PA_LIKELY(memchunk.length))
+ pa_source_post(u->source, &memchunk);
+
+ ret = l;
+ break;
+ }
+
+ pa_memblock_unref(memchunk.memblock);
+
+ return ret;
++#else
+ int ret = 0;
+ pa_memchunk memchunk;
+
+ pa_assert(u);
+ pa_assert(u->profile == PA_BLUETOOTH_PROFILE_A2DP_SOURCE);
+ 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 (;;) {
+ bool found_tstamp = false;
+ pa_usec_t tstamp;
+ struct sbc_info *sbc_info;
+ struct rtp_header *header;
- payload = (struct rtp_payload*) ((uint8_t*) sbc_info->buffer + sizeof(*header));
++ struct rtp_sbc_payload *payload;
+ const void *p;
+ void *d;
+ ssize_t l;
+ size_t to_write, to_decode;
+ size_t total_written = 0;
+
+ a2dp_prepare_buffer(u);
+
+ sbc_info = &u->sbc_info;
+ header = sbc_info->buffer;
- (u->read_link_mtu - sizeof(struct rtp_header) - sizeof(struct rtp_payload))
++ payload = (struct rtp_sbc_payload*) ((uint8_t*) sbc_info->buffer + sizeof(*header));
+
+ l = pa_read(u->stream_fd, sbc_info->buffer, sbc_info->buffer_size, &u->stream_write_type);
+
+ 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 socket: %s", l < 0 ? pa_cstrerror(errno) : "EOF");
+ ret = -1;
+ break;
+ }
+
+ pa_assert((size_t) l <= sbc_info->buffer_size);
+
+ /* TODO: get timestamp from rtp */
+ if (!found_tstamp) {
+ /* pa_log_warn("Couldn't find SO_TIMESTAMP data in auxiliary recvmsg() data!"); */
+ tstamp = pa_rtclock_now();
+ }
+
+ p = (uint8_t*) sbc_info->buffer + sizeof(*header) + sizeof(*payload);
+ to_decode = l - sizeof(*header) - sizeof(*payload);
+
+ d = pa_memblock_acquire(memchunk.memblock);
+ to_write = memchunk.length = pa_memblock_get_length(memchunk.memblock);
+
+ while (PA_LIKELY(to_decode > 0)) {
+ size_t written;
+ ssize_t decoded;
+
+ decoded = sbc_decode(&sbc_info->sbc,
+ p, to_decode,
+ d, to_write,
+ &written);
+
+ if (PA_UNLIKELY(decoded <= 0)) {
+ pa_log_error("SBC decoding error (%li)", (long) decoded);
+ pa_memblock_release(memchunk.memblock);
+ pa_memblock_unref(memchunk.memblock);
+ return 0;
+ }
+
+ total_written += written;
+
+ /* Reset frame length, it can be changed due to bitpool change */
+ sbc_info->frame_length = sbc_get_frame_length(&sbc_info->sbc);
+
+ pa_assert_fp((size_t) decoded <= to_decode);
+ pa_assert_fp((size_t) decoded == sbc_info->frame_length);
+
+ pa_assert_fp((size_t) written == sbc_info->codesize);
+
+ p = (const uint8_t*) p + decoded;
+ to_decode -= decoded;
+
+ d = (uint8_t*) d + written;
+ to_write -= written;
+ }
+
+ u->read_index += (uint64_t) total_written;
+ 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);
+
+ memchunk.length -= to_write;
+
+ pa_memblock_release(memchunk.memblock);
+
+ pa_source_post(u->source, &memchunk);
+
+ ret = l;
+ break;
+ }
+
+ pa_memblock_unref(memchunk.memblock);
+
+ return ret;
++#endif
+}
+
++#ifndef __TIZEN_BT_6__
+/* Run from I/O thread */
+static void a2dp_set_bitpool(struct userdata *u, uint8_t bitpool) {
+ struct sbc_info *sbc_info;
+
+ pa_assert(u);
+
+ sbc_info = &u->sbc_info;
+
+ if (sbc_info->sbc.bitpool == bitpool)
+ return;
+
+ if (bitpool > sbc_info->max_bitpool)
+ bitpool = sbc_info->max_bitpool;
+ else if (bitpool < sbc_info->min_bitpool)
+ bitpool = sbc_info->min_bitpool;
+
+ sbc_info->sbc.bitpool = bitpool;
+
+ sbc_info->codesize = sbc_get_codesize(&sbc_info->sbc);
+ sbc_info->frame_length = sbc_get_frame_length(&sbc_info->sbc);
+
+ pa_log_debug("Bitpool has changed to %u", sbc_info->sbc.bitpool);
+
+ u->read_block_size =
- (u->write_link_mtu - sizeof(struct rtp_header) - sizeof(struct rtp_payload))
++ (u->read_link_mtu - sizeof(struct rtp_header) - sizeof(struct rtp_sbc_payload))
+ / sbc_info->frame_length * sbc_info->codesize;
+
+ u->write_block_size =
++ (u->write_link_mtu - sizeof(struct rtp_header) - sizeof(struct rtp_sbc_payload))
+ / sbc_info->frame_length * sbc_info->codesize;
+
+ 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 + pa_bytes_to_usec(u->write_block_size, &u->sample_spec));
+}
+
+/* Run from I/O thread */
+static void a2dp_reduce_bitpool(struct userdata *u) {
+ struct sbc_info *sbc_info;
+ uint8_t bitpool;
+
+ pa_assert(u);
+
+ sbc_info = &u->sbc_info;
+
+ /* Check if bitpool is already at its limit */
+ if (sbc_info->sbc.bitpool <= BITPOOL_DEC_LIMIT)
+ return;
+
+ bitpool = sbc_info->sbc.bitpool - BITPOOL_DEC_STEP;
+
+ if (bitpool < BITPOOL_DEC_LIMIT)
+ bitpool = BITPOOL_DEC_LIMIT;
+
+ a2dp_set_bitpool(u, bitpool);
}
++#endif
+
+ static void update_sink_buffer_size(struct userdata *u) {
+ int old_bufsize;
+ socklen_t len = sizeof(int);
+ int ret;
+
+ ret = getsockopt(u->stream_fd, SOL_SOCKET, SO_SNDBUF, &old_bufsize, &len);
+ if (ret == -1) {
+ pa_log_warn("Changing bluetooth buffer size: Failed to getsockopt(SO_SNDBUF): %s", pa_cstrerror(errno));
+ } else {
+ int new_bufsize;
+
+ /* Set send buffer size as small as possible. The minimum value is 1024 according to the
+ * socket man page. The data is written to the socket in chunks of write_block_size, so
+ * there should at least be room for two chunks in the buffer. Generally, write_block_size
+ * is larger than 512. If not, use the next multiple of write_block_size which is larger
+ * than 1024. */
+ new_bufsize = 2 * u->write_block_size;
+ if (new_bufsize < 1024)
+ new_bufsize = (1024 / u->write_block_size + 1) * u->write_block_size;
+
+ /* The kernel internally doubles the buffer size that was set by setsockopt and getsockopt
+ * returns the doubled value. */
+ if (new_bufsize != old_bufsize / 2) {
+ ret = setsockopt(u->stream_fd, SOL_SOCKET, SO_SNDBUF, &new_bufsize, len);
+ if (ret == -1)
+ pa_log_warn("Changing bluetooth buffer size: Failed to change from %d to %d: %s", old_bufsize / 2, new_bufsize, pa_cstrerror(errno));
+ else
+ pa_log_info("Changing bluetooth buffer size: Changed from %d to %d", old_bufsize / 2, new_bufsize);
+ }
+ }
+ }
static void teardown_stream(struct userdata *u) {
if (u->rtpoll_item) {
if (u->stream_fd < 0)
return u->stream_fd;
+ /* transport_acquired must be set before calling
+ * pa_bluetooth_transport_set_state() */
u->transport_acquired = true;
+
+#ifdef __TIZEN_BT__
+ u->transport_suspended_by_remote = false;
+#endif
pa_log_info("Transport %s acquired: fd %d", u->transport->path, u->stream_fd);
++#ifdef __TIZEN_BT_6__
+ if (u->transport->state == PA_BLUETOOTH_TRANSPORT_STATE_IDLE) {
+ if (pa_thread_mq_get() != NULL)
+ pa_asyncmsgq_post(pa_thread_mq_get()->outq, PA_MSGOBJECT(u->msg), BLUETOOTH_MESSAGE_SET_TRANSPORT_PLAYING, NULL, 0, NULL, NULL);
+ else
+ pa_bluetooth_transport_set_state(u->transport, PA_BLUETOOTH_TRANSPORT_STATE_PLAYING);
+ }
++#endif
+
return 0;
}
}
if (!pa_frame_aligned(u->write_block_size, &u->sink->sample_spec)) {
+#ifdef __TIZEN__
+ pa_log_debug("Got invalid write MTU: %zu, rounding down", u->write_block_size);
+#else
pa_log_debug("Got invalid write MTU: %lu, rounding down", u->write_block_size);
+#endif
u->write_block_size = pa_frame_align(u->write_block_size, &u->sink->sample_spec);
}
-
+#ifdef __TIZEN_BT__
+ } else if(u->sbc_info.sbc_initialized) {
+#else
} else {
- (u->read_link_mtu - sizeof(struct rtp_header) - sizeof(struct rtp_payload))
+#endif
++#ifdef __TIZEN_BT_6__
+ pa_assert(u->a2dp_codec);
+ if (u->profile == PA_BLUETOOTH_PROFILE_A2DP_SINK) {
+ u->write_block_size = u->a2dp_codec->get_write_block_size(u->encoder_info, u->write_link_mtu);
+ } else {
+ u->read_block_size = u->a2dp_codec->get_read_block_size(u->decoder_info, u->read_link_mtu);
+ }
+ }
+
+ if (u->sink)
+ handle_sink_block_size_change(u);
+
+ if (u->source)
+ pa_source_set_fixed_latency_within_thread(u->source,
+ (u->profile == PA_BLUETOOTH_PROFILE_A2DP_SOURCE ?
+ FIXED_LATENCY_RECORD_A2DP : FIXED_LATENCY_RECORD_SCO) +
+ pa_bytes_to_usec(u->read_block_size, &u->decoder_sample_spec));
++#else
+ u->read_block_size =
- (u->write_link_mtu - sizeof(struct rtp_header) - sizeof(struct rtp_payload))
++ (u->read_link_mtu - sizeof(struct rtp_header) - sizeof(struct rtp_sbc_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_sbc_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,
+ (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,
+ (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));
++#endif
}
/* Run from I/O thread */
pa_log_info("Transport %s resuming", u->transport->path);
++#ifdef __TIZEN_BT_6__
+ if (u->profile == PA_BLUETOOTH_PROFILE_A2DP_SINK) {
+ pa_assert(u->a2dp_codec);
+ if (u->a2dp_codec->reset(u->encoder_info) < 0)
+ return -1;
+ } else if (u->profile == PA_BLUETOOTH_PROFILE_A2DP_SOURCE) {
+ pa_assert(u->a2dp_codec);
+ if (u->a2dp_codec->reset(u->decoder_info) < 0)
+ return -1;
+ }
+
+ transport_config_mtu(u);
+
+ pa_make_fd_nonblock(u->stream_fd);
+ pa_make_socket_low_delay(u->stream_fd);
+
+ one = 1;
+ if (setsockopt(u->stream_fd, SOL_SOCKET, SO_TIMESTAMP, &one, sizeof(one)) < 0)
+ pa_log_warn("Failed to enable SO_TIMESTAMP: %s", pa_cstrerror(errno));
+
+ pa_log_debug("Stream properly set up, we're ready to roll!");
++#else
+ transport_config_mtu(u);
+
+ pa_make_fd_nonblock(u->stream_fd);
+ pa_make_socket_low_delay(u->stream_fd);
+
+ one = 1;
+ if (setsockopt(u->stream_fd, SOL_SOCKET, SO_TIMESTAMP, &one, sizeof(one)) < 0)
+ pa_log_warn("Failed to enable SO_TIMESTAMP: %s", pa_cstrerror(errno));
+
+ pa_log_debug("Stream properly set up, we're ready to roll!");
+
+ if (u->profile == PA_BLUETOOTH_PROFILE_A2DP_SINK)
+ a2dp_set_bitpool(u, u->sbc_info.max_bitpool);
++#endif
u->rtpoll_item = pa_rtpoll_item_new(u->rtpoll, PA_RTPOLL_NEVER, 1);
pollfd = pa_rtpoll_item_get_pollfd(u->rtpoll_item, NULL);
if (transport_error < 0) {
if (transport_error != -EAGAIN)
return false;
- } else
+ } else {
++#ifdef __TIZEN_BT_6__
+ if (setup_stream(u) < 0)
+ return false;
++#else
+ setup_stream(u);
++#endif
+ }
return true;
}
/* Run from IO thread */
static int source_process_msg(pa_msgobject *o, int code, void *data, int64_t offset, pa_memchunk *chunk) {
++#ifdef __TIZEN_BT_6__
+ struct userdata *u = PA_SOURCE(o)->userdata;
+
+ pa_assert(u->source == PA_SOURCE(o));
+ pa_assert(u->transport);
+
+ switch (code) {
+
+ case PA_SOURCE_MESSAGE_GET_LATENCY: {
+ int64_t wi, ri;
+
+ if (u->read_smoother) {
+ wi = pa_smoother_get(u->read_smoother, pa_rtclock_now());
+ ri = pa_bytes_to_usec(u->read_index, &u->decoder_sample_spec);
+
+ *((int64_t*) data) = u->source->thread_info.fixed_latency + wi - ri;
+ } else
+ *((int64_t*) data) = 0;
+
+ return 0;
+ }
+
+ case PA_SOURCE_MESSAGE_SETUP_STREAM:
+ /* Skip stream setup if stream_fd has been invalidated.
+ This can occur if the stream has already been set up and
+ then immediately received POLLHUP. If the stream has
+ already been set up earlier, then this setup_stream()
+ call is redundant anyway, but currently the code
+ is such that this kind of unnecessary setup_stream()
+ calls can happen. */
+ if (u->stream_fd < 0)
+ pa_log_debug("Skip source stream setup while closing");
+ else
+ setup_stream(u);
+ return 0;
+
+ }
+
+ return pa_source_process_msg(o, code, data, offset, chunk);
++#else
+ struct userdata *u = PA_SOURCE(o)->userdata;
+ bool failed = false;
+ int r;
+
+ pa_assert(u->source == PA_SOURCE(o));
+ pa_assert(u->transport);
+
+ switch (code) {
+
+ case PA_SOURCE_MESSAGE_SET_STATE:
+
+ switch ((pa_source_state_t) PA_PTR_TO_UINT(data)) {
+
+ case PA_SOURCE_SUSPENDED:
+ /* Ignore if transition is PA_SOURCE_INIT->PA_SOURCE_SUSPENDED */
+ if (!PA_SOURCE_IS_OPENED(u->source->thread_info.state))
+ break;
+
+ /* Stop the device if the sink is suspended as well */
+ if (!u->sink || u->sink->state == PA_SINK_SUSPENDED)
+ transport_release(u);
+
+ if (u->read_smoother)
+ pa_smoother_pause(u->read_smoother, pa_rtclock_now());
+
+ break;
+
+ case PA_SOURCE_IDLE:
+ case PA_SOURCE_RUNNING:
+ if (u->source->thread_info.state != PA_SOURCE_SUSPENDED)
+ break;
+
+ /* Resume the device if the sink was suspended as well */
+ if (!u->sink || !PA_SINK_IS_OPENED(u->sink->thread_info.state))
+ failed = !setup_transport_and_stream(u);
+
+ /* We don't resume the smoother here. Instead we
+ * wait until the first packet arrives */
+
+ break;
+
+ case PA_SOURCE_UNLINKED:
+ case PA_SOURCE_INIT:
+ case PA_SOURCE_INVALID_STATE:
+ break;
+ }
+
+ break;
+
+ case PA_SOURCE_MESSAGE_GET_LATENCY: {
+ int64_t wi, ri;
+
+ if (u->read_smoother) {
+ wi = pa_smoother_get(u->read_smoother, pa_rtclock_now());
+ ri = pa_bytes_to_usec(u->read_index, &u->sample_spec);
+
+ *((int64_t*) data) = u->source->thread_info.fixed_latency + wi - ri;
+ } else
+ *((int64_t*) data) = 0;
+
+ return 0;
+ }
+
+ case PA_SOURCE_MESSAGE_SETUP_STREAM:
+ setup_stream(u);
+ return 0;
+
+ }
+
+ r = pa_source_process_msg(o, code, data, offset, chunk);
+
+ return (r < 0 || !failed) ? r : -1;
++#endif
+ }
+
+ /* Called from the IO thread. */
+ static int source_set_state_in_io_thread_cb(pa_source *s, pa_source_state_t new_state, pa_suspend_cause_t new_suspend_cause) {
+ struct userdata *u;
+
+ pa_assert(s);
+ pa_assert_se(u = s->userdata);
+
+ switch (new_state) {
+
+ case PA_SOURCE_SUSPENDED:
+ /* Ignore if transition is PA_SOURCE_INIT->PA_SOURCE_SUSPENDED */
+ if (!PA_SOURCE_IS_OPENED(s->thread_info.state))
+ break;
+
+ /* Stop the device if the sink is suspended as well */
+ if (!u->sink || u->sink->state == PA_SINK_SUSPENDED)
+ transport_release(u);
+
+ if (u->read_smoother)
+ pa_smoother_pause(u->read_smoother, pa_rtclock_now());
+
+ break;
+
+ case PA_SOURCE_IDLE:
+ case PA_SOURCE_RUNNING:
+ if (s->thread_info.state != PA_SOURCE_SUSPENDED)
+ break;
+
+ /* Resume the device if the sink was suspended as well */
+ if (!u->sink || !PA_SINK_IS_OPENED(u->sink->thread_info.state))
+ if (!setup_transport_and_stream(u))
+ return -1;
+
+ /* We don't resume the smoother here. Instead we
+ * wait until the first packet arrives */
+
+ break;
+
+ case PA_SOURCE_UNLINKED:
+ case PA_SOURCE_INIT:
+ case PA_SOURCE_INVALID_STATE:
+ break;
+ }
+
+ return 0;
}
/* Run from main thread */
if (volume < PA_VOLUME_NORM)
volume++;
++#ifdef __TIZEN_BT_6__
+ pa_cvolume_set(&s->real_volume, u->decoder_sample_spec.channels, volume);
++#else
+ pa_cvolume_set(&s->real_volume, u->sample_spec.channels, volume);
++#endif
/* Set soft volume when in headset role */
if (u->profile == PA_BLUETOOTH_PROFILE_HEADSET_AUDIO_GATEWAY)
++#ifdef __TIZEN_BT_6__
+ pa_cvolume_set(&s->soft_volume, u->decoder_sample_spec.channels, volume);
++#else
+ pa_cvolume_set(&s->soft_volume, u->sample_spec.channels, volume);
++#endif
/* If we are in the AG role, we send a command to the head set to change
* the microphone gain. In the HS role, source and sink are swapped, so
data.name = pa_sprintf_malloc("bluez_source.%s.%s", u->device->address, pa_bluetooth_profile_to_string(u->profile));
data.namereg_fail = false;
pa_proplist_sets(data.proplist, "bluetooth.protocol", pa_bluetooth_profile_to_string(u->profile));
++#ifdef __TIZEN_BT_6__
+ pa_source_new_data_set_sample_spec(&data, &u->decoder_sample_spec);
++#else
+ pa_source_new_data_set_sample_spec(&data, &u->sample_spec);
++#endif
if (u->profile == PA_BLUETOOTH_PROFILE_HEADSET_HEAD_UNIT)
pa_proplist_sets(data.proplist, PA_PROP_DEVICE_INTENDED_ROLES, "phone");
/* Run from IO thread */
static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offset, pa_memchunk *chunk) {
++#ifdef __TIZEN_BT_6__
++ struct userdata *u = PA_SINK(o)->userdata;
++
++ pa_assert(u->sink == PA_SINK(o));
++ pa_assert(u->transport);
++
++ switch (code) {
++
++ case PA_SINK_MESSAGE_GET_LATENCY: {
++ int64_t wi = 0, ri = 0;
++
++ if (u->read_smoother) {
++ ri = pa_smoother_get(u->read_smoother, pa_rtclock_now());
++ wi = pa_bytes_to_usec(u->write_index + u->write_block_size, &u->encoder_sample_spec);
++ } else if (u->started_at) {
++ ri = pa_rtclock_now() - u->started_at;
++ wi = pa_bytes_to_usec(u->write_index, &u->encoder_sample_spec);
++ }
++
++ *((int64_t*) data) = u->sink->thread_info.fixed_latency + wi - ri;
++
++ return 0;
++ }
++
++ case PA_SINK_MESSAGE_SETUP_STREAM:
++ /* Skip stream setup if stream_fd has been invalidated.
++ This can occur if the stream has already been set up and
++ then immediately received POLLHUP. If the stream has
++ already been set up earlier, then this setup_stream()
++ call is redundant anyway, but currently the code
++ is such that this kind of unnecessary setup_stream()
++ calls can happen. */
++ if (u->stream_fd < 0)
++ pa_log_debug("Skip sink stream setup while closing");
++ else
++ setup_stream(u);
++ return 0;
++ }
++
++ return pa_sink_process_msg(o, code, data, offset, chunk);
++#else
struct userdata *u = PA_SINK(o)->userdata;
+ bool failed = false;
+ int r;
pa_assert(u->sink == PA_SINK(o));
pa_assert(u->transport);
return 0;
}
- return pa_sink_process_msg(o, code, data, offset, chunk);
+ r = pa_sink_process_msg(o, code, data, offset, chunk);
+
+ return (r < 0 || !failed) ? r : -1;
++#endif
+ }
+
+ /* Called from the IO thread. */
+ static int sink_set_state_in_io_thread_cb(pa_sink *s, pa_sink_state_t new_state, pa_suspend_cause_t new_suspend_cause) {
+ struct userdata *u;
+
+ pa_assert(s);
+ pa_assert_se(u = s->userdata);
+
+ switch (new_state) {
+
+ case PA_SINK_SUSPENDED:
+ /* Ignore if transition is PA_SINK_INIT->PA_SINK_SUSPENDED */
+ if (!PA_SINK_IS_OPENED(s->thread_info.state))
+ break;
+
+ /* Stop the device if the source is suspended as well */
+ if (!u->source || u->source->state == PA_SOURCE_SUSPENDED)
+ /* We deliberately ignore whether stopping
+ * actually worked. Since the stream_fd is
+ * closed it doesn't really matter */
+ transport_release(u);
+
+ break;
+
+ case PA_SINK_IDLE:
+ case PA_SINK_RUNNING:
+ if (s->thread_info.state != PA_SINK_SUSPENDED)
+ break;
+
+ /* Resume the device if the source was suspended as well */
+ if (!u->source || !PA_SOURCE_IS_OPENED(u->source->thread_info.state))
+ if (!setup_transport_and_stream(u))
+ return -1;
+
+ break;
+
+ case PA_SINK_UNLINKED:
+ case PA_SINK_INIT:
+ case PA_SINK_INVALID_STATE:
+ break;
+ }
+
+ return 0;
}
/* Run from main thread */
if (volume < PA_VOLUME_NORM)
volume++;
++#ifdef __TIZEN_BT_6__
+ pa_cvolume_set(&s->real_volume, u->encoder_sample_spec.channels, volume);
++#else
+ pa_cvolume_set(&s->real_volume, u->sample_spec.channels, volume);
++#endif
/* Set soft volume when in headset role */
if (u->profile == PA_BLUETOOTH_PROFILE_HEADSET_AUDIO_GATEWAY)
++#ifdef __TIZEN_BT_6__
+ pa_cvolume_set(&s->soft_volume, u->encoder_sample_spec.channels, volume);
++#else
+ pa_cvolume_set(&s->soft_volume, u->sample_spec.channels, volume);
++#endif
/* If we are in the AG role, we send a command to the head set to change
* the speaker gain. In the HS role, source and sink are swapped, so
data.name = pa_sprintf_malloc("bluez_sink.%s.%s", u->device->address, pa_bluetooth_profile_to_string(u->profile));
data.namereg_fail = false;
pa_proplist_sets(data.proplist, "bluetooth.protocol", pa_bluetooth_profile_to_string(u->profile));
++#ifdef __TIZEN_BT_6__
+ pa_sink_new_data_set_sample_spec(&data, &u->encoder_sample_spec);
++#else
+ pa_sink_new_data_set_sample_spec(&data, &u->sample_spec);
++#endif
if (u->profile == PA_BLUETOOTH_PROFILE_HEADSET_HEAD_UNIT)
pa_proplist_sets(data.proplist, PA_PROP_DEVICE_INTENDED_ROLES, "phone");
}
/* Run from main thread */
- static void transport_config(struct userdata *u) {
+ static int transport_config(struct userdata *u) {
if (u->profile == PA_BLUETOOTH_PROFILE_HEADSET_HEAD_UNIT || u->profile == PA_BLUETOOTH_PROFILE_HEADSET_AUDIO_GATEWAY) {
++#ifdef __TIZEN_BT_6__
+ u->encoder_sample_spec.format = PA_SAMPLE_S16LE;
+ u->encoder_sample_spec.channels = 1;
+ u->encoder_sample_spec.rate = 8000;
+ u->decoder_sample_spec.format = PA_SAMPLE_S16LE;
+ u->decoder_sample_spec.channels = 1;
+ u->decoder_sample_spec.rate = 8000;
+ return 0;
++#else
+ u->sample_spec.format = PA_SAMPLE_S16LE;
+ u->sample_spec.channels = 1;
+ u->sample_spec.rate = 8000;
++ return 0;
++#endif
} else {
- return;
+ sbc_info_t *sbc_info = &u->sbc_info;
+ a2dp_sbc_t *config;
+ const pa_bluetooth_transport *t;
+ a2dp_aptx_t *aptx_config;
+
+ pa_assert(u->transport);
+
+#ifdef __TIZEN_BT__
+ if (u->is_aptx_supported) {
+ t = u->transport;
+ if (t->codec == A2DP_CODEC_VENDOR) {
+ 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) {
+ struct pa_bluetooth_vendor_codec_ops *ops = NULL;
+ size_t size;
+ pa_log("A2DP_CODEC_NON_A2DP and this is APTX Codec");
+ pa_proplist_get(u->vendor_codec_list, "APTX", &ops, &size);
+ if (ops) {
+ ops->transport_config(u);
+ u->selected_ops = ops;
+ } else {
+ pa_log_error("APTX ops is NULL");
+ }
- return;
+ } else {
+ pa_log("A2DP_CODEC_NON_A2DP but this is not APTX Codec");
+ }
+ }
+ }
+#endif
+
+ 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;
+
+ 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->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->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;
+
+ /* 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 ? 8 : 4, sbc_info->sbc.blocks, sbc_info->sbc.bitpool);
++#ifdef __TIZEN_BT_6__
+ bool is_a2dp_sink = u->profile == PA_BLUETOOTH_PROFILE_A2DP_SINK;
+ void *info;
+
+ pa_assert(u->transport);
+
+ pa_assert(!u->a2dp_codec);
+ pa_assert(!u->encoder_info);
+ pa_assert(!u->decoder_info);
+
+ u->a2dp_codec = u->transport->a2dp_codec;
+ pa_assert(u->a2dp_codec);
+
+ info = u->a2dp_codec->init(is_a2dp_sink, false, u->transport->config, u->transport->config_size, is_a2dp_sink ? &u->encoder_sample_spec : &u->decoder_sample_spec);
+ if (is_a2dp_sink)
+ u->encoder_info = info;
+ else
+ u->decoder_info = info;
+
+ if (!info)
+ return -1;
+
++#endif
+ return 0;
}
}
return r;
}
+ static int write_block(struct userdata *u) {
+ int n_written;
+
+ if (u->write_index <= 0)
+ u->started_at = pa_rtclock_now();
+
+ if (u->profile == PA_BLUETOOTH_PROFILE_A2DP_SINK) {
+ if ((n_written = a2dp_process_render(u)) < 0)
+ return -1;
+ } else {
+ if ((n_written = sco_process_render(u)) < 0)
+ return -1;
+ }
+
+ return n_written;
+ }
+
+
/* I/O thread function */
static void thread_func(void *userdata) {
++#ifndef __TIZEN_BT_6__
+ struct userdata *u = userdata;
+ unsigned do_write = 0;
+ unsigned pending_read_bytes = 0;
+ bool writable = false;
+
+ pa_assert(u);
+ pa_assert(u->transport);
+
+ pa_log_debug("IO Thread starting up");
+
+ if (u->core->realtime_scheduling)
+ pa_make_realtime(u->core->realtime_priority);
+
+ pa_thread_mq_install(&u->thread_mq);
+
+ /* Setup the stream only if the transport was already acquired */
+ if (u->transport_acquired)
+ setup_stream(u);
+
+ for (;;) {
+ struct pollfd *pollfd;
+ int ret;
+ bool disable_timer = true;
+
+ pollfd = u->rtpoll_item ? pa_rtpoll_item_get_pollfd(u->rtpoll_item, NULL) : NULL;
+
+ if (pollfd && (pollfd->revents & ~(POLLOUT|POLLIN))) {
+ pa_log_info("FD error: %s%s%s%s",
+ pollfd->revents & POLLERR ? "POLLERR " :"",
+ pollfd->revents & POLLHUP ? "POLLHUP " :"",
+ pollfd->revents & POLLPRI ? "POLLPRI " :"",
+ pollfd->revents & POLLNVAL ? "POLLNVAL " :"");
+
+ if (pollfd->revents & POLLHUP) {
+ pollfd = NULL;
+ teardown_stream(u);
+ do_write = 0;
+ pending_read_bytes = 0;
+ writable = false;
+ pa_asyncmsgq_post(pa_thread_mq_get()->outq, PA_MSGOBJECT(u->msg), BLUETOOTH_MESSAGE_STREAM_FD_HUP, NULL, 0, NULL, NULL);
+ } else
+ goto fail;
+ }
+
+ if (u->source && PA_SOURCE_IS_LINKED(u->source->thread_info.state)) {
+
+ /* We should send two blocks to the device before we expect
+ * a response. */
+
+ if (u->write_index == 0 && u->read_index <= 0)
+ do_write = 2;
+
+ if (pollfd && (pollfd->revents & POLLIN)) {
+ int n_read;
+
+ if (u->profile == PA_BLUETOOTH_PROFILE_A2DP_SOURCE)
+ n_read = a2dp_process_push(u);
+ else
+ n_read = sco_process_push(u);
+
+ if (n_read < 0)
+ goto fail;
+
+ if (n_read > 0) {
+ /* We just read something, so we are supposed to write something, too */
+ pending_read_bytes += n_read;
+ do_write += pending_read_bytes / u->write_block_size;
+ pending_read_bytes = pending_read_bytes % u->write_block_size;
+ }
+ }
+ }
+
+ if (u->sink && PA_SINK_IS_LINKED(u->sink->thread_info.state)) {
+
+ if (PA_UNLIKELY(u->sink->thread_info.rewind_requested))
+ pa_sink_process_rewind(u->sink, 0);
+
+ if (pollfd) {
+ if (pollfd->revents & POLLOUT)
+ writable = true;
+
+ if ((!u->source || !PA_SOURCE_IS_LINKED(u->source->thread_info.state)) && do_write <= 0 && writable) {
+ pa_usec_t time_passed;
+ pa_usec_t audio_sent;
+
+ /* Hmm, there is no input stream we could synchronize
+ * to. So let's do things by time */
+
+ time_passed = pa_rtclock_now() - u->started_at;
+ audio_sent = pa_bytes_to_usec(u->write_index, &u->sample_spec);
+
+ if (audio_sent <= time_passed) {
+ pa_usec_t audio_to_send = time_passed - audio_sent;
+
+ /* Never try to catch up for more than 100ms */
+ if (u->write_index > 0 && audio_to_send > MAX_PLAYBACK_CATCH_UP_USEC) {
+ pa_usec_t skip_usec;
+ uint64_t skip_bytes;
+
+ skip_usec = audio_to_send - MAX_PLAYBACK_CATCH_UP_USEC;
+ skip_bytes = pa_usec_to_bytes(skip_usec, &u->sample_spec);
+
+ if (skip_bytes > 0) {
+ pa_memchunk tmp;
+
+ pa_log_warn("Skipping %llu us (= %llu bytes) in audio stream",
+ (unsigned long long) skip_usec,
+ (unsigned long long) skip_bytes);
+
+ pa_sink_render_full(u->sink, skip_bytes, &tmp);
+ pa_memblock_unref(tmp.memblock);
+ u->write_index += skip_bytes;
+
+ if (u->profile == PA_BLUETOOTH_PROFILE_A2DP_SINK)
+ a2dp_reduce_bitpool(u);
+ }
+ }
+
+ do_write = 1;
+ pending_read_bytes = 0;
+ }
+ }
+
+ if (writable && do_write > 0) {
+ int n_written = 0;
+
+ if (u->write_index <= 0)
+ u->started_at = pa_rtclock_now();
+#ifdef __TIZEN_BT__
+ if ((u->profile == PA_BLUETOOTH_PROFILE_A2DP_SINK) &&
+ !u->transport_suspended_by_remote) {
+ if(u->sbc_info.sbc_initialized) {
+ if ((n_written = a2dp_process_render(u)) < 0)
+ goto fail;
+ } else {
+ if (u->selected_ops)
+ if ((n_written = u->selected_ops->process_render(u)) < 0)
+ goto fail;
+ else
+ goto fail;
+ }
+ } else if ((u->profile == PA_BLUETOOTH_PROFILE_A2DP_SINK) &&
+ u->transport_suspended_by_remote) {
+ if ((n_written = a2dp_process_null_render(u)) < 0)
+ goto fail;
+#else
+ if (u->profile == PA_BLUETOOTH_PROFILE_A2DP_SINK) {
+ if ((n_written = a2dp_process_render(u)) < 0)
+ goto fail;
+#endif
+ } else {
+ if ((n_written = sco_process_render(u)) < 0)
+ goto fail;
+ }
+
+ if (n_written == 0)
+ pa_log("Broken kernel: we got EAGAIN on write() after POLLOUT!");
+
+ do_write -= n_written;
+ writable = false;
+ }
+
+ if ((!u->source || !PA_SOURCE_IS_LINKED(u->source->thread_info.state)) && do_write <= 0) {
+ pa_usec_t sleep_for;
+ pa_usec_t time_passed, next_write_at;
+
+ if (writable) {
+ /* Hmm, there is no input stream we could synchronize
+ * to. So let's estimate when we need to wake up the latest */
+ time_passed = pa_rtclock_now() - u->started_at;
+ next_write_at = pa_bytes_to_usec(u->write_index, &u->sample_spec);
+ sleep_for = time_passed < next_write_at ? next_write_at - time_passed : 0;
+ /* pa_log("Sleeping for %lu; time passed %lu, next write at %lu", (unsigned long) sleep_for, (unsigned long) time_passed, (unsigned long)next_write_at); */
+ } else
+ /* drop stream every 500 ms */
+ sleep_for = PA_USEC_PER_MSEC * 500;
+
+ pa_rtpoll_set_timer_relative(u->rtpoll, sleep_for);
+ disable_timer = false;
+ }
+ }
+ }
+
+ if (disable_timer)
+ pa_rtpoll_set_timer_disabled(u->rtpoll);
+
+ /* Hmm, nothing to do. Let's sleep */
+ if (pollfd)
+ pollfd->events = (short) (((u->sink && PA_SINK_IS_LINKED(u->sink->thread_info.state) && !writable) ? POLLOUT : 0) |
+ (u->source && PA_SOURCE_IS_LINKED(u->source->thread_info.state) ? POLLIN : 0));
+
+ if ((ret = pa_rtpoll_run(u->rtpoll)) < 0) {
+ pa_log_debug("pa_rtpoll_run failed with: %d", ret);
+ goto fail;
+ }
+ if (ret == 0) {
+ pa_log_debug("IO thread shutdown requested, stopping cleanly");
+ transport_release(u);
+ goto finish;
+ }
+ }
++#else
+ struct userdata *u = userdata;
+ unsigned blocks_to_write = 0;
+ unsigned bytes_to_write = 0;
+
+ pa_assert(u);
+ pa_assert(u->transport);
+
+ pa_log_debug("IO Thread starting up");
+
+ if (u->core->realtime_scheduling)
+ pa_thread_make_realtime(u->core->realtime_priority);
+
+ pa_thread_mq_install(&u->thread_mq);
+
+ /* Setup the stream only if the transport was already acquired */
+ if (u->transport_acquired)
+ setup_stream(u);
+
+ for (;;) {
+ struct pollfd *pollfd;
+ int ret;
+ bool disable_timer = true;
+ bool writable = false;
+ bool have_source = u->source ? PA_SOURCE_IS_LINKED(u->source->thread_info.state) : false;
+ bool have_sink = u->sink ? PA_SINK_IS_LINKED(u->sink->thread_info.state) : false;
+
+ pollfd = u->rtpoll_item ? pa_rtpoll_item_get_pollfd(u->rtpoll_item, NULL) : NULL;
+
+ /* Check for stream error or close */
+ if (pollfd && (pollfd->revents & ~(POLLOUT|POLLIN))) {
+ pa_log_info("FD error: %s%s%s%s",
+ pollfd->revents & POLLERR ? "POLLERR " :"",
+ pollfd->revents & POLLHUP ? "POLLHUP " :"",
+ pollfd->revents & POLLPRI ? "POLLPRI " :"",
+ pollfd->revents & POLLNVAL ? "POLLNVAL " :"");
+
+ if (pollfd->revents & POLLHUP) {
+ pollfd = NULL;
+ teardown_stream(u);
+ blocks_to_write = 0;
+ bytes_to_write = 0;
+ pa_asyncmsgq_post(pa_thread_mq_get()->outq, PA_MSGOBJECT(u->msg), BLUETOOTH_MESSAGE_STREAM_FD_HUP, NULL, 0, NULL, NULL);
+ } else
+ goto fail;
+ }
+
+ /* If there is a pollfd, the stream is set up and we need to do something */
+ if (pollfd) {
+
+ /* Handle source if present */
+ if (have_source) {
+
+ /* We should send two blocks to the device before we expect a response. */
+ if (have_sink && u->write_index == 0 && u->read_index <= 0)
+ blocks_to_write = 2;
+
+ /* If we got woken up by POLLIN let's do some reading */
+ if (pollfd->revents & POLLIN) {
+ int n_read;
+
+ if (u->profile == PA_BLUETOOTH_PROFILE_A2DP_SOURCE)
+ n_read = a2dp_process_push(u);
+ else
+ n_read = sco_process_push(u);
+
+ if (n_read < 0)
+ goto fail;
+
+ if (have_sink && n_read > 0) {
+ /* We just read something, so we are supposed to write something, too */
+ bytes_to_write += n_read;
+ blocks_to_write += bytes_to_write / u->write_block_size;
+ bytes_to_write = bytes_to_write % u->write_block_size;
+ }
+ }
+ }
+
+ /* Handle sink if present */
+ if (have_sink) {
+
+ /* Process rewinds */
+ if (PA_UNLIKELY(u->sink->thread_info.rewind_requested))
+ pa_sink_process_rewind(u->sink, 0);
+
+ /* Test if the stream is writable */
+ if (pollfd->revents & POLLOUT)
+ writable = true;
+
+ /* If we have a source, we let the source determine the timing
+ * for the sink */
+ if (have_source) {
+
+ if (writable && blocks_to_write > 0) {
+ int result;
+
+ if ((result = write_block(u)) < 0)
+ goto fail;
+
+ blocks_to_write -= result;
+
+ /* writable controls whether we set POLLOUT when polling - we set it to
+ * false to enable POLLOUT. If there are more blocks to write, we want to
+ * be woken up immediately when the socket becomes writable. If there
+ * aren't currently any more blocks to write, then we'll have to wait
+ * until we've received more data, so in that case we only want to set
+ * POLLIN. Note that when we are woken up the next time, POLLOUT won't be
+ * set in revents even if the socket has meanwhile become writable, which
+ * may seem bad, but in that case we'll set POLLOUT in the subsequent
+ * poll, and the poll will return immediately, so our writes won't be
+ * delayed. */
+ if (blocks_to_write > 0)
+ writable = false;
+ }
+
+ /* There is no source, we have to use the system clock for timing */
+ } else {
+ bool have_written = false;
+ pa_usec_t time_passed = 0;
+ pa_usec_t audio_sent = 0;
+
+ if (u->started_at) {
+ time_passed = pa_rtclock_now() - u->started_at;
+ audio_sent = pa_bytes_to_usec(u->write_index, &u->encoder_sample_spec);
+ }
+
+ /* A new block needs to be sent. */
+ if (audio_sent <= time_passed) {
+ size_t bytes_to_send = pa_usec_to_bytes(time_passed - audio_sent, &u->encoder_sample_spec);
+
+ /* There are more than two blocks that need to be written. It seems that
+ * the socket has not been accepting data fast enough (could be due to
+ * hiccups in the wireless transmission). We need to discard everything
+ * older than two block sizes to keep the latency from growing. */
+ if (bytes_to_send > 2 * u->write_block_size) {
+ uint64_t skip_bytes;
+ pa_memchunk tmp;
+ size_t mempool_max_block_size = pa_mempool_block_size_max(u->core->mempool);
+ pa_usec_t skip_usec;
+
+ skip_bytes = bytes_to_send - 2 * u->write_block_size;
+ skip_usec = pa_bytes_to_usec(skip_bytes, &u->encoder_sample_spec);
+
+ pa_log_debug("Skipping %llu us (= %llu bytes) in audio stream",
+ (unsigned long long) skip_usec,
+ (unsigned long long) skip_bytes);
+
+ while (skip_bytes > 0) {
+ size_t bytes_to_render;
+
+ if (skip_bytes > mempool_max_block_size)
+ bytes_to_render = mempool_max_block_size;
+ else
+ bytes_to_render = skip_bytes;
+
+ pa_sink_render_full(u->sink, bytes_to_render, &tmp);
+ pa_memblock_unref(tmp.memblock);
+ u->write_index += bytes_to_render;
+ skip_bytes -= bytes_to_render;
+ }
+
+ if (u->write_index > 0 && u->profile == PA_BLUETOOTH_PROFILE_A2DP_SINK) {
+ size_t new_write_block_size = u->a2dp_codec->reduce_encoder_bitrate(u->encoder_info, u->write_link_mtu);
+ if (new_write_block_size) {
+ u->write_block_size = new_write_block_size;
+ handle_sink_block_size_change(u);
+ }
+ }
+ }
+
+ blocks_to_write = 1;
+ }
+
+ /* If the stream is writable, send some data if necessary */
+ if (writable && blocks_to_write > 0) {
+ int result;
+
+ if ((result = write_block(u)) < 0)
+ goto fail;
+
+ blocks_to_write -= result;
+ writable = false;
+ if (result)
+ have_written = true;
+ }
+
+ /* If nothing was written during this iteration, either the stream
+ * is not writable or there was no write pending. Set up a timer that
+ * will wake up the thread when the next data needs to be written. */
+ if (!have_written) {
+ pa_usec_t sleep_for;
+ pa_usec_t next_write_at;
+
+ if (writable) {
+ /* There was no write pending on this iteration of the loop.
+ * Let's estimate when we need to wake up next */
+ next_write_at = pa_bytes_to_usec(u->write_index, &u->encoder_sample_spec);
+ sleep_for = time_passed < next_write_at ? next_write_at - time_passed : 0;
+ /* pa_log("Sleeping for %lu; time passed %lu, next write at %lu", (unsigned long) sleep_for, (unsigned long) time_passed, (unsigned long)next_write_at); */
+ } else
+ /* We could not write because the stream was not ready. Let's try
+ * again in 500 ms and drop audio if we still can't write. The
+ * thread will also be woken up when we can write again. */
+ sleep_for = PA_USEC_PER_MSEC * 500;
+
+ pa_rtpoll_set_timer_relative(u->rtpoll, sleep_for);
+ disable_timer = false;
+ }
+ }
+ }
+
+ /* Set events to wake up the thread */
+ pollfd->events = (short) (((have_sink && !writable) ? POLLOUT : 0) | (have_source ? POLLIN : 0));
+
+ }
+
+ if (disable_timer)
+ pa_rtpoll_set_timer_disabled(u->rtpoll);
+
+ if ((ret = pa_rtpoll_run(u->rtpoll)) < 0) {
+ pa_log_debug("pa_rtpoll_run failed with: %d", ret);
+ goto fail;
+ }
+
+ if (ret == 0) {
+ pa_log_debug("IO thread shutdown requested, stopping cleanly");
+ transport_release(u);
+ goto finish;
+ }
+ }
++#endif
fail:
/* If this was no regular exit from the loop we have to continue processing messages until we receive PA_MESSAGE_SHUTDOWN */
p = PA_CARD_PROFILE_DATA(cp);
break;
-
case PA_BLUETOOTH_PROFILE_HEADSET_HEAD_UNIT:
cp = pa_card_profile_new(name, _("Headset Head Unit (HSP/HFP)"), sizeof(pa_bluetooth_profile_t));
- cp->priority = 20;
+ cp->priority = 30;
cp->n_sinks = 1;
cp->n_sources = 1;
cp->max_sink_channels = 1;
if (volume < PA_VOLUME_NORM)
volume++;
++#ifdef __TIZEN_BT_6__
+ pa_cvolume_set(&v, u->encoder_sample_spec.channels, volume);
++#else
+ pa_cvolume_set(&v, u->sample_spec.channels, volume);
++#endif
if (t->profile == PA_BLUETOOTH_PROFILE_HEADSET_HEAD_UNIT)
pa_sink_volume_changed(u->sink, &v);
else
if (volume < PA_VOLUME_NORM)
volume++;
++#ifdef __TIZEN_BT_6__
+ pa_cvolume_set(&v, u->decoder_sample_spec.channels, volume);
++#else
+ pa_cvolume_set(&v, u->sample_spec.channels, volume);
++#endif
if (t->profile == PA_BLUETOOTH_PROFILE_HEADSET_HEAD_UNIT)
pa_source_volume_changed(u->source, &v);
if (u->transport_microphone_gain_changed_slot)
pa_hook_slot_free(u->transport_microphone_gain_changed_slot);
++#ifdef __TIZEN_BT_6__
+ if (u->encoder_buffer)
+ pa_xfree(u->encoder_buffer);
+
+ if (u->decoder_buffer)
+ pa_xfree(u->decoder_buffer);
++#else
+ if (u->sbc_info.buffer)
+ pa_xfree(u->sbc_info.buffer);
+
+ if (u->sbc_info.sbc_initialized)
+ sbc_finish(&u->sbc_info.sbc);
++#endif
if (u->msg)
pa_xfree(u->msg);
#include <pulsecore/module.h>
#include <pulsecore/modargs.h>
#include <pulsecore/shared.h>
-
#include "bluez5-util.h"
- #include "module-bluez5-discover-symdef.h"
-
PA_MODULE_AUTHOR("João Paulo Rechi Vita");
PA_MODULE_DESCRIPTION("Detect available BlueZ 5 Bluetooth audio devices and load BlueZ 5 Bluetooth audio drivers");
PA_MODULE_VERSION(PACKAGE_VERSION);
-PA_MODULE_LOAD_ONCE(true);
+#ifdef __TIZEN_BT__
+#define HAVE_BLUEZ_5_NATIVE_HEADSET
+
+PA_MODULE_USAGE("sco_sink=<name of sink> "
+ "sco_source=<name of source> "
+ "headset=ofono|native|auto"
+);
+#else
PA_MODULE_USAGE(
"headset=ofono|native|auto"
+ "autodetect_mtu=<boolean>"
);
+#endif
+PA_MODULE_LOAD_ONCE(true);
+#ifdef __TIZEN_BT__
+static const char* const valid_modargs[] = {
+ "headset",
+ "autodetect_mtu",
+ "sco_sink",
+ "sco_source",
+ NULL
+};
+#else
static const char* const valid_modargs[] = {
"headset",
"autodetect_mtu",
if (!module_loaded && pa_bluetooth_device_any_transport_connected(d)) {
/* a new device has been connected */
pa_module *m;
+#ifdef __TIZEN_BT__
+ char *args = pa_sprintf_malloc("address=\"%s\" path=\"%s\" autodetect_mtu=%i", d->address, d->path, (int)u->autodetect_mtu);
+#else
char *args = pa_sprintf_malloc("path=%s autodetect_mtu=%i", d->path, (int)u->autodetect_mtu);
+#endif
+
+#ifdef __TIZEN_BT__
+ if (pa_bluetooth_device_sink_transport_connected(d) == true)
+ args = pa_sprintf_malloc("%s profile=\"a2dp_sink\"", args);
+
+ if (pa_bluetooth_device_source_transport_connected(d) == true)
+ args = pa_sprintf_malloc("%s profile=\"a2dp_source\"", args);
+#endif
pa_log_debug("Loading module-bluez5-device %s", args);
- m = pa_module_load(u->module->core, "module-bluez5-device", args);
+ pa_module_load(&m, u->module->core, "module-bluez5-device", args);
pa_xfree(args);
if (m)
#include <pulsecore/modargs.h>
#include <pulsecore/proplist-util.h>
- #include "module-filter-apply-symdef.h"
-
+#ifdef TIZEN_FILTER_GROUP
+#define PA_PROP_FILTER_APPLY_GROUP PA_PROP_FILTER_APPLY".%s.group"
+#endif
#define PA_PROP_FILTER_APPLY_PARAMETERS PA_PROP_FILTER_APPLY".%s.parameters"
#define PA_PROP_FILTER_APPLY_MOVING "filter.apply.moving"
#define PA_PROP_FILTER_APPLY_SET_BY_MFA "filter.apply.set_by_mfa"
return parameters;
}
+#ifdef TIZEN_FILTER_GROUP
+static const char* get_filter_group(pa_object *o, const char *want, bool is_sink_input) {
+ const char *group;
+ char *prop_group;
+ pa_proplist *pl;
+
+ if (is_sink_input)
+ pl = PA_SINK_INPUT(o)->proplist;
+ else
+ pl = PA_SOURCE_OUTPUT(o)->proplist;
+
+ prop_group = pa_sprintf_malloc(PA_PROP_FILTER_APPLY_GROUP, want);
+ group = pa_proplist_gets(pl, prop_group);
+ pa_xfree(prop_group);
+
+ return group;
+}
+#endif
+
/* This function is used to set or unset the filter related stream properties. This is necessary
* if a stream does not have filter.apply set and is manually moved to a filter sink or source.
- * In this case, the properies must be temporarily set and removed when the stream is moved away
+ * In this case, the properties must be temporarily set and removed when the stream is moved away
* from the filter. */
static void set_filter_properties(pa_proplist *pl, struct filter *filter, bool set_properties) {
char *prop_parameters;
pa_log_debug("Loading %s with arguments '%s'", module_name, args);
- if ((m = pa_module_load(u->core, module_name, args))) {
+ if (pa_module_load(&m, u->core, module_name, args) >= 0) {
+#ifdef TIZEN_FILTER_GROUP
+ find_filters_for_module(u, m, want, group, parameters);
+#else
find_filters_for_module(u, m, want, parameters);
+#endif
filter = pa_hashmap_get(u->filters, fltr);
done_something = true;
}
"cork_roles=<Comma separated list of roles which will be corked> "
"global=<Should we operate globally or only inside the same device?>"
"use_source_trigger=<Do we trigger a cork by a role of source-output as well as sink-input's? Default: false>"
- );
+#ifdef __TIZEN__
+ "skip_sinks=<Comma separated list of sink names which will skip corking>"
+#endif
+ );
static const char* const valid_modargs[] = {
"trigger_roles",
goto fail;
}
+ port = raop_create_port(u, server);
+ if (port == NULL) {
+ pa_log("Failed to create port object");
+ goto fail;
+ }
+
+ profile = raop_create_profile();
+ pa_hashmap_put(port->profiles, profile->name, profile);
+
+ description = pa_proplist_gets(data.proplist, PA_PROP_DEVICE_DESCRIPTION);
+ if (description == NULL)
+ description = server;
+
+ u->card = raop_create_card(m, port, profile, server, description);
+ if (u->card == NULL) {
+ pa_log("Failed to create card object");
+ goto fail;
+ }
+
+ data.card = u->card;
+ pa_hashmap_put(data.ports, port->name, port);
+
+#ifdef __TIZEN__
+ u->sink = pa_sink_new(m->core, &data, PA_SINK_LATENCY | PA_SINK_NETWORK | PA_SINK_FLAT_VOLUME);
+#else
u->sink = pa_sink_new(m->core, &data, PA_SINK_LATENCY | PA_SINK_NETWORK);
+#endif
pa_sink_new_data_done(&data);
if (!(u->sink)) {
static void cork_or_duck(struct userdata *u, pa_sink_input *i, const char *interaction_role, const char *trigger_role, bool interaction_applied, struct group *g) {
if (u->duck && !interaction_applied) {
- if (!g->fade_needed || (pa_sink_input_get_state(i) == PA_SINK_INPUT_CORKED)) {
+#ifdef __TIZEN__
+ if (g->fade_durs.in || g->fade_durs.out) {
+ pa_cvolume_ramp vol_ramp;
+
+ pa_cvolume_ramp_set(&vol_ramp, i->volume.channels, PA_VOLUME_RAMP_TYPE_LINEAR, g->fade_durs.out, g->volume);
- if (pa_sink_input_get_state(i) == PA_SINK_INPUT_RUNNING) {
++ if (!g->fade_needed || i->state == PA_SINK_INPUT_CORKED) {
+ pa_cvolume vol;
+ vol.channels = 1;
+ vol.values[0] = g->volume;
+
+ pa_log_debug("Found a '%s' stream(%u) that should be ducked by '%s'.", interaction_role, i->index, g->name);
+
+ pa_sink_input_add_volume_factor(i, g->name, &vol);
+ pa_sink_input_add_volume_ramp_factor(i, g->name, &vol_ramp, false);
+ } else {
+ pa_log_debug("Found a '%s' stream(%u) that should be ducked with fade-out(%lums) by '%s'.", interaction_role, i->index, g->fade_durs.out, g->name);
+ pa_sink_input_add_volume_ramp_factor(i, g->name, &vol_ramp, true);
+
++ if (i->state == PA_SINK_INPUT_RUNNING) {
+ if (pa_idxset_size(g->triggered_streams) == 1) {
+ pa_sink_input *si = pa_idxset_first(g->triggered_streams, NULL);
+ pa_sink_input_set_silence_to_first_peek(si, true, g->fade_durs.out * 1000);
+ }
+ }
+ }
+ } else {
+ pa_cvolume vol;
+ vol.channels = 1;
+ vol.values[0] = g->volume;
+
+ pa_log_debug("Found a '%s' stream of '%s' that ducks a '%s' stream(%u).", trigger_role, g->name, interaction_role, i->index);
+ pa_sink_input_add_volume_factor(i, g->name, &vol);
+ }
+#else
pa_cvolume vol;
vol.channels = 1;
vol.values[0] = g->volume;
}
static pa_hook_result_t sink_input_unlink_cb(pa_core *core, pa_sink_input *i, struct userdata *u) {
- int j;
+#ifdef __TIZEN__
++ uint32_t j;
+ pa_core_assert_ref(core);
+#endif
pa_sink_input_assert_ref(i);
+#ifdef __TIZEN__
+ for (j = 0; j < u->n_groups; j++)
+ pa_idxset_remove_by_data(u->groups[j]->interacted_inputs, (void*)i, NULL);
+#endif
return process(u, PA_OBJECT(i), false, false);
}
pa_cvolume* pa_cvolume_inc(pa_cvolume *v, pa_volume_t inc);
/** Decrease the volume passed in by 'dec'. The proportions between
- * the channels are kept. \since 0.9.16 */
+ * the channels are kept. Returns \a v, or NULL on error. \since 0.9.16 */
pa_cvolume* pa_cvolume_dec(pa_cvolume *v, pa_volume_t dec);
+#ifdef __TIZEN__
+/** 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;
+
+/** A structure encapsulating a multichannel volume ramp */
+typedef struct pa_cvolume_ramp {
+ uint8_t channels;
+ pa_volume_ramp 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 the volume ramp of the first n channels to PA_VOLUME_NORM */
+#define pa_cvolume_ramp_reset(a, n, d, l) pa_cvolume_ramp_set((a), (n), (d), (l), PA_VOLUME_NORM)
+
+/** 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);
+
+/** Return non-zero if the specified volume ramp is compatible with the
+ * specified sample spec. */
+int pa_cvolume_ramp_compatible(const pa_cvolume_ramp *ramp, const pa_sample_spec *ss);
+
+/** Return non-zero when the passed volume ramp structure is valid */
+int pa_cvolume_ramp_valid(const pa_cvolume_ramp *ramp);
+
+/** Multiply two per-channel ramp target volumes and return the result in
+ * dest. */
+pa_cvolume_ramp *pa_sw_cvolume_ramp_multiply(pa_cvolume_ramp *dest, const pa_cvolume_ramp *a, const pa_cvolume_ramp *b);
+#endif
PA_C_DECL_END
#endif
#endif
#endif
++#ifndef __TIZEN_BT_6__
+#ifdef HAVE_SCHED_H
+#include <sched.h>
+
+#if defined(__linux__) && !defined(SCHED_RESET_ON_FORK)
+#define SCHED_RESET_ON_FORK 0x40000000
+#endif
+#endif
++#endif
+
#ifdef HAVE_SYS_RESOURCE_H
#include <sys/resource.h>
#endif
return b;
}
++#ifndef __TIZEN_BT_6__
+#ifdef _POSIX_PRIORITY_SCHEDULING
+static int set_scheduler(int rtprio) {
+#ifdef HAVE_SCHED_H
+ struct sched_param sp;
+#ifdef HAVE_DBUS
+ int r;
+ long long rttime;
+#ifdef RLIMIT_RTTIME
+ struct rlimit rl;
+#endif
+ DBusError error;
+ DBusConnection *bus;
+
+ dbus_error_init(&error);
+#endif
+
+ pa_zero(sp);
+ sp.sched_priority = rtprio;
+
+#ifdef SCHED_RESET_ON_FORK
+ if (pthread_setschedparam(pthread_self(), SCHED_RR|SCHED_RESET_ON_FORK, &sp) == 0) {
+ pa_log_debug("SCHED_RR|SCHED_RESET_ON_FORK worked.");
+ return 0;
+ }
+#endif
+
+ if (pthread_setschedparam(pthread_self(), SCHED_RR, &sp) == 0) {
+ pa_log_debug("SCHED_RR worked.");
+ return 0;
+ }
+#endif /* HAVE_SCHED_H */
+
+#ifdef HAVE_DBUS
+ /* Try to talk to RealtimeKit */
+
+ if (!(bus = dbus_bus_get_private(DBUS_BUS_SYSTEM, &error))) {
+ pa_log("Failed to connect to system bus: %s", error.message);
+ dbus_error_free(&error);
+ errno = -EIO;
+ return -1;
+ }
+
+ /* We need to disable exit on disconnect because otherwise
+ * dbus_shutdown will kill us. See
+ * https://bugs.freedesktop.org/show_bug.cgi?id=16924 */
+ dbus_connection_set_exit_on_disconnect(bus, FALSE);
+
+ rttime = rtkit_get_rttime_usec_max(bus);
+ if (rttime >= 0) {
+#ifdef RLIMIT_RTTIME
+ r = getrlimit(RLIMIT_RTTIME, &rl);
+
+ if (r >= 0 && (long long) rl.rlim_max > rttime) {
+ pa_log_info("Clamping rlimit-rttime to %lld for RealtimeKit", rttime);
+ rl.rlim_cur = rl.rlim_max = rttime;
+ r = setrlimit(RLIMIT_RTTIME, &rl);
+
+ if (r < 0)
+ pa_log("setrlimit() failed: %s", pa_cstrerror(errno));
+ }
+#endif
+ r = rtkit_make_realtime(bus, 0, rtprio);
+ dbus_connection_close(bus);
+ dbus_connection_unref(bus);
+
+ if (r >= 0) {
+ pa_log_debug("RealtimeKit worked.");
+ return 0;
+ }
+
+ errno = -r;
+ } else {
+ dbus_connection_close(bus);
+ dbus_connection_unref(bus);
+ errno = -rttime;
+ }
+
+#else
+ errno = 0;
+#endif
+
+ return -1;
+}
+#endif
+
+/* Make the current thread a realtime thread, and acquire the highest
+ * rtprio we can get that is less or equal the specified parameter. If
+ * the thread is already realtime, don't do anything. */
+int pa_make_realtime(int rtprio) {
+
+#if defined(OS_IS_DARWIN)
+ struct thread_time_constraint_policy ttcpolicy;
+ uint64_t freq = 0;
+ size_t size = sizeof(freq);
+ int ret;
+
+ ret = sysctlbyname("hw.cpufrequency", &freq, &size, NULL, 0);
+ if (ret < 0) {
+ pa_log_info("Unable to read CPU frequency, acquisition of real-time scheduling failed.");
+ return -1;
+ }
+
+ pa_log_debug("sysctl for hw.cpufrequency: %llu", freq);
+
+ /* See http://developer.apple.com/library/mac/#documentation/Darwin/Conceptual/KernelProgramming/scheduler/scheduler.html */
+ ttcpolicy.period = freq / 160;
+ ttcpolicy.computation = freq / 3300;
+ ttcpolicy.constraint = freq / 2200;
+ ttcpolicy.preemptible = 1;
+
+ ret = thread_policy_set(mach_thread_self(),
+ THREAD_TIME_CONSTRAINT_POLICY,
+ (thread_policy_t) &ttcpolicy,
+ THREAD_TIME_CONSTRAINT_POLICY_COUNT);
+ if (ret) {
+ pa_log_info("Unable to set real-time thread priority (%08x).", ret);
+ return -1;
+ }
+
+ pa_log_info("Successfully acquired real-time thread priority.");
+ return 0;
+
+#elif defined(_POSIX_PRIORITY_SCHEDULING)
+ int p;
+
+ if (set_scheduler(rtprio) >= 0) {
+ pa_log_info("Successfully enabled SCHED_RR scheduling for thread, with priority %i.", rtprio);
+ return 0;
+ }
+
+ for (p = rtprio-1; p >= 1; p--)
+ if (set_scheduler(p) >= 0) {
+ pa_log_info("Successfully enabled SCHED_RR scheduling for thread, with priority %i, which is lower than the requested %i.", p, rtprio);
+ return 0;
+ }
+#elif defined(OS_IS_WIN32)
+ /* Windows only allows realtime scheduling to be set on a per process basis.
+ * Therefore, instead of making the thread realtime, just give it the highest non-realtime priority. */
+ if (SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_TIME_CRITICAL)) {
+ pa_log_info("Successfully enabled THREAD_PRIORITY_TIME_CRITICAL scheduling for thread.");
+ return 0;
+ }
+
+ pa_log_warn("SetThreadPriority() failed: 0x%08X", GetLastError());
+ errno = EPERM;
+#else
+ errno = ENOTSUP;
+#endif
+ pa_log_info("Failed to acquire real-time scheduling: %s", pa_cstrerror(errno));
+ return -1;
+}
++#endif
+
#ifdef HAVE_SYS_RESOURCE_H
static int set_nice(int nice_level) {
#ifdef HAVE_DBUS
if (!data->sink) {
pa_sink *sink = pa_namereg_get(core, NULL, PA_NAMEREG_SINK);
pa_return_val_if_fail(sink, -PA_ERR_NOENTITY);
- pa_sink_input_new_data_set_sink(data, sink, false);
+ pa_sink_input_new_data_set_sink(data, sink, false, false);
+#ifdef __TIZEN__
+ } else {
- pa_sink_input_new_data_set_sink(data, data->sink, false);
++ pa_sink_input_new_data_set_sink(data, data->sink, false, false);
+#endif
}
/* If something didn't pick a format for us, pick the top-most format since
if (state == i->thread_info.state)
return;
-
- if ((state == PA_SINK_INPUT_DRAINED || state == PA_SINK_INPUT_RUNNING) &&
- !(i->thread_info.state == PA_SINK_INPUT_DRAINED || i->thread_info.state != PA_SINK_INPUT_RUNNING))
- pa_atomic_store(&i->thread_info.drained, 1);
-
+#ifdef TIZEN_EMPTY_POP
+ if (state != PA_SINK_INPUT_RUNNING && _empty_pop_is_started(i))
+ _empty_pop_reset(i);
+#endif
corking = state == PA_SINK_INPUT_CORKED && i->thread_info.state == PA_SINK_INPUT_RUNNING;
uncorking = i->thread_info.state == PA_SINK_INPUT_CORKED && state == PA_SINK_INPUT_RUNNING;
/* SET_STATE is allowed to fail only when resuming. */
pa_assert(resuming);
- if (s->set_state)
- s->set_state(s, PA_SINK_SUSPENDED);
+ if (s->set_state_in_main_thread)
+ s->set_state_in_main_thread(s, PA_SINK_SUSPENDED, 0);
+
+ /* If resuming fails, we set the state to SUSPENDED and
+ * suspend_cause to 0. */
+ state = PA_SINK_SUSPENDED;
+ suspend_cause = 0;
+ state_changed = false;
+ suspend_cause_changed = suspend_cause != s->suspend_cause;
+ resuming = false;
+
+ /* We know the state isn't changing. If the suspend cause isn't
+ * changing either, then there's nothing more to do. */
+ if (!suspend_cause_changed)
+ return ret;
}
+ }
+#ifdef TIZEN_PCM_DUMP
+ /* close file for dump pcm */
+ if (s->pcm_dump_fp && (s->core->pcm_dump_option & PA_PCM_DUMP_OPTION_SEPARATED) && suspending) {
+ fclose(s->pcm_dump_fp);
+ pa_log_info("%s closed", s->dump_path);
+ pa_xfree(s->dump_path);
+ s->pcm_dump_fp = NULL;
+ }
+#endif
+ old_suspend_cause = s->suspend_cause;
if (suspend_cause_changed) {
char old_cause_buf[PA_SUSPEND_CAUSE_TO_STRING_BUF_SIZE];
char new_cause_buf[PA_SUSPEND_CAUSE_TO_STRING_BUF_SIZE];
pa_return_val_if_fail(source, -PA_ERR_NOENTITY);
}
- pa_source_output_new_data_set_source(data, source, false);
+ pa_source_output_new_data_set_source(data, source, false, false);
+ }
+#ifdef __TIZEN__
- } else {
- pa_source_output_new_data_set_source(data, data->source, false);
- #endif
++ else {
++ pa_source_output_new_data_set_source(data, data->source, false, false);
+ }
++#endif
++
/* If something didn't pick a format for us, pick the top-most format since
* we assume this is sorted in priority order */
/* SET_STATE is allowed to fail only when resuming. */
pa_assert(resuming);
- if (s->set_state)
- s->set_state(s, PA_SOURCE_SUSPENDED);
+ if (s->set_state_in_main_thread)
+ s->set_state_in_main_thread(s, PA_SOURCE_SUSPENDED, 0);
+
+ /* If resuming fails, we set the state to SUSPENDED and
+ * suspend_cause to 0. */
+ state = PA_SOURCE_SUSPENDED;
+ suspend_cause = 0;
+ state_changed = false;
+ suspend_cause_changed = suspend_cause != s->suspend_cause;
+ resuming = false;
+
+ /* We know the state isn't changing. If the suspend cause isn't
+ * changing either, then there's nothing more to do. */
+ if (!suspend_cause_changed)
+ return ret;
}
+ }
+#ifdef TIZEN_PCM_DUMP
+ /* close file for dump pcm */
+ if (s->pcm_dump_fp && (s->core->pcm_dump_option & PA_PCM_DUMP_OPTION_SEPARATED) && suspending) {
+ fclose(s->pcm_dump_fp);
+ pa_log_info("%s closed", s->dump_path);
+ pa_xfree(s->dump_path);
+ s->pcm_dump_fp = NULL;
+ }
+#endif
+ old_suspend_cause = s->suspend_cause;
if (suspend_cause_changed) {
char old_cause_buf[PA_SUSPEND_CAUSE_TO_STRING_BUF_SIZE];
char new_cause_buf[PA_SUSPEND_CAUSE_TO_STRING_BUF_SIZE];