comparison libervia/web/pages/calls/_browser/webrtc.py @ 1604:4a9679369856

browser (call): implements group calls: rel 430
author Goffi <goffi@goffi.org>
date Wed, 15 May 2024 17:40:33 +0200
parents 6feac4a25e60
children
comparison
equal deleted inserted replaced
1603:e105d7719479 1604:4a9679369856
283 on_connection_lost_cb=None, 283 on_connection_lost_cb=None,
284 on_video_devices=None, 284 on_video_devices=None,
285 on_reset_cb=None, 285 on_reset_cb=None,
286 file_only: bool = False, 286 file_only: bool = False,
287 extra_data: dict | None = None, 287 extra_data: dict | None = None,
288 local_video_elt = None,
289 remote_video_elt = None,
290 local_stream = None
288 ): 291 ):
289 """Initialise WebRTC instance. 292 """Initialise WebRTC instance.
290 293
291 @param screen_sharing_cb: callable function for screen sharing event 294 @param screen_sharing_cb: callable function for screen sharing event
292 @param on_connection_established_cb: callable function for connection established 295 @param on_connection_established_cb: callable function for connection established
322 if not file_only: 325 if not file_only:
323 # Initially populate the video devices list 326 # Initially populate the video devices list
324 aio.run(self._populate_video_devices()) 327 aio.run(self._populate_video_devices())
325 328
326 # video elements 329 # video elements
327 self.local_video_elt = document["local_video"] 330 self.local_video_elt = local_video_elt
328 self.remote_video_elt = document["remote_video"] 331 assert remote_video_elt is not None
332 self.remote_video_elt = remote_video_elt
329 else: 333 else:
330 self.file_sender = None 334 self.file_sender = None
335
336 self.local_stream = local_stream
331 337
332 # muting 338 # muting
333 self.is_audio_muted = None 339 self.is_audio_muted = None
334 self.is_video_muted = None 340 self.is_video_muted = None
335 341
648 654
649 @param audio: True if an audio flux is required. 655 @param audio: True if an audio flux is required.
650 @param video: True if a video flux is required. 656 @param video: True if a video flux is required.
651 """ 657 """
652 media_constraints = {"audio": audio, "video": video} 658 media_constraints = {"audio": audio, "video": video}
653 local_stream = await window.navigator.mediaDevices.getUserMedia(media_constraints) 659 if self.local_stream is None:
654 660 self.local_stream = await window.navigator.mediaDevices.getUserMedia(
655 if not local_stream: 661 media_constraints
662 )
663
664 if not self.local_stream:
656 log.error("Failed to get the media stream.") 665 log.error("Failed to get the media stream.")
657 return 666 return
658 667
659 self.local_video_elt.srcObject = local_stream 668 if self.local_video_elt is not None:
660 669 self.local_video_elt.srcObject = self.local_stream
661 for track in local_stream.getTracks(): 670
671 for track in self.local_stream.getTracks():
662 self._peer_connection.addTrack(track) 672 self._peer_connection.addTrack(track)
663 673
664 async def _replace_user_video( 674 async def _replace_user_video(
665 self, 675 self,
666 screen: bool = False, 676 screen: bool = False,
674 media_constraints = {"video": {"cursor": "always"}} 684 media_constraints = {"video": {"cursor": "always"}}
675 new_stream = await window.navigator.mediaDevices.getDisplayMedia( 685 new_stream = await window.navigator.mediaDevices.getDisplayMedia(
676 media_constraints 686 media_constraints
677 ) 687 )
678 else: 688 else:
679 if self.local_video_elt.srcObject: 689 if self.local_video_elt is not None and self.local_video_elt.srcObject:
680 for track in self.local_video_elt.srcObject.getTracks(): 690 for track in self.local_video_elt.srcObject.getTracks():
681 if track.kind == "video": 691 if track.kind == "video":
682 track.stop() 692 track.stop()
683 693
684 media_constraints = { 694 media_constraints = {
702 if not new_video_tracks: 712 if not new_video_tracks:
703 log.error("Failed to retrieve the video track from the new stream.") 713 log.error("Failed to retrieve the video track from the new stream.")
704 return None 714 return None
705 715
706 # Retrieve the current local stream's video track. 716 # Retrieve the current local stream's video track.
707 local_stream = self.local_video_elt.srcObject 717 if self.local_video_elt is None:
718 local_stream = None
719 else:
720 local_stream = self.local_video_elt.srcObject
708 if local_stream: 721 if local_stream:
709 local_video_tracks = [ 722 local_video_tracks = [
710 track for track in local_stream.getTracks() if track.kind == "video" 723 track for track in local_stream.getTracks() if track.kind == "video"
711 ] 724 ]
712 if local_video_tracks: 725 if local_video_tracks:
776 if not new_video_tracks: 789 if not new_video_tracks:
777 log.error("Failed to retrieve the video track from the new stream.") 790 log.error("Failed to retrieve the video track from the new stream.")
778 return 791 return
779 792
780 # Update local video element's stream 793 # Update local video element's stream
781 local_stream = self.local_video_elt.srcObject 794 if self.local_video_elt is None:
795 local_stream = None
796 else:
797 local_stream = self.local_video_elt.srcObject
782 if local_stream: 798 if local_stream:
783 local_video_tracks = [ 799 local_video_tracks = [
784 track for track in local_stream.getTracks() if track.kind == "video" 800 track for track in local_stream.getTracks() if track.kind == "video"
785 ] 801 ]
786 if local_video_tracks: 802 if local_video_tracks:
977 self.sid, 993 self.sid,
978 json.dumps(ice_data), 994 json.dumps(ice_data),
979 ) 995 )
980 self.local_candidates_buffer.clear() 996 self.local_candidates_buffer.clear()
981 997
998 async def prepare_call(
999 self,
1000 audio: bool = True,
1001 video: bool = True
1002 ) -> dict:
1003 """Prepare a call.
1004
1005 Create RTCPeerConnection instance, and get use media.
1006
1007 @param audio: True if an audio flux is required
1008 @param video: True if a video flux is required
1009 @return: Call Data
1010 """
1011 await self._create_peer_connection()
1012 await self._get_user_media(audio, video)
1013 return await self._get_call_data()
1014
982 async def make_call( 1015 async def make_call(
983 self, callee_jid: jid.JID, audio: bool = True, video: bool = True 1016 self, callee_jid: jid.JID, audio: bool = True, video: bool = True
984 ) -> None: 1017 ) -> None:
985 """ 1018 """
986 @param audio: True if an audio flux is required 1019 @param audio: True if an audio flux is required
987 @param video: True if a video flux is required 1020 @param video: True if a video flux is required
988 """ 1021 """
989 await self._create_peer_connection() 1022 call_data = await self.prepare_call(audio, video)
990 await self._get_user_media(audio, video)
991 call_data = await self._get_call_data()
992 log.info(f"calling {callee_jid!r}") 1023 log.info(f"calling {callee_jid!r}")
993 self.sid = await bridge.call_start(str(callee_jid), json.dumps(call_data)) 1024 self.sid = await bridge.call_start(str(callee_jid), json.dumps(call_data))
994 log.debug(f"Call SID: {self.sid}") 1025 log.debug(f"Call SID: {self.sid}")
995 await self._send_buffered_local_candidates() 1026 await self._send_buffered_local_candidates()
996 1027
1096 black_image_data = ( 1127 black_image_data = (
1097 "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0" 1128 "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0"
1098 "lEQVR42mP8/wcAAwAB/uzNq7sAAAAASUVORK5CYII=" 1129 "lEQVR42mP8/wcAAwAB/uzNq7sAAAAASUVORK5CYII="
1099 ) 1130 )
1100 1131
1132 remote_video = self.remote_video_elt
1101 local_video = self.local_video_elt 1133 local_video = self.local_video_elt
1102 remote_video = self.remote_video_elt 1134 if local_video is not None:
1103 if local_video.srcObject: 1135 if local_video.srcObject:
1104 for track in local_video.srcObject.getTracks(): 1136 for track in local_video.srcObject.getTracks():
1105 track.stop() 1137 track.stop()
1106 local_video.src = black_image_data 1138 local_video.src = black_image_data
1107 1139
1108 if remote_video.srcObject: 1140 if remote_video.srcObject:
1109 for track in remote_video.srcObject.getTracks(): 1141 for track in remote_video.srcObject.getTracks():
1110 track.stop() 1142 track.stop()
1111 remote_video.src = black_image_data 1143 remote_video.src = black_image_data
1122 assert media_type in ("audio", "video"), "Invalid media type" 1154 assert media_type in ("audio", "video"), "Invalid media type"
1123 1155
1124 local_video = self.local_video_elt 1156 local_video = self.local_video_elt
1125 is_muted_attr = f"is_{media_type}_muted" 1157 is_muted_attr = f"is_{media_type}_muted"
1126 1158
1127 if local_video.srcObject: 1159 if local_video is not None and local_video.srcObject:
1128 track_getter = getattr( 1160 track_getter = getattr(
1129 local_video.srcObject, f"get{media_type.capitalize()}Tracks" 1161 local_video.srcObject, f"get{media_type.capitalize()}Tracks"
1130 ) 1162 )
1131 for track in track_getter(): 1163 for track in track_getter():
1132 track.enabled = not track.enabled 1164 track.enabled = not track.enabled