Skip to content

Commit 721ca58

Browse files
committed
add quick start
Former-commit-id: 88ce84b
1 parent 500155c commit 721ca58

6 files changed

Lines changed: 407 additions & 6 deletions

File tree

iOS/APIExample.xcodeproj/project.pbxproj

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,9 @@
88

99
/* Begin PBXBuildFile section */
1010
0318857924CD667A00C699EB /* SettingsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0318857824CD667A00C699EB /* SettingsViewController.swift */; };
11+
0339D6D224E91B80008739CD /* QuickSwitchChannelVCItem.xib in Resources */ = {isa = PBXBuildFile; fileRef = 0339D6D124E91B80008739CD /* QuickSwitchChannelVCItem.xib */; };
12+
0339D6D424E91BAA008739CD /* QuickSwitchChannelVCItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0339D6D324E91BAA008739CD /* QuickSwitchChannelVCItem.swift */; };
13+
0339D6D624E91CEB008739CD /* QuickSwitchChannel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0339D6D524E91CEB008739CD /* QuickSwitchChannel.swift */; };
1114
036C42A924D27AB000A59000 /* CustomVideoSourceMediaIO.swift in Sources */ = {isa = PBXBuildFile; fileRef = 036C42A824D27AB000A59000 /* CustomVideoSourceMediaIO.swift */; };
1215
036C42AC24D292A700A59000 /* AgoraCameraSourceMediaIO.swift in Sources */ = {isa = PBXBuildFile; fileRef = 036C42AB24D292A700A59000 /* AgoraCameraSourceMediaIO.swift */; };
1316
036C42AE24D2950A00A59000 /* CustomVideoSourcePush.swift in Sources */ = {isa = PBXBuildFile; fileRef = 036C42AD24D2950A00A59000 /* CustomVideoSourcePush.swift */; };
@@ -51,6 +54,9 @@
5154

