Skip to content

Commit 73f5208

Browse files
Hamawishamzaawais96slycoder
authored
feat: Allow user to share screen on iOS through In app capture method (#631)
* feat: screen sharing on ios * added delegate for TVIAppScreenSource * lint issue fixed Co-authored-by: Hamza Awais <[email protected]> Co-authored-by: Jonathan Chang <[email protected]>
1 parent 47753cc commit 73f5208

File tree

3 files changed

+105
-40
lines changed

3 files changed

+105
-40
lines changed

Example/index.js

Lines changed: 34 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ const styles = StyleSheet.create(styleSheet);
2424
const Example = (props) => {
2525
const [isAudioEnabled, setIsAudioEnabled] = useState(true);
2626
const [isVideoEnabled, setIsVideoEnabled] = useState(true);
27+
const [isScreenShareEnabled, setIsScreenShareEnabled] = useState(false);
2728
const [status, setStatus] = useState("disconnected");
2829
const [participants, setParticipants] = useState(new Map());
2930
const [videoTracks, setVideoTracks] = useState(new Map());
@@ -35,7 +36,11 @@ const Example = (props) => {
3536
await _requestAudioPermission();
3637
await _requestCameraPermission();
3738
}
38-
twilioVideo.current.connect({ accessToken: token, enableNetworkQualityReporting: true, dominantSpeakerEnabled: true});
39+
twilioVideo.current.connect({
40+
accessToken: token,
41+
enableNetworkQualityReporting: true,
42+
dominantSpeakerEnabled: true,
43+
});
3944
setStatus("connecting");
4045
};
4146

@@ -49,6 +54,11 @@ const Example = (props) => {
4954
.then((isEnabled) => setIsAudioEnabled(isEnabled));
5055
};
5156

57+
const _onShareButtonPressed = () => {
58+
twilioVideo.current.toggleScreenSharing(!isSharing);
59+
setIsSharing(!isSharing);
60+
};
61+
5262
const _onFlipButtonPress = () => {
5363
twilioVideo.current.flipCamera();
5464
};
@@ -93,11 +103,24 @@ const Example = (props) => {
93103
};
94104

95105
const _onNetworkLevelChanged = ({ participant, isLocalUser, quality }) => {
96-
console.log("Participant", participant, "isLocalUser", isLocalUser, "quality", quality);
106+
console.log(
107+
"Participant",
108+
participant,
109+
"isLocalUser",
110+
isLocalUser,
111+
"quality",
112+
quality
113+
);
97114
};
98115

99116
const _onDominantSpeakerDidChange = ({ roomName, roomSid, participant }) => {
100-
console.log("onDominantSpeakerDidChange", `roomName: ${roomName}`, `roomSid: ${roomSid}`, "participant:", participant);
117+
console.log(
118+
"onDominantSpeakerDidChange",
119+
`roomName: ${roomName}`,
120+
`roomSid: ${roomSid}`,
121+
"participant:",
122+
participant
123+
);
101124
};
102125

103126
const _requestAudioPermission = () => {
@@ -177,6 +200,14 @@ const Example = (props) => {
177200
>
178201
<Text style={{ fontSize: 12 }}>Flip</Text>
179202
</TouchableOpacity>
203+
<TouchableOpacity
204+
style={styles.optionButton}
205+
onPress={_onShareButtonPressed}
206+
>
207+
<Text style={{ fontSize: 12 }}>
208+
{isSharing ? "Stop Sharing" : "Start Sharing"}
209+
</Text>
210+
</TouchableOpacity>
180211
<TwilioVideoLocalView enabled={true} style={styles.localVideo} />
181212
</View>
182213
</View>

ios/RCTTWVideoModule.m

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,12 +61,13 @@
6161
}
6262

6363

64-
@interface RCTTWVideoModule () <TVIRemoteDataTrackDelegate, TVIRemoteParticipantDelegate, TVIRoomDelegate, TVICameraSourceDelegate, TVILocalParticipantDelegate>
64+
@interface RCTTWVideoModule () <TVIRemoteDataTrackDelegate, TVIRemoteParticipantDelegate, TVIRoomDelegate, TVICameraSourceDelegate, TVILocalParticipantDelegate, TVIAppScreenSourceDelegate>
6565

6666
@property (strong, nonatomic) TVICameraSource *camera;
6767
@property (strong, nonatomic) TVILocalVideoTrack* localVideoTrack;
6868
@property (strong, nonatomic) TVILocalAudioTrack* localAudioTrack;
6969
@property (strong, nonatomic) TVILocalDataTrack* localDataTrack;
70+
@property (strong, nonatomic) TVIAppScreenSource *screen;
7071
@property (strong, nonatomic) TVILocalParticipant* localParticipant;
7172
@property (strong, nonatomic) TVIRoom *room;
7273
@property (nonatomic) BOOL listening;
@@ -287,6 +288,29 @@ - (bool)_setLocalVideoEnabled:(bool)enabled cameraType:(NSString *)cameraType {
287288
}
288289
}
289290

291+
RCT_EXPORT_METHOD(toggleScreenSharing: (BOOL) value) {
292+
if (value) {
293+
TVIAppScreenSourceOptions *options = [TVIAppScreenSourceOptions optionsWithBlock:^(TVIAppScreenSourceOptionsBuilder * _Nonnull builder) {
294+
295+
}];
296+
self.screen = [[TVIAppScreenSource alloc] initWithOptions:options delegate:self];
297+
if (self.screen == nil) {
298+
return;
299+
}
300+
self.localVideoTrack = [TVILocalVideoTrack trackWithSource:self.screen enabled:YES name:@"screen"];
301+
if(self.localVideoTrack != nil){
302+
TVILocalParticipant *localParticipant = self.room.localParticipant;
303+
[localParticipant publishVideoTrack:self.localVideoTrack];
304+
}
305+
[self.screen startCapture];
306+
} else {
307+
[self unpublishLocalVideo];
308+
[self.screen stopCapture];
309+
self.localVideoTrack = nil;
310+
}
311+
}
312+
313+
290314
RCT_EXPORT_METHOD(toggleSoundSetup:(BOOL)speaker) {
291315
NSError *error = nil;
292316
kTVIDefaultAVAudioSessionConfigurationBlock();

src/TwilioVideo.ios.js

Lines changed: 46 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,6 @@ const { TWVideoModule } = NativeModules
1414

1515
export default class TwilioVideo extends Component {
1616
static propTypes = {
17-
/**
18-
* Flag that enables screen sharing RCTRootView instead of camera capture
19-
*/
20-
screenShare: PropTypes.bool,
2117
/**
2218
* Called when the room has connected
2319
*
@@ -157,7 +153,7 @@ export default class TwilioVideo extends Component {
157153
*/
158154
autoInitializeCamera: PropTypes.bool,
159155
...View.propTypes
160-
}
156+
};
161157

162158
constructor (props) {
163159
super(props)
@@ -216,6 +212,13 @@ export default class TwilioVideo extends Component {
216212
TWVideoModule.flipCamera()
217213
}
218214

215+
/**
216+
* Toggle screen sharing
217+
*/
218+
toggleScreenSharing (status) {
219+
TWVideoModule.toggleScreenSharing(status)
220+
}
221+
219222
/**
220223
* Toggle audio setup from speaker (default) and headset
221224
*/
@@ -247,7 +250,8 @@ export default class TwilioVideo extends Component {
247250
enableNetworkQualityReporting = false,
248251
dominantSpeakerEnabled = false
249252
}) {
250-
TWVideoModule.connect(accessToken,
253+
TWVideoModule.connect(
254+
accessToken,
251255
roomName,
252256
enableAudio,
253257
enableVideo,
@@ -319,124 +323,130 @@ export default class TwilioVideo extends Component {
319323

320324
_unregisterEvents () {
321325
TWVideoModule.changeListenerStatus(false)
322-
this._subscriptions.forEach(e => e.remove())
326+
this._subscriptions.forEach((e) => e.remove())
323327
this._subscriptions = []
324328
}
325329

326330
_registerEvents () {
327331
TWVideoModule.changeListenerStatus(true)
328332
this._subscriptions = [
329-
this._eventEmitter.addListener('roomDidConnect', data => {
333+
this._eventEmitter.addListener('roomDidConnect', (data) => {
330334
if (this.props.onRoomDidConnect) {
331335
this.props.onRoomDidConnect(data)
332336
}
333337
}),
334-
this._eventEmitter.addListener('roomDidDisconnect', data => {
338+
this._eventEmitter.addListener('roomDidDisconnect', (data) => {
335339
if (this.props.onRoomDidDisconnect) {
336340
this.props.onRoomDidDisconnect(data)
337341
}
338342
}),
339-
this._eventEmitter.addListener('roomDidFailToConnect', data => {
343+
this._eventEmitter.addListener('roomDidFailToConnect', (data) => {
340344
if (this.props.onRoomDidFailToConnect) {
341345
this.props.onRoomDidFailToConnect(data)
342346
}
343347
}),
344-
this._eventEmitter.addListener('roomParticipantDidConnect', data => {
348+
this._eventEmitter.addListener('roomParticipantDidConnect', (data) => {
345349
if (this.props.onRoomParticipantDidConnect) {
346350
this.props.onRoomParticipantDidConnect(data)
347351
}
348352
}),
349-
this._eventEmitter.addListener('roomParticipantDidDisconnect', data => {
353+
this._eventEmitter.addListener('roomParticipantDidDisconnect', (data) => {
350354
if (this.props.onRoomParticipantDidDisconnect) {
351355
this.props.onRoomParticipantDidDisconnect(data)
352356
}
353357
}),
354-
this._eventEmitter.addListener('participantAddedVideoTrack', data => {
358+
this._eventEmitter.addListener('participantAddedVideoTrack', (data) => {
355359
if (this.props.onParticipantAddedVideoTrack) {
356360
this.props.onParticipantAddedVideoTrack(data)
357361
}
358362
}),
359-
this._eventEmitter.addListener('participantAddedDataTrack', data => {
363+
this._eventEmitter.addListener('participantAddedDataTrack', (data) => {
360364
if (this.props.onParticipantAddedDataTrack) {
361365
this.props.onParticipantAddedDataTrack(data)
362366
}
363367
}),
364-
this._eventEmitter.addListener('participantRemovedDataTrack', data => {
368+
this._eventEmitter.addListener('participantRemovedDataTrack', (data) => {
365369
if (this.props.onParticipantRemovedDataTrack) {
366370
this.props.onParticipantRemovedDataTrack(data)
367371
}
368372
}),
369-
this._eventEmitter.addListener('participantRemovedVideoTrack', data => {
373+
this._eventEmitter.addListener('participantRemovedVideoTrack', (data) => {
370374
if (this.props.onParticipantRemovedVideoTrack) {
371375
this.props.onParticipantRemovedVideoTrack(data)
372376
}
373377
}),
374-
this._eventEmitter.addListener('participantAddedAudioTrack', data => {
378+
this._eventEmitter.addListener('participantAddedAudioTrack', (data) => {
375379
if (this.props.onParticipantAddedAudioTrack) {
376380
this.props.onParticipantAddedAudioTrack(data)
377381
}
378382
}),
379-
this._eventEmitter.addListener('participantRemovedAudioTrack', data => {
383+
this._eventEmitter.addListener('participantRemovedAudioTrack', (data) => {
380384
if (this.props.onParticipantRemovedAudioTrack) {
381385
this.props.onParticipantRemovedAudioTrack(data)
382386
}
383387
}),
384-
this._eventEmitter.addListener('participantEnabledVideoTrack', data => {
388+
this._eventEmitter.addListener('participantEnabledVideoTrack', (data) => {
385389
if (this.props.onParticipantEnabledVideoTrack) {
386390
this.props.onParticipantEnabledVideoTrack(data)
387391
}
388392
}),
389-
this._eventEmitter.addListener('participantDisabledVideoTrack', data => {
390-
if (this.props.onParticipantDisabledVideoTrack) {
391-
this.props.onParticipantDisabledVideoTrack(data)
393+
this._eventEmitter.addListener(
394+
'participantDisabledVideoTrack',
395+
(data) => {
396+
if (this.props.onParticipantDisabledVideoTrack) {
397+
this.props.onParticipantDisabledVideoTrack(data)
398+
}
392399
}
393-
}),
394-
this._eventEmitter.addListener('participantEnabledAudioTrack', data => {
400+
),
401+
this._eventEmitter.addListener('participantEnabledAudioTrack', (data) => {
395402
if (this.props.onParticipantEnabledAudioTrack) {
396403
this.props.onParticipantEnabledAudioTrack(data)
397404
}
398405
}),
399-
this._eventEmitter.addListener('participantDisabledAudioTrack', data => {
400-
if (this.props.onParticipantDisabledAudioTrack) {
401-
this.props.onParticipantDisabledAudioTrack(data)
406+
this._eventEmitter.addListener(
407+
'participantDisabledAudioTrack',
408+
(data) => {
409+
if (this.props.onParticipantDisabledAudioTrack) {
410+
this.props.onParticipantDisabledAudioTrack(data)
411+
}
402412
}
403-
}),
404-
this._eventEmitter.addListener('dataTrackMessageReceived', data => {
413+
),
414+
this._eventEmitter.addListener('dataTrackMessageReceived', (data) => {
405415
if (this.props.onDataTrackMessageReceived) {
406416
this.props.onDataTrackMessageReceived(data)
407417
}
408418
}),
409-
this._eventEmitter.addListener('cameraDidStart', data => {
419+
this._eventEmitter.addListener('cameraDidStart', (data) => {
410420
if (this.props.onCameraDidStart) {
411421
this.props.onCameraDidStart(data)
412422
}
413423
}),
414-
this._eventEmitter.addListener('cameraWasInterrupted', data => {
424+
this._eventEmitter.addListener('cameraWasInterrupted', (data) => {
415425
if (this.props.onCameraWasInterrupted) {
416426
this.props.onCameraWasInterrupted(data)
417427
}
418428
}),
419-
this._eventEmitter.addListener('cameraInterruptionEnded', data => {
429+
this._eventEmitter.addListener('cameraInterruptionEnded', (data) => {
420430
if (this.props.onCameraInterruptionEnded) {
421431
this.props.onCameraInterruptionEnded(data)
422432
}
423433
}),
424-
this._eventEmitter.addListener('cameraDidStopRunning', data => {
434+
this._eventEmitter.addListener('cameraDidStopRunning', (data) => {
425435
if (this.props.onCameraDidStopRunning) {
426436
this.props.onCameraDidStopRunning(data)
427437
}
428438
}),
429-
this._eventEmitter.addListener('statsReceived', data => {
439+
this._eventEmitter.addListener('statsReceived', (data) => {
430440
if (this.props.onStatsReceived) {
431441
this.props.onStatsReceived(data)
432442
}
433443
}),
434-
this._eventEmitter.addListener('networkQualityLevelsChanged', data => {
444+
this._eventEmitter.addListener('networkQualityLevelsChanged', (data) => {
435445
if (this.props.onNetworkQualityLevelsChanged) {
436446
this.props.onNetworkQualityLevelsChanged(data)
437447
}
438448
}),
439-
this._eventEmitter.addListener('onDominantSpeakerDidChange', data => {
449+
this._eventEmitter.addListener('onDominantSpeakerDidChange', (data) => {
440450
if (this.props.onDominantSpeakerDidChange) {
441451
this.props.onDominantSpeakerDidChange(data)
442452
}

0 commit comments

Comments
 (0)