@@ -289,88 +289,6 @@ AVAudioSession.sharedInstance().requestRecordPermission { (granted) in
289
289
}
290
290
```
291
291
292
- ## Display local video
293
-
294
- Before starting a call, you can manage settings related to video. In this quickstart, we introduce the implementation of toggling local video before or during a call.
295
-
296
- First we need to access local cameras with ` deviceManager ` . Once the desired camera is selected, we can construct ` LocalVideoStream ` and create a ` VideoStreamRenderer ` , and then attach it to ` previewView ` . During the call, we can use ` startVideo ` or ` stopVideo ` to start or stop sending ` LocalVideoStream ` to remote participants. This function also works with handling incoming calls.
297
-
298
- ``` Swift
299
- func toggleLocalVideo () {
300
- // toggling video before call starts
301
- if (call == nil )
302
- {
303
- if (! sendingVideo)
304
- {
305
- self .callClient = CallClient ()
306
- self .callClient .getDeviceManager { (deviceManager, error) in
307
- if (error == nil ) {
308
- print (" Got device manager instance" )
309
- self .deviceManager = deviceManager
310
- } else {
311
- print (" Failed to get device manager instance" )
312
- }
313
- }
314
- guard let deviceManager = deviceManager else {
315
- return
316
- }
317
- let camera = deviceManager.cameras .first
318
- let scalingMode = ScalingMode.fit
319
- if (self .localVideoStream == nil ) {
320
- self .localVideoStream = [LocalVideoStream]()
321
- }
322
- localVideoStream! .append (LocalVideoStream (camera : camera! ))
323
- previewRenderer = try ! VideoStreamRenderer (localVideoStream : localVideoStream! .first ! )
324
- previewView = try ! previewRenderer! .createView (withOptions : CreateViewOptions (scalingMode :scalingMode))
325
- self .sendingVideo = true
326
- }
327
- else {
328
- self .sendingVideo = false
329
- self .previewView = nil
330
- self .previewRenderer ! .dispose ()
331
- self .previewRenderer = nil
332
- }
333
- }
334
- // toggle local video during the call
335
- else {
336
- if (sendingVideo) {
337
- teamsCall! .stopVideo (stream : localVideoStream! .first ! ) { (error) in
338
- if (error != nil ) {
339
- print (" cannot stop video" )
340
- }
341
- else {
342
- self .sendingVideo = false
343
- self .previewView = nil
344
- self .previewRenderer ! .dispose ()
345
- self .previewRenderer = nil
346
- }
347
- }
348
- }
349
- else {
350
- guard let deviceManager = deviceManager else {
351
- return
352
- }
353
- let camera = deviceManager.cameras .first
354
- let scalingMode = ScalingMode.fit
355
- if (self .localVideoStream == nil ) {
356
- self .localVideoStream = [LocalVideoStream]()
357
- }
358
- localVideoStream! .append (LocalVideoStream (camera : camera! ))
359
- previewRenderer = try ! VideoStreamRenderer (localVideoStream : localVideoStream! .first ! )
360
- previewView = try ! previewRenderer! .createView (withOptions : CreateViewOptions (scalingMode :scalingMode))
361
- teamsCall! .startVideo (stream :(localVideoStream? .first )! ) { (error) in
362
- if (error != nil ) {
363
- print (" cannot start video" )
364
- }
365
- else {
366
- self .sendingVideo = true
367
- }
368
- }
369
- }
370
- }
371
- }
372
- ```
373
-
374
292
## Place an outgoing call
375
293
376
294
The ` startCall ` method is set as the action that is performed when the Start Call button is tapped. In this quickstart, outgoing calls are audio only by default. To start a call with video, we need to set ` VideoOptions ` with ` LocalVideoStream ` and pass it with ` startCallOptions ` to set initial options for the call.
@@ -428,6 +346,7 @@ func setTeamsCallAndObserver(call:TeamsCall, error:Error?) {
428
346
self .teamsCall = call
429
347
self .teamsCallObserver = TeamsCallObserver (self )
430
348
self .teamsCall ! .delegate = self .teamsCallObserver
349
+ // Attach a RemoteParticipant observer
431
350
self .remoteParticipantObserver = RemoteParticipantObserver (self )
432
351
} else {
433
352
print (" Failed to get teams call object" )
@@ -472,11 +391,6 @@ final class TeamsIncomingCallHandler: NSObject, TeamsCallAgentDelegate, TeamsInc
472
391
473
392
We need to create an instance of ` TeamsIncomingCallHandler ` by adding the following code to the ` onAppear ` callback in ` ContentView.swift ` :
474
393
475
- ``` Swift
476
- let teamsIncomingCallHandler = TeamsIncomingCallHandler.getOrCreateInstance ()
477
- teamsIncomingCallHandler.contentView = self
478
- ```
479
-
480
394
Set a delegate to the ` TeamsCallAgent ` after the ` TeamsCallAgent ` being successfully created:
481
395
482
396
``` Swift
@@ -487,7 +401,6 @@ Once there's an incoming call, the `TeamsIncomingCallHandler` calls the function
487
401
488
402
``` Swift
489
403
func showIncomingCallBanner (_ incomingCall : TeamsIncomingCall) {
490
- isIncomingCall = true
491
404
self .teamsIncomingCall = incomingCall
492
405
}
493
406
```
@@ -496,65 +409,37 @@ The actions attached to `answer` and `decline` are implemented as the following
496
409
497
410
``` Swift
498
411
func answerIncomingCall () {
499
- isIncomingCall = false
500
412
let options = AcceptTeamsCallOptions ()
501
- if (self .incomingCall != nil ) {
502
- guard let deviceManager = deviceManager else {
503
- return
504
- }
505
- if (self .localVideoStream == nil ) {
506
- self .localVideoStream = [LocalVideoStream]()
507
- }
508
- if (sendingVideo)
509
- {
510
- let camera = deviceManager.cameras .first
511
- localVideoStream! .append (LocalVideoStream (camera : camera! ))
512
- let videoOptions = VideoOptions (localVideoStreams : localVideoStream! )
513
- options.videoOptions = videoOptions
514
- }
515
- self .incomingCall ! .accept (options : options) { (call, error) in
516
- setCallAndObersever (call : call, error : error)
517
- }
413
+ guard let teamsIncomingCall = self .teamsIncomingCall else {
414
+ print (" No active incoming call" )
415
+ return
518
416
}
519
- }
520
-
521
- func declineIncomingCall (){
522
- self .incomingCall ! .reject { (error) in }
523
- isIncomingCall = false
524
- }
525
- ```
526
-
527
- ## Remote participant video streams
528
417
529
- We can create a ` RemoteVideoStreamData ` class to handle rendering video streams of remote participant.
530
-
531
- ``` Swift
532
- public class RemoteVideoStreamData : NSObject , RendererDelegate {
533
- public func videoStreamRenderer (didFailToStart renderer : VideoStreamRenderer) {
534
- owner.errorMessage = " Renderer failed to start"
418
+ guard let deviceManager = deviceManager else {
419
+ print (" No device manager instance" )
420
+ return
535
421
}
536
-
537
- private var owner:ContentView
538
- let stream:RemoteVideoStream
539
- var renderer:VideoStreamRenderer? {
540
- didSet {
541
- if renderer != nil {
542
- renderer! .delegate = self
543
- }
544
- }
422
+
423
+ if localVideoStream == nil {
424
+ localVideoStream = [LocalVideoStream]()
545
425
}
546
-
547
- var views:[RendererView] = []
548
- init (view :ContentView, stream :RemoteVideoStream) {
549
- owner = view
550
- self .stream = stream
426
+
427
+ if sendingVideo
428
+ {
429
+ let camera = deviceManager.cameras .first
430
+ localVideoStream! .append (LocalVideoStream (camera : camera! ))
431
+ let videoOptions = VideoOptions (localVideoStreams : localVideoStream! )
432
+ options.videoOptions = videoOptions
551
433
}
552
-
553
- public func videoStreamRenderer (didRenderFirstFrame renderer : VideoStreamRenderer) {
554
- let size:StreamSize = renderer.size
555
- owner.remoteVideoSize = String (size.width ) + " X " + String (size.height )
434
+
435
+ teamsIncomingCall.accept (options : options) { (call, error) in
436
+ setTeamsCallAndObserver (call : call, error : error)
556
437
}
557
438
}
439
+
440
+ func declineIncomingCall (){
441
+ self .teamsIncomingCall ? .reject { (error) in }
442
+ }
558
443
```
559
444
560
445
## Subscribe to events
@@ -577,83 +462,20 @@ public class TeamsCallObserver: NSObject, TeamsCallDelegate, TeamsIncomingCallDe
577
462
// render remote video streams when remote participant changes
578
463
public func teamsCall (_ teamsCall : TeamsCall, didUpdateRemoteParticipant args : ParticipantsUpdatedEventArgs) {
579
464
for participant in args.addedParticipants {
580
- participant.delegate = owner.remoteParticipantObserver
581
- for stream in participant.videoStreams {
582
- if ! owner.remoteVideoStreamData.isEmpty {
583
- return
584
- }
585
- let data:RemoteVideoStreamData = RemoteVideoStreamData (view : owner, stream : stream)
586
- let scalingMode = ScalingMode.fit
587
- data.renderer = try ! VideoStreamRenderer (remoteVideoStream : stream)
588
- let view:RendererView = try ! data.renderer ! .createView (withOptions : CreateViewOptions (scalingMode :scalingMode))
589
- data.views .append (view)
590
- self .owner .remoteViews .append (view)
591
- owner.remoteVideoStreamData [stream.id ] = data
592
- }
593
- owner.remoteParticipant = participant
465
+ participant.delegate = self .remoteParticipantObserver
594
466
}
595
467
}
596
468
597
469
// Handle remote video streams when the call is connected
598
470
public func initialCallParticipant () {
599
471
for participant in owner.teamsCall.remoteParticipants {
600
- participant.delegate = owner .remoteParticipantObserver
472
+ participant.delegate = self .remoteParticipantObserver
601
473
for stream in participant.videoStreams {
602
474
renderRemoteStream (stream)
603
475
}
604
476
owner.remoteParticipant = participant
605
477
}
606
478
}
607
-
608
- // create render for RemoteVideoStream and attach it to view
609
- public func renderRemoteStream (_ stream : RemoteVideoStream! ) {
610
- if ! owner.remoteVideoStreamData.isEmpty {
611
- return
612
- }
613
- let data:RemoteVideoStreamData = RemoteVideoStreamData (view : owner, stream : stream)
614
- let scalingMode = ScalingMode.fit
615
- data.renderer = try ! VideoStreamRenderer (remoteVideoStream : stream)
616
- let view:RendererView = try ! data.renderer ! .createView (withOptions : CreateViewOptions (scalingMode :scalingMode))
617
- self .owner .remoteViews .append (view)
618
- owner.remoteVideoStreamData [stream.id ] = data
619
- }
620
- }
621
- ```
622
-
623
- ## Remote participant Management
624
-
625
- All remote participants are represented with the ` RemoteParticipant ` type and are available through the ` remoteParticipants ` collection on a call instance.
626
-
627
- We can implement a ` RemoteParticipantObserver ` class to subscribe to the updates on remote video streams of remote participants.
628
-
629
- ``` Swift
630
- public class RemoteParticipantObserver : NSObject , RemoteParticipantDelegate {
631
- private var owner:ContentView
632
- init (_ view :ContentView) {
633
- owner = view
634
- }
635
-
636
- public func renderRemoteStream (_ stream : RemoteVideoStream! ) {
637
- let data:RemoteVideoStreamData = RemoteVideoStreamData (view : owner, stream : stream)
638
- let scalingMode = ScalingMode.fit
639
- data.renderer = try ! VideoStreamRenderer (remoteVideoStream : stream)
640
- let view:RendererView = try ! data.renderer ! .createView (withOptions : CreateViewOptions (scalingMode :scalingMode))
641
- self .owner .remoteViews .append (view)
642
- owner.remoteVideoStreamData [stream.id ] = data
643
- }
644
-
645
- // render RemoteVideoStream when remote participant turns on the video, dispose the renderer when remote video is off
646
- public func remoteParticipant (_ remoteParticipant : RemoteParticipant, didUpdateVideoStreams args : RemoteVideoStreamsEventArgs) {
647
- for stream in args.addedRemoteVideoStreams {
648
- renderRemoteStream (stream)
649
- }
650
- for stream in args.removedRemoteVideoStreams {
651
- for data in owner.remoteVideoStreamData.values {
652
- data.renderer ? .dispose ()
653
- }
654
- owner.remoteViews .removeAll ()
655
- }
656
- }
657
479
}
658
480
```
659
481
0 commit comments