このトピックでは、共同ストリーミングの手順とサンプルコードについて説明し、この機能を迅速に統合するのに役立ちます。
ソリューション概要
リアルタイムコミュニケーションと CDN バイパスストリーミングを使用して、超低遅延とより多くの参加者とのリアルタイムインタラクションを実現します。
ストリーマーと共同ストリーマーは、ARTC SDK を介して RTC ルームにストリームをプッシュします。
アプリケーションサーバーはストリーム変更イベントを監視し、StartLiveMPUTask - 混合ストリームタスクを作成 (新規) API を呼び出して、RTC ルームから CDN にストリームを転送します。
通常の視聴者は ApsaraVideo Player SDK を使用して CDN からストリームをプルして視聴します。
ストリーミングと共同ストリーミングの基本的なプロセスは次のとおりです:
共同ストリーミング前 | ストリーマー側: ARTC SDK を使用して RTC ルームに参加し、リアルタイムの音声およびビデオストリームをプッシュします。 アプリケーションサーバー: RTC ルームのストリーム変更イベントを監視します。ストリーマーがストリームをプッシュした後、StartLiveMPUTask - 混合ストリームタスクを作成 (新規) を呼び出してバイパスストリーミングタスク (Task1) を開始し、CDN アップストリーミング URL を渡し、RTC ルームから CDN にストリームを転送します。 視聴者: CDN 再生 URL と共に ApsaraVideo Player SDK を使用してストリームをプルして再生します。 |
共同ストリーミング中 | 共同ストリーマー:
ストリーマー: RTC ルームから共同ストリーマーのリアルタイム音声およびビデオストリームをプルし、レンダリングビューを設定して、リアルタイムでレンダリングします。 アプリケーションサーバー: RTC ルームのストリーム変更イベントを監視します。共同ストリーマーがストリームをプッシュした後、バイパスストリーミングタスク (Task1) を更新し、モードをバイパスから混合ストリームに変更し、ストリーマーと共同ストリーマーのレイアウトを渡します。 視聴者: 追加の操作は必要ありません。ライブストリームは、ストリーマーのみの表示から、ストリーマーと共同ストリーマーの両方の表示に自動的に切り替わります。 |
共同ストリーミングの終了 | 共同ストリーマー:
ストリーマー: 共同ストリーマーの音声およびビデオストリームのプルを停止します。 アプリケーションサーバー: RTC ルームのストリーム変更イベントを監視します。共同ストリーマーがストリームのプッシュを停止した後、混合ストリームタスク (Task1) を更新し、モードを混合ストリームからバイパスに変更します。 視聴者: 追加の操作は必要ありません。ライブストリームは、ストリーマーと共同ストリーマーの両方の表示から、ストリーマーのみの表示に自動的に切り替わります。 |
RTC ルームのストリーム変更イベントコールバックの監視に関連する API については、「CreateEventSub - ルームメッセージコールバックのサブスクリプションを作成」をご参照ください。
バイパスストリーミングタスクの開始に関連する API については、「StartLiveMPUTask - 混合ストリームタスクを作成 (新規)」をご参照ください。
実装手順
ステップ 1: ストリーマーがブロードキャストを開始
ストリーマーがブロードキャストを開始するための基本的なプロセス:
1. ストリーマーが RTC ルームにストリームをプッシュ
ストリーマーは ARTC SDK を使用して RTC ルームにストリームをプッシュします。
Android
ARTC SDK を使用して RTC ルームに参加し、ストリームをプッシュする詳細な手順については、「実装手順」をご参照ください。
// ARTC 関連クラスをインポート
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);
}
// チャンネルモードを設定
mAliRtcEngine.setChannelProfile(AliRtcEngine.AliRTCSdkChannelProfile.AliRTCSdkInteractiveLive);
mAliRtcEngine.setClientRole(AliRtcEngine.AliRTCSdkClientRole.AliRTCSdkInteractive);
mAliRtcEngine.setAudioProfile(AliRtcEngine.AliRtcAudioProfile.AliRtcEngineHighQualityMode, AliRtcEngine.AliRtcAudioScenario.AliRtcSceneMusicMode);
//ビデオエンコーディングパラメーターを設定
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);
mAliRtcEngine.publishLocalAudioStream(true);
mAliRtcEngine.publishLocalVideoStream(true);
mAliRtcEngine.setDefaultSubscribeAllRemoteAudioStreams(true);
mAliRtcEngine.subscribeAllRemoteAudioStreams(true);
mAliRtcEngine.setDefaultSubscribeAllRemoteVideoStreams(true);
mAliRtcEngine.subscribeAllRemoteVideoStreams(true);
//関連するコールバックを設定
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: 必ず例外を処理してください。SDK はさまざまな回復ポリシーを試しましたが、それでも回復できません。 */
ToastHelper.showToast(VideoChatActivity.this, R.string.video_chat_connection_failed, Toast.LENGTH_SHORT);
} else {
/* TODO: 必要に応じて例外を処理します。通常はデータ統計や UI の変更のために、ビジネスコードが追加されます。 */
}
}
});
}
@Override
public void OnLocalDeviceException(AliRtcEngine.AliRtcEngineLocalDeviceType deviceType, AliRtcEngine.AliRtcEngineLocalDeviceExceptionType exceptionType, String msg){
super.OnLocalDeviceException(deviceType, exceptionType, msg);
/* TODO: 必ず例外を処理してください。SDK がすべての回復ポリシーを試してもデバイスを使用できない場合は、デバイスエラーをユーザーに通知することをお勧めします。 */
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: 必ずこれを処理してください。トークンはまもなく期限切れになります。ビジネスは、現在のチャンネルとユーザーの新しい認証情報を取得するトリガーを起動し、refreshAuthInfo を設定する必要があります。 */
}
@Override
public void onRemoteUserOnLineNotify(String uid, int elapsed){
super.onRemoteUserOnLineNotify(uid, elapsed);
}
// onRemoteUserOffLineNotify コールバックでリモートビデオストリームのレンダリングコントロール設定を削除
@Override
public void onRemoteUserOffLineNotify(String uid, AliRtcEngine.AliRtcUserOfflineReason reason){
super.onRemoteUserOffLineNotify(uid, reason);
}
// onRemoteTrackAvailableNotify コールバックでリモートビデオストリームのレンダリングコントロールを設定
@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);
}
}
}
}
});
}
/* ビジネスでは、異なるデバイスが同じ UserID を競合する状況が発生する可能性があるため、これも処理する必要があります */
@Override
public void onBye(int code){
handler.post(new Runnable() {
@Override
public void run() {
String msg = "onBye code:" + code;
}
});
}
};
mAliRtcEngine.setRtcEngineEventListener(mRtcEngineEventListener);
mAliRtcEngine.setRtcEngineNotify(mRtcEngineNotify);
//ローカルプレビュー
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();
//RTC ルームに参加
mAliRtcEngine.joinChannel(token, null, null, null);
iOS
ARTC SDK を使用して RTC ルームに参加し、ストリームをプッシュする詳細な手順については、「実装手順」をご参照ください。
// ARTC 関連クラスをインポート
import AliVCSDK_ARTC
private var rtcEngine: AliRtcEngine? = nil
// エンジンを作成してコールバックを設定
let engine = AliRtcEngine.sharedInstance(self, extras:nil)
...
self.rtcEngine = engine
// チャンネルモードを設定
engine.setChannelProfile(AliRtcChannelProfile.interactivelive)
engine.setClientRole(AliRtcClientRole.roleInteractive)
engine.setAudioProfile(AliRtcAudioProfile.engineHighQualityMode, audio_scene: AliRtcAudioScenario.sceneMusicMode)
//ビデオエンコーディングパラメーターを設定
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)
engine.publishLocalVideoStream(true)
engine.publishLocalAudioStream(true)
engine.setDefaultSubscribeAllRemoteAudioStreams(true)
engine.subscribeAllRemoteAudioStreams(true)
engine.setDefaultSubscribeAllRemoteVideoStreams(true)
engine.subscribeAllRemoteVideoStreams(true)
//関連するコールバックを設定
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) {
// リモートユーザーがオンラインになる
"onRemoteUserOlineNotify uid: \(uid)".printLog()
}
func onRemoteUserOffLineNotify(_ uid: String, offlineReason reason: AliRtcUserOfflineReason) {
// リモートユーザーがオフラインになる
"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: 必ずこれを処理してください。トークンはまもなく期限切れになります。ビジネスは、現在のチャンネルとユーザーの新しい認証情報を取得するトリガーを起動し、refreshAuthInfo を設定する必要があります。 */
}
func onAuthInfoExpired() {
"onAuthInfoExpired".printLog()
/* TODO: 必ずこれを処理してください。トークンが無効であることを通知し、会議から退出してエンジンを解放します。 */
}
func onBye(_ code: Int32) {
"onBye code: \(code)".printLog()
/* TODO: 必ずこれを処理してください。ビジネスでは、異なるデバイスが同じ UserID を競合する状況が発生する可能性があります。 */
}
func onLocalDeviceException(_ deviceType: AliRtcLocalDeviceType, exceptionType: AliRtcLocalDeviceExceptionType, message msg: String?) {
"onLocalDeviceException deviceType: \(deviceType) exceptionType: \(exceptionType)".printLog()
/* TODO: 必ずこれを処理してください。SDK がすべての回復ポリシーを試してもデバイスを使用できない場合は、デバイスエラーをユーザーに通知することをお勧めします。 */
}
func onConnectionStatusChange(_ status: AliRtcConnectionStatus, reason: AliRtcConnectionStatusChangeReason) {
"onConnectionStatusChange status: \(status) reason: \(reason)".printLog()
if status == .failed {
/* TODO: 必ずこれを処理してください。SDK がすべての回復ポリシーを試しても回復できない場合は、ユーザーに通知することをお勧めします。 */
}
else {
/* TODO: 必要に応じてこれを処理します。通常はデータ統計や UI の変更のために、ビジネスコードを追加します。 */
}
}
}
//ローカルプレビュー
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()
//RTC ルームに参加
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)
}
2. アプリケーションサーバーが転送タスクを開始して RTC ルームのストリームを CDN に転送
アプリケーションサーバーは、RTC ルームメッセージコールバックのサブスクリプションを作成して、ルーム内のストリーマープッシュストリームイベントを監視します。RTC ルームメッセージのサブスクライブに関する詳細な API 情報については、「CreateEventSub - ルームメッセージコールバックのサブスクリプションを作成」をご参照ください。
ストリーマーが RTC ルームにストリームをプッシュしたという通知を受け取った後、バイパスストリーミング OpenAPI StartLiveMPUTask を呼び出して、RTC ルームから CDN にストリームを転送します。バイパスストリーミング API の詳細については、「StartLiveMPUTask - 混合ストリームタスクを作成 (新規)」をご参照ください。
説明ストリーマーがブロードキャストを開始するとき、
MixModeを 0 に設定して、トランスコーディングなしの単一ストリーム転送を示すことができます。API にはライブストリーミングのアップストリーミング URL が必要で、RTMP プロトコルのみをサポートします。この URL の生成方法については、「アップストリーミング URL と再生 URL を生成する」をご参照ください。アプリケーションサーバーは CDN ストリームプッシュコールバックを監視します。ストリームが CDN に転送された後、ライブストリーミング再生 URL を配信して、視聴者に再生を開始するよう通知します。CDN ストリームプッシュコールバックの詳細については、「コールバック設定」をご参照ください。
3. 視聴者が ApsaraVideo Player SDK を使用してストリームをプルして再生
視聴者がアプリケーションサーバーからストリームプル通知を受け取ると、ApsaraVideo Player インスタンスを作成し、ライブストリーミング再生 URL を使用して再生します。プレーヤー API と使用方法の詳細については、「ApsaraVideo Player SDK」をご参照ください。
通常の視聴者向けの CDN 再生 URL を RTMP フォーマットから HTTP-FLV フォーマットに変更することを推奨します。どちらも同じコンテンツを含んでいますが、使用する伝送プロトコルが異なります。HTTP は主流のインターネットプロトコルとして、より成熟したネットワーク最適化基盤を持ち、デフォルトポート 80/443 を使用するため、ファイアウォールを通過しやすくなっています。RTMP プロトコルは古く、一般的なポート 1935 が制限される可能性があり、再生の安定性に影響を与えることがあります。全体として、HTTP-FLV は互換性と再生体験 (スタッタリングや遅延など) の点で RTMP よりも優れているため、最初の選択肢として HTTP-FLV を使用することをお勧めします。
Android
AliPlayer aliPlayer = AliPlayerFactory.createAliPlayer(context);
aliPlayer.setAutoPlay(true);
UrlSource urlSource = new UrlSource();
urlSource.setUri("http://test.alivecdn.com/live/streamId.flv?auth_key=XXX"); // 視聴者の CDN ストリーミング URL。
aliPlayer.setDataSource(urlSource);
aliPlayer.prepare();iOS
self.cdnPlayer = [[AliPlayer alloc] init];
self.cdnPlayer.delegate = self;
self.cdnPlayer.autoPlay = YES;
AVPUrlSource *source = [[AVPUrlSource alloc] urlWithString:@""http://test.alivecdn.com/live/streamId.flv?auth_key=XXX"];
[self.cdnPlayer setUrlSource:source];
[self.cdnPlayer prepare];
ステップ 2: 視聴者の共同ストリーミング
ストリーマーと視聴者の共同ストリーミングの基本的なプロセス:
1. 共同ストリーマーが CDN ストリームの再生を停止し、RTC ルームにストリームをプッシュ
共同ストリーマーは ApsaraVideo Player の CDN ストリーム再生を停止し、プレーヤーエンジンを破棄します。
Android
aliPlayer.stop(); aliPlayer = nul;iOS
[self.cdnPlayer stop]; [self.cdnPlayer clearScreen]; self.cdnPlayer.playerView = nil;共同ストリーマーが RTC ルームにストリームをプッシュ: 共同ストリーマーが RTC ルームにストリームをプッシュするプロセスは、上記で説明したストリーマーのプロセスと同じです。詳細については、「RTC ルームにストリームをプッシュ」をご参照ください。
共同ストリーマーがストリーマーのレンダリングビューを設定します。
Android
エンジンを初期化する際に、対応するコールバック
mAliRtcEngine.setRtcEngineNotifyを設定します。onRemoteTrackAvailableNotifyコールバックでリモートユーザーのリモートビューを設定する必要があります。サンプルコードは次のとおりです:@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); } } }); }iOS
リモートユーザーがストリームをプッシュまたは停止すると、
onRemoteTrackAvailableNotifyコールバックがトリガーされます。このコールバックで、リモートビューを設定または削除します。サンプルコードは次のとおりです:func onRemoteTrackAvailableNotify(_ uid: String, audioTrack: AliRtcAudioTrack, videoTrack: AliRtcVideoTrack) { "onRemoteTrackAvailableNotify uid: \(uid) audioTrack: \(audioTrack) videoTrack: \(videoTrack)".printLog() // リモートユーザーのストリームステータス 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) } }
2. ストリーマーが共同ストリーマーのレンダリングビューを設定
ストリーマーが共同ストリーマーのビューをレンダリングするプロセスは、共同ストリーマーがストリーマーのビューをレンダリングするプロセスと同じです。上記の手順をご参照ください。
3. アプリケーションサーバーがバイパスストリーミングから混合ストリームに更新
アプリケーションサーバーが、共同ストリーマーが RTC ルームにストリームをプッシュしたという通知を受け取った後、UpdateLiveMPUTask - 混合ストリームタスクを更新 (新規) を呼び出して元のバイパスストリーミング taskID を更新し、MixMode を 1 に設定してバイパスから混合ストリームに変更し、ストリーマーと共同ストリーマーの混合ストリームレイアウトを設定します。
ステップ 3: 視聴者が共同ストリーミングを終了
ストリーマーと視聴者間の共同ストリーミングを終了するための基本的なプロセス:
1. 共同ストリーマーが RTC ルームへのストリームプッシュを停止し、プレーヤーで CDN ストリームをプルするように切り替える
共同ストリーマーは RTC ルームから退出 し、RTC エンジンを破棄します。
Android
mAliRtcEngine.stopPreview(); mAliRtcEngine.setLocalViewConfig(null, AliRtcVideoTrackCamera); mAliRtcEngine.leaveChannel(); mAliRtcEngine.destroy(); mAliRtcEngine = null;iOS
self.rtcEngine?.stopPreview() self.rtcEngine?.leaveChannel() AliRtcEngine.destroy() self.rtcEngine = nilプレーヤーエンジンを作成し、CDN ストリームを再生します。手順は、上記で説明した通常の視聴者が ApsaraVideo Player SDK を使用してストリームをプルして再生する場合と同じです。
2. ストリーマーが共同ストリーマーの音声およびビデオストリームのプルを停止
ストリーマーは共同ストリーマーの音声およびビデオストリームの再生を停止し、onRemoteTrackAvailableNotify でリモートビューを削除します。
3. アプリケーションサーバーが混合ストリームからバイパスストリーミングに更新
アプリケーションサーバーが共同ストリーマーが RTC ルームから退出したというコールバック通知を受け取った後、UpdateLiveMPUTask - 混合ストリームタスクを更新 (新規) を呼び出して元の混合ストリーム taskID を更新し、MixMode を 0 に設定して混合ストリームからバイパスに変更します。CDN ライブストリームビューは、ストリーマーと共同ストリーマーの両方を表示する状態から、ストリーマーのみを表示する状態に変わります。