def host_room(room, username): queue = Queue() threading.Thread(target=get_messages_thread, args=(queue, username)).start() rtc = RTCPeerConnection(rtcConfiguration) channel = Channel(rtc.createDataChannel("data", negotiated=True, id=0)) offer = run(rtc.createOffer()) run(rtc.setLocalDescription(offer)) res = post("/api/host/%s" % room, { "room": room, "offer": object_to_string(offer) }, username) print("Got res %s" % res) run(rtc.setRemoteDescription(object_from_string(res["answer"]))) for candidate in rtc.sctp.transport.transport.iceGatherer.getLocalCandidates( ): send_message(username, res["username"], "ice", fix_candidate(object_to_string(candidate))) time.sleep(3) while not queue.empty(): for message in queue.get(): if message["type"] == "ice": print("Got candidate: %s" % message["data"]) rtc.sctp.transport.transport.addRemoteCandidate( object_from_string(fix_candidate2(message["data"]))) rtc.sctp.transport.transport.addRemoteCandidate(None) return channel, rtc
async def run(pc): session = ClientSession() async with session.ws_connect("ws://39.102.116.49:8080") as ws: async for msg in ws: if msg.type == WSMsgType.TEXT: data = json.loads(msg.data) if data["type"] == "offerOrAnswer": await pc.setRemoteDescription( object_from_string(json.dumps(data["msg"]))) if data["msg"]["type"] == "offer": pc.addTrack(FlagVideoStreamTrack()) await pc.setLocalDescription(await pc.createAnswer()) await ws.send_str( json.dumps({ "type": "offerOrAnswer", "msg": json.loads( object_to_string(pc.localDescription)), })) elif data["type"] == "candidate": try: await pc.addIceCandidate( object_from_string(json.dumps(data["msg"]))) except: pass
async def websocket_coroutine(): session = aiohttp.ClientSession() async with session.ws_connect(WEBSOCKET_URI) as ws: print("websocket connected") request = json.dumps({ "what": "call" }) await ws.send_str(request) async for msg in ws: print(msg) if msg.type == aiohttp.WSMsgType.TEXT: params = json.loads(msg.data) print(params) if params["what"] == "offer": print("offer received") uv4l_sdp = object_from_string(params["data"]) await pc.setRemoteDescription(uv4l_sdp) await pc.setLocalDescription(await pc.createAnswer()) local_sdp = object_to_string(pc.localDescription) print(local_sdp) print(type(local_sdp)) await ws.send_str(json.dumps({ "what": "answer", "data": local_sdp })) elif params['what'] == 'hangup': print("hangup received") await ws.close() break else: print(msg) elif msg.type == aiohttp.WSMsgType.ERROR: break
async def create_offer(self): channel = self.pc.createDataChannel("chat") self.channel = channel await self.pc.setLocalDescription(await self.pc.createOffer()) return object_to_string(self.pc.localDescription)
async def _set_offer(self, pc, signaling): await signaling.connect() channel = pc.createDataChannel("chat") self.channel = channel @channel.on("open") def on_open(): asyncio.ensure_future(self.send(channel)) @channel.on("message") def on_message(message): self.process_msg(message, channel) await pc.setLocalDescription(await pc.createOffer()) local_description = object_to_string(pc.localDescription) response = { MSG_FIELD.TYPE: NODE_EVENTS.WEBRTC_OFFER, MSG_FIELD.PAYLOAD: local_description, MSG_FIELD.FROM: self._origin, } forward_payload = { MSG_FIELD.TYPE: GRID_EVENTS.FORWARD, MSG_FIELD.DESTINATION: self._destination, MSG_FIELD.CONTENT: response, } self._grid.send(json.dumps(forward_payload)) await self.consume_signaling(pc, signaling)
async def consume_signaling(self, pc, signaling): while True: if self._msg == "": await asyncio.sleep(5) continue obj = object_from_string(self._msg) if isinstance(obj, RTCSessionDescription): await pc.setRemoteDescription(obj) if obj.type == "offer": # send answer await pc.setLocalDescription(await pc.createAnswer()) local_description = object_to_string(pc.localDescription) response = { MSG_FIELD.TYPE: NODE_EVENTS.WEBRTC_ANSWER, MSG_FIELD.FROM: self._origin, MSG_FIELD.PAYLOAD: local_description, } forward_payload = { MSG_FIELD.TYPE: GRID_EVENTS.FORWARD, MSG_FIELD.DESTINATION: self._destination, MSG_FIELD.CONTENT: response, } self._grid.send(json.dumps(forward_payload)) elif isinstance(obj, RTCIceCandidate): pc.addIceCandidate(obj) elif obj is BYE: print("Exiting") break self._msg = ""
async def consume_signaling(self, pc, signaling): # Async keep-alive connection thread while self.available: sleep_time = 0 if self._msg == "": await asyncio.sleep(sleep_time) continue obj = object_from_string(self._msg) if isinstance(obj, RTCSessionDescription): await pc.setRemoteDescription(obj) if obj.type == "offer": # send answer await pc.setLocalDescription(await pc.createAnswer()) local_description = object_to_string(pc.localDescription) response = { MSG_FIELD.TYPE: NODE_EVENTS.WEBRTC_ANSWER, MSG_FIELD.FROM: self._origin, MSG_FIELD.PAYLOAD: local_description, } forward_payload = { MSG_FIELD.TYPE: GRID_EVENTS.FORWARD, MSG_FIELD.DESTINATION: self._destination, MSG_FIELD.CONTENT: response, } self._grid.send(json.dumps(forward_payload)) sleep_time = 10 self._msg = "" raise Exception
async def send(self, obj): message = object_to_string(obj) print('>', message) await self.websocket.send(json.dumps({ 'cmd': 'send', 'msg': message, }))
def receive_sync(self): loop = asyncio.get_event_loop() message = loop.run_until_complete(self.receive()) if message and self._javascript_callable: message = object_to_string(message) print('receive:', message) message = json.loads(message) message = IPython.display.JSON(message) return message
async def create_answer(self, offer): offer = object_from_string(offer) await self.pc.setRemoteDescription(offer) await self.pc.setLocalDescription(await self.pc.createAnswer()) @self.pc.on("datachannel") def on_datachannel(channel): self.channel = channel return object_to_string(self.pc.localDescription)
async def send(self, obj): message = object_to_string(obj) logger.debug("> " + message) if self.__is_initiator: await self._http.post(self.__post_url, data=message) else: await self._websocket.send( json.dumps({ "cmd": "send", "msg": message }))
async def send(self, obj): message = object_to_string(obj) print('>', message) if self.__is_initiator: await self.client.post(self.__post_url, data=message) else: await self.websocket.send( json.dumps({ 'cmd': 'send', 'msg': message, }))
def test_candidate_to_string(self): candidate = RTCIceCandidate(component=1, foundation='0', ip='192.168.99.7', port=33543, priority=2122252543, protocol='UDP', type='host') candidate.sdpMid = 'audio' candidate.sdpMLineIndex = 0 self.assertEqual(object_to_string( candidate ), '{"candidate": "candidate:0 1 UDP 2122252543 192.168.99.7 33543 typ host", "id": "audio", "label": 0, "type": "candidate"}' ) # noqa
def test_candidate_to_string(self): candidate = RTCIceCandidate( component=1, foundation="0", ip="192.168.99.7", port=33543, priority=2122252543, protocol="UDP", type="host", ) candidate.sdpMid = "audio" candidate.sdpMLineIndex = 0 self.assertEqual( object_to_string(candidate), '{"candidate": "candidate:0 1 UDP 2122252543 192.168.99.7 33543 typ host", "id": "audio", "label": 0, "type": "candidate"}', )
async def _set_offer(self) -> str: """Initialize a Real Time Communication Data Channel, set datachannel callbacks/tasks, and send offer payload message. :return: returns a signaling offer payload containing local description. :rtype: str """ try: # Use the Peer Connection structure to # set the channel as a RTCDataChannel. self.channel = self.peer_connection.createDataChannel( "datachannel") # This method will be called by as a callback # function by the aioRTC lib when the when # the connection opens. @self.channel.on("open") async def on_open() -> None: # type : ignore self.__producer_task = asyncio.ensure_future(self.producer()) # This method is the aioRTC "consumer" task # and will be running as long as connection remains. # At this point we're just setting the method behavior # It'll start running after the connection opens. @self.channel.on("message") async def on_message( message: Union[bin, str]) -> None: # type: ignore # Forward all received messages to our own consumer method. await self.consumer(msg=message) # Set peer_connection to generate an offer message type. await self.peer_connection.setLocalDescription( await self.peer_connection.createOffer()) # Generates the local description structure # and serialize it to string afterwards. local_description = object_to_string( self.peer_connection.localDescription) # Return the Offer local_description payload. return local_description except Exception as e: log = f"Got an exception in WebRTCConnection _set_offer. {e}" logger.error(log) raise e
async def WebRTCSignalingService(request): ws = web.WebSocketResponse() await ws.prepare(request) async for msg in ws: print(msg) if msg.type == aiohttp.WSMsgType.TEXT: params = json.loads(msg.data) print(type(params)) print(params) if params["what"] == "call": print("call received") # prepare media pc.addTrack(VideoStreamTrack) await pc.setLocalDescription(await pc.createOffer()) uv4l_sdp = object_to_string(pc.localDescription) print(type(uv4l_sdp)) await ws.send_str( json.dumps({ "what": "offer", "data": uv4l_sdp })) elif params["what"] == "answer": print("answer received") local_sdp = object_from_string(params["data"]) await pc.setRemoteDescription(local_sdp) print('setRemoteDescription(<local_sdp>)') # await pc.setRemoteDescription(local_sdp) elif params["what"] == "addIceCandidate": print("addIceCandidate received") elif params['what'] == 'hangup': print("hangup received") await ws.close() else: await ws.send_json(msg.data + '\n received.') elif msg.type == aiohttp.WSMsgType.ERROR: print('ws connection closed with exception %s' % ws.exception()) print('websocket connection closed') return ws
async def consume_signaling(self, pc, signaling): """Consume signaling to go through all the webrtc connection protocol. Args: pc: Peer Connection. signaling: Webrtc signaling instance. Exception: ConnectionClosedException: Exception used to finish this connection and close this thread. """ # Async keep-alive connection thread while self.available: sleep_time = 0 if self._msg == "": await asyncio.sleep(sleep_time) continue obj = object_from_string(self._msg) if isinstance(obj, RTCSessionDescription): await pc.setRemoteDescription(obj) if obj.type == "offer": # send answer await pc.setLocalDescription(await pc.createAnswer()) local_description = object_to_string(pc.localDescription) response = { MSG_FIELD.TYPE: NODE_EVENTS.WEBRTC_ANSWER, MSG_FIELD.FROM: self._origin, MSG_FIELD.PAYLOAD: local_description, } forward_payload = { MSG_FIELD.TYPE: GRID_EVENTS.FORWARD, MSG_FIELD.DESTINATION: self._destination, MSG_FIELD.CONTENT: response, } self._grid.send(json.dumps(forward_payload)) sleep_time = 10 self._msg = "" raise Exception
async def _process_answer(self, payload: str) -> Union[str, None]: # Converts payload received by # the other peer in aioRTC Object # instance. try: msg = object_from_string(payload) # Check if Object instance is a # description of RTC Session. if isinstance(msg, RTCSessionDescription): # Use the target's network address/metadata # to set the remote description of this peer. # This will basically say to this peer how to find/connect # with to other peer. await self.peer_connection.setRemoteDescription(msg) # If it's an offer message type, # generates your own local description # and send it back in order to tell # to the other peer how to find you. if msg.type == "offer": # Set peer_connection to generate an offer message type. await self.peer_connection.setLocalDescription( await self.peer_connection.createAnswer()) # Generates the local description structure # and serialize it to string afterwards. local_description = object_to_string( self.peer_connection.localDescription) # Returns the answer peer's local description return local_description except Exception as e: log = f"Got an exception in WebRTCConnection _process_answer. {e}" logger.error(log) raise e return None
async def send(self, obj): message = object_to_string(obj) print('>', message) await self._http.post(self.__post_url, data=message)
def test_bye_to_string(self): self.assertEqual(object_to_string(BYE), '{"type": "bye"}')
async def _set_offer(self) -> str: """ Initialize a Real-Time Communication Data Channel, set data channel callbacks/tasks, and send offer payload message. :return: returns a signaling offer payload containing local description. :rtype: str """ try: # Use the Peer Connection structure to # set the channel as a RTCDataChannel. self.channel = self.peer_connection.createDataChannel( "datachannel", ) # Keep send buffer busy with chunks self.channel.bufferedAmountLowThreshold = 4 * DC_MAX_CHUNK_SIZE # This method will be called by aioRTC lib as a callback # function when the connection opens. @self.channel.on("open") async def on_open() -> None: # type : ignore self.__producer_task = asyncio.ensure_future(self.producer()) chunked_msg = [] chunks_pending = 0 # This method is the aioRTC "consumer" task # and will be running as long as connection remains. # At this point we're just setting the method behavior # It'll start running after the connection opens. @self.channel.on("message") async def on_message(raw: bytes) -> None: nonlocal chunked_msg, chunks_pending chunk = OrderedChunk.load(raw) message = chunk.data if message == DC_CHUNK_START_SIGN: chunks_pending = chunk.idx chunked_msg = [b""] * chunks_pending elif chunks_pending: if chunked_msg[chunk.idx] == b"": chunks_pending -= 1 chunked_msg[chunk.idx] = message if chunks_pending == 0: await self.consumer(msg=b"".join(chunked_msg)) else: # Forward all received messages to our own consumer method. await self.consumer(msg=message) # Set peer_connection to generate an offer message type. await self.peer_connection.setLocalDescription( await self.peer_connection.createOffer() ) # Generates the local description structure # and serialize it to string afterwards. local_description = object_to_string(self.peer_connection.localDescription) # Return the Offer local_description payload. return local_description except Exception as e: traceback_and_raise(e)
async def send(self, message): if not self._javascript_callable or type(message) != str: message = object_to_string(message) self._webrtc_server.send_message(self._room, self.__peer_id, message)