|
| 1 | +// |
| 2 | +// JoinChannelVC.swift |
| 3 | +// APIExample |
| 4 | +// |
| 5 | +// Created by 张乾泽 on 2020/4/17. |
| 6 | +// Copyright © 2020 Agora Corp. All rights reserved. |
| 7 | +// |
| 8 | +import UIKit |
| 9 | +import AGEVideoLayout |
| 10 | +import AgoraRtcKit |
| 11 | + |
| 12 | +struct ChannelInfo { |
| 13 | + let channelName: String |
| 14 | +} |
| 15 | + |
| 16 | +extension ChannelInfo { |
| 17 | + static func AllChannelList(_ channelName:String) -> [ChannelInfo] { |
| 18 | + var channels = [ChannelInfo]() |
| 19 | + for index in 1..<5 { |
| 20 | + let channel = ChannelInfo( |
| 21 | + channelName: "\(channelName)\(index)" |
| 22 | + ) |
| 23 | + channels.append(channel) |
| 24 | + } |
| 25 | + return channels |
| 26 | + } |
| 27 | +} |
| 28 | + |
| 29 | +class QuickSwitchChannel: BaseViewController { |
| 30 | + var pageViewController: UIPageViewController! |
| 31 | + var channels = [ChannelInfo]() |
| 32 | + var currentIndex = 0 |
| 33 | + |
| 34 | + @IBOutlet var container: AGEVideoContainer! |
| 35 | + var hostView: UIView? |
| 36 | + var hostChannel: ChannelInfo? |
| 37 | + var agoraKit: AgoraRtcEngineKit! |
| 38 | + |
| 39 | + // indicate if current instance has joined channel |
| 40 | + var isJoined: Bool = false |
| 41 | + |
| 42 | + func setupPageController(channelName: String) { |
| 43 | + channels = ChannelInfo.AllChannelList(channelName) |
| 44 | + pageViewController = UIPageViewController(transitionStyle: .scroll, navigationOrientation: .horizontal, options: nil) |
| 45 | + pageViewController.delegate = self |
| 46 | + |
| 47 | + if let vc = channelViewController(at: currentIndex) { |
| 48 | + pageViewController.setViewControllers([vc], direction: .forward, animated: false, completion: nil) |
| 49 | + setHostViewController(vc) |
| 50 | + } |
| 51 | + pageViewController.dataSource = self |
| 52 | + |
| 53 | + addChild(pageViewController) |
| 54 | + view.addSubview(pageViewController.view) |
| 55 | + |
| 56 | + pageViewController!.view.frame = view.bounds |
| 57 | + pageViewController.didMove(toParent: self) |
| 58 | + |
| 59 | + // Add the page view controller's gesture recognizers to the view controller's view so that the gestures are started more easily. |
| 60 | + view.gestureRecognizers = pageViewController.gestureRecognizers |
| 61 | + } |
| 62 | + |
| 63 | + func setHostViewController(_ viewController: QuickSwitchChannelVCItem) { |
| 64 | + hostChannel = viewController.channel |
| 65 | + hostView = viewController.hostRenderView |
| 66 | + viewController.showLoading(true) |
| 67 | + } |
| 68 | + |
| 69 | + override func viewDidLoad() { |
| 70 | + super.viewDidLoad() |
| 71 | + |
| 72 | + // set up agora instance when view loaded |
| 73 | + agoraKit = AgoraRtcEngineKit.sharedEngine(withAppId: KeyCenter.AppId, delegate: self) |
| 74 | + |
| 75 | + // get channel name from configs |
| 76 | + guard let channelName = configs["channelName"] as? String else {return} |
| 77 | + |
| 78 | + // setup UIPageController for swipe changing vc |
| 79 | + setupPageController(channelName:channelName) |
| 80 | + |
| 81 | + // enable video module and set up video encoding configs |
| 82 | + agoraKit.enableVideo() |
| 83 | + agoraKit.setChannelProfile(.liveBroadcasting) |
| 84 | + agoraKit.setClientRole(.audience) |
| 85 | + |
| 86 | + // Set audio route to speaker |
| 87 | + agoraKit.setDefaultAudioRouteToSpeakerphone(true) |
| 88 | + |
| 89 | + // start joining channel |
| 90 | + // 1. Users can only see each other after they join the |
| 91 | + // same channel successfully using the same app id. |
| 92 | + // 2. If app certificate is turned on at dashboard, token is needed |
| 93 | + // when joining channel. The channel name and uid used to calculate |
| 94 | + // the token has to match the ones used for channel join |
| 95 | + let result = agoraKit.joinChannel(byToken: nil, channelId: channels[currentIndex].channelName, info: nil, uid: 0) |
| 96 | + if result != 0 { |
| 97 | + // Usually happens with invalid parameters |
| 98 | + // Error code description can be found at: |
| 99 | + // en: https://docs.agora.io/en/Voice/API%20Reference/oc/Constants/AgoraErrorCode.html |
| 100 | + // cn: https://docs.agora.io/cn/Voice/API%20Reference/oc/Constants/AgoraErrorCode.html |
| 101 | + self.showAlert(title: "Error", message: "joinChannel call failed: \(result), please check your params") |
| 102 | + } |
| 103 | + } |
| 104 | + |
| 105 | + override func willMove(toParent parent: UIViewController?) { |
| 106 | + if parent == nil { |
| 107 | + // leave channel when exiting the view |
| 108 | + if isJoined { |
| 109 | + agoraKit.leaveChannel { (stats) -> Void in |
| 110 | + LogUtils.log(message: "left channel, duration: \(stats.duration)", level: .info) |
| 111 | + } |
| 112 | + } |
| 113 | + } |
| 114 | + } |
| 115 | +} |
| 116 | + |
| 117 | +/// agora rtc engine delegate events |
| 118 | +extension QuickSwitchChannel: AgoraRtcEngineDelegate { |
| 119 | + func rtcEngine(_ engine: AgoraRtcEngineKit, didJoinChannel channel: String, withUid uid: UInt, elapsed: Int) { |
| 120 | + self.isJoined = true |
| 121 | + LogUtils.log(message: "Join \(channel) with uid \(uid) elapsed \(elapsed)ms", level: .info) |
| 122 | + |
| 123 | + guard let currentVC = pageViewController.viewControllers?.first as? QuickSwitchChannelVCItem, |
| 124 | + currentVC.channel.channelName == channel else { |
| 125 | + return |
| 126 | + } |
| 127 | + |
| 128 | + currentVC.showLoading(false) |
| 129 | + } |
| 130 | + |
| 131 | + /// callback when warning occured for agora sdk, warning can usually be ignored, still it's nice to check out |
| 132 | + /// what is happening |
| 133 | + /// Warning code description can be found at: |
| 134 | + /// en: https://docs.agora.io/en/Voice/API%20Reference/oc/Constants/AgoraWarningCode.html |
| 135 | + /// cn: https://docs.agora.io/cn/Voice/API%20Reference/oc/Constants/AgoraWarningCode.html |
| 136 | + /// @param warningCode warning code of the problem |
| 137 | + func rtcEngine(_ engine: AgoraRtcEngineKit, didOccurWarning warningCode: AgoraWarningCode) { |
| 138 | + LogUtils.log(message: "warning: \(warningCode.description)", level: .warning) |
| 139 | + } |
| 140 | + |
| 141 | + /// callback when error occured for agora sdk, you are recommended to display the error descriptions on demand |
| 142 | + /// to let user know something wrong is happening |
| 143 | + /// Error code description can be found at: |
| 144 | + /// en: https://docs.agora.io/en/Voice/API%20Reference/oc/Constants/AgoraErrorCode.html |
| 145 | + /// cn: https://docs.agora.io/cn/Voice/API%20Reference/oc/Constants/AgoraErrorCode.html |
| 146 | + /// @param errorCode error code of the problem |
| 147 | + func rtcEngine(_ engine: AgoraRtcEngineKit, didOccurError errorCode: AgoraErrorCode) { |
| 148 | + LogUtils.log(message: "error: \(errorCode)", level: .error) |
| 149 | + self.showAlert(title: "Error", message: "Error \(errorCode.description) occur") |
| 150 | + } |
| 151 | + |
| 152 | + /// callback when a remote user is joinning the channel, note audience in live broadcast mode will NOT trigger this event |
| 153 | + /// @param uid uid of remote joined user |
| 154 | + /// @param elapsed time elapse since current sdk instance join the channel in ms |
| 155 | + func rtcEngine(_ engine: AgoraRtcEngineKit, didJoinedOfUid uid: UInt, elapsed: Int) { |
| 156 | + LogUtils.log(message: "remote user join: \(uid) \(elapsed)ms", level: .info) |
| 157 | + |
| 158 | + guard let currentVC = pageViewController.viewControllers?.first as? QuickSwitchChannelVCItem, |
| 159 | + currentVC.channel.channelName == hostChannel?.channelName else { |
| 160 | + return |
| 161 | + } |
| 162 | + |
| 163 | + // Only one remote video view is available for this |
| 164 | + // tutorial. Here we check if there exists a surface |
| 165 | + // view tagged as this uid. |
| 166 | + let videoCanvas = AgoraRtcVideoCanvas() |
| 167 | + videoCanvas.uid = uid |
| 168 | + // the view to be binded |
| 169 | + videoCanvas.view = hostView |
| 170 | + videoCanvas.renderMode = .hidden |
| 171 | + agoraKit.setupRemoteVideo(videoCanvas) |
| 172 | + } |
| 173 | + |
| 174 | + /// callback when a remote user is leaving the channel, note audience in live broadcast mode will NOT trigger this event |
| 175 | + /// @param uid uid of remote joined user |
| 176 | + /// @param reason reason why this user left, note this event may be triggered when the remote user |
| 177 | + /// become an audience in live broadcasting profile |
| 178 | + func rtcEngine(_ engine: AgoraRtcEngineKit, didOfflineOfUid uid: UInt, reason: AgoraUserOfflineReason) { |
| 179 | + LogUtils.log(message: "remote user left: \(uid) reason \(reason)", level: .info) |
| 180 | + |
| 181 | + guard let currentVC = pageViewController.viewControllers?.first as? QuickSwitchChannelVCItem, |
| 182 | + currentVC.channel.channelName == hostChannel?.channelName else { |
| 183 | + return |
| 184 | + } |
| 185 | + |
| 186 | + // to unlink your view from sdk, so that your view reference will be released |
| 187 | + // note the video will stay at its last frame, to completely remove it |
| 188 | + // you will need to remove the EAGL sublayer from your binded view |
| 189 | + let videoCanvas = AgoraRtcVideoCanvas() |
| 190 | + videoCanvas.uid = uid |
| 191 | + // the view to be binded |
| 192 | + videoCanvas.view = nil |
| 193 | + videoCanvas.renderMode = .hidden |
| 194 | + agoraKit.setupRemoteVideo(videoCanvas) |
| 195 | + } |
| 196 | +} |
| 197 | + |
| 198 | +private extension QuickSwitchChannel { |
| 199 | + func channelViewController(at index: Int) -> QuickSwitchChannelVCItem? { |
| 200 | + guard channels.count > 0 else { |
| 201 | + return nil |
| 202 | + } |
| 203 | + |
| 204 | + var vcIndex = index |
| 205 | + if vcIndex >= channels.count { |
| 206 | + vcIndex = 0 |
| 207 | + } else if vcIndex < 0 { |
| 208 | + vcIndex = channels.count - 1 |
| 209 | + } |
| 210 | + |
| 211 | + let channelVC = QuickSwitchChannelVCItem(nibName: "QuickSwitchChannelVCItem", bundle: nil) |
| 212 | + channelVC.index = vcIndex |
| 213 | + channelVC.channel = channels[vcIndex] |
| 214 | + |
| 215 | + return channelVC |
| 216 | + } |
| 217 | +} |
| 218 | + |
| 219 | +extension QuickSwitchChannel: UIPageViewControllerDataSource { |
| 220 | + func pageViewController(_ pageViewController: UIPageViewController, viewControllerAfter viewController: UIViewController) -> UIViewController? { |
| 221 | + guard let channelVC = viewController as? QuickSwitchChannelVCItem else { |
| 222 | + return nil |
| 223 | + } |
| 224 | + return channelViewController(at: channelVC.index + 1) |
| 225 | + } |
| 226 | + |
| 227 | + func pageViewController(_ pageViewController: UIPageViewController, viewControllerBefore viewController: UIViewController) -> UIViewController? { |
| 228 | + guard let channelVC = viewController as? QuickSwitchChannelVCItem else { |
| 229 | + return nil |
| 230 | + } |
| 231 | + return channelViewController(at: channelVC.index - 1) |
| 232 | + } |
| 233 | +} |
| 234 | + |
| 235 | +extension QuickSwitchChannel : UIPageViewControllerDelegate |
| 236 | +{ |
| 237 | + func pageViewController(_ pageViewController: UIPageViewController, didFinishAnimating finished: Bool, previousViewControllers: [UIViewController], transitionCompleted completed: Bool) { |
| 238 | + guard let previousVC = previousViewControllers.last as? QuickSwitchChannelVCItem, |
| 239 | + let currentVC = pageViewController.viewControllers?.first as? QuickSwitchChannelVCItem, |
| 240 | + previousVC != currentVC else { |
| 241 | + return |
| 242 | + } |
| 243 | + |
| 244 | + setHostViewController(currentVC) |
| 245 | + agoraKit.switchChannel(byToken: nil, channelId: currentVC.channel.channelName, joinSuccess: nil) |
| 246 | + } |
| 247 | +} |
0 commit comments