Exemple #1
0
    def query_stat(self):
        try:
            if self._admin_ws_client is None:
                self._admin_ws_client = WSClient(self.admin_url, self._recv_msg_cbk, None, protocols=['janus-admin-protocol'])

            common_args = {}
            if self._admin_secret:
                common_args['admin_secret'] = self._admin_secret
            response = self.send_request(self._admin_ws_client, create_janus_msg('list_sessions', **common_args))
            sessions = response.get('sessions', [])
            handles = []
            for session_id in sessions:

                response = self.send_request(self._admin_ws_client,
                                             create_janus_msg('list_handles', session_id=session_id, **common_args))
                handles.extend(response.get('handles', []))
            self.set_stat(session_num=len(sessions), handle_num=len(handles))
        except Exception as e:
            if self._has_destroy:
                return
            log.warning('Calculate stat of janus server({}) failed: {}'.format(self.admin_url, e))
            self.set_stat(session_num=-1, handle_num=-1)   # stop post statistic
            if self._admin_ws_client:
                try:
                    self._admin_ws_client.close()
                except Exception:
                    pass
                self._admin_ws_client = None
Exemple #2
0
 def send_trickle(self, candidate=None, candidates=None):
     if self._has_detach:
         raise JanusCloudError('backend handle {} has been destroyed'.format(self.handle_id),
                               JANUS_ERROR_PLUGIN_DETACH)
     if candidate is None and candidates is None:
         raise JanusCloudError('Missing mandatory element (candidate|candidates)',
                               JANUS_ERROR_MISSING_MANDATORY_ELEMENT)
     if candidate and candidates:
         raise JanusCloudError('Can\'t have both candidate and candidates',
                               JANUS_ERROR_INVALID_JSON)
     params = {}
     if candidate:
         params['candidate'] = candidate
     if candidates:
         params['candidates'] = candidates
     trickle_msg = create_janus_msg('trickle', handle_id=self.handle_id, **params)
     response = self._session.send_request(trickle_msg, ignore_ack=False)
     if response['janus'] == 'ack':
         pass # successful
     elif response['janus'] == 'error':
         raise JanusCloudError(response['error']['reason'], response['error']['code'])
     else:
         raise JanusCloudError(
             'unknown backend response {}'.format(response),
             JANUS_ERROR_BAD_GATEWAY)
Exemple #3
0
    def detach(self):
        """ detach this handle from the session

        return:
            no value
        note: no exception would be raised
        """
        if self._has_detach:
            return
        self._has_detach = True

        # stop async event greenlet
        if not self._async_event_queue.full():
            self._async_event_queue.put(stop_message)
        self._async_event_greenlet = None

        if self._session:
            self._session.on_handle_detached(self.handle_id)
            try:
                detach_message = create_janus_msg('detach', handle_id=self.handle_id)
                self._session.send_request(detach_message)
            except Exception:
                log.exception('Detach backend handle {} error'.format(self.handle_id))

            self._session = None
Exemple #4
0
    def pingpong(self):
        try:
            if self._ws_client is None:
                self._ws_client = WSClient(self.url,
                                           self._recv_msg_cbk,
                                           self._close_cbk,
                                           protocols=['janus-protocol'])
            ping_start_ts = get_monotonic_time()
            self.send_request(self._ws_client, create_janus_msg('ping'))
            ping_end_ts = get_monotonic_time()
            ping_latency = ping_end_ts - ping_start_ts
            if self._hwm_threshold and ping_latency > self._hwm_threshold:
                self.set_status(JANUS_SERVER_STATUS_HWM)
            else:
                self.set_status(JANUS_SERVER_STATUS_NORMAL)

        except Exception as e:
            if self._has_destroy:
                return
            log.warning('Poll janus server({}) failed: {}'.format(self.url, e))
            self.set_status(JANUS_SERVER_STATUS_ABNORMAL)
            if self._ws_client:
                try:
                    self._ws_client.close()
                except Exception:
                    pass
                self._ws_client = None
