async def handle_message(message): print('<', message) if message['type'] == 'bye': if player: player.stop() recorder.stop() return True if message['type'] == 'offer': await pc.setRemoteDescription(RTCSessionDescription(**message)) await pc.setLocalDescription(await pc.createAnswer()) await signaling.send_message(description_to_dict(pc.localDescription)) if player: player.start() recorder.start() elif message['type'] == 'answer': await pc.setRemoteDescription(RTCSessionDescription(**message)) if player: player.start() recorder.start() elif message['type'] == 'candidate': candidate = candidate_from_sdp(message['candidate'].split(':', 1)[1]) candidate.sdpMid = message['id'] candidate.sdpMLineIndex = message['label'] pc.addIceCandidate(candidate) return False
async def restartIce(self, iceParameters): logging.debug('restartIce()') self._remoteSdp.updateIceParameters(iceParameters) if not self._transportReady: return if self._direction == 'send': # NOTE: aiortc RTCPeerConnection createOffer do not have iceRestart options offer = await self._pc.createOffer() logging.debug( f'restartIce() | calling pc.setLocalDescription() [offer:{offer}]' ) await self._pc.setLocalDescription(offer) answer: RTCSessionDescription = RTCSessionDescription( type='answer', sdp=self._remoteSdp.getSdp()) logging.debug( f'restartIce() | calling pc.setRemoteDescription() [answer:{answer}]' ) await self._pc.setRemoteDescription(answer) else: offer: RTCSessionDescription = RTCSessionDescription( type='offer', sdp=self._remoteSdp.getSdp()) logging.debug( f'restartIce() | calling pc.setRemoteDescription() [offer:{offer}]' ) await self._pc.setRemoteDescription(offer) answer = await self._pc.createAnswer() logging.debug( f'restartIce() | calling pc.setLocalDescription() [answer:{answer}]' ) await self._pc.setLocalDescription(answer)
async def handle_message(message): print('<', message) if message['type'] == 'offer': await pc.setRemoteDescription(RTCSessionDescription(**message)) await pc.setLocalDescription(await pc.createAnswer()) await signaling.send_description(pc.localDescription) elif message['type'] == 'answer': await pc.setRemoteDescription(RTCSessionDescription(**message)) elif message['type'] == 'candidate': candidate = candidate_from_sdp(message['candidate'].split(':', 1)[1]) candidate.sdpMid = message['id'] candidate.sdpMLineIndex = message['label'] pc.addIceCandidate(candidate)
def unity_object_from_string(message_str): message = json.loads(message_str) if message["type"] == "sdp": if "answer" in message: return RTCSessionDescription(type="answer", sdp=message["answer"]) else: return RTCSessionDescription(type="offer", sdp=message["offer"]) elif message["type"] == "ice" and message["candidate"]: candidate = candidate_from_sdp(message["candidate"].split(":", 1)[1]) candidate.sdpMid = message["sdpMid"] candidate.sdpMLineIndex = message["sdpMLineindex"] return candidate elif message["type"] == "bye": return BYE
async def receive(self, trackId: str, kind: MediaKind, rtpParameters: RtpParameters) -> HandlerReceiveResult: options = HandlerReceiveOptions(trackId=trackId, kind=kind, rtpParameters=rtpParameters) self._assertRecvDirection() logging.debug( f'receive() [trackId:{options.trackId}, kind:{options.kind}]') localId = options.rtpParameters.mid if options.rtpParameters.mid != None else str( len(self._mapMidTransceiver)) self.remoteSdp.receive(mid=localId, kind=options.kind, offerRtpParameters=options.rtpParameters, streamId=options.rtpParameters.rtcp.cname, trackId=options.trackId) offer: RTCSessionDescription = RTCSessionDescription( type='offer', sdp=self.remoteSdp.getSdp()) logging.debug( f'receive() | calling pc.setRemoteDescription() [offer:{offer}]') await self.pc.setRemoteDescription(offer) answer: RTCSessionDescription = await self.pc.createAnswer() localSdpDict = sdp_transform.parse(answer.sdp) answerMediaDict = [ m for m in localSdpDict.get('media') if str(m.get('mid')) == localId ][0] # May need to modify codec parameters in the answer based on codec # parameters in the offer. applyCodecParameters(offerRtpParameters=options.rtpParameters, answerMediaDict=answerMediaDict) answer = RTCSessionDescription(type='answer', sdp=sdp_transform.write(localSdpDict)) if not self._transportReady: await self._setupTransport(localDtlsRole='client', localSdpDict=localSdpDict) logging.debug( f'receive() | calling pc.setLocalDescription() [answer:{answer}]') await self.pc.setLocalDescription(answer) transceivers = [ t for t in self.pc.getTransceivers() if t.mid == localId ] if not transceivers: raise Exception('new RTCRtpTransceiver not found') # Store in the map. transceiver = transceivers[0] self._mapMidTransceiver[localId] = transceiver return HandlerReceiveResult(localId=localId, track=transceiver.receiver.track, rtpReceiver=transceiver.receiver)
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 run_answer(pc): # done = asyncio.Event() # @pc.on('datachannel') # def on_datachannel(channel): # @channel.on('message') # def on_message(message): # # reply # message = 'pong' # channel_log(channel, '>', message) # channel.send(message) # # quit # done.set() # # receive offer # print('-- Please enter remote offer --') # offer_json = json.loads(input()) # await pc.setRemoteDescription(RTCSessionDescription( # sdp=offer_json['sdp'], # type=offer_json['type'])) # print() # # send answer # await pc.setLocalDescription(await pc.createAnswer()) # answer = pc.localDescription # print('-- Your answer --') # print(json.dumps({ # 'sdp': answer.sdp, # 'type': answer.type # })) # print() # await done.wait() #
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 }))
async def offer(request): global camera params = await request.json() offer = RTCSessionDescription(sdp=params["sdp"], type=params["type"]) video_track = H264EncodedStreamTrack(RATE) camera = GstH264Camera(RATE, video_track, rtsp_input, webcam_input) 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 offer(request): params = await request.json() offer = RTCSessionDescription( sdp=params['sdp'], type=params['type']) pc = RTCPeerConnection() pc._consumers = [] pcs.append(pc) @pc.on('track') def on_track(track): if track.kind == 'audio': pc._consumers.append(asyncio.ensure_future(consume_audio(track))) if track.kind == 'video': pc._consumers.append(asyncio.ensure_future(consume_video(track))) 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 run_offer(pc): # add video track width = 320 height = 240 local_video = CombinedVideoStreamTrack(tracks=[ ColorVideoStreamTrack(width=width, height=height, color=BLUE), ColorVideoStreamTrack(width=width, height=height, color=GREEN), ColorVideoStreamTrack(width=width, height=height, color=RED), ]) pc.addTrack(local_video) # send offer await pc.setLocalDescription(await pc.createOffer()) offer = pc.localDescription print('-- Your offer --') print(json.dumps({ 'sdp': offer.sdp, 'type': offer.type })) print() # receive answer print('-- Please enter remote answer --') answer_json = json.loads(input()) await pc.setRemoteDescription(RTCSessionDescription( sdp=answer_json['sdp'], type=answer_json['type'])) print() print('Sending video for 10s') await asyncio.sleep(10)
async def run_answer(pc): remote_track = None @pc.on('track') def on_track(track): nonlocal remote_track assert track.kind == 'video' remote_track = track # receive offer print('-- Please enter remote offer --') offer_json = json.loads(input()) await pc.setRemoteDescription(RTCSessionDescription( sdp=offer_json['sdp'], type=offer_json['type'])) print() # send answer await pc.setLocalDescription(await pc.createAnswer()) answer = pc.localDescription print('-- Your answer --') print(json.dumps({ 'sdp': answer.sdp, 'type': answer.type })) print() print('Receiving video, press CTRL-C to stop') while True: frame = await remote_track.recv() data_bgr = frame_to_bgr(frame) cv2.imwrite(OUTPUT_PATH, data_bgr)
async def __offer(self, request): """ Generates JSON Response with a WebRTC Peer Connection of Video Server. """ # get offer from params params = await request.json() offer = RTCSessionDescription(sdp=params["sdp"], type=params["type"]) # initiate stream if not (self.__default_rtc_server is None) and not (self.__default_rtc_server.is_launched): if self.__logging: logger.debug("Initiating Video Streaming.") self.__default_rtc_server.launch() # setup RTC peer connection - interface represents a WebRTC connection # between the local computer and a remote peer. pc = RTCPeerConnection() self.__pcs.add(pc) if self.__logging: logger.info("Created WebRTC Peer Connection.") # track ICE connection state changes @pc.on("iceconnectionstatechange") async def on_iceconnectionstatechange(): logger.debug("ICE connection state is %s" % pc.iceConnectionState) if pc.iceConnectionState == "failed": logger.error("ICE connection state failed.") # check if Live Broadcasting is enabled if self.__relay is None: # if not, close connection. await pc.close() self.__pcs.discard(pc) # Change the remote description associated with the connection. await pc.setRemoteDescription(offer) # retrieve list of RTCRtpTransceiver objects that are currently attached to the connection for t in pc.getTransceivers(): # Increments performance significantly, IDK why this works as H265 codec is not even supported :D capabilities = RTCRtpSender.getCapabilities("video") preferences = list( filter(lambda x: x.name == "H265", capabilities.codecs)) t.setCodecPreferences(preferences) # add video server to peer track if t.kind == "video": pc.addTrack( self.__relay.subscribe(self.config["server"]) if not ( self.__relay is None) else self.config["server"]) # Create an SDP answer to an offer received from a remote peer answer = await pc.createAnswer() # Change the local description for the answer await pc.setLocalDescription(answer) # return Starlette json response return JSONResponse({ "sdp": pc.localDescription.sdp, "type": pc.localDescription.type })
async def object_from_string(message_str): if(isinstance(message_str,list)): return message_str message = None; if(isinstance(message_str,dict)): if "sdp" in message_str["message"]: message = message_str["message"]["sdp"] if "candidate" in message_str["message"]: message = message_str["message"] #["candidate"] if "stick" in message_str["message"]: message = message_str["message"] else: message = json.loads(message_str) if message is not None: if "type" in message and message["type"] in ["answer", "offer"]: return RTCSessionDescription(**message) elif ("type" in message and message["type"] == "candidate" and message["candidate"]) or message["candidate"]: candidate = None if(isinstance(message["candidate"], dict )): candidate = candidate_from_sdp(message["candidate"]["candidate"].split(":", 1)[1]) else: candidate = candidate_from_sdp(message["candidate"].split(":", 1)[1]) if(isinstance(message["candidate"], dict )): candidate.sdpMid = message["candidate"]["sdpMid"] candidate.sdpMLineIndex = message["candidate"]["sdpMLineIndex"] else: candidate.sdpMid = message["id"] candidate.sdpMLineIndex = message["label"] return candidate elif message["type"] == "bye": return BYE return message
def test_bad_type(self): with self.assertRaises(ValueError) as cm: RTCSessionDescription(sdp='v=0\r\n', type='bogus') self.assertEqual( str(cm.exception), "'type' must be in ['offer', 'pranswer', 'answer', 'rollback'] (got 'bogus')" )
def test_webgear_rtc_options(options): """ Test for various WebGear_RTC API internal options """ try: web = WebGear_RTC(source=return_testvideo_path(), logging=True, **options) client = TestClient(web(), raise_server_exceptions=True) response = client.get("/") assert response.status_code == 200 (offer_pc, data) = get_RTCPeer_payload() response_rtc_answer = client.post( "/offer", data=data, headers={"Content-Type": "application/json"}, ) params = response_rtc_answer.json() answer = RTCSessionDescription(sdp=params["sdp"], type=params["type"]) run(offer_pc.setRemoteDescription(answer)) response_rtc_offer = client.get( "/offer", data=data, headers={"Content-Type": "application/json"}, ) assert response_rtc_offer.status_code == 200 run(offer_pc.close()) web.shutdown() except Exception as e: if isinstance(e, AssertionError): logger.exception(str(e)) elif isinstance(e, requests.exceptions.Timeout): logger.exceptions(str(e)) else: pytest.fail(str(e))
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() 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("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 video = create_local_tracks(True) 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) 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 run_caller_rtc(pc): channel = pc.createDataChannel("matrix-webRTC-ping-pong-chat") channel_log(channel, "-", "created by local party") async def send_message(): while True: message = await output_message_queue.aget() channel.send(message) print("waiting for message...") @channel.on("open") def on_open(): print() print() print("----------WebRTC Connection Established--------------") print() received_event.set() asyncio.ensure_future(send_message()) @channel.on("message") async def on_message(message): await input_message_queue.aput(message) # send offer offer = await pc.createOffer() await pc.setLocalDescription(offer) await aio_to_matrix_queue.aput(pc.localDescription) answer = await matrix_to_aio_queue.aget() rtc_description = RTCSessionDescription(answer['sdp'], answer['type']) await pc.setRemoteDescription(rtc_description)
async def publish(plugin, player): """ Send video to the room. """ pc = RTCPeerConnection() pcs.add(pc) # configure media media = {"audio": False, "video": True} if player: print("Play") pc.addTrack(player) else: pc.addTrack(VideoStreamTrack()) # send offer await pc.setLocalDescription(await pc.createOffer()) request = {"request": "configure"} request.update(media) response = await plugin.send({ "body": request, "jsep": { "sdp": pc.localDescription.sdp, "trickle": False, "type": pc.localDescription.type, }, }) # apply answer await pc.setRemoteDescription( RTCSessionDescription(sdp=response["jsep"]["sdp"], type=response["jsep"]["type"]))
async def run_answer(pc): done = asyncio.Event() @pc.on('datachannel') def on_datachannel(channel): @channel.on('message') def on_message(message): # reply message = 'pong' channel_log(channel, '>', message) channel.send(message) # quit done.set() # receive offer print('-- Please enter remote offer --') offer_json = json.loads(input()) await pc.setRemoteDescription( RTCSessionDescription(sdp=offer_json['sdp'], type=offer_json['type'])) print() # send answer await pc.setLocalDescription(await pc.createAnswer()) answer = pc.localDescription print('-- Your answer --') print(json.dumps({'sdp': answer.sdp, 'type': answer.type})) print() await done.wait()
async def process_ice(self, message: Any) -> None: if self.connection_state == "HELLO" and message["kind"] == "OFFER": offer = RTCSessionDescription(sdp=message["sdp"], type=message["type"]) print(self.connection_id, "Received offer") self.create_peerconnection() assert self.pc is not None await self.pc.setRemoteDescription(offer) answer = await self.pc.createAnswer() answer.sdp += "\na=fmtp:111 minptime=10;useinbandfec=0;maxaveragebitrate=262144;stereo=1;sprop-stereo=1;cbr=1" await self.pc.setLocalDescription(answer) assert self.websocket is not None await self.websocket.send( json.dumps({ "kind": "ANSWER", "type": self.pc.localDescription.type, "sdp": self.pc.localDescription.sdp, })) self.connection_state = "ANSWER" print(self.connection_id, "Sent answer") else: print( self.connection_state, "Incorrect kind {} for state {}".format( message["kind"], self.connection_state), )
async def run_offer(pc): done = asyncio.Event() channel = pc.createDataChannel('chat') channel_log(channel, '-', 'created by local party') channel_watch(channel) @channel.on('message') def on_message(message): # quit done.set() # send offer await pc.setLocalDescription(await pc.createOffer()) offer = pc.localDescription print('-- Your offer --') print(json.dumps({'sdp': offer.sdp, 'type': offer.type})) print() # receive answer print('-- Please enter remote answer --') answer_json = json.loads(input()) await pc.setRemoteDescription( RTCSessionDescription(sdp=answer_json['sdp'], type=answer_json['type'])) print() # send message message = 'ping' channel_log(channel, '>', message) channel.send(message) await done.wait()
def _webrtc_thread_impl( self, sdp: str, type_: str, ): logger.debug("_webrtc_thread_impl starts", ) loop = asyncio.new_event_loop() self._loop = loop offer = RTCSessionDescription(sdp, type_) def callback(localDescription): self._answer_queue.put(localDescription) video_transformer = None if self.video_transformer_factory: video_transformer = self.video_transformer_factory() video_receiver = None if self.mode == WebRtcMode.SENDONLY: video_receiver = VideoReceiver(queue_maxsize=1) audio_receiver = None if self.mode == WebRtcMode.SENDONLY: audio_receiver = AudioReceiver(queue_maxsize=1) self._video_transformer = video_transformer self._video_receiver = video_receiver self._audio_receiver = audio_receiver @self.pc.on("iceconnectionstatechange") async def on_iceconnectionstatechange(): iceConnectionState = self.pc.iceConnectionState if iceConnectionState == "closed" or iceConnectionState == "failed": self._unset_transformers() loop.create_task( _process_offer( self.mode, self.pc, offer, player_factory=self.player_factory, in_recorder_factory=self.in_recorder_factory, out_recorder_factory=self.out_recorder_factory, video_transformer=video_transformer, video_receiver=video_receiver, audio_receiver=audio_receiver, async_transform=self.async_transform, callback=callback, )) try: loop.run_forever() finally: logger.debug("Event loop %s has stopped.", loop) loop.run_until_complete(self.pc.close()) loop.run_until_complete(loop.shutdown_asyncgens()) loop.close() logger.debug("Event loop %s cleaned up.", loop)
async def receive(self): print('-- Please enter remote description --') descr_dict = json.loads(input()) print() return RTCSessionDescription( sdp=descr_dict['sdp'], type=descr_dict['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) await pc.setRemoteDescription(offer) #for t in pc.getTransceivers(): pc.addTrack(FlagVideoStreamTrack()) 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(self, request): await check_permission(request, 'realtime_video') params = await request.json() offer = RTCSessionDescription(sdp=params["sdp"], type=params["type"]) pc = RTCPeerConnection() self._pcs.add(pc) logger.info(f"Created for {request.remote}") track = await self._get_video_fun() @pc.on("connectionstatechange") async def on_connectionstatechange(): logger.info(f"Connection state: {pc.connectionState}") # закрытие подлючения if pc.connectionState == "failed": await pc.close() track.stop() self._pcs.discard(pc) await pc.setRemoteDescription(offer) if track: pc.addTrack(track) # отправить 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(self, request): params = await request.json() offer = RTCSessionDescription(sdp=params["sdp"], type=params["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) await pc.setRemoteDescription(offer) pc.addTrack(self.video_tracker.prepare()) 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 accept_offer(_sdp, _type): offer = RTCSessionDescription(sdp=_sdp, type=_type) options = {"framerate": "10", "video_size": "640x480"} # player = MediaPlayer("/dev/video0", format="v4l2", options=options) audio_track = CustomAudioStreamTrack() #"/dev/video0" video_track = OpenCVVideoStreamTrack(video_device=0, options=options) # handle offer await PC.setRemoteDescription(offer) # await recorder.start() # dont wait to get track, we only want to send out for t in PC.getTransceivers(): if t.kind == "audio" and audio_track: PC.addTrack(audio_track) # elif t.kind == "video" and player.video: elif t.kind == "video" and video_track: PC.addTrack(video_track) # send answer answer = await PC.createAnswer() await PC.setLocalDescription(answer) return answer