/** * 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); } } }
/** * 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; }
/** * Start receiving the fax * * @return void */ protected function _faxReceive() { $signal = Streamwide_Engine_Signal::factory(Streamwide_Engine_Signal::FAXRECEIVE, $this->_msCallLeg->getName(), array('filename' => $this->_savePath)); if (false === $signal->send()) { return $this->dispatchErrorEvent(self::FAXRECEIVE_SIGNAL_SEND_ERR_CODE); } $this->_subscribeToEngineEvents(); return $this->dispatchEvent(new Streamwide_Engine_Events_Event(Streamwide_Engine_Events_Event::FAX_RECEIVING_STARTED)); }
/** * To put a call leg on hold we start by sending a MOVED. If the call leg answers with OKMOVED * we deal with it in this method * * @param Streamwide_Engine_Signal $signal * @return void */ protected function _handleOkMovedSignal(Streamwide_Engine_Signal $signal) { $params = $signal->getParams(); $this->_callLeg->setParams($params); return $this->dispatchEvent(new Streamwide_Engine_Events_Event(Streamwide_Engine_Events_Event::CALL_LEG_ON_HOLD)); }
/** * Perform the transfer * * @return boolean * @throws RuntimeException */ public function transfer() { if (!isset($this->_options[self::OPT_TRANSFER_DESTINATION])) { throw new RuntimeException('Transfer destination has not been provided'); } if (!$this->_sipCallLeg->isAlive()) { $this->dispatchErrorEvent(self::DEAD_CALL_LEG_ERR_CODE); return false; } if ($this->isTransferring()) { $this->dispatchErrorEvent(self::ALREADY_TRANSFERRING_ERR_CODE); return false; } $params = array('number' => $this->_options[self::OPT_TRANSFER_DESTINATION]); $headers = $this->_options[self::OPT_TRANSFER_HEADERS]; if (!empty($headers)) { foreach ($headers as $name => $value) { $params[$name] = $value; } } $transferSignal = Streamwide_Engine_Signal::factory(Streamwide_Engine_Signal::TRANSFER, $this->_sipCallLeg->getName(), $params); if (false === $transferSignal->send()) { $this->dispatchErrorEvent(self::TRANSFER_SIGNAL_SEND_ERR_CODE); return false; } $this->_subscribeToEngineEvents(); $this->_stateManager->setState(self::STATE_TRANSFERRING); return true; }
/** * Update the RTPROXY call leg with the new sdp * * @param string $sdp * @param string $source The source of the sdp (one of the two values: left or right) * @return void */ protected function _updateRtpProxyCallLeg($sdp, $source) { $signal = Streamwide_Engine_Signal::factory(Streamwide_Engine_Signal::RTPPROXY, $this->_rtpProxyCallLeg->getName(), array($source . 'sdp' => $sdp)); $signal->send(); }
/** * 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); }
/** * We have received the FAILSDP signal from the SIP call leg we need to * relay it to the MS call leg and dispatch an error event * * @param Streamwide_Engine_Signal $signal * @return void */ protected function _handleFailSdpSignal(Streamwide_Engine_Signal $signal) { $this->_unsubscribeFromEngineEvents(); if (false === $signal->relay($this->_rightCallLeg->getName())) { return $this->dispatchErrorEvent(self::FAILSDP_SIGNAL_RELAY_ERR_CODE); } $params = $signal->getParams(); $failureCode = isset($params['code']) ? $params['code'] : null; return $this->dispatchErrorEvent(self::MS_NOT_CREATED_ERR_CODE, array('failureCode' => $failureCode, 'signal' => $signal)); }
protected function _handleFailMovedSignal(Streamwide_Engine_Signal $signal) { $this->_unsubscribeFromEngineEvents(); if ($this->_sipCallLegCreationFailed) { if (!$this->_triggerErrorEventOnRemoteSipFailure) { return; } $errorCode = self::SIP_NOT_CREATED_ERR_CODE; $failureCode = $this->_sipCallLegCreationFailureCode; } else { $errorCode = self::MS_NOT_UPDATED_ERR_CODE; $params = $signal->getParams(); $failureCode = isset($params['code']) ? $params['code'] : null; $killSignal = Streamwide_Engine_Signal::factory(Streamwide_Engine_Signal::KILL, $this->_leftCallLeg->getName()); $killSignal->send(); } $this->dispatchErrorEvent($errorCode, array('failureCode' => $failureCode, 'signal' => $signal)); }
public function stopRecognition() { $stop = Streamwide_Engine_Signal::factory(Streamwide_Engine_Signal::STOP, $this->_asrCallLeg->getName()); if (false === $stop->send()) { $this->dispatchErrorEvent(self::STOP_SIGNAL_SEND_FAILURE_ERR_CODE); return false; } $this->_unsubscribeFromEngineEvents(); return true; }
/** * Retrieves the prerelay callback for a signal * * @param Streamwide_Engine_Signal $signal * @return string|array|void */ protected function _retrievePreRelayCallback(Streamwide_Engine_Signal $signal) { for ($i = 0, $n = count($this->_eventsList); $i < $n; $i++) { $event = $this->_eventsList[$i]; if ($event['name'] === $signal->getName()) { return $event['callback']; } } }
/** * We have received OKMOVED from the callee SIP call leg. We need to send * OKSDP to caller SIP call leg. * * @param Streamwide_Engine_Signal $signal * @return void */ protected function _handleOkMovedSignal(Streamwide_Engine_Signal $signal) { $this->_okMovedReceived = true; $params = $signal->getParams(); $okSdpSignal = Streamwide_Engine_Signal::factory(Streamwide_Engine_Signal::OKSDP, $this->_rightCallLeg->getName(), array('sdp' => $params['sdp'])); if (false === $okSdpSignal->send()) { $this->_unsubscribeFromEngineEvents(); return $this->dispatchErrorEvent(self::OKSDP_SIGNAL_SEND_ERR_CODE); } }
/** * We have received a CHILD from the MS call leg, we need to send FAIL to * the SIP call leg and dispatch an error event * * @param Streamwide_Engine_Signal $signal * @return void */ protected function _handleChildSignal(Streamwide_Engine_Signal $signal) { $this->_unsubscribeFromEngineEvents(); $remote = $signal->getRemote(); // handle death of SIP call leg if ($remote === $this->_leftCallLeg->getName()) { if ($this->_leftCallLeg->isAlive()) { $this->_leftCallLeg->setDead(); } return $this->dispatchErrorEvent(self::CALL_LEG_DIED_ERR_CODE, array('callLeg' => $this->_leftCallLeg)); } // handle death of MS call leg if ($remote === $this->_rightCallLeg->getName()) { $failSignal = Streamwide_Engine_Signal::factory(Streamwide_Engine_Signal::FAIL, $this->_leftCallLeg->getName()); if (false === $failSignal->send()) { return $this->dispatchErrorEvent(self::FAIL_SIGNAL_SEND_ERR_CODE); } if ($this->_rightCallLeg->isAlive()) { $this->_rightCallLeg->setDead(); } return $this->dispatchErrorEvent(self::CALL_LEG_DIED_ERR_CODE, array('callLeg' => $this->_rightCallLeg)); } }
/** * Handle FAIL signal. The call leg was not created. We need to dispatch an error * event. * * @param Streamwide_Engine_Signal $signal * @param string $errorCode * @return void */ protected function _handleFailSignal(Streamwide_Engine_Signal $signal, $errorCode) { $this->_unsubscribeFromEngineEvents(); $this->_callLeg->setDead(); $params = $signal->getParams(); $failureCode = isset($params['code']) ? $params['code'] : null; $this->dispatchErrorEvent($errorCode, array('failureCode' => $failureCode, 'signal' => $signal)); }
/** * Go forward or backward in the play of the currently played media * * @param integer $amount * @param integer $direction Defaults to 1 (go forward) * @return boolean * @throws RuntimeException */ public function goto($amount, $direction = 1) { if (!$this->isPlaying() && !$this->isPaused()) { $this->dispatchErrorEvent(self::NOT_PLAYING_OR_PAUSED_ERR_CODE); return false; } if (is_float($amount) || is_string($amount) && preg_match('~^[1-9][0-9]*$~', $amount) === 1) { $amount = (int) $amount; } if (!is_int($amount) || $amount < 1) { return false; } if (!is_int($direction)) { $direction = 1; } if ($direction > 0) { $gotoAmount = sprintf('+%d', $amount); } elseif ($direction < 0) { $gotoAmount = sprintf('-%d', $amount); } else { $gotoAmount = sprintf('%d', $amount); } if (null === $this->_mediaServerCallLeg) { throw new RuntimeException('Media server call leg has not been set'); } $signal = Streamwide_Engine_Signal::factory(Streamwide_Engine_Signal::GOTO, $this->_mediaServerCallLeg->getName(), array('time' => $gotoAmount)); // send the GOTO signal // if the sending of the signal failed dispatch an ERROR event if (false === $signal->send()) { $this->dispatchErrorEvent(Streamwide_Engine_Media_Player::GOTO_SIGNAL_SEND_FAILURE_ERR_CODE); return false; } 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 the filter settings to the engine for activation, deactivation or update * * @param boolean $activate */ protected function _applyFilterSettings($activate = true) { $name = Streamwide_Engine_Signal::SET; $remote = $this->_mediaServerCallLeg->getName(); $params = array(); $params['name'] = $this->_getFilterFullName(); $params['activate'] = 'false'; if (true === $activate) { $params['activate'] = 'true'; $params = array_merge($params, $this->_params); } $set = Streamwide_Engine_Signal::factory($name, $remote, $params); return $set->send(); }
/** * @return void */ protected function _killLeftCallLeg() { $shouldKillLeftCallLeg = $this->_options[self::OPT_KILL_LEFT_CALL_LEG]; if ($shouldKillLeftCallLeg) { $killSignal = Streamwide_Engine_Signal::factory(Streamwide_Engine_Signal::KILL, $this->_leftCallLeg->getName()); if ($killSignal->send()) { $this->_leftCallLeg->setDead(); } } }
/** * 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(); }
protected function _handleFailMovedSignal(Streamwide_Engine_Signal $signal) { $remote = $signal->getRemote(); $params = $signal->getParams(); $failureCode = isset($params['code']) ? $params['code'] : null; if ($remote === $this->_leftCallLeg->getName()) { $this->_sipCallLegUpdateFailed = true; $signal = Streamwide_Engine_Signal::factory(Streamwide_Engine_Signal::FAILSDP, $this->_rightCallLeg->getName(), null !== $failureCode ? array('code' => $failureCode) : $failureCode); if (false === $signal->send()) { $this->_unsubscribeFromEngineEvents(); $this->dispatchErrorEvent(self::FAILSDP_SIGNAL_SEND_ERR_CODE); } return; } if ($remote === $this->_rightCallLeg->getName()) { $this->_unsubscribeFromEngineEvents(); $errorCode = self::MS_NOT_UPDATED_ERR_CODE; if ($this->_sipCallLegUpdateFailed) { $errorCode = self::SIP_NOT_UPDATED_ERR_CODE; } return $this->dispatchErrorEvent($errorCode, array('failureCode' => $failureCode, 'signal' => $signal)); } }
/** * Send the SET signal to SW Engine to deactivate detection * * @param array|null $params * @return void */ protected function _deactivateDetection(array $params = null) { $invariantParams = array('name' => $this->_detectorName, 'activate' => 'false'); if (is_array($params) && !empty($params)) { $params = array_merge($invariantParams, $params); } else { $params = $invariantParams; } $setSignal = Streamwide_Engine_Signal::factory(Streamwide_Engine_Signal::SET, $this->_msCallLeg->getName(), $params); $setSignal->send(); }
/** * We have received FAILMOVED. If it came from SIP call leg we need to send a * FAILSDP to the MS call leg and dispatch an error event. If it came from MS call * leg the fax environment could not be created * * @param Streamwide_Engine_Signal $signal * @return void */ protected function _handleFailMovedSignal(Streamwide_Engine_Signal $signal) { $this->_unsubscribeFromEngineEvents(); $remote = $signal->getRemote(); if ($remote === $this->_leftCallLeg->getName()) { $failSdpSignal = Streamwide_Engine_Signal::factory(Streamwide_Engine_Signal::FAILSDP, $this->_rightCallLeg->getName(), $signal->getParams()); if (false === $failSdpSignal->send()) { $this->dispatchErrorEvent(self::FAILSDP_SIGNAL_SEND_ERR_CODE); } return null; } if ($remote === $this->_rightCallLeg->getName()) { return $this->dispatchErrorEvent(self::FAX_ENV_NOT_CREATED_ERR_CODE); } }
/** * Handle the PROK signal * * @param Streamwide_Engine_Signal $signal * @return void */ protected function _handleProkSignal(Streamwide_Engine_Signal $signal) { if (false === $signal->relay($this->_leftCallLeg->getName())) { $this->_unsubscribeFromEngineEvents(); return $this->dispatchErrorEvent(self::PROK_SIGNAL_RELAY_ERR_CODE); } }
/** * Callback. Called when the transfer has failed. Will notify the call leg that * emitted the TRANSFER and will attempt to reconnect the call legs. * * @param Streamwide_Engine_Events_Event $event * @return void */ public function onTransferFailure(Streamwide_Engine_Events_Event $event) { $this->_connector->reset(); $failTransfer = Streamwide_Engine_Signal::factory(Streamwide_Engine_Signal::FAILTRANSFER, $this->_transferSource->getName()); if (false === $failTransfer->send()) { $this->dispatchErrorEvent(self::FAILTRANSFER_SIGNAL_SEND_ERR_CODE); return; } $this->_revertTransfer(); }
/** * Handle a CHILD signal received in the middle of the connection process * * @param Streamwide_Engine_Signal $signal * @param string $errorCode * @return void */ protected function _handleChildSignal(Streamwide_Engine_Signal $signal, $errorCode) { $this->_unsubscribeFromEngineEvents(); $remoteName = $signal->getRemote(); $defunctCallLeg = $remoteName === $this->_leftCallLeg->getName() ? $this->_leftCallLeg : $this->_rightCallLeg; if ($defunctCallLeg->isAlive()) { $defunctCallLeg->setDead(); } return $this->dispatchErrorEvent($errorCode, array('callLeg' => $defunctCallLeg)); }
/** * Stop recording * * @return boolean */ public function stop() { if (!$this->isRecording()) { $this->dispatchErrorEvent(self::NOT_RECORDING_ERR_CODE); return false; } if (null === $this->_mediaServerCallLeg) { throw new RuntimeException('Media server call leg has not been set'); } $signal = Streamwide_Engine_Signal::factory(Streamwide_Engine_Signal::RECORDSTOP, $this->_mediaServerCallLeg->getName(), $this->_storage->toArray()); if (false === $signal->send()) { $this->dispatchErrorEvent(self::RECORDSTOP_SIGNAL_SEND_FAILURE_ERR_CODE); return false; } $this->_recordingStopTime = time(); $this->_stateManager->setState(self::STATE_READY); return $this->_delayRecorderStoppedEventDispatch(); }
/** * During the reconnect process we can receive a MOVED from the right SIP call leg. If this occurs * before we received OKMOVED from the right SIP call leg we need to reply with a FAILMOVED. * * @param Streamwide_Engine_Signal $signal * @return void */ protected function _handleMovedSignal(Streamwide_Engine_Signal $signal) { if ($this->_rightOkMovedReceived) { $this->_movedSignal = $signal; } else { $failMovedSignal = Streamwide_Engine_Signal::factory(Streamwide_Engine_Signal::FAILMOVED, $this->_rightCallLeg->getName(), array('code' => '491')); if (false === $failMovedSignal->send()) { $this->_unsubscribeFromEngineEvents(); return $this->dispatchErrorEvent(self::FAILMOVED_SIGNAL_SEND_ERR_CODE); } } }
/** * Send the FAXSEND signal * * @return void */ protected function _faxSend() { $params = $this->_preparePages(); if (empty($params)) { return $this->dispatchErrorEvent(self::FAX_PAGES_NOT_PROVIDED_ERR_CODE); } $signal = Streamwide_Engine_Signal::factory(Streamwide_Engine_Signal::FAXSEND, $this->_msCallLeg->getName(), $params); if (false === $signal->send()) { return $this->dispatchErrorEvent(self::FAXSEND_SIGNAL_SEND_ERR_CODE); } $this->dispatchEvent(new Streamwide_Engine_Events_Event(Streamwide_Engine_Events_Event::FAX_SENDING_STARTED)); $this->_subscribeToEngineEvents(); }
/** * Disarm the timer * * @return boolean */ public function disarm() { if (!$this->isArmed()) { $this->dispatchErrorEvent(self::NOT_ARMED_ERR_CODE); return false; } $signal = Streamwide_Engine_Signal::factory(Streamwide_Engine_Signal::DISARM, self::REMOTE_NAME, array('reference' => $this->_name)); // send the DISARM signal $ret = $signal->send(); // dispatch an ERROR event and exit if the signal could not be sent if (false === $ret) { $this->dispatchErrorEvent(self::DISARM_SIGNAL_SEND_FAILURE_ERR_CODE); return $ret; } // unsubscribe from the SIGNAL_IN_TIMEOUT event $this->_unsubscribeFromEngineEvents(); // 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 an TIMEOUT_TIMER_DISARMED event $this->dispatchEvent(new Streamwide_Engine_Events_Event(Streamwide_Engine_Events_Event::DISARMED)); return $ret; }
/** * Because we are sending 2 CREATE signals, we can receive two OK signals as response (if * everything is well). On OK received from the SIP call leg we send an OKSDP signal * to the MS call leg. On OK received from the MS call leg we dispatch the CONNECTED event * * @param Streamwide_Engine_Signal $signal * @return void */ protected function _handleOkSignal(Streamwide_Engine_Signal $signal) { $remoteName = $signal->getRemote(); $params = $signal->getParams(); if ($remoteName === $this->_rightCallLeg->getName()) { // The OK came from the media server call leg $this->_rightCallLeg->setAlive(); $this->_rightCallLeg->okSent(); $this->_rightCallLeg->setParams($params); if ($this->_msNegotiationActive) { if ($this->_leftCallLeg->hasSentOrReceivedOk()) { $params = $this->_leftCallLeg->getParams(); $moved = Streamwide_Engine_Signal::factory(Streamwide_Engine_Signal::MOVED, $this->_rightCallLeg->getName(), array('sdp' => $params['sdp'])); if (false === $moved->send()) { $this->_unsubscribeFromEngineEvents(); return $this->dispatchErrorEvent(self::MOVED_SIGNAL_SEND_ERR_CODE); } } $event = new Streamwide_Engine_Events_Event(Streamwide_Engine_Events_Event::EARLY_MEDIA_NEGOTIATED); return $this->dispatchEvent($event); } else { $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); } } if ($remoteName === $this->_leftCallLeg->getName()) { // The OK came from the sip call leg $this->_leftCallLeg->setAlive(); $this->_leftCallLeg->okSent(); $this->_leftCallLeg->setParams($params); if ($this->_msNegotiationActive) { if ($this->_rightCallLeg->hasSentOrReceivedOk()) { $moved = Streamwide_Engine_Signal::factory(Streamwide_Engine_Signal::MOVED, $this->_rightCallLeg->getName(), array('sdp' => $params['sdp'])); if (false === $moved->send()) { $this->_unsubscribeFromEngineEvents(); return $this->dispatchErrorEvent(self::MOVED_SIGNAL_SEND_ERR_CODE); } } return; } else { $okSdp = Streamwide_Engine_Signal::factory(Streamwide_Engine_Signal::OKSDP, $this->_rightCallLeg->getName(), array('sdp' => $params['sdp'])); if (false === $okSdp->send()) { $this->_unsubscribeFromEngineEvents(); return $this->dispatchErrorEvent(self::OKSDP_SIGNAL_SEND_ERR_CODE); } } } }