diff --git a/lib/models/call_member.dart b/lib/models/call_member.dart index f9d3a82..80a3584 100644 --- a/lib/models/call_member.dart +++ b/lib/models/call_member.dart @@ -1,4 +1,5 @@ import 'package:flutter/material.dart'; +import 'package:flutter_webrtc/media_stream.dart'; /// Single call member information /// @@ -9,10 +10,14 @@ enum MemberStatus { JOINED, READY } class CallMember { final int userID; MemberStatus status; + MediaStream stream; CallMember({ @required this.userID, this.status = MemberStatus.JOINED, }) : assert(userID != null), assert(status != null); + + bool get hasVideoStream => + stream != null && stream.getVideoTracks().length > 0; } diff --git a/lib/ui/screens/call_screen.dart b/lib/ui/screens/call_screen.dart index c486f02..d5de546 100644 --- a/lib/ui/screens/call_screen.dart +++ b/lib/ui/screens/call_screen.dart @@ -324,7 +324,12 @@ class _CallScreenState extends SafeState { // Register callbacks peerConnection.onIceCandidate = (c) => CallsHelper.sendIceCandidate(convID, memberID, c); - peerConnection.onAddStream = (s) => _renderers[memberID].srcObject = s; + peerConnection.onAddStream = (s) { + setState(() { + _membersList.getUser(memberID).stream = s; + _renderers[memberID].srcObject = s; + }); + }; // Request an offer to establish a peer connection await CallsHelper.requestOffer(convID, memberID); @@ -373,7 +378,8 @@ class _CallScreenState extends SafeState { /// Call this when a user has interrupted streaming Future _removeRemotePeerConnection(int memberID) async { - _membersList.getUser(memberID).status = MemberStatus.JOINED; + final member = _membersList.getUser(memberID); + member.status = MemberStatus.JOINED; setState(() {}); if (_peersConnections.containsKey(memberID)) { @@ -385,6 +391,11 @@ class _CallScreenState extends SafeState { await _renderers[memberID].dispose(); _renderers.remove(memberID); } + + if (member.stream != null) { + member.stream.dispose(); + member.stream = null; + } } /// Call this when a member has completely left the call @@ -491,7 +502,8 @@ class _CallScreenState extends SafeState { // Remove peers videos Column( children: _membersList.readyPeers - .where((f) => _renderers.containsKey(f.userID)) + .where( + (f) => f.hasVideoStream && _renderers.containsKey(f.userID)) .map((f) => _buildMemberVideo(f.userID)) .toList(), ), @@ -528,6 +540,7 @@ class _CallScreenState extends SafeState { children: [ // Show / hide user video button _FooterButton( + visible: _canMakeVideoCall, icon: Icon(_isLocalStreamVisible ? Icons.visibility : Icons.visibility_off), @@ -549,6 +562,7 @@ class _CallScreenState extends SafeState { // Toggle video button _FooterButton( + visible: _canMakeVideoCall, icon: Icon(isStreamingVideo && !isVideoMuted ? Icons.videocam : Icons.videocam_off), @@ -583,17 +597,21 @@ class _CallScreenState extends SafeState { class _FooterButton extends StatelessWidget { final Function() onPressed; final Widget icon; + final bool visible; const _FooterButton({ Key key, @required this.icon, @required this.onPressed, + this.visible = true, }) : assert(onPressed != null), assert(icon != null), + assert(visible != null), super(key: key); @override Widget build(BuildContext context) { + if (!visible) return Container(); return Expanded( child: IconButton( icon: icon,