Exemple #5
0
    def send_message(self, body, jsep=None):
        if self._has_detach:
            raise JanusCloudError(
                'backend handle {} has been destroyed'.format(self.handle_id),
                JANUS_ERROR_PLUGIN_DETACH)

        params = dict()
        params['body'] = body
        if jsep:
            params['jsep'] = jsep

        message = create_janus_msg('message',
                                   handle_id=self.handle_id,
                                   **params)
        response = self._session.send_request(message)
        if response['janus'] == 'event' or response['janus'] == 'success':
            data = response['plugindata']['data']
            reply_jsep = response.get('jsep')
            return data, reply_jsep
        elif response['janus'] == 'error':
            raise JanusCloudError(response['error']['reason'],
                                  response['error']['code'])
        else:
            raise JanusCloudError(
                'unknown backend response {}'.format(response),
                JANUS_ERROR_BAD_GATEWAY)
Exemple #6
0
    def _handle_trickle(self, request):

        trickle_params_schema = Schema({
            Optional('candidate'): dict,
            Optional('candidates'): [dict],
            AutoDel(str): object  # for all other key we don't care
        })
        params = trickle_params_schema.validate(request.message)
        candidate = params.get('candidate')
        candidates = params.get('candidates')

        if candidate is None and candidates is None:
            raise JanusCloudError(
                'Missing mandatory element (candidate|candidates)',
                JANUS_ERROR_MISSING_MANDATORY_ELEMENT)

        if candidate and candidates:
            raise JanusCloudError('Can\'t have both candidate and candidates',
                                  JANUS_ERROR_INVALID_JSON)

        # dispatch to plugin handle
        handle = self._get_plugin_handle(request)
        handle.handle_trickle(candidate=candidate, candidates=candidates)

        return create_janus_msg('ack', request.session_id, request.transaction)
Exemple #7
0
 def _push_event(self, method, transaction=None, **kwargs):
     if self._has_destroy:
         return
     event = create_janus_msg(method, self._session.session_id, transaction, **kwargs)
     event['sender'] = self.handle_id
     if self.opaque_id:
         event['opaque_id'] = self.opaque_id
     self._session.notify_event(event)
Exemple #8
0
 def _handle_destroy(self, request):
     if request.session_id == 0:
         raise JanusCloudError(
             "Unhandled request '{}' at this path".format(request.janus),
             JANUS_ERROR_INVALID_REQUEST_PATH)
     self._frontend_session_mgr.destroy_session(request.session_id)
     return create_janus_msg('success', request.session_id,
                             request.transaction)
 def _kick_timeout_sessions(self, session):
     session_id = session.session_id
     transport = session.ts
     session.notify_event(create_janus_msg('timeout', session_id))
     session.destroy()
     if transport:
         # notify the transport
         transport.session_over(session_id, True, False)
Exemple #10
0
 def _send_plugin_event(self, to_user, data, jsep=None):
     params = dict()
     params['plugindata'] = {
         'plugin': self.plugin_package_name,
         'data': data
     }
     if jsep:
         params['jsep'] = jsep
     event = create_janus_msg('event', **params)
     self._send_aync_event(to_user, event)
Exemple #11
0
 def _create_janus_session(self):
     response = self.send_request(create_janus_msg('create'))
     if response['janus'] == 'success':
         return response['data']['id']
     elif response['janus'] == 'error':
         raise JanusCloudError(
             'Create session error for Janus server {} with reason {}'.
             format(self.url,
                    response['error']['reason']), response['error']['code'])
     else:
         raise JanusCloudError(
             'Create session error for Janus server: {} with invalid response {}'
             .format(self.url, response), JANUS_ERROR_BAD_GATEWAY)
Exemple #12
0
 def _get_session_timeout(self):
     response = self.send_request(create_janus_msg('info'))
     if response['janus'] == 'server_info':
         return response.get('session-timeout', 30)
     elif response['janus'] == 'error':
         raise JanusCloudError(
             'Create session error for Janus server {} with reason {}'.
             format(self.url,
                    response['error']['reason']), response['error']['code'])
     else:
         raise JanusCloudError(
             'Create session error for Janus server: {} with invalid response {}'
             .format(self.url, response), JANUS_ERROR_BAD_GATEWAY)
