Todos os produtos
Search
Central de documentação

ApsaraVideo Live:Modo de participação como convidado

Última atualização: Jun 30, 2026

A transmissão conjunta permite que um espectador entre na transmissão ao vivo como coapresentador via Real-Time Communication (RTC), enquanto os demais espectadores continuam assistindo pelo CDN. A implementação envolve o envio de streams para uma sala RTC, o gerenciamento de tarefas de mixagem no servidor e a transição entre a reprodução via CDN e a participação via RTC.

Como funciona a transmissão conjunta

image

O apresentador e o coapresentador enviam streams para uma sala RTC por meio do ARTC SDK. O servidor de aplicativos monitora eventos de alteração de stream e chama a API StartLiveMPUTask para encaminhar os streams da sala RTC para o CDN. Os espectadores regulares recebem e assistem ao stream do CDN usando o ApsaraVideo Player SDK.

Ciclo de vida da transmissão conjunta

Fase

Apresentador

Coapresentador

Servidor de aplicativos

Espectadores

Antes da transmissão conjunta

Entra na sala RTC e envia streams de áudio e vídeo pelo ARTC SDK.

Assiste ao stream do CDN via ApsaraVideo Player.

Detecta o envio de stream do apresentador e chama a API StartLiveMPUTask com MixMode=0 (bypass) para encaminhar o stream ao CDN.

Recebem e reproduzem o stream do CDN via ApsaraVideo Player.

Durante a transmissão conjunta

Recebe o stream RTC do coapresentador e faz a renderização.

Interrompe a reprodução do CDN, entra na sala RTC, envia streams e renderiza o stream RTC do apresentador.

Detecta o envio de stream do coapresentador e chama a API UpdateLiveMPUTask para alternar para MixMode=1 (mixagem de streams) com um layout para ambos os participantes.

Nenhuma ação necessária. A transmissão ao vivo passa a exibir automaticamente ambos os participantes.

Fim da transmissão conjunta

Para de receber o stream do coapresentador.

Sai da sala RTC, destrói a instância RTC e retoma a reprodução via CDN.

Detecta a saída do coapresentador e chama a API UpdateLiveMPUTask para reverter para MixMode=0 (bypass).

Nenhuma ação necessária. A transmissão ao vivo volta automaticamente a exibir apenas o apresentador.

Nota

Para monitorar eventos de alteração de stream na sala RTC, crie uma assinatura com a API CreateEventSub.

APIs principais

API

Finalidade

Quando chamar

StartLiveMPUTask

Encaminhar streams da sala RTC para o CDN

Após o apresentador iniciar o envio de stream

UpdateLiveMPUTask

Alternar entre bypass e mixagem de streams

Quando um coapresentador entra ou sai

CreateEventSub

Assinar eventos de alteração de stream na sala RTC

Antes de iniciar a transmissão (recomendado)

Etapa 1: Iniciar a transmissão (apresentador)

image

1. Enviar um stream para a sala RTC

O apresentador inicializa o ARTC SDK, configure os parâmetros de codificação e entra na sala RTC.

Para consultar o guia completo de integração do ARTC SDK, visualize:

Inicializar o mecanismo

Android

// Import ARTC related classes
import com.alivc.rtc.AliRtcEngine;
import com.alivc.rtc.AliRtcEngineEventListener;
import com.alivc.rtc.AliRtcEngineNotify;

private AliRtcEngine mAliRtcEngine = null;
if(mAliRtcEngine == null) {
    mAliRtcEngine = AliRtcEngine.getInstance(this);
}

// Set channel mode to interactive live streaming
mAliRtcEngine.setChannelProfile(AliRtcEngine.AliRTCSdkChannelProfile.AliRTCSdkInteractiveLive);
mAliRtcEngine.setClientRole(AliRtcEngine.AliRTCSdkClientRole.AliRTCSdkInteractive);

iOS (Swift)

import AliVCSDK_ARTC

private var rtcEngine: AliRtcEngine? = nil

// Create the engine and set the delegate
let engine = AliRtcEngine.sharedInstance(self, extras: nil)
self.rtcEngine = engine

// Set channel mode to interactive live streaming
engine.setChannelProfile(AliRtcChannelProfile.interactivelive)
engine.setClientRole(AliRtcClientRole.roleInteractive)

