Este guia demonstra como integrar o ARTC SDK ao seu projeto Android para criar um aplicativo de áudio e vídeo em tempo real, ideal para casos de uso como transmissões ao vivo interativas e chamadas de vídeo.
Descrição dos recursos
Antes de começar, compreenda os seguintes conceitos fundamentais:
ARTC SDK: SDK fornecido pela Alibaba Cloud que auxilia desenvolvedores a implementar rapidamente interações de áudio e vídeo em tempo real.
Global Realtime Transport Network (GRTN): Rede distribuída globalmente, projetada para mídia em tempo real, que garante comunicação com latência ultrabaixa, alta qualidade e segurança.
Canal: Sala virtual onde os usuários entram para se comunicar. Todos os participantes do mesmo canal podem interagir em tempo real.
Host: Usuário com permissão para publicar fluxos de áudio e vídeo em um canal e assinar fluxos publicados por outros hosts.
Viewer: Usuário que pode assinar fluxos de áudio e vídeo em um canal, mas não tem permissão para publicar seus próprios fluxos.
Processo básico para implementar interação de áudio e vídeo em tempo real:
-
Chame
setChannelProfilepara definir o cenário e chamejoinChannelpara entrar em um canal:Cenário de chamada de vídeo: Todos os usuários atuam como hosts, podendo publicar e assinar fluxos.
Cenário de transmissão interativa: Defina as funções com
setClientRoleantes de entrar no canal. Para usuários que publicarão fluxos, defina a função como host. Caso o usuário precise apenas assinar fluxos, defina a função como viewer.
-
Após entrar no canal, os comportamentos de publicação e assinatura variam conforme a função do usuário:
Todos os participantes recebem fluxos de áudio e vídeo dentro desse canal.
O host publica fluxos de áudio e vídeo no canal.
Se um viewer desejar publicar fluxos, chame o método
setClientRolepara alterar sua função para host.
Projeto de exemplo
O ARTC SDK oferece um projeto de exemplo open-source para aplicativos de áudio e vídeo em tempo real.
Requisitos de ambiente
Antes de executar o projeto de exemplo, certifique-se de que seu ambiente de desenvolvimento atenda aos seguintes requisitos:
Ferramenta de desenvolvimento: Android Studio 2020.3.1 ou superior.
-
Dispositivo de teste: Dispositivo rodando Android 5.0 (SDK API Level 21) ou superior.
NotaRecomendamos o uso de um dispositivo físico para testes, pois alguns emuladores podem não oferecer toda a funcionalidade necessária.
Rede: Conexão estável com a internet.
Aplicativo: Obtenha o AppID e a AppKey do seu aplicativo ARTC. Para mais detalhes, consulte Criar um aplicativo ARTC.
Criar um projeto (opcional)
Esta seção explica como criar um projeto e adicionar as permissões necessárias para interação de áudio e vídeo. Se você já possui um projeto, pule esta etapa.
Abra o Android Studio e selecione New Project.
Selecione Phone and Tablet e escolha um modelo inicial. Este exemplo utiliza Empty Views Activity.

Configure as informações do projeto, incluindo nome, nome do pacote, local de salvamento, linguagem (Java neste exemplo) e linguagem de configuração de build (Groovy DSL neste exemplo).