Exemple #13
0
    def _handle_message(self, request):

        message_params_schema = Schema({
            'body': dict,
            Optional('jsep'): dict,
            AutoDel(str): object  # for all other key we don't care
        })
        params = message_params_schema.validate(request.message)

        # dispatch to plugin handle
        handle = self._get_plugin_handle(request)
        result, content = handle.handle_message(request.transaction, **params)
        if result == JANUS_PLUGIN_OK:
            if content is None or not isinstance(content, dict):
                raise JanusCloudError(
                    "Plugin didn't provide any content for this synchronous response"
                    if content is None else
                    "Plugin returned an invalid JSON response",
                    JANUS_ERROR_PLUGIN_MESSAGE)
            response = create_janus_msg('success',
                                        request.session_id,
                                        request.transaction,
                                        sender=request.handle_id)
            if handle.opaque_id:
                response['opaque_id'] = handle.opaque_id
            response['plugindata'] = {
                'plugin': handle.plugin_package_name,
                'data': content
            }
        elif result == JANUS_PLUGIN_OK_WAIT:
            response = create_janus_msg('ack', request.session_id,
                                        request.transaction)
            if content:
                response['hint'] = content
        else:
            raise JanusCloudError('Plugin returned a severe (unknown) error',
                                  JANUS_ERROR_PLUGIN_MESSAGE)

        return response
Exemple #14
0
 def _handle_attach(self, request):
     session = self._get_session(request)
     attach_params_schema = Schema({
         'plugin': StrVal(max_len=64),
         Optional('opaque_id'): StrVal(max_len=64),
         AutoDel(str): object  # for all other key we don't care
     })
     params = attach_params_schema.validate(request.message)
     handle = session.attach_handle(**params)
     return create_janus_msg('success',
                             request.session_id,
                             request.transaction,
                             data={'id': handle.handle_id})
Exemple #15
0
 def send_hangup(self):
     if self._has_detach:
         raise JanusCloudError('backend handle {} has been destroyed'.format(self.handle_id),
                               JANUS_ERROR_PLUGIN_DETACH)
     hangup_msg = create_janus_msg('hangup', handle_id=self.handle_id)
     response = self._session.send_request(hangup_msg)
     if response['janus'] == 'success':
         pass # successful
     elif response['janus'] == 'error':
         raise JanusCloudError(response['error']['reason'], response['error']['code'])
     else:
         raise JanusCloudError(
             'unknown backend response {}'.format(response),
             JANUS_ERROR_BAD_GATEWAY)
Exemple #16
0
    def attach_handle(self,
                      plugin_package_name,
                      opaque_id=None,
                      handle_listener=None):
        """

        :param plugin_pacakge_name:  str plugin package name
        :param opaque_id:   str opaque id
        :param handle_listener: handle related  callback listener which cannot block
        :return: BackendHandle object
        """
        if self.state == BACKEND_SESSION_STATE_DESTROYED:
            raise JanusCloudError(
                'Session has destroy for Janus server: {}'.format(self.url),
                JANUS_ERROR_SERVICE_UNAVAILABLE)

        attach_request_msg = create_janus_msg('attach',
                                              plugin=plugin_package_name)
        if opaque_id:
            attach_request_msg['opaque_id'] = opaque_id

        response = self.send_request(attach_request_msg)  # would block for IO
        if response['janus'] == 'success':
            handle_id = response['data']['id']
        elif response['janus'] == 'error':
            raise JanusCloudError(
                'attach error for Janus server {} with reason {}'.format(
                    self.url, response['error']['reason']),
                response['error']['code'])
        else:
            raise JanusCloudError(
                'attach error for Janus server: {} with invalid response {}'.
                format(self.url, response), JANUS_ERROR_BAD_GATEWAY)

        # check again when wake up from block IO
        if self.state == BACKEND_SESSION_STATE_DESTROYED:
            raise JanusCloudError(
                'Session has destroy for Janus server: {}'.format(self.url),
                JANUS_ERROR_SERVICE_UNAVAILABLE)

        handle = BackendHandle(handle_id,
                               plugin_package_name,
                               self,
                               opaque_id=opaque_id,
                               handle_listener=handle_listener)
        self._handles[handle_id] = handle
        if self._auto_destroy_greenlet:
            gevent.kill(self._auto_destroy_greenlet)
            self._auto_destroy_greenlet = None
        return handle
