Skip to content

Commit ccf6580

Browse files
author
xianing
committed
adapt iOS 37200, more changes
1 parent b431e2f commit ccf6580

15 files changed

Lines changed: 1018 additions & 183 deletions

File tree

Android/APIExample/app/src/main/java/io/agora/api/example/examples/advanced/customaudio/PCMRecordService.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -132,7 +132,6 @@ private void readFile(File file) {
132132
stopped = false;
133133
byte[] buffer = new byte[frameSize];
134134
if ((len = fis.read(buffer)) == -1) continue;
135-
Log.d("dqm", "frameSize: " + frameSize + " len: " + len);
136135
try {
137136
/**Pushes the external audio frame to the Agora SDK for encoding.
138137
* @param data External audio data to be pushed.
@@ -144,8 +143,9 @@ private void readFile(File file) {
144143
* @return
145144
* 0: Success.
146145
* < 0: Failure.*/
147-
CustomAudioSource.engine.pushExternalAudioFrame(
148-
ByteBuffer.wrap(buffer), 0, CustomAudioSource.rtcConnection2.localUid);
146+
// int ret = CustomAudioSource.engine.pushExternalAudioFrame(
147+
// ByteBuffer.wrap(buffer), 0, CustomAudioSource.rtcConnection2.localUid);
148+
int ret = CustomAudioSource.engine.pushExternalAudioFrame(buffer, len);
149149
} catch (Exception e) {
150150
e.printStackTrace();
151151
}

iOS/APIExample.xcodeproj/project.pbxproj

Lines changed: 127 additions & 31 deletions
Large diffs are not rendered by default.

iOS/APIExample/Common/AgoraExtension.swift

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,17 @@ extension AgoraVideoOutputOrientationMode {
6060
}
6161
}
6262

