diff options
3 files changed, 4907 insertions, 0 deletions
diff --git a/meta-ivi-demo/recipes-multimedia/audiomanager/audiomanager/0001-Porting-Pulse-Control-Interface-from-AM-v1.x-to-AM-v.patch b/meta-ivi-demo/recipes-multimedia/audiomanager/audiomanager/0001-Porting-Pulse-Control-Interface-from-AM-v1.x-to-AM-v.patch new file mode 100644 index 0000000..bcf043d --- /dev/null +++ b/meta-ivi-demo/recipes-multimedia/audiomanager/audiomanager/0001-Porting-Pulse-Control-Interface-from-AM-v1.x-to-AM-v.patch @@ -0,0 +1,2555 @@ +From 3e41213883357530c83c96f2eef2d0614056d497 Mon Sep 17 00:00:00 2001 +From: Adrian Scarlat <adrian.scarlat@windriver.com> +Date: Mon, 8 Sep 2014 16:51:55 +0300 +Subject: [PATCH] Porting Pulse Control Interface from AM v1.x to AM v3.0 + +This Control Interface is the "heart" of the AM; It is needed +by 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_CONTROL_PLUGIN=ON; +There is one configuration file that is used at the moment by the Control Interface: + 1. libPluginControlInterface.conf - configuration file for defining the Sources, + Source Classes, Sinks, Sink Classes, Gateways, Routes and Mixing rules form them; + +Modified files: + CMakeLists.txt + +Added new folders: + PluginControlInterfacePulse/ + +Added new files: + PluginControlInterfacePulse/CMakeLists.txt + PluginControlInterfacePulse/data/libPluginControlInterface.conf + PluginControlInterfacePulse/include/ControlConfig.h + PluginControlInterfacePulse/include/ControlSender.h + PluginControlInterfacePulse/src/ControlSender.cpp + +Signed-off-by: Adrian Scarlat <adrian.scarlat@windriver.com> +--- + CMakeLists.txt | 7 + + PluginControlInterfacePulse/CMakeLists.txt | 93 ++ + .../data/libPluginControlInterface.conf | 93 ++ + .../include/ControlConfig.h | 221 +++ + .../include/ControlSender.h | 283 ++++ + PluginControlInterfacePulse/src/ControlSender.cpp | 1763 ++++++++++++++++++++ + 6 files changed, 2460 insertions(+) + create mode 100644 PluginControlInterfacePulse/CMakeLists.txt + create mode 100755 PluginControlInterfacePulse/data/libPluginControlInterface.conf + create mode 100644 PluginControlInterfacePulse/include/ControlConfig.h + create mode 100644 PluginControlInterfacePulse/include/ControlSender.h + create mode 100644 PluginControlInterfacePulse/src/ControlSender.cpp + +diff --git a/CMakeLists.txt b/CMakeLists.txt +index 48e7ae2..25c8e95 100755 +--- a/CMakeLists.txt ++++ b/CMakeLists.txt +@@ -98,6 +98,9 @@ OPTION ( WITH_NSM + OPTION( WITH_PULSE_ROUTING_PLUGIN + "Enable PULSE Audio routing plugin interface" 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_PULSE_ROUTING_PLUGIN) +@@ -260,7 +263,11 @@ if(WITH_PLUGIN_ROUTING) + 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) +diff --git a/PluginControlInterfacePulse/CMakeLists.txt b/PluginControlInterfacePulse/CMakeLists.txt +new file mode 100644 +index 0000000..567fc35 +--- /dev/null ++++ b/PluginControlInterfacePulse/CMakeLists.txt +@@ -0,0 +1,93 @@ ++############################################################################ ++# 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 Control 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: ++# ++# 08.09.2014, Adrian Scarlat, First version of the code; ++# Porting code from AM ver1.x; ++############################################################################ ++ ++cmake_minimum_required(VERSION 2.6) ++ ++PROJECT(PluginControlInterfacePulse) ++ ++set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -rdynamic") ++set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g") ++ ++FIND_PACKAGE(PkgConfig) ++ ++OPTION( WITH_DOCUMENTATION ++ "Build together with Doxygen Documentation" OFF ) ++ ++SET(PLUGINS_OUTPUT_PATH ${CMAKE_SOURCE_DIR}/bin/plugins) ++set(LIBRARY_OUTPUT_PATH ${PLUGINS_OUTPUT_PATH}/control) ++set(DOC_OUTPUT_PATH ${CMAKE_SOURCE_DIR}/doc/ControlPlugin) ++set(INCLUDE_FOLDER "include") ++SET(LIB_INSTALL_SUFFIX "audioManager") ++ ++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}/control/IAmControlSend.h" VERSION_BUFFER LIMIT 6000) ++STRING(REGEX MATCH "ControlSendVersion*.[^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 control interface version ${LIB_INTERFACE_VERSION}") ++ ++FIND_PACKAGE(NSM REQUIRED) ++ ++INCLUDE_DIRECTORIES( ++ ${CMAKE_SOURCE_DIR} ++ ${STD_INCLUDE_DIRS} ++ ${CMAKE_CURRENT_BINARY_DIR} ++ ${AUDIO_INCLUDE_FOLDER} ++ ${PROJECT_INCLUDE_FOLDER} ++ ${INCLUDE_FOLDER} ++ ${NSM_INCLUDE_DIR} ++) ++ ++# all source files go here ++file(GLOB PLUGIN_CONTROL_SRCS_CXX "src/*.cpp") ++ ++add_library(PluginControlInterface SHARED ${PLUGIN_CONTROL_SRCS_CXX}) ++ ++TARGET_LINK_LIBRARIES(PluginControlInterface ++ ${DLT_LIBRARIES}) ++ ++IF(WITH_DOCUMENTATION) ++ file(MAKE_DIRECTORY ${DOC_OUTPUT_PATH}) ++ configure_file(${DOXY_FILE} ${DOC_OUTPUT_PATH}/Doxyfile @ONLY IMMEDIATE) ++ add_custom_target (PluginControlInterfaceDBusDocs 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 PluginControlInterface ++ DESTINATION "lib/${LIB_INSTALL_SUFFIX}/control/" ++ PERMISSIONS OWNER_EXECUTE OWNER_WRITE OWNER_READ GROUP_EXECUTE GROUP_READ WORLD_EXECUTE WORLD_READ ++ COMPONENT sampleplugins ++) ++ ++INSTALL(FILES data/libPluginControlInterface.conf ++ DESTINATION "lib/${LIB_INSTALL_SUFFIX}/control/" ++ PERMISSIONS OWNER_EXECUTE OWNER_WRITE OWNER_READ GROUP_EXECUTE GROUP_READ WORLD_EXECUTE WORLD_READ ++ COMPONENT sampleplugins ++) ++ ++CONFIGURE_FILE( ${CMAKE_SOURCE_DIR}/PluginControlInterfacePulse/data/libPluginControlInterface.conf ${PLUGINS_OUTPUT_PATH}/control/libPluginControlInterface.conf) +diff --git a/PluginControlInterfacePulse/data/libPluginControlInterface.conf b/PluginControlInterfacePulse/data/libPluginControlInterface.conf +new file mode 100755 +index 0000000..2976c97 +--- /dev/null ++++ b/PluginControlInterfacePulse/data/libPluginControlInterface.conf +@@ -0,0 +1,93 @@ ++############################################################################ ++# 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: ++# ++# 08.09.2014, Adrian Scarlat, First version of the code; ++# Porting code from AM ver1.x to AM ver3.0; ++# Added Copyright and License information; ++# ++# 19.09.2014, Adrian Scarlat, Added information regarding configuration options; ++# ++############################################################################ ++ ++# SourceClass has the following format: SourceClassName|SourceClassType ++[SourceClass] ++Entertainment|Main ++Browser|Main ++Navigation|Interrupt ++TTS|Interrupt ++Analogic|Main ++Digital|Main ++############################################################################ ++# SinkClass has the following format: SinkClassName|SinkClassType ++[SinkClass] ++HifiAudio|Main ++############################################################################ ++# Sinks and sources are clustered into independent parts which are capable of ++# exchanging audio with each other (AudioDomains). ++# Examples for possible AudioDomains: ++# - PulseAudio, Alsa, Jack, DSP, FPGA, MOST, In-chip switching matrix; ++# Between these AudioDomains, Audio can be interchanged via Gateways; ++# The format for a Gateway is: Sink from DomainX -> Source from DomainY; ++# Gateways always have a direction and can only transport one stream at a time. ++# Several gateways connecting the same domains together can exist in parallel ++# so that more than one source can be connected to more than one sink from the ++# same domains at the same time. ++#[Gateway] ++#Pulse2Dirana Primary:Alsa Primary>Primary Channel ++#Pulse2Dirana Secondary:Alsa Secondary>Secondary Channel ++############################################################################ ++# Routes ar defined from a SourceClass to a Sink. A route is a ">" separated list ++# of Sources and Sinks. Routes have the following format: ++# SOURCE_CATEGORY:[SOURCE_NAME>SINK_NAME] ++# Where: ++# - SOURCE_CATEGORY = "Source" or "SourceClass"; ++# If SOURCE_CATEGORY=SourceClass, the first SOURCE_NAME must be the name of a source class ++# Otherwise SOURCE_NAME and SINK_NAME should be the name of a source and a sink. ++# A Gateway might be used to replace a Sink-Source pair: ++# Route1: SourceClass1 > Sink1 > Source2 > Sink2 ++# Is equivalent to ++# Route2: SourceClass1 > Gateway1 > Sink 2 ++# If and only if Gateway1 = Sink1 > Source2 ++[Route] ++Entertainment>Entertainment>HifiAudio ++Browser>Browser>HifiAudio ++Navigation>Navigation>HifiAudio ++Analogic>Analogic>HifiAudio ++Digital>Digital>HifiAudio ++#Entertainment>Pulse2Dirana Primary>HifiAudio ++#Navigation>Pulse2Dirana Secondary>HifiAudio ++#Analogic>HifiAudio ++############################################################################ ++# Mixing rule should be something like: ++# - if a new source is in the class XXX ++# - if there is a connection like [SourceClass->Sink] ++# - apply an action in: Domain/ ++# Mixing line format: [SourceClass]>[ACTION]|[ACTION]... ++# Where ACTION = SourceClass-Sink:ACTION_TYPE,ACTION_TARGET:ActionTarget ++# Where ACTION_TYPE = Mute, Unmute, Pause, Resume, DecreaseVolume, IncreaseVolume, Disconnect ++# ActionTarget = Domain/Sink or Domain/Source ++[Mixing] ++#Entertainment:Entertainment>Hifi Audio:Disconnect:PulseAudio:Source|Entertainment>Hifi Audio:Disconnect:RoutingSenderDIRANA:Source|Browser>Hifi Audio:Disconnect:PulseAudio:Source|Browser>Hifi Audio:Disconnect:RoutingSenderDIRANA:Source|Analogic>Hifi Audio:Disconnect:RoutingSenderDIRANA:Source ++Entertainment:Entertainment>HifiAudio:Disconnect:PulseAudio:Source|Entertainment>HifiAudio:Disconnect:RoutingSenderPulse:Source|Browser>HifiAudio:Disconnect:PulseAudio:Source|Browser>HifiAudio:Disconnect:RoutingSenderPulse:Source|Analogic>HifiAudio:Disconnect:RoutingSenderPulse:Source ++#Analogic:Entertainment>Hifi Audio:Disconnect:PulseAudio:Source|Entertainment>Hifi Audio:Disconnect:RoutingSenderDIRANA:Source|Browser>Hifi Audio:Disconnect:PulseAudio:Source|Browser>Hifi Audio:Disconnect:RoutingSenderDIRANA:Source|Analogic>Hifi Audio:Disconnect:RoutingSenderDIRANA:Source ++Analogic:Entertainment>HifiAudio:Disconnect:PulseAudio:Source|Entertainment>HifiAudio:Disconnect:RoutingSenderPulse:Source|Browser>HifiAudio:Disconnect:PulseAudio:Source|Browser>HifiAudio:Disconnect:RoutingSenderPulse:Source|Analogic>HifiAudio:Disconnect:RoutingSenderPulse:Source ++#Navigation:Entertainment>Hifi Audio:DecreaseVolume:RoutingSenderDIRANA:Source|Analogic>Hifi Audio:DecreaseVolume:RoutingSenderDIRANA:Source|Browser>Hifi Audio:Mute:PulseAudio:Source| ++#Browser:Entertainment>Hifi Audio:Disconnect:PulseAudio:Source|Entertainment>Hifi Audio:Disconnect:RoutingSenderDIRANA:Source|Analogic>Hifi Audio:Disconnect:RoutingSenderDIRANA:Source ++#|TTS>HifiAudio Sink:Disconnect:Pulse:Source ++#TTS:Entertainment>HifiAudio Sink:DecreaseVolume:Dirana:Source ++# ++#TODO: define default mixing rule ? ++# +diff --git a/PluginControlInterfacePulse/include/ControlConfig.h b/PluginControlInterfacePulse/include/ControlConfig.h +new file mode 100644 +index 0000000..7dbf73e +--- /dev/null ++++ b/PluginControlInterfacePulse/include/ControlConfig.h +@@ -0,0 +1,221 @@ ++/** ++ * 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 Control 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: ++ * ++ * 08.09.2014, Adrian Scarlat, First version of the code; ++ * Porting from AM ver 1.x; ++ */ ++ ++#ifndef CONTROLCONFIG_H_ ++#define CONTROLCONFIG_H_ ++ ++#include "audiomanagertypes.h" ++#include <string> ++ ++ ++/* Defines */ ++ ++#define SOURCE_TYPE_INTERRUPT (1) ++#define SOURCE_TYPE_MAIN (2) ++#define SINK_TYPE_MAIN (2) ++ ++#define ACTION_TYPE_STR(x) (\ ++(x == AT_MUTE) ? "AT_MUTE" : \ ++(x == AT_UNMUTE) ? "AT_UNMUTE" : \ ++(x == AT_PAUSE) ? "AT_PAUSE" : \ ++(x == AT_RESUME) ? "AT_RESUME" : \ ++(x == AT_VOLUME_DECREASE) ? "AT_VOLUME_DECREASE" : \ ++(x == AT_VOLUME_INCREASE) ? "AT_VOLUME_INCREASE" : \ ++(x == AT_DISCONNECT) ? "AT_DISCONNECT" : \ ++(x == AT_CONNECT) ? "AT_CONNECT" : \ ++#x":Unknown") ++ ++ ++/** ++ * Action types sorted by priority: lowest - the most important ++ */ ++enum am_ActionType_e ++{ ++ AT_DISCONNECT=0, ++ AT_PAUSE=1, ++ AT_MUTE=2, ++ AT_UNMUTE=3, ++ AT_VOLUME_DECREASE=4, ++ AT_CONNECT=5, ++ AT_RESUME=6, ++ AT_VOLUME_INCREASE=7, ++ AT_MAX ++}; ++ ++ ++enum am_ActionTarget_e ++{ ++ ATG_SINK=0, ++ ATG_SOURCE=1, ++ ATG_GW_SINK=2, ++ ATG_GW_SOURCE=3, ++ ATG_MAX ++}; ++ ++ ++/* ++ * e.g. Entertainment : Entertainment>HifiAudio Sink:Disconnect:Pulse:Source ++ */ ++struct am_ConnectAction_s { ++ bool interrupting; /* The action is triggered by an interrupting source class: e.g. TTS, Phone */ ++ bool pending; /* The action was not applied yet. */ ++ /* e.g. there is an action with higher priority which is already applied to this connection */ ++ am::am_sourceClass_t activeSourceClassID; /* The name of the source class of existing connections - the one to be controlled */ ++ am::am_sinkID_t activeSinkID; /* The name of the sink of existing connections - the one to be controlled */ ++ am_ActionType_e actionType; /* What kind of action the controller decided: mute, pause, volume adjustment */ ++ am::am_domainID_t targetDomain; /* The domain of target element */ ++ am_ActionTarget_e targetElement; /* What element is controlled: sink, source or gateway */ ++ /** ++ * Define a list of inverse actions: if the controller applied an action during connection procedure, ++ * the inverse action (of any) shall be applied on disconnect. ++ */ ++ static am_ActionType_e inverseAction(am_ActionType_e type) ++ { ++ switch(type) ++ { ++ case AT_MUTE: return (AT_UNMUTE); ++ case AT_UNMUTE: return (AT_MUTE); ++ ++ case AT_PAUSE: return (AT_RESUME); ++ case AT_RESUME: return (AT_PAUSE); ++ ++ case AT_VOLUME_DECREASE: return (AT_VOLUME_INCREASE); ++ case AT_VOLUME_INCREASE: return (AT_VOLUME_DECREASE); ++ ++ case AT_DISCONNECT: return (AT_CONNECT);//disconnection is not an invertible action; maybe it is ++ case AT_CONNECT: return (AT_MAX);//connection is not an invertible action ++ ++ default: return (AT_MAX); // default return value ++ } ++ } ++ ++ ++ /** ++ * Compare everything except action type. ++ */ ++ bool weekCompare(struct am_ConnectAction_s &o) const ++ { ++ return ((this->activeSourceClassID == o.activeSourceClassID) && ++ (this->activeSinkID == o.activeSinkID) && ++ (this->targetDomain == o.targetDomain) && ++ (this->targetElement == o.targetElement)); ++ } ++ ++ /** ++ * Compare action type ++ */ ++ bool operator < (struct am_ConnectAction_s &o) const ++ { ++ return this->actionType < o.actionType; ++ } ++ ++}; ++ ++ ++struct am_MixingRule_s { ++ am::am_sourceClass_t mixingSourceClassID; /* the name of the source class to be connected/disconnected */ ++ std::vector<am_ConnectAction_s> actions; /* actions: e.g decrease volume for source of class Entertainment */ ++}; ++ ++#endif ++ ++ ++/* ++USE-CASE 1 ++========== ++ ++Start Entertainment(connectionID = 1) ++ ++Start Phone (connectionID = 2) ++ (A1)- actions caused by connectionID[2]: action on connectionID = 1 - mute source ++ ++Start Navigation (connectionID = 3) ++ (A2) - actions cause by connectionID[3]: action on connectionID = 1 - decrease volume (pending): A1 - higher priority than A2 ++ (A3) - action caused by connectionID[2]: action on connectionID = 3 - decrease volume (applied) ++ ++ ++End Phone ++ - inverse action (A1) - unmute connectionID = 1 ++ - Check if there is any pending action that could be applied, but was blocked by A1 ++ (A2) - actions cause by connectionID[3]: action on connectionID = 1 - decrease volume ++ - inverse action (A3) - increase volume connectionID = 3 ++ - Check if there is any pending action that could be applied, but was blocked by A3 - none ++ ++ ++ ++USE-CASE 2 ++========== ++ ++ ++Start Entertainment(connectionID = 1) ++ ++Start Navigation (connectionID = 2) ++ (A1) - actions cause by connectionID[2]: action on connectionID = 1 - decrease volume (applied) ++ ++ ++Start Phone (connectionID = 3) ++ (A2) - action caused by connectionID[3]: action on connectionID = 2 - decrease volume (applied) ++ (A3) - actions caused by connectionID[1]: action on connectionID = 1 - mute source; A1 - pending ++ ++ ++End Phone ++ - inverse action (A2) - increase volume connectionID = 2 ++ - Check if there is any pending action that could be applied, but was blocked by A3 - none ++ - inverse action (A3) - unmute connectionID = 1 ++ - Check if there is any pending action that could be applied, but was blocked by A3 ++ (A1) - actions cause by connectionID[2]: action on connectionID = 1 - decrease volume (applied) ++ ++ ++ ++ ++USE-CASE 3 ++========== ++ ++ ++Start Entertainment(connectionID = 1) ++ ++Start Navigation (connectionID = 2) ++ (A1) - actions cause by connectionID[2]: action on connectionID = 1 - decrease volume (applied) ++ ++ ++Start Phone (connectionID = 3) ++ (A2) - action caused by connectionID[3]: action on connectionID = 2 - decrease volume (applied) ++ (A3) - actions caused by connectionID[3]: action on connectionID = 1 - mute source; A1 - pending ++ ++End Navigation ++ - inverse action (A1) - A1 is pending ... no action to apply ++ ++End Phone ++ - inverse action (A2) - increase volume connectionID = 2 ++ - connection does not exits ++ - inverse action (A3) - unmute connectionID = 1 ++*/ ++ ++ ++ ++/* ++ ++MainAction -> list of subconnection, each subconnection, a list of actions ++ each action has a triggering main connection (other) ++ ++//what actions this main connection was causing ++MainAction -> a map of subconnections and corresponding action ++ ++*/ +diff --git a/PluginControlInterfacePulse/include/ControlSender.h b/PluginControlInterfacePulse/include/ControlSender.h +new file mode 100644 +index 0000000..940661b +--- /dev/null ++++ b/PluginControlInterfacePulse/include/ControlSender.h +@@ -0,0 +1,283 @@ ++/** ++ * 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 Control 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: ++ * ++ * 08.09.2014, Adrian Scarlat, First version of the code; ++ * porting from AM ver 1.x; ++ */ ++ ++#ifndef CONTROLSENDER_H_ ++#define CONTROLSENDER_H_ ++ ++#include "ControlConfig.h" ++#include "control/IAmControlSend.h" ++#include <map> ++ ++using namespace am; ++ ++class ControlSenderPlugin: public IAmControlSend ++{ ++public: ++ ControlSenderPlugin(); ++ ++ virtual ~ControlSenderPlugin(); ++ ++ am_Error_e startupController(IAmControlReceive* controlreceiveinterface); ++ void setControllerReady(); ++ void setControllerRundown(const int16_t signal); ++ ++ am_Error_e hookUserConnectionRequest( ++ const am_sourceID_t sourceID, ++ const am_sinkID_t sinkID, ++ am_mainConnectionID_t& mainConnectionID); ++ ++ am_Error_e hookUserDisconnectionRequest(const am_mainConnectionID_t connectionID); ++ ++ am_Error_e hookUserSetMainSinkSoundProperty( ++ const am_sinkID_t sinkID, ++ const am_MainSoundProperty_s& soundProperty); ++ ++ am_Error_e hookUserSetMainSourceSoundProperty( ++ const am_sourceID_t sourceID, ++ const am_MainSoundProperty_s& soundProperty); ++ ++ am_Error_e hookUserSetSystemProperty(const am_SystemProperty_s& property); ++ ++ am_Error_e hookUserVolumeChange( ++ const am_sinkID_t SinkID, ++ const am_mainVolume_t newVolume); ++ ++ am_Error_e hookUserVolumeStep( ++ const am_sinkID_t SinkID, ++ const int16_t increment); ++ ++ am_Error_e hookUserSetSinkMuteState( ++ const am_sinkID_t sinkID, ++ const am_MuteState_e muteState); ++ ++ am_Error_e hookSystemRegisterDomain( ++ const am_Domain_s& domainData, ++ am_domainID_t& domainID); ++ ++ am_Error_e hookSystemDeregisterDomain(const am_domainID_t domainID); ++ ++ void hookSystemDomainRegistrationComplete(const am_domainID_t domainID); ++ ++ am_Error_e hookSystemRegisterSink( ++ const am_Sink_s& sinkData, ++ am_sinkID_t& sinkID); ++ ++ am_Error_e hookSystemDeregisterSink(const am_sinkID_t sinkID); ++ ++ am_Error_e hookSystemRegisterSource( ++ const am_Source_s& sourceData, ++ am_sourceID_t& sourceID); ++ ++ am_Error_e hookSystemDeregisterSource(const am_sourceID_t sourceID); ++ ++ am_Error_e hookSystemRegisterGateway( ++ const am_Gateway_s& gatewayData, ++ am_gatewayID_t& gatewayID); ++ ++ am_Error_e hookSystemDeregisterGateway(const am_gatewayID_t gatewayID); ++ ++ am_Error_e hookSystemRegisterCrossfader( ++ const am_Crossfader_s& crossfaderData, ++ am_crossfaderID_t& crossfaderID); ++ ++ am_Error_e hookSystemDeregisterCrossfader( ++ const am_crossfaderID_t crossfaderID); ++ ++ void hookSystemSinkVolumeTick( ++ const am_Handle_s handle, ++ const am_sinkID_t sinkID, ++ const am_volume_t volume); ++ ++ void hookSystemSourceVolumeTick( ++ const am_Handle_s handle, ++ const am_sourceID_t sourceID, ++ const am_volume_t volume); ++ ++ void hookSystemInterruptStateChange( ++ const am_sourceID_t sourceID, ++ const am_InterruptState_e interruptState); ++ ++ void hookSystemSinkAvailablityStateChange( ++ const am_sinkID_t sinkID, ++ const am_Availability_s& availability); ++ ++ void hookSystemSourceAvailablityStateChange( ++ const am_sourceID_t sourceID, ++ const am_Availability_s& availability); ++ ++ void hookSystemDomainStateChange( ++ const am_domainID_t domainID, ++ const am_DomainState_e state); ++ ++ void hookSystemReceiveEarlyData(const std::vector<am_EarlyData_s>& data); ++ ++ void hookSystemSpeedChange(const am_speed_t speed); ++ ++ void hookSystemTimingInformationChanged( ++ const am_mainConnectionID_t mainConnectionID, ++ const am_timeSync_t time); ++ ++ void cbAckConnect( ++ const am_Handle_s handle, ++ const am_Error_e errorID); ++ ++ void cbAckDisconnect( ++ const am_Handle_s handle, ++ const am_Error_e errorID); ++ ++ void cbAckCrossFade( ++ const am_Handle_s handle, ++ const am_HotSink_e hostsink, ++ const am_Error_e error); ++ ++ void cbAckSetSinkVolumeChange( ++ const am_Handle_s handle, ++ const am_volume_t volume, ++ const am_Error_e error); ++ ++ void cbAckSetSourceVolumeChange( ++ const am_Handle_s handle, ++ const am_volume_t voulme, ++ const am_Error_e error); ++ ++ void cbAckSetSourceState( ++ const am_Handle_s handle, ++ const am_Error_e error); ++ ++ void cbAckSetSourceSoundProperty( ++ const am_Handle_s handle, ++ const am_Error_e error); ++ ++ void cbAckSetSourceSoundProperties( ++ const am_Handle_s handle, ++ const am_Error_e error); ++ ++ void cbAckSetSinkSoundProperty( ++ const am_Handle_s handle, ++ const am_Error_e error); ++ ++ void cbAckSetSinkSoundProperties( ++ const am_Handle_s handle, ++ const am_Error_e error); ++ ++ am_Error_e getConnectionFormatChoice( ++ const am_sourceID_t sourceID, ++ const am_sinkID_t sinkID, ++ const am_Route_s listRoute, ++ const std::vector<am_CustomConnectionFormat_t> listPossibleConnectionFormats, ++ std::vector<am_CustomConnectionFormat_t>& listPrioConnectionFormats); ++ void getInterfaceVersion(std::string& version) const; ++ void confirmCommandReady(const am_Error_e error); ++ void confirmRoutingReady(const am_Error_e error); ++ void confirmCommandRundown(const am_Error_e error); ++ void confirmRoutingRundown(const am_Error_e error); ++ ++ am_Error_e hookSystemUpdateSink(const am_sinkID_t sinkID, const am_sinkClass_t sinkClassID, const std::vector<am_SoundProperty_s>& listSoundProperties, const std::vector<am_CustomConnectionFormat_t>& listConnectionFormats, const std::vector<am_MainSoundProperty_s>& listMainSoundProperties); ++ am_Error_e hookSystemUpdateSource(const am_sourceID_t sourceID, const am_sourceClass_t sourceClassID, const std::vector<am_SoundProperty_s>& listSoundProperties, const std::vector<am_CustomConnectionFormat_t>& listConnectionFormats, const std::vector<am_MainSoundProperty_s>& listMainSoundProperties); ++ am_Error_e hookSystemUpdateGateway(const am_gatewayID_t gatewayID, const std::vector<am_CustomConnectionFormat_t>& listSourceConnectionFormats, const std::vector<am_CustomConnectionFormat_t>& listSinkConnectionFormats, const std::vector<bool>& convertionMatrix); ++ void cbAckSetVolumes(const am_Handle_s handle, const std::vector<am_Volumes_s>& listVolumes, const am_Error_e error) ; ++ void cbAckSetSinkNotificationConfiguration(const am_Handle_s handle, const am_Error_e error) ; ++ void cbAckSetSourceNotificationConfiguration(const am_Handle_s handle, const am_Error_e error) ; ++ void hookSinkNotificationDataChanged(const am_sinkID_t sinkID, const am_NotificationPayload_s& payload) ; ++ void hookSourceNotificationDataChanged(const am_sourceID_t sourceID, const am_NotificationPayload_s& payload) ; ++ am_Error_e hookUserSetMainSinkNotificationConfiguration(const am_sinkID_t sinkID, const am_NotificationConfiguration_s& notificationConfiguration) ; ++ am_Error_e hookUserSetMainSourceNotificationConfiguration(const am_sourceID_t sourceID, const am_NotificationConfiguration_s& notificationConfiguration) ; ++ void hookSystemNodeStateChanged(const NsmNodeState_e NodeStateId) ; ++ void hookSystemNodeApplicationModeChanged(const NsmApplicationMode_e ApplicationModeId) ; ++ void hookSystemSessionStateChanged(const std::string& sessionName, const NsmSeat_e seatID, const NsmSessionState_e sessionStateID) ; ++ NsmErrorStatus_e hookSystemLifecycleRequest(const uint32_t Request, const uint32_t RequestId) ; ++ ++private: ++ bool isStaticRoutingEnabled() {return true;} ++ ++ /** ++ * Load controller configuration file. The name and path of the configuration file ++ * should be identical to library (except the extensions which is .conf instead of .so) ++ */ ++ am_Error_e loadConfig(); ++ ++ /** ++ * Apply some actions on top of main connection specified by (connectionID) ++ * If interrupt=true, the action like "disconnect" is reversible (e.g. when the main connection ++ * triggering this action, is destroyed, connectionID will be re-established, otherwise actions like "disconnect" are permanent) ++ * If inverse = true, action is inversely applied: (e.g. action = MUTE and inverse = true, we apply UNMUTE) ++ */ ++ am_Error_e applyActionOnConnection( ++ am_mainConnectionID_t connectionID, ++ am_ConnectAction_s &action, ++ bool interrupt, ++ bool inverse, ++ bool force = false); ++ ++ am_Error_e applyActionOnSubConnection( ++ am_mainConnectionID_t connectionID, ++ am_Connection_s subConnection, ++ am_ConnectAction_s &action, ++ bool interrupt, ++ bool inverse); ++ ++ am_Error_e getStaticRoute( ++ const bool onlyfree, ++ const am_sourceID_t sourceID, ++ const am_sinkID_t sinkID, ++ std::vector<am_Route_s>& returnList); ++ ++ am_Error_e getSourceInfo( ++ const am_sourceID_t sourceID, ++ am_Source_s &sourceData); ++ ++/* ++ * TODO: REVIEW DESCRIPTION ++ * A mapping between connection and controller actions taken during connection. ++ * To be used to apply inverse actions during disconnection: ++ * am_mainConnectionID_t - the main connection request triggering the action ++ * am_connectionID_t - the sub-connection target of the action ++ * list of am_ConnectAction_s - actions to be applied on this connection ++ * in this list, only one action is not pending ++ * and the non pending action has the higher priority ++ * (e.g. disconnect > mute > decrease volume) ++ */ ++ std::map<am_mainConnectionID_t, ++ std::map<am_mainConnectionID_t, ++ std::vector<am_ConnectAction_s> > > mConnectionID2Actions; ++/* ++ * A mapping between main connection and and the list of associated sub-connections. ++ * First element in the list has the same connectionID as the map key. ++ */ ++ std::map<am_mainConnectionID_t, ++ std::vector<am_Connection_s> > mConnectionID2Subconnections; ++ ++ IAmControlReceive *mControlReceiveInterface; ++ std::vector<am_Route_s> mStaticRoutes; ++ std::vector<am_MixingRule_s> mMixingRules; ++ ++/* ++ * map of structures registered during initialization/configuration phase; ++ * DO NOT USE DIRECTLY ++ */ ++ std::map<std::string, am_Source_s> mNames2Sources; ++ std::map<std::string, am_Sink_s> mNames2Sinks; ++ std::map<std::string, am_SinkClass_s> mNames2SinksClasses; ++ std::map<std::string, am_SourceClass_s> mNames2SourceClasses; ++ std::map<std::string, am_Domain_s> mNames2Domains; ++ std::map<std::string, am_Gateway_s> mNames2Gateways; ++}; ++ ++#endif /* CONTROLSENDER_H_ */ +diff --git a/PluginControlInterfacePulse/src/ControlSender.cpp b/PluginControlInterfacePulse/src/ControlSender.cpp +new file mode 100644 +index 0000000..eb02b0e +--- /dev/null ++++ b/PluginControlInterfacePulse/src/ControlSender.cpp +@@ -0,0 +1,1763 @@ ++/** ++ * 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 Control 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: ++ * ++ * 08.09.2014, Adrian Scarlat, First version of the code; ++ * Porting from AM ver 1.0; ++ */ ++ ++/*Includes*/ ++#include "ControlSender.h" ++#include "ControlConfig.h" ++#include "control/IAmControlReceive.h" ++#include "shared/CAmDltWrapper.h" ++ ++#include <cassert> ++#include <string.h> ++#include <stdio.h> ++#include <iostream> ++#include <sys/types.h> ++#include <unistd.h> ++#include <errno.h> ++ ++ ++/*Defines*/ ++#define LIBNAMECTL "libPluginControlInterface.so" ++#define CFGNAMECTL "libPluginControlInterface.conf" ++ ++DLT_DECLARE_CONTEXT(controlSender) ++ ++using namespace am; ++ ++extern "C" IAmControlSend* PluginControlInterfaceFactory() ++{ ++ return (new ControlSenderPlugin()); ++} ++ ++extern "C" void destroyControlPluginInterface(IAmControlSend* controlSendInterface) ++{ ++ delete controlSendInterface; ++} ++ ++ControlSenderPlugin::ControlSenderPlugin() : ++ mControlReceiveInterface(NULL) ++{ ++} ++ ++ControlSenderPlugin::~ControlSenderPlugin() ++{ ++} ++ ++am_Error_e ControlSenderPlugin::startupController(IAmControlReceive *controlreceiveinterface) ++{ ++ assert(controlreceiveinterface); ++ mControlReceiveInterface = controlreceiveinterface; ++ //here is a good place to insert SystemProperties into the database... ++ return E_OK; ++} ++ ++void ControlSenderPlugin::setControllerReady() ++{ ++ //here is a good place to insert Source and SinkClasses into the database... ++ loadConfig(); ++ mControlReceiveInterface->setRoutingReady(); ++ mControlReceiveInterface->setCommandReady(); ++} ++ ++void ControlSenderPlugin::setControllerRundown(const int16_t signal) ++{ ++ logInfo("CAmControlSenderBase::setControllerRundown() was called signal=",signal); ++ if (signal==2) ++ mControlReceiveInterface->confirmControllerRundown(E_UNKNOWN); ++ ++ mControlReceiveInterface->confirmControllerRundown(E_OK); ++} ++ ++am_Error_e ControlSenderPlugin::hookUserConnectionRequest(const am_sourceID_t sourceID, const am_sinkID_t sinkID, am_mainConnectionID_t & mainConnectionID) ++{ ++ //TODO: set sink / source volume depending on mixing rules - delta - system property ++ //TODO: the mixing depends on source CLASSES? ++ // ++ //TODO: shall we implement some kind of source auto-exclusion ? e.g. two source from te same class are not allowed simultaneously (fifo) ++ //TODO: shall we implement an action = destroy? apart from disconnect? disconnect is reversible, destroy is not reversible and the main connection is deleted? ++ //TODO: maybe we simply should return E_ALREADY_EXISTS - and tis should not be a real error code (if connection already exists, maybe that's fine) ++ ++ //TODO: apply actions only on active main connections!!!! ++ ++ //TODO: interrupting sources on top of main sources -> main src:temporary disconnection; ++ //TODO: main sources on top of main sources -> old main src:permanent disconnection; ++ ++ ++ ++ std::vector<am_Route_s> audioRoute; ++ if (!this->isStaticRoutingEnabled()) ++ { ++ this->mControlReceiveInterface->getRoute(true, sourceID, sinkID, audioRoute); ++ } ++ else ++ { ++ this->getStaticRoute(true, sourceID, sinkID, audioRoute); ++ } ++ ++//TODO: if there is no static route? is there any default route ? ++//TODO: if there is no static route? can we use dynamic routing ? ++ logInfo("ControlSenderPlugin::hookUserConnectionRequest audioRoute is empty ? ", audioRoute.empty()); ++ if (audioRoute.empty()) ++ { ++ return am::E_NOT_POSSIBLE; ++ } ++ ++/* ++ * Find out the source class info for the new main connection. ++ * Also find out if this connection is interrupting or not ++ */ ++ am_SourceClass_s newClassInfo; ++ this->mControlReceiveInterface->getSourceClassInfoDB(sourceID, newClassInfo); ++ ++ bool interrupting = false; ++ for (uint16_t i = 0; i < newClassInfo.listClassProperties.size(); i++) ++ { ++ if (newClassInfo.listClassProperties[i].classProperty == CP_GENIVI_SOURCE_TYPE && ++ newClassInfo.listClassProperties[i].value == SOURCE_TYPE_INTERRUPT) ++ { ++ interrupting = true; ++ } ++ } ++ ++ logInfo("{BEGIN Connection request Source class:", ++ newClassInfo.name, ++ " [ sourceID:", sourceID, ", sinkID:", sinkID, "] - interrupting:" ,interrupting); ++ ++ std::vector<am_MainConnection_s> existingConnections; ++ ++ this->mControlReceiveInterface->getListMainConnections(existingConnections); ++ std::vector<am_MainConnection_s>::iterator iter = existingConnections.begin(); ++ std::vector<am_MainConnection_s>::iterator iterEnd = existingConnections.end(); ++ ++ iter = existingConnections.begin(); ++/* ++* iterate connections to remove already existing connections for the same source/sink ++*/ ++ bool listChanged = false; ++ for (; iter < iterEnd; ++iter) ++ { ++ if (iter->sinkID == sinkID && iter->sourceID == sourceID) ++ { ++ //main connection already exists - exclude it ++ logInfo("END Connection request - ALREADY EXISTS}"); ++ return E_ALREADY_EXISTS; ++ } ++ } ++ if (listChanged) ++ { ++ this->mControlReceiveInterface->getListMainConnections(existingConnections); ++ iter = existingConnections.begin(); ++ iterEnd = existingConnections.end(); ++ } ++ ++//TODO: first connect everything in the audio path ++//TODO: if one sub-connection is not successful - disconnect everything ++//TODO: only if every sub-connection was successful - apply the mixing rules ++ ++ std::map<am_mainConnectionID_t, std::vector<am_ConnectAction_s> > mainConnection2Actions; ++ ++/*************************************************************** ++ * START MIXING RULES EVALUATION ++ * - maintain a list of actions associated to a connection; ++ * according to the mixing rules ++ * - the inverse actions shall be applied during disconnection ++ ***************************************************************/ ++ ++/* ++ * Iterate audio mixing rules, then iterate existing connections; ++ * if the new source class match the mixing source class ++ * find out if any action should be taken, find out the target ++ * of the action -> domain/sink/source. ++ */ ++ for (uint16_t i = 0; i < mMixingRules.size(); i++) ++ { ++/* ++ * the source class of the new connection matches the source class of the mixing rule ++ */ ++ if (newClassInfo.sourceClassID == mMixingRules[i].mixingSourceClassID) ++ { ++ iter = existingConnections.begin(); ++/* ++ * iterate main connections ++ */ ++ for (; iter < iterEnd; ++iter) ++ { ++ am_SourceClass_s srcClassOfConn; ++ am_sinkID_t sinkIdOfConn = iter->sinkID; ++ ++ this->mControlReceiveInterface->getSourceClassInfoDB( ++ iter->sourceID, srcClassOfConn); ++ ++ am_sourceClass_t srcClassIdOfConn = srcClassOfConn.sourceClassID; ++/* ++ * iterate the actions associated to the mixing rule ++ */ ++ for (uint16_t j = 0; j < mMixingRules[i].actions.size(); j++) ++ { ++/* ++ * if the source class of the action in the mixing rule matches the source class ++ of an existing connection AND the sink of the action in the mixing rule matches ++ the sink of an existing connection - then the action must be applied on ++ existing connection ++ */ ++ if (mMixingRules[i].actions[j].activeSourceClassID == srcClassIdOfConn && ++ mMixingRules[i].actions[j].activeSinkID == sinkIdOfConn) ++ { ++ mainConnection2Actions[iter->mainConnectionID].push_back(mMixingRules[i].actions[j]); ++ logInfo("Apply action on existing connection:", iter->mainConnectionID, ++ "caused by new connection - unknow ID yet"); ++ this->applyActionOnConnection(iter->mainConnectionID, mMixingRules[i].actions[j], interrupting, false); ++ ++ }//end if (existingClassInfo.name.compare( ++ }//end for actionsCount ++ }//end for existingConnections ++ }//end if (newClassInfo.name.compare(mixingRules[i].sourceClassName ++ }//end for mixingRules ++ ++/** ++ * After actions cause by the new connections were applied on existing connections, ++ * connect all elements in the route to create sub-connections; ++ * for each connection, store the reference in: mConnectionID2Subconnections ++ */ ++ //for static routing - only one route in the list of audioRoute ++ std::vector<am_RoutingElement_s>::iterator iterRoute = audioRoute[0].route.begin(); ++ std::vector<am_RoutingElement_s>::iterator iterRouteEnd = audioRoute[0].route.end(); ++ ++ std::vector<am_Connection_s> subconnections; ++ am_MainConnection_s mainConnection; ++ ++ for (; iterRoute < iterRouteEnd; iterRoute++) ++ { ++ am_Handle_s h; ++ ++ am_mainConnectionID_t subConnectionID; ++ ++ am_Error_e ret = mControlReceiveInterface->connect( ++ h, subConnectionID, iterRoute->connectionFormat, ++ iterRoute->sourceID, iterRoute->sinkID); ++ ++ am_SourceClass_s srcClassOfConn; ++ ++ this->mControlReceiveInterface->getSourceClassInfoDB( ++ iterRoute->sourceID, srcClassOfConn); ++ ++ logInfo("Subconnection created: subConnectionID", subConnectionID); ++ logInfo("Subconnection: sourceID:", iterRoute->sourceID , ++ ", sourceClassID:", srcClassOfConn.sourceClassID, ++ ", sinkID:", iterRoute->sinkID, "]"); ++ ++ if (ret == E_OK || ret == E_ALREADY_EXISTS) ++ { ++ am_Connection_s tempConnection; ++ tempConnection.sinkID = iterRoute->sinkID; ++ tempConnection.sourceID = iterRoute->sourceID; ++ tempConnection.connectionID = subConnectionID; ++ ++ subconnections.push_back(tempConnection); ++ mainConnection.listConnectionID.push_back(tempConnection.connectionID); ++ } ++ else ++ { ++ //TODO: disconnect already connected in case of error ++ logInfo("END Connection request with error code:", ret, "}"); ++ return ret; ++ } ++ } ++ ++/* ++ * Once the sub-connections were created, we are ready to store the main connections ++ */ ++ ++ mainConnection.mainConnectionID = 0; ++ mainConnection.connectionState = CS_CONNECTED; ++ mainConnection.sinkID = sinkID; ++ mainConnection.sourceID = sourceID; ++ am_Error_e ret = this->mControlReceiveInterface->enterMainConnectionDB( ++ mainConnection, mainConnectionID); ++ mainConnection.mainConnectionID = mainConnectionID; ++ ++ this->mConnectionID2Subconnections[mainConnectionID] = subconnections; ++ this->mConnectionID2Actions[mainConnectionID] = mainConnection2Actions; ++ ++ if (ret != E_OK) ++ { ++ logInfo("END Connection request with error code:", ret, "}"); ++ return ret; ++ } ++ else ++ { ++ logInfo("Mainconnection: mainConnectionID", mainConnectionID); ++ } ++ //TODO - check the status ++ ++ ++/* ++ * Iterate audio mixing rules, then iterate actions, ++ * if the source class of an action matches the new source class name ++ * find out if there is an existing connection matching the mixingSourceClass ++ */ ++ for (uint16_t i = 0; i < mMixingRules.size(); i++) ++ { ++ for (uint16_t j = 0; j < mMixingRules[i].actions.size(); j++) ++ { ++/* ++ * if the source class of the action in the mixing rule matches the source class ++ of an existing connection AND the sink of the action in the mixing rule matches ++ the sink of an existing connection - then the action must be applied on ++ existing connection ++ */ ++ if (mMixingRules[i].actions[j].activeSourceClassID == newClassInfo.sourceClassID) ++ { ++ iter = existingConnections.begin(); ++ /* ++ * iterate connections ++ */ ++ for (; iter < iterEnd; ++iter) ++ { ++ am_SourceClass_s srcClassOfConn; ++ ++ this->mControlReceiveInterface->getSourceClassInfoDB( ++ iter->sourceID, srcClassOfConn); ++ ++ am_sourceClass_t srcClassIdOfConn = srcClassOfConn.sourceClassID; ++ if (srcClassIdOfConn == mMixingRules[i].mixingSourceClassID) ++ { ++ if (this->mConnectionID2Actions[mainConnectionID].find( ++ iter->mainConnectionID) != ++ this->mConnectionID2Actions[mainConnectionID].end()) ++ { ++ //there was already an action applied for iter->connectionID caused by mainConnection.connectionID ++ //connections should not exclude each other ++ logInfo("No action to apply on new connection:", mainConnection.mainConnectionID, ++ "since there was already an action applyed to existing connection", iter->mainConnectionID); ++ } ++ else ++ { ++ logInfo("Apply action on new connection:", mainConnection.mainConnectionID, ++ "caused by existing connection", iter->mainConnectionID); ++ ++ interrupting = false; ++ for (uint16_t x = 0; x < srcClassOfConn.listClassProperties.size(); x++) ++ { ++ if (srcClassOfConn.listClassProperties[x].classProperty == CP_GENIVI_SOURCE_TYPE && ++ srcClassOfConn.listClassProperties[x].value == SOURCE_TYPE_INTERRUPT) ++ { ++ interrupting = true; ++ } ++ } ++ ++ this->mConnectionID2Actions[iter->mainConnectionID][mainConnectionID].push_back(mMixingRules[i].actions[j]); ++ this->applyActionOnConnection(mainConnection.mainConnectionID, mMixingRules[i].actions[j], interrupting, false); ++ } ++ } ++ } ++ } ++ } ++ } ++ ++ logInfo("END Connection request - SUCCESS}"); ++ return E_OK; ++} ++ ++ ++am_Error_e ControlSenderPlugin::hookUserDisconnectionRequest(const am_mainConnectionID_t connectionID) ++{ ++ am_Handle_s h; ++ ++ if (connectionID == 0) ++ { ++ logError("Disconnect what :-o ? connection ID = 0"); ++ return E_NON_EXISTENT; ++ } ++ ++ //TODO: restore other sources - resume ? or restore volume? - then disconnect ++ am_SourceClass_s oldClassInfo; ++ bool interrupting = false; ++ ++ std::vector<am_MainConnection_s> existingConnections; ++ ++ this->mControlReceiveInterface->getListMainConnections(existingConnections); ++ logInfo("{BEGIN Disconnection request: connectionID:", connectionID); ++ std::vector<am_MainConnection_s>::iterator iter = existingConnections.begin(); ++ std::vector<am_MainConnection_s>::iterator iterEnd = existingConnections.end(); ++ ++ for (; iter < iterEnd; ++iter) ++ { ++ if (iter->mainConnectionID == connectionID) ++ {//check other existing connection , maybe volume should be restored ++ this->mControlReceiveInterface->getSourceClassInfoDB(iter->sourceID, oldClassInfo); ++ for (unsigned int i = 0; i < oldClassInfo.listClassProperties.size(); i++) ++ { ++ if (oldClassInfo.listClassProperties[i].classProperty == CP_GENIVI_SOURCE_TYPE && ++ oldClassInfo.listClassProperties[i].value == SOURCE_TYPE_INTERRUPT) ++ { ++ interrupting = true; ++ } ++ } ++ break; ++ } ++ } ++ ++ //save the list of inverse actions ++ std::map<am_mainConnectionID_t, ++ std::vector<am_ConnectAction_s> > connectionID2Actions = this->mConnectionID2Actions[connectionID]; ++ ++ this->mConnectionID2Actions[connectionID].clear(); ++ ++ //apply the inverse action - of actions caused by this connection ++ iter = existingConnections.begin(); ++ for (; iter < iterEnd; ++iter) ++ { ++ logInfo("Check apply inverse action on existing connection:", iter->mainConnectionID, ++ "caused by disconnecion connection", connectionID); ++ ++ std::vector<am_ConnectAction_s>::iterator actionsIter ++ = connectionID2Actions[iter->mainConnectionID].begin(); ++ std::vector<am_ConnectAction_s>::iterator actionsIterEnd ++ = connectionID2Actions[iter->mainConnectionID].end(); ++ ++ for (; actionsIter < actionsIterEnd; actionsIter++) ++ { ++ //apply the inverse action ++ logInfo("Apply inverse action on existing connection:", iter->mainConnectionID, ++ "caused by disconnecion connection", connectionID); ++ ++ applyActionOnConnection(iter->mainConnectionID, *actionsIter, interrupting, true); ++ }//end for actions ++ }//end for existing connections ++ ++ ++ //apply the inverse action - of actions applyed to this connection ++ iter = existingConnections.begin(); ++ for (; iter < iterEnd; ++iter) ++ { ++ std::map<am_mainConnectionID_t, ++ std::vector<am_ConnectAction_s> > connectionID2Actions = this->mConnectionID2Actions[iter->mainConnectionID]; ++ ++ logInfo("Check apply inverse action on existing connection:", iter->mainConnectionID, ++ "caused by disconnecion connection", connectionID); ++ ++ std::vector<am_ConnectAction_s>::iterator actionsIter ++ = connectionID2Actions[connectionID].begin(); ++ std::vector<am_ConnectAction_s>::iterator actionsIterEnd ++ = connectionID2Actions[connectionID].end(); ++ ++ for (; actionsIter < actionsIterEnd; actionsIter++) ++ { ++ //apply the inverse action ++ logInfo("Apply inverse action on disconnecion connection", connectionID); ++ ++ applyActionOnConnection(connectionID, *actionsIter, interrupting, true, true); ++ }//end for actions ++ ++ } ++ ++ //iterate sub-connections and disconnect ++ std::vector<am_Connection_s>::iterator subConnIter = ++ this->mConnectionID2Subconnections[connectionID].begin(); ++ std::vector<am_Connection_s>::iterator subConnIterEnd = ++ this->mConnectionID2Subconnections[connectionID].end(); ++ ++ for (; subConnIter < subConnIterEnd; subConnIter++) ++ { ++ logInfo("Subdisconnection: subConnectionID", subConnIter->connectionID); ++ mControlReceiveInterface->disconnect(h, subConnIter->connectionID); ++ } ++ ++ //TODO: return value depends on all disconnect() results. ++ this->mConnectionID2Subconnections[connectionID].clear(); ++ ++ this->mControlReceiveInterface->changeMainConnectionStateDB( ++ connectionID, CS_DISCONNECTED); ++ this->mControlReceiveInterface->removeMainConnectionDB(connectionID); ++ ++ ++ logInfo("END Disconnection request}"); ++ ++ return E_OK; ++} ++ ++ ++ ++/** ++ * Find out the sub-connection target of this action ++ * apply the action and return the connectionID of the affected ++ * sub-connection ++ */ ++am_Error_e ControlSenderPlugin::applyActionOnConnection( ++ am_mainConnectionID_t connectionID, ++ am_ConnectAction_s &action, ++ bool interrupt, ++ bool inverse, ++ bool force) ++{ ++ am_Error_e ret; ++ ++ /** ++ * Check if current action is conflicting an existing action, ++ * do not apply, or cancel existing action and apply this one. ++ * if action is not inverse then: ++ * - current action may not be applied ++ * - current action might cause other actions to become pending ++ */ ++ std::vector<am_MainConnection_s> existingConnections; ++ ++ this->mControlReceiveInterface->getListMainConnections(existingConnections); ++ ++ std::vector<am_MainConnection_s>::iterator iter1 = existingConnections.begin(); ++ std::vector<am_MainConnection_s>::iterator iter1End = existingConnections.end(); ++ ++ std::vector<am_MainConnection_s>::iterator iter2 = existingConnections.begin(); ++ std::vector<am_MainConnection_s>::iterator iter2End = existingConnections.end(); ++ ++ if (!force) ++ { ++ ++ for (; iter1 < iter1End; ++iter1) ++ { ++ std::map<am_mainConnectionID_t, ++ std::vector<am_ConnectAction_s> > connectionID2Actions = ++ this->mConnectionID2Actions[iter1->mainConnectionID]; ++ ++ iter2 = existingConnections.begin(); ++ for (; iter2 < iter2End; ++iter2) ++ { ++ std::vector<am_ConnectAction_s>::iterator actionsIter ++ = connectionID2Actions[iter2->mainConnectionID].begin(); ++ std::vector<am_ConnectAction_s>::iterator actionsIterEnd ++ = connectionID2Actions[iter2->mainConnectionID].end(); ++ ++ for (; actionsIter < actionsIterEnd; actionsIter++) ++ { ++ if (action.weekCompare(*actionsIter)) ++ { ++ if (!inverse) ++ { ++ if (actionsIter->actionType < action.actionType) ++ { ++ logInfo("Abort action", ACTION_TYPE_STR(action.actionType), ++ "on connection:", connectionID, ++ "since", ACTION_TYPE_STR(actionsIter->actionType), ++ "is active; triggering mainConnection:", iter1->mainConnectionID); ++ return am::E_ABORTED; ++ } ++ } ++ else ++ { ++ if (actionsIter->actionType < am_ConnectAction_s::inverseAction(action.actionType)) ++ { ++ logInfo("Abort action", ACTION_TYPE_STR(am_ConnectAction_s::inverseAction(action.actionType)), ++ "on connection:", connectionID, ++ "since", ACTION_TYPE_STR(actionsIter->actionType), ++ "is active; triggering mainConnection:", iter1->mainConnectionID); ++ return am::E_ABORTED; ++ } ++ } ++ } ++ } ++ } ++ } ++ ++ } ++ //if action is inverse then: ++ // - some pending connections might become active ++ ++ //iterate sub-connections for main connection to find out the targetElement ++ std::vector<am_Connection_s>::iterator iter = mConnectionID2Subconnections[connectionID].begin(); ++ std::vector<am_Connection_s>::iterator iterEnd = mConnectionID2Subconnections[connectionID].end(); ++ ++ for (; iter < iterEnd; iter++) ++ { ++ am_Connection_s currentConn = *iter; ++ am_Source_s currentSrc; ++ ++ //find out the domain of currentConn. ++ this->getSourceInfo(currentConn.sourceID, currentSrc); ++ ++ if (action.targetDomain == currentSrc.domainID) ++ { ++ ret = applyActionOnSubConnection( ++ connectionID, ++ currentConn, ++ action, ++ interrupt, ++ inverse); ++ } ++ } ++ ++ ++ iter1 = existingConnections.begin(); ++ iter2 = existingConnections.begin(); ++ //apply existing action excluded by currently disappearing action ++ for (; iter1 < iter1End; ++iter1) ++ { ++ std::map<am_mainConnectionID_t, ++ std::vector<am_ConnectAction_s> > connectionID2Actions = ++ this->mConnectionID2Actions[iter1->mainConnectionID]; ++ ++ iter2 = existingConnections.begin(); ++ for (; iter2 < iter2End; ++iter2) ++ { ++ std::vector<am_ConnectAction_s>::iterator actionsIter ++ = connectionID2Actions[iter2->mainConnectionID].begin(); ++ std::vector<am_ConnectAction_s>::iterator actionsIterEnd ++ = connectionID2Actions[iter2->mainConnectionID].end(); ++ ++ for (; actionsIter < actionsIterEnd; actionsIter++) ++ { ++ if (action.weekCompare(*actionsIter)) ++ { ++ if (inverse) ++ { ++ if (actionsIter->actionType > action.actionType) ++ { ++ applyActionOnConnection(iter2->mainConnectionID, *actionsIter, false, false); ++ } ++ } ++ } ++ } ++ } ++ } ++ ++ return ret; ++} ++ ++am_Error_e ControlSenderPlugin::applyActionOnSubConnection( ++ am_mainConnectionID_t connectionID, ++ am_Connection_s subConnection, ++ am_ConnectAction_s &action, ++ bool interrupt, ++ bool inverse) ++{ ++ am_Handle_s h; ++ am_ActionType_e actionType = inverse ? ++ am_ConnectAction_s::inverseAction(action.actionType) : ++ action.actionType; ++ ++ logInfo("Apply action", ACTION_TYPE_STR(actionType), ++ "on sub-connectionID:", subConnection.connectionID, ++ "[sourceID:", subConnection.sourceID, ++ ", sinkID:", subConnection.sinkID, "]"); ++ ++ //TODO: is this main connection suspended or restored? ++ //TODO: - should check all sub-connections - current implementation is wrong overall connection state is decided by one sub-connection ++ ++ switch (actionType) ++ { ++ case AT_CONNECT: ++ { ++ logInfo("Apply connect: sub-connectionID:", ++ subConnection.connectionID, ++ "[sourceID:", subConnection.sourceID, ++ ", sinkID:", subConnection.sinkID, "]"); ++ ++ if (subConnection.sourceID == 0 || ++ subConnection.sinkID == 0) ++ { ++ logError("Can not apply re-connect sink or source not defined"); ++ return E_NOT_POSSIBLE; ++ } ++ mControlReceiveInterface->connect(h, ++ subConnection.connectionID, ++ subConnection.connectionFormat, ++ subConnection.sourceID, ++ subConnection.sinkID); ++ ++ am_Connection_s tempConnection; ++ tempConnection.sinkID = subConnection.sinkID; ++ tempConnection.sourceID = subConnection.sourceID; ++ tempConnection.connectionID = subConnection.connectionID; ++ ++ //this old connection shall be replaced with a new one. ++ this->mConnectionID2Subconnections[connectionID].push_back( ++ tempConnection ++ ); ++ ++ //TODO: this new connection id shall replace the subconnection. ++ ++ this->mControlReceiveInterface-> ++ changeMainConnectionStateDB( ++ connectionID, ++ CS_CONNECTED); ++ break; ++ } ++ case AT_DISCONNECT: ++ { ++ this->mControlReceiveInterface->changeMainConnectionStateDB( ++ connectionID, CS_DISCONNECTED); ++ ++ logInfo("Apply disconnect: sub-connectionID:", ++ subConnection.connectionID, ++ "[sourceID:", subConnection.sourceID, ++ ", sinkID:", subConnection.sinkID, "]"); ++ ++ mControlReceiveInterface->disconnect(h, subConnection.connectionID); ++ ++ this->mControlReceiveInterface-> ++ changeMainConnectionStateDB( ++ connectionID, ++ interrupt ? CS_SUSPENDED : CS_DISCONNECTED); ++ ++ if (!interrupt) ++ { ++ this->mControlReceiveInterface-> ++ removeMainConnectionDB(connectionID); ++ } ++ break; ++ } ++ case AT_VOLUME_DECREASE: ++ { ++ if (action.targetElement == ATG_SOURCE) ++ { ++ logInfo("Apply decrease volume: sourceID:", subConnection.sourceID); ++ mControlReceiveInterface->setSourceVolume( ++ h, subConnection.sourceID, 50, am::RAMP_GENIVI_NO_PLOP, 0); ++ } ++ if (action.targetElement == ATG_SINK) ++ { ++ logInfo("Apply decrease volume: sinkID:", subConnection.sinkID); ++ mControlReceiveInterface->setSinkVolume( ++ h, subConnection.sinkID, 50, am::RAMP_GENIVI_NO_PLOP, 0); ++ } ++ } ++ break; ++ ++ case AT_VOLUME_INCREASE: ++ { ++ if (action.targetElement == ATG_SOURCE) ++ { ++ logInfo("Apply increase volume: sourceID:", subConnection.sourceID); ++ mControlReceiveInterface->setSourceVolume( ++ h, subConnection.sourceID, 100, am::RAMP_GENIVI_NO_PLOP, 0); ++ } ++ if (action.targetElement == ATG_SINK) ++ { ++ logInfo("Apply increase volume: sinkID:", subConnection.sinkID); ++ mControlReceiveInterface->setSinkVolume( ++ h, subConnection.sinkID, 100, am::RAMP_GENIVI_NO_PLOP, 0); ++ } ++ break; ++ } ++ case AT_MUTE: ++ { ++ if (action.targetElement == ATG_SOURCE) ++ { ++ logInfo("Apply mute: sourceID:", subConnection.sourceID); ++ mControlReceiveInterface->setSourceVolume( ++ h, subConnection.sourceID, 0, am::RAMP_GENIVI_NO_PLOP, 0); ++ } ++ if (action.targetElement == ATG_SINK) ++ { ++ //TODO: replace setVolume(0) with a real setMute function ++ logInfo("Apply mute: sinkID:", subConnection.sinkID); ++ mControlReceiveInterface->setSinkVolume( ++ h, subConnection.sinkID, 0, am::RAMP_GENIVI_NO_PLOP, 0); ++ } ++ ++ this->mControlReceiveInterface-> ++ changeMainConnectionStateDB( ++ connectionID, ++ CS_SUSPENDED); ++ break; ++ } ++ case AT_UNMUTE: ++ { ++ if (action.targetElement == ATG_SOURCE) ++ { ++ logInfo("Apply unmute: sourceID:", subConnection.sourceID); ++ mControlReceiveInterface->setSourceVolume( ++ h, subConnection.sourceID, 100, am::RAMP_GENIVI_NO_PLOP, 0); ++ } ++ if (action.targetElement == ATG_SINK) ++ { ++ //TODO: replace setVolume(100) with a real setMute function ++ logInfo("Apply unmute: sinkID:", subConnection.sinkID); ++ mControlReceiveInterface->setSinkVolume( ++ h, subConnection.sinkID, 100, am::RAMP_GENIVI_NO_PLOP, 0); ++ } ++ ++ this->mControlReceiveInterface-> ++ changeMainConnectionStateDB( ++ connectionID, ++ CS_CONNECTED); ++ } ++ ++ //TODO: case for PAUSE/RESUME ++ default: ++ break; ++ }//end switch (mixingRules[i].actions[j].actionType ++ return E_OK; ++} ++ ++am_Error_e ControlSenderPlugin::hookUserSetMainSinkSoundProperty(const am_sinkID_t sinkID, const am_MainSoundProperty_s & soundProperty) ++{ ++ (void) sinkID; ++ (void) soundProperty; ++ return E_NOT_USED; ++} ++ ++am_Error_e ControlSenderPlugin::hookUserSetMainSourceSoundProperty(const am_sourceID_t sourceID, const am_MainSoundProperty_s & soundProperty) ++{ ++ (void) sourceID; ++ (void) soundProperty; ++ return E_NOT_USED; ++} ++ ++am_Error_e ControlSenderPlugin::hookUserSetSystemProperty(const am_SystemProperty_s & property) ++{ ++ (void) property; ++ return E_NOT_USED; ++} ++ ++am_Error_e ControlSenderPlugin::hookUserVolumeChange(const am_sinkID_t SinkID, const am_mainVolume_t newVolume) ++{ ++ am_Handle_s h; ++ return this->mControlReceiveInterface->setSinkVolume( ++ h, SinkID, newVolume, am::RAMP_GENIVI_NO_PLOP, 0); ++} ++ ++am_Error_e ControlSenderPlugin::hookUserVolumeStep(const am_sinkID_t SinkID, const int16_t increment) ++{ ++ (void) SinkID; ++ (void) increment; ++ return E_NOT_USED; ++} ++ ++am_Error_e ControlSenderPlugin::hookUserSetSinkMuteState(const am_sinkID_t sinkID, const am_MuteState_e muteState) ++{ ++ (void) sinkID; ++ (void) muteState; ++ return E_NOT_USED; ++} ++ ++am_Error_e ControlSenderPlugin::hookSystemRegisterDomain(const am_Domain_s & domainData, am_domainID_t & domainID) ++{ ++ am_Error_e retVal = mControlReceiveInterface->enterDomainDB(domainData, domainID); ++ if (retVal == E_OK) { ++ this->mNames2Domains[domainData.name] = domainData; ++ this->mNames2Domains[domainData.name].domainID = domainID; ++ } ++ logInfo("Register Domain: name:" , domainData.name, ", domainId:", domainID); ++ return retVal; ++} ++ ++am_Error_e ControlSenderPlugin::hookSystemDeregisterDomain(const am_domainID_t domainID) ++{ ++ (void) domainID; ++ return E_NOT_USED; ++} ++ ++void ControlSenderPlugin::hookSystemDomainRegistrationComplete(const am_domainID_t domainID) ++{ ++/* ++ * refresh config after each domain registration. ++ * elements already registered will not register again ++*/ ++ logInfo("Domain registration completed", domainID); ++ loadConfig(); ++} ++ ++am_Error_e ControlSenderPlugin::hookSystemRegisterSink(const am_Sink_s & sinkData, am_sinkID_t & sinkID) ++{ ++ logInfo("Register Sink: name:" , sinkData.name, ", sinkId:", sinkID); ++ am_Error_e retVal = mControlReceiveInterface->enterSinkDB(sinkData, sinkID); ++ if (retVal == E_OK) { ++ this->mNames2Sinks[sinkData.name] = sinkData; ++ this->mNames2Sinks[sinkData.name].sinkID = sinkID; ++ } ++ logInfo("Register Sink: name:" , sinkData.name, ", sinkId:", sinkID); ++ return retVal; ++} ++ ++am_Error_e ControlSenderPlugin::hookSystemDeregisterSink(const am_sinkID_t sinkID) ++{ ++ (void) sinkID; ++ return E_NOT_USED; ++} ++ ++am_Error_e ControlSenderPlugin::hookSystemRegisterSource(const am_Source_s & sourceData, am_sourceID_t & sourceID) ++{ ++ am_Error_e retVal = mControlReceiveInterface->enterSourceDB(sourceData, sourceID); ++ if (retVal == E_OK) ++ { ++ this->mNames2Sources[sourceData.name] = sourceData; ++ this->mNames2Sources[sourceData.name].sourceID = sourceID; ++ } ++ logInfo("Register Source: name:" , sourceData.name, ", sourceId:", sourceID); ++ return retVal; ++} ++ ++am_Error_e ControlSenderPlugin::hookSystemDeregisterSource(const am_sourceID_t sourceID) ++{ ++ (void) sourceID; ++ return E_NOT_USED; ++} ++ ++am_Error_e ControlSenderPlugin::hookSystemRegisterGateway(const am_Gateway_s & gatewayData, am_gatewayID_t & gatewayID) ++{ ++ (void) gatewayData; ++ (void) gatewayID; ++ return E_NOT_USED; ++} ++ ++am_Error_e ControlSenderPlugin::hookSystemDeregisterGateway(const am_gatewayID_t gatewayID) ++{ ++ (void) gatewayID; ++ return E_NOT_USED; ++} ++ ++am_Error_e ControlSenderPlugin::hookSystemRegisterCrossfader(const am_Crossfader_s & crossfaderData, am_crossfaderID_t & crossfaderID) ++{ ++ (void) crossfaderData; ++ (void) crossfaderID; ++ return E_NOT_USED; ++} ++ ++am_Error_e ControlSenderPlugin::hookSystemDeregisterCrossfader(const am_crossfaderID_t crossfaderID) ++{ ++ (void) crossfaderID; ++ return E_NOT_USED; ++} ++ ++void ControlSenderPlugin::hookSystemSinkVolumeTick(const am_Handle_s handle, const am_sinkID_t sinkID, const am_volume_t volume) ++{ ++ (void) handle; ++ (void) sinkID; ++ (void) volume; ++} ++ ++void ControlSenderPlugin::hookSystemSourceVolumeTick(const am_Handle_s handle, const am_sourceID_t sourceID, const am_volume_t volume) ++{ ++ (void) handle; ++ (void) sourceID; ++ (void) volume; ++} ++ ++void ControlSenderPlugin::hookSystemInterruptStateChange(const am_sourceID_t sourceID, const am_InterruptState_e interruptState) ++{ ++ (void) sourceID; ++ (void) interruptState; ++} ++ ++void ControlSenderPlugin::hookSystemSinkAvailablityStateChange(const am_sinkID_t sinkID, const am_Availability_s & availability) ++{ ++ (void) sinkID; ++ (void) availability; ++} ++ ++void ControlSenderPlugin::hookSystemSourceAvailablityStateChange(const am_sourceID_t sourceID, const am_Availability_s & availability) ++{ ++ (void) sourceID; ++ (void) availability; ++} ++ ++void ControlSenderPlugin::hookSystemDomainStateChange(const am_domainID_t domainID, const am_DomainState_e state) ++{ ++ (void) domainID; ++ (void) state; ++} ++ ++void ControlSenderPlugin::hookSystemReceiveEarlyData(const std::vector<am_EarlyData_s> & data) ++{ ++ (void) data; ++} ++ ++void ControlSenderPlugin::hookSystemSpeedChange(const am_speed_t speed) ++{ ++ (void) speed; ++} ++ ++void ControlSenderPlugin::hookSystemTimingInformationChanged(const am_mainConnectionID_t mainConnectionID, const am_timeSync_t time) ++{ ++ (void) mainConnectionID; ++ (void) time; ++} ++ ++void ControlSenderPlugin::cbAckConnect(const am_Handle_s handle, const am_Error_e errorID) ++{ ++ (void) handle; ++ (void) errorID; ++} ++ ++void ControlSenderPlugin::cbAckDisconnect(const am_Handle_s handle, const am_Error_e errorID) ++{ ++ (void) handle; ++ (void) errorID; ++} ++ ++void ControlSenderPlugin::cbAckCrossFade(const am_Handle_s handle, const am_HotSink_e hostsink, const am_Error_e error) ++{ ++ (void) handle; ++ (void) hostsink; ++ (void) error; ++} ++ ++void ControlSenderPlugin::cbAckSetSinkVolumeChange(const am_Handle_s handle, const am_volume_t volume, const am_Error_e error) ++{ ++ (void) error; ++ (void) volume; ++ (void) handle; ++} ++ ++void ControlSenderPlugin::cbAckSetSourceVolumeChange(const am_Handle_s handle, const am_volume_t voulme, const am_Error_e error) ++{ ++ (void) error; ++ (void) voulme; ++ (void) handle; ++} ++ ++void ControlSenderPlugin::cbAckSetSourceState(const am_Handle_s handle, const am_Error_e error) ++{ ++ (void) error; ++ (void) handle; ++} ++ ++void ControlSenderPlugin::cbAckSetSourceSoundProperty(const am_Handle_s handle, const am_Error_e error) ++{ ++ (void) error; ++ (void) handle; ++} ++ ++void ControlSenderPlugin::cbAckSetSinkSoundProperty(const am_Handle_s handle, const am_Error_e error) ++{ ++ (void) error; ++ (void) handle; ++} ++ ++void ControlSenderPlugin::cbAckSetSourceSoundProperties(const am_Handle_s handle, const am_Error_e error) ++{ ++ (void) error; ++ (void) handle; ++} ++ ++void ControlSenderPlugin::cbAckSetSinkSoundProperties(const am_Handle_s handle, const am_Error_e error) ++{ ++ (void) error; ++ (void) handle; ++} ++ ++am_Error_e ControlSenderPlugin::getConnectionFormatChoice(const am_sourceID_t sourceID, const am_sinkID_t sinkID, const am_Route_s listRoute, const std::vector<am_CustomConnectionFormat_t> listPossibleConnectionFormats, std::vector<am_CustomConnectionFormat_t> & listPrioConnectionFormats) ++{ ++ (void) sourceID; ++ (void) sinkID; ++ (void) listRoute; ++ //ok, this is cheap. In a real product you have your preferences, right? ++ listPrioConnectionFormats = listPossibleConnectionFormats; ++ return (E_OK); ++} ++ ++void ControlSenderPlugin::getInterfaceVersion(std::string & version) const ++{ ++ version = "3.0"; ++} ++ ++void ControlSenderPlugin::confirmCommandReady(const am_Error_e error) ++{ ++ (void) error; ++ logInfo("ControlSenderPlugin got Routing Ready confirmed"); ++} ++ ++void ControlSenderPlugin::confirmRoutingReady(const am_Error_e error) ++{ ++ (void) error; ++ logInfo("ControlSenderPlugin got Command Ready confirmed"); ++} ++ ++void ControlSenderPlugin::confirmCommandRundown(const am_Error_e error) ++{ ++ (void) error; ++ logInfo("ControlSenderPlugin got Routing Rundown confirmed"); ++} ++ ++void ControlSenderPlugin::confirmRoutingRundown(const am_Error_e error) ++{ ++ (void) error; ++ logInfo("ControlSenderPlugin got Command Rundown confirmed"); ++} ++ ++am_Error_e ControlSenderPlugin::getStaticRoute( ++ const bool onlyfree, ++ const am_sourceID_t sourceID, ++ const am_sinkID_t sinkID, ++ std::vector<am_Route_s>& returnList) ++{ ++ std::vector<am_Route_s>::iterator iter = this->mStaticRoutes.begin(); ++ std::vector<am_Route_s>::iterator iterEnd = this->mStaticRoutes.end(); ++ for (; iter < iterEnd; iter++) ++ { ++ if (iter->sourceID == sourceID && ++ iter->sinkID == sinkID) ++ { ++ returnList.push_back(*iter); ++ break; ++ } ++ } ++ return E_OK; ++} ++ ++/** ++ * Load audio policy and priority controller config. This SHOULD be called after ++ * all the plugins were loaded and initialized. ++ */ ++am_Error_e ControlSenderPlugin::loadConfig() ++{ ++ //get current library path - search: /proc/< getpid() >/maps ++ char proc_maps_file_name[256]; ++ char line[4096]; ++ char lib_name[256]; ++ char *tmp; ++ pid_t pid = getpid(); ++ int configGroup; /* 0-SourceClass,1-SinkClass,2-Gw,3-Route,4-Mixing */ ++ ++ sprintf(proc_maps_file_name, "/proc/%d/maps", pid); ++ FILE *proc_maps = fopen(proc_maps_file_name, "r"); ++ ++ lib_name[0] = '\0'; ++ ++ while (!feof(proc_maps)) ++ { ++ char *cnt = fgets(line, 4095, 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, LIBNAMECTL) >= lib_name)) ++ { ++ strcpy(strrchr(lib_name, '/') + 1, CFGNAMECTL); ++ logInfo("CONTROLER - Load config file:", lib_name); ++ ++ break; ++ } ++ } ++ ++ fclose(proc_maps); ++ ++ if (lib_name[0] == 0) ++ { ++ logError("Error loading config file:", lib_name); ++ return E_UNKNOWN; ++ } ++ ++ FILE *config = fopen(lib_name, "r"); ++ ++ while (!feof(config)) ++ { ++ fgets(line, 4095, config); ++ ++ if (strlen(line) == 0 || line[0] == '#') continue; ++ ++ char *tmp = strtok(line, "|\n:>"); ++ if (tmp == NULL) continue; ++/* ++ * Set the current config group ++ */ ++ if (strcmp("[SourceClass]", tmp) == 0) ++ { ++ configGroup = 0; ++ continue; ++ } ++ if (strcmp("[SinkClass]", tmp) == 0) ++ { ++ configGroup = 1; ++ continue; ++ } ++ if (strcmp("[Gateway]", tmp) == 0) ++ { ++ configGroup = 2; ++ continue; ++ } ++ if (strcmp("[Route]", tmp) == 0) ++ { ++ configGroup = 3; ++ continue; ++ } ++ if (strcmp("[Mixing]", tmp) == 0) ++ { ++ configGroup = 4; ++ continue; ++ } ++ switch (configGroup) /* 0-SourceClass,1-SinkClass,2-Route,3-Mixing */ ++ { ++ case 0: ++/* ++ * SourceClass format: NAME|TYPE ++ * TYPE=Main|Interrupt ++ */ ++ { ++ am_SourceClass_s l_newSourceClass; ++ l_newSourceClass.sourceClassID = 0; ++ l_newSourceClass.name = std::string(tmp); ++ ++ if (this->mNames2SourceClasses[ ++ l_newSourceClass.name ++ ].sourceClassID != 0) ++ { ++ //source class already added ++ break; ++ } ++ ++ am_ClassProperty_s newClassProperty; ++ newClassProperty.classProperty = CP_GENIVI_SOURCE_TYPE; ++ ++ char *tmp = strtok(NULL, "|\n");//type ++ if (tmp != NULL && (strcmp(tmp, "Interrupt") == 0)) ++ { ++ newClassProperty.value = SOURCE_TYPE_INTERRUPT; ++ } ++ if (tmp != NULL && (strcmp(tmp, "Main") == 0)) ++ { ++ newClassProperty.value = SOURCE_TYPE_MAIN; ++ } ++ ++ l_newSourceClass.listClassProperties.push_back(newClassProperty); ++ ++ am_Error_e e = this->mControlReceiveInterface->enterSourceClassDB( ++ l_newSourceClass.sourceClassID, ++ l_newSourceClass); ++ logInfo("ControlSenderPlugin::loadConfig: Source registered?? '", l_newSourceClass.name , ++ "', id=", l_newSourceClass.sourceClassID, ", error=", e); ++ if (e != E_OK) break; ++ ++ this->mNames2SourceClasses[l_newSourceClass.name] = ++ l_newSourceClass; ++ ++ break; ++ }//end case 0 - source class ++ case 1: ++/* ++ * SinkClass: NAME|TYPE ++ * TYPE=Main; currently sink class type is unused: ++ * might be - audio output; recording app; phone sink ++ */ ++ { ++ am_SinkClass_s l_newSinkClass; ++ l_newSinkClass.sinkClassID = 0; ++ l_newSinkClass.name = std::string(tmp); ++ ++ if (this->mNames2SinksClasses[ ++ l_newSinkClass.name ++ ].sinkClassID != 0) ++ { ++ //sink class already added ++ break; ++ } ++ ++ am_ClassProperty_s newClassProperty; ++ newClassProperty.classProperty = CP_GENIVI_SOURCE_TYPE; ++ ++ char *tmp = strtok(line, "|\n");//type ++ if (tmp != NULL && strcmp(tmp, "Main")) ++ { ++ newClassProperty.value = SINK_TYPE_MAIN; ++ } ++ ++ l_newSinkClass.listClassProperties.push_back(newClassProperty); ++ ++ am_Error_e e = mControlReceiveInterface->enterSinkClassDB( ++ l_newSinkClass, ++ l_newSinkClass.sinkClassID); ++ ++ if (e != E_OK) break; ++ ++ this->mNames2SinksClasses[l_newSinkClass.name] = ++ l_newSinkClass; ++ ++ logInfo("New sink class name:", l_newSinkClass.name, "sinkClassId:", l_newSinkClass.sinkClassID); ++ std::cout << "New sink class name:" << l_newSinkClass.name << "sinkClassId:" << l_newSinkClass.sinkClassID << "\n"; ++ break; ++ }//end case 1 - sink class ++ case 2: ++/* ++ * Gateway format: NAME:SOURCE>SINK ++ * SOURCE and SINK must be the name of an already registered Source/Sink ++ */ ++ { ++ am_Gateway_s l_newGateway; ++ l_newGateway.gatewayID = 0; ++ l_newGateway.name = std::string(tmp); ++ ++ if (this->mNames2Gateways[ ++ l_newGateway.name ++ ].gatewayID!= 0) ++ { ++ //sink class already added ++ break; ++ } ++ ++ tmp = strtok(NULL, ">"); ++ l_newGateway.sinkID = this->mNames2Sinks[std::string(tmp)].sinkID; ++ l_newGateway.domainSinkID = this->mNames2Sinks[std::string(tmp)].domainID; ++ l_newGateway.listSinkFormats = this->mNames2Sinks[std::string(tmp)].listConnectionFormats; ++ ++ tmp = strtok(NULL, "\n"); ++ l_newGateway.sourceID = this->mNames2Sources[tmp].sourceID; ++ l_newGateway.domainSourceID = this->mNames2Sources[tmp].domainID; ++ l_newGateway.listSourceFormats = this->mNames2Sources[std::string(tmp)].listConnectionFormats; ++ l_newGateway.controlDomainID = l_newGateway.domainSourceID; ++ l_newGateway.convertionMatrix.push_back(true); ++ ++ if (l_newGateway.sinkID == 0 || ++ l_newGateway.sourceID == 0 || ++ l_newGateway.domainSourceID == 0 || ++ l_newGateway.domainSinkID == 0) break; ++ ++ ++ am_Error_e e = this->mControlReceiveInterface->enterGatewayDB( ++ l_newGateway, ++ l_newGateway.gatewayID); ++ if (e != E_OK) break; ++ ++ this->mNames2Gateways[l_newGateway.name] = l_newGateway; ++ logInfo("New sink class name:", l_newGateway.name, "sinkId:", ++ l_newGateway.sinkID, "sourceID:", l_newGateway.sourceID); ++ break; ++ }//end case 2 - gateway ++ case 3: ++/* ++ * Route format: SourceClass>Sink>Source>...Sink>Source>Sink ++ * : SourceClass>Sink>Source>Gateway>Sink...>Source>Sink ++ */ ++ { ++ //first element - source class ++ am_SourceClass_s routeSrcClass = ++ this->mNames2SourceClasses[std::string(tmp)]; ++ //Do nothing if source class is not registered yet ++ if (routeSrcClass.sourceClassID == 0) break; ++ ++ std::string prevElement; ++ bool prevElementWasSrc = true; ++ am_sourceID_t prevSrcID = 0; ++ am_sourceID_t prevSinkID = 0; ++ std::vector<am_RoutingElement_s> route; ++ ++ while (tmp != NULL && *tmp != 0) ++ { ++ tmp = strtok(NULL, "\n>"); ++ if (tmp == NULL || *tmp == 0) break; ++ ++ std::string name(tmp); ++ if (prevElementWasSrc) ++ { ++ //next element should be a sink or a gw ++ am_Sink_s sink = this->mNames2Sinks[name]; ++ if (sink.sinkID) ++ { ++ am_RoutingElement_s newRouteElement; ++ newRouteElement.sourceID = prevSrcID; ++ newRouteElement.sinkID = sink.sinkID; ++ newRouteElement.domainID= sink.domainID; ++ newRouteElement.connectionFormat = am::CF_GENIVI_STEREO; ++ //TODO: connection format should not be hardcoded ++ route.push_back(newRouteElement); ++ prevSinkID = newRouteElement.sinkID; ++ prevElementWasSrc = false; ++ } ++ else ++ { ++ am_Gateway_s gw = this->mNames2Gateways[name]; ++ if (gw.gatewayID) ++ { ++ am_RoutingElement_s newRouteElement; ++ newRouteElement.sourceID = prevSrcID; ++ newRouteElement.sinkID = gw.sinkID; ++ newRouteElement.domainID= gw.domainSinkID; ++ newRouteElement.connectionFormat = am::CF_GENIVI_STEREO; ++ //TODO: connection format should not be hardcoded ++ route.push_back(newRouteElement); ++ ++ prevSrcID = gw.sourceID; ++ prevElementWasSrc = true; ++ } ++ else ++ { ++ break; ++ } ++ } ++ } ++ else ++ {//prevElementWasSink ++ am_Source_s source = this->mNames2Sources[name]; ++ if (source.sourceID) ++ { ++ prevElementWasSrc = true; ++ prevSrcID = source.sourceID; ++ } ++ else ++ { ++ break; ++ } ++ ++ } ++ } ++ ++ //the route should end with a Sink ++ if (!prevElementWasSrc) ++ { ++ std::vector<am_Source_s> allSources; ++ this->mControlReceiveInterface->getListSources(allSources); ++ for (unsigned int i = 0; i < allSources.size(); i++) ++ { ++ if (allSources[i].sourceClassID == ++ routeSrcClass.sourceClassID) ++ { ++ am_Route_s newRoute; ++ newRoute.sinkID = prevSinkID; ++ newRoute.sourceID = allSources[i].sourceID; ++ newRoute.route = route; ++ ++ am_RoutingElement_s firstRouteElement = newRoute.route[0]; ++ firstRouteElement.sourceID = newRoute.sourceID; ++ newRoute.route[0] = firstRouteElement; ++ ++ //check if route already exists ++ bool routeAlreadyExists = false; ++ for (unsigned int m = 0; m < this->mStaticRoutes.size(); m++) ++ { ++ if (this->mStaticRoutes[m].sourceID == newRoute.sourceID && ++ this->mStaticRoutes[m].sinkID == newRoute.sinkID) ++ { ++ routeAlreadyExists = true; ++ } ++ } ++ if (!routeAlreadyExists) ++ { ++ this->mStaticRoutes.push_back(newRoute); ++ } ++ } ++ } ++ } ++ else ++ { ++ logError("Wrong route format:", line); ++ } ++ break; ++ }//end case 3 - route ++ case 4: ++/* ++ * Mixing: SourceClass>ACTIONS ++ */ ++ { ++ am_SourceClass_s mixingSrcClass = ++ this->mNames2SourceClasses[std::string(tmp)]; ++ if (mixingSrcClass.sourceClassID == 0) break; ++ std::vector<am_ConnectAction_s> actions; ++ while (tmp != NULL && *tmp != 0) ++ { ++ am_ConnectAction_s newAction; ++ tmp = strtok(NULL, "\n>:|");//activeSourceClass ++ if (tmp == NULL) ++ { ++ logError("Wrong active source class"); ++ break; ++ } ++ ++ am_SourceClass_s activeSrcClass = ++ this->mNames2SourceClasses[std::string(tmp)]; ++ if (activeSrcClass.sourceClassID == 0) ++ { ++ logError("Wrong active source class - no ID"); ++ break; ++ } ++ ++ tmp = strtok(NULL, "\n>:|");//activeSink ++ if (tmp == NULL) ++ { ++ logError("Wrong active sink"); ++ break; ++ } ++ ++ am_Sink_s activeSink = ++ this->mNames2Sinks[std::string(tmp)]; ++ if (activeSink.sinkID == 0) ++ { ++ logError("Wrong active sink - no ID"); ++ break; ++ } ++ ++ tmp = strtok(NULL, "\n>:|");//actionType ++ if (tmp == NULL) ++ { ++ logError("Wrong action type"); ++ break; ++ } ++ ++ am_ActionType_e actionType = AT_DISCONNECT; ++ if (strcmp("Mute", tmp) == 0) ++ actionType = AT_MUTE; ++ else if (strcmp("Unmute", tmp) == 0) ++ actionType = AT_UNMUTE; ++ else if (strcmp("Pause", tmp) == 0) ++ actionType = AT_PAUSE; ++ else if (strcmp("Resume", tmp) == 0) ++ actionType = AT_RESUME; ++ else if (strcmp("DecreaseVolume", tmp) == 0) ++ actionType = AT_VOLUME_DECREASE; ++ else if (strcmp("IncreaseVolume", tmp) == 0) ++ actionType = AT_VOLUME_DECREASE; ++ else if (strcmp("Disconnect", tmp) == 0) ++ actionType = AT_DISCONNECT; ++ else ++ { ++ logError("Wrong action type - enum"); ++ break; ++ } ++ ++ tmp = strtok(NULL, "\n>:|");//targetDomain ++ if (tmp == NULL) ++ { ++ logError("Target domain is wrong"); ++ break; ++ } ++ ++ am_Domain_s targetDomain = ++ this->mNames2Domains[std::string(tmp)]; ++ ++ if (targetDomain.domainID == 0) ++ { ++ logError("Target domain is wrong - no ID"); ++ break; ++ } ++ ++ am_ActionTarget_e targetElement; ++ tmp = strtok(NULL, "\n>:|");//targetElement ++ if (strcmp("Source", tmp) == 0) ++ targetElement = ATG_SOURCE; ++ else if (strcmp("Sink", tmp) == 0) ++ targetElement = ATG_SINK; ++ else ++ { ++ logError("Target element is wrong"); ++ break; ++ } ++ ++ newAction.activeSourceClassID = activeSrcClass.sourceClassID; ++ newAction.activeSinkID = activeSink.sinkID; ++ newAction.actionType = actionType; ++ newAction.targetDomain = targetDomain.domainID; ++ newAction.targetElement = targetElement; ++ actions.push_back(newAction); ++ } ++ if (actions.size() > 0) ++ { ++ am_MixingRule_s newMixingRule; ++ newMixingRule.mixingSourceClassID = mixingSrcClass.sourceClassID; ++ newMixingRule.actions = actions; ++ //do not add duplicates ++ bool ruleAlreadyExists = false; ++ for (unsigned int n = 0; n < this->mMixingRules.size(); n++) ++ { ++ if (this->mMixingRules[n].mixingSourceClassID == newMixingRule.mixingSourceClassID) ++ { ++ //TODO: is it necessary to compare the actions, too? ++ ruleAlreadyExists = true; ++ break; ++ } ++ } ++ if (!ruleAlreadyExists) ++ { ++ this->mMixingRules.push_back(newMixingRule); ++ } ++ } ++ break; ++ }//end case 4: - mixing ++ } ++ } ++ ++ ++ std::vector<am_Route_s>::iterator iter = mStaticRoutes.begin(); ++ std::vector<am_Route_s>::iterator iterEnd = mStaticRoutes.end(); ++ for (; iter != iterEnd; iter++) ++ { ++ logInfo("{BEGIN Add route: STATRT:SourceID:", iter->sourceID, "-> END:SinkID:", iter->sinkID); ++ ++ std::vector<am_RoutingElement_s>::iterator iter1 = iter->route.begin(); ++ std::vector<am_RoutingElement_s>::iterator iter1End = iter->route.end(); ++ for (; iter1 < iter1End; iter1++) ++ { ++ logInfo("Subroute: SourceID:", iter1->sourceID, "-> SinkID:", iter1->sinkID); ++ } ++ logInfo("END Add route}"); ++ } ++ ++ std::vector<am_MixingRule_s>::iterator iter1 = mMixingRules.begin(); ++ std::vector<am_MixingRule_s>::iterator iter1End = mMixingRules.end(); ++ for (; iter1 != iter1End; iter1++) ++ { ++ logInfo("{BEGIN Add mixing rule: STATRT:mixingSourceClassID:", iter1->mixingSourceClassID); ++ ++ std::vector<am_ConnectAction_s>::iterator iter2 = iter1->actions.begin(); ++ std::vector<am_ConnectAction_s>::iterator iter2End = iter1->actions.end(); ++ for (; iter2 < iter2End; iter2++) ++ { ++ logInfo("Action: activeSourceClassID:", iter2->activeSourceClassID, ++ ";activeSinkID:", iter2->activeSinkID, ++ ";actionType:", iter2->actionType, ++ ";targetDomain:", iter2->targetDomain, ++ ";targetElement:", iter2->targetElement); ++ } ++ logInfo("END Add mixing rule}"); ++ } ++ ++ fclose(config); ++ return E_OK; ++} ++ ++am_Error_e ControlSenderPlugin::getSourceInfo( ++ const am_sourceID_t sourceID, ++ am_Source_s &sourceData) ++{ ++ std::vector<am_Source_s> listSources; ++ this->mControlReceiveInterface->getListSources(listSources); ++ std::vector<am_Source_s>::iterator iter = listSources.begin(); ++ std::vector<am_Source_s>::iterator iterEnd = listSources.end(); ++ for (; iter < iterEnd; iter++) ++ { ++ if (iter->sourceID == sourceID) ++ { ++ sourceData = *iter; ++ return E_OK; ++ } ++ } ++ return E_NON_EXISTENT; ++} ++ ++am_Error_e ControlSenderPlugin::hookSystemUpdateSink(const am_sinkID_t sinkID, const am_sinkClass_t sinkClassID, const std::vector<am_SoundProperty_s>& listSoundProperties, const std::vector<am_CustomConnectionFormat_t>& listConnectionFormats, const std::vector<am_MainSoundProperty_s>& listMainSoundProperties) ++{ ++ (void) sinkID; ++ (void) sinkClassID; ++ (void) listMainSoundProperties; ++ (void) listConnectionFormats; ++ (void) listSoundProperties; ++ return (E_NOT_USED); ++} ++ ++am_Error_e ControlSenderPlugin::hookSystemUpdateSource(const am_sourceID_t sourceID, const am_sourceClass_t sourceClassID, const std::vector<am_SoundProperty_s>& listSoundProperties, const std::vector<am_CustomConnectionFormat_t>& listConnectionFormats, const std::vector<am_MainSoundProperty_s>& listMainSoundProperties) ++{ ++ (void) sourceID; ++ (void) sourceClassID; ++ (void) listSoundProperties; ++ (void) listMainSoundProperties; ++ (void) listConnectionFormats; ++ return (E_NOT_USED); ++} ++ ++am_Error_e ControlSenderPlugin::hookSystemUpdateGateway(const am_gatewayID_t gatewayID, const std::vector<am_CustomConnectionFormat_t>& listSourceConnectionFormats, const std::vector<am_CustomConnectionFormat_t>& listSinkConnectionFormats, const std::vector<bool>& convertionMatrix) ++{ ++ (void) gatewayID; ++ (void) listSourceConnectionFormats; ++ (void) listSinkConnectionFormats; ++ (void) convertionMatrix; ++ return (E_NOT_USED); ++} ++ ++void ControlSenderPlugin::cbAckSetVolumes(const am_Handle_s handle, const std::vector<am_Volumes_s>& listVolumes, const am_Error_e error) ++{ ++ (void) handle; ++ (void) listVolumes; ++ (void) error; ++} ++ ++void ControlSenderPlugin::cbAckSetSinkNotificationConfiguration(const am_Handle_s handle, const am_Error_e error) ++{ ++ (void) handle; ++ (void) error; ++} ++ ++ ++void ControlSenderPlugin::cbAckSetSourceNotificationConfiguration(const am_Handle_s handle, const am_Error_e error) ++{ ++ (void) handle; ++ (void) error; ++} ++ ++ ++void ControlSenderPlugin::hookSinkNotificationDataChanged(const am_sinkID_t sinkID, const am_NotificationPayload_s& payload) ++{ ++ (void) sinkID; ++ (void) payload; ++} ++ ++void ControlSenderPlugin::hookSourceNotificationDataChanged(const am_sourceID_t sourceID, const am_NotificationPayload_s& payload) ++{ ++ (void) sourceID; ++ (void) payload; ++} ++ ++am_Error_e ControlSenderPlugin::hookUserSetMainSinkNotificationConfiguration(const am_sinkID_t sinkID, const am_NotificationConfiguration_s& notificationConfiguration) ++{ ++ (void) sinkID; ++ (void) notificationConfiguration; ++ return (E_NOT_USED); ++} ++ ++am_Error_e ControlSenderPlugin::hookUserSetMainSourceNotificationConfiguration(const am_sourceID_t sourceID, const am_NotificationConfiguration_s& notificationConfiguration) ++{ ++ (void) sourceID; ++ (void) notificationConfiguration; ++ return (E_NOT_USED); ++} ++ ++void ControlSenderPlugin::hookSystemNodeStateChanged(const NsmNodeState_e NodeStateId) ++{ ++ (void) NodeStateId; ++ //here you can process informations about the notestate ++} ++ ++void ControlSenderPlugin::hookSystemNodeApplicationModeChanged(const NsmApplicationMode_e ApplicationModeId) ++{ ++ (void) ApplicationModeId; ++} ++ ++void ControlSenderPlugin::hookSystemSessionStateChanged(const std::string& sessionName, const NsmSeat_e seatID, const NsmSessionState_e sessionStateID) ++{ ++ (void) sessionName; ++ (void) seatID; ++ (void) sessionStateID; ++} ++ ++NsmErrorStatus_e ControlSenderPlugin::hookSystemLifecycleRequest(const uint32_t Request, const uint32_t RequestId) ++{ ++ (void) Request; ++ (void) RequestId; ++ logInfo("CAmControlSenderBase::hookSystemLifecycleRequest request=",Request," requestID=",RequestId); ++ return (NsmErrorStatus_Error); ++} ++ +-- +1.7.9.5 + diff --git a/meta-ivi-demo/recipes-multimedia/audiomanager/audiomanager/0001-Porting-Pulse-Routing-Interface-from-AM-v1.x-to-AM-v.patch b/meta-ivi-demo/recipes-multimedia/audiomanager/audiomanager/0001-Porting-Pulse-Routing-Interface-from-AM-v1.x-to-AM-v.patch new file mode 100644 index 0000000..d01854d --- /dev/null +++ b/meta-ivi-demo/recipes-multimedia/audiomanager/audiomanager/0001-Porting-Pulse-Routing-Interface-from-AM-v1.x-to-AM-v.patch @@ -0,0 +1,2342 @@ +From 68c0d6ef9b1070505c1664fe567948a9f661da28 Mon Sep 17 00:00:00 2001 +From: Adrian Scarlat <adrian.scarlat@windriver.com> +Date: Thu, 21 Aug 2014 14:52:46 +0300 +Subject: [PATCH] Porting Pulse Routing Interface from AM v1.x to AM v3.0 + +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; +There are two configuration files that are use for the moment: + 1. libPluginRoutingInterfacePULSE.conf - configuration file for + Pulse Routing Plugin; it will be loaded at runtime by the Pulse Routing Interface; + 2. genivi-amgr.pa - configuration file for PulseAudio; + It must be used to start PulseAudio daemon on the system; + +Changed files: + CMakeLists.txt + +Added new folders: + PluginRoutingInterfacePulse/ + +Added new files: + PluginRoutingInterfacePulse/CMakeLists.txt + PluginRoutingInterfacePulse/README + PluginRoutingInterfacePulse/cmake/FindDBUS.cmake + PluginRoutingInterfacePulse/data/asound.conf + PluginRoutingInterfacePulse/data/client.conf + PluginRoutingInterfacePulse/data/daemon.conf + PluginRoutingInterfacePulse/data/genivi-amgr.pa + 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> +--- + CMakeLists.txt | 24 +- + PluginRoutingInterfacePulse/CMakeLists.txt | 120 +++ + PluginRoutingInterfacePulse/README | 11 + + PluginRoutingInterfacePulse/cmake/FindDBUS.cmake | 38 + + PluginRoutingInterfacePulse/data/asound.conf | 35 + + PluginRoutingInterfacePulse/data/client.conf | 22 + + PluginRoutingInterfacePulse/data/daemon.conf | 44 + + PluginRoutingInterfacePulse/data/genivi-amgr.pa | 68 ++ + .../data/libPluginRoutingInterfacePULSE.conf | 59 ++ + .../include/RoutingSenderMainloopPULSE.h | 97 ++ + .../include/RoutingSenderPULSE.h | 131 +++ + .../src/RoutingSenderMainloopPULSE.cpp | 610 +++++++++++++ + .../src/RoutingSenderPULSE.cpp | 923 ++++++++++++++++++++ + 13 files changed, 2176 insertions(+), 6 deletions(-) + create mode 100644 PluginRoutingInterfacePulse/CMakeLists.txt + create mode 100644 PluginRoutingInterfacePulse/README + create mode 100644 PluginRoutingInterfacePulse/cmake/FindDBUS.cmake + create mode 100644 PluginRoutingInterfacePulse/data/asound.conf + create mode 100644 PluginRoutingInterfacePulse/data/client.conf + create mode 100644 PluginRoutingInterfacePulse/data/daemon.conf + create mode 100644 PluginRoutingInterfacePulse/data/genivi-amgr.pa + create mode 100644 PluginRoutingInterfacePulse/data/libPluginRoutingInterfacePULSE.conf + create mode 100644 PluginRoutingInterfacePulse/include/RoutingSenderMainloopPULSE.h + create mode 100644 PluginRoutingInterfacePulse/include/RoutingSenderPULSE.h + create mode 100644 PluginRoutingInterfacePulse/src/RoutingSenderMainloopPULSE.cpp + create mode 100644 PluginRoutingInterfacePulse/src/RoutingSenderPULSE.cpp + +diff --git a/CMakeLists.txt b/CMakeLists.txt +index 3e70e4f..48e7ae2 100755 +--- a/CMakeLists.txt ++++ b/CMakeLists.txt +@@ -95,7 +95,15 @@ OPTION ( WITH_NSM + OPTION ( WITH_DATABASE_STORAGE + "build with sqlite as in memory storage" 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) +@@ -239,12 +247,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/PluginRoutingInterfacePulse/CMakeLists.txt b/PluginRoutingInterfacePulse/CMakeLists.txt +new file mode 100644 +index 0000000..b889f6a +--- /dev/null ++++ b/PluginRoutingInterfacePulse/CMakeLists.txt +@@ -0,0 +1,120 @@ ++############################################################################ ++# 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 ++) ++INSTALL(FILES data/client.conf ++ 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/daemon.conf ++ 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/asound.conf ++ 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/genivi-amgr.pa ++ DESTINATION "lib/${LIB_INSTALL_SUFFIX}/routing" ++ PERMISSIONS OWNER_EXECUTE OWNER_WRITE OWNER_READ GROUP_EXECUTE GROUP_READ WORLD_EXECUTE WORLD_READ ++ COMPONENT sampleplugins ++) ++ ++CONFIGURE_FILE( ${CMAKE_SOURCE_DIR}/PluginRoutingInterfacePulse/data/libPluginRoutingInterfacePULSE.conf ${PLUGINS_OUTPUT_PATH}/routing/libPluginRoutingInterfacePULSE.conf ) ++CONFIGURE_FILE( ${CMAKE_SOURCE_DIR}/PluginRoutingInterfacePulse/data/genivi-amgr.pa ${PLUGINS_OUTPUT_PATH}/routing/genivi-amgr.pa ) +diff --git a/PluginRoutingInterfacePulse/README b/PluginRoutingInterfacePulse/README +new file mode 100644 +index 0000000..7c9b082 +--- /dev/null ++++ b/PluginRoutingInterfacePulse/README +@@ -0,0 +1,11 @@ ++Description of PluginRoutingInterfacePulse ++------------------------------------------ ++- The PluginRoutingInterfacePulse is used by the AM PoC application to communicate with Pulse ++ present on the system on which the AM PoC application will be deployed; ++ ++Build intstructions ++------------------- ++- To build the Pulse Routing Plugin pass the following option to CMake: -DWITH_PULSE_ROUTING_PLUGIN=ON ++ ++ ++ +diff --git a/PluginRoutingInterfacePulse/cmake/FindDBUS.cmake b/PluginRoutingInterfacePulse/cmake/FindDBUS.cmake +new file mode 100644 +index 0000000..9a24f18 +--- /dev/null ++++ b/PluginRoutingInterfacePulse/cmake/FindDBUS.cmake +@@ -0,0 +1,38 @@ ++############################################################################ ++# 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; ++############################################################################ ++ ++FIND_PATH(DBUS_INCLUDE_DIR dbus/dbus.h /usr/include/dbus-1.0) ++FIND_PATH(DBUS_ARCH_INCLUDE_DIR dbus/dbus-arch-deps.h /usr/lib/dbus-1.0/include /usr/lib/*/dbus-1.0/include ) ++FIND_LIBRARY(DBUS_LIBRARY NAMES dbus-1 PATHS /lib) ++ ++SET( DBUS_FOUND "NO" ) ++IF(DBUS_LIBRARY) ++ SET( DBUS_FOUND "YES" ) ++ message(STATUS "Found DBUS libs: ${DBUS_LIBRARY}") ++ message(STATUS "Found DBUS include: ${DBUS_INCLUDE_DIR}") ++ message(STATUS "Found DBUS arch dependent include: ${DBUS_ARCH_INCLUDE_DIR}") ++ENDIF(DBUS_LIBRARY) ++ ++MARK_AS_ADVANCED( ++ DBUS_INCLUDE_DIR ++ DBUS_ARCH_INCLUDE_DIR ++ DBUS_LIBRARY ++) +diff --git a/PluginRoutingInterfacePulse/data/asound.conf b/PluginRoutingInterfacePulse/data/asound.conf +new file mode 100644 +index 0000000..e17f837 +--- /dev/null ++++ b/PluginRoutingInterfacePulse/data/asound.conf +@@ -0,0 +1,35 @@ ++############################################################################ ++# 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; ++############################################################################ ++pcm.pulse { ++ type pulse ++} ++ ++ctl.pulse { ++ type pulse ++} ++ ++pcm.!default { ++ type pulse ++} ++ ++ctl.!default { ++ type pulse ++} +diff --git a/PluginRoutingInterfacePulse/data/client.conf b/PluginRoutingInterfacePulse/data/client.conf +new file mode 100644 +index 0000000..c607b8d +--- /dev/null ++++ b/PluginRoutingInterfacePulse/data/client.conf +@@ -0,0 +1,22 @@ ++############################################################################ ++# 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; ++############################################################################ ++ ++default-server = 127.0.0.1 +diff --git a/PluginRoutingInterfacePulse/data/daemon.conf b/PluginRoutingInterfacePulse/data/daemon.conf +new file mode 100644 +index 0000000..0a291d4 +--- /dev/null ++++ b/PluginRoutingInterfacePulse/data/daemon.conf +@@ -0,0 +1,44 @@ ++# SPDX license identifier: LGPL-2.0 ++# ++# This file is part of PulseAudio. ++# ++# PulseAudio is free software; you can redistribute it and/or modify ++# it under the terms of the GNU Lesser General Public License as published by ++# the Free Software Foundation; either version 2 of the License, or ++# (at your option) any later version. ++# ++# PulseAudio is distributed in the hope that it will be useful, but ++# WITHOUT ANY WARRANTY; without even the implied warranty of ++# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++# General Public License for more details. ++# ++# You should have received a copy of the GNU Lesser General Public License ++# along with PulseAudio; if not, write to the Free Software ++# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 ++# USA. ++ ++## Configuration file for the PulseAudio daemon. See pulse-daemon.conf(5) for ++## more information. Default values are commented out. Use either ; or # for ++## commenting. ++# ++# 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; ++############################################################################ ++enable-shm=yes ++shm-size-bytes=8388608 #8MB # setting this 0 will use the system-default, usually 64 MiB ++exit-idle-time=-1 ++high-priority=yes ++nice-level=-11 ++realtime-scheduling=yes ++realtime-priority=5 ++resample-method=trivial ++flat-volumes=no ++default-sample-format = s16le ++default-sample-rate = 44100 ++default-sample-channels = 2 ++default-fragment-size-msec = 3 ++default-fragments = 8 +diff --git a/PluginRoutingInterfacePulse/data/genivi-amgr.pa b/PluginRoutingInterfacePulse/data/genivi-amgr.pa +new file mode 100644 +index 0000000..596ec19 +--- /dev/null ++++ b/PluginRoutingInterfacePulse/data/genivi-amgr.pa +@@ -0,0 +1,68 @@ ++############################################################################ ++# 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; ++############################################################################ ++.nofail ++ ++.fail ++ ++### Automatically restore the volume of streams and devices ++load-module module-device-restore ++load-module module-stream-restore ++load-module module-card-restore ++ ++### Automatically load driver modules depending on the hardware available ++.ifexists module-udev-detect.so ++load-module module-udev-detect ++.else ++### Use the static hardware detection module (for systems that lack udev support) ++load-module module-detect ++.endif ++ ++### Load several protocols ++.ifexists module-esound-protocol-unix.so ++load-module module-esound-protocol-unix ++.endif ++load-module module-native-protocol-unix ++ ++### Automatically restore the default sink/source when changed by the user ++### during runtime ++### NOTE: This should be loaded as early as possible so that subsequent modules ++### that look up the default sink/source get the right value ++load-module module-default-device-restore ++ ++### Automatically move streams to the default sink if the sink they are ++### connected to dies, similar for sources ++load-module module-rescue-streams ++ ++### Make sure we always have a sink around, even if it is a null sink. ++load-module module-always-sink ++ ++### Modules to allow autoloading of filters (such as echo cancellation) ++### on demand. module-filter-heuristics tries to determine what filters ++### make sense, and module-filter-apply does the heavy-lifting of ++### loading modules and rerouting streams. ++load-module module-filter-heuristics ++load-module module-filter-apply ++ ++#Make the specified sink (identified by its symbolic name) the default sink. ++set-default-sink 0 ++# Make the specified source (identified by its symbolic name) the default source. ++set-default-source 0 ++ +diff --git a/PluginRoutingInterfacePulse/data/libPluginRoutingInterfacePULSE.conf b/PluginRoutingInterfacePulse/data/libPluginRoutingInterfacePULSE.conf +new file mode 100644 +index 0000000..43fae53 +--- /dev/null ++++ b/PluginRoutingInterfacePulse/data/libPluginRoutingInterfacePULSE.conf +@@ -0,0 +1,59 @@ ++############################################################################ ++# 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 ++#################### ++## Test purpose audio sources ++Source|Sink Input|Entertainment|Media player|application.process.binary|MediaServiceGlueLogic ++Source|Sink Input|Entertainment|Browser|application.process.binary|WebRenderingGlueLogic ++Source|Sink Input|Navigation|Navigation|application.name|Test AMGRv2 TTS ++## Real audio sources ++#Source|Sink Input|Entertainment|Browser|application.process.binary|PilotHMI ++#Source|Sink Input|Entertainment|Gst Launch command line player|application.process.binary|gst-launch-0.10 ++#Source|Sink Input|Navigation|ALSA command line player|application.process.binary|aplay ++#Source|Sink Input|Entertainment|Microphone Loopback|media.icon_name|audio-input-microphone ++#Source|Source|Entertainment|Microphone|device.string|hw:0,1 ++#################### ++# Sinks ++#################### ++Sink|Sink|HifiAudio|Alsa Primary|ievice.string|hw:0,1 ++Sink|Sink|HifiAudio|Alsa Secondary|device.string|hw:0,0 ++# !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..0def61f +--- /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..05259b0 +--- /dev/null ++++ b/PluginRoutingInterfacePulse/src/RoutingSenderPULSE.cpp +@@ -0,0 +1,923 @@ ++/** ++ * 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; ++ this->loadConfig(); ++} ++ ++ ++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; ++ ++ //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); ++ ++ //printf("\nADS:Before: this->m_sinks[i].clazz = %s\n", this->m_sinks[i].clazz.c_str()); ++ //printf("\nADS:Before: this->m_sinks[i].sink.sinkClassID = %d\n", this->m_sinks[i].sink.sinkClassID); ++ this->m_routingReceiver->peekSinkClassID( ++ this->m_sinks[i].clazz, ++ this->m_sinks[i].sink.sinkClassID); ++ //this->m_sinks[i].sink.sinkClassID = 1; ++ //printf("\nADS:After: this->m_sinks[i].clazz = %s\n", this->m_sinks[i].clazz.c_str()); ++ //printf("\nADS:After: this->m_sinks[i].sink.sinkClassID = %d\n", 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 ++ //printf("ADS--> asyncConnect\n"); ++ 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 ++ } ++} +-- +1.7.9.5 + diff --git a/meta-ivi-demo/recipes-multimedia/audiomanager/audiomanager_6.1.bbappend b/meta-ivi-demo/recipes-multimedia/audiomanager/audiomanager_6.1.bbappend new file mode 100644 index 0000000..b16c4b0 --- /dev/null +++ b/meta-ivi-demo/recipes-multimedia/audiomanager/audiomanager_6.1.bbappend @@ -0,0 +1,10 @@ +FILESEXTRAPATHS_append := ":${THISDIR}/${PN}" + +SRC_URI_append = " file://0001-Porting-Pulse-Routing-Interface-from-AM-v1.x-to-AM-v.patch \ + file://0001-Porting-Pulse-Control-Interface-from-AM-v1.x-to-AM-v.patch \ + " +EXTRA_OECMAKE += "-DWITH_PULSE_ROUTING_PLUGIN=ON -DWITH_PULSE_CONTROL_PLUGIN=ON -DWITH_ENABLED_IPC=DBUS" + +FILES_${PN} += "${libdir}/audioManager/control/* \ + ${libdir}/audioManager/routing/* \ + " |