Mercurial > libervia-web
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 |