Tous les produits
Search
Centre de documentation

ApsaraVideo Live:Mode invité

Dernière mise à jour :Aug 19, 2026

Le co-streaming permet à un spectateur de rejoindre un streamer en direct en tant que co-animateur via la communication en temps réel (RTC), tandis que les autres spectateurs continuent de regarder le flux via le CDN. La mise en œuvre implique la diffusion de flux vers une salle RTC, la gestion des tâches de mixage côté serveur et la transition entre la lecture CDN et la participation RTC.

Fonctionnement du co-streaming

image

Le streamer et le co-streamer diffusent leurs flux vers une salle RTC via le SDK ARTC. Le serveur d'application surveille les événements de changement de flux et appelle l'API StartLiveMPUTask pour transférer les flux de la salle RTC vers ApsaraVideo Live CDN. Les spectateurs récupèrent et regardent le flux CDN via le SDK ApsaraVideo Player.

Cycle de vie du co-streaming

Phase

Streamer

Co-streamer

Serveur d'application

Spectateurs

Avant le co-streaming

Rejoint la salle RTC et diffuse les flux audio/vidéo via le SDK ARTC.

Regarde le flux CDN via ApsaraVideo Player.

Détecte la diffusion du streamer, puis appelle StartLiveMPUTask avec MixMode=0 (transfert direct) pour acheminer le flux vers le CDN.

Récupère et lit le flux CDN via ApsaraVideo Player.

Pendant le co-streaming

Récupère le flux RTC du co-streamer et l'affiche.

Arrête la lecture CDN, rejoint la salle RTC, diffuse son flux et affiche le flux RTC du streamer.

Détecte la diffusion du co-streamer, puis appelle UpdateLiveMPUTask pour passer à MixMode=1 (mixage de flux) avec une disposition incluant les deux participants.

Aucune action requise. Le flux en direct bascule automatiquement pour afficher les deux participants.

Fin du co-streaming

Arrête la récupération du flux du co-streamer.

Quitte la salle RTC, détruit l'instance RTC et revient à la lecture CDN.

Détecte le départ du co-streamer, puis appelle UpdateLiveMPUTask pour revenir à MixMode=0 (transfert direct).

Aucune action requise. Le flux en direct revient automatiquement au streamer seul.

Remarque

Pour surveiller les événements de changement de flux dans la salle RTC, créez un abonnement avec CreateEventSub.

API clés

API

Objectif

Moment de l'appel

StartLiveMPUTask

Transférer les flux de la salle RTC vers le CDN

Après que le streamer a commencé à diffuser

UpdateLiveMPUTask

Basculer entre le transfert direct et le mixage de flux

Lorsqu'un co-streamer rejoint ou quitte la session

CreateEventSub

S'abonner aux événements de changement de flux dans la salle RTC

Avant de démarrer la diffusion (recommandé)

Étape 1 : Démarrer la diffusion (streamer)

image

1. Diffuser un flux vers la salle RTC

Le streamer initialise le SDK ARTC, configure les paramètres d'encodage et rejoint la salle RTC.

Pour le guide complet d'intégration du SDK ARTC, consultez :

Initialiser le moteur

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)

Configurer l'encodage audio et vidéo

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)

Publier les flux locaux et s'abonner aux flux distants

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)

Enregistrer les rappels

Gérez les changements d'état de connexion, les événements des utilisateurs distants et les conditions d'erreur.

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
        }
    }
}

Configurer l'aperçu local et rejoindre la salle

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. Transférer les flux RTC vers le CDN (serveur d'application)

Une fois que le streamer a diffusé un flux vers la salle RTC :

  1. Abonnez-vous aux événements de changement de flux. Appelez CreateEventSub pour recevoir des notifications lorsque les utilisateurs commencent ou arrêtent de diffuser dans la salle RTC.

  2. Démarrez une tâche de transfert direct. Après avoir reçu la notification de diffusion, appelez StartLiveMPUTask avec MixMode=0 (transfert de flux unique sans transcodage). Indiquez l'URL d'ingestion CDN, qui doit utiliser le protocole RTMP. Pour générer cette URL, consultez Générer les URL d'ingestion et de lecture.

  3. Distribuez l'URL de lecture. Surveillez les rappels de diffusion CDN pour détecter quand le transfert réussit, puis distribuez l'URL de lecture aux spectateurs. Pour plus de détails sur la configuration des rappels, consultez Paramètres de rappel.

3. Lire le flux CDN (spectateurs)

Lorsque les spectateurs reçoivent l'URL de lecture du serveur d'application, créez une instance ApsaraVideo Player et démarrez la lecture. Pour la référence complète de l'API du lecteur, consultez le SDK ApsaraVideo Player.

Remarque

Utilisez HTTP-FLV plutôt que RTMP pour l'URL de lecture CDN. Bien que les deux protocoles diffusent le même contenu, HTTP-FLV utilise les ports standard 80/443, offre une meilleure optimisation réseau et assure une lecture plus stable avec moins de saccades et une latence réduite. Le port par défaut 1935 de RTMP peut être bloqué par les pare-feu.

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];

Étape 2 : Démarrer le co-streaming (le spectateur rejoint en tant que co-animateur)

image

Côté co-streamer

1. Arrêter la lecture CDN

Détruisez l'instance du lecteur avant de rejoindre la salle RTC.

Android

aliPlayer.stop();
aliPlayer = null;

iOS

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

L'arrêt du lecteur CDN est obligatoire, il ne s'agit pas d'une étape de nettoyage facultative. Si le lecteur CDN continue de fonctionner, l'appareil du co-streamer lit simultanément l'audio CDN et l'audio RTC, ce qui produit un écho ou un doublement audio. Une fois le lecteur CDN arrêté, l'appareil ne lit plus que l'audio RTC et l'écho disparaît.

2. Rejoindre la salle RTC

Le co-streamer diffuse un flux vers la salle RTC en suivant le même processus que le streamer.

3. Afficher la vidéo du streamer

Lorsque la piste distante du streamer devient disponible, configurez la vue de rendu dans le rappel 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)
    }
}

Côté streamer

Le streamer affiche la vidéo du co-streamer en utilisant le même rappel onRemoteTrackAvailableNotify présenté ci-dessus.

Serveur d'application

Une fois que le co-streamer a commencé à diffuser, appelez UpdateLiveMPUTask pour mettre à jour la tâche de transfert direct existante :

  • Définissez MixMode sur 1 (mixage de flux).

  • Configurez la disposition du mixage de flux pour le streamer et le co-streamer.

Étape 3 : Terminer le co-streaming

image

Côté co-streamer

1. Quitter la salle 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. Reprendre la lecture CDN

Créez une nouvelle instance ApsaraVideo Player et lisez le flux CDN.

Côté streamer

Le streamer arrête la récupération des flux audio et vidéo du co-streamer. La vue de rendu est automatiquement nettoyée dans le rappel onRemoteTrackAvailableNotify lorsque videoTrack passe à AliRtcVideoTrackNo.

Serveur d'application

Une fois que le co-streamer a quitté la salle RTC, appelez UpdateLiveMPUTask pour mettre à jour la tâche de mixage de flux :

  • Rétablissez MixMode sur 0 (transfert direct, flux unique).

La vue du flux en direct CDN revient automatiquement à l'affichage du streamer seul.

Références