Configurar a codificação de áudio e vídeo

Android

// Audio: high-quality music mode
mAliRtcEngine.setAudioProfile(
    AliRtcEngine.AliRtcAudioProfile.AliRtcEngineHighQualityMode,
    AliRtcEngine.AliRtcAudioScenario.AliRtcSceneMusicMode
);

// Video: 720x1280 at 20 fps, 1200 kbps
AliRtcEngine.AliRtcVideoEncoderConfiguration config =
    new AliRtcEngine.AliRtcVideoEncoderConfiguration();
config.dimensions = new AliRtcEngine.AliRtcVideoDimensions(720, 1280);
config.frameRate = 20;
config.bitrate = 1200;
config.keyFrameInterval = 2000;
config.orientationMode = AliRtcVideoEncoderOrientationModeAdaptive;
mAliRtcEngine.setVideoEncoderConfiguration(config);

iOS (Swift)

// Audio: high-quality music mode
engine.setAudioProfile(
    AliRtcAudioProfile.engineHighQualityMode,
    audio_scene: AliRtcAudioScenario.sceneMusicMode
)

// Video: 720x1280 at 20 fps, 1200 kbps
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)

Publicar streams locais e assinar streams remotos

Android

// Publish local audio and video
mAliRtcEngine.publishLocalAudioStream(true);
mAliRtcEngine.publishLocalVideoStream(true);

// Subscribe to all remote streams
mAliRtcEngine.setDefaultSubscribeAllRemoteAudioStreams(true);
mAliRtcEngine.subscribeAllRemoteAudioStreams(true);
mAliRtcEngine.setDefaultSubscribeAllRemoteVideoStreams(true);
mAliRtcEngine.subscribeAllRemoteVideoStreams(true);

iOS (Swift)

// Publish local audio and video
engine.publishLocalVideoStream(true)
engine.publishLocalAudioStream(true)

// Subscribe to all remote streams
engine.setDefaultSubscribeAllRemoteAudioStreams(true)
engine.subscribeAllRemoteAudioStreams(true)
engine.setDefaultSubscribeAllRemoteVideoStreams(true)
engine.subscribeAllRemoteVideoStreams(true)

Registrar callbacks

Gerencie alterações no status da conexão, eventos de usuários remotos e condições de erro.

