Este guia demonstra como integrar o ARTC SDK ao seu projeto iOS para criar uma aplicação de áudio e vídeo em tempo real, ideal para casos de uso como transmissões ao vivo interativas e chamadas de vídeo.
Conceitos principais
Antes de começar, revise os seguintes conceitos fundamentais:
ARTC SDK: SDK da Alibaba Cloud para interações de áudio e vídeo em tempo real.
GRTN: Rede Global de Transporte em Tempo Real da Alibaba Cloud, que oferece serviços de comunicação de áudio e vídeo com latência ultrabaixa, alta qualidade, segurança e confiabilidade.
canal: Sala virtual destinada a interações de áudio e vídeo em tempo real.
host: Função que permite ao usuário publicar fluxos de áudio e vídeo em um canal, além de assinar fluxos publicados por outros hosts.
viewer: Função que habilita o usuário a assinar fluxos de áudio e vídeo em um canal, sem permissão para publicar fluxos.
-
Chame
setChannelProfilepara definir o cenário do canal e, em seguida, chame joinChannel para entrar em um canal:No cenário de chamada de vídeo, todos os usuários assumem a função de host e podem publicar e assinar fluxos.
No cenário de transmissão interativa, é necessário chamar
setClientRolepara configurar a função do usuário. Defina a função como host para os usuários que irão publicar um fluxo. Caso o usuário precise apenas assinar um fluxo, defina sua função como viewer.
-
Após entrar em um canal, a função do usuário determina se ele pode publicar ou assinar fluxos:
Todos os participantes de um canal podem assinar seus fluxos de áudio e vídeo.
Um host tem permissão para publicar fluxos de áudio e vídeo no canal.
Se um viewer precisar publicar um fluxo, deverá chamar o método
setClientRolepara alterar sua função para host.
Projeto de exemplo
O ARTC SDK fornece um projeto de exemplo open-source para aplicações de áudio e vídeo em tempo real.
Pré-requisitos
Antes de executar o projeto de exemplo, certifique-se de que seu ambiente de desenvolvimento atenda aos seguintes requisitos:
Ferramenta de desenvolvimento: Xcode 14.0 ou superior. Recomenda-se utilizar a versão estável mais recente.
Configuração recomendada: CocoaPods 1.9.3 ou superior.
Dispositivo de teste: Um dispositivo físico iOS executando iOS 9.0 ou posterior.
Utilize um dispositivo físico para testes, pois os simuladores podem não oferecer suporte a determinados recursos.
Rede: É necessária uma conexão de rede estável.
Configuração da aplicação: Obtenha o AppID e a AppKey da sua aplicação ApsaraVideo Real-time Communication. Para mais informações, consulte Criar aplicação.
Criar um projeto (opcional)
Esta seção descreve como criar um novo projeto e adicionar as permissões necessárias para interação de áudio e vídeo. Caso já possua um projeto, ignore esta etapa.
Abra o Xcode, acesse File > New > Project e selecione o modelo App. Na tela seguinte, defina Interface como Storyboard e Language como Swift.

