本文檔將介紹如何在您的 iOS 專案中整合 ARTC SDK, 快速實現一個簡單的純音頻互動App,適用於語音通話、語聊房等情境。
功能介紹
在開始前,您需要瞭解以下有關音視頻即時互動的基本概念:
ARTC SDK:阿里雲即時音視頻產品,協助開發中快速實現即時音視頻互動的SDK。
頻道:房間的概念,在同一個頻道內的使用者可以進行即時互動。
主播:可在頻道內發布音視頻流,並可訂閱其他主播發布的音視頻流。
觀眾:可在頻道內訂閱音視頻流,不能發布音視頻流。
下圖展示了實現語音通話及語聊房的基本流程:
使用者需要先調用
joinChannel加入頻道,才能進行推流、拉流:普通純語音通話情境:所有使用者都是主播角色,可以進行推流和拉流;
語聊房情境:需要在頻道內推流的使用者佈建主播角色;如果使用者只需要拉流,不需要推流,則設定觀眾角色;
通過
setClientRole為使用者佈建不同的角色。
加入頻道後,不同角色的使用者有不同的推拉流行為:
所有頻道內的使用者都可以接收相同頻道內的音視頻流;
主播角色可以在頻道內推音視頻流;
觀眾如果需要推流,需要調用
setClientRole方法,將使用者角色切換成主播,便可以推流。
樣本專案
阿里雲ARTC SDK提供了開源的樣本專案供客戶參考,您可以前往下載或查看樣本源碼。
前提條件
在實現功能以前,請確保您的開發環境滿足:
開發工具:Xcode 14.0 及以上版本,推薦使用最新正式版本。
配置推薦:CocoaPods 1.9.3 及以上版本。
測試裝置:iOS 9.0 及以上版本的測試裝置。
實現步驟
下面將以語聊房情境為例進行示範,相關功能時序如下:
語聊房情境主要特點如下:
純音頻:頻道內僅包含音頻,不包含視頻。
主播/觀眾角色:頻道內角色分為主播和觀眾角色,主播角色可以推拉音頻流,觀眾角色只能拉取主播推送的音頻流;觀眾角色可以切換為主播角色。
實現純音頻互動
1、申請許可權請求
進入音視訊通話時,雖然SDK會檢查是否已在App中授予了所需要的許可權,當為保障體驗,建議在發起通話前檢查視頻拍攝及麥克風採集的許可權。
func checkMicrophonePermission(completion: @escaping (Bool) -> Void) {
let status = AVCaptureDevice.authorizationStatus(for: .audio)
switch status {
case .notDetermined:
AVCaptureDevice.requestAccess(for: .audio) { granted in
completion(granted)
}
case .authorized:
completion(true)
default:
completion(false)
}
}
func checkCameraPermission(completion: @escaping (Bool) -> Void) {
let status = AVCaptureDevice.authorizationStatus(for: .video)
switch status {
case .notDetermined:
AVCaptureDevice.requestAccess(for: .video) { granted in
completion(granted)
}
case .authorized:
completion(true)
default:
completion(false)
}
}
// 使用樣本
checkMicrophonePermission { granted in
if granted {
print("使用者已授權麥克風")
} else {
print("使用者未授權麥克風")
}
}
checkCameraPermission { granted in
if granted {
print("使用者已授權網路攝影機")
} else {
print("使用者未授權網路攝影機")
}
}
2、鑒權Token
加入ARTC頻道需要一個鑒權Token,用於鑒權使用者的合法身份,其鑒權Token建置規則參見:Token鑒權。Token 產生有兩種方式:單參數方式和多參數方式,不同的Token產生方式需要調用SDK不同的加入頻道(joinChannel)的介面。
上線發布階段:
由於Token的產生需要使用AppKey,寫死在用戶端存在泄漏的風險,因此強烈建議線上業務通過業務Server產生下發給用戶端。
開發調試階段:
開發調試階段,如果業務Server還沒有產生Token的邏輯,可以暫時參考APIExample上的Token產生邏輯,產生臨時Token,其參考代碼如下:
class ARTCTokenHelper: NSObject {
/**
* RTC AppId
*/
public static let AppId = "<RTC AppId>"
/**
* RTC AppKey
*/
public static let AppKey = "<RTC AppKey>"
/**
* 根據channelId,userId, timestamp 產生多參數入會的 token
* Generate a multi-parameter meeting token based on channelId, userId, and timestamp
*/
public func generateAuthInfoToken(appId: String = ARTCTokenHelper.AppId, appKey: String = ARTCTokenHelper.AppKey, channelId: String, userId: String, timestamp: Int64) -> String {
let stringBuilder = appId + appKey + channelId + userId + "\(timestamp)"
let token = ARTCTokenHelper.GetSHA256(stringBuilder)
return token
}
/**
* 根據channelId,userId, nonce 產生單參數入會 的token
* Generate a single-parameter meeting token based on channelId, userId, and nonce
*/
public func generateJoinToken(appId: String = ARTCTokenHelper.AppId, appKey: String = ARTCTokenHelper.AppKey, channelId: String, userId: String, timestamp: Int64, nonce: String = "") -> String {
let token = self.generateAuthInfoToken(appId: appId, appKey: appKey, channelId: channelId, userId: userId, timestamp: timestamp)
let tokenJson: [String: Any] = [
"appid": appId,
"channelid": channelId,
"userid": userId,
"nonce": nonce,
"timestamp": timestamp,
"token": token
]
if let jsonData = try? JSONSerialization.data(withJSONObject: tokenJson, options: []),
let base64Token = jsonData.base64EncodedString() as String? {
return base64Token
}
return ""
}
/**
* 字串簽名
* String signing (SHA256)
*/
private static func GetSHA256(_ input: String) -> String {
// 將輸入字串轉換為資料
let data = Data(input.utf8)
// 建立用於儲存雜湊結果的緩衝區
var hash = [UInt8](repeating: 0, count: Int(CC_SHA256_DIGEST_LENGTH))
// 計算 SHA-256 雜湊值
data.withUnsafeBytes {
_ = CC_SHA256($0.baseAddress, CC_LONG(data.count), &hash)
}
// 將雜湊值轉換為十六進位字串
return hash.map { String(format: "%02hhx", $0) }.joined()
}
}
3. 建立並初始化引擎
建立 RTC 引擎
調用getInstance建立 RTC 引擎對象。
private var rtcEngine: AliRtcEngine? = nil
// 建立引擎並設定回調
let engine = AliRtcEngine.sharedInstance(self, extras:nil)
self.rtcEngine = engine初始化引擎
調用
setChannelProfile介面設定頻道為互動模式。根據業務情境中使用者的角色,調用
setClientRole介面為使用者佈建主播/觀眾角色。調用
setAudioProfile介面設定音頻品質與情境模式。
// 設定頻道模式為互動模式,RTC下都使用AliRtcInteractivelive
engine.setChannelProfile(AliRtcChannelProfile.interactivelive)
// 設定角色
if self.isAnchor {
// 主播模式,需要推音視頻流,設定AliRtcClientRoleInteractive
engine.setClientRole(AliRtcClientRole.roleInteractive)
}
else {
// 觀眾模式,不需要推音視頻流,設定AliRtcClientRolelive
engine.setClientRole(AliRtcClientRole.rolelive)
}
// 設定音頻Profile,預設使用高音質模式AliRtcEngineHighQualityMode及音樂模式AliRtcSceneMusicMode
engine.setAudioProfile(AliRtcAudioProfile.engineHighQualityMode, audio_scene: AliRtcAudioScenario.sceneMusicMode)實現常用回調
SDK 在運行過程中如遇到異常情況,會優先嘗試內部重試機制以自動回復。對於無法自行解決的錯誤,SDK 會通過預定義的回調介面通知您的應用程式。
以下是一些 SDK 無法處理、需由應用程式層監聽和響應的關鍵回調:
異常發生原因 | 回調及參數 | 解決方案 | 說明 |
鑒權失敗 |
| 發生錯誤時App需要檢查Token是否正確。 | 在使用者主動調用API時,若鑒權失敗,系統將在調用API的回調中返回鑒權失敗的錯誤資訊。 |
鑒權將要到期 |
| 發生該異常時App需要重新擷取最新的鑒權資訊後,再調用 | 鑒權到期錯誤在兩種情況下出現:使用者調用API或程式執行期間。因此,錯誤反饋將通過API回調或通過獨立的錯誤回調通知。 |
鑒權到期 |
| 發生該異常時App需要重新入會。 | 鑒權到期錯誤在兩種情況下出現:使用者調用API或程式執行期間。因此,錯誤反饋將通過API回調或通過獨立的錯誤回調通知。 |
網路連接異常 |
| 發生該異常時APP需要重新入會。 | SDK具備一定時間斷網自動回復能力,但若斷線時間超出預設閾值,會觸發逾時並中斷連線。此時,App應檢查網路狀態並指導使用者重新加入會議。 |
被踢下線 |
|
| RTC服務提供了管理員可以主動移除參與者的功能。 |
本地裝置異常 |
| 發生該異常時App需要檢測許可權、裝置硬體是否正常。 | RTC服務支援裝置檢測和異常診斷的能力;當本地裝置發生異常時,RTC服務會通過回調的方式通知客戶本地裝置異常,此時,若SDK無法自行解決問題,則App需要介入以查看裝置是否正常。 |
extension VideoCallMainVC: AliRtcEngineDelegate {
func onJoinChannelResult(_ result: Int32, channel: String, elapsed: Int32) {
"onJoinChannelResult1 result: \(result)".printLog()
}
func onJoinChannelResult(_ result: Int32, channel: String, userId: String, elapsed: Int32) {
"onJoinChannelResult2 result: \(result)".printLog()
}
func onRemoteUser(onLineNotify uid: String, elapsed: Int32) {
// 遠端使用者的上線
"onRemoteUserOlineNotify uid: \(uid)".printLog()
}
func onRemoteUserOffLineNotify(_ uid: String, offlineReason reason: AliRtcUserOfflineReason) {
// 遠端使用者的下線
"onRemoteUserOffLineNotify uid: \(uid) reason: \(reason)".printLog()
}
func onRemoteTrackAvailableNotify(_ uid: String, audioTrack: AliRtcAudioTrack, videoTrack: AliRtcVideoTrack) {
"onRemoteTrackAvailableNotify uid: \(uid) audioTrack: \(audioTrack) videoTrack: \(videoTrack)".printLog()
}
func onAuthInfoWillExpire() {
"onAuthInfoWillExpire".printLog()
/* TODO: 務必處理;Token即將到期,需要業務觸發重新擷取當前channel,user的鑒權資訊,然後設定refreshAuthInfo即可 */
}
func onAuthInfoExpired() {
"onAuthInfoExpired".printLog()
/* TODO: 務必處理;提示Token失效,並執行離會與釋放引擎 */
}
func onBye(_ code: Int32) {
"onBye code: \(code)".printLog()
/* TODO: 務必處理;業務可能會觸發同一個UserID的不同裝置搶佔的情況 */
}
func onLocalDeviceException(_ deviceType: AliRtcLocalDeviceType, exceptionType: AliRtcLocalDeviceExceptionType, message msg: String?) {
"onLocalDeviceException deviceType: \(deviceType) exceptionType: \(exceptionType)".printLog()
/* TODO: 務必處理;建議業務提示裝置錯誤,此時SDK內部已經嘗試了各種恢複策略已經無法繼續使用時才會上報 */
}
func onConnectionStatusChange(_ status: AliRtcConnectionStatus, reason: AliRtcConnectionStatusChangeReason) {
"onConnectionStatusChange status: \(status) reason: \(reason)".printLog()
if status == .failed {
/* TODO: 務必處理;建議業務提示使用者,此時SDK內部已經嘗試了各種恢複策略已經無法繼續使用時才會上報 */
}
else {
/* TODO: 可選處理;增加業務代碼,一般用於資料統計、UI變化 */
}
}
}4. 設定推拉流屬性
SDK 預設情況下會自動推送和拉取頻道內的音視頻流
設定為觀眾模式後只能拉流,publishLocalAudioStream 無效
對於主播和觀眾均可以設定為下面的配置
// 設定音頻Profile,預設使用高音質模式AliRtcEngineHighQualityMode及音樂模式AliRtcSceneMusicMode
engine.setAudioProfile(AliRtcAudioProfile.engineHighQualityMode, audio_scene: AliRtcAudioScenario.sceneMusicMode)
// 語聊情境,不需要publish視頻
engine.publishLocalVideoStream(false)
// 設定預設訂閱遠端的音頻
engine.setDefaultSubscribeAllRemoteAudioStreams(true)
engine.subscribeAllRemoteAudioStreams(true)5. 加入頻道開始純音頻互動
調用joinChannel介面加入頻道。
注意:
如果token是單參數規則產生的,需要調用SDK單參數的joinChannel[1/3]介面,如果是多參數規則產生的,需要調用SDK多參數的joinChannel[2/3]介面。調用完加入頻道後,可以在onJoinChannelResult回調中拿到加入頻道結果,如果result為0,則表示加入頻道成功,否則需要檢查傳進來的Token是否非法。
self.rtcEngine?.joinChannel(joinToken, channelId: nil, userId: nil, name: nil) 6. 結束純音頻互動
音頻互動結束,需要離開房間並銷毀引擎,按照下列步驟結束音視頻互動
調用
leaveChannel離會。調用
destroy銷毀引擎,並釋放相關資源。
self.rtcEngine?.leaveChannel()
AliRtcEngine.destroy()
self.rtcEngine = nil7. (可選)觀眾上下麥
業務情境中,如果觀眾角色的使用者想要推流,需要調用setClientRole將觀眾角色切換為主播角色。
// 切換為主播角色
self.rtcEngine?.setClientRole(AliRtcClientRole.roleInteractive)
// 切換為觀眾角色
self.rtcEngine?.setClientRole(AliRtcClientRole.rolelive)相關文檔
有關音訊更多操作,例如耳返、音量和說話人回調等,請參考音頻常用操作和配置。
設定人聲效果,例如變聲、美聲、混響等,請參考設定變聲、混響、美聲。
如果需要播放背景音樂、伴奏音樂檔案等,請參考播放與推流外部輸入音頻(包括音效、伴奏)。