Clique em Finish e aguarde a sincronização do projeto.
Configurar o projeto
Etapa 5: Criar uma interface de usuário
Crie uma interface de usuário adequada ao seu cenário. O código de exemplo abaixo, destinado a chamadas de vídeo, cria duas visualizações para exibir os fluxos de vídeo local e remoto. Utilize-o como referência durante o desenvolvimento.
Implementação
Esta seção detalha o uso do ARTC SDK para construir um aplicativo básico de áudio e vídeo em tempo real. Copie o exemplo de código completo para o seu projeto e teste a funcionalidade. As etapas a seguir explicam as chamadas principais da API.
O diagrama abaixo ilustra o fluxo de trabalho básico para implementar uma chamada de vídeo:
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 o projeto de demonstração do ARTC para Android.
1. Solicitar permissões
Ao iniciar uma chamada de vídeo, verifique se as permissões necessárias foram concedidas ao aplicativo:
private static final int REQUEST_PERMISSION_CODE = 101;
private static final String[] PERMISSION_MANIFEST = {
Manifest.permission.RECORD_AUDIO,
Manifest.permission.READ_PHONE_STATE,
Manifest.permission.WRITE_EXTERNAL_STORAGE,
Manifest.permission.READ_EXTERNAL_STORAGE,
Manifest.permission.CAMERA
};
private static final String[] PERMISSION_MANIFEST33 = {
Manifest.permission.RECORD_AUDIO,
Manifest.permission.READ_PHONE_STATE,
Manifest.permission.CAMERA
};
private static String[] getPermissions() {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU) {
return PERMISSION_MANIFEST;
}
return PERMISSION_MANIFEST33;
}
public boolean checkOrRequestPermission() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
if (ContextCompat.checkSelfPermission(this, "android.permission.CAMERA") != PackageManager.PERMISSION_GRANTED
|| ContextCompat.checkSelfPermission(this, "android.permission.RECORD_AUDIO") != PackageManager.PERMISSION_GRANTED) {
requestPermissions(getPermissions(), REQUEST_PERMISSION_CODE);
return false;
}
}
return true;
}
2. Obter um token de autenticação
Entrar em um canal ARTC exige um token de autenticação para verificar a identidade do usuário. Para detalhes sobre a geração do token, consulte Implementar autenticação baseada em token. A geração pode ocorrer via método de parâmetro único ou multiparâmetro. O método escolhido determina qual API joinChannel deve ser chamada.
Para ambientes de produção:
Como a geração do token requer sua AppKey, codificá-la diretamente no 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 do APIExample para criar um token provisório. O código de referência é o seguinte:
public final class ARTCTokenHelper {
/**
* RTC AppId
*/
public static String AppId = "";
/**
* RTC AppKey
*/
public static String AppKey = "";
/**
* Generate a single-parameter token for joining a meeting based on channelId, userId, timestamp, and nonce.
*/
public static String generateSingleParameterToken(String appId, String appKey, String channelId, String userId, long timestamp, String nonce) {
StringBuilder stringBuilder = new StringBuilder()
.append(appId)
.append(appKey)
.append(channelId)
.append(userId)
.append(timestamp);
String token = getSHA256(stringBuilder.toString());
try{
JSONObject tokenJson = new JSONObject();
tokenJson.put("appid", AppId);
tokenJson.put("channelid", channelId);
tokenJson.put("userid", userId);
tokenJson.put("nonce", nonce);
tokenJson.put("timestamp", timestamp);
tokenJson.put("token", token);
String base64Token = Base64.encodeToString(tokenJson.toString().getBytes(StandardCharsets.UTF_8), Base64.NO_WRAP);
return base64Token;
}catch (Exception e) {
e.printStackTrace();
}
return null;
}
/**
* Generate a single-parameter token for joining a meeting based on channelId, userId, and timestamp.
*/
public static String generateSingleParameterToken(String appId, String appKey, String channelId, String userId, long timestamp) {
return generateSingleParameterToken(appId, appKey, channelId, userId, timestamp, "");
}
public static String getSHA256(String str) {
try {
MessageDigest messageDigest = MessageDigest.getInstance("SHA-256");
byte[] hash = messageDigest.digest(str.getBytes(StandardCharsets.UTF_8));
return byte2Hex(hash);
} catch (NoSuchAlgorithmException e) {
// Consider logging the exception and/or re-throwing as a RuntimeException
e.printStackTrace();
}
return "";
}
private static String byte2Hex(byte[] bytes) {
StringBuilder stringBuilder = new StringBuilder();
for (byte b : bytes) {
String hex = Integer.toHexString(0xff & b);
if (hex.length() == 1) {
// Use single quote for char
stringBuilder.append('0');
}
stringBuilder.append(hex);
}
return stringBuilder.toString();
}
public static long getTimesTamp() {
return System.currentTimeMillis() / 1000 + 60 * 60 * 24;
}
}
3. Importar classes do ARTC SDK
Importe as classes e interfaces relevantes do ARTC SDK:
// Import ARTC classes
import com.alivc.rtc.AliRtcEngine;
import com.alivc.rtc.AliRtcEngineEventListener;
import com.alivc.rtc.AliRtcEngineNotify;
4. Criar e inicializar o mecanismo
-
Criar o mecanismo RTC
Chame o método
getInstance[1/2]para criar uma instância deAliRtcEngine.private AliRtcEngine mAliRtcEngine = null; if(mAliRtcEngine == null) { mAliRtcEngine = AliRtcEngine.getInstance(this); } -
Inicializar o mecanismo
-
Chame
setChannelProfilepara configurar o canal comoAliRTCSdkInteractiveLive(modo interativo).Conforme suas necessidades de negócio, escolha o modo interativo (adequado para cenários de entretenimento interativo) ou o modo de comunicação (ideal para transmissões um-para-um ou um-para-muitos). Selecionar o modo correto garante uma experiência fluida e uso eficiente dos recursos de rede.
Modo
Publicação
Assinatura
Descrição
Modo interativo
Limitado 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, garantindo uma transmissão ininterrupta.
Neste modo, os hosts são responsáveis pela interação, enquanto os viewers apenas consomem conteúdo. Se suas necessidades de negócio puderem mudar, considere usar o modo interativo por padrão. Sua flexibilidade permite adaptação a diferentes requisitos de interação através do ajuste das 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 no modo interativo. O objetivo é simplificar as operações, permitindo que os usuários obtenham a funcionalidade desejada com menos chamadas de API.
-
Chame
setClientRolepara definir a função do usuário comoAliRTCSdkInteractive(host) ouAliRTCSdkLive(viewer). Nota: A função host publica e assina por padrão. A função viewer apenas assina por padrão, com pré-visualização e publicação desativadas.NotaQuando um usuário altera sua função dentro de um canal, o sistema ajusta o status de publicação de áudio e vídeo correspondentemente:
Alteração de host para viewer: O sistema interrompe a publicação dos fluxos locais de áudio e vídeo. Os fluxos remotos assinados não são afetados, e o usuário continua assistindo aos demais participantes.
Alteração de viewer para host: O sistema inicia a publicação dos fluxos locais de áudio e vídeo. Os fluxos remotos assinados permanecem inalterados, e o usuário continua assistindo aos outros participantes.
// Set the channel mode to interactive mode, use AliRTCSdkInteractiveLive for RTC mAliRtcEngine.setChannelProfile(AliRtcEngine.AliRTCSdkChannelProfile.AliRTCSdkInteractiveLive); // Set the user role, use AliRTCSdkInteractive to both ingest and pull streams, use AliRTCSdkLive if you only pull streams without ingesting mAliRtcEngine.setClientRole(AliRtcEngine.AliRTCSdkClientRole.AliRTCSdkInteractive);
-
-
Definir callbacks comuns
Se o SDK encontrar um problema durante a operação, ele tentará primeiro se recuperar automaticamente usando seus mecanismos internos de nova tentativa. Para erros que não conseguir resolver sozinho, o SDK notificará seu aplicativo através de interfaces de callback predefinidas.
Abaixo estão os principais callbacks para problemas que o SDK não consegue tratar, os quais seu aplicativo deve monitorar e responder:
Causa da exceção
Callback e parâmetros
Solução
Descrição
Falha na autenticação
result no callback onJoinChannelResult retorna AliRtcErrJoinBadToken.
O aplicativo 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 separado.
Token expirado
onAuthInfoExpired
O aplicativo deve 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 separado.
Problema de conexão de rede
O callback onConnectionStatusChange retorna AliRtcConnectionStatusFailed.
O aplicativo deve entrar novamente no canal.
O SDK consegue se recuperar automaticamente de breves desconexões de rede. Se o tempo de desconexão exceder um limiar, ocorrerá timeout. O aplicativo 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 do aplicativo e se o hardware está funcionando corretamente.
Quando ocorre uma exceção de dispositivo local que o SDK não consegue resolver, ele notifica o aplicativo via callback. O aplicativo deve então intervir para verificar o status do dispositivo.
private AliRtcEngineEventListener mRtcEngineEventListener = new AliRtcEngineEventListener() { @Override public void onJoinChannelResult(int result, String channel, String userId, int elapsed) { super.onJoinChannelResult(result, channel, userId, elapsed); handleJoinResult(result, channel, userId); } @Override public void onLeaveChannelResult(int result, AliRtcEngine.AliRtcStats stats){ super.onLeaveChannelResult(result, stats); } @Override public void onConnectionStatusChange(AliRtcEngine.AliRtcConnectionStatus status, AliRtcEngine.AliRtcConnectionStatusChangeReason reason){ super.onConnectionStatusChange(status, reason); handler.post(new Runnable() { @Override public void run() { if(status == AliRtcEngine.AliRtcConnectionStatus.AliRtcConnectionStatusFailed) { /* TODO: Must handle. We recommend notifying the user. This is reported only after the SDK's internal recovery strategies have failed. */ ToastHelper.showToast(VideoChatActivity.this, R.string.video_chat_connection_failed, Toast.LENGTH_SHORT); } else { /* TODO: Optional. Add business logic here, typically for data analytics or UI changes. */ } } }); } @Override public void OnLocalDeviceException(AliRtcEngine.AliRtcEngineLocalDeviceType deviceType, AliRtcEngine.AliRtcEngineLocalDeviceExceptionType exceptionType, String msg){ super.OnLocalDeviceException(deviceType, exceptionType, msg); /* TODO: Must handle. We recommend notifying the user of the device error. This is reported only after the SDK's internal recovery strategies have failed. */ handler.post(new Runnable() { @Override public void run() { String str = "OnLocalDeviceException deviceType: " + deviceType + " exceptionType: " + exceptionType + " msg: " + msg; ToastHelper.showToast(VideoChatActivity.this, str, Toast.LENGTH_SHORT); } }); } }; private AliRtcEngineNotify mRtcEngineNotify = new AliRtcEngineNotify() { @Override public void onAuthInfoWillExpire() { super.onAuthInfoWillExpire(); /* TODO: Must handle. When this callback is triggered, retrieve a new token for the current user and channel, then call refreshAuthInfo to update it. */ } @Override public void onRemoteUserOnLineNotify(String uid, int elapsed){ super.onRemoteUserOnLineNotify(uid, elapsed); } // Unset the remote video stream renderer in the onRemoteUserOffLineNotify callback. @Override public void onRemoteUserOffLineNotify(String uid, AliRtcEngine.AliRtcUserOfflineReason reason){ super.onRemoteUserOffLineNotify(uid, reason); } // Set the remote video stream renderer in the onRemoteTrackAvailableNotify callback. @Override public void onRemoteTrackAvailableNotify(String uid, AliRtcEngine.AliRtcAudioTrack audioTrack, AliRtcEngine.AliRtcVideoTrack videoTrack){ handler.post(new Runnable() { @Override public void run() { if(videoTrack == AliRtcVideoTrackCamera) { SurfaceView surfaceView = mAliRtcEngine.createRenderSurfaceView(VideoChatActivity.this); surfaceView.setZOrderMediaOverlay(true); FrameLayout view = getAvailableView(); if (view == null) { return; } remoteViews.put(uid, view); view.addView(surfaceView, new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT)); AliRtcEngine.AliRtcVideoCanvas remoteVideoCanvas = new AliRtcEngine.AliRtcVideoCanvas(); remoteVideoCanvas.view = surfaceView; mAliRtcEngine.setRemoteViewConfig(remoteVideoCanvas, uid, AliRtcVideoTrackCamera); } else if(videoTrack == AliRtcVideoTrackNo) { if(remoteViews.containsKey(uid)) { ViewGroup view = remoteViews.get(uid); if(view != null) { view.removeAllViews(); remoteViews.remove(uid); mAliRtcEngine.setRemoteViewConfig(null, uid, AliRtcVideoTrackCamera); } } } } }); } /* Your app must also handle cases where multiple devices attempt to join with the same UserID. */ @Override public void onBye(int code){ handler.post(new Runnable() { @Override public void run() { String msg = "onBye code:" + code; ToastHelper.showToast(VideoChatActivity.this, msg, Toast.LENGTH_SHORT); } }); } }; mAliRtcEngine.setRtcEngineEventListener(mRtcEngineEventListener); mAliRtcEngine.setRtcEngineNotify(mRtcEngineNotify);
5. Definir propriedades de áudio e vídeo
-
Definir propriedades de áudio
Chame
setAudioProfilepara configurar o modo de codificação de áudio e o cenário.mAliRtcEngine.setAudioProfile(AliRtcEngine.AliRtcAudioProfile.AliRtcEngineHighQualityMode, AliRtcEngine.AliRtcAudioScenario.AliRtcSceneMusicMode); -
Definir propriedades de vídeo
Configure as propriedades do fluxo de vídeo publicado, como resolução, taxa de bits e taxa de quadros.
// Set video encoding parameters. AliRtcEngine.AliRtcVideoEncoderConfiguration aliRtcVideoEncoderConfiguration = new AliRtcEngine.AliRtcVideoEncoderConfiguration(); aliRtcVideoEncoderConfiguration.dimensions = new AliRtcEngine.AliRtcVideoDimensions( 720, 1280); aliRtcVideoEncoderConfiguration.frameRate = 20; aliRtcVideoEncoderConfiguration.bitrate = 1200; aliRtcVideoEncoderConfiguration.keyFrameInterval = 2000; aliRtcVideoEncoderConfiguration.orientationMode = AliRtcVideoEncoderOrientationModeAdaptive; mAliRtcEngine.setVideoEncoderConfiguration(aliRtcVideoEncoderConfiguration);
6. Definir propriedades de publicação e assinatura
Configure a publicação de fluxos de áudio/vídeo e defina a assinatura padrão para os fluxos de todos os usuários:
Chame publishLocalAudioStream para publicar um fluxo de áudio.
Chame publishLocalVideoStream para publicar um fluxo de vídeo. Para chamadas apenas de áudio, defina este valor como false.
// The SDK publishes audio by default, so you don't need to call publishLocalAudioStream.
mAliRtcEngine.publishLocalAudioStream(true);
// For video calls, you don't need to call publishLocalVideoStream(true) as the SDK publishes video by default.
// For audio-only calls, you need to call publishLocalVideoStream(false) to disable video publishing.
mAliRtcEngine.publishLocalVideoStream(true);
// Set default subscription to all remote audio and video streams.
mAliRtcEngine.setDefaultSubscribeAllRemoteAudioStreams(true);
mAliRtcEngine.subscribeAllRemoteAudioStreams(true);
mAliRtcEngine.setDefaultSubscribeAllRemoteVideoStreams(true);
mAliRtcEngine.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. Chame os métodos acima para substituir esse comportamento padrão.
7. Ativar pré-visualização local
Chame setLocalViewConfig para configurar a visualização de renderização local e definir as propriedades de exibição do vídeo local.
Chame startPreview para iniciar a pré-visualização do vídeo local.
mLocalVideoCanvas = new AliRtcEngine.AliRtcVideoCanvas();
SurfaceView localSurfaceView = mAliRtcEngine.createRenderSurfaceView(VideoChatActivity.this);
localSurfaceView.setZOrderOnTop(true);
localSurfaceView.setZOrderMediaOverlay(true);
FrameLayout fl_local = findViewById(R.id.fl_local);
fl_local.addView(localSurfaceView, layoutParams);
mLocalVideoCanvas.view = localSurfaceView;
mAliRtcEngine.setLocalViewConfig(mLocalVideoCanvas, AliRtcVideoTrackCamera);
mAliRtcEngine.startPreview();
8. Entrar em um canal
Chame joinChannel para entrar no canal. Se o token foi gerado pelo método de parâmetro único, chame a operação [joinChannel[1/3]](t2309760.xdita#758b964acc7jd). Se foi gerado pelo método multiparâmetro, chame o método [joinChannel[2/3]](t2309760.xdita#766e40a1cfk4p). O resultado é retornado no callback onJoinChannelResult. Um resultado 0 indica entrada bem-sucedida. Um resultado diferente de zero pode indicar um token inválido.
mAliRtcEngine.joinChannel(token, null, null, null);
Após entrar no canal, o SDK publicará e assinará fluxos de acordo com os parâmetros definidos anteriormente.
O SDK publica e assina automaticamente por padrão para reduzir o número de chamadas de API que o cliente precisa fazer.
9. Definir a visualização remota
Ao inicializar o mecanismo, configure o callback correspondente com mAliRtcEngine.setRtcEngineNotify. No callback onRemoteTrackAvailableNotify, configure a visualização para o usuário remoto. Código de exemplo:
@Override
public void onRemoteTrackAvailableNotify(String uid, AliRtcEngine.AliRtcAudioTrack audioTrack, AliRtcEngine.AliRtcVideoTrack videoTrack){
handler.post(new Runnable() {
@Override
public void run() {
if(videoTrack == AliRtcVideoTrackCamera) {
SurfaceView surfaceView = mAliRtcEngine.createRenderSurfaceView(VideoChatActivity.this);
surfaceView.setZOrderMediaOverlay(true);
FrameLayout fl_remote = findViewById(R.id.fl_remote);
if (fl_remote == null) {
return;
}
fl_remote.addView(surfaceView, new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
AliRtcEngine.AliRtcVideoCanvas remoteVideoCanvas = new AliRtcEngine.AliRtcVideoCanvas();
remoteVideoCanvas.view = surfaceView;
mAliRtcEngine.setRemoteViewConfig(remoteVideoCanvas, uid, AliRtcVideoTrackCamera);
} else if(videoTrack == AliRtcVideoTrackNo) {
FrameLayout fl_remote = findViewById(R.id.fl_remote);
fl_remote.removeAllViews();
mAliRtcEngine.setRemoteViewConfig(null, uid, AliRtcVideoTrackCamera);
}
}
});
}
10. Sair do canal e destruir o mecanismo
Ao finalizar a sessão, saia do canal e destrua o mecanismo:
Chame
stopPreviewpara parar a pré-visualização do vídeo.Chame
leaveChannelpara sair do canal.Chame
destroypara destruir o mecanismo e liberar seus recursos.
private void destroyRtcEngine() {
mAliRtcEngine.stopPreview();
mAliRtcEngine.setLocalViewConfig(null, AliRtcVideoTrackCamera);
mAliRtcEngine.leaveChannel();
mAliRtcEngine.destroy();
mAliRtcEngine = null;
}
11. Demonstração do efeito
