summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAdrian Scarlat <adrian.scarlat@windriver.com>2015-05-22 14:16:45 +0000
committerJames Thomas <james.thomas@codethink.co.uk>2015-05-22 14:18:47 +0000
commitba709ee7d4f9f81af638a3d3c640b7152bbe32bc (patch)
tree3d05af7d27b462dbafcea61116474c9d9da3b3fd
parent64d2ba454ecfb1ea9bef3b4b717989afa58db1c7 (diff)
downloadaudiomanager-ba709ee7d4f9f81af638a3d3c640b7152bbe32bc.tar.gz
This Routing Interface is needed for any application that will
be developed on top of AM and will use PulseAudio Sound Server to control the sources and sinks present on the system. It must be loaded by AM; The interface can be built by supplying cmake with the -DWITH_PULSE_ROUTING_PLUGIN=ON; After building one configuration file will be available: 1. libPluginRoutingInterfacePULSE.conf - configuration file for Pulse Routing Plugin; it will be loaded at runtime by the Pulse Routing Interface; Changed files: CMakeLists.txt Added new folders: PluginRoutingInterfacePulse/ Added new files: PluginRoutingInterfacePulse/CMakeLists.txt PluginRoutingInterfacePulse/README PluginRoutingInterfacePulse/data/libPluginRoutingInterfacePULSE.conf PluginRoutingInterfacePulse/include/RoutingSenderMainloopPULSE.h PluginRoutingInterfacePulse/include/RoutingSenderPULSE.h PluginRoutingInterfacePulse/src/RoutingSenderMainloopPULSE.cpp PluginRoutingInterfacePulse/src/RoutingSenderPULSE.cpp Signed-off-by: Adrian Scarlat <adrian.scarlat@windriver.com>
-rwxr-xr-xCMakeLists.txt24
-rwxr-xr-xCMakeLists.txt.orig377
-rw-r--r--PluginRoutingInterfacePulse/CMakeLists.txt103
-rw-r--r--PluginRoutingInterfacePulse/README50
-rw-r--r--PluginRoutingInterfacePulse/data/libPluginRoutingInterfacePULSE.conf55
-rw-r--r--PluginRoutingInterfacePulse/include/RoutingSenderMainloopPULSE.h97
-rw-r--r--PluginRoutingInterfacePulse/include/RoutingSenderPULSE.h131
-rw-r--r--PluginRoutingInterfacePulse/src/RoutingSenderMainloopPULSE.cpp610
-rw-r--r--PluginRoutingInterfacePulse/src/RoutingSenderPULSE.cpp914
9 files changed, 2355 insertions, 6 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index abf2a20..be3ed83 100755
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -98,7 +98,15 @@ OPTION ( WITH_NSM
OPTION( WITH_PULSE_CONTROL_PLUGIN
"Enable PULSE Audio control plugin interface" OFF)
+OPTION( WITH_PULSE_ROUTING_PLUGIN
+ "Enable PULSE Audio routing plugin interface" OFF )
+
SET (WITH_COMMON_API_GEN ON CACHE INTERNAL "hide this!" FORCE)
+
+IF (WITH_PULSE_ROUTING_PLUGIN)
+ SET (WITH_DBUS_WRAPPER ON CACHE INTERNAL "hide this!" FORCE)
+ SET (WITH_CAPI_WRAPPER OFF CACHE INTERNAL "hide this!" FORCE)
+ENDIF (WITH_PULSE_ROUTING_PLUGIN)
IF (WITH_ENABLED_IPC STREQUAL "DBUS")
SET (WITH_DBUS_WRAPPER ON CACHE INTERNAL "hide this!" FORCE)
@@ -242,12 +250,16 @@ if(WITH_PLUGIN_COMMAND)
endif(WITH_PLUGIN_COMMAND)
if(WITH_PLUGIN_ROUTING)
- add_subdirectory (PluginRoutingInterfaceAsync)
- if(WITH_DBUS_WRAPPER)
- add_subdirectory (PluginRoutingInterfaceDbus)
- elseif(WITH_CAPI_WRAPPER)
- add_subdirectory (PluginRoutingInterfaceCAPI)
- endif()
+ if (WITH_PULSE_ROUTING_PLUGIN)
+ add_subdirectory (PluginRoutingInterfacePulse)
+ else ()
+ add_subdirectory (PluginRoutingInterfaceAsync)
+ if(WITH_DBUS_WRAPPER)
+ add_subdirectory (PluginRoutingInterfaceDbus)
+ elseif(WITH_CAPI_WRAPPER)
+ add_subdirectory (PluginRoutingInterfaceCAPI)
+ endif()
+ endif()
endif(WITH_PLUGIN_ROUTING)
if(WITH_PLUGIN_CONTROL)
diff --git a/CMakeLists.txt.orig b/CMakeLists.txt.orig
new file mode 100755
index 0000000..abf2a20
--- /dev/null
+++ b/CMakeLists.txt.orig
@@ -0,0 +1,377 @@
+# Copyright (C) 2012, BMW AG
+#
+# This file is part of GENIVI Project AudioManager.
+#
+# Contributions are licensed to the GENIVI Alliance under one or more
+# Contribution License Agreements.
+#
+# copyright
+# This Source Code Form is subject to the terms of the
+# Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed with
+# this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+#
+# author Christian Mueller, christian.ei.mueller@bmw.de BMW 2011,2012
+#
+# For further information see http://www.genivi.org/.
+#
+
+
+cmake_minimum_required(VERSION 2.6)
+include(CMakeDependentOption)
+
+execute_process(COMMAND git describe --tags WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
+ OUTPUT_VARIABLE DAEMONVERSION
+ OUTPUT_STRIP_TRAILING_WHITESPACE)
+
+IF (NOT DAEMONVERSION)
+ #Can be changed via passing -DVERSION="XXX" to cmake
+ IF(NOT DEFINED VERSION)
+ SET( DAEMONVERSION "homebrew-${CMAKE_SOURCE_DIR}" )
+ ELSE (NOT DEFINED VERSION)
+ SET( DAEMONVERSION "${VERSION}" )
+ ENDIF(NOT DEFINED VERSION)
+ELSE (NOT DAEMONVERSION)
+ STRING(REGEX REPLACE "(-)[^-]+$" "" DAEMONVERSION ${DAEMONVERSION})
+ STRING(REGEX REPLACE "-" "." DAEMONVERSION ${DAEMONVERSION})
+ENDIF(NOT DAEMONVERSION)
+
+message(STATUS "Build Version ${DAEMONVERSION}")
+
+execute_process(COMMAND git log --pretty=short WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
+ OUTPUT_FILE ${CMAKE_CURRENT_SOURCE_DIR}/CHANGELOG)
+
+PROJECT(AudioManagerDeamon)
+
+message( STATUS "CMAKE_TOOLCHAIN_FILE='${CMAKE_TOOLCHAIN_FILE}'" )
+
+FIND_PACKAGE(PkgConfig)
+
+SET(WITH_ENABLED_IPC "CAPI" CACHE STRING "Disable 'NONE' / Enable Common-API 'CAPI' or Dbus 'DBUS' Support")
+
+SET_PROPERTY(CACHE WITH_ENABLED_IPC PROPERTY STRINGS "NONE" "CAPI" "DBUS")
+
+OPTION( WITH_DLT
+ "Enable automotive-DLT Support" ON )
+
+OPTION( WITH_TESTS
+ "Build together with all available unitTest" ON )
+
+OPTION( WITH_DOCUMENTATION
+ "Build together with Doxygen Documentation" OFF )
+
+OPTION( WITH_PLUGIN_COMMAND
+ "Build command pluings" ON)
+
+OPTION( WITH_PLUGIN_CONTROL
+ "Build control plugin" ON)
+
+OPTION( WITH_PLUGIN_ROUTING
+ "Build routing pluings" ON)
+
+OPTION( WITH_TELNET
+ "build with Telnetserver (can only work with SocketHandler)" ON)
+
+OPTION ( WITH_SYSTEMD_WATCHDOG
+ "build with systemD support & watchdog" OFF)
+
+OPTION ( USE_BUILD_LIBS
+ "build with default library path = build path" ON)
+
+OPTION ( GLIB_DBUS_TYPES_TOLERANT
+ "build dbus with tolerance towards glib 16bit/32bit handling" ON)
+
+IF (WITH_ENABLED_IPC STREQUAL "NONE")
+ SET (ENABLE_NSM OFF)
+ELSE ()
+ SET (ENABLE_NSM ON)
+ENDIF ()
+
+CMAKE_DEPENDENT_OPTION(WITH_NSM "build with NSM support" ON
+ "ENABLE_NSM" OFF)
+
+OPTION ( WITH_NSM
+ "build with NSM support" ON)
+
+ OPTION ( WITH_DATABASE_STORAGE
+ "build with sqlite as in memory storage" OFF)
+
+OPTION( WITH_PULSE_CONTROL_PLUGIN
+ "Enable PULSE Audio control plugin interface" OFF)
+
+SET (WITH_COMMON_API_GEN ON CACHE INTERNAL "hide this!" FORCE)
+
+IF (WITH_ENABLED_IPC STREQUAL "DBUS")
+ SET (WITH_DBUS_WRAPPER ON CACHE INTERNAL "hide this!" FORCE)
+ SET (WITH_CAPI_WRAPPER OFF CACHE INTERNAL "hide this!" FORCE)
+ELSEIF(WITH_ENABLED_IPC STREQUAL "CAPI")
+ SET (WITH_CAPI_WRAPPER ON CACHE INTERNAL "hide this!" FORCE)
+ SET (WITH_DBUS_WRAPPER OFF CACHE INTERNAL "hide this!" FORCE)
+
+ MACRO(INSERT_DBUS_CONF_IF_NEEDED IN_PLACEHOLDER IN_SRC_DBUS_CONF OUT_DST_DBUS_CONF)
+ # Checks the variable in the template
+ if(NOT EXISTS ${CMAKE_SOURCE_DIR}/AudioManagerDaemon/fidls/AudioManager_dbus.conf.in )
+ FILE(WRITE ${CMAKE_SOURCE_DIR}/AudioManagerDaemon/fidls/AudioManager_dbus.conf.in
+ "################################################ CMAKE GENERATED #################################################")
+ endif( )
+
+ FILE(READ ${CMAKE_SOURCE_DIR}/AudioManagerDaemon/fidls/AudioManager_dbus.conf.in DBUS_CONF_IN)
+ if( NOT DBUS_CONF_IN MATCHES ${IN_PLACEHOLDER} )
+ if( NOT DBUS_CONF_IN STREQUAL "" )
+ FILE(APPEND ${CMAKE_SOURCE_DIR}/AudioManagerDaemon/fidls/AudioManager_dbus.conf.in "\r\n\r\n")
+ endif( )
+ FILE(APPEND ${CMAKE_SOURCE_DIR}/AudioManagerDaemon/fidls/AudioManager_dbus.conf.in ${IN_PLACEHOLDER})
+ endif( )
+ # Reads out the common-api dbus configuration for the node state manager.
+ FILE(READ ${IN_SRC_DBUS_CONF} ${OUT_DST_DBUS_CONF})
+ ENDMACRO(INSERT_DBUS_CONF_IF_NEEDED)
+
+ ELSEIF(WITH_ENABLED_IPC STREQUAL "NONE")
+ SET (WITH_CAPI_WRAPPER OFF CACHE INTERNAL "hide this!" FORCE)
+ SET (WITH_DBUS_WRAPPER OFF CACHE INTERNAL "hide this!" FORCE)
+ENDIF ()
+
+IF (NOT WITH_DBUS_WRAPPER AND NOT WITH_CAPI_WRAPPER)
+ SET (WITH_NSM OFF)
+ENDIF (NOT WITH_DBUS_WRAPPER AND NOT WITH_CAPI_WRAPPER)
+
+#Can be changed via passing -DDBUS_SERVICE_PREFIX="XXX" to cmake
+IF(NOT DEFINED DBUS_SERVICE_PREFIX)
+ SET( DBUS_SERVICE_PREFIX "org.genivi.audiomanager\0")
+ENDIF(NOT DEFINED DBUS_SERVICE_PREFIX)
+
+#Can be changed via passing -DDBUS_SERVICE_OBJECT_PATH="XXX" to cmake
+IF(NOT DEFINED DBUS_SERVICE_OBJECT_PATH)
+ SET( DBUS_SERVICE_OBJECT_PATH "/org/genivi/audiomanager\0" )
+ENDIF(NOT DEFINED DBUS_SERVICE_OBJECT_PATH)
+
+#Can be changed via passing -DDEFAULT_TELNETPORT="XXX" to cmake
+IF(NOT DEFINED DEFAULT_TELNETPORT)
+ SET( DEFAULT_TELNETPORT 6080 )
+ENDIF(NOT DEFINED DEFAULT_TELNETPORT)
+
+#Can be changed via passing -DMAX_TELNETCONNECTIONS="XXX" to cmake
+IF(NOT DEFINED MAX_TELNETCONNECTIONS)
+ SET( MAX_TELNETCONNECTIONS 3 )
+ENDIF(NOT DEFINED MAX_TELNETCONNECTIONS)
+
+#Can be changed via passing -DNSM_BUS_INTERFACE="XXX" to cmake
+IF(NOT DEFINED NSM_BUS_INTERFACE)
+ SET( NSM_BUS_INTERFACE "org.genivi.NodeStateManager")
+ENDIF(NOT DEFINED NSM_BUS_INTERFACE)
+
+#Can be changed via passing -DNSM_PATH="XXX" to cmake
+IF(NOT DEFINED NSM_PATH)
+ SET( NSM_PATH "/org/genivi/NodeStateManager")
+ENDIF(NOT DEFINED NSM_PATH)
+
+#Can be changed via passing -DNSM_INTERFACE="XXX" to cmake
+IF(NOT DEFINED NSM_INTERFACE)
+ SET( NSM_INTERFACE "org.genivi.NodeStateManager.Consumer")
+ENDIF(NOT DEFINED NSM_INTERFACE)
+
+SET(PLUGINS_OUTPUT_PATH ${CMAKE_SOURCE_DIR}/bin/plugins)
+SET(LIB_INSTALL_SUFFIX "audioManager")
+
+if(USE_BUILD_LIBS)
+ IF(NOT DEFINED DEFAULT_PLUGIN_COMMAND_DIR)
+ SET(DEFAULT_PLUGIN_COMMAND_DIR "${PLUGINS_OUTPUT_PATH}/command")
+ ENDIF(NOT DEFINED DEFAULT_PLUGIN_COMMAND_DIR)
+ IF(NOT DEFINED DEFAULT_PLUGIN_ROUTING_DIR)
+ SET(DEFAULT_PLUGIN_ROUTING_DIR "${PLUGINS_OUTPUT_PATH}/routing")
+ ENDIF(NOT DEFINED DEFAULT_PLUGIN_ROUTING_DIR)
+ IF(NOT DEFINED CONTROLLER_PLUGIN)
+ SET(CONTROLLER_PLUGIN "${PLUGINS_OUTPUT_PATH}/control/libPluginControlInterface.so")
+ ENDIF(NOT DEFINED CONTROLLER_PLUGIN)
+else(USE_BUILD_LIBS)
+ IF(NOT DEFINED DEFAULT_PLUGIN_COMMAND_DIR)
+ SET(DEFAULT_PLUGIN_COMMAND_DIR "${CMAKE_INSTALL_PREFIX}/lib/${LIB_INSTALL_SUFFIX}/command")
+ ENDIF(NOT DEFINED DEFAULT_PLUGIN_COMMAND_DIR)
+ IF(NOT DEFINED DEFAULT_PLUGIN_ROUTING_DIR)
+ SET(DEFAULT_PLUGIN_ROUTING_DIR "${CMAKE_INSTALL_PREFIX}/lib/${LIB_INSTALL_SUFFIX}/routing")
+ ENDIF(NOT DEFINED DEFAULT_PLUGIN_ROUTING_DIR)
+ IF(NOT DEFINED CONTROLLER_PLUGIN)
+ SET(CONTROLLER_PLUGIN "${CMAKE_INSTALL_PREFIX}/lib/${LIB_INSTALL_SUFFIX}/control/libPluginControlInterface.so")
+ ENDIF(NOT DEFINED CONTROLLER_PLUGIN)
+endif(USE_BUILD_LIBS)
+
+IF(EXISTS "ProjectSpecific/")
+ SET(PROJECT_INCLUDE_FOLDER ${CMAKE_SOURCE_DIR}/ProjectSpecific/include)
+endif(EXISTS "ProjectSpecific/")
+
+SET(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${CMAKE_SOURCE_DIR}/cmake)
+SET(AUDIO_INCLUDE_FOLDER ${CMAKE_SOURCE_DIR}/include)
+SET(DOC_OUTPUT_PATH ${CMAKE_SOURCE_DIR}/doc)
+SET(EXECUTABLE_OUTPUT_PATH ${CMAKE_SOURCE_DIR}/bin)
+SET(TEST_EXECUTABLE_OUTPUT_PATH ${CMAKE_SOURCE_DIR}/bin/test)
+SET(DOXY_PROTOTYPE ${CMAKE_SOURCE_DIR}/cmake/DoxyFile.in)
+SET(DOXY_FILE ${CMAKE_CURRENT_BINARY_DIR}/DoxyFile)
+
+CONFIGURE_FILE( ${CMAKE_SOURCE_DIR}/AudioManagerDaemon/docx/Doxyfile.in ${CMAKE_CURRENT_BINARY_DIR}/DoxyFile )
+
+IF(WITH_DLT)
+ pkg_check_modules(DLT REQUIRED automotive-dlt>=2.2.0)
+ENDIF(WITH_DLT)
+
+IF(WITH_TESTS)
+ add_subdirectory(googleMock)
+ set(GMOCK_INCLUDE_DIR "${CMAKE_SOURCE_DIR}/googleMock/include")
+ set(GOOGLE_TEST_INCLUDE_DIR "${CMAKE_SOURCE_DIR}/googleMock/gtest/include")
+ENDIF(WITH_TESTS)
+
+IF(WITH_DOCUMENTATION)
+ find_package(Doxygen)
+ configure_file(${DOXY_FILE} ${DOC_OUTPUT_PATH}/Doxyfile @ONLY IMMEDIATE)
+ configure_file("README.html" ${DOC_OUTPUT_PATH}/html/README.html)
+ add_custom_target (AudioManangerDoku ALL
+ COMMAND ${DOXYGEN_EXECUTABLE} ${DOC_OUTPUT_PATH}/Doxyfile WORKING_DIRECTORY ${DOC_OUTPUT_PATH}
+ SOURCES ${CMAKE_SOURCE_DIR} ${DOC_OUTPUT_PATH}/Doxyfile
+ )
+ENDIF(WITH_DOCUMENTATION)
+
+##global build flags set(CPACK_RPM_COMPONENT_INSTALL ON)
+set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g -Wall -Wextra -std=gnu++0x -D_GNU_SOURCE -pedantic -Wno-variadic-macros -Wno-long-long")
+#set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g -Wall -Wextra -std=c++98 -D_GNU_SOURCE")
+
+
+if(WITH_PLUGIN_COMMAND)
+ if(WITH_DBUS_WRAPPER)
+ add_subdirectory (PluginCommandInterfaceDbus)
+ elseif(WITH_CAPI_WRAPPER)
+ add_subdirectory (PluginCommandInterfaceCAPI)
+ endif()
+endif(WITH_PLUGIN_COMMAND)
+
+if(WITH_PLUGIN_ROUTING)
+ add_subdirectory (PluginRoutingInterfaceAsync)
+ if(WITH_DBUS_WRAPPER)
+ add_subdirectory (PluginRoutingInterfaceDbus)
+ elseif(WITH_CAPI_WRAPPER)
+ add_subdirectory (PluginRoutingInterfaceCAPI)
+ endif()
+endif(WITH_PLUGIN_ROUTING)
+
+if(WITH_PLUGIN_CONTROL)
+ if(WITH_PULSE_CONTROL_PLUGIN)
+ add_subdirectory (PluginControlInterfacePulse)
+ else ()
+ add_subdirectory (PluginControlInterface)
+ endif(WITH_PULSE_CONTROL_PLUGIN)
+endif(WITH_PLUGIN_CONTROL)
+
+add_subdirectory (AudioManagerDaemon)
+
+
+IF(EXISTS "${CMAKE_SOURCE_DIR}/ProjectSpecific/")
+ add_subdirectory (ProjectSpecific)
+endif(EXISTS "${CMAKE_SOURCE_DIR}/ProjectSpecific/")
+
+# uninstall target
+configure_file(
+ "${CMAKE_CURRENT_SOURCE_DIR}/cmake/cmake_uninstall.cmake.in"
+ "${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake"
+ IMMEDIATE @ONLY)
+
+add_custom_target(uninstall
+ COMMAND ${CMAKE_COMMAND} -P ${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake)
+
+# Here starts package creation
+SET(CPACK_SET_DESTDIR ON)
+SET(CPACK_OUTPUT_FILE_PREFIX ${CMAKE_SOURCE_DIR}/packages)
+SET(CPACK_GENERATOR "DEB")
+SET(CPACK_PACKAGE_NAME "AudioManager")
+SET(CPACK_PACKAGE_VENDOR "GENIVI")
+SET(CPACK_PACKAGE_DESCRIPTION_SUMMARY "AudioManager: This component manages audio in the GENIVI context")
+SET(CPACK_PACKAGE_VERSION "${DAEMONVERSION}")
+SET(CPACK_RESOURCE_FILE_LICENSE ${CMAKE_SOURCE_DIR}/LICENCE)
+SET(CPACK_RESOURCE_FILE_README ${CMAKE_SOURCE_DIR}/README)
+SET(CPACK_PACKAGE_CONTACT "Christian Linke(BMW) christian.linke@bmw.de")
+SET(CPACK_PACKAGE_INSTALL_DIRECTORY "genivi")
+SET(CPACK_DEBIAN_ARCHITECTURE ${CMAKE_SYSTEM_PROCESSOR})
+SET(CPACK_DEBIAN_PACKAGE_DEPENDS "*")
+SET(CPACK_STRIP_FILES TRUE)
+SET(CPACK_PACKAGE_DESCRIPTION_FILE ${CMAKE_SOURCE_DIR}/README)
+SET(CPACK_SOURCE_GENERATOR "TGZ")
+SET(CPACK_SOURCE_IGNORE_FILES ".settings*" ".cproject" "/\\\\.metadata" "\\\\.#" "/#" ".*~" "/\\\\.git" "${CMAKE_CURRENT_BINARY_DIR}" "bin/" "packages/" "config.h")
+
+IF(WITH_TESTS)
+ get_property(ADD_DEPEND GLOBAL PROPERTY tests_prop)
+ list(REMOVE_DUPLICATES ADD_DEPEND)
+ list(APPEND ALL_DEPEND ${ADD_DEPEND})
+ FOREACH (dep ${ADD_DEPEND})
+ SET(tests_DEPENDENCIES "${dep} ,${tests_DEPENDENCIES}")
+ ENDFOREACH(dep)
+ STRING(REGEX REPLACE ".$" "" tests_DEPENDENCIES ${tests_DEPENDENCIES})
+ENDIF(WITH_TESTS)
+
+#evaluate the properties
+if(WITH_MAIN)
+get_property(ADD_DEPEND GLOBAL PROPERTY bin_prop)
+list(REMOVE_DUPLICATES ADD_DEPEND)
+set(ALL_DEPEND ${ADD_DEPEND})
+FOREACH (dep ${ADD_DEPEND})
+ SET(bin_DEPENDENCIES "${dep} ,${bin_DEPENDENCIES}")
+ENDFOREACH(dep)
+STRING(REGEX REPLACE ".$" "" bin_DEPENDENCIES ${bin_DEPENDENCIES})
+endif(WITH_MAIN)
+
+if(WITH_PLUGIN_COMMAND OR WITH_PLUGIN_CONTROL OR WITH_PLUGIN_ROUTING)
+get_property(ADD_DEPEND GLOBAL PROPERTY sampleplugins_prop)
+list(REMOVE_DUPLICATES ADD_DEPEND)
+list(APPEND ALL_DEPEND ${ADD_DEPEND})
+FOREACH (dep ${ADD_DEPEND})
+ SET(sampleplugins_DEPENDENCIES "${dep} ,${sampleplugins_DEPENDENCIES}")
+ENDFOREACH(dep)
+STRING(REGEX REPLACE ".$" "" sampleplugins_DEPENDENCIES ${sampleplugins_DEPENDENCIES})
+endif(WITH_PLUGIN_COMMAND OR WITH_PLUGIN_CONTROL OR WITH_PLUGIN_ROUTING)
+
+get_property(ADD_DEPEND GLOBAL PROPERTY dev_prop)
+list(REMOVE_DUPLICATES ADD_DEPEND)
+list(APPEND ALL_DEPEND ${ADD_DEPEND})
+FOREACH (dep ${ADD_DEPEND})
+ SET(dev_DEPENDENCIES "${dep} ,${dev_DEPENDENCIES}")
+ENDFOREACH(dep)
+STRING(REGEX REPLACE ".$" "" dev_DEPENDENCIES ${dev_DEPENDENCIES})
+
+list(REMOVE_DUPLICATES ALL_DEPEND)
+list(REMOVE_ITEM ALL_DEPEND "audiomanager-bin")
+FOREACH (dep ${ALL_DEPEND})
+ SET(all_DEPENDENCIES "${dep} ,${all_DEPENDENCIES}")
+ENDFOREACH(dep)
+STRING(REGEX REPLACE ".$" "" all_DEPENDENCIES ${all_DEPENDENCIES})
+execute_process(COMMAND cp ${CMAKE_MODULE_PATH}/add_package_dependencies.sh ${CMAKE_CURRENT_BINARY_DIR})
+
+#component based dep package generation is only supported above 2.8.5
+IF (${CMAKE_VERSION} VERSION_GREATER 2.8.5)
+
+ SET(CPACK_COMPONENTS_ALL bin sampleplugins tests dev)
+ SET(CPACK_COMPONENTS_IGNORE_GROUPS 1)
+ SET(CPACK_DEB_COMPONENT_INSTALL ON)
+ ADD_CUSTOM_TARGET(genivi_package
+ COMMAND ${CMAKE_COMMAND} ${CMAKE_BINARY_DIR} -DUSE_BUILD_LIBS=OFF
+ COMMAND make package
+ COMMAND ${CMAKE_CURRENT_BINARY_DIR}/add_package_dependencies.sh ${CPACK_PACKAGE_NAME}-${CPACK_PACKAGE_VERSION}-Linux-bin.deb ${CPACK_OUTPUT_FILE_PREFIX} \"${bin_DEPENDENCIES}\"
+ COMMAND ${CMAKE_CURRENT_BINARY_DIR}/add_package_dependencies.sh ${CPACK_PACKAGE_NAME}-${CPACK_PACKAGE_VERSION}-Linux-sampleplugins.deb ${CPACK_OUTPUT_FILE_PREFIX} \"${sampleplugins_DEPENDENCIES}\"
+ COMMAND ${CMAKE_CURRENT_BINARY_DIR}/add_package_dependencies.sh ${CPACK_PACKAGE_NAME}-${CPACK_PACKAGE_VERSION}-Linux-tests.deb ${CPACK_OUTPUT_FILE_PREFIX} \"${tests_DEPENDENCIES}\"
+ COMMAND ${CMAKE_CURRENT_BINARY_DIR}/add_package_dependencies.sh ${CPACK_PACKAGE_NAME}-${CPACK_PACKAGE_VERSION}-Linux-dev.deb ${CPACK_OUTPUT_FILE_PREFIX} \"${dev_DEPENDENCIES}\"
+ )
+ELSE (${CMAKE_VERSION} VERSION_GREATER 2.8.5)
+ IF(${CMAKE_VERSION} VERSION_GREATER 2.8.3)
+ ADD_CUSTOM_TARGET(genivi_package
+ COMMAND ${CMAKE_COMMAND} ${CMAKE_BINARY_DIR} -DUSE_BUILD_LIBS=OFF
+ COMMAND make package
+ COMMAND ${CMAKE_CURRENT_BINARY_DIR}A logical block opening on t/add_package_dependencies.sh ${CPACK_PACKAGE_NAME}-${CPACK_PACKAGE_VERSION}-Linux.deb ${CPACK_OUTPUT_FILE_PREFIX} \"${all_DEPENDENCIES}\"
+ )
+ ELSE(${CMAKE_VERSION} VERSION_GREATER 2.8.3)
+ ADD_CUSTOM_TARGET(genivi_package
+ COMMAND ${CMAKE_COMMAND} ${CMAKE_BINARY_DIR} -DUSE_BUILD_LIBS=OFF
+ COMMAND make package
+ COMMAND mkdir -p ../${CPACK_OUTPUT_FILE_PREFIX}
+ COMMAND mv ${CMAKE_CURRENT_BINARY_DIR}/${CPACK_PACKAGE_NAME}-${CPACK_PACKAGE_VERSION}-Linux.deb ${CPACK_OUTPUT_FILE_PREFIX}
+ COMMAND ${CMAKE_CURRENT_BINARY_DIR}/add_package_dependencies.sh ${CPACK_PACKAGE_NAME}-${CPACK_PACKAGE_VERSION}-Linux.deb ${CPACK_OUTPUT_FILE_PREFIX} \"${all_DEPENDENCIES}\"
+ )
+ ENDIF(${CMAKE_VERSION} VERSION_GREATER 2.8.3)
+ENDIF (${CMAKE_VERSION} VERSION_GREATER 2.8.5)
+
+INCLUDE(CPack)
+
+
diff --git a/PluginRoutingInterfacePulse/CMakeLists.txt b/PluginRoutingInterfacePulse/CMakeLists.txt
new file mode 100644
index 0000000..1a15b40
--- /dev/null
+++ b/PluginRoutingInterfacePulse/CMakeLists.txt
@@ -0,0 +1,103 @@
+############################################################################
+# SPDX license identifier: MPL-2.0
+#
+# Copyright (C) 2012-2014, Wind River Systems
+# Copyright (C) 2014, GENIVI Alliance
+#
+# This file is part of Pulse Audio Interface Routing Plugin.
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License (MPL), v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+#
+# For further information see http://www.genivi.org/.
+#
+# List of changes:
+#
+# 21.08.2014, Adrian Scarlat, First version of the code;
+# Porting code from AM ver1.x to AM ver3.0;
+# Added Copyright and License information;
+############################################################################
+
+cmake_minimum_required(VERSION 2.6)
+
+PROJECT(PluginRoutingInterfacePULSE)
+
+set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -rdynamic")
+set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g")
+FILE(GLOB pulseConf "${CMAKE_CURRENT_SOURCE_DIR}/README*")
+FIND_PACKAGE(PkgConfig)
+
+OPTION( WITH_DOCUMENTATION
+ "Build together with Doxygen Documentation" OFF )
+
+SET(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${CMAKE_SOURCE_DIR}/cmake)
+SET(PLUGINS_OUTPUT_PATH ${CMAKE_SOURCE_DIR}/bin/plugins)
+set(LIBRARY_OUTPUT_PATH ${PLUGINS_OUTPUT_PATH}/routing)
+set(DOC_OUTPUT_PATH ${CMAKE_SOURCE_DIR}/doc/RoutingPlugin)
+set(INCLUDES_FOLDER "include")
+SET(LIB_INSTALL_SUFFIX "audioManager")
+
+FIND_PACKAGE(DBUS REQUIRED)
+FIND_PATH(AUDIO_INCLUDE_FOLDER audiomanagertypes.h /usr/include)
+
+if(DEFINED AUDIO_INCLUDE_FOLDER)
+ message(STATUS "Found AudioManager include: ${AUDIO_INCLUDE_FOLDER}")
+else(DEFINED AUDIO_INCLUDE_FOLDER)
+ message(STATUS "Did not found AudioManager include!")
+endif(DEFINED AUDIO_INCLUDE_FOLDER)
+
+FILE(READ "${AUDIO_INCLUDE_FOLDER}/routing/IAmRoutingSend.h" VERSION_BUFFER LIMIT 6000)
+STRING(REGEX MATCH "RoutingSendVersion*.[^0-9]*[0-9]" LIB_INTERFACE_VERSION_STRING ${VERSION_BUFFER})
+STRING(REGEX REPLACE "[^0-9]" "" LIB_INTERFACE_VERSION ${LIB_INTERFACE_VERSION_STRING})
+MESSAGE(STATUS "Building against routing interface version ${LIB_INTERFACE_VERSION}")
+
+SET(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${CMAKE_SOURCE_DIR}/cmake)
+FIND_PACKAGE(DBUS REQUIRED)
+
+INCLUDE_DIRECTORIES(
+ ${CMAKE_SOURCE_DIR}
+ ${CMAKE_CURRENT_BINARY_DIR}
+ ${DBUS_INCLUDE_DIR}
+ ${DBUS_ARCH_INCLUDE_DIR}
+ ${AUDIO_INCLUDE_FOLDER}
+ ${INCLUDES_FOLDER}
+)
+
+# all source files go here
+file(GLOB PLUGINDBUS_SRCS_CXX "src/*.cpp")
+
+add_library(PluginRoutingInterfacePULSE SHARED ${PLUGINDBUS_SRCS_CXX})
+
+TARGET_LINK_LIBRARIES(PluginRoutingInterfacePULSE
+ pulse
+ ${DLT_LIBRARIES}
+ ${DBUS_LIBRARY}
+)
+
+IF(WITH_DOCUMENTATION)
+ file(MAKE_DIRECTORY ${DOC_OUTPUT_PATH})
+ configure_file(${DOXY_FILE} ${DOC_OUTPUT_PATH}/Doxyfile @ONLY IMMEDIATE)
+ add_custom_target (PluginRoutingInterfacePULSEDocs ALL
+ COMMAND ${DOXYGEN_EXECUTABLE} ${DOC_OUTPUT_PATH}/Doxyfile WORKING_DIRECTORY ${DOC_OUTPUT_PATH}
+ SOURCES ${PROJECT_BINARY_DIR} ${DOC_OUTPUT_PATH}/Doxyfile
+ )
+ENDIF(WITH_DOCUMENTATION)
+
+INSTALL(TARGETS PluginRoutingInterfacePULSE
+ DESTINATION "lib/${LIB_INSTALL_SUFFIX}/routing"
+ PERMISSIONS OWNER_EXECUTE OWNER_WRITE OWNER_READ GROUP_EXECUTE GROUP_READ WORLD_EXECUTE WORLD_READ
+ COMPONENT sampleplugins
+)
+
+INSTALL(FILES data/libPluginRoutingInterfacePULSE.conf
+ DESTINATION "lib/${LIB_INSTALL_SUFFIX}/routing"
+ PERMISSIONS OWNER_EXECUTE OWNER_WRITE OWNER_READ GROUP_EXECUTE GROUP_READ WORLD_EXECUTE WORLD_READ
+ COMPONENT sampleplugins
+)
+
+# Uncomment the following five lines bellow (that start with #CONFIGURE_FILE...)
+# to make those files available in build environment;
+# For meta-ivi deployment purposes leave them commented.
+
+#CONFIGURE_FILE( ${CMAKE_SOURCE_DIR}/PluginRoutingInterfacePulse/data/libPluginRoutingInterfacePULSE.conf ${PLUGINS_OUTPUT_PATH}/routing/libPluginRoutingInterfacePULSE.conf )
diff --git a/PluginRoutingInterfacePulse/README b/PluginRoutingInterfacePulse/README
new file mode 100644
index 0000000..8238b94
--- /dev/null
+++ b/PluginRoutingInterfacePulse/README
@@ -0,0 +1,50 @@
+GENIVI_AudioManager_PluginRoutingInterfacePulse
+===============================================
+:Author: Adrian Scarlat <adrian.scarlat@windriver.com>
+:doctitle: GENIVI_AudioManager_PluginRoutingInterfacePulse
+
+SPDX license identifier: MPL-2.0
+
+Copyright (C) 2011-2014, Wind River Systems
+Copyright (C) 2014, GENIVI Alliance
+
+This file is part of AudioManager Pulse Audio Interface Routing Plugin.
+
+This Source Code Form is subject to the terms of the Mozilla Public
+License (MPL), v. 2.0. If a copy of the MPL was not distributed with this
+file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+For further information see http://www.genivi.org/.
+
+== Documentation
+Documentation is provided by doxygen. In order to use this, please compile the
+AudioManager like this:
+cmake -DWITH_DOCUMENTATION=ON
+make
+
+== Description of Pulse Routing Plugin
+The PluginRoutingInterfacePulse is used by the AM PoC application to communicate
+with PulseAudio present on the system on which the AM PoC application will be
+deployed.
+
+== Build intstructions
+Execute the following command from audiomanager/ folder:
+mkdir BUILD
+cd BUILD
+cmake -DWITH_ENABLED_IPC=DBUS -DWITH_PULSE_ROUTING_PLUGIN=ON ..
+make [ add to make command "-j 4" if you have a 4 CPU.
+
+If all goes well a bin/ folder will be made.
+Change to it by executing cd ../bin/
+
+In order to use the AudioManager with the PluginRoutingInterfacePulse, the
+AudioManager must be compiled with PluginRoutingInterfacePulse and with
+PluginControlInterfacePulse.For achieving this please consult the README
+from the PluginControlInterfacePulse Project also.
+
+== Available files after building
+libPluginRoutingInterfacePULSE.conf -- This is Pulse Routing Interface
+configuration files.It is used for configuring Sources and Sinks on the system.
+The contend on this file must be in sync with libPluginControlInterface.conf
+file from the PluginControlInterfacePulse Projec.Also some PulseAudio knowledge
+is advisable.
diff --git a/PluginRoutingInterfacePulse/data/libPluginRoutingInterfacePULSE.conf b/PluginRoutingInterfacePulse/data/libPluginRoutingInterfacePULSE.conf
new file mode 100644
index 0000000..d8b9868
--- /dev/null
+++ b/PluginRoutingInterfacePulse/data/libPluginRoutingInterfacePULSE.conf
@@ -0,0 +1,55 @@
+############################################################################
+# SPDX license identifier: MPL-2.0
+#
+# Copyright (C) 2012-2014, Wind River Systems
+# Copyright (C) 2014, GENIVI Alliance
+#
+# This file is part of Pulse Audio Interface Routing Plugin.
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License (MPL), v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+#
+# For further information see http://www.genivi.org/.
+#
+# List of changes:
+#
+# 21.08.2014, Adrian Scarlat, First version of the code;
+# Porting code from AM ver1.x to AM ver3.0;
+# Added Copyright and License information;
+############################################################################
+#
+# config line format:
+#
+# TYPE|PULSE TYPE|CLASS|NAME|PROPERTY_NAME|PROPERTY_VALUE
+#
+# TYPE="Source" or "Sink"
+#
+# PULSE_TYPE="Sink Input" or "Source" for TYPE="Source"
+# PULSE_TYPE="Source Output" or "Sink" for TYPE="Sink"
+#
+# NAME=Any string not containing separator |
+#
+# CLASS=Any string not containing separator | - name should be consistent with Controller config
+#
+# PROPERTY_NAME=a Pulse recognized element property, e.g. "application.process.binary" or "device.class"
+# for PULSE_TYPE=Source or PULSE_TYPE=Sink, take a look ad device string
+# PROPERTY_VALUE=any string corresponding to the property value
+#
+# TODO: provide support for multiple attributes filtering
+# TODO: comment at eof is mandatory! this looks like a bug for the moment
+####################
+# Sources
+####################
+Source|Sink Input|Entertainment|MediaPlayer|media.role|MEDIA
+Source|Sink Input|Navigation|NaviPlayer|media.role|NAVI
+Source|Sink Input|TTS|TTSPlayer|media.role|TextToSpeach
+Source|Sink Input|Telephony|Skype|media.role|skype
+Source|Sink Input|Analogic|ReverseBeep|media.role|reverse
+####################
+# Sinks
+####################
+Sink|Sink|HifiAudio|AlsaPrimary|na|na
+Sink|Sink|HifiAudio|AlsaSecondary|na|na
+# !END
+
diff --git a/PluginRoutingInterfacePulse/include/RoutingSenderMainloopPULSE.h b/PluginRoutingInterfacePulse/include/RoutingSenderMainloopPULSE.h
new file mode 100644
index 0000000..ff0c1d8
--- /dev/null
+++ b/PluginRoutingInterfacePulse/include/RoutingSenderMainloopPULSE.h
@@ -0,0 +1,97 @@
+/**
+ * SPDX license identifier: MPL-2.0
+ *
+ * Copyright (C) 2011-2014, Wind River Systems
+ * Copyright (C) 2014, GENIVI Alliance
+ *
+ * This file is part of Pulse Audio Interface Routing Plugin.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License (MPL), v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * For further information see http://www.genivi.org/.
+ *
+ * List of changes:
+ *
+ * 21.08.2014, Adrian Scarlat, First version of the code;
+ * Porting code from AM ver1.x to AM ver3.0;
+ * Added Copyright and License information;
+ */
+
+#ifndef ROUTINGSENDERMAINLOPPPULSE_H_
+#define ROUTINGSENDERMAINLOPPPULSE_H_
+
+/* Defines */
+/* In PulseAudio the volume value range from 0 (silence) to 0x10000U=65536 ("maximum" sensible volume). */
+#define MAX_PULSE_VOLUME (0x10000U)
+
+bool routing_sender_create_mainloop(void *thiz);
+
+void * routing_sender_start_mainloop(void *thiz);
+
+void routing_sender_get_sink_info_callback(
+ pa_context *c,
+ const pa_sink_info *i,
+ int is_last, void *thiz);
+
+void routing_sender_context_state_callback(pa_context *c, void *thiz);
+
+bool routing_sender_get_source_info(pa_context *c, void *thiz);
+
+void routing_sender_pa_event_callback(
+ pa_context *c,
+ pa_subscription_event_type_t t,
+ uint32_t idx, void *thiz);
+
+void routing_sender_get_sink_input_info_callback(
+ pa_context *c,
+ const pa_sink_input_info *i,
+ int eol,
+ void *thiz);
+
+void routing_sender_get_source_output_info_callback(
+ pa_context *c,
+ const pa_source_output_info *i,
+ int eol,
+ void *userdata);
+
+bool routing_sender_move_sink_input(
+ pa_context *c,
+ uint32_t sink_input_index,
+ uint32_t sink_index,
+ void *thiz);
+
+bool routing_sender_move_source_output(
+ pa_context *c,
+ uint32_t source_output_index,
+ uint32_t source_index,
+ void *thiz);
+
+bool routing_sender_sink_input_volume_ramp(
+ pa_context *c,
+ uint32_t sink_input_index,
+ uint32_t crt_volume,
+ uint32_t volume,
+ uint16_t ramp_time,
+ void *thiz);
+
+bool routing_sender_sink_input_volume(
+ pa_context *c,
+ uint32_t sink_input_index,
+ uint32_t volume,
+ void *thiz);
+
+bool routing_sender_sink_input_mute(
+ pa_context *c,
+ uint32_t sink_input_index,
+ bool mute,
+ void *thiz);
+
+bool routing_sender_sink_volume(
+ pa_context *c,
+ uint32_t sink_index,
+ uint32_t volume,
+ void *thiz);
+
+#endif
diff --git a/PluginRoutingInterfacePulse/include/RoutingSenderPULSE.h b/PluginRoutingInterfacePulse/include/RoutingSenderPULSE.h
new file mode 100644
index 0000000..0fc2197
--- /dev/null
+++ b/PluginRoutingInterfacePulse/include/RoutingSenderPULSE.h
@@ -0,0 +1,131 @@
+/**
+ * SPDX license identifier: MPL-2.0
+ *
+ * Copyright (C) 2011-2014, Wind River Systems
+ * Copyright (C) 2014, GENIVI Alliance
+ *
+ * This file is part of Pulse Audio Interface Routing Plugin.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License (MPL), v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * For further information see http://www.genivi.org/.
+ *
+ * List of changes:
+ *
+ * 21.08.2014, Adrian Scarlat, First version of the code;
+ * Porting code from AM ver1.x to AM ver3.0;
+ * Added Copyright and License information;
+ */
+
+#ifndef ROUTINGSENDERPULSE_H_
+#define ROUTINGSENDERPULSE_H_
+
+/* Includes */
+#include "routing/IAmRoutingSend.h"
+#include <signal.h>
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+#include <assert.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <limits.h>
+#include <getopt.h>
+#include <locale.h>
+#include <map>
+#include <pulse/pulseaudio.h>
+
+using namespace am;
+
+struct RoutingSenderPULSEConnection
+{
+ am_connectionID_t connectionID;
+ am_sourceID_t sourceID;
+ am_sinkID_t sinkID;
+ am_Handle_s handle;
+ bool pending;
+};
+
+
+struct RoutingSenderPULSESourceSinkConfig
+{
+ am_Source_s source;
+ am_Sink_s sink;
+
+ std::string name;//e.g. {"gst-launch-0.10", "mono", "aplay"};
+ std::string clazz;//e.g. {"Entertainment", "Navigation", "TTS"};
+ std::string propertyName;//e.g. {"application.process.binary", "application.process.binary", "application.process.app"};
+ std::string propertyValue;//
+};
+
+
+/* Prototypes */
+class RoutingSenderPULSE : public IAmRoutingSend
+{
+public:
+ RoutingSenderPULSE(pa_context *p_paContext);
+ ~RoutingSenderPULSE();
+
+ am::am_Error_e startupInterface(am::IAmRoutingReceive* p_routingReceiver);
+ void setRoutingReady(uint16_t handle);
+ void setRoutingRundown(uint16_t handle);
+ am_Error_e asyncAbort(const am_Handle_s handle);
+ am_Error_e asyncConnect(const am_Handle_s handle, const am_connectionID_t connectionID, const am_sourceID_t sourceID, const am_sinkID_t sinkID, const am_CustomConnectionFormat_t connectionFormat);
+ am_Error_e asyncDisconnect(const am_Handle_s handle, const am_connectionID_t connectionID);
+ am_Error_e asyncSetSinkVolume(const am_Handle_s handle, const am_sinkID_t sinkID, const am_volume_t volume, const am_CustomRampType_t ramp, const am_time_t time);
+ am_Error_e asyncSetSourceVolume(const am_Handle_s handle, const am_sourceID_t sourceID, const am_volume_t volume, const am_CustomRampType_t ramp, const am_time_t time);
+ am_Error_e asyncSetSourceState(const am_Handle_s handle, const am_sourceID_t sourceID, const am_SourceState_e state);
+ am_Error_e asyncSetSinkSoundProperties(const am_Handle_s handle, const am_sinkID_t sinkID, const std::vector<am_SoundProperty_s>& listSoundProperties);
+ am_Error_e asyncSetSinkSoundProperty(const am_Handle_s handle, const am_sinkID_t sinkID, const am_SoundProperty_s& soundProperty);
+ am_Error_e asyncSetSourceSoundProperties(const am_Handle_s handle, const am_sourceID_t sourceID, const std::vector<am_SoundProperty_s>& listSoundProperties);
+ am_Error_e asyncSetSourceSoundProperty(const am_Handle_s handle, const am_sourceID_t sourceID, const am_SoundProperty_s& soundProperty);
+ am_Error_e asyncCrossFade(const am_Handle_s handle, const am_crossfaderID_t crossfaderID, const am_HotSink_e hotSink, const am_CustomRampType_t rampType, const am_time_t time);
+ am_Error_e setDomainState(const am_domainID_t domainID, const am_DomainState_e domainState);
+ am_Error_e returnBusName(std::string& BusName) const;
+ void getInterfaceVersion(std::string& out_ver) const;
+
+ void setPAContext(pa_context *p_paContext) {
+ this->m_paContext = p_paContext;
+ }
+ am_Error_e asyncSetVolumes(const am_Handle_s handle, const std::vector<am_Volumes_s>& listVolumes);
+ am_Error_e asyncSetSinkNotificationConfiguration(const am_Handle_s handle, const am_sinkID_t sinkID, const am_NotificationConfiguration_s& notificationConfiguration);
+ am_Error_e asyncSetSourceNotificationConfiguration(const am_Handle_s handle, const am_sourceID_t sourceID, const am_NotificationConfiguration_s& notificationConfiguration);
+//Pulse Audio callbacks
+ void getSinkInfoCallback(pa_context *c, const pa_sink_info *i, int is_last, void *userdata);
+ void getSourceInfoCallback(pa_context *c, const pa_source_info *i, int is_last, void *userdata);
+ void getSinkInputInfoCallback(pa_context *c, const pa_sink_input_info *i, void *userdata);
+ void getSourceOutputInfoCallback(pa_context *c, const pa_source_output_info *i, void *userdata);
+
+private:
+ void loadConfig();
+
+ am_Domain_s m_domain;
+
+ std::vector<RoutingSenderPULSESourceSinkConfig> m_sinks;
+ std::vector<RoutingSenderPULSESourceSinkConfig> m_sources;
+
+ std::map<uint16_t, uint32_t> m_sourceToPASinkInput;
+ std::map<uint16_t, uint32_t> m_sourceToPASource;
+ std::map<uint16_t, uint32_t> m_sinkToPASourceOutput;
+ std::map<uint16_t, uint32_t> m_sinkToPASink;
+
+ uint16_t m_paSinkNullIndex;
+ uint16_t m_paSourceNullIndex;
+
+ IAmRoutingReceive *m_routingReceiver;
+ pa_context *m_paContext;
+
+/**
+ * Maintain a list of pending actions: there is a high change that the HMI first call connect,
+ * then the audio client start to play, therefore, sink-input is not yet created by the time "connect" method was called.
+ * same for volume? not sure - probably the sink input is created when the user change the volume.
+ * same for disconnect? not sure - probably the sink input was already created by the time the user is calling disconnect
+ */
+ std::vector<RoutingSenderPULSEConnection> m_activeConnections;
+ std::map<uint16_t, uint16_t> m_sinkToVolume;
+ std::map<uint16_t, uint16_t> m_sourceToVolume;
+};
+
+#endif
diff --git a/PluginRoutingInterfacePulse/src/RoutingSenderMainloopPULSE.cpp b/PluginRoutingInterfacePulse/src/RoutingSenderMainloopPULSE.cpp
new file mode 100644
index 0000000..5738b42
--- /dev/null
+++ b/PluginRoutingInterfacePulse/src/RoutingSenderMainloopPULSE.cpp
@@ -0,0 +1,610 @@
+/**
+ * SPDX license identifier: MPL-2.0
+ *
+ * Copyright (C) 2011-2014, Wind River Systems
+ * Copyright (C) 2014, GENIVI Alliance
+ *
+ * This file is part of Pulse Audio Interface Routing Plugin.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License (MPL), v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * For further information see http://www.genivi.org/.
+ *
+ * List of changes:
+ *
+ * 21.08.2014, Adrian Scarlat, First version of the code;
+ * Porting code from AM ver1.x to AM ver3.0;
+ * Added Copyright and License information;
+ */
+
+/* Includes */
+
+#include <pthread.h>
+#include "shared/CAmDltWrapper.h"
+#include "RoutingSenderPULSE.h"
+#include "RoutingSenderMainloopPULSE.h"
+
+static pthread_t *p_thread;
+static pa_mainloop *main_loop;
+
+/* struct used for ramp_volume changing */
+typedef struct ramp_volume
+{
+ uint32_t sink_input_index;
+ uint32_t volume_ini;
+ uint32_t volume_end;
+ /* max ramp time in ms */
+ uint16_t ramp_max_time;
+ /* current delay between calls in ms */
+ uint16_t ramp_crt_elapsed;
+ /* aux used to calculate delay between calls */
+ timespec start_time;
+};
+/* map used for storing ramp_volume information for sources */
+std::map<uint32_t, ramp_volume> g_sinkInputId2rampVolume;
+
+/* Defines */
+#define QUIT_REASON_PA_DOWN 1
+#define QUIT_REASON_AMGR_DOWN 2
+
+DLT_IMPORT_CONTEXT(routingPulse)
+
+bool routing_sender_create_mainloop(void *thiz)
+{
+ if (!thiz) {
+ logError("Can not create an working thread for Pulse Audio without a Routing plugin\n");
+ return false;
+ }
+ p_thread = (pthread_t *) malloc(sizeof(pthread_t));
+ pthread_create(p_thread, NULL, routing_sender_start_mainloop, thiz);
+ return true;
+}
+
+
+void* routing_sender_start_mainloop(void *thiz)
+{
+ pa_mainloop_api *mainloop_api;
+ pa_proplist *proplist;
+ pa_context *context;
+ int ret = 0;
+
+ if (!thiz) {
+ logError("Can not create an working thread for Pulse Audio without a Routing plugin\n");
+ return NULL;
+ }
+
+ proplist = pa_proplist_new();
+ if (pa_proplist_sets(proplist, PA_PROP_APPLICATION_NAME, "RoutingPULSE") < 0) {
+ logError("Can not prepare Pulse Audio main loop: pa_proplist_sets\n");
+ goto end;
+ }
+
+ if (!(main_loop = pa_mainloop_new()))
+ {
+ logError("Can not prepare Pulse Audio main loop: pa_mainloop_new\n");
+ goto end;
+ }
+
+ if (!(mainloop_api = pa_mainloop_get_api(main_loop)))
+ {
+ logError("Can not prepare Pulse Audio main loop: pa_mainloop_get_api\n");
+ goto end;
+ }
+
+ if (!(context = pa_context_new_with_proplist(mainloop_api, NULL, proplist)))
+ {
+ logError("Can not prepare Pulse Audio main loop: pa_context_new_with_proplist\n");
+ goto end;
+ }
+
+ pa_context_set_state_callback(context, routing_sender_context_state_callback, thiz);
+
+ if (pa_context_connect(context, NULL, pa_context_flags_t(0), NULL) < 0)
+ {
+ logError("Can not prepare Pulse Audio main loop: pa_context_new_with_proplist\n");
+ goto end;
+ }
+ ((RoutingSenderPULSE *) thiz)->setPAContext(context);
+
+ pa_mainloop_run(main_loop, &ret);
+
+end:
+ if (ret == QUIT_REASON_PA_DOWN)
+ {
+ ret = 0;
+ //mail loop ended
+ if (context)
+ {
+ pa_context_disconnect(context);
+ pa_context_unref(context);
+ context = NULL;
+ }
+
+ if (proplist)
+ {
+ pa_proplist_free(proplist);
+ proplist = NULL;
+ }
+
+ if (main_loop)
+ {
+ //pa_signal_done();
+ pa_signal_done();
+ pa_mainloop_free(main_loop);
+ main_loop = NULL;
+ }
+
+ //... pulse audio is down ... retry to connect
+ usleep(100000);
+ routing_sender_start_mainloop(thiz);
+ }
+ //TDOO: else if -check other value for "ret"
+ else
+ {
+ return thiz;
+ }
+}
+
+
+void routing_sender_pa_event_callback(pa_context *c, pa_subscription_event_type_t t, uint32_t idx, void *thiz)
+{
+ switch(t)
+ {
+ case PA_SUBSCRIPTION_EVENT_SINK_INPUT:
+ {
+ pa_context_get_sink_input_info(
+ c, idx, routing_sender_get_sink_input_info_callback, thiz);
+ break;
+ }
+ case PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT:
+ {
+ pa_context_get_sink_input_info(
+ c, idx, routing_sender_get_sink_input_info_callback, thiz);
+ break;
+ }
+ default:
+ {
+ logInfo("Pulse Audio event", t, "was ignored");
+ }
+ }
+}
+
+
+void routing_sender_get_sink_input_info_callback(pa_context *c, const pa_sink_input_info *i, int eol, void *userdata)
+{
+ (void) eol;
+
+ RoutingSenderPULSE* thiz = (RoutingSenderPULSE*) userdata;
+ if (!thiz)
+ {
+ logError("pa_context_get_sink_input_info was called with wrong params\n");
+ return;
+ }
+
+ /* init map of ramp_volumes, usefull later */
+ if (i != NULL && strcmp(i->name,"null") != 0)
+ {
+ ramp_volume source_ramp_volume;
+ source_ramp_volume.sink_input_index = i->index;
+ source_ramp_volume.ramp_crt_elapsed = 0;
+ g_sinkInputId2rampVolume.insert(std::make_pair(i->index, source_ramp_volume));
+ }
+
+ thiz->getSinkInputInfoCallback(c, i, userdata);
+}
+
+
+void routing_sender_get_source_output_info_callback(pa_context *c, const pa_source_output_info *i, int eol, void *userdata)
+{
+ (void) eol;
+
+ RoutingSenderPULSE* thiz = (RoutingSenderPULSE*) userdata;
+ if (!thiz)
+ {
+ logError("pa_context_get_surce_output_info was called with wrong params\n");
+ return;
+ }
+ thiz->getSourceOutputInfoCallback(c, i, userdata);
+}
+
+
+
+void routing_sender_get_sink_info_callback(pa_context *c, const pa_sink_info *i, int is_last, void *userdata)
+{
+ RoutingSenderPULSE* thiz = (RoutingSenderPULSE*) userdata;
+ if (!thiz)
+ {
+ logError("pa_context_get_sink_info was called with wrong params\n");
+ return;
+ }
+ thiz->getSinkInfoCallback(c, i, is_last, userdata);
+}
+
+
+void routing_sender_get_source_info_callback(pa_context *c, const pa_source_info *i, int is_last, void *userdata)
+{
+ RoutingSenderPULSE* thiz = (RoutingSenderPULSE*) userdata;
+ if (!thiz)
+ {
+ logError("pa_context_get_source_info was called with wrong params\n");
+ return;
+ }
+ thiz->getSourceInfoCallback(c, i, is_last, userdata);
+}
+
+
+void routing_sender_subscriber_callback(pa_context *c, int success, void *thiz) {
+ if (success)
+ {
+ pa_operation *o = pa_context_get_sink_info_list(c, routing_sender_get_sink_info_callback, thiz);
+ if (o)
+ {
+ pa_operation_unref(o);
+ }
+ else
+ {
+ logError("Unable to create Pulse Audio operation:",
+ "pa_context_get_sink_info_list");
+ }
+ }
+ else
+ {
+ logError("routing_sender_subscriber_callback: success = false");
+ }
+}
+
+
+void routing_sender_context_state_callback(pa_context *c, void *thiz)
+ {
+
+ if (pa_context_get_state(c) == PA_CONTEXT_FAILED)
+ {
+ pa_mainloop_quit(main_loop, QUIT_REASON_PA_DOWN);
+ }
+ if (pa_context_get_state(c) == PA_CONTEXT_READY)
+ {
+ //when context is ready - subscriber for server events (we are mainly interested in sink inputs & source outputs)
+ pa_context_set_subscribe_callback(c, routing_sender_pa_event_callback, thiz);
+ pa_operation *o = pa_context_subscribe(c, PA_SUBSCRIPTION_MASK_ALL, routing_sender_subscriber_callback, thiz);
+ if (o)
+ {
+ pa_operation_unref(o);
+ }
+ else
+ {
+ logError("Unable to create Pulse Audio operation:",
+ "pa_context_subscribe");
+ }
+ }
+ //other states are not relevant
+}
+
+
+bool routing_sender_get_source_info(pa_context *c, void *thiz) {
+ if (pa_context_get_state(c) == PA_CONTEXT_READY)
+ {
+ pa_operation *o = pa_context_get_source_info_list(c, routing_sender_get_source_info_callback, thiz);
+ if (o)
+ {
+ pa_operation_unref(o);
+ }
+ else
+ {
+ logError("Unable to create Pulse Audio operation:",
+ "pa_context_get_sink_info_list");
+ return false;
+ }
+ }
+ else
+ {
+ logError("Can not get Pulse Audio sources info - context not ready\n");
+ return false;
+ }
+
+ return true;
+}
+
+
+bool routing_sender_move_sink_input(pa_context *c, uint32_t sink_input_index, uint32_t sink_index, void *thiz)
+{
+ if (pa_context_get_state(c) == PA_CONTEXT_READY)
+ {
+ pa_operation *o = pa_context_move_sink_input_by_index(c, sink_input_index, sink_index, NULL, thiz);
+ if (o)
+ {
+ pa_operation_unref(o);
+ }
+ else
+ {
+ logError("Unable to create Pulse Audio operation:",
+ "pa_context_move_sink_input_by_index");
+ return false;
+ }
+ }
+ else
+ {
+ logError("Can not move sink input - context not ready\n");
+ return false;
+ }
+ return true;
+}
+
+
+bool routing_sender_move_source_output(pa_context *c, uint32_t source_output_index, uint32_t source_index, void *thiz)
+{
+ if (pa_context_get_state(c) == PA_CONTEXT_READY)
+ {
+ pa_operation *o = pa_context_move_source_output_by_index(c, source_output_index, source_index, NULL, thiz);
+ if (o)
+ {
+ pa_operation_unref(o);
+ }
+ else
+ {
+ logError("Unable to create Pulse Audio operation:",
+ "pa_context_set_sink_input_volume");
+ return false;
+ }
+ }
+ else
+ {
+ logError("Can not move source output - context not ready\n");
+ return false;
+ }
+ return true;
+}
+
+static inline uint16_t timespec2mili(const timespec & time)
+{
+ return (uint16_t)((time.tv_nsec == -1 && time.tv_sec == -1) ?
+ -1 :
+ time.tv_sec * 1000 + time.tv_nsec / 1000000);
+}
+
+/* Considers that time2 > time1 */
+static inline uint16_t timespec2DeltaMili(const timespec & time1, const timespec & time2)
+{
+ timespec l_deltaTime;
+ l_deltaTime.tv_sec = time2.tv_sec - time1.tv_sec;
+ l_deltaTime.tv_nsec = time2.tv_nsec - time1.tv_nsec;
+ return timespec2mili(l_deltaTime);
+}
+
+static void routing_sender_sink_input_volume_cb(pa_context *c, int success, void *data)
+{
+ logInfo("routing_sender_sink_input_volume_cb: success=", success, " data=", data);
+ if (success)
+ {
+ ramp_volume * l_ramp_volume = (ramp_volume *)data;
+ timespec l_endTime;
+ clock_gettime(0, &l_endTime);
+ l_ramp_volume->ramp_crt_elapsed = timespec2DeltaMili(l_ramp_volume->start_time, l_endTime);
+ if (l_ramp_volume->ramp_crt_elapsed >= l_ramp_volume->ramp_max_time)
+ {
+ return;
+ }
+
+ logInfo("routing_sender_sink_input_volume_cb: ms elapsed=", l_ramp_volume->ramp_crt_elapsed, " of ", l_ramp_volume->ramp_max_time);
+
+ /* ######## Calculate new volume with formula: ##########
+ crt_time x ( vol_end - vol_ini )
+ new_vol = vol_ini + ----------------------------------
+ max_time
+ ###################################################### */
+ uint32_t new_volume =
+ l_ramp_volume->volume_ini +
+ ( ( l_ramp_volume->ramp_crt_elapsed * ( l_ramp_volume->volume_end - l_ramp_volume->volume_ini ) ) / l_ramp_volume->ramp_max_time );
+ logInfo("routing_sender_sink_input_volume_cb: vol_ini=",l_ramp_volume->volume_ini,"vol_crt=",new_volume,"vol_end=",l_ramp_volume->volume_end);
+
+
+ /* ***** Set volume again ***** */
+ pa_cvolume *volumeCh = (pa_cvolume *) malloc(sizeof(pa_cvolume));
+ volumeCh->channels = 1;//TODO: check is stream is mono / stereo
+ volumeCh->values[0] = new_volume;
+
+ pa_operation *o = pa_context_set_sink_input_volume(c, l_ramp_volume->sink_input_index, volumeCh, NULL, NULL);
+ if (o)
+ {
+ pa_operation_unref(o);
+ }
+ else
+ {
+ logError("Unable to create Pulse Audio operation:",
+ "pa_context_set_sink_input_volume");
+ return ;
+ }
+ usleep(10000);
+
+ volumeCh->channels = 2;//TODO: check is stream is mono / stereo
+ volumeCh->values[0] = new_volume;
+ volumeCh->values[1] = new_volume;
+ logInfo("routing_sender_sink_input_volume_cb: will set vol=", new_volume);
+ o = pa_context_set_sink_input_volume(c, l_ramp_volume->sink_input_index, volumeCh, routing_sender_sink_input_volume_cb, l_ramp_volume);
+ if (o)
+ {
+ pa_operation_unref(o);
+ }
+ else
+ {
+ logError("Unable to create Pulse Audio operation:",
+ "pa_context_set_sink_input_volume");
+ return ;
+ }
+ }
+
+}
+
+bool routing_sender_sink_input_volume_ramp(pa_context *c, uint32_t sink_input_index, uint32_t crt_volume, uint32_t volume, uint16_t ramp_time, void *thiz)
+{
+ if (pa_context_get_state(c) == PA_CONTEXT_READY)
+ {
+ /* before everything, check to see if ramp_volume struct exists */
+ std::map<uint32_t, ramp_volume>::iterator iter = g_sinkInputId2rampVolume.find(sink_input_index);
+ std::map<uint32_t, ramp_volume>::iterator iterEnd = g_sinkInputId2rampVolume.end();
+ if (iter != iterEnd)
+ {
+ /* set test volume with only 1 unit more or less, just to see how callback responds */
+ pa_volume_t test_volume = ((crt_volume * MAX_PULSE_VOLUME) / 100);
+ test_volume += volume > crt_volume ? 1 : -1;
+
+ pa_cvolume *volumeCh = (pa_cvolume *) malloc(sizeof(pa_cvolume));
+ volumeCh->channels = 1;//TODO: check is stream is mono / stereo
+ volumeCh->values[0] = test_volume;
+
+ pa_operation *o = pa_context_set_sink_input_volume(c, sink_input_index, volumeCh, NULL, NULL);
+ if (o)
+ {
+ pa_operation_unref(o);
+ }
+ else
+ {
+ logError("Unable to create Pulse Audio operation:",
+ "pa_context_set_sink_input_volume");
+ return false;
+ }
+
+ volumeCh->channels = 2;//TODO: check is stream is mono / stereo
+ volumeCh->values[0] = test_volume;
+ volumeCh->values[1] = test_volume;
+
+ ramp_volume * l_ramp_volume = (ramp_volume *) &iter->second;
+ logInfo("routing_sender_sink_input_volume_ramp: searching ",sink_input_index,"found ramp_vlume struct with sinkInputId ",l_ramp_volume->sink_input_index," (should be equal) ");
+ l_ramp_volume->volume_ini = (crt_volume * MAX_PULSE_VOLUME) / 100;
+ l_ramp_volume->volume_end = (volume * MAX_PULSE_VOLUME) / 100;
+ l_ramp_volume->ramp_max_time = ramp_time;
+ clock_gettime(0, &l_ramp_volume->start_time);
+ l_ramp_volume->ramp_crt_elapsed = 0;
+ logInfo("routing_sender_sink_input_volume_ramp: will set vol=", test_volume);
+ o = pa_context_set_sink_input_volume(c, sink_input_index, volumeCh, routing_sender_sink_input_volume_cb, l_ramp_volume);
+ if (o)
+ {
+ pa_operation_unref(o);
+ }
+ else
+ {
+ logError("Unable to create Pulse Audio operation:",
+ "pa_context_set_sink_input_volume");
+ return false;
+ }
+ }
+ else
+ {
+ logInfo("routing_sender_sink_input_volume_ramp: didn't find struct with sinkInputId ", sink_input_index);
+ /* make-it the old traditional way */
+ return routing_sender_sink_input_volume(c, sink_input_index, volume, thiz);
+ }
+
+
+ }
+ else
+ {
+ logError("Can not set sink input volume - context not ready\n");
+ return false;
+ }
+ return true;
+}
+
+bool routing_sender_sink_input_volume(pa_context *c, uint32_t sink_input_index, uint32_t volume, void *thiz)
+{
+ if (pa_context_get_state(c) == PA_CONTEXT_READY)
+ {
+ pa_cvolume *volumeCh = (pa_cvolume *) malloc(sizeof(pa_cvolume));
+ volumeCh->channels = 1;//TODO: check is stream is mono / stereo
+ volumeCh->values[0] = (volume * MAX_PULSE_VOLUME) / 100;
+
+ pa_operation *o = pa_context_set_sink_input_volume(c, sink_input_index, volumeCh, NULL, thiz);
+ if (o)
+ {
+ pa_operation_unref(o);
+ }
+ else
+ {
+ logError("Unable to create Pulse Audio operation:",
+ "pa_context_set_sink_input_volume");
+ return false;
+ }
+
+ volumeCh->channels = 2;//TODO: check is stream is mono / stereo
+ volumeCh->values[0] = (volume * MAX_PULSE_VOLUME) / 100;
+ volumeCh->values[1] = (volume * MAX_PULSE_VOLUME) / 100;
+
+ o = pa_context_set_sink_input_volume(c, sink_input_index, volumeCh, NULL, NULL);
+ if (o)
+ {
+ pa_operation_unref(o);
+ }
+ else
+ {
+ logError("Unable to create Pulse Audio operation:",
+ "pa_context_set_sink_input_volume");
+ return false;
+ }
+ }
+ else
+ {
+ logError("Can not set sink input volume - context not ready\n");
+ return false;
+ }
+ return true;
+}
+
+
+bool routing_sender_sink_input_mute(pa_context *c, uint32_t sink_input_index, bool mute, void *thiz)
+{
+ if (pa_context_get_state(c) == PA_CONTEXT_READY)
+ {
+ pa_operation *o = pa_context_set_sink_input_mute(
+ c, sink_input_index, mute ? 1 : 0, NULL, thiz);
+ if (o)
+ {
+ pa_operation_unref(o);
+ }
+ else
+ {
+ logError("Unable to create Pulse Audio operation:",
+ "pa_context_set_sink_input_mute");
+ return false;
+ }
+ }
+ else
+ {
+ logError("Can not set sink input volume - context not ready\n");
+ return false;
+ }
+ return true;
+}
+
+
+bool routing_sender_sink_volume(pa_context *c, uint32_t sink_index, uint32_t volume, void *thiz)
+{
+ if (pa_context_get_state(c) == PA_CONTEXT_READY)
+ {
+ pa_cvolume *volumeCh = (pa_cvolume *) malloc(sizeof(pa_cvolume));
+ volumeCh->channels = 2;//TODO: check is stream is mono / stereo
+ volumeCh->values[0] = (volume * MAX_PULSE_VOLUME) / 100;
+ volumeCh->values[1] = (volume * MAX_PULSE_VOLUME) / 100;
+
+ pa_operation *o = pa_context_set_sink_volume_by_index(c, sink_index, volumeCh, NULL, thiz);
+ if (o)
+ {
+ pa_operation_unref(o);
+ }
+ else
+ {
+ logError("Unable to create Pulse Audio operation:",
+ "pa_context_set_sink_input_volume");
+ return false;
+ }
+ }
+ else
+ {
+ logError("Can not set sink input volume - context not ready\n");
+ return false;
+ }
+ return true;
+}
+
+//TODO - implements mute/un-mute and sink suspend(pause)
+//TODO - IMPORTANT !! implement volume change for sink input even multiple sink inputs are created during one connection
diff --git a/PluginRoutingInterfacePulse/src/RoutingSenderPULSE.cpp b/PluginRoutingInterfacePulse/src/RoutingSenderPULSE.cpp
new file mode 100644
index 0000000..17422e9
--- /dev/null
+++ b/PluginRoutingInterfacePulse/src/RoutingSenderPULSE.cpp
@@ -0,0 +1,914 @@
+/**
+ * SPDX license identifier: MPL-2.0
+ *
+ * Copyright (C) 2011-2014, Wind River Systems
+ * Copyright (C) 2014, GENIVI Alliance
+ *
+ * This file is part of Pulse Audio Interface Routing Plugin.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License (MPL), v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * For further information see http://www.genivi.org/.
+ *
+ * List of changes:
+ *
+ * 21.08.2014, Adrian Scarlat, First version of the code;
+ * Porting code from AM ver1.x to AM ver3.0;
+ * Added Copyright and License information;
+ *
+ *
+ * DESCRIPTION
+ *
+ * This module is handling requests form AudioManager daemon and redirect them to Pulse Audio server.
+ * It keeps track of existing audio sink, sources, sink-input, sources-input and performs connection/disconnection
+ * and other operations upon AudioManager daemon request.
+ *
+ * The modul is configured with a static list of sink and sources.
+ * Sinks are: audio output(e.g. speakers) or recording applications.
+ * Sources are: playback applications or audio input(e.g. microphone)
+ *
+ * In PulseAudio server those are classified as follows:
+ * - audio output - sinks: identified by name or properties: device.api (e.g = "alsa") & device.class(e.g. = "sound")
+ * - audio input - sources: identified by name or properties: device.api (e.g = "alsa") & device.class(e.g. = "sound")
+ * - playback applications - sink inputs: identified by application.name (e.g. "ALSA plug-in [chromium-browser]",
+ * - recording applications - source outputs: identified by application.name (e.g. "ALSA plug-in [chromium-browser]",
+ * application.process.user = "popai", application.process.binary = "chromium-browser")
+ *
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <iostream>
+
+#include "shared/CAmDltWrapper.h"
+
+#include "RoutingSenderPULSE.h"
+#include "RoutingSenderMainloopPULSE.h"
+
+
+
+#define LIBNAME "libPluginRoutingInterfacePULSE.so"
+#define CFGNAME "libPluginRoutingInterfacePULSE.conf"
+
+/* Globals */
+
+
+/* Defines */
+DLT_DECLARE_CONTEXT(routingPulse)
+/* Maximum source volume measured in percentage. Minimum value is 0% */
+#define MAX_SOURCE_VOLUME (100)
+
+
+/**
+ * Factory function for the plug-in to be used by audio manager daemon with dlopen & dlsym functions.
+ * Pattern "libraryName"FACTORY
+ *
+ * @author Ionut Popa (ionut.popa@windriver.com)
+ * @return an instance of RoutingSendInterface or type RoutingSenderPULSE.
+ */
+extern "C" IAmRoutingSend* PluginRoutingInterfacePULSEFactory()
+{
+
+ return (new RoutingSenderPULSE(NULL));
+}
+
+/**
+ * Destructor function for the plug-in to be used by audio manager daemon with dl_open & dl_sym functions.
+ *
+ * @param routingSendInterface - the instance created by PluginRoutingInterfaceDbusFactory
+ * @author Ionut Popa (ionut.popa@windriver.com)
+ */
+extern "C" void destroyPluginRoutingInterfacePULSE(IAmRoutingSend* routingSendInterface)
+{
+ delete routingSendInterface;//virtual destructor -> our constructor will be called too
+}
+
+
+/**
+ * Constructor.
+ * @param p_paContext - reference to PulseAudio context
+ */
+RoutingSenderPULSE::RoutingSenderPULSE(pa_context *p_paContext)
+{
+ this->m_paSinkNullIndex = -1;
+ this->m_paSourceNullIndex = -1;
+ this->m_paContext = p_paContext;
+}
+
+
+void RoutingSenderPULSE::loadConfig()
+{
+ //get current library path - search: /proc/< getpid() >/maps
+ char proc_maps_file_name[256];
+ char line[256];
+ char lib_name[256];
+ char *tmp;
+ pid_t pid = getpid();
+ snprintf(proc_maps_file_name, 256, "/proc/%d/maps", pid);
+ FILE *proc_maps = fopen(proc_maps_file_name, "r");
+
+ while (!feof(proc_maps))
+ {
+ char *cnt = fgets(line, 256, proc_maps);
+ if (strlen(line) == 0 || line[0] == '#')
+ {
+ continue;
+ }
+ if (cnt == NULL) continue;
+ //tmp0 tmp1 tmp2 tmp3 tmp4 lib_name);
+ tmp = strtok(line, " ");//address-interval
+ if(tmp == NULL) continue;
+
+ tmp = strtok(NULL, " ");//rights
+ if(tmp == NULL) continue;
+
+ tmp = strtok(NULL, " ");//offset
+ if(tmp == NULL) continue;
+
+ strtok(NULL, " ");//dev
+ if(tmp == NULL) continue;
+
+ tmp = strtok(NULL, " \n");//inode
+ if(tmp == NULL) continue;
+
+ tmp = strtok(NULL, " \n");
+ if(tmp == NULL) continue;
+
+ strcpy(lib_name, tmp);
+ if ((lib_name != NULL) && (strstr(lib_name, LIBNAME) >= lib_name))
+ {
+ strcpy(strrchr(lib_name, '/') + 1, CFGNAME);
+ logInfo("PULSE - config file name: %s\n", lib_name);
+
+ FILE *config = fopen(lib_name, "r");
+
+ while (config && !feof(config))
+ {
+ char *cnt = fgets(line, 256, config);
+ if (!line || strlen(line) == 0) continue;
+ //config format line: TYPE|PULSE TYPE|NAME|CLASS|PROPERTY_NAME|PROPERTY_VALUE
+ //TYPE="Source" or "Sink"
+
+ char *tmp = strtok(line, "|");//type
+ if (strcmp("Sink", tmp) == 0)
+ {
+ //add sink config
+ RoutingSenderPULSESourceSinkConfig sinkConfig;
+
+ tmp = strtok(NULL, "|");//pulse type - not used for the moment
+
+ tmp = strtok(NULL, "|");//class
+ sinkConfig.clazz = std::string(tmp);
+
+ tmp = strtok(NULL, "|");//name
+ sinkConfig.name = std::string(tmp);
+
+ tmp = strtok(NULL, "|");//property name
+ sinkConfig.propertyName = std::string(tmp);
+
+ tmp = strtok(NULL, "|\n");//property value
+ sinkConfig.propertyValue = std::string(tmp);
+
+ m_sinks.push_back(sinkConfig);
+ logInfo("sinkConfig: sinkConfig.clazz=", sinkConfig.clazz, " sinkConfig.name=", sinkConfig.name, " sinkConfig.propertyName=", sinkConfig.propertyName, " sinkConfig.propertyValue=", sinkConfig.propertyValue);
+ }
+ if (strcmp("Source", tmp) == 0)
+ {
+ //add source config
+ RoutingSenderPULSESourceSinkConfig sourceConfig;
+
+ tmp = strtok(NULL, "|");//pulse type - not used for the moment
+
+ tmp = strtok(NULL, "|");//class
+ sourceConfig.clazz = std::string(tmp);
+
+ tmp = strtok(NULL, "|");//name
+ sourceConfig.name = std::string(tmp);
+
+ tmp = strtok(NULL, "|");//property name
+ sourceConfig.propertyName = std::string(tmp);
+
+ tmp = strtok(NULL, "|\n");//property value
+ sourceConfig.propertyValue = std::string(tmp);
+
+ m_sources.push_back(sourceConfig);
+ logInfo("sourceConfig: sourceConfig.clazz=", sourceConfig.clazz, " sourceConfig.name=", sourceConfig.name, " sourceConfig.propertyName=", sourceConfig.propertyName, " sourceConfig.propertyValue=", sourceConfig.propertyValue);
+ }
+ }
+
+ if (config)
+ fclose(config);
+ break;
+ }
+ }
+
+ fclose(proc_maps);
+}
+
+
+/**
+ * Destructor.
+ */
+RoutingSenderPULSE::~RoutingSenderPULSE()
+{
+ //TODO: Disconnect from pulse: quit main loop and free the context and stuff
+}
+
+/**
+ * Connecting sender & receiver
+ * @author Ionut Popa (ionut.popa@windriver.com)
+ */
+am_Error_e RoutingSenderPULSE::startupInterface(am::IAmRoutingReceive *p_routingReceiver)
+{
+ this->m_routingReceiver = p_routingReceiver;
+ return am::E_OK;
+}
+
+void RoutingSenderPULSE::setRoutingReady(uint16_t handle)
+{
+ //TODO: do not register sinks with the same name
+
+ int i;
+ this->loadConfig();
+ //first register Domain = PulseAudio
+ this->m_domain.name = "PulseAudio";
+ this->returnBusName(this->m_domain.busname);//set domain bus name = current interface bus name
+ this->m_domain.nodename = "PulseAudio";
+ this->m_domain.early = false;
+ this->m_domain.complete = true;
+ this->m_domain.state = am::DS_CONTROLLED;
+
+ this->m_domain.domainID = 0;
+ this->m_routingReceiver->registerDomain(this->m_domain, this->m_domain.domainID);
+
+ am_SoundProperty_s l_spTreble;
+ l_spTreble.type = SP_GENIVI_BASS;
+ l_spTreble.value = 0;
+
+ am_SoundProperty_s l_spMid;
+ l_spMid.type = SP_GENIVI_MID;
+ l_spMid.value = 0;
+
+ am_SoundProperty_s l_spBass;
+ l_spBass.type = SP_GENIVI_BASS;
+ l_spBass.value = 0;
+
+ //register sources (sink inputs & sinks)
+ for (i = 0; i < m_sources.size(); i++)
+ {
+ am_sourceID_t l_newSourceID = 0;
+ this->m_sources[i].source.sourceID = l_newSourceID;
+ this->m_sources[i].source.name = m_sources[i].name;
+ this->m_sources[i].source.sourceState = am::SS_ON;
+ this->m_sources[i].source.domainID = this->m_domain.domainID;
+ this->m_sources[i].source.visible = true;
+ this->m_sources[i].source.volume = MAX_SOURCE_VOLUME; /* initialize source volume to 100% */
+
+ this->m_sources[i].source.listConnectionFormats.push_back(am::CF_GENIVI_STEREO);
+ this->m_routingReceiver->peekSourceClassID(
+ this->m_sources[i].clazz,
+ this->m_sources[i].source.sourceClassID);
+
+ this->m_routingReceiver->registerSource(this->m_sources[i].source, l_newSourceID);
+
+ this->m_sources[i].source.sourceID = l_newSourceID;
+ m_sourceToPASinkInput[l_newSourceID] = -1;
+ m_sourceToPASource[l_newSourceID] = -1;
+
+ logInfo("PULSE - register source:"
+ ,m_sources[i].name
+ , "(", m_sources[i].propertyName , ", ", m_sources[i].propertyValue, ")");
+ m_sourceToVolume[l_newSourceID] = MAX_SOURCE_VOLUME;//initially all the sources are at 100%
+ }
+
+ //register sinks (source outputs & sources)
+ for (i = 0; i < m_sinks.size(); i++)
+ {
+ am_sinkID_t l_newsinkID = 0;
+ this->m_sinks[i].sink.sinkID = l_newsinkID;
+ this->m_sinks[i].sink.name = this->m_sinks[i].name;
+ this->m_sinks[i].sink.muteState = am::MS_MUTED;
+ this->m_sinks[i].sink.domainID = this->m_domain.domainID;
+ this->m_sinks[i].sink.visible = true;
+
+ this->m_sinks[i].sink.listSoundProperties.push_back(l_spTreble);
+ this->m_sinks[i].sink.listSoundProperties.push_back(l_spMid);
+ this->m_sinks[i].sink.listSoundProperties.push_back(l_spBass);
+ this->m_sinks[i].sink.listConnectionFormats.push_back(am::CF_GENIVI_STEREO);
+
+ this->m_routingReceiver->peekSinkClassID(
+ this->m_sinks[i].clazz,
+ this->m_sinks[i].sink.sinkClassID);
+ this->m_routingReceiver->registerSink(this->m_sinks[i].sink, l_newsinkID);
+ this->m_sinks[i].sink.sinkID = l_newsinkID;
+ m_sinkToPASourceOutput[l_newsinkID] = -1;
+ m_sinkToPASink[l_newsinkID] = -1;
+
+ logInfo("PULSE - register sink:"
+ ,m_sinks[i].name
+ , "(", m_sinks[i].propertyName , ", ", m_sinks[i].propertyValue, ")");
+ }
+
+ logInfo("PULSE - routingInterfacesReady");
+ this->m_routingReceiver->confirmRoutingReady(handle, am::E_OK);
+
+ //register pulse sink & sources, sink inputs & source outputs - > start the main PA loop
+ routing_sender_create_mainloop((void *) this);
+}
+
+void RoutingSenderPULSE::setRoutingRundown(uint16_t handle)
+{
+ this->m_routingReceiver->confirmRoutingRundown(handle, am::E_OK);
+ //TODO: implement this
+}
+
+am_Error_e RoutingSenderPULSE::asyncAbort(const am_Handle_s handle)
+{
+ (void) handle;
+ return E_NOT_USED;
+}
+
+
+am_Error_e RoutingSenderPULSE::asyncConnect(
+ const am_Handle_s handle,
+ const am_connectionID_t connectionID,
+ const am_sourceID_t sourceID,
+ const am_sinkID_t sinkID,
+ const am_CustomConnectionFormat_t connectionFormat)
+{
+ //TODO: check stuff like connectionFormat
+ logInfo("PULSE - asyncConnect() - start");
+ //add source,sink & connectionID to a list of connections maintained by Routing Pulse Engine
+ RoutingSenderPULSEConnection l_newConnection;
+ l_newConnection.sinkID = sinkID;
+ l_newConnection.sourceID = sourceID;
+ l_newConnection.connectionID = connectionID;
+ l_newConnection.handle = handle;
+
+ //by default - sources ar connected at 100% -> controller is responsible to setSourcevolume if needed
+
+ m_sourceToVolume[sourceID] = MAX_SOURCE_VOLUME;
+
+
+ if (m_sinkToPASink[sinkID] != -1)
+ {
+ if (m_sourceToPASinkInput[sourceID] != -1)
+ {
+ if (routing_sender_move_sink_input(
+ this->m_paContext,
+ m_sourceToPASinkInput[sourceID],
+ m_sinkToPASink[sinkID],
+ this))
+ {
+ //TODO: add callback for pulse move sink input -> to send confirmation; for the moment directly send confirmation
+
+ logInfo("PULSE - asyncConnect() - connectionID:", connectionID,
+ "move sinkInputIndex:", m_sourceToPASinkInput[sourceID], "to sinkIndex:", m_sinkToPASink[sinkID]);
+ }
+ else
+ {
+ this->m_routingReceiver->ackConnect(handle, connectionID, am::E_NOT_POSSIBLE);
+ return am::E_NOT_POSSIBLE;
+ }
+ }//else move_sink_input will be called later
+ }
+ else if (m_sourceToPASource[sourceID] != -1)
+ {
+ if (m_sinkToPASourceOutput[sinkID] != -1)
+ {
+ if (routing_sender_move_source_output(
+ this->m_paContext,
+ m_sinkToPASourceOutput[sinkID],
+ m_sourceToPASource[sourceID],
+ this))
+ {
+ //TODO: add callback for pulse move sink input -> to send confirmation; for the moment directly send confirmation
+
+ logInfo("PULSE - asyncConnect() - connectionID:", connectionID,
+ "move sourceOutputIndex:", m_sinkToPASourceOutput[sinkID], "to sourceIndex:", m_sourceToPASource[sourceID]);
+
+ }
+ else
+ {
+ this->m_routingReceiver->ackConnect(handle, connectionID, am::E_NOT_POSSIBLE);
+ return am::E_NOT_POSSIBLE;
+ }
+ }//else move_sink_input will be called later
+ }
+ else
+ {
+ logError("Sink and source for connection not identified:",
+ sinkID, sourceID, connectionID);
+ return am::E_NOT_POSSIBLE;
+ }
+
+ m_activeConnections.push_back(l_newConnection);
+
+ this->m_routingReceiver->ackConnect(handle, connectionID, am::E_OK);
+
+/**
+ * TODO: connection is always possible ? check that
+*/
+
+ return am::E_OK;
+}
+
+
+am_Error_e RoutingSenderPULSE::asyncDisconnect(const am_Handle_s handle, const am_connectionID_t connectionID)
+{
+ //get connection by ID ... not to many connections, therefore linear search is fast enough
+ std::vector<RoutingSenderPULSEConnection>::iterator iter = m_activeConnections.begin();
+ std::vector<RoutingSenderPULSEConnection>::iterator iterEnd = m_activeConnections.end();
+ for (; iter < iterEnd; ++iter)
+ {
+ if (iter->connectionID == connectionID)
+ {
+ if (m_sourceToPASinkInput[iter->sourceID] != -1)
+ {
+ if (this->m_paSinkNullIndex >= 0)
+ {
+ //if null sink is defined - disconnect = move sink input to null
+ logInfo("PULSE - asyncDisconnect() - connection found - move sinkInputIndex:",
+ m_sourceToPASinkInput[iter->sourceID], "to NULL sinkIndex:", this->m_paSinkNullIndex);
+
+ routing_sender_move_sink_input(
+ this->m_paContext,
+ this->m_sourceToPASinkInput[iter->sourceID],
+ this->m_paSinkNullIndex,
+ this);
+ //TODO: add callback for pulse move sink input -> to send confirmation; for the moment directly send confirmation
+ this->m_routingReceiver->ackDisconnect(handle, connectionID, am::E_OK);
+
+ }
+ }
+ else if (m_sinkToPASourceOutput[iter->sinkID] != -1)
+ {
+ if (this->m_paSourceNullIndex >= 0)
+ {
+ //if null source is defined - disconnect = move source output to null
+ logInfo("PULSE - asyncDisconnect() - connection found - move sourceOutputIndex:",
+ m_sinkToPASourceOutput[iter->sinkID], "to NULL sourceIndex:", this->m_paSourceNullIndex);
+
+ routing_sender_move_source_output(
+ this->m_paContext,
+ m_sourceToPASinkInput[iter->sourceID],
+ this->m_paSinkNullIndex,
+ this);
+ //TODO: add callback for pulse move sink input -> to send confirmation; for the moment directly send confirmation
+ this->m_routingReceiver->ackDisconnect(handle, connectionID, am::E_OK);
+
+ //remove connection from the list of active connections
+ iter = m_activeConnections.erase(iter);
+
+ break;
+ }
+ }
+ else
+ {
+ logInfo("PULSE - asyncDisconnect() - connection found - but no sink input or source");
+ this->m_routingReceiver->ackDisconnect(handle, connectionID, am::E_OK);
+ }
+ //remove connection from the list of active connections
+ iter = m_activeConnections.erase(iter);
+
+ break;
+ }
+ }
+ return am::E_OK;
+}
+
+am_Error_e RoutingSenderPULSE::asyncSetSinkVolume(
+ const am_Handle_s handle,
+ const am_sinkID_t sinkID,
+ const am_volume_t volume,
+ const am_CustomRampType_t ramp,
+ const am_time_t time)
+{
+ (void) ramp;
+ (void) time;
+
+ logInfo("PULSE - asyncSetSinkVolume() - volume:", volume, "sink index:", this->m_sinkToPASink[sinkID]);
+
+ routing_sender_sink_volume(
+ this->m_paContext,
+ this->m_sinkToPASink[sinkID],
+ volume,
+ this);
+ this->m_routingReceiver->ackSetSinkVolumeChange(handle, volume, E_OK);
+ return E_OK;
+}
+
+am_Error_e RoutingSenderPULSE::asyncSetSourceVolume(
+ const am_Handle_s handle,
+ const am_sourceID_t sourceID,
+ const am_volume_t volume,
+ const am_CustomRampType_t ramp,
+ const am_time_t time)
+{
+ (void) ramp;
+ (void) time;
+
+ am_volume_t crt_volume = this->m_sourceToVolume[sourceID];
+ this->m_sourceToVolume[sourceID] = volume;
+
+ logInfo("PULSE - asyncSetSourceVolume() - volume:", volume, "sink input index:", this->m_sourceToPASinkInput[sourceID]);
+ if (m_sourceToPASinkInput[sourceID] != -1)
+ {
+ if (time == 0)
+ {/* without ramp time */
+ routing_sender_sink_input_volume(
+ this->m_paContext,
+ this->m_sourceToPASinkInput[sourceID],
+ volume,
+ this);
+ }
+ else
+ {/* with ramp time */
+ routing_sender_sink_input_volume_ramp(
+ this->m_paContext,
+ this->m_sourceToPASinkInput[sourceID],
+ crt_volume,
+ volume,
+ (uint16_t)time,
+ this);
+ }
+ }
+ else
+ {
+ logInfo("PULSE - sink input not registered yet - should wait for registration before update the volume");
+ }
+ this->m_routingReceiver->ackSetSourceVolumeChange(handle, volume, E_OK);
+
+}
+
+am_Error_e RoutingSenderPULSE::asyncSetSourceState(
+ const am_Handle_s handle,
+ const am_sourceID_t sourceID,
+ const am_SourceState_e state)
+{
+ logInfo("PULSE - asyncSetSourceState", state);
+ switch (state)
+ {
+ case SS_ON:
+ {
+ routing_sender_sink_input_mute(
+ this->m_paContext,
+ this->m_sourceToPASinkInput[sourceID],
+ false,
+ this
+ );
+ break;
+ }
+ case SS_OFF:
+ case SS_PAUSED:
+ {
+ //TODO: mute source in case of PAUSE or OFF - is there a better way to pause ? maybe suspending the associated sink?
+ routing_sender_sink_input_mute(
+ this->m_paContext,
+ this->m_sourceToPASinkInput[sourceID],
+ true,
+ this
+ );
+ break;
+ }
+ default:
+ {
+ logError("RoutingSenderPULSE::asyncSetSourceState - wrong source state\n");
+ this->m_routingReceiver->ackSetSourceState(handle, E_NOT_POSSIBLE);
+ return E_NOT_POSSIBLE;
+ }
+ }
+ this->m_routingReceiver->ackSetSourceState(handle, E_OK);
+ return E_OK;
+}
+
+am_Error_e RoutingSenderPULSE::asyncSetSinkSoundProperties(
+ const am_Handle_s handle,
+ const am_sinkID_t sinkID,
+ const std::vector<am_SoundProperty_s>& listSoundProperties)
+{
+ (void) handle;
+ (void) sinkID;
+ (void) listSoundProperties;
+ return E_NOT_USED;
+}
+
+am_Error_e RoutingSenderPULSE::asyncSetSinkSoundProperty(
+ const am_Handle_s handle,
+ const am_sinkID_t sinkID,
+ const am_SoundProperty_s& soundProperty)
+{
+ (void) handle;
+ (void) sinkID;
+ (void) soundProperty;
+ return E_NOT_USED;
+}
+
+am_Error_e RoutingSenderPULSE::asyncSetSourceSoundProperties(
+ const am_Handle_s handle,
+ const am_sourceID_t sourceID,
+ const std::vector<am_SoundProperty_s>& listSoundProperties)
+{
+ (void) handle;
+ (void) sourceID;
+ (void) listSoundProperties;
+ return E_NOT_USED;
+}
+
+am_Error_e RoutingSenderPULSE::asyncSetSourceSoundProperty(
+ const am_Handle_s handle,
+ const am_sourceID_t sourceID,
+ const am_SoundProperty_s& soundProperty)
+{
+ (void) handle;
+ (void) sourceID;
+ (void) soundProperty;
+ return E_NOT_USED;
+}
+
+am_Error_e RoutingSenderPULSE::asyncCrossFade(
+ const am_Handle_s handle,
+ const am_crossfaderID_t crossfaderID,
+ const am_HotSink_e hotSink,
+ const am_CustomRampType_t rampType,
+ const am_time_t time)
+{
+ (void) handle;
+ (void) crossfaderID;
+ (void) hotSink;
+ (void) rampType;
+ (void) time;
+ return E_NOT_USED;
+}
+
+am_Error_e RoutingSenderPULSE::setDomainState(
+ const am_domainID_t domainID,
+ const am_DomainState_e domainState)
+{
+ (void) domainID;
+ (void) domainState;
+ return E_NOT_USED;
+}
+
+am_Error_e RoutingSenderPULSE::returnBusName(std::string& BusName) const {
+ BusName = "RoutingPULSE";
+ return E_OK;
+}
+
+void RoutingSenderPULSE::getInterfaceVersion(std::string& out_ver) const
+{
+ out_ver = "3.0";
+}
+
+am_Error_e RoutingSenderPULSE::asyncSetVolumes(const am_Handle_s handle, const std::vector<am_Volumes_s>& listVolumes)
+{
+ (void) handle;
+ (void) listVolumes;
+ //todo: implement asyncSetVolumes;
+ return (E_NOT_USED);
+}
+
+am_Error_e RoutingSenderPULSE::asyncSetSinkNotificationConfiguration(const am_Handle_s handle, const am_sinkID_t sinkID, const am_NotificationConfiguration_s& notificationConfiguration)
+{
+ (void) handle;
+ (void) sinkID;
+ (void) notificationConfiguration;
+ //todo: implement asyncSetSinkNotificationConfiguration;
+ return (E_NOT_USED);
+}
+
+am_Error_e RoutingSenderPULSE::asyncSetSourceNotificationConfiguration(const am_Handle_s handle, const am_sourceID_t sourceID, const am_NotificationConfiguration_s& notificationConfiguration)
+{
+ (void) handle;
+ (void) sourceID;
+ (void) notificationConfiguration;
+ //todo: implement asyncSetSourceNotificationConfiguration;
+ return (E_NOT_USED);
+}
+/*******************************************************************************
+ * Private methods
+ ******************************************************************************/
+
+
+void RoutingSenderPULSE::getSinkInputInfoCallback(pa_context *c, const pa_sink_input_info *i, void *userdata)
+{
+ if (i == NULL)
+ {
+ return;
+ }
+
+ //search for corresponding Source
+ std::vector<RoutingSenderPULSESourceSinkConfig>::iterator iter = m_sources.begin();
+ std::vector<RoutingSenderPULSESourceSinkConfig>::iterator iterEnd = m_sources.end();
+ for (; iter < iterEnd; ++iter)
+ {
+ //try to match source PulseAudio properties against config properties
+ const char *property_value = pa_proplist_gets(i->proplist, iter->propertyName.c_str());
+
+ if (property_value &&
+ ( std::string::npos != std::string(property_value).find(iter->propertyValue) ||
+ std::string::npos != iter->propertyValue.find(property_value)) )
+ {
+ logInfo("PULSE - sink input registered:"
+ , " sinkInputIndex:", i->index, "sourceID:", iter->source.sourceID);
+
+ logInfo("PULSE - sink input details:"
+ , " prop_val: ", property_value, " iter->prop_val: ", iter->propertyValue);
+
+ m_sourceToPASinkInput[iter->source.sourceID] = i->index;
+
+ //iterate pending connection request
+ // -> if there is a connection pending such that sink input "i" matches source from Connect() - create the connection in pulse
+ std::vector<RoutingSenderPULSEConnection>::iterator iterConn = m_activeConnections.begin();
+ std::vector<RoutingSenderPULSEConnection>::iterator iterConnEnd = m_activeConnections.end();
+ for (; iterConn < iterConnEnd; ++iterConn)
+ {
+ if (iterConn->sourceID == iter->source.sourceID)
+ {
+ logInfo("PULSE - asyncConnect() - connectionID:", iterConn->connectionID,
+ "move sinkInputIndex:", m_sourceToPASinkInput[iterConn->sourceID], "to sinkIndex:", m_sinkToPASink[iterConn->sinkID]);
+
+ routing_sender_move_sink_input(
+ this->m_paContext,
+ m_sourceToPASinkInput[iterConn->sourceID],
+ m_sinkToPASink[iterConn->sinkID],
+ this);
+
+ //TODO: add callback for pulse move sink input -> to send confirmation; for the moment directly send confirmation
+ this->m_routingReceiver->ackConnect(iterConn->handle, iterConn->connectionID, am::E_OK);
+ }
+ }
+ //check of controller already requested vol adjustment for this source
+ bool requiresVolUpdate = false;
+ for (int j = 0; j < i->volume.channels; j++)
+ {
+ if ((i->volume.values[j]*MAX_SOURCE_VOLUME / MAX_PULSE_VOLUME) != m_sourceToVolume[iter->source.sourceID])
+ {
+ requiresVolUpdate = true;
+ logInfo("PULSE - sink registerd with vol:", (i->volume.values[j]*MAX_SOURCE_VOLUME / MAX_PULSE_VOLUME),
+ "; should be changed to:",
+ m_sourceToVolume[iter->source.sourceID]);
+ break;
+ }
+ }
+ if (requiresVolUpdate)
+ {
+ routing_sender_sink_input_volume(
+ this->m_paContext,
+ m_sourceToPASinkInput[iter->source.sourceID],
+ m_sourceToVolume[iter->source.sourceID],
+ this);
+ }
+ //TODO: check mute state was requested by controller.
+ break;
+ }
+ }
+}
+
+
+void RoutingSenderPULSE::getSourceOutputInfoCallback(pa_context *c, const pa_source_output_info *i, void *userdata)
+{
+ if (i == NULL)
+ {
+ return;
+ }
+
+ //search for corresponding Source
+ std::vector<RoutingSenderPULSESourceSinkConfig>::iterator iter = m_sinks.begin();
+ std::vector<RoutingSenderPULSESourceSinkConfig>::iterator iterEnd = m_sinks.end();
+ for (; iter < iterEnd; ++iter)
+ {
+ //try to match source PulseAudio properties agains config properties
+ const char *property_value = pa_proplist_gets(i->proplist, iter->propertyName.c_str());
+
+ if (property_value &&
+ ( std::string::npos != std::string(property_value).find(iter->propertyValue) ||
+ std::string::npos != iter->propertyValue.find(property_value)) )
+ {
+ logInfo("PULSE - source output registered:"
+ , " sourceOutputIndex:", i->index, "sinkID:", iter->sink.sinkID);
+
+ m_sinkToPASourceOutput[iter->sink.sinkID] = i->index;
+
+ //iterate pending connection request
+ // -> if there is a connection pending such that sink input "i" matches source from Connect() - create the connection in pulse
+ std::vector<RoutingSenderPULSEConnection>::iterator iterConn = m_activeConnections.begin();
+ std::vector<RoutingSenderPULSEConnection>::iterator iterConnEnd = m_activeConnections.end();
+ for (; iterConn < iterConnEnd; ++iterConn)
+ {
+ if (iterConn->sinkID == iter->sink.sinkID)
+ {
+ logInfo("PULSE - asyncConnect() - connectionID:", iterConn->connectionID,
+ "move sourceOutputIndex:", m_sinkToPASourceOutput[iterConn->sinkID], "to sourceIndex:", m_sourceToPASource[iterConn->sourceID]);
+
+ routing_sender_move_source_output(
+ this->m_paContext,
+ m_sinkToPASourceOutput[iterConn->sinkID],
+ m_sourceToPASource[iterConn->sourceID],
+ this);
+
+ //TODO: add callback for pulse move source output -> to send confirmation; for the moment directly send confirmation
+ this->m_routingReceiver->ackConnect(iterConn->handle, iterConn->connectionID, am::E_OK);
+ }
+ }
+
+ break;
+ }
+ }
+}
+
+
+void RoutingSenderPULSE::getSinkInfoCallback(pa_context *c, const pa_sink_info *i, int is_last, void *userdata)
+{
+ if (i != NULL)
+ {
+ if (strcmp("null", i->name) == 0)
+ {
+ this->m_paSinkNullIndex = i->index;
+ }
+
+ //search for corresponding (already registered) Sink
+ std::vector<RoutingSenderPULSESourceSinkConfig>::iterator iter = m_sinks.begin();
+ std::vector<RoutingSenderPULSESourceSinkConfig>::iterator iterEnd = m_sinks.end();
+ for (; iter < iterEnd; ++iter)
+ {
+ //first try to match the sink name from pulse audio sink name
+ if (iter->sink.name == std::string(i->name))
+ {
+ logInfo("PULSE sink name PA:", i->name, "config name:" ,iter->sink.name);
+ logInfo("PULSE - PA sink:", i->index,
+ "corresponding to AMGR sink:", iter->sink.sinkID, " - found");
+ m_sinkToPASink[iter->sink.sinkID] = i->index;
+ }
+ else
+ {
+ //try to match sink PulseAudio properties against config properties
+ const char *property_value = pa_proplist_gets(i->proplist, iter->propertyName.c_str());
+
+ if (!property_value) continue;
+
+ if (std::string::npos != iter->propertyValue.find(property_value))
+ {
+ logInfo("PULSE - PA sink:", i->index,
+ "corresponding to AMGR sink:", iter->sink.sinkID, " - found");
+
+ m_sinkToPASink[iter->sink.sinkID] = i->index;
+ }
+ }
+ }
+ }
+ else if (is_last)
+ {
+ routing_sender_get_source_info(this->m_paContext, this);
+ logInfo("PULSE - PA sinks registration completed");
+ }
+}
+
+void RoutingSenderPULSE::getSourceInfoCallback(pa_context *c, const pa_source_info *i, int is_last, void *userdata)
+{
+ if (i != NULL)
+ {
+ if (strcmp("null", i->name) == 0)
+ {
+ this->m_paSourceNullIndex = i->index;
+ }
+
+ //search for corresponding (already registered) Source
+ std::vector<RoutingSenderPULSESourceSinkConfig>::iterator iter = m_sources.begin();
+ std::vector<RoutingSenderPULSESourceSinkConfig>::iterator iterEnd = m_sources.end();
+ for (; iter < iterEnd; ++iter)
+ {
+ //first try to match the sink name from pulse audio sink name
+ if (iter->sink.name == std::string(i->name))
+ {
+ logInfo("PULSE - PA source:", i->index,
+ "corresponding to AMGR source:", iter->source.sourceID, " - found");
+ m_sourceToPASource[iter->source.sourceID] = i->index;
+ }
+ else
+ {
+ //try to match source PulseAudio properties against config properties
+ const char *property_value = pa_proplist_gets(i->proplist, iter->propertyName.c_str());
+
+ if (!property_value) continue;
+
+ if (std::string::npos != iter->propertyValue.find(property_value))
+ {
+ logInfo("PULSE - PA source:", i->index,
+ "corresponding to AMGR source:", iter->source.sourceID, " - found");
+
+ m_sourceToPASource[iter->source.sourceID] = i->index;
+ }
+ }
+
+ }
+ }
+ else if (is_last)
+ {
+ this->m_routingReceiver->hookDomainRegistrationComplete(this->m_domain.domainID);
+ logInfo("PULSE - PA sinks and source registration completed");
+ //TODO: - search for existing sink inputs & sources outputs
+ }
+}