@@ -3,23 +3,25 @@ import LiveDigitalSDK
3
3
import AVFoundation
4
4
5
5
6
- final class ViewController : UIViewController {
6
+ final class SessionVC : UIViewController {
7
7
private struct Constants {
8
8
static let moodhoodAPIHost = URL ( string: " https://moodhood-api.livedigital.space " ) !
9
9
static let moodhoodClientId = " moodhood-demo "
10
10
static let moodhoodClientSecret = " demo12345abcde6789zxcvDemo "
11
11
static let loadBalancerHost = URL ( string: " https://lb.livedigital.space " ) !
12
- static let testSpaceId = " 6144806aac512ee6cd29be10 "
13
- static let testRoomId = " 66cbf30dd3522636cfeb77cb "
14
12
}
15
13
14
+ var spaceId : String ?
15
+ var roomId : String ?
16
+
16
17
@IBOutlet var localPreviewShadowView : UIView !
17
18
@IBOutlet var localPreviewContainer : UIView !
18
19
@IBOutlet var buttonsContainer : UIView !
19
20
@IBOutlet var localAudioButton : UIButton !
20
21
@IBOutlet var localVideoButton : UIButton !
21
22
@IBOutlet var peersViewsScroller : UIScrollView !
22
23
@IBOutlet var peersViewsContainer : UIStackView !
24
+ @IBOutlet var finishButton : UIButton !
23
25
private var peerViews = [ PeerId: PeerView] ( )
24
26
private var peers = [ PeerId: Peer] ( )
25
27
@@ -80,6 +82,10 @@ final class ViewController: UIViewController {
80
82
super. viewDidLoad ( )
81
83
print ( " LiveDigitalSDK version: \( LiveDigital . version ( ) ) " )
82
84
85
+ finishButton. layer. masksToBounds = true
86
+ finishButton. layer. cornerRadius = 20
87
+ finishButton. backgroundColor = . systemRed
88
+
83
89
buttonsContainer. layer. masksToBounds = true
84
90
buttonsContainer. layer. cornerRadius = 20
85
91
@@ -99,20 +105,22 @@ final class ViewController: UIViewController {
99
105
100
106
AVCaptureDevice . requestAccess ( for: . audio) { granted in
101
107
AVCaptureDevice . requestAccess ( for: . video) { granted in
102
- self . startConferenceSession ( )
108
+ DispatchQueue . main. async {
109
+ self . startConferenceSession ( )
110
+ }
103
111
}
104
112
}
105
113
}
106
114
}
107
115
108
116
// MARK: - LiveDigitalSessionManagerDelegate implementation
109
117
110
- extension ViewController : LiveDigitalSessionManagerDelegate {
118
+ extension SessionVC : LiveDigitalSessionManagerDelegate {
111
119
}
112
120
113
121
// MARK: - CameraManagerDelegate implementation
114
122
115
- extension ViewController : CameraManagerDelegate {
123
+ extension SessionVC : CameraManagerDelegate {
116
124
func cameraManagerFailed( cameraManager: CameraManager , error: MediaCapturerError ) {
117
125
print ( " \( cameraManager) failed with error \( error) " )
118
126
}
@@ -123,7 +131,7 @@ extension ViewController: CameraManagerDelegate {
123
131
124
132
// MARK: - AudioRouterDelegate implementation
125
133
126
- extension ViewController : AudioRouterDelegate {
134
+ extension SessionVC : AudioRouterDelegate {
127
135
func needRestartAudio( ) {
128
136
}
129
137
@@ -133,9 +141,9 @@ extension ViewController: AudioRouterDelegate {
133
141
134
142
// MARK: - ChannelSessionObserver implementation
135
143
136
- extension ViewController : ChannelSessionObserver {
144
+ extension SessionVC : ChannelSessionObserver {
137
145
func channelSessionJoinedChannel( _ channelSession: any LiveDigitalSDK . ChannelSession ) {
138
- guard let participantId, let userToken else {
146
+ guard let participantId, let userToken, let spaceId , let roomId else {
139
147
print ( " Failed to join room: missing participantId or userToken " )
140
148
return
141
149
}
@@ -144,8 +152,8 @@ extension ViewController: ChannelSessionObserver {
144
152
do {
145
153
try await apiClient. joinRoom (
146
154
userToken: userToken,
147
- space: Constants . testSpaceId ,
148
- room: Constants . testRoomId ,
155
+ space: spaceId ,
156
+ room: roomId ,
149
157
participant: participantId
150
158
)
151
159
print ( " Joined room " )
@@ -240,7 +248,7 @@ extension ViewController: ChannelSessionObserver {
240
248
241
249
// MARK: - ChannelSessionDelegate implementation
242
250
243
- extension ViewController : ChannelSessionDelegate {
251
+ extension SessionVC : ChannelSessionDelegate {
244
252
func sessionNeedsRestart( _ channelSession: ChannelSession ) {
245
253
self . channelSession? . start (
246
254
mediaRole: . host,
@@ -271,23 +279,27 @@ extension ViewController: ChannelSessionDelegate {
271
279
}
272
280
273
281
// MARK: - Private methods
274
- private extension ViewController {
282
+ private extension SessionVC {
275
283
func startConferenceSession( ) {
284
+ updateLocalVideoEnabled ( false )
285
+ updateLocalAudioEnabled ( false )
286
+
287
+ guard let spaceId, let roomId else {
288
+ print ( " Skip session start: spaceId or roomId is not specified " )
289
+ return
290
+ }
291
+
276
292
Task {
277
293
let userToken = try await apiClient. authorizeAsGuest ( )
278
294
print ( " Created user token: \( userToken) " )
279
295
280
- let room = try await apiClient. fetchRoom (
281
- userToken: userToken,
282
- space: Constants . testSpaceId,
283
- room: Constants . testRoomId
284
- )
296
+ let room = try await apiClient. fetchRoom ( userToken: userToken, space: spaceId, room: roomId)
285
297
print ( " Fetched room details: \( room) " )
286
298
287
299
let participant = try await apiClient. createParticipant (
288
300
userToken: userToken,
289
- space: Constants . testSpaceId ,
290
- room: Constants . testRoomId ,
301
+ space: spaceId ,
302
+ room: roomId ,
291
303
clientUniqueId: clientUniqueId,
292
304
role: " host " ,
293
305
name: UIDevice . current. name
@@ -296,7 +308,7 @@ private extension ViewController {
296
308
297
309
let signalingToken = try await apiClient. createSignalingToken (
298
310
userToken: userToken,
299
- space: Constants . testSpaceId ,
311
+ space: spaceId ,
300
312
participant: participant. id
301
313
)
302
314
print ( " Created signaling token: \( signalingToken) " )
@@ -349,19 +361,14 @@ private extension ViewController {
349
361
print ( " Failed to start session with error: \( error) " )
350
362
}
351
363
352
- self . updateLocalVideoEnabled ( false )
364
+ self . updateLocalVideoEnabled ( self . videoSource != nil )
353
365
self . updateLocalAudioEnabled ( self . audioSource != nil )
354
366
} )
355
-
356
- startVideoSource ( )
357
- startAudioSource ( )
358
367
}
359
368
360
- func startVideoSource( ) {
369
+ func startVideoSource( _ completion : ( ( VideoSource ? ) -> Void ) ) {
361
370
switch engine. startVideoSource ( position: . front) {
362
371
case let . success( videoSource) :
363
- self . videoSource = videoSource
364
-
365
372
videoSource. localVideoView. translatesAutoresizingMaskIntoConstraints = false
366
373
localPreviewContainer. addSubview ( videoSource. localVideoView)
367
374
localPreviewContainer. leftAnchor
@@ -376,8 +383,11 @@ private extension ViewController {
376
383
localPreviewContainer. bottomAnchor
377
384
. constraint ( equalTo: videoSource. localVideoView. bottomAnchor)
378
385
. isActive = true
386
+ completion ( videoSource)
387
+
379
388
case let . failure( error) :
380
389
print ( " Failed to start video source: \( error) " )
390
+ completion ( nil )
381
391
}
382
392
}
383
393
@@ -401,6 +411,14 @@ private extension ViewController {
401
411
}
402
412
403
413
func updateLocalVideoEnabled( _ enabled: Bool ) {
414
+ if enabled, videoSource == nil {
415
+ startVideoSource { [ weak self] source in
416
+ self ? . videoSource = source
417
+ self ? . updateLocalVideoEnabled ( enabled)
418
+ }
419
+ return
420
+ }
421
+
404
422
guard let session = channelSession, let source = videoSource else {
405
423
print ( " Failed to update local video state: channel or video source is undefined. " )
406
424
updateVideoButtonState ( isOn: false )
@@ -411,11 +429,19 @@ private extension ViewController {
411
429
session. addVideoSource ( source)
412
430
} else {
413
431
session. removeVideoSource ( source)
432
+ engine. stopVideoSource ( source)
433
+ if let camSource = source as? VideoSourceWithPreview {
434
+ camSource. localVideoView. removeFromSuperview ( )
435
+ }
436
+ videoSource = nil
414
437
}
415
438
updateVideoButtonState ( isOn: enabled)
416
439
}
417
440
418
441
func updateLocalAudioEnabled( _ enabled: Bool ) {
442
+ if enabled, audioSource == nil {
443
+ startAudioSource ( )
444
+ }
419
445
guard let session = channelSession, let source = audioSource else {
420
446
print ( " Failed to update local audio state: channel or audio source is undefined. " )
421
447
updateAudioButtonState ( isOn: false )
@@ -426,6 +452,8 @@ private extension ViewController {
426
452
session. addAudioSource ( source)
427
453
} else {
428
454
session. removeAudioSource ( source)
455
+ engine. stopAudioSource ( source)
456
+ audioSource = nil
429
457
}
430
458
updateAudioButtonState ( isOn: enabled)
431
459
}
@@ -506,7 +534,7 @@ private extension ViewController {
506
534
507
535
// MARK: - UI Actions
508
536
509
- private extension ViewController {
537
+ private extension SessionVC {
510
538
@IBAction
511
539
func toggleMicrophoneEnabled( _ sender: UIButton ) {
512
540
updateLocalAudioEnabled ( !sender. isSelected)
@@ -523,11 +551,30 @@ private extension ViewController {
523
551
print ( " Failed to flip camera: \( error) " )
524
552
}
525
553
}
554
+
555
+ @IBAction
556
+ func finishSession( _ sender: UIButton ) {
557
+ if let videoSource {
558
+ engine. stopVideoSource ( videoSource)
559
+ }
560
+ if let audioSource {
561
+ engine. stopAudioSource ( audioSource)
562
+ }
563
+
564
+ guard let channelSession else {
565
+ dismiss ( animated: true )
566
+ return
567
+ }
568
+ finishButton. isEnabled = false
569
+ channelSession. stop ( completion: { [ weak self] in
570
+ self ? . dismiss ( animated: true )
571
+ } )
572
+ }
526
573
}
527
574
528
575
// MARK: - UIScrollViewDelegate implementation
529
576
530
- extension ViewController : UIScrollViewDelegate {
577
+ extension SessionVC : UIScrollViewDelegate {
531
578
func scrollViewDidScroll( _ scrollView: UIScrollView ) {
532
579
updateVisiblePeersVideos ( )
533
580
}
@@ -539,7 +586,7 @@ extension ViewController: UIScrollViewDelegate {
539
586
540
587
// MARK: - UIContextMenuInteractionDelegate implementation
541
588
542
- extension ViewController : UIContextMenuInteractionDelegate {
589
+ extension SessionVC : UIContextMenuInteractionDelegate {
543
590
func contextMenuInteraction( _ interaction: UIContextMenuInteraction ,
544
591
configurationForMenuAtLocation location: CGPoint ) -> UIContextMenuConfiguration ? {
545
592
0 commit comments