summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-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
+ }
+}