63+
extension AgoraClientRole {
64+
func description() -> String {
65+
switch self {
66+
case .broadcaster: return "Broadcaster".localized
67+
case .audience: return "Audience".localized
68+
default:
69+
return "\(self.rawValue)"
70+
}
71+
}
72+
}
73+
6374
extension AgoraAudioProfile {
6475
func description() -> String {
6576
switch self {

iOS/APIExample/Examples/Advanced/LiveStreaming/Base.lproj/LiveStreaming.storyboard

Lines changed: 226 additions & 0 deletions
Large diffs are not rendered by default.
Lines changed: 321 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,321 @@
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+
class LiveStreamingEntry : UIViewController
13+
{
14+
@IBOutlet weak var joinButton: UIButton!
15+
@IBOutlet weak var channelTextField: UITextField!
16+
let identifier = "LiveStreaming"
17+
var role:AgoraClientRole = .broadcaster
18+
19+
override func viewDidLoad() {
20+
super.viewDidLoad()
21+
}
22+
23+
func getRoleAction(_ role: AgoraClientRole) -> UIAlertAction{
24+
return UIAlertAction(title: "\(role.description())", style: .default, handler: {[unowned self] action in
25+
self.role = role
26+
self.doJoin()
27+
})
28+
}
29+
30+
31+
@IBAction func doJoinPressed(sender: UIButton) {
32+
guard let _ = channelTextField.text else {return}
33+
//resign channel text field
34+
channelTextField.resignFirstResponder()
35+
36+
//display role picker
37+
let alert = UIAlertController(title: "Pick Role".localized, message: nil, preferredStyle: UIDevice.current.userInterfaceIdiom == .pad ? UIAlertController.Style.alert : UIAlertController.Style.actionSheet)
38+
alert.addAction(getRoleAction(.broadcaster))
39+
alert.addAction(getRoleAction(.audience))
40+
alert.addCancelAction()
41+
present(alert, animated: true, completion: nil)
42+
}
43+
44+
func doJoin() {
45+
guard let channelName = channelTextField.text else {return}
46+
let storyBoard: UIStoryboard = UIStoryboard(name: identifier, bundle: nil)
47+
// create new view controller every time to ensure we get a clean vc
48+
guard let newViewController = storyBoard.instantiateViewController(withIdentifier: identifier) as? BaseViewController else {return}
49+
newViewController.title = channelName
50+
newViewController.configs = ["channelName":channelName, "role":self.role]
51+
self.navigationController?.pushViewController(newViewController, animated: true)
52+
}
53+
}
54+
55+
class LiveStreamingMain: BaseViewController {
56+
var foregroundVideo = Bundle.loadView(fromNib: "VideoView", withType: VideoView.self)
57+
var backgroundVideo = Bundle.loadView(fromNib: "VideoView", withType: VideoView.self)
58+
@IBOutlet weak var foregroundVideoContainer:UIView!
59+
@IBOutlet weak var backgroundVideoContainer:UIView!
60+
@IBOutlet weak var clientRoleToggleView:UIView!
61+
@IBOutlet weak var ultraLowLatencyToggleView:UIView!
62+
@IBOutlet weak var clientRoleToggle:UISwitch!
63+
@IBOutlet weak var ultraLowLatencyToggle:UISwitch!
64+
var remoteUid: UInt? {
65+
didSet {
66+
foregroundVideoContainer.isHidden = !(role == .broadcaster && remoteUid != nil)
67+
}
68+
}
69+
var agoraKit: AgoraRtcEngineKit!
70+
var role: AgoraClientRole = .broadcaster {
71+
didSet {
72+
foregroundVideoContainer.isHidden = !(role == .broadcaster && remoteUid != nil)
73+
ultraLowLatencyToggle.isEnabled = role == .audience
74+
}
75+
}
76+
var isLocalVideoForeground = false {
77+
didSet {
78+
if isLocalVideoForeground {
79+
foregroundVideo.setPlaceholder(text: "Local Host".localized)
80+
backgroundVideo.setPlaceholder(text: "Remote Host".localized)
81+
} else {
82+
foregroundVideo.setPlaceholder(text: "Remote Host".localized)
83+
backgroundVideo.setPlaceholder(text: "Local Host".localized)
84+
}
85+
}
86+
}
87+
var isUltraLowLatencyOn: Bool = false
88+
89+
// indicate if current instance has joined channel
90+
var isJoined: Bool = false
91+
92+
override func viewDidLoad() {
93+
super.viewDidLoad()
94+
95+
// layout render view
96+
foregroundVideoContainer.addSubview(foregroundVideo)
97+
backgroundVideoContainer.addSubview(backgroundVideo)
98+
foregroundVideo.bindFrameToSuperviewBounds()
99+
backgroundVideo.bindFrameToSuperviewBounds()
100+
101+
// set up agora instance when view loadedlet config = AgoraRtcEngineConfig()
102+
let config = AgoraRtcEngineConfig()
103+
config.appId = KeyCenter.AppId
104+
config.channelProfile = .liveBroadcasting
105+
agoraKit = AgoraRtcEngineKit.sharedEngine(with: config, delegate: self)
106+
agoraKit.setLogFile(LogUtils.sdkLogPath())
107+
108+
// get channel name from configs
109+
guard let channelName = configs["channelName"] as? String, let clientRole = configs["role"] as? AgoraClientRole else {return}
110+
111+
role = clientRole
112+
// for audience put local video in foreground
113+
isLocalVideoForeground = role == .audience
114+
// if inital role is broadcaster, do not show audience options
115+
clientRoleToggleView.isHidden = role == .broadcaster
116+
ultraLowLatencyToggleView.isHidden = role == .broadcaster
117+
118+
// make this room live broadcasting room
119+
updateClientRole(role)
120+
121+
// enable video module and set up video encoding configs
122+
agoraKit.enableVideo()
123+
124+
// Set audio route to speaker
125+
agoraKit.setDefaultAudioRouteToSpeakerphone(true)
126+
127+
// start joining channel
128+
// 1. Users can only see each other after they join the
129+
// same channel successfully using the same app id.
130+
// 2. If app certificate is turned on at dashboard, token is needed
131+
// when joining channel. The channel name and uid used to calculate
132+
// the token has to match the ones used for channel join
133+
let option = AgoraRtcChannelMediaOptions()
134+
option.publishCameraTrack = .of(true)
135+
option.clientRoleType = .of((Int32)(AgoraClientRole.broadcaster.rawValue))
136+
137+
let result = agoraKit.joinChannel(byToken: KeyCenter.Token, channelId: channelName, uid: 0, mediaOptions: option)
138+
if result != 0 {
139+
// Usually happens with invalid parameters
140+
// Error code description can be found at:
141+
// en: https://docs.agora.io/en/Voice/API%20Reference/oc/Constants/AgoraErrorCode.html
142+
// cn: https://docs.agora.io/cn/Voice/API%20Reference/oc/Constants/AgoraErrorCode.html
143+
self.showAlert(title: "Error", message: "joinChannel call failed: \(result), please check your params")
144+
}
145+
}
146+
147+
/// make myself a broadcaster
148+
func becomeBroadcaster() {
149+
guard let resolution = GlobalSettings.shared.getSetting(key: "resolution")?.selectedOption().value as? CGSize,
150+
let fps = GlobalSettings.shared.getSetting(key: "fps")?.selectedOption().value as? AgoraVideoFrameRate,
151+
let orientation = GlobalSettings.shared.getSetting(key: "orientation")?.selectedOption().value as? AgoraVideoOutputOrientationMode else {
152+
LogUtils.log(message: "invalid video configurations, failed to become broadcaster", level: .error)
153+
return
154+
}
155+
agoraKit.setVideoEncoderConfiguration(AgoraVideoEncoderConfiguration(size: resolution,
156+
frameRate: fps,
157+
bitrate: AgoraVideoBitrateStandard,
158+
orientationMode: orientation, mirrorMode: .auto))
159+
160+
// set up local video to render your local camera preview
161+
let videoCanvas = AgoraRtcVideoCanvas()
162+
videoCanvas.uid = 0
163+
// the view to be binded
164+
videoCanvas.view = localVideoCanvas()
165+
videoCanvas.renderMode = .hidden
166+
agoraKit.setupLocalVideo(videoCanvas)
167+
// you have to call startPreview to see local video
168+
agoraKit.startPreview()
169+
170+
agoraKit.setClientRole(.broadcaster, options: nil)
171+
}
172+
173+
/// make myself an audience
174+
func becomeAudience() {
175+
// unbind view
176+
agoraKit.setupLocalVideo(nil)
177+
// You have to provide client role options if set to audience
178+
let options = AgoraClientRoleOptions()
179+
options.audienceLatencyLevel = isUltraLowLatencyOn ? .ultraLowLatency : .lowLatency
180+
agoraKit.setClientRole(.audience, options: options)
181+
}
182+
183+
func localVideoCanvas() -> UIView {
184+
return isLocalVideoForeground ? foregroundVideo.videoView : backgroundVideo.videoView
185+
}
186+
187+
func remoteVideoCanvas() -> UIView {
188+
return isLocalVideoForeground ? backgroundVideo.videoView : foregroundVideo.videoView
189+
}
190+
191+
@IBAction func onTapForegroundVideo(_ sender:UIGestureRecognizer) {
192+
isLocalVideoForeground = !isLocalVideoForeground
193+
194+
let localVideoCanvas = AgoraRtcVideoCanvas()
195+
localVideoCanvas.uid = 0
196+
localVideoCanvas.renderMode = .hidden
197+
localVideoCanvas.view = self.localVideoCanvas()
198+
199+
let remoteVideoCanvas = AgoraRtcVideoCanvas()
200+
remoteVideoCanvas.renderMode = .hidden
201+
remoteVideoCanvas.view = self.remoteVideoCanvas()
202+
203+
agoraKit.setupLocalVideo(localVideoCanvas)
204+
if let uid = remoteUid {
205+
remoteVideoCanvas.uid = uid
206+
agoraKit.setupRemoteVideo(remoteVideoCanvas)
207+
}
208+
}
209+
210+
@IBAction func onToggleClientRole(_ sender:UISwitch) {
211+
let role:AgoraClientRole = sender.isOn ? .broadcaster : .audience
212+
updateClientRole(role)
213+
}
214+
215+
fileprivate func updateClientRole(_ role:AgoraClientRole) {
216+
self.role = role
217+
if(role == .broadcaster) {
218+
becomeBroadcaster()
219+
} else {
220+
becomeAudience()
221+
}
222+
}
223+
224+
@IBAction func onToggleUltraLowLatency(_ sender:UISwitch) {
225+
updateUltraLowLatency(sender.isOn)
226+
}
227+
228+
fileprivate func updateUltraLowLatency(_ enabled:Bool) {
229+
if(self.role == .audience) {
230+
self.isUltraLowLatencyOn = enabled
231+
updateClientRole(.audience)
232+
}
233+
}
234+
235+
override func willMove(toParent parent: UIViewController?) {
236+
if parent == nil {
237+
// leave channel when exiting the view
238+
if isJoined {
239+
agoraKit.leaveChannel { (stats) -> Void in
240+
LogUtils.log(message: "left channel, duration: \(stats.duration)", level: .info)
241+
}
242+
}
243+
}
244+
}
245+
}
246+
247+
/// agora rtc engine delegate events
248+
extension LiveStreamingMain: AgoraRtcEngineDelegate {
249+
/// callback when warning occured for agora sdk, warning can usually be ignored, still it's nice to check out
250+
/// what is happening
251+
/// Warning code description can be found at:
252+
/// en: https://docs.agora.io/en/Voice/API%20Reference/oc/Constants/AgoraWarningCode.html
253+
/// cn: https://docs.agora.io/cn/Voice/API%20Reference/oc/Constants/AgoraWarningCode.html
254+
/// @param warningCode warning code of the problem
255+
func rtcEngine(_ engine: AgoraRtcEngineKit, didOccurWarning warningCode: AgoraWarningCode) {
256+
LogUtils.log(message: "warning: \(warningCode.description)", level: .warning)
257+
}
258+
259+
/// callback when error occured for agora sdk, you are recommended to display the error descriptions on demand
260+
/// to let user know something wrong is happening
261+
/// Error code description can be found at:
262+
/// en: https://docs.agora.io/en/Voice/API%20Reference/oc/Constants/AgoraErrorCode.html
263+
/// cn: https://docs.agora.io/cn/Voice/API%20Reference/oc/Constants/AgoraErrorCode.html
264+
/// @param errorCode error code of the problem
265+
func rtcEngine(_ engine: AgoraRtcEngineKit, didOccurError errorCode: AgoraErrorCode) {
266+
LogUtils.log(message: "error: \(errorCode)", level: .error)
267+
self.showAlert(title: "Error", message: "Error \(errorCode.description) occur")
268+
}
269+
270+
/// callback when the local user joins a specified channel.
271+
/// @param channel
272+
/// @param uid uid of local user
273+
/// @param elapsed time elapse since current sdk instance join the channel in ms
274+
func rtcEngine(_ engine: AgoraRtcEngineKit, didJoinChannel channel: String, withUid uid: UInt, elapsed: Int) {
275+
isJoined = true
276+
LogUtils.log(message: "Join \(channel) with uid \(uid) elapsed \(elapsed)ms", level: .info)
277+
}
278+
279+
/// callback when a remote user is joinning the channel, note audience in live broadcast mode will NOT trigger this event
280+
/// @param uid uid of remote joined user
281+
/// @param elapsed time elapse since current sdk instance join the channel in ms
282+
func rtcEngine(_ engine: AgoraRtcEngineKit, didJoinedOfUid uid: UInt, elapsed: Int) {
283+
LogUtils.log(message: "remote user join: \(uid) \(elapsed)ms", level: .info)
284+
285+
//record remote uid
286+
remoteUid = uid
287+
288+
// Only one remote video view is available for this
289+
// tutorial. Here we check if there exists a surface
290+
// view tagged as this uid.
291+
let videoCanvas = AgoraRtcVideoCanvas()
292+
videoCanvas.uid = uid
293+
// the view to be binded
294+
videoCanvas.view = remoteVideoCanvas()
295+
videoCanvas.renderMode = .hidden
296+
agoraKit.setupRemoteVideo(videoCanvas)
297+
}
298+
299+
/// callback when a remote user is leaving the channel, note audience in live broadcast mode will NOT trigger this event
300+
/// @param uid uid of remote joined user
301+
/// @param reason reason why this user left, note this event may be triggered when the remote user
302+
/// become an audience in live broadcasting profile
303+
func rtcEngine(_ engine: AgoraRtcEngineKit, didOfflineOfUid uid: UInt, reason: AgoraUserOfflineReason) {
304+
LogUtils.log(message: "remote user left: \(uid) reason \(reason)", level: .info)
305+
306+
//clear remote uid
307+
if(remoteUid == uid){
308+
remoteUid = nil
309+
}
310+
311+
// to unlink your view from sdk, so that your view reference will be released
312+
// note the video will stay at its last frame, to completely remove it
313+
// you will need to remove the EAGL sublayer from your binded view
314+
let videoCanvas = AgoraRtcVideoCanvas()
315+
videoCanvas.uid = uid
316+
// the view to be binded
317+
videoCanvas.view = nil
318+
videoCanvas.renderMode = .hidden
319+
agoraKit.setupRemoteVideo(videoCanvas)
320+
}
321+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
2+
/* Class = "UITextField"; placeholder = "Enter channel name"; ObjectID = "GWc-L5-fZV"; */
3+
"GWc-L5-fZV.placeholder" = "输入频道名";
4+
5+
/* Class = "UILabel"; text = "Ultra Low Latency"; ObjectID = "Lzz-2R-G7f"; */
6+
"Lzz-2R-G7f.text" = "极速直播";
7+
8+
/* Class = "UILabel"; text = "Co-host"; ObjectID = "XcJ-am-UAb"; */
9+
"XcJ-am-UAb.text" = "连麦";
10+
11+
/* Class = "UIButton"; normalTitle = "Join"; ObjectID = "kbN-ZR-nNn"; */
12+
"kbN-ZR-nNn.normalTitle" = "加入频道";

0 commit comments

Comments
 (0)