/** * Run the controller. Dequeues signals from the SW Engine's internal queue and * notifies the listeners that a signal from SW Engine has been received * * @return void */ public function run() { $timeout = $this->_options[self::OPT_TIMEOUT]; Streamwide_Engine_Logger::info('Going to main loop'); for (;;) { // Fetch a new signal from the SW Engine's queue $signal = Streamwide_Engine_Signal::dequeue(array('timeout' => $timeout)); if (false === $signal) { continue; } // Update the loggers event items Streamwide_Engine_Logger::updateLogEventItems(array($signal->getPhpId() => $signal->getParams())); // Log the received signal Streamwide_Engine_Logger::dump($signal->toArray(), 'Received event from SW Engine:'); if ($signal->getName() === Streamwide_Engine_Signal::CREATE) { // We have received a CREATE (new call), we need to create a new application to handle // the call if (false === ($application = $this->_createNewApplication($signal))) { continue; } // Add the new application to the controller's running apps storage (will call // the application's start method) $this->addApp($signal->getPhpId(), $application); } else { // We have received a signal from SW Engine, we need to notify the listeners $event = new Streamwide_Engine_Events_Event($signal->getName()); $event->setParam('signal', $signal); $this->dispatchEvent($event); } } }
/** * Saves the direction parameter (if there is one) * * @param Streamwide_Engine_Events_Event $event * @return void */ public function onConnect(Streamwide_Engine_Events_Event $event) { $okSignal = $event->getParam('signal'); $params = $okSignal->getParams(); if (array_key_exists('direction', $params)) { $this->_direction = $params['direction']; } }
/** * Kills a call leg * * @param Streamwide_Engine_Call_Leg_Abstract $callLeg * @param boolean $force * @return boolean */ public function kill(Streamwide_Engine_Call_Leg_Abstract $callLeg, $force = false) { if (false === $force && !$callLeg->isAlive()) { $this->dispatchErrorEvent(self::CALL_LEG_NOT_ALIVE_ERR_CODE); return false; } $callLegName = $callLeg->getName(); $signal = Streamwide_Engine_Signal::factory(Streamwide_Engine_Signal::KILL, $callLegName); if (false === $signal->send()) { $this->dispatchErrorEvent(self::KILL_SIGNAL_SEND_FAILURE_ERR_CODE); return false; } $callLeg->setDead(); $event = new Streamwide_Engine_Events_Event(Streamwide_Engine_Events_Event::SUCCESS); $event->setParam('callLeg', $callLeg); $this->dispatchEvent($event); return true; }
/** * Send a RING signal to the SIP call leg * * @return void */ protected function _sendRing() { // retrieve the OK signal from the event object $okSignal = $this->_event->getParam('signal'); // get a reference to the SIP call leg $sipCallLeg = $this->_widget->getLeftCallLeg(); // send the RING signal $ringSignal = Streamwide_Engine_Signal::factory(Streamwide_Engine_Signal::RING, $sipCallLeg->getName(), $okSignal->getParams()); $ringSignal->send(); }
/** * Send PROGRESS to the SIP call leg * * @return void */ protected function _sendProgress() { // extract the OK signal parameters $okSignal = $this->_event->getParam('signal'); $params = $okSignal->getParams(); // get a reference to the SIP call leg $sipCallLeg = $this->_widget->getLeftCallLeg(); // send the PROGRESS signal to the SIP call leg $progressSignal = Streamwide_Engine_Signal::factory(Streamwide_Engine_Signal::PROGRESS, $sipCallLeg->getName(), array('proto' => 'SIP', 'sdp' => $params['sdp'])); $progressSignal->send(); }
/** * Starts the relayer object * * @param Streamwide_Engine_Events_Event $event * @return void */ public function onConnect(Streamwide_Engine_Events_Event $event) { $this->_relayer->setLeftCallLeg($this->_widget->getLeftCallLeg()); $this->_relayer->setRightCallLeg($this->_widget->getRightCallLeg()); if (!$this->_relayer->isRunning()) { $this->_relayer->start(); } // Do we need to treat a MOVED signal? $specification = Streamwide_Engine_NotifyFilter_Factory::factory(Streamwide_Engine_NotifyFilter_Factory::T_EVT_PARAM, Streamwide_Engine_NotifyFilter_Factory::FILTER_INSTANCE_OF, array('moved', 'Streamwide_Engine_Signal')); if ($specification->isSatisfiedBy($event)) { $movedSignal = $event->getParam('moved'); $event = new Streamwide_Engine_Events_Event(Streamwide_Engine_Events_Event::MOVED); $event->setParam('signal', $movedSignal); return $this->_relayer->onSignalReceived($event); } }
protected function _handleOkMovedSignal(Streamwide_Engine_Signal $signal) { $this->_unsubscribeFromEngineEvents(); $event = new Streamwide_Engine_Events_Event(Streamwide_Engine_Events_Event::CONNECTED); $event->setParam('signal', $signal); if (null !== $this->_movedSignal) { $event->setParam('moved', $this->_movedSignal); } return $this->dispatchEvent($event); }
public function onWordRecognized(Streamwide_Engine_Events_Event $event) { $signal = $event->getParam('signal'); $params = $signal->getParams(); if ($params['word'] === '@') { $this->dispatchErrorEvent(self::ASR_RECOGNITION_FAILURE_ERR_CODE); return; } $event = new Streamwide_Engine_Events_Event(Streamwide_Engine_Events_Event::WORD_RECOGNIZED); $event->setParam('signal', $signal); $this->dispatchEvent($event); }
/** * Callback to respond to the TIMEOUT SW Engine signal * * @param Streamwide_Engine_Events_Event $event * @return void */ public function onTimeout(Streamwide_Engine_Events_Event $event) { if (!$this->isArmed()) { return $this->dispatchErrorEvent(self::ALREADY_ARMED_ERR_CODE); } // mark the timer as READY $this->_stateManager->setState(self::STATE_READY); // lower the flag so, that the next call to arm() will // generate a new name $this->_nameGenerated = false; // dispatch a TIMEOUT_TIMER_TIMEOUT event $signal = $event->getParam('signal'); $event = new Streamwide_Engine_Events_Event(Streamwide_Engine_Events_Event::TIMEOUT); $event->setParam('signal', $signal); $this->dispatchEvent($event); }
/** * If we received OKMOVED from the right SIP call leg, we need to send an OKSDP to * the left SIP call leg. If we received OKMOVED from left SIP call leg reconnect * was successfull and we need to dispatch the CONNECTED event * * @param Streamwide_Engine_Signal $signal * @return void */ protected function _handleOkMovedSignal(Streamwide_Engine_Signal $signal) { $params = $signal->getParams(); $remoteName = $signal->getRemote(); if ($remoteName === $this->_rightCallLeg->getName()) { $this->_rightOkMovedReceived = true; $this->_rightCallLeg->setParams($params); $okSdpSignal = Streamwide_Engine_Signal::factory(Streamwide_Engine_Signal::OKSDP, $this->_leftCallLeg->getName(), array('sdp' => $params['sdp'])); if (false === $okSdpSignal->send()) { $this->_unsubscribeFromEngineEvents(); return $this->dispatchErrorEvent(self::OKSDP_SIGNAL_SEND_ERR_CODE); } return null; } if ($remoteName === $this->_leftCallLeg->getName()) { $this->_unsubscribeFromEngineEvents(); $this->_leftCallLeg->setParams($params); $event = new Streamwide_Engine_Events_Event(Streamwide_Engine_Events_Event::CONNECTED); $event->setParam('signal', $signal); if (null !== $this->_movedSignal) { $event->setParam('moved', $this->_movedSignal); } return $this->dispatchEvent($event); } }
/** * Handle the OK signal * * @param Streamwide_Engine_Signal $signal * @return void */ protected function _handleOkSignal(Streamwide_Engine_Signal $signal) { $this->_unsubscribeFromEngineEvents(); $this->_rightCallLeg->setAlive(); $this->_rightCallLeg->okSent(); $this->_rightCallLeg->setParams($signal->getParams()); if (false === $signal->relay($this->_leftCallLeg->getName())) { return $this->dispatchErrorEvent(self::OK_SIGNAL_RELAY_ERR_CODE); } $this->_leftCallLeg->okReceived(); $event = new Streamwide_Engine_Events_Event(Streamwide_Engine_Events_Event::CONNECTED); $event->setParam('signal', $signal); return $this->dispatchEvent($event); }
/** * We have received OKMOVED, we need see who its from. If it came from * the SIP call leg we need to send an OKSDP signal to MS call leg, if it * came from MS call leg we need to relay it to the SIP call leg and * dispatch the FAX_ENV_CREATED event * * @param Streamwide_Engine_Signal $signal * @return void */ protected function _handleOkMovedSignal(Streamwide_Engine_Signal $signal) { $remote = $signal->getRemote(); if ($remote === $this->_leftCallLeg->getName()) { $this->_leftCallLeg->getParams($signal->getParams()); $oksdpSignal = Streamwide_Engine_Signal::factory(Streamwide_Engine_Signal::OKSDP, $this->_rightCallLeg->getName(), $signal->getParams()); if (false === $oksdpSignal->send()) { $this->_unsubscribeFromEngineEvents(); return $this->dispatchErrorEvent(self::OKSDP_SIGNAL_SEND_ERR_CODE); } return null; } if ($remote === $this->_rightCallLeg->getName()) { $this->_rightCallLeg->setParams($signal->getParams()); if (false === $signal->relay($this->_leftCallLeg->getName())) { $this->_unsubscribeFromEngineEvents(); return $this->dispatchErrorEvent(self::OKMOVED_SIGNAL_RELAY_ERR_CODE); } $event = new Streamwide_Engine_Events_Event(Streamwide_Engine_Events_Event::CONNECTED); $event->setParam('signal', $signal); return $this->dispatchEvent($event); } }
/** * Handles the FAIL event * * @param Streamwide_Engine_Events_Event $event * @return void */ protected function _handleFailEvent(Streamwide_Engine_Events_Event $event) { // Make sure to unsubscribe the event listeners as early as possible. // In case of retry the call legs names will be regenerated so the // event listeners MUST be recreated. In case of letting the decorated // widget handle the FAIL signal we also need to remove the event listeners $this->_unsubscribeFromEngineEvents(); $signal = $event->getParam('signal'); $params = $signal->getParams(); $code = null; if (array_key_exists('code', $params)) { $code = $params['code']; } if (null !== $code && !$this->isOutgoingStopCode($code)) { $this->_failEvent = $event; return $this->_connect(); } $this->_widget->removeEventListener(Streamwide_Engine_Events_Event::ERROR, array('callback' => array($this, 'onError'))); $this->_widget->triggerErrorEventOnRemoteSipFailure(true); return $this->_widget->onSignalReceived($event); }
/** * @param Streamwide_Engine_Events_Event $event * @return void */ public function onSignalRelayed(Streamwide_Engine_Events_Event $event) { $signal = $event->getParam('signal'); switch ($signal->getName()) { case Streamwide_Engine_Signal::TRANSFER: $this->_handleTransferSignal($signal); break; case Streamwide_Engine_Signal::OKTRANSFER: $this->_handleOkTransferSignal($signal); break; case Streamwide_Engine_Signal::FAILTRANSFER: $this->_handleFailTransferSignal($signal); break; } }
/** * Dispatch an error event * * @param string $code * @param array|null $eventParams * @return void */ public function dispatchErrorEvent($code, array $eventParams = null) { $errorEvent = new Streamwide_Engine_Events_Event(Streamwide_Engine_Events_Event::ERROR); $errorEvent->setErrorCode($code); if (array_key_exists($code, $this->_errors)) { $errorEvent->setErrorMessage($this->_errors[$code]); } if (is_array($eventParams) && !empty($eventParams)) { foreach (new ArrayIterator($eventParams) as $key => $param) { $errorEvent->setParam($key, $param); } } $this->dispatchEvent($errorEvent); }
/** * Deal with the 2 possible timeout types * * @param Streamwide_Engine_Events_Event $event * @return void */ public function onTimeout(Streamwide_Engine_Events_Event $event) { $timeoutType = $event->getContextParam('timeoutType'); if (self::TIMEOUT_FIRSTDIGIT === $timeoutType) { $shouldPromptOnNoInput = !empty($this->_options[self::OPT_NO_INPUT_PROMPT_PLAYLIST]); if ($shouldPromptOnNoInput) { $this->_saveTry(); $this->_counter->decrement(); if ($this->_counter->hasMoreTries()) { $this->_listenForKeyPresses(self::PROMPT_NO_INPUT); $this->_playNoInputPrompt(); } else { $this->dispatchErrorEvent(self::NO_TRIES_LEFT_ERR_CODE); } } else { $this->_retry(); } } elseif (self::TIMEOUT_INTERDIGIT === $timeoutType) { $this->_timer->reset(); $this->_mediaPlayer->reset(); $this->_dtmfHandler->reset(); $event = new Streamwide_Engine_Events_Event(Streamwide_Engine_Events_Event::FINISHED); $event->setParam('number', $this->_number); $this->dispatchEvent($event); } else { throw new RuntimeException('Invalid context'); } }
/** * Decrement the counter * * @param integer $step * @return boolean */ public function decrement($step = 1) { $this->_triesLeft -= $step; if ($this->_triesLeft < 0) { $this->_triesLeft = 0; } if (0 === $this->_triesLeft) { $this->dispatchErrorEvent(self::NO_TRIES_LEFT_ERR_CODE); return false; } $event = new Streamwide_Engine_Events_Event(Streamwide_Engine_Events_Event::OK); $event->setParam('triesLeft', $this->_triesLeft); $this->dispatchEvent($event); return true; }
/** * Handle the PROGRESS signal * * @param Streamwide_Engine_Signal $signal * @return void */ protected function _handleProgressSignal(Streamwide_Engine_Signal $signal) { if (false === $signal->relay($this->_leftCallLeg->getName())) { $this->_unsubscribeFromEngineEvents(); return $this->dispatchErrorEvent(self::PROGRESS_SIGNAL_RELAY_ERR_CODE); } $event = new Streamwide_Engine_Events_Event(Streamwide_Engine_Events_Event::PROGRESS); $event->setParam('signal', $signal); return $this->dispatchEvent($event); }
/** * We have received the OK signal from the MS call leg, we need to * relay it to the SIP call leg, and dispatch the CONNECTED event * * @param Streamwide_Engine_Signal $signal * @return void */ protected function _handleOkSignal(Streamwide_Engine_Signal $signal) { // unsubscribe from the OK, FAIL, SDP, OKSDP and FAILSDP signals $this->_unsubscribeFromEngineEvents(); // set MS call leg as alive and set the call leg params $params = $signal->getParams(); $this->_rightCallLeg->setAlive(); $this->_rightCallLeg->okSent(); $this->_rightCallLeg->setParams($params); // If the _blockOkSignalRelaying flag has been raised // exit the callback without relaying the OK signal if ($this->_blockOkSignalRelaying) { return null; } // relay the OK signal to the SIP call leg if (false === $signal->relay($this->_leftCallLeg->getName())) { return $this->dispatchErrorEvent(self::OK_SIGNAL_RELAY_ERR_CODE); } // mark the SIP call leg as having received the OK signal $this->_leftCallLeg->okReceived(); // dispatch the CONNECTED event $event = new Streamwide_Engine_Events_Event(Streamwide_Engine_Events_Event::CONNECTED); $event->setParam('signal', $signal); return $this->dispatchEvent($event); }
/** * Callback. Called when the transfer is successful. Will notify the call leg * that emitted the TRANSFER request, attempt to kill it and dispatch * a TRANSFERRED event * * @param Streamwide_Engine_Events_Event $event * @return void */ public function onTransferSuccess(Streamwide_Engine_Events_Event $event) { $this->_connector->reset(); $okTransfer = Streamwide_Engine_Signal::factory(Streamwide_Engine_Signal::OKTRANSFER, $this->_transferSource->getName()); if (false === $okTransfer->send()) { $this->dispatchErrorEvent(self::OKTRANSFER_SIGNAL_SEND_ERR_CODE); return; } $kill = Streamwide_Engine_Signal::factory(Streamwide_Engine_Signal::KILL, $this->_transferSource->getName()); if (false === $kill->send()) { $this->dispatchErrorEvent(self::KILL_SIGNAL_SEND_ERR_CODE); return; } $signal = $event->getParam('signal'); $event = new Streamwide_Engine_Events_Event(Streamwide_Engine_Events_Event::TRANSFERRED); $event->setParam('signal', $signal); $this->dispatchEvent($event); }
/** * We have received OK from the MS call leg. We can consider the reinivite was successfull, * so we dispatch the CONNECTED event. * * @param Streamwide_Engine_Signal $signal * @return void */ protected function _handleOkSignal(Streamwide_Engine_Signal $signal) { $this->_unsubscribeFromEngineEvents(); $params = $signal->getParams(); $this->_rightCallLeg->setAlive(); $this->_rightCallLeg->okSent(); $this->_rightCallLeg->setParams($params); $event = new Streamwide_Engine_Events_Event(Streamwide_Engine_Events_Event::CONNECTED); $event->setParam('signal', $signal); if (null !== $this->_movedSignal) { $event->setParam('moved', $this->_movedSignal); } return $this->dispatchEvent($event); }
/** * Handle the delaying of the RECORDER_STOPPED event (if it's the case). This is a workaround for a problem * in SW Engine where the recorded file is not immediately available after sending the RECORDSTOP signal * * @return void * @throws RuntimeException */ protected function _delayRecorderStoppedEventDispatch() { $event = new Streamwide_Engine_Events_Event(Streamwide_Engine_Events_Event::STOPPED); $event->setParam('recordStartTime', $this->_recordingStartTime); $event->setParam('recordStopTime', $this->_recordingStopTime); $delay = $this->_options[self::OPT_RECORDER_STOPPED_EVENT_DISPATCH_DELAY]; if (0 === $delay) { $this->dispatchEvent($event); return true; } if (null === $this->_timer) { throw new RuntimeException('Timer object not set'); } $this->_timer->reset(); $this->_timer->setOptions(array(Streamwide_Engine_Timer_Timeout::OPT_DELAY => $delay)); $this->_timer->addEventListener(Streamwide_Engine_Events_Event::TIMEOUT, array('callback' => array($this, 'dispatchEvent'), 'args' => $event, 'options' => array('autoRemove' => 'before'))); $armed = $this->_timer->arm(); if (!$armed) { $this->_timer->flushEventListeners(); return false; } return true; }
/** * Handles an event triggered by the receiving of a signal * * @param Streamwide_Engine_Events_Event $event * @return void */ public function onSignalReceived(Streamwide_Engine_Events_Event $event) { $signal = $event->getParam('signal'); $remote = $signal->getRemote(); $params = $signal->getParams(); if (!is_array($params) || empty($params) || !array_key_exists('sdp', $params)) { $message = 'No "sdp" parameter found in the "%s" signal. Signal will be skipped'; trigger_error(sprintf($message, $signal->getName()), E_USER_NOTICE); return; } if ($remote === $this->getLeftCallLeg()->getName()) { $source = 'left'; } elseif ($remote === $this->getRightCallLeg()->getName()) { $source = 'right'; } else { $message = 'Unexpected source for "%s" signal. Signal will be skipped'; trigger_error(sprintf($message, $signal->getName()), E_USER_NOTICE); return; } $this->_updateRtpProxyCallLeg($params['sdp'], $source); $modifiedSdp = $this->_modifySdp($params['sdp'], $source); $params['sdp'] = $modifiedSdp; $signal->setParams($params); $event->setParam('signal', $signal); }
/** * We have received ENDOFFAX, we need to check it's "ok" parameter to see if * everything went fine * * @param Streamwide_Engine_Events_Event $event * @return void */ public function onEndOfFax(Streamwide_Engine_Events_Event $event) { $signal = $event->getParam('signal'); $params = $signal->getParams(); $this->_forcedFaxNegotiator->reset(); $this->_unsubscribeFromEngineEvents(); if (array_key_exists('ok', $params) && $params['ok'] === 'true') { return $this->dispatchEvent(new Streamwide_Engine_Events_Event(Streamwide_Engine_Events_Event::FAX_SENT)); } return $this->dispatchErrorEvent(self::FAX_NOT_SENT_ERR_CODE); }
/** * The transfer has failed. We need to dispatch an ERROR event * * @param Streamwide_Engine_Events_Event $event * @return void */ public function onTransferFailure(Streamwide_Engine_Events_Event $event) { $this->_unsubscribeFromEngineEvents(); $this->_stateManager->setState(self::STATE_READY); $signal = $event->getParam('signal'); $params = $signal->getParams(); $failureCode = isset($params['code']) ? $params['code'] : null; $this->dispatchErrorEvent(self::TRANSFER_FAILED_ERR_CODE, array('failureCode' => $failureCode, 'signal' => $signal)); }
/** * Handles a valid key * * @param string $key * @param string $promptType * @return void */ protected function _handleValidKey($key, $promptType = null) { $this->_handleKeyPress($promptType); $event = new Streamwide_Engine_Events_Event(Streamwide_Engine_Events_Event::OPTION_CHOSEN); $event->setParam('menuOption', $key); $this->dispatchEvent($event); }
/** * Deals with any signals received from the call leg during the suspension process. We improve * the code from the parent method by listening to a MOVED signal that can come after receiving * OKMOVED in response to the MOVED we sent to start the suspension process. * * @param Streamwide_Engine_Events_Event $event * @return void */ public function onSignalReceived(Streamwide_Engine_Events_Event $event) { $eventType = $event->getEventType(); $signal = $event->getParam('signal'); switch ($eventType) { case Streamwide_Engine_Events_Event::MOVED: return $this->_handleMovedSignal($signal); case Streamwide_Engine_Events_Event::OKMOVED: return $this->_handleOkMovedSignal($signal); case Streamwide_Engine_Events_Event::FAILMOVED: return $this->_handleFailMovedSignal($signal); case Streamwide_Engine_Events_Event::CHILD: return $this->_handleChildSignal($signal); } return null; }
/** * Handle the OK signal. The call leg was created successfully. Mark the * call leg as alive and set its params. Dispatch a CALL_LEG_CREATED event * * @param Streamwide_Engine_Signal $signal * @return void */ protected function _handleOkSignal(Streamwide_Engine_Signal $signal) { $this->_unsubscribeFromEngineEvents(); $this->_callLeg->setAlive(); $this->_callLeg->hasSentOrReceivedOk(); $this->_callLeg->setParams(array_merge($signal->getParams(), $this->_creationParams)); $event = new Streamwide_Engine_Events_Event(Streamwide_Engine_Events_Event::CALL_LEG_CREATED); $event->setParam('callLeg', $this->_callLeg); $this->dispatchEvent($event); }
/** * Handle a DTMF signal * * @param Streamwide_Engine_Events_Event $event * @return void */ public function onDtmf(Streamwide_Engine_Events_Event $event) { // if the handler is not in the listening state we should not treat the event if (!$this->isListening()) { return null; } // extract the source of the DTMF signal $signal = $event->getParam('signal'); $remote = $signal->getRemote(); // check to see if we have an allowed source if (!$this->_isAllowedDtmfSource($remote)) { return null; } // extract the DTMF value $params = $signal->getParams(); $dtmf = $params['dtmf']; // add the received dtmf to the received dtmfs list $this->_receivedDtmfs[] = $dtmf; // check if the limit is reached if ($this->_isLimitReached()) { $this->stopListening(); } // if the dtmf is "treatable" add it to the handled dtmfs list and dispatch // a KEY event if ($this->_isAllowedDtmf($dtmf)) { $this->_handledDtmfs[] = $dtmf; if ($this->_canDtmfStopListeningProcess($dtmf)) { $this->stopListening(); } $event = new Streamwide_Engine_Events_Event(Streamwide_Engine_Events_Event::KEY); $event->setParam('receivedKey', $dtmf); $event->setParam('source', $remote); $event->setParam('signal', $signal); $this->dispatchEvent($event); } else { if ($this->_canDtmfStopListeningProcess($dtmf, false)) { $this->stopListening(); } if ($this->_shouldSignalWrongKey()) { // if the handler is set to signal unexpected keys, dispatch an DTMF_HANDLER_UNEXPECTED_KEY event $event = new Streamwide_Engine_Events_Event(Streamwide_Engine_Events_Event::UNEXPECTED_KEY); $event->setParam('receivedKey', $dtmf); $event->setParam('source', $remote); $event->setParam('signal', $signal); $this->dispatchEvent($event); } } }
/** * Checks to see if a relay session can be ended by searching the received * event type in the relay session parameters array. * If found the relay session is ended, if not nothing happens. * * @param Streamwide_Engine_Events_Event $event * @return void */ protected function _endRelaySession(Streamwide_Engine_Events_Event $event) { if ($this->_isRelaySessionEndingEvent($event->getEventType())) { $this->_isRelaySessionStarted = false; $relaySessionEndedEvt = new Streamwide_Engine_Events_Event(Streamwide_Engine_Events_Event::RELAY_SESSION_ENDED); $relaySessionEndedEvt->setParam('signal', $event->getParam('signal')); return $this->dispatchEvent($relaySessionEndedEvt); } }