Configure as definições do seu projeto, incluindo
Bundle Identifier,SigningeMinimum Deployments.
Configurar o projeto
Etapa 3: Criar uma interface de usuário
Desenvolva uma interface de usuário adequada ao seu cenário de interação em tempo real. Para este exemplo de chamada de vídeo com múltiplos participantes, crie uma ScrollView. Quando um usuário entrar na chamada, adicione uma visualização de vídeo a esse contêiner. Ao sair, remova a respectiva visualização e atualize o layout.
Implementação
Esta seção explica como utilizar o ARTC SDK para desenvolver uma aplicação básica de áudio e vídeo em tempo real. Copie o exemplo de código para testar rapidamente e siga as etapas para compreender as chamadas essenciais da API.
O diagrama abaixo ilustra o fluxo de trabalho básico de uma chamada de áudio e vídeo em tempo real:
Exemplo de código para o cenário de chamada de vídeo
Para obter detalhes sobre o código de exemplo completo, consulte Executar projeto de demonstração do ARTC para iOS.
1. Solicitar permissões
Embora o SDK verifique as permissões necessárias ao iniciar uma chamada, recomenda-se validar previamente o acesso à câmera e ao microfone para garantir uma experiência de usuário fluida.
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. Obter um token de autenticação
A entrada em um canal ARTC exige um token de autenticação para validar a identidade do usuário. Para detalhes sobre a geração do token, consulte Implementar autenticação baseada em token. A criação do token pode ocorrer via método de parâmetro único ou multiparâmetro, e a escolha define qual API joinChannel deve ser invocada.
Para ambientes de produção:
Como a geração do token requer sua AppKey, codificá-la diretamente no lado do cliente representa um risco de segurança. Em produção, recomendamos fortemente gerar o token no servidor e enviá-lo ao cliente.
Para desenvolvimento e depuração:
Durante o desenvolvimento, caso seu servidor de negócios ainda não possua a lógica de geração de tokens, utilize temporariamente a lógica disponível no APIExample para criar um token provisório. O código de referência é o seguinte:
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. Importar o módulo ARTC SDK
// Import the ARTC module.
import AliVCSDK_ARTC
4. Criar e inicializar o mecanismo
-
Criar o mecanismo RTC
Invoque o método sharedInstance para instanciar um objeto
AliRtcEngine.private var rtcEngine: AliRtcEngine? = nil // Create the engine and set the delegate. let engine = AliRtcEngine.sharedInstance(self, extras:nil) ... self.rtcEngine = engine -
Inicializar o mecanismo
-
Execute
setChannelProfilepara configurar o canal comoAliRTCInteractiveLive(modo interativo).Conforme suas necessidades de negócio, escolha o modo interativo (adequado para cenários de entretenimento) ou o modo de comunicação (ideal para transmissões um-para-um ou um-para-muitos). A seleção correta garante melhor experiência do usuário e uso eficiente dos recursos de rede.
Modo
Publicação
Assinatura
Descrição
Modo interativo
Restrito por função. Apenas usuários com a função host podem publicar fluxos.
Os participantes podem alternar funções flexivelmente durante a sessão.
Sem restrições de função. Todos os participantes têm permissão para assinar fluxos.
No modo interativo, eventos como entrada ou saída de um host do canal, ou início de publicação de fluxo, são notificados aos viewers em tempo real. Por outro lado, as atividades dos viewers não são notificadas aos hosts, assegurando a continuidade da transmissão.
Neste modo, os hosts gerenciam a interação, enquanto os viewers apenas consomem conteúdo. Se houver possibilidade de mudança nos requisitos de negócio, considere adotar o modo interativo como padrão. Sua flexibilidade permite adaptar-se a diferentes demandas de interação ajustando as funções dos usuários.
Modo de comunicação
Sem restrições de função. Todos os participantes têm permissão para publicar fluxos.
Sem restrições de função. Todos os participantes têm permissão para assinar fluxos.
No modo de comunicação, os participantes têm ciência da presença uns dos outros.
Embora este modo não diferencie funções de usuário, ele equivale funcionalmente à função host do modo interativo. O objetivo é simplificar as operações, permitindo alcançar a funcionalidade desejada com menos chamadas de API.
-
Utilize
setClientRolepara definir a função do usuário comoAliRTCSdkInteractive(host) ouAliRTCSdkLive(viewer). A função host publica e assina fluxos por padrão, enquanto a função viewer apenas assina, tendo a pré-visualização local e a publicação desativadas inicialmente.Nota: Ao alternar de host para viewer, o sistema interrompe a publicação dos fluxos locais de áudio e vídeo, mas as assinaturas existentes permanecem ativas. Na transição inversa (viewer para host), o sistema inicia a publicação dos fluxos locais, sem impactar as assinaturas já estabelecidas.
// Set the channel profile to Interactive Mode. Use AliRtcInteractivelive for all RTC scenarios. engine.setChannelProfile(AliRtcChannelProfile.interactivelive) // Set the client role. Use AliRtcClientRoleInteractive for users who need to both publish and subscribe. Use AliRtcClientRolelive for users who only subscribe. engine.setClientRole(AliRtcClientRole.roleInteractive)
-
-
Implementar callbacks comuns
Caso o SDK enfrente problemas durante a operação, ele tentará primeiramente uma recuperação automática através de mecanismos internos de nova tentativa. Para erros insolúveis internamente, o SDK notificará sua aplicação por meio de interfaces de callback predefinidas.
Abaixo estão os principais callbacks para situações que o SDK não consegue resolver automaticamente, exigindo monitoramento e resposta da sua aplicação:
Causa da exceção
Callback e parâmetros
Solução
Descrição
Falha na autenticação
result em onJoinChannelResult retorna AliRtcErrJoinBadToken.
A aplicação deve verificar se o token está correto.
Quando um usuário chama uma API e a autenticação falha, o callback da API retornará um erro de falha de autenticação.
Token prestes a expirar
onAuthInfoWillExpire
Obtenha um novo token e chame refreshAuthInfo para atualizar as informações.
Um erro de expiração de token pode ocorrer tanto na chamada de uma API quanto durante a execução. O erro é reportado via callbacks da API ou um callback de erro específico.
Token expirado
onAuthInfoExpired
A aplicação precisa entrar novamente no canal.
Um erro de expiração de token pode ocorrer tanto na chamada de uma API quanto durante a execução. O erro é reportado via callbacks da API ou um callback de erro específico.
Problema de conexão de rede
O callback onConnectionStatusChange retorna AliRtcConnectionStatusFailed.
A aplicação precisa entrar novamente no canal.
O SDK consegue se recuperar automaticamente de breves desconexões de rede. Se o tempo de desconexão exceder um determinado limiar, ocorrerá timeout. A aplicação deve verificar o status da rede e orientar o usuário a reconectar.
Removido do canal
onBye
AliRtcOnByeUserReplaced: Verifique se outro usuário entrou com o mesmo userId.
AliRtcOnByeBeKickedOut: O usuário foi removido do canal e precisa entrar novamente.
AliRtcOnByeChannelTerminated: O canal foi encerrado e o usuário precisa entrar novamente.
O serviço RTC permite que um administrador remova participantes.
Exceção de dispositivo local
onLocalDeviceException
Verifique as permissões da aplicação e se o hardware está funcionando corretamente.
Quando ocorre uma exceção de dispositivo local que o SDK não consegue resolver, ele notifica a aplicação via callback. A aplicação deve então intervir para verificar o status do dispositivo.
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: Must handle. The token is about to expire. Your app needs to get a new token for the current channel and user, then call refreshAuthInfo. */ } func onAuthInfoExpired() { "onAuthInfoExpired".printLog() /* TODO: Must handle. Notify the user that the token has expired, then leave the channel and destroy the engine. */ } func onBye(_ code: Int32) { "onBye code: \(code)".printLog() /* TODO: Must handle. This callback is triggered if another device logs in with the same UserID, kicking the current device out of the channel. */ } func onLocalDeviceException(_ deviceType: AliRtcLocalDeviceType, exceptionType: AliRtcLocalDeviceExceptionType, message msg: String?) { "onLocalDeviceException deviceType: \(deviceType) exceptionType: \(exceptionType)".printLog() /* TODO: Must handle. We recommend notifying the user of a device error. This callback is triggered when the SDK's internal recovery strategies have failed.*/ } func onConnectionStatusChange(_ status: AliRtcConnectionStatus, reason: AliRtcConnectionStatusChangeReason) { "onConnectionStatusChange status: \(status) reason: \(reason)".printLog() if status == .failed { /* TODO: Must handle. We recommend notifying the user. This callback is triggered when the SDK's internal recovery strategies have failed. */ } else { /* TODO: Optional. You can add business logic here, typically for analytics or UI updates. */ } } }
5. Definir propriedades de áudio e vídeo
-
Configurar propriedades de áudio
Use
setAudioProfilepara estabelecer o modo de codificação de áudio e o cenário desejado.// Set the audio profile. The default is high-quality mode (AliRtcEngineHighQualityMode) and music scenario (AliRtcSceneMusicMode). engine.setAudioProfile(AliRtcAudioProfile.engineHighQualityMode, audio_scene: AliRtcAudioScenario.sceneMusicMode) -
Configurar propriedades de vídeo
Defina os atributos do fluxo de vídeo publicado, como resolução, taxa de bits e taxa de quadros.
// Set the video encoder configuration. let config = AliRtcVideoEncoderConfiguration() config.dimensions = CGSize(width: 720, height: 1280) config.frameRate = 20 config.bitrate = 1200 config.keyFrameInterval = 2000 config.orientationMode = AliRtcVideoEncoderOrientationMode.adaptive engine.setVideoEncoderConfiguration(config) engine.setCapturePipelineScaleMode(.post)
6. Definir propriedades de publicação e assinatura
Configure a publicação de fluxos de áudio/vídeo e estabeleça a assinatura automática de todos os fluxos dos usuários como padrão:
Invoque
publishLocalAudioStreampara publicar um fluxo de áudio.Invoque
publishLocalVideoStreampara publicar um fluxo de vídeo. Para chamadas apenas de áudio, defina este valor como false.
// By default, the SDK publishes the audio stream. Calling publishLocalAudioStream(true) is optional.
engine.publishLocalVideoStream(true)
// By default, the SDK publishes the video stream. For a video call, calling publishLocalVideoStream(true) is optional.
// To stop publishing video for a voice-only call, call publishLocalVideoStream(false).
engine.publishLocalAudioStream(true)
// Set the default to subscribe to all remote audio and video streams.
engine.setDefaultSubscribeAllRemoteAudioStreams(true)
engine.subscribeAllRemoteAudioStreams(true)
engine.setDefaultSubscribeAllRemoteVideoStreams(true)
engine.subscribeAllRemoteVideoStreams(true)
Por padrão, o SDK publica automaticamente os fluxos locais de áudio e vídeo e assina os fluxos de áudio e vídeo de todos os outros usuários no canal. Utilize os métodos acima para sobrescrever esse comportamento padrão.
7. Iniciar pré-visualização local
Chame
setLocalViewConfigpara configurar a visualização de renderização local e ajustar as propriedades de exibição do vídeo local.Execute o método
startPreviewpara iniciar a pré-visualização do vídeo local.
let videoView = self.createVideoView(uid: self.userId)
let canvas = AliVideoCanvas()
canvas.view = videoView.canvasView
canvas.renderMode = .auto
canvas.mirrorMode = .onlyFrontCameraPreviewEnabled
canvas.rotationMode = ._0
self.rtcEngine?.setLocalViewConfig(canvas, for: AliRtcVideoTrack.camera)
self.rtcEngine?.startPreview()
8. Entrar em um canal
Chame joinChannel para acessar o canal. Se o token foi gerado pelo método de parâmetro único, utilize a operação [joinChannel[3/3]](t2309850.xdita#7ab2c22015zro). O resultado será retornado no callback onJoinChannelResult. Um valor 0 indica sucesso na entrada. Valores diferentes de zero podem indicar um token inválido.
let ret = self.rtcEngine?.joinChannel(joinToken, channelId: nil, userId: nil, name: nil) { [weak self] errCode, channelId, userId, elapsed in
if errCode == 0 {
// success
}
else {
// failed
}
let resultMsg = "\(msg) \n CallbackErrorCode: \(errCode)"
resultMsg.printLog()
UIAlertController.showAlertWithMainThread(msg: resultMsg, vc: self!)
}
let resultMsg = "\(msg) \n ReturnErrorCode: \(ret ?? 0)"
resultMsg.printLog()
if ret != 0 {
UIAlertController.showAlertWithMainThread(msg: resultMsg, vc: self)
}
Após entrar no canal, o SDK publicará e assinará fluxos conforme os parâmetros definidos anteriormente.
O SDK realiza publicação e assinatura automaticamente por padrão, reduzindo a quantidade de chamadas de API necessárias no cliente.
9. Configurar a visualização remota
Quando um usuário remoto inicia ou interrompe a publicação de um fluxo, o callback onRemoteTrackAvailableNotify é acionado. Nesse callback, configure ou remova a visualização do usuário remoto. Exemplo de código:
func onRemoteTrackAvailableNotify(_ uid: String, audioTrack: AliRtcAudioTrack, videoTrack: AliRtcVideoTrack) {
"onRemoteTrackAvailableNotify uid: \(uid) audioTrack: \(audioTrack) videoTrack: \(videoTrack)".printLog()
// Stream status of a remote user.
if audioTrack != .no {
let videoView = self.videoViewList.first { $0.uidLabel.text == uid }
if videoView == nil {
_ = self.createVideoView(uid: uid)
}
}
if videoTrack != .no {
var videoView = self.videoViewList.first { $0.uidLabel.text == uid }
if videoView == nil {
videoView = self.createVideoView(uid: uid)
}
let canvas = AliVideoCanvas()
canvas.view = videoView!.canvasView
canvas.renderMode = .auto
canvas.mirrorMode = .onlyFrontCameraPreviewEnabled
canvas.rotationMode = ._0
self.rtcEngine?.setRemoteViewConfig(canvas, uid: uid, for: AliRtcVideoTrack.camera)
}
else {
self.rtcEngine?.setRemoteViewConfig(nil, uid: uid, for: AliRtcVideoTrack.camera)
}
if audioTrack == .no && videoTrack == .no {
self.removeVideoView(uid: uid)
self.rtcEngine?.setRemoteViewConfig(nil, uid: uid, for: AliRtcVideoTrack.camera)
}
}
10. Sair do canal e destruir o mecanismo
Ao finalizar a sessão, saia do canal e destrua o mecanismo para liberar recursos:
Chame
stopPreviewpara encerrar a pré-visualização de vídeo.Chame
leaveChannelpara sair do canal.Chame
destroypara destruir o mecanismo e liberar os recursos associados.
self.rtcEngine?.stopPreview()
self.rtcEngine?.leaveChannel()
AliRtcEngine.destroy()
self.rtcEngine = nil
11. Demonstração do efeito

