Développez des applications interactives audio uniquement, telles que les appels vocaux et les salons de discussion vocale, en intégrant le SDK ARTC à votre projet iOS.
Concepts clés
| Terme | Définition |
|---|---|
| SDK ARTC | Le SDK d'ApsaraVideo Real-time Communication (ARTC). Il ajoute des fonctionnalités audio et vidéo en temps réel à votre application. |
| Canal | Un espace virtuel où les utilisateurs interagissent en temps réel. |
| Animateur | Utilisateur pouvant publier et s'abonner aux flux audio et vidéo dans un canal. Correspond à AliRtcClientRole.roleInteractive dans le SDK. |
| Spectateur | Utilisateur pouvant s'abonner aux flux audio et vidéo dans un canal, mais ne pouvant pas publier de flux. Correspond à AliRtcClientRole.rolelive dans le SDK. |
Interaction entre canaux, animateurs et spectateurs
Tous les utilisateurs appellent joinChannel pour rejoindre un canal avant de publier ou de s'abonner aux flux.
| Scénario | Rôles | Comportement |
|---|---|---|
| Appel audio uniquement | Tous les utilisateurs sont des animateurs | Chaque utilisateur peut publier et s'abonner aux flux. |
| Salon de discussion vocale | Les animateurs publient des flux ; les spectateurs s'abonnent uniquement | Appelez setClientRole pour attribuer les rôles. |
Après avoir rejoint un canal :
Tous les utilisateurs peuvent recevoir les flux audio et vidéo des autres utilisateurs du même canal.
Seuls les animateurs peuvent publier des flux audio et vidéo dans le canal.
Pour permettre à un spectateur de publier un flux, appelez
setClientRoleafin de basculer son rôle vers celui d'animateur.
Exemple de projet
Téléchargez ou consultez le code source de l'exemple depuis le projet open source du SDK ARTC sur GitHub.
Prérequis
Avant de commencer, assurez-vous de disposer des éléments suivants :
Xcode 14.0 ou version ultérieure installé. Utilisez la dernière version officielle.
CocoaPods 1.9.3 ou version ultérieure installé.
Un appareil de test physique exécutant iOS 9.0 ou version ultérieure.
Une connexion réseau stable.
Un AppID et une AppKey ARTC. Pour plus d'informations, consultez la rubrique Créer une application.
Un projet configuré avec le SDK ARTC, incluant les autorisations audio et réseau. Pour plus d'informations, consultez la rubrique Implémenter un appel audio et vidéo.
Utilisez un appareil physique pour les tests. Les simulateurs peuvent ne pas prendre en charge certaines fonctionnalités.
Implémenter l'interaction audio uniquement
1. Demander les autorisations
Demandez les autorisations d'accès à la caméra et au microphone avant de démarrer un appel. Le SDK vérifie automatiquement les autorisations, mais solliciter l'utilisateur dès le début offre une expérience plus fluide.
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)
}
}
// Usage example
checkMicrophonePermission { granted in
if granted {
print("Microphone access granted.")
} else {
print("Microphone access denied.")
}
}
checkCameraPermission { granted in
if granted {
print("Camera access granted.")
} else {
print("Camera access denied.")
}
}
2. Obtenir un jeton d'authentification
La jointure d'un canal ARTC nécessite un jeton d'authentification. Les jetons peuvent être générés via une méthode à paramètre unique ou à paramètres multiples, ce qui détermine l'API joinChannel à appeler. Pour plus de détails, consultez la rubrique Implémenter l'authentification basée sur les jetons.
Environnements de production
Générez le jeton sur votre serveur et envoyez-le au client. Coder en dur la AppKey côté client présente un risque de sécurité.
Développement et débogage
Si votre serveur ne génère pas encore de jetons, vous pouvez utiliser temporairement la logique de génération de jetons issue de l'APIExample :
class ARTCTokenHelper: NSObject {
/**
* RTC AppId
*/
public static let AppId = "<RTC AppId>"
/**
* RTC AppKey
*/
public static let AppKey = "<RTC AppKey>"
/**
* Generate a multi-parameter token for joining a channel 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
}
/**
* Generate a single-parameter token for joining a channel 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 ""
}
/**
* Sign a string using SHA256.
* String signing (SHA256)
*/
private static func GetSHA256(_ input: String) -> String {
// Convert input string to data.
let data = Data(input.utf8)
// Create a buffer to store the hash result.
var hash = [UInt8](repeating: 0, count: Int(CC_SHA256_DIGEST_LENGTH))
// Calculate the SHA-256 hash.
data.withUnsafeBytes {
_ = CC_SHA256($0.baseAddress, CC_LONG(data.count), &hash)
}
// Convert the hash to a hexadecimal string.
return hash.map { String(format: "%02hhx", $0) }.joined()
}
}
3. Créer et initialiser le moteur
Créer le moteur RTC
Appelez sharedInstance pour créer un objet moteur RTC.
private var rtcEngine: AliRtcEngine? = nil
// Create the engine and set the callback.
let engine = AliRtcEngine.sharedInstance(self, extras:nil)
self.rtcEngine = engine
Initialiser le moteur
Configurez le profil du canal, le rôle de l'utilisateur et les paramètres audio :
| Méthode | Objectif | Valeur |
|---|---|---|
setChannelProfile |
Définir le profil du canal sur le streaming live interactif | AliRtcChannelProfile.interactivelive |
setClientRole |
Attribuer le rôle de l'utilisateur | Animateur : AliRtcClientRole.roleInteractive ; Spectateur : AliRtcClientRole.rolelive |
setAudioProfile |
Définir la qualité audio et le mode de scénario | AliRtcAudioProfile.engineHighQualityMode, AliRtcAudioScenario.sceneMusicMode |
// Set the channel profile to interactive live streaming. AliRtcInteractivelive is used for all RTC scenarios.
engine.setChannelProfile(AliRtcChannelProfile.interactivelive)
// Set the role.
if self.isAnchor {
// For streamer mode, which requires publishing audio and video streams, set the role to AliRtcClientRoleInteractive.
engine.setClientRole(AliRtcClientRole.roleInteractive)
}
else {
// For viewer mode, which does not require publishing audio and video streams, set the role to AliRtcClientRolelive.
engine.setClientRole(AliRtcClientRole.rolelive)
}
// Set the audio profile. By default, high-quality mode (AliRtcEngineHighQualityMode) and music mode (AliRtcSceneMusicMode) are used.
engine.setAudioProfile(AliRtcAudioProfile.engineHighQualityMode, audio_scene: AliRtcAudioScenario.sceneMusicMode)
4. Configurer la publication et l'abonnement
Par défaut, le SDK publie et s'abonne automatiquement aux flux audio et vidéo dans le canal.
Une fois le rôle défini sur spectateur, la méthode
publishLocalAudioStreamdevient invalide.La configuration suivante s'applique aussi bien aux animateurs qu'aux spectateurs.
// Set the audio profile. By default, high-quality mode (AliRtcEngineHighQualityMode) and music mode (AliRtcAudioScenario.sceneMusicMode) are used.
engine.setAudioProfile(AliRtcAudioProfile.engineHighQualityMode, audio_scene: AliRtcAudioScenario.sceneMusicMode)
// For a voice chat scenario, you do not need to publish a video stream.
engine.publishLocalVideoStream(false)
// Set the default subscription to remote audio streams.
engine.setDefaultSubscribeAllRemoteAudioStreams(true)
engine.subscribeAllRemoteAudioStreams(true)
5. Rejoindre un canal
Appelez joinChannel pour rejoindre un canal et démarrer l'interaction audio uniquement.
Si le jeton a été généré via la méthode à paramètre unique, appelez la méthode [joinChannel[1/3]](t2309760.xdita#758b964acc7jd) à paramètre unique. Si le jeton a été généré via la méthode à paramètres multiples, appelez la méthode [joinChannel[2/3]](t2309760.xdita#766e40a1cfk4p) à paramètres multiples. Le rappel onJoinChannelResult renvoie le résultat. Une valeur de 0 indique un succès. Toute autre valeur indique un échec — vérifiez la validité du jeton.
self.rtcEngine?.joinChannel(joinToken, channelId: nil, userId: nil, name: nil)
6. Mettre fin à l'interaction
Quittez le canal et détruisez le moteur pour libérer les ressources :
Appelez
leaveChannelpour quitter le canal.Appelez
destroypour détruire le moteur et libérer les ressources.
self.rtcEngine?.leaveChannel()
AliRtcEngine.destroy()
self.rtcEngine = nil
7. (Facultatif) Basculer entre les rôles de spectateur et d'animateur
Appelez setClientRole pour faire passer un spectateur au rôle d'animateur (pour publier des flux) ou un animateur au rôle de spectateur (pour arrêter la publication).
// Switch to the streamer role.
self.rtcEngine?.setClientRole(AliRtcClientRole.roleInteractive)
// Switch to the viewer role.
self.rtcEngine?.setClientRole(AliRtcClientRole.rolelive)
Gérer les rappels du moteur
Le SDK récupère automatiquement la plupart des exceptions. Pour les erreurs qu'il ne peut pas résoudre, le SDK notifie votre application via des rappels.
| Cause | Rappel et paramètres | Solution |
|---|---|---|
| Échec de l'authentification | onJoinChannelResult renvoie AliRtcErrJoinBadToken |
Vérifiez si le jeton est correct. |
| Le jeton est sur le point d'expirer | onWillAuthInfoExpire |
Obtenez les dernières informations d'authentification, puis appelez refreshAuthInfo. |
| Le jeton a expiré | onAuthInfoExpired |
Invitez l'utilisateur à rejoindre à nouveau le canal. |
| Erreur de connectivité réseau | onConnectionStatusChange renvoie AliRtcConnectionStatusFailed |
Le SDK peut récupérer automatiquement après de brèves déconnexions réseau. Si la déconnexion dépasse le seuil de délai d'attente, vérifiez l'état du réseau et invitez l'utilisateur à rejoindre à nouveau le canal. |
| Déconnecté de force | onBye |
AliRtcOnByeUserReplaced : Vérifiez si l'ID utilisateur est dupliqué. AliRtcOnByeBeKickedOut : L'utilisateur a été expulsé par le service. Rejoignez à nouveau le canal. AliRtcOnByeChannelTerminated : Le canal a été terminé. Rejoignez à nouveau le canal. |
| Exception de l'appareil local | onLocalDeviceException |
Vérifiez les autorisations et l'état matériel de l'appareil. |
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) {
// A remote user comes online.
"onRemoteUserOlineNotify uid: \(uid)".printLog()
}
func onRemoteUserOffLineNotify(_ uid: String, offlineReason reason: AliRtcUserOfflineReason) {
// A remote user goes offline.
"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: You must handle this callback. The token is about to expire. Your application must get a new token for the current channel and user, and then call refreshAuthInfo. */
}
func onAuthInfoExpired() {
"onAuthInfoExpired".printLog()
/* TODO: You must handle this callback. Prompt the user that the token has expired, and then leave the channel and release the engine. */
}
func onBye(_ code: Int32) {
"onBye code: \(code)".printLog()
/* TODO: You must handle this callback. Your business may trigger a scenario where different devices with the same user ID compete for access. */
}
func onLocalDeviceException(_ deviceType: AliRtcLocalDeviceType, exceptionType: AliRtcLocalDeviceExceptionType, message msg: String?) {
"onLocalDeviceException deviceType: \(deviceType) exceptionType: \(exceptionType)".printLog()
/* TODO: You must handle this callback. We recommend that your application prompts the user about the device error. This callback is triggered only after the SDK has tried all recovery policies and still cannot continue. */
}
func onConnectionStatusChange(_ status: AliRtcConnectionStatus, reason: AliRtcConnectionStatusChangeReason) {
"onConnectionStatusChange status: \(status) reason: \(reason)".printLog()
if status == .failed {
/* TODO: You must handle this callback. We recommend that your application prompts the user. This callback is triggered only after the SDK has tried all recovery policies and still cannot continue. */
}
else {
/* TODO: Optional. Add business logic, usually for data statistics or UI changes. */
}
}
}
Opérations connexes
Opérations et configurations audio courantes — monitoring intra-auriculaire, contrôle du volume et rappels du haut-parleur.
Configurer les modulateurs de voix, la réverbération et l'embellissement — effets vocaux tels que modulateurs de voix et réverbération.
Lire et ingérer des entrées audio externes (y compris les effets sonores et l'accompagnement) — musique de fond et fichiers audio d'accompagnement.