self.parent_session = None # type: Optional[VideoroomSessionInfo] # for subscribers this is their main session (the one used to join), for publishers is None
self.publisher_id = None # janus publisher ID for publishers / publisher session ID for subscribers
self.slow_download = False
self.slow_upload = False
self.feeds = PublisherFeedContainer() # keeps references to all the other participant's publisher feeds that we subscribed to
self.log.debug('subscribe to {account} in room {session.room.uri} {feeds}'.format(account=publisher_session.account.id, session=videoroom_session, feeds=len(base_session.feeds)))
raise APIError('Unknown room session to detach: {request.feed}'.format(request=request))
if videoroom_session.parent_session.id != request.session:
raise APIError('{request.feed} is not an attached feed of {request.session}'.format(request=request))
videoroom_session.janus_handle.feed_detach()
# safety net in case we do not get any answer for the feed_detach request
# todo: to be adjusted later after pseudo-synchronous communication with janus is implemented
self.log.debug('unsubscribe from {account} in room {session.room.uri}'.format(account=videoroom_session.room[videoroom_session.publisher_id].account.id, session=videoroom_session))
modified = ', '.join('{}={}'.format(key, options[key]) for key in options)
media = 'video'
try:
has_video = options['video']
except KeyError:
pass
else:
if not has_video:
media = 'audio only'
self.log.info('switched to {media} media to {account} in room {session.room.uri}'.format(account=videoroom_session.room[videoroom_session.publisher_id].account.id, session=videoroom_session, media=media))
body = cpim_message.content if isinstance(cpim_message.content, str) else cpim_message.content.decode()
content_type = cpim_message.content_type
sender = cpim_message.sender or FromHeader(SIPURI.parse('{}'.format(data.sender)), data.displayname)
disposition = next(([item.strip() for item in header.value.split(',')] for header in cpim_message.additional_headers if header.name == 'Disposition-Notification'), None)
message_id = next((header.value for header in cpim_message.additional_headers if header.name == 'Message-ID'), None)
content = message.content if isinstance(message.content, str) else message.content.decode('latin1') # preserve binary data for transmitting over JSON
if any(header.name == 'Message-Type' and header.value == 'status' and header.namespace == 'urn:ag-projects:xml:ns:cpim' for header in message.additional_headers):
body = CPIMPayload.decode(notification.sender.body)
reason = data.reason.decode() if isinstance(data.reason, bytes) else data.reason
callid = data.headers.get('Call-ID', Null).body if hasattr(data, 'headers') else None
self.log.warning('could not deliver message to %s: %d %s (%s)' % (', '.join(([str(item.uri) for item in body.recipients])), data.code, reason, callid))
message_id = next((header.value for header in body.additional_headers if header.name == 'Message-ID'), None)
result = ObjectProperty(SIPResultRegistering) # type: SIPResultRegistering
class SIPRegisteredEvent(SIPPluginData):
sip = FixedValueProperty('event')
result = ObjectProperty(SIPResultRegistered) # type: SIPResultRegistered
class SIPRegistrationFailedEvent(SIPPluginData):
sip = FixedValueProperty('event')
result = ObjectProperty(SIPResultRegistrationFailed) # type: SIPResultRegistrationFailed
class SIPUnregisteringEvent(SIPPluginData):
sip = FixedValueProperty('event')
result = ObjectProperty(SIPResultUnregistering) # type: SIPResultUnregistering
class SIPUnregisteredEvent(SIPPluginData):
sip = FixedValueProperty('event')
result = ObjectProperty(SIPResultUnregistered) # type: SIPResultRegistered
class SIPCallingEvent(SIPPluginData):
sip = FixedValueProperty('event')
result = ObjectProperty(SIPResultCalling) # type: SIPResultCalling
class SIPRingingEvent(SIPPluginData):
sip = FixedValueProperty('event')
result = ObjectProperty(SIPResultRinging) # type: SIPResultRinging
class SIPProceedingEvent(SIPPluginData):
sip = FixedValueProperty('event')
result = ObjectProperty(SIPResultProceeding) # type: SIPResultProceeding
class SIPProgressEvent(SIPPluginData):
sip = FixedValueProperty('event')
result = ObjectProperty(SIPResultProgress) # type: SIPResultProgress
call_id = StringProperty(optional=True)
class SIPDecliningEvent(SIPPluginData):
sip = FixedValueProperty('event')
result = ObjectProperty(SIPResultDeclining) # type: SIPResultDeclining
class SIPAcceptingEvent(SIPPluginData):
sip = FixedValueProperty('event')
result = ObjectProperty(SIPResultAccepting) # type: SIPResultAccepting
class SIPAcceptedEvent(SIPPluginData):
sip = FixedValueProperty('event')
result = ObjectProperty(SIPResultAccepted) # type: SIPResultAccepted
call_id = StringProperty()
class SIPHoldingEvent(SIPPluginData):
sip = FixedValueProperty('event')
result = ObjectProperty(SIPResultHolding) # type: SIPResultHolding
class SIPResumingEvent(SIPPluginData):
sip = FixedValueProperty('event')
result = ObjectProperty(SIPResultResuming) # type: SIPResultResuming
class SIPHangingUpEvent(SIPPluginData):
sip = FixedValueProperty('event')
result = ObjectProperty(SIPResultHangingUp) # type: SIPResultHangingUp
class SIPHangupEvent(SIPPluginData):
sip = FixedValueProperty('event')
result = ObjectProperty(SIPResultHangup) # type: SIPResultHangup
class SIPIncomingCallEvent(SIPPluginData):
sip = FixedValueProperty('event')
result = ObjectProperty(SIPResultIncomingCall) # type: SIPResultIncomingCall
call_id = StringProperty()
class SIPMissedCallEvent(SIPPluginData):
sip = FixedValueProperty('event')
result = ObjectProperty(SIPResultMissedCall) # type: SIPResultMissedCall
class SIPInfoEvent(SIPPluginData):
sip = FixedValueProperty('event')
result = ObjectProperty(SIPResultInfo) # type: SIPResultInfo
class SIPInfoSentEvent(SIPPluginData):
sip = FixedValueProperty('event')
result = ObjectProperty(SIPResultInfoSent) # type: SIPResultInfoSent
class SIPMessageEvent(SIPPluginData):
sip = FixedValueProperty('event')
call_id = StringProperty(optional=True)
result = ObjectProperty(SIPResultMessage) # type: SIPResultMessage
class SIPMessageSentEvent(SIPPluginData):
sip = FixedValueProperty('event')
result = ObjectProperty(SIPResultMessageSent) # type: SIPResultMessageSent
class SIPMessageDeliveryEvent(SIPPluginData):
sip = FixedValueProperty('event')
call_id = StringProperty(optional=True)
result = ObjectProperty(SIPResultMessageDelivery) # type: SIPResultMessageSent
class SIPDTMFSentEvent(SIPPluginData):
sip = FixedValueProperty('event')
result = ObjectProperty(SIPResultDTMFSent) # type: SIPResultDTMFSent
# Videoroom plugin data messages
class VideoroomCreated(VideoroomPluginData):
videoroom = FixedValueProperty('created')
room = IntegerProperty()
# permanent = BooleanProperty() # this is not available in older janus versions. not used.
class VideoroomEdited(VideoroomPluginData):
videoroom = FixedValueProperty('edited')
room = IntegerProperty()
# permanent = BooleanProperty() # this is not available in older janus versions. not used.
class VideoroomDestroyed(VideoroomPluginData): # this comes both in the response to 'destroy' and in an event to participants still in the room when destroyed (if any)
videoroom = FixedValueProperty('destroyed')
room = IntegerProperty()
# permanent = BooleanProperty(optional=True) # this is not available in older janus versions (only present in the response, but not in the event). not used.
# private_id = IntegerProperty() # this is not available in older janus versions. not used.
class VideoroomAttached(VideoroomPluginData):
videoroom = FixedValueProperty('attached')
room = IntegerProperty()
id = IntegerProperty()
display = StringProperty(optional=True)
class VideoroomSlowLink(VideoroomPluginData):
videoroom = FixedValueProperty('slow_link')
# current_bitrate = IntegerProperty() # this is actually defined as 'current-bitrate' in JSON, so we cannot map it to an attribute name. also not used.
class VideoroomConfiguredEvent(VideoroomPluginData):
__event__ = 'configured'
videoroom = FixedValueProperty('event')
room = IntegerProperty()
configured = FixedValueProperty('ok')
class VideoroomLeftEvent(VideoroomPluginData):
__event__ = 'left'
videoroom = FixedValueProperty('event')
room = IntegerProperty()
left = FixedValueProperty('ok')
class VideoroomLeavingEvent(VideoroomPluginData):
__event__ = 'leaving'
videoroom = FixedValueProperty('event')
room = IntegerProperty()
leaving = AbstractProperty() # this is either a participant id or the string "ok"
reason = StringProperty(optional=True)
class VideoroomKickedEvent(VideoroomPluginData):
__event__ = 'kicked'
videoroom = FixedValueProperty('event')
room = IntegerProperty()
kicked = IntegerProperty()
class VideoroomUnpublishedEvent(VideoroomPluginData):
__event__ = 'unpublished'
videoroom = FixedValueProperty('event')
room = IntegerProperty()
unpublished = AbstractProperty() # this is either a participant id or the string "ok"
class VideoroomPausedEvent(VideoroomPluginData):
__event__ = 'paused'
videoroom = FixedValueProperty('event')
room = IntegerProperty()
paused = FixedValueProperty('ok')
class VideoroomSwitchedEvent(VideoroomPluginData):
__event__ = 'switched'
videoroom = FixedValueProperty('event')
room = IntegerProperty()
id = IntegerProperty()
switched = FixedValueProperty('ok')
class VideoroomJoiningEvent(VideoroomPluginData): # only sent if room has notify_joining == True (default is False). Can be used to monitor non-publishers.
__event__ = 'joining'
videoroom = FixedValueProperty('event')
room = IntegerProperty()
joining = ObjectProperty(UserId) # type: UserId
class VideoroomDisplayEvent(VideoroomPluginData): # participant display name change
__event__ = 'display'
videoroom = FixedValueProperty('event')
id = IntegerProperty()
display = StringProperty()
class VideoroomSubstreamEvent(VideoroomPluginData): # simulcast substream change
__event__ = 'substream'
videoroom = FixedValueProperty('event')
room = IntegerProperty()
substream = IntegerProperty()
class VideoroomTemporalEvent(VideoroomPluginData): # simulcast temporal layer change
__event__ = 'temporal'
videoroom = FixedValueProperty('event')
room = IntegerProperty()
temporal = IntegerProperty()
class VideoroomSpatialLayerEvent(VideoroomPluginData): # SVC spatial layer change
__event__ = 'spatial_layer'
videoroom = FixedValueProperty('event')
room = IntegerProperty()
spatial_layer = IntegerProperty()
class VideoroomTemporalLayerEvent(VideoroomPluginData): # SVC temporal layer change
__event__ = 'temporal_layer'
videoroom = FixedValueProperty('event')
room = IntegerProperty()
temporal_layer = IntegerProperty()
# Janus message to model mapping
class ProtocolError(Exception):
pass
class PluginDataHandler(object):
pass
class SIPDataHandler(PluginDataHandler):
__plugin__ = 'sip'
__classmap__ = {cls.__id__: cls for cls in subclasses(SIPPluginData) if cls.__plugin__ in cls.__properties__}