Android

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(() -> {
            if(status == AliRtcEngine.AliRtcConnectionStatus.AliRtcConnectionStatusFailed) {
                // Handle unrecoverable connection failure
                ToastHelper.showToast(VideoChatActivity.this,
                    R.string.video_chat_connection_failed, Toast.LENGTH_SHORT);
            }
        });
    }

    @Override
    public void OnLocalDeviceException(AliRtcEngine.AliRtcEngineLocalDeviceType deviceType,
            AliRtcEngine.AliRtcEngineLocalDeviceExceptionType exceptionType, String msg){
        super.OnLocalDeviceException(deviceType, exceptionType, msg);
        // Notify the user when the SDK cannot recover the device
        handler.post(() -> {
            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();
        // Refresh the token before it expires.
        // Obtain new auth info for the current channel and user,
        // then call refreshAuthInfo.
    }

    @Override
    public void onRemoteUserOnLineNotify(String uid, int elapsed){
        super.onRemoteUserOnLineNotify(uid, elapsed);
    }

    @Override
    public void onRemoteUserOffLineNotify(String uid,
            AliRtcEngine.AliRtcUserOfflineReason reason){
        super.onRemoteUserOffLineNotify(uid, reason);
        // Remove the remote video rendering view
    }

    @Override
    public void onRemoteTrackAvailableNotify(String uid,
            AliRtcEngine.AliRtcAudioTrack audioTrack,
            AliRtcEngine.AliRtcVideoTrack videoTrack){
        // Set or remove remote video rendering.
            // See "Render the streamer's video" in Step 2.
    }

    @Override
    public void onBye(int code){
        // Handle session termination (e.g., another device using the same UserID)
    }
};

mAliRtcEngine.setRtcEngineEventListener(mRtcEngineEventListener);
mAliRtcEngine.setRtcEngineNotify(mRtcEngineNotify);

iOS (Swift)

extension VideoCallMainVC: AliRtcEngineDelegate {

    func onJoinChannelResult(_ result: Int32, channel: String, userId: String, elapsed: Int32) {
        "onJoinChannelResult result: \(result)".printLog()
    }

    func onRemoteUser(onLineNotify uid: String, elapsed: Int32) {
        "onRemoteUserOnlineNotify uid: \(uid)".printLog()
    }

    func onRemoteUserOffLineNotify(_ uid: String, offlineReason reason: AliRtcUserOfflineReason) {
        "onRemoteUserOffLineNotify uid: \(uid) reason: \(reason)".printLog()
    }

    func onRemoteTrackAvailableNotify(_ uid: String, audioTrack: AliRtcAudioTrack,
            videoTrack: AliRtcVideoTrack) {
        // Set or remove remote video rendering.
        // See "Render the streamer's video" in Step 2.
    }

    func onAuthInfoWillExpire() {
        // Refresh the token before it expires.
        // Obtain new auth info for the current channel and user,
        // then call refreshAuthInfo.
    }

    func onAuthInfoExpired() {
        // Token is invalid. Leave the channel and release the engine.
    }

    func onBye(_ code: Int32) {
        // Handle session termination (e.g., another device using the same UserID)
    }

    func onLocalDeviceException(_ deviceType: AliRtcLocalDeviceType,
            exceptionType: AliRtcLocalDeviceExceptionType, message msg: String?) {
        // Notify the user when the SDK cannot recover the device
    }

    func onConnectionStatusChange(_ status: AliRtcConnectionStatus,
            reason: AliRtcConnectionStatusChangeReason) {
        if status == .failed {
            // Handle unrecoverable connection failure
        }
    }
}

Configurar a visualização local e entrar na sala

Android

// Local preview
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();

// Join the RTC room
mAliRtcEngine.joinChannel(token, null, null, null);

iOS (Swift)

// Local preview
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()

// Join the RTC room
let ret = self.rtcEngine?.joinChannel(joinToken, channelId: nil, userId: nil, name: nil) {
    [weak self] errCode, channelId, userId, elapsed in
    if errCode == 0 {
        // Joined successfully
    } else {
        // Handle join failure
    }
}

2. Encaminhar streams RTC para o CDN (servidor de aplicativos)

Depois que o apresentador envia um stream para a sala RTC:

  1. Assine eventos de alteração de stream. Chame a API CreateEventSub para receber notificações quando usuários iniciarem ou interromperem o envio de streams na sala RTC.

  2. Inicie uma tarefa de streaming em bypass. Ao receber a notificação de envio de stream, chame a API StartLiveMPUTask com MixMode=0 (encaminhamento de stream único sem transcodificação). Informe a URL de ingestão do CDN, que deve usar o protocolo RTMP. Para gerar essa URL, consulte Generate ingest and playback URLs.

  3. Distribua a URL de reprodução. Monitore os callbacks de envio de stream do CDN para detectar quando o encaminhamento for bem-sucedido e, em seguida, distribua a URL de reprodução aos espectadores. Para detalhes sobre a configuração de callbacks, visualize Callback settings.

3. Reproduzir o stream do CDN (espectadores)

Ao receberem a URL de reprodução do servidor de aplicativos, os espectadores devem criar uma instância do ApsaraVideo Player e iniciar a reprodução. Para consultar a referência completa da API do player, visualize ApsaraVideo Player SDK.

Nota

Utilize HTTP-FLV em vez de RTMP para a URL de reprodução do CDN. Ambos entregam o mesmo conteúdo, mas o HTTP-FLV usa as portas padrão 80/443, oferece melhor otimização de rede e proporciona uma reprodução mais estável, com menos travamentos e menor latência. A porta padrão do RTMP (1935) pode ser bloqueada por firewalls.

Android

AliPlayer aliPlayer = AliPlayerFactory.createAliPlayer(context);
aliPlayer.setAutoPlay(true);

UrlSource urlSource = new UrlSource();
// Use HTTP-FLV for CDN playback
urlSource.setUri("http://test.alivecdn.com/live/streamId.flv?auth_key=XXX");
aliPlayer.setDataSource(urlSource);
aliPlayer.prepare();

iOS (Objective-C)

self.cdnPlayer = [[AliPlayer alloc] init];
self.cdnPlayer.delegate = self;
self.cdnPlayer.autoPlay = YES;

// Use HTTP-FLV for CDN playback
AVPUrlSource *source = [[AVPUrlSource alloc] urlWithString:@"http://test.alivecdn.com/live/streamId.flv?auth_key=XXX"];
[self.cdnPlayer setUrlSource:source];
[self.cdnPlayer prepare];

Etapa 2: Iniciar a transmissão conjunta (espectador entra como coapresentador)

image

Lado do coapresentador

1. Interromper a reprodução do CDN

Destrua a instância do player antes de entrar na sala RTC.

Android

aliPlayer.stop();
aliPlayer = null;

iOS

[self.cdnPlayer stop];
[self.cdnPlayer clearScreen];
self.cdnPlayer.playerView = nil;

2. Entrar na sala RTC

O coapresentador envia um stream para a sala RTC seguindo o mesmo processo do apresentador. Consulte a seção Enviar um stream para a sala RTC acima.

3. Renderizar o vídeo do apresentador

Quando a faixa remota do apresentador estiver disponível, configure a visualização de renderização no callback onRemoteTrackAvailableNotify.

Android

@Override
public void onRemoteTrackAvailableNotify(String uid,
        AliRtcEngine.AliRtcAudioTrack audioTrack,
        AliRtcEngine.AliRtcVideoTrack videoTrack){
    handler.post(() -> {
        if(videoTrack == AliRtcVideoTrackCamera) {
            // Create and attach the remote rendering view
            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) {
            // Remove the remote rendering view
            FrameLayout fl_remote = findViewById(R.id.fl_remote);
            fl_remote.removeAllViews();
            mAliRtcEngine.setRemoteViewConfig(null, uid, AliRtcVideoTrackCamera);
        }
    });
}

iOS (Swift)

func onRemoteTrackAvailableNotify(_ uid: String, audioTrack: AliRtcAudioTrack,
        videoTrack: AliRtcVideoTrack) {
    if audioTrack != .no {
        let videoView = self.videoViewList.first { $0.uidLabel.text == uid }
        if videoView == nil {
            _ = self.createVideoView(uid: uid)
        }
    }

    if videoTrack != .no {
        // Create or reuse the video view, then configure rendering
        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 {
        // Remote user stopped all tracks -- clean up
        self.removeVideoView(uid: uid)
        self.rtcEngine?.setRemoteViewConfig(nil, uid: uid, for: AliRtcVideoTrack.camera)
    }
}

Lado do apresentador

O apresentador renderiza o vídeo do coapresentador utilizando o mesmo callback onRemoteTrackAvailableNotify demonstrado acima.

Servidor de aplicativos

Após o coapresentador enviar um stream, chame a API UpdateLiveMPUTask para atualize a tarefa de streaming em bypass existente:

  • Defina MixMode como 1 (mixagem de streams).

  • Configure o layout de mixagem de streams para incluir tanto o apresentador quanto o coapresentador.

Etapa 3: Encerrar a transmissão conjunta

image

Lado do coapresentador

1. Sair da sala RTC

Android

mAliRtcEngine.stopPreview();
mAliRtcEngine.setLocalViewConfig(null, AliRtcVideoTrackCamera);
mAliRtcEngine.leaveChannel();
mAliRtcEngine.destroy();
mAliRtcEngine = null;

iOS (Swift)

self.rtcEngine?.stopPreview()
self.rtcEngine?.leaveChannel()
AliRtcEngine.destroy()
self.rtcEngine = nil

2. Retomar a reprodução do CDN

Crie uma nova instância do ApsaraVideo Player e reproduza o stream do CDN. O processo é idêntico ao descrito na seção Reproduzir o stream do CDN (espectadores) acima.

Lado do apresentador

O apresentador interrompe o recebimento dos streams de áudio e vídeo do coapresentador. A visualização de renderização é limpa automaticamente no callback onRemoteTrackAvailableNotify quando videoTrack muda para AliRtcVideoTrackNo.

Servidor de aplicativos

Depois que o coapresentador sai da sala RTC, chame a API UpdateLiveMPUTask para atualize a tarefa de mixagem de streams:

  • Reverta MixMode para 0 (bypass, encaminhamento de stream único).

A visualização da transmissão ao vivo no CDN passa automaticamente de ambos os participantes para apenas o apresentador.

Referências