315 lines
11 KiB
Swift
315 lines
11 KiB
Swift
//
|
|
// Agora.swift
|
|
// SodaLive
|
|
//
|
|
// Created by klaus on 2023/08/14.
|
|
//
|
|
|
|
import Foundation
|
|
import AgoraRtcKit
|
|
import AgoraRtmKit
|
|
|
|
final class Agora {
|
|
static let shared = Agora()
|
|
|
|
func initialize() {
|
|
initRtcEngine()
|
|
initRtmClient()
|
|
}
|
|
|
|
func deInit() {
|
|
deInitRtcEngine()
|
|
deInitRtmClient()
|
|
}
|
|
|
|
// MARK: RTC
|
|
private var rtcEngine: AgoraRtcEngineKit?
|
|
var rtcEngineDelegate: AgoraRtcEngineDelegate?
|
|
|
|
func initRtcEngine() {
|
|
rtcEngine = AgoraRtcEngineKit.sharedEngine(withAppId: AGORA_APP_ID, delegate: rtcEngineDelegate)
|
|
rtcEngine?.setChannelProfile(.liveBroadcasting)
|
|
rtcEngine?.enableAudio()
|
|
rtcEngine?.enableAudioVolumeIndication(500, smooth: 3, reportVad: true)
|
|
}
|
|
|
|
func deInitRtcEngine() {
|
|
if let rtcEngine = rtcEngine {
|
|
rtcEngine.leaveChannel(nil)
|
|
|
|
DispatchQueue.global(qos: .userInitiated).async {
|
|
AgoraRtcEngineKit.destroy()
|
|
}
|
|
}
|
|
rtcEngine = nil
|
|
}
|
|
|
|
func joinRtcChannel(rtcToken: String, channelName: String) {
|
|
let userId = UserDefaults.int(forKey: .userId)
|
|
|
|
rtcEngine?.joinChannel(
|
|
byToken: rtcToken,
|
|
channelId: channelName,
|
|
info: nil,
|
|
uid: UInt(userId),
|
|
joinSuccess: nil
|
|
)
|
|
|
|
rtcEngine?.setAudioProfile(.musicHighQualityStereo)
|
|
rtcEngine?.setAudioScenario(.gameStreaming)
|
|
}
|
|
|
|
func setRole(role: AgoraClientRole) {
|
|
self.rtcEngine?.setClientRole(role)
|
|
}
|
|
|
|
func mute(_ isMute: Bool) {
|
|
rtcEngine?.muteLocalAudioStream(isMute)
|
|
}
|
|
|
|
func speakerMute(_ isMute: Bool) {
|
|
rtcEngine?.muteAllRemoteAudioStreams(isMute)
|
|
}
|
|
|
|
func getRtcConnectionState() -> AgoraConnectionState {
|
|
return rtcEngine!.getConnectionState()
|
|
}
|
|
|
|
// MARK: RTM
|
|
private var rtmKit: AgoraRtmClientKit?
|
|
private var roomChannelName: String? = nil
|
|
|
|
// 상태 플래그: RTM 로그인 완료 여부
|
|
private var rtmLoggedIn: Bool = false
|
|
|
|
// 상태 플래그: RTM 로그인 진행 중 여부
|
|
private var rtmLoginInProgress: Bool = false
|
|
|
|
var rtmClientDelegate: AgoraRtmClientDelegate?
|
|
|
|
func initRtmClient() {
|
|
if rtmKit != nil {
|
|
rtmKit?.logout()
|
|
rtmKit?.destroy()
|
|
rtmKit = nil
|
|
}
|
|
let userId = UserDefaults.int(forKey: .userId)
|
|
let config = AgoraRtmClientConfig(appId: AGORA_APP_ID, userId: String(userId))
|
|
rtmKit = try? AgoraRtmClientKit(config, delegate: rtmClientDelegate)
|
|
}
|
|
|
|
func deInitRtmClient() {
|
|
let userId = UserDefaults.int(forKey: .userId)
|
|
let group = DispatchGroup()
|
|
|
|
if let channel = roomChannelName {
|
|
group.enter()
|
|
rtmKit?.unsubscribe(channel) { [weak self] _, error in
|
|
if let error = error {
|
|
DEBUG_LOG("RTM unsubscribe fail - \(error.operation)")
|
|
DEBUG_LOG("RTM unsubscribe fail - \(error.errorCode)")
|
|
DEBUG_LOG("RTM unsubscribe fail - \(error.reason)")
|
|
} else {
|
|
DEBUG_LOG("RTM unsubscribe - \(channel)")
|
|
self?.roomChannelName = nil
|
|
}
|
|
group.leave()
|
|
}
|
|
}
|
|
|
|
group.enter()
|
|
rtmKit?.unsubscribe("inbox_\(userId)") { _, error in
|
|
if let error = error {
|
|
DEBUG_LOG("RTM unsubscribe fail - \(error.operation)")
|
|
DEBUG_LOG("RTM unsubscribe fail - \(error.errorCode)")
|
|
DEBUG_LOG("RTM unsubscribe fail - \(error.reason)")
|
|
} else {
|
|
DEBUG_LOG("RTM unsubscribe - inbox_\(userId)")
|
|
}
|
|
group.leave()
|
|
}
|
|
|
|
group.notify(queue: .global(qos: .userInitiated)) { [weak self] in
|
|
guard let self = self else { return }
|
|
self.rtmKit?.logout()
|
|
self.rtmKit?.destroy()
|
|
self.rtmKit = nil
|
|
self.rtmLoggedIn = false
|
|
self.rtmLoginInProgress = false
|
|
}
|
|
}
|
|
|
|
func rtmLogin(
|
|
creatorId: Int,
|
|
rtmToken: String,
|
|
channelName: String,
|
|
onConnectSuccess: @escaping (Bool) -> Void,
|
|
onConnectFail: @escaping () -> Void
|
|
) {
|
|
if rtmLoggedIn && roomChannelName == channelName {
|
|
DEBUG_LOG("rtmLogin - already logged in and subscribed. skip")
|
|
return
|
|
}
|
|
|
|
// 로그인 시도 중이면 재호출 방지
|
|
if (rtmLoginInProgress) {
|
|
DEBUG_LOG("rtmLogin - already in progress. skip")
|
|
return
|
|
}
|
|
|
|
roomChannelName = channelName
|
|
|
|
func attemptLogin(_ attempt: Int) {
|
|
rtmKit?.login(rtmToken) { [weak self] response, error in
|
|
if let error = error {
|
|
DEBUG_LOG("rtmClient login - fail (attempt=\(attempt)), \(error.reason)")
|
|
if attempt < 4 {
|
|
|
|
} else {
|
|
self?.rtmLoginInProgress = false
|
|
onConnectFail()
|
|
}
|
|
} else {
|
|
DEBUG_LOG("rtmClient login - success (attempt=\(attempt))")
|
|
// 로그인 성공 후 두 채널 구독 시도
|
|
self?.subscribeChannel(
|
|
creatorId: creatorId,
|
|
onConnectSuccess: onConnectSuccess,
|
|
onConnectFail: onConnectFail
|
|
)
|
|
}
|
|
}
|
|
}
|
|
|
|
rtmLoginInProgress = true
|
|
attemptLogin(1)
|
|
}
|
|
|
|
private func subscribeChannel(
|
|
creatorId: Int,
|
|
onConnectSuccess: @escaping (Bool) -> Void,
|
|
onConnectFail: @escaping () -> Void
|
|
) {
|
|
let targetRoom = roomChannelName
|
|
if (targetRoom == nil) {
|
|
DEBUG_LOG("subscribeChannel - roomChannelName is nil")
|
|
onConnectFail()
|
|
return
|
|
}
|
|
|
|
var completed = false
|
|
var roomSubscribed = false
|
|
var inboxSubscribed = false
|
|
let userId = UserDefaults.int(forKey: .userId)
|
|
|
|
func completeSuccessIfReady() {
|
|
if (!completed && roomSubscribed && inboxSubscribed) {
|
|
completed = true
|
|
rtmLoggedIn = true
|
|
rtmLoginInProgress = false
|
|
DEBUG_LOG("RTM subscribe - both channels subscribed")
|
|
if userId == creatorId {
|
|
self.setRole(role: .broadcaster)
|
|
} else {
|
|
self.setRole(role: .audience)
|
|
}
|
|
onConnectSuccess(userId == creatorId)
|
|
}
|
|
}
|
|
|
|
func failOnce(_ reason: String?) {
|
|
if (!completed) {
|
|
completed = true
|
|
if let reason = reason {
|
|
DEBUG_LOG("RTM subscribe failed: \(reason)")
|
|
} else {
|
|
DEBUG_LOG("RTM subscribe failed: nil")
|
|
}
|
|
onConnectFail()
|
|
}
|
|
}
|
|
|
|
func subscribeRoom(_ attempt: Int) {
|
|
DEBUG_LOG("RTM subscribe(room: \(targetRoom!)) attempt=\(attempt)")
|
|
rtmKit?.subscribe(channelName: targetRoom!, option: nil) { _, error in
|
|
if error != nil {
|
|
DEBUG_LOG("RTM subscribe(room) failure at attempt=\(attempt) operation=\(error!.operation) reason=\(error!.reason) code=\(error!.errorCode)")
|
|
if (attempt < 4) {
|
|
subscribeRoom(attempt + 1)
|
|
} else {
|
|
failOnce("room subscribe failed after 3 retries (4 attempts)")
|
|
}
|
|
} else {
|
|
DEBUG_LOG("RTM subscribe(room) success at attempt=\(attempt)")
|
|
roomSubscribed = true
|
|
completeSuccessIfReady()
|
|
}
|
|
}
|
|
}
|
|
|
|
func subscribeInbox(_ attempt: Int) {
|
|
let inboxChannel = "inbox_\(userId)"
|
|
DEBUG_LOG("RTM subscribe(inbox: \(inboxChannel)) attempt=\(attempt)")
|
|
rtmKit?.subscribe(channelName: inboxChannel, option: nil) { _, error in
|
|
if error != nil {
|
|
DEBUG_LOG("RTM subscribe(inbox) failure at attempt=\(attempt) operation=\(error!.operation) reason=\(error!.reason) code=\(error!.errorCode)")
|
|
if (attempt < 4) {
|
|
subscribeInbox(attempt + 1)
|
|
} else {
|
|
failOnce("room subscribe failed after 3 retries (4 attempts)")
|
|
}
|
|
} else {
|
|
DEBUG_LOG("RTM subscribe(inbox) success at attempt=\(attempt)")
|
|
inboxSubscribed = true
|
|
completeSuccessIfReady()
|
|
}
|
|
}
|
|
}
|
|
|
|
// 두 채널 구독을 병렬로 시도
|
|
subscribeRoom(1)
|
|
subscribeInbox(1)
|
|
}
|
|
|
|
func sendMessageToPeer(peerId: String, rawMessage: Data, completion: AgoraRtmOperationBlock?) {
|
|
rtmKit?.publish(channelName: "inbox_\(peerId)", data: rawMessage, option: nil, completion: completion)
|
|
}
|
|
|
|
func sendRawMessageToPeer(peerId: String, rawMessage: LiveRoomChatRawMessage, completion: AgoraRtmOperationBlock? = nil, fail: (() -> Void)? = nil) {
|
|
let encoder = JSONEncoder()
|
|
let jsonMessageData = try? encoder.encode(rawMessage)
|
|
|
|
if let jsonMessageData = jsonMessageData {
|
|
rtmKit?.publish(channelName: "inbox_\(peerId)", data: jsonMessageData, option: nil, completion: completion)
|
|
} else {
|
|
if let fail = fail {
|
|
fail()
|
|
}
|
|
}
|
|
}
|
|
|
|
func sendMessageToGroup(textMessage: String, completion: @escaping AgoraRtmOperationBlock) {
|
|
guard !textMessage.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty else { return }
|
|
guard let channelName = roomChannelName else { return }
|
|
|
|
rtmKit?.publish(channelName: channelName, message: textMessage, option: nil, completion: completion)
|
|
}
|
|
|
|
func sendRawMessageToGroup(rawMessage: LiveRoomChatRawMessage, completion: AgoraRtmOperationBlock? = nil, fail: (() -> Void)? = nil) {
|
|
let encoder = JSONEncoder()
|
|
let jsonMessageData = try? encoder.encode(rawMessage)
|
|
|
|
if let jsonMessageData = jsonMessageData, let channelName = roomChannelName {
|
|
rtmKit?.publish(channelName: channelName, data: jsonMessageData, option: nil, completion: completion)
|
|
} else {
|
|
if let fail = fail {
|
|
fail()
|
|
}
|
|
}
|
|
}
|
|
|
|
func isRtmLoggedIn() -> Bool {
|
|
return rtmLoggedIn
|
|
}
|
|
}
|