5255
/* Begin PBXFileReference section */
5356
0318857824CD667A00C699EB /* SettingsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsViewController.swift; sourceTree = "<group>"; };
57+
0339D6D124E91B80008739CD /* QuickSwitchChannelVCItem.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = QuickSwitchChannelVCItem.xib; sourceTree = "<group>"; };
58+
0339D6D324E91BAA008739CD /* QuickSwitchChannelVCItem.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = QuickSwitchChannelVCItem.swift; sourceTree = "<group>"; };
59+
0339D6D524E91CEB008739CD /* QuickSwitchChannel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = QuickSwitchChannel.swift; sourceTree = "<group>"; };
5460
036C42A824D27AB000A59000 /* CustomVideoSourceMediaIO.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CustomVideoSourceMediaIO.swift; sourceTree = "<group>"; };
5561
036C42AB24D292A700A59000 /* AgoraCameraSourceMediaIO.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AgoraCameraSourceMediaIO.swift; sourceTree = "<group>"; };
5662
036C42AD24D2950A00A59000 /* CustomVideoSourcePush.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CustomVideoSourcePush.swift; sourceTree = "<group>"; };
@@ -138,6 +144,16 @@
138144
path = Settings;
139145
sourceTree = "<group>";
140146
};
147+
0339D6CA24E9170D008739CD /* QuickSwitchChannel */ = {
148+
isa = PBXGroup;
149+
children = (
150+
0339D6D524E91CEB008739CD /* QuickSwitchChannel.swift */,
151+
0339D6D324E91BAA008739CD /* QuickSwitchChannelVCItem.swift */,
152+
0339D6D124E91B80008739CD /* QuickSwitchChannelVCItem.xib */,
153+
);
154+
path = QuickSwitchChannel;
155+
sourceTree = "<group>";
156+
};
141157
036C42AA24D292A700A59000 /* ExternalVideo */ = {
142158
isa = PBXGroup;
143159
children = (
@@ -288,6 +304,7 @@
288304
036C42A824D27AB000A59000 /* CustomVideoSourceMediaIO.swift */,
289305
036C42AD24D2950A00A59000 /* CustomVideoSourcePush.swift */,
290306
036C42B724D57F6D00A59000 /* RawMediaData.swift */,
307+
0339D6CA24E9170D008739CD /* QuickSwitchChannel */,
291308
);
292309
path = Advanced;
293310
sourceTree = "<group>";
@@ -375,6 +392,7 @@
375392
isa = PBXResourcesBuildPhase;
376393
buildActionMask = 2147483647;
377394
files = (
395+
0339D6D224E91B80008739CD /* QuickSwitchChannelVCItem.xib in Resources */,
378396
A7CA48C424553CF700507435 /* Popover.storyboard in Resources */,
379397
03D13BDC2448758B00B599B3 /* LaunchScreen.storyboard in Resources */,
380398
03D13BD92448758B00B599B3 /* Assets.xcassets in Resources */,
@@ -441,6 +459,7 @@
441459
036C42AE24D2950A00A59000 /* CustomVideoSourcePush.swift in Sources */,
442460
03DF1D9324CFC29700DF7151 /* ExternalAudio.mm in Sources */,
443461
03DF1D7824CFBF4800DF7151 /* CustomAudioSource.swift in Sources */,
462+
0339D6D424E91BAA008739CD /* QuickSwitchChannelVCItem.swift in Sources */,
444463
A75A56DB24A0603100D0089E /* JoinChannelVideo.swift in Sources */,
445464
036C42B624D2A3C600A59000 /* AgoraMetalShader.metal in Sources */,
446465
036C42BF24D5853200A59000 /* AgoraMediaRawData.m in Sources */,
@@ -464,6 +483,7 @@
464483
03D13BD02448758900B599B3 /* AppDelegate.swift in Sources */,
465484
A7847F922458062900469187 /* StatisticsInfo.swift in Sources */,
466485
03BCEC762449EB5000ED7177 /* LogViewController.swift in Sources */,
486+
0339D6D624E91CEB008739CD /* QuickSwitchChannel.swift in Sources */,
467487
A7847F942458089E00469187 /* AgoraExtension.swift in Sources */,
468488
03BCEC50244938C500ED7177 /* BaseViewController.swift in Sources */,
469489
);

iOS/APIExample/Base.lproj/Main.storyboard

Lines changed: 21 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
<?xml version="1.0" encoding="UTF-8"?>
2-
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="16097" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="HWc-eV-zrm">
2+
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="16097.2" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="HWc-eV-zrm">
33
<device id="retina6_1" orientation="portrait" appearance="light"/>
44
<dependencies>
55
<deployment identifier="iOS"/>
@@ -231,7 +231,7 @@
231231
</viewController>
232232
<placeholder placeholderIdentifier="IBFirstResponder" id="8dQ-Wz-bG7" userLabel="First Responder" customClass="UIResponder" sceneMemberID="firstResponder"/>
233233
</objects>
234-
<point key="canvasLocation" x="-820.28985507246387" y="930.80357142857133"/>
234+
<point key="canvasLocation" x="3130" y="931"/>
235235
</scene>
236236
<!--Video Metadata Main-->
237237
<scene sceneID="yfV-9t-Qse">
@@ -273,7 +273,7 @@
273273
</viewController>
274274
<placeholder placeholderIdentifier="IBFirstResponder" id="2sa-cD-j1C" userLabel="First Responder" customClass="UIResponder" sceneMemberID="firstResponder"/>
275275
</objects>
276-
<point key="canvasLocation" x="-1730" y="1936"/>
276+
<point key="canvasLocation" x="-2113" y="1936"/>
277277
</scene>
278278
<!--Join Channel Video-->
279279
<scene sceneID="537-qh-Aaa">
@@ -533,7 +533,7 @@
533533
</viewController>
534534
<placeholder placeholderIdentifier="IBFirstResponder" id="yWQ-bO-5v1" userLabel="First Responder" customClass="UIResponder" sceneMemberID="firstResponder"/>
535535
</objects>
536-
<point key="canvasLocation" x="-820.28985507246387" y="930.80357142857133"/>
536+
<point key="canvasLocation" x="-1333" y="1936"/>
537537
</scene>
538538
<!--Navigation Controller-->
539539
<scene sceneID="ur4-qk-WHW">
@@ -581,7 +581,7 @@
581581
</viewController>
582582
<placeholder placeholderIdentifier="IBFirstResponder" id="5gf-tx-tSn" userLabel="First Responder" customClass="UIResponder" sceneMemberID="firstResponder"/>
583583
</objects>
584-
<point key="canvasLocation" x="-820.28985507246387" y="930.80357142857133"/>
584+
<point key="canvasLocation" x="1938" y="226"/>
585585
</scene>
586586
<!--CustomAudioRender-->
587587
<scene sceneID="Ke9-pN-KXH">
@@ -712,5 +712,21 @@
712712
</objects>
713713
<point key="canvasLocation" x="118.84057971014494" y="930.80357142857133"/>
714714
</scene>
715+
<!--Quick Switch Channel-->
716+
<scene sceneID="5D2-2i-zm0">
717+
<objects>
718+
<viewController storyboardIdentifier="QuickSwitchChannel" title="Join Channel Video" id="iib-g5-GmB" userLabel="Quick Switch Channel" customClass="QuickSwitchChannel" customModule="APIExample" customModuleProvider="target" sceneMemberID="viewController">
719+
<view key="view" contentMode="scaleToFill" id="k6C-Db-Vrt">
720+
<rect key="frame" x="0.0" y="0.0" width="414" height="896"/>
721+
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
722+
<color key="backgroundColor" systemColor="systemBackgroundColor" cocoaTouchSystemColor="whiteColor"/>
723+
<viewLayoutGuide key="safeArea" id="8LW-2Z-rjf"/>
724+
</view>
725+
<navigationItem key="navigationItem" title="Join Channel" id="O4p-Hd-Lr5"/>
726+
</viewController>
727+
<placeholder placeholderIdentifier="IBFirstResponder" id="cDv-f1-m4W" userLabel="First Responder" customClass="UIResponder" sceneMemberID="firstResponder"/>
728+
</objects>
729+
<point key="canvasLocation" x="2275" y="1936"/>
730+
</scene>
715731
</scenes>
716732
</document>
Lines changed: 247 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,247 @@
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

Comments
 (0)