Exemple #17
0
    def handle_trickle(self, candidate=None, candidates=None):
        log.debug('handle_trickle for p2pcall handle {}.candidate:{} candidates:{}'.
                 format(self.handle_id, candidate, candidates))

        if candidate:
            if candidates:
                candidates.append(candidate)
            else:
                candidates = [candidate]

        if self.p2pcall_user is None or self.p2pcall_user.incall is False or self._trickle_holding:
            self._pending_candidates.extend(candidates)
        else:
            trickle_msg = create_janus_msg('trickle', candidates=candidates)
            self._send_aync_event(self.p2pcall_user.peer_name, trickle_msg)
Exemple #18
0
    def async_send_message(self, body, jsep=None):
        if self._has_detach:
            raise JanusCloudError(
                'backend handle {} has been destroyed'.format(self.handle_id),
                JANUS_ERROR_PLUGIN_DETACH)

        params = dict()
        params['body'] = body
        if jsep:
            params['jsep'] = jsep

        message = create_janus_msg('message',
                                   handle_id=self.handle_id,
                                   **params)
        self._session.async_send_request(message)
Exemple #19
0
    def _handle_create(self, request):
        create_params_schema = Schema({
            Optional('id'):
            IntVal(min=1, max=9007199254740992),
            AutoDel(str):
            object  # for all other key we don't care
        })

        params = create_params_schema.validate(request.message)
        session_id = params.get('id', 0)
        session = self._frontend_session_mgr.create_new_session(
            session_id, request.transport)
        return create_janus_msg('success',
                                0,
                                request.transaction,
                                data={'id': session.session_id})
Exemple #20
0
    def _keepalive_routine(self):
        gevent.sleep(self._keepalive_interval)
        keepalive_msg = create_janus_msg('keepalive')
        while self.state == BACKEND_SESSION_STATE_ACTIVE:
            try:
                # if there is no handle existed and auto destroy is enabled, just schedule the destroy route
                if not self._handles:
                    if self._auto_destroy and self._auto_destroy_greenlet is None:
                        self._auto_destroy_greenlet = gevent.spawn_later(
                            self._auto_destroy, self._auto_destroy_routine)

                self.send_request(keepalive_msg, ignore_ack=False)

            except Exception as e:
                log.exception('Keepalive failed for backend session {}'.format(
                    self.url))
                self.destroy()
            else:
                gevent.sleep(self._keepalive_interval)
Exemple #21
0
    def detach(self):

        if self._has_destroy:
            return
        self._has_destroy = True

        # stop async message greenlet
        if not self._async_message_queue.full():
            self._async_message_queue.put(stop_message)
        self._async_message_greenlet = None

        # send detach event
        event = create_janus_msg('detached', self._session.session_id)
        event['sender'] = self.handle_id
        if self.opaque_id:
            event['opaque_id'] = self.opaque_id
        self._session.notify_event(event)

        log.info('handle {} is detach from plugin {}'.format(self.handle_id, self.plugin_package_name))
Exemple #22
0
    def _handle_info(self, request):
        reply = create_janus_msg('server_info', 0, request.transaction)
        reply['name'] = 'Janus-Cloud Proxy'
        reply['author'] = 'OpenSight'
        reply['email'] = '*****@*****.**'
        reply['website'] = 'https://github.com/OpenSight/janus-cloud'
        reply['server_name'] = self._proxy_conf.get('general',
                                                    {}).get('server_name', '')
        reply['session-timeout'] = self._proxy_conf.get('general', {}).get(
            'session_timeout', 60)

        plugin_info_list = {}
        for plugin in get_plugin_list():
            plugin_info = {
                'version_string': plugin.get_version_string(),
                'description': plugin.get_description(),
                'author': plugin.get_author(),
                'name': plugin.get_name(),
                'version': plugin.get_version(),
            }
            plugin_info_list[plugin.get_package()] = plugin_info
        reply['plugins'] = plugin_info_list

        return reply
Exemple #23
0
 def _handle_hangup(self, request):
     handle = self._get_plugin_handle(request)
     handle.handle_hangup()
     return create_janus_msg('success', request.session_id,
                             request.transaction)
Exemple #24
0
 def _handle_detach(self, request):
     self._get_plugin_handle(request)  # check handle exist
     session = self._get_session(request)
     session.detach_handle(request.handle_id)
     return create_janus_msg('success', request.session_id,
                             request.transaction)
Exemple #25
0
 def _handle_ping(self, request):
     return create_janus_msg('pong', 0, request.transaction)
Exemple #26
0
    def _handle_async_message(self, transaction, body, jsep):
        try:
            result = None
            request = body.get('request')
            if request is None:
                raise JanusCloudError(
                    'Request {}  format invalid'.format(body),
                    JANUS_P2PCALL_ERROR_INVALID_ELEMENT)
            if request == 'list':
                username_list = self._plugin.user_dao.get_username_list()
                result = {'list': username_list}
            elif request == 'register':
                if self.p2pcall_user:
                    raise JanusCloudError(
                        'Already registered ({})'.format(
                            self.p2pcall_user.username),
                        JANUS_P2PCALL_ERROR_ALREADY_REGISTERED)
                body = username_schema.validate(body)
                username = body['username']
                # valid, register this new user
                api_url = self._plugin.api_base_url + '/' + username
                new_p2pcall_user = P2PCallUser(username,
                                               handle=self,
                                               api_url=api_url)
                self._plugin.user_dao.add(new_p2pcall_user)
                self.p2pcall_user = new_p2pcall_user
                result = {'event': 'registered', 'username': username}
            elif request == 'call':
                if self.p2pcall_user is None:
                    raise JanusCloudError('Register a username first',
                                          JANUS_P2PCALL_ERROR_REGISTER_FIRST)
                if self.p2pcall_user.incall:
                    raise JanusCloudError('Already in a call',
                                          JANUS_P2PCALL_ERROR_ALREADY_IN_CALL)

                body = username_schema.validate(body)
                username = body['username']
                if username == self.p2pcall_user.username:
                    raise JanusCloudError(
                        'You can\'t call yourself... use the EchoTest for that',
                        JANUS_P2PCALL_ERROR_USE_ECHO_TEST)
                peer = self._plugin.user_dao.get_by_username(username)
                if peer is None:
                    raise JanusCloudError(
                        'Username \'{}\' doesn\'t exist'.format(username),
                        JANUS_P2PCALL_ERROR_NO_SUCH_USERNAME)
                if jsep is None:
                    raise JanusCloudError('Missing SDP',
                                          JANUS_P2PCALL_ERROR_MISSING_SDP)

                if peer.incall:
                    log.debug('{} is busy'.format(username))
                    result = {
                        'event': 'hangup',
                        'username': self.p2pcall_user.username,
                        'reason': 'User busy'
                    }
                else:
                    # start the calling process
                    try:
                        self.p2pcall_user.peer_name = username
                        self.p2pcall_user.incall = True
                        self.p2pcall_user.utime = time.time()
                        self._trickle_holding = True  # buffer the trickle candidates util
                        # peer receiving incoming call event
                        # update the user dao
                        self._plugin.user_dao.update(self.p2pcall_user)

                        # send the imcomingcall event to the peer
                        call = {
                            'videocall': 'event',
                            'result': {
                                'event': 'incomingcall',
                                'username': self.p2pcall_user.username
                            }
                        }
                        self._send_plugin_event(username, call, jsep)

                        self._trickle_holding = False
                        # send the buffer candidates
                        if len(self._pending_candidates) > 0:
                            candidates = self._pending_candidates
                            self._pending_candidates.clear()
                            trickle_msg = create_janus_msg(
                                'trickle', candidates=candidates)
                            self._send_aync_event(self.p2pcall_user.peer_name,
                                                  trickle_msg)

                        result = {'event': 'calling'}
                    except Exception:
                        self.p2pcall_user.peer_name = ''
                        self.p2pcall_user.incall = False
                        self._trickle_holding = False
                        # update the user dao
                        self._plugin.user_dao.update(self.p2pcall_user)
                        raise

            elif request == 'accept':
                if self.p2pcall_user is None or self.p2pcall_user.incall is False \
                        or self.p2pcall_user.peer_name == '':
                    raise JanusCloudError('No incoming call to accept',
                                          JANUS_P2PCALL_ERROR_NO_CALL)
                if jsep is None:
                    raise JanusCloudError('Missing SDP',
                                          JANUS_P2PCALL_ERROR_MISSING_SDP)

                # send the accepted event to the peer
                call = {
                    'videocall': 'event',
                    'result': {
                        'event': 'accepted',
                        'username': self.p2pcall_user.username
                    }
                }
                self._send_plugin_event(self.p2pcall_user.peer_name, call,
                                        jsep)

                result = {'event': 'accepted'}

            elif request == 'set':
                if self.p2pcall_user is None or self.p2pcall_user.incall is False \
                        or self.p2pcall_user.peer_name == '':
                    raise JanusCloudError('Not in call',
                                          JANUS_P2PCALL_ERROR_NO_CALL)
                if jsep is None:
                    raise JanusCloudError('Missing SDP',
                                          JANUS_P2PCALL_ERROR_MISSING_SDP)

                # send the accepted event to the peer
                call = {
                    'videocall': 'event',
                    'result': {
                        'event': 'update',
                    }
                }
                self._send_plugin_event(self.p2pcall_user.peer_name, call,
                                        jsep)

                result = {'event': 'set'}
            elif request == 'hangup':
                reason = str(body.get('reason', 'We did the hangup'))

                if self.p2pcall_user and self.p2pcall_user.incall:

                    peer_name = self.p2pcall_user.peer_name
                    self.p2pcall_user.peer_name = ''
                    self.p2pcall_user.incall = False
                    self.p2pcall_user.utime = time.time()
                    self._plugin.user_dao.update(self.p2pcall_user)

                    try:
                        call = {
                            'videocall': 'event',
                            'result': {
                                'event': 'hangup',
                                'username': self.p2pcall_user.username,
                                'reason': reason
                            }
                        }
                        self._send_plugin_event(peer_name, call, jsep)
                    except Exception:
                        log.warning(
                            'fail to hangup to \'{}\''.format(peer_name))

                    log.debug("{} is hanging up the call with {}".format(
                        self.p2pcall_user.username, peer_name))
                else:
                    log.warning('No call to hangup')

                if self.p2pcall_user:
                    username = self.p2pcall_user.username
                else:
                    username = '******'
                result = {
                    'event': 'hangup',
                    'username': username,
                    'reason': 'Explicit hangup'
                }

            else:
                log.error('unknown request {}'.format(request))
                raise JanusCloudError('Unknown request {{}}'.format(request),
                                      JANUS_P2PCALL_ERROR_INVALID_REQUEST)

            # Process successfully
            data = {
                'videocall': 'event',
            }
            if result:
                data['result'] = result
            self._push_plugin_event(data, transaction=transaction)

            if result and result.get('event') == 'accepted':
                self._push_event('webrtcup')

        except JanusCloudError as e:
            log.exception(
                'Fail to handle async message ({}) for handle {}'.format(
                    body, self.handle_id))
            self._push_plugin_event(
                {
                    'videocall': 'event',
                    'error_code': e.code,
                    'error': str(e),
                },
                transaction=transaction)
        except SchemaError as e:
            log.exception('invalid message format ({}) for handle {}'.format(
                body, self.handle_id))
            self._push_plugin_event(
                {
                    'videocall': 'event',
                    'error_code': JANUS_P2PCALL_ERROR_INVALID_ELEMENT,
                    'error': str(e),
                },
                transaction=transaction)
        except Exception as e:
            log.exception(
                'Fail to handle async message ({}) for handle {}'.format(
                    body, self.handle_id))
            self._push_plugin_event(
                {
                    'videocall': 'event',
                    'error_code': JANUS_ERROR_BAD_GATEWAY,
                    'error': str(e),
                },
                transaction=transaction)
Exemple #27
0
 def _handle_keepalive(self, request):
     log.debug('Got a keep-alive on session {0}'.format(request.session_id))
     session = self._get_session(request)
     return create_janus_msg('ack', request.session_id, request.transaction)
Exemple #28
0
 def handle_hangup(self):
     log.debug('handle_hangup for p2pcall Handle {}'.format(self.handle_id))
     if self.p2pcall_user and self.p2pcall_user.incall:
         hangup_msg = create_janus_msg('hangup', reason='Peer Hangup')
         self._send_aync_event(self.p2pcall_user.peer_name, hangup_msg)
Exemple #29
0
 def _handle_claim(self, request):
     session = self._get_session(request)
     session.transport_claim(request.transport)
     return create_janus_msg('success', request.session_id,
                             request.transaction)