@@ -8,6 +8,7 @@ import 'package:async/async.dart' show CancelableOperation;
88import 'package:collection/collection.dart' ;
99import 'package:internet_connection_checker_plus/internet_connection_checker_plus.dart' ;
1010import 'package:meta/meta.dart' ;
11+ import 'package:rxdart/transformers.dart' ;
1112import 'package:stream_webrtc_flutter/stream_webrtc_flutter.dart' as rtc;
1213import 'package:stream_webrtc_flutter/stream_webrtc_flutter.dart' ;
1314import 'package:synchronized/synchronized.dart' ;
@@ -87,6 +88,7 @@ const _idConnect = 6;
8788const _idAwait = 7 ;
8889const _idFastReconnectTimeout = 8 ;
8990const _idReconnect = 9 ;
91+ const _idNativeWebRtc = 10 ;
9092
9193const _tag = 'SV:Call' ;
9294int _callSeq = 1 ;
@@ -381,12 +383,20 @@ class Call {
381383 _observeState ();
382384 _observeReconnectEvents ();
383385 _observeUserId ();
386+ _observeNativeWebRtcEventStream ();
384387
385388 _logger.v (() => '[_init] initialized' );
386389 _initialized = true ;
387390 });
388391 }
389392
393+ void _observeNativeWebRtcEventStream () {
394+ _subscriptions.add (
395+ _idNativeWebRtc,
396+ _onNativeWebRtcEvent (),
397+ );
398+ }
399+
390400 void _observeState () {
391401 _subscriptions.add (
392402 _idState,
@@ -458,6 +468,52 @@ class Call {
458468 state.settings.audio.redundantCodingEnabled;
459469 }
460470
471+ StreamSubscription <NativeWebRtcEvent > _onNativeWebRtcEvent () {
472+ return RtcMediaDeviceNotifier .instance.nativeWebRtcEventsStream ().listen ((
473+ event,
474+ ) {
475+ _logger.d (
476+ () => '[_onNativeWebRtcEvent] screenSharingStopped: $event ' ,
477+ );
478+
479+ switch (event) {
480+ case ScreenSharingStoppedEvent _:
481+ if (CurrentPlatform .isIos) {
482+ // On iOS only one broadcast extension can be active at a time
483+ setScreenShareEnabled (enabled: false );
484+ } else {
485+ final trackId = event.data? ['trackId' ] as String ? ;
486+ if (trackId != null && state.value.localParticipant != null ) {
487+ final track = getTrack (
488+ state.value.localParticipant! .trackIdPrefix,
489+ SfuTrackType .screenShare,
490+ );
491+
492+ if (track? .mediaTrack.id == trackId) {
493+ setScreenShareEnabled (enabled: false );
494+ }
495+ }
496+ }
497+ break ;
498+ case ScreenSharingStartedEvent _:
499+ _stateManager.participantSetScreenShareEnabled (
500+ enabled: true ,
501+ );
502+
503+ _connectOptions = _connectOptions.copyWith (
504+ screenShare: TrackOption .enabled (
505+ constraints: const ScreenShareConstraints (
506+ useiOSBroadcastExtension: true ,
507+ ),
508+ ),
509+ );
510+ break ;
511+ default :
512+ return ;
513+ }
514+ });
515+ }
516+
461517 Future <void > _onCoordinatorEvent (StreamCallEvent event) async {
462518 // Return if the event is not for this call.
463519 if (event.callCid != state.value.callCid) return ;
@@ -559,7 +615,6 @@ class Call {
559615 return _stateManager.callMetadataChanged (event.metadata);
560616 case StreamCallSessionStartedEvent _:
561617 return _stateManager.callMetadataChanged (event.metadata);
562-
563618 default :
564619 break ;
565620 }
@@ -1906,15 +1961,21 @@ class Call {
19061961 if (cameraOption is TrackProvided ) {
19071962 return _setLocalTrack (cameraOption.track);
19081963 } else if (cameraOption is TrackEnabled ) {
1964+ final constraints = cameraOption.constraints is CameraConstraints
1965+ ? cameraOption.constraints as CameraConstraints ?
1966+ : null ;
1967+
19091968 return setCameraEnabled (
19101969 enabled: true ,
1911- constraints: CameraConstraints (
1912- facingMode: facingMode,
1913- deviceId: deviceId,
1914- params:
1915- targetResolution? .toVideoParams () ??
1916- RtcVideoParametersPresets .h720_16x9,
1917- ),
1970+ constraints:
1971+ constraints ??
1972+ CameraConstraints (
1973+ facingMode: facingMode,
1974+ deviceId: deviceId,
1975+ params:
1976+ targetResolution? .toVideoParams () ??
1977+ RtcVideoParametersPresets .h720_16x9,
1978+ ),
19181979 );
19191980 }
19201981
@@ -1925,7 +1986,10 @@ class Call {
19251986 if (microphoneOption is TrackProvided ) {
19261987 await _setLocalTrack (microphoneOption.track);
19271988 } else if (microphoneOption is TrackEnabled ) {
1928- await setMicrophoneEnabled (enabled: true );
1989+ final constraints = microphoneOption.constraints is AudioConstraints
1990+ ? microphoneOption.constraints as AudioConstraints ?
1991+ : null ;
1992+ await setMicrophoneEnabled (enabled: true , constraints: constraints);
19291993 }
19301994 }
19311995
@@ -1936,15 +2000,22 @@ class Call {
19362000 if (screenShareOption is TrackProvided ) {
19372001 await _setLocalTrack (screenShareOption.track);
19382002 } else if (screenShareOption is TrackEnabled ) {
2003+ final constraints =
2004+ screenShareOption.constraints is ScreenShareConstraints
2005+ ? screenShareOption.constraints as ScreenShareConstraints ?
2006+ : null ;
2007+
19392008 await setScreenShareEnabled (
19402009 enabled: true ,
1941- constraints: ScreenShareConstraints (
1942- params:
1943- targetResolution? .toVideoParams (
1944- defaultBitrate: RtcVideoParametersPresets .k1080pBitrate,
1945- ) ??
1946- RtcVideoParametersPresets .h1080_16x9,
1947- ),
2010+ constraints:
2011+ constraints ??
2012+ ScreenShareConstraints (
2013+ params:
2014+ targetResolution? .toVideoParams (
2015+ defaultBitrate: RtcVideoParametersPresets .k1080pBitrate,
2016+ ) ??
2017+ RtcVideoParametersPresets .h1080_16x9,
2018+ ),
19482019 );
19492020 }
19502021 }
@@ -1962,13 +2033,19 @@ class Call {
19622033 final mediaConstraints = track.mediaConstraints;
19632034 if (mediaConstraints is AudioConstraints ) {
19642035 _logger.v (() => '[setLocalTrack]: setMicrophoneEnabled true' );
1965- await setMicrophoneEnabled (enabled: true );
2036+ await setMicrophoneEnabled (
2037+ enabled: true ,
2038+ constraints: mediaConstraints,
2039+ );
19662040 } else if (mediaConstraints is CameraConstraints ) {
19672041 _logger.v (() => '[setLocalTrack]: setCameraEnabled true' );
1968- await setCameraEnabled (enabled: true );
2042+ await setCameraEnabled (enabled: true , constraints : mediaConstraints );
19692043 } else if (mediaConstraints is ScreenShareConstraints ) {
19702044 _logger.v (() => '[setLocalTrack] setScreenShareEnabled true' );
1971- await setScreenShareEnabled (enabled: true );
2045+ await setScreenShareEnabled (
2046+ enabled: true ,
2047+ constraints: mediaConstraints,
2048+ );
19722049 } else {
19732050 streamLog.e (
19742051 _tag,
@@ -2749,7 +2826,9 @@ class Call {
27492826 );
27502827
27512828 _connectOptions = _connectOptions.copyWith (
2752- camera: enabled ? TrackOption .enabled () : TrackOption .disabled (),
2829+ camera: enabled
2830+ ? TrackOption .enabled (constraints: constraints)
2831+ : TrackOption .disabled (),
27532832 cameraFacingMode: constraints? .facingMode ?? FacingMode .user,
27542833 );
27552834 }
@@ -2811,7 +2890,9 @@ class Call {
28112890 );
28122891
28132892 _connectOptions = _connectOptions.copyWith (
2814- microphone: enabled ? TrackOption .enabled () : TrackOption .disabled (),
2893+ microphone: enabled
2894+ ? TrackOption .enabled (constraints: constraints)
2895+ : TrackOption .disabled (),
28152896 );
28162897 }
28172898
@@ -2848,16 +2929,27 @@ class Call {
28482929 ) ??
28492930 Result .error ('Call session is null, cannot start screen share' );
28502931
2932+ // In case of iOS Broadcast Extension, we don't update the state here
2933+ // We listen to the ScreenShareStarted event instead
2934+ if (CurrentPlatform .isIos &&
2935+ constraints is ScreenShareConstraints &&
2936+ constraints.useiOSBroadcastExtension) {
2937+ return result.map ((_) => none);
2938+ }
2939+
28512940 if (result.isSuccess) {
28522941 _stateManager.participantSetScreenShareEnabled (
28532942 enabled: enabled,
28542943 );
28552944
28562945 _connectOptions = _connectOptions.copyWith (
2857- screenShare: enabled ? TrackOption .enabled () : TrackOption .disabled (),
2946+ screenShare: enabled
2947+ ? TrackOption .enabled (constraints: updatedConstraints)
2948+ : TrackOption .disabled (),
28582949 );
28592950
28602951 if (enabled) {
2952+ // [web only] Automatically stop screen share when the track ends
28612953 result.getDataOrNull ()? .mediaTrack.onEnded = () {
28622954 setScreenShareEnabled (enabled: false );
28632955 };
0 commit comments