ApsaraVideo Live supports the battle feature. It allows multiple streamers across different live rooms to start a real-time competition, which viewers can enjoy watching. This topic describes how to start a battle between streamers and provides relevant sample code.
Overview of how to start a battle
Push SDK implements live battles based on ApsaraVideo Real-time Communication (ARTC). This can help users start interactive streaming that engages more people at an ultra-low latency.
Before a battle: Two streamers use an ingest URL based on ARTC to ingest a stream respectively, and viewers pull the streams from CDN.
During a battle: The streamers use a streaming URL based on ARTC to pull the stream of each other at an ultra-low latency.
After a battle: The streamers stop pulling the stream of each other.
As shown in the preceding figure, Viewer C is watching the stream of Streamer A and Viewer D is watching the stream of Streamer B. If Streamer A and Streamer B want to start a battle with each other, they need to perform the following operations.
Streamer A: Streamer A uses the streaming URL of Streamer B to play the stream of Streamer B at an ultra-low latency. At the same time, Streamer A performs the stream mixing operation to mix the stream of Streamer A and the stream of Streamer B into a single stream, which Viewer C can pull from CDN for playback.
Streamer B: Streamer B uses the streaming URL of Streamer A to play the stream of Streamer A at an ultra-low latency. At the same time, Streamer B performs the stream mixing operation to mix the stream of Streamer B and the stream of Streamer A into a single stream, which Viewer D can pull from CDN for playback.
Viewer C and Viewer D do not need to perform any operations. The mixed stream is automatically displayed to them.
Usage notes
The following section describes how to use the interactive edition of Push SDK to start a battle.
For more information about how to integrate the interactive edition of Push SDK, see the following topics:
Step 1: Enable the co-streaming feature
For information about how to enable the co-streaming feature, see Get started with co-streaming.
Step 2: Generate ingest and streaming URLs for a battle
You can construct ingest and streaming URLs of the streamers in a battle and the CDN streaming URL of viewers by using the concatenation rules. For more information, see Ingest and streaming URLs of different streamers in battle scenarios and CDN streaming URLs of viewers.
Step 3: Start stream ingest for Streamer A
Create an AlivcLivePushConfig object
Create an AlivcLivePushConfig object that is used to configure stream ingest. Set livePushMode to AlivcLivePushInteractiveMode and configure parameters such as the resolution, frame rate, and bitrate.
ImportantIf you want to implement co-streaming between a native client on Android or iOS and the web, the HTML5 compatibility mode must be enabled on the native client. Otherwise, users on the web are seeing a black screen sent from users on the native client. To enable the HTML5 compatibility mode, call AlivcLivePushConfig#setH5CompatibleMode on the Native client. For more information, see the API reference of the native SDK.
Sample code for Android:
// Initialize the class for stream ingest configurations. mAlivcLivePushConfig = new AlivcLivePushConfig(); // Specify the stream ingest mode. By default, the regular stream ingest mode is used. mAlivcLivePushConfig.setLivePushMode(AlivcLiveMode.AlivcLiveInteractiveMode); // Specify the resolution. The default resolution is 540p. mAlivcLivePushConfig.setResolution(AlivcResolutionEnum.RESOLUTION_540P); // Specify the frame rate. The default frame rate is 20 frames per second (FPS). mAlivcLivePushConfig.setFps(AlivcFpsEnum.FPS_25); // Specify the group of pictures (GOP) size. Unit: seconds. The default GOP size is 2 seconds. mAlivcLivePushConfig.setVideoEncodeGop(AlivcVideoEncodeGopEnum.GOP_TWO); // Specify whether to enable adaptive bitrate streaming. The default value is true. mAlivcLivePushConfig.setEnableBitrateControl(true); // Specify the screen orientation. The default screen orientation is portrait. You can configure the settings that allow you to press the Home key to change the orientation to landscape left or landscape right. mAlivcLivePushConfig.setPreviewOrientation(AlivcPreviewOrientationEnum.ORIENTATION_PORTRAIT); // Specify the audio encoding format. The default format is AAC-LC. mAlivcLivePushConfig.setAudioProfile(AlivcAudioAACProfileEnum.AAC_LC); // Specify the video encoding mode. By default, hardware encoding is used. mAlivcLivePushConfig.setVideoEncodeMode(AlivcEncodeModeEnum.Encode_MODE_HARD); // Specify the audio encoding mode. By default, software encoding is used. mAlivcLivePushConfig.setAudioEncodeMode(AlivcEncodeModeEnum.Encode_MODE_SOFT); // Specify whether to use the front camera or the rear camera. By default, the front camera is used. mAlivcLivePushConfig.setCameraType(AlivcLivePushCameraTypeEnum.CAMERA_TYPE_FRONT); // Specify the image that is ingested when your app is switched to the background or video stream ingest is paused. mAlivcLivePushConfig.setPausePushImage("TODO: Image Path"); // Specify the image that is ingested in poor network conditions. mAlivcLivePushConfig.setNetworkPoorPushImage("TODO: Image Path");
Sample code for iOS:
AlivcLivePushConfig *rtcPushConfig = [[AlivcLivePushConfig alloc] init]; rtcPushConfig.livePushMode = AlivcLivePushInteractiveMode; rtcPushConfig.resolution = AlivcLivePushResolution540P; rtcPushConfig.fps = AlivcLivePushFPS20; rtcPushConfig.enableAutoBitrate = true; rtcPushConfig.orientation = AlivcLivePushOrientationPortrait; rtcPushConfig.enableAutoResolution = YES;
Create an AlivcLivePusher object
Create an AlivcLivePusher object as the stream ingest instance. Pass in the AlivcLivePushConfig object that was created and configure relevant callbacks.
Sample code for Android:
AlivcLivePusher alivcLivePusher = new AlivcLivePusher(); alivcLivePusher.init(context, alivcLivePushConfig); alivcLivePusher.setLivePushErrorListener(new AlivcLivePushErrorListener() {}); alivcLivePusher.setLivePushInfoListener(new AlivcLivePushInfoListener() {}); alivcLivePusher.setLivePushNetworkListener(new AlivcLivePushNetworkListener() {});
Sample code for iOS:
AlivcLivePusher *rtcPusher = [[AlivcLivePusher alloc] initWithConfig:rtcPushConfig]; [rtcPusher setInfoDelegate:self]; [rtcPusher setErrorDelegate:self]; [rtcPusher setNetworkDelegate:self];
Start stream ingest in battle mode
Use the ingest URL for the battle to ingest a stream. For more information about how to obtain an ingest URL, see Step 2: Generate ingest and streaming URLs for a battle.
Sample code for Android:
alivcLivePusher.startPreview(context, frameLayout, isAnchor); alivcLivePusher.startPushAysnc("artc://live.aliyun.com/push/123?timestamp=1661596947&token=XXX&userId=123&sdkAppId=XXX"); // The ingest URL of Streamer A.
Sample code for iOS:
[rtcPusher startPushWithURL:@"artc://live.aliyun.com/push/123?timestamp=1661596947&token=XXX&userId=123&sdkAppId=XXX"]; // The ingest URL of Streamer A.
Sample code for Windows:
pusher_->startPush("artc://live.aliyun.com/push/123?timestamp=1661596947&token=XXX&userId=123&sdkAppId=XXX");
Step 4: Start stream ingest for Streamer B
Create an AlivcLivePushConfig object
Create an AlivcLivePushConfig object that is used to configure stream ingest. Set livePushMode to AlivcLivePushInteractiveMode and configure parameters such as the resolution, frame rate, and bitrate.
ImportantIf you want to implement co-streaming between a native client on Android or iOS and the web, the HTML5 compatibility mode must be enabled on the native client. Otherwise, users on the web are seeing a black screen sent from users on the native client. To enable the HTML5 compatibility mode, call AlivcLivePushConfig#setH5CompatibleMode on the Native client. For more information, see the API reference of the native SDK.
Sample code for Android:
// Initialize the class for stream ingest configurations. mAlivcLivePushConfig = new AlivcLivePushConfig(); // Specify the stream ingest mode. By default, the regular stream ingest mode is used. mAlivcLivePushConfig.setLivePushMode(AlivcLiveMode.AlivcLiveInteractiveMode); // Specify the resolution. The default resolution is 540p. mAlivcLivePushConfig.setResolution(AlivcResolutionEnum.RESOLUTION_540P); // Specify the frame rate. The default frame rate is 20 frames per second (FPS). mAlivcLivePushConfig.setFps(AlivcFpsEnum.FPS_25); // Specify the group of pictures (GOP) size. Unit: seconds. The default GOP size is 2 seconds. mAlivcLivePushConfig.setVideoEncodeGop(AlivcVideoEncodeGopEnum.GOP_TWO); // Specify whether to enable adaptive bitrate streaming. The default value is true. mAlivcLivePushConfig.setEnableBitrateControl(true); // Specify the screen orientation. The default screen orientation is portrait. You can configure the settings that allow you to press the Home key to change the orientation to landscape left or landscape right. mAlivcLivePushConfig.setPreviewOrientation(AlivcPreviewOrientationEnum.ORIENTATION_PORTRAIT); // Specify the audio encoding format. The default format is AAC-LC. mAlivcLivePushConfig.setAudioProfile(AlivcAudioAACProfileEnum.AAC_LC); // Specify the video encoding mode. By default, hardware encoding is used. mAlivcLivePushConfig.setVideoEncodeMode(AlivcEncodeModeEnum.Encode_MODE_HARD); // Specify the audio encoding mode. By default, software encoding is used. mAlivcLivePushConfig.setAudioEncodeMode(AlivcEncodeModeEnum.Encode_MODE_SOFT); // Specify whether to use the front camera or the rear camera. By default, the front camera is used. mAlivcLivePushConfig.setCameraType(AlivcLivePushCameraTypeEnum.CAMERA_TYPE_FRONT); // Specify the image that is ingested when your app is switched to the background or video stream ingest is paused. mAlivcLivePushConfig.setPausePushImage("TODO: Image Path"); // Specify the image that is ingested in poor network conditions. mAlivcLivePushConfig.setNetworkPoorPushImage("TODO: Image Path");
Sample code for iOS:
AlivcLivePushConfig *rtcPushConfig = [[AlivcLivePushConfig alloc] init]; rtcPushConfig.livePushMode = AlivcLivePushInteractiveMode; rtcPushConfig.resolution = AlivcLivePushResolution540P; rtcPushConfig.fps = AlivcLivePushFPS20; rtcPushConfig.enableAutoBitrate = true; rtcPushConfig.orientation = AlivcLivePushOrientationPortrait; rtcPushConfig.enableAutoResolution = YES;
Create an AlivcLivePusher object
Create an AlivcLivePusher object as the stream ingest instance. Pass in the AlivcLivePushConfig object that was created and configure relevant callbacks.
Sample code for Android:
AlivcLivePusher alivcLivePusher = new AlivcLivePusher(); alivcLivePusher.init(context, alivcLivePushConfig); alivcLivePusher.setLivePushErrorListener(new AlivcLivePushErrorListener() {}); alivcLivePusher.setLivePushInfoListener(new AlivcLivePushInfoListener() {}); alivcLivePusher.setLivePushNetworkListener(new AlivcLivePushNetworkListener() {});
Sample code for iOS:
AlivcLivePusher *rtcPusher = [[AlivcLivePusher alloc] initWithConfig:rtcPushConfig]; [rtcPusher setInfoDelegate:self]; [rtcPusher setErrorDelegate:self]; [rtcPusher setNetworkDelegate:self];
Start stream ingest in battle mode
Use the ingest URL for the battle to ingest a stream. For more information about how to obtain an ingest URL, see Step 2: Generate ingest and streaming URLs for a battle.
Sample code for Android:
alivcLivePusher.startPreview(context, frameLayout, isAnchor); alivcLivePusher.startPushAysnc("artc://live.aliyun.com/push/456?timestamp=1661596947&token=XXX&userId=456&sdkAppId=XXX"); // The ingest URL of Streamer B.
Sample code for iOS:
[rtcPusher startPushWithURL:@"artc://live.aliyun.com/push/456?timestamp=1661596947&token=XXX&userId=456&sdkAppId=XXX"]; // The ingest URL of Streamer B.
Sample code for Windows:
pusher_->startPush("artc://live.aliyun.com/push/456?timestamp=1661596947&token=XXX&userId=456&sdkAppId=XXX");
Step 5: Start the battle
Streamer A calls AlivcLivePlayer to play the stream of Streamer B, and Streamer B calls AlivcLivePlayer to play the stream of Streamer A.
Streamer A and Streamer B play the stream of each other and start the battle.
Streamer A:
Sample code for Android:
AlivcLivePlayConfig config = new AlivcLivePlayConfig(); config.isFullScreen = isAnchor; AlivcLivePlayer alivcLivePlayer = new AlivcLivePlayerImpl(context, AlivcLiveMode.AlivcLiveInteractiveMode); alivcLivePlayer.setPlayInfoListener(new AlivcLivePlayInfoListener() {}); mAlivcLivePlayer.setupWithConfig(config); mAlivcLivePlayer.setPlayView(frameLayout); alivcLivePlayer.startPlay("artc://live.aliyun.com/play/456?timestamp=1661596947&token=XXX&userId=456&sdkAppId=XXX"); // The streaming URL of Streamer B.
Sample code for iOS:
AlivcLivePlayer *rtcPlayer = [[AlivcLivePlayer alloc] init]; [rtcPlayer setLivePlayerDelegate:self]; [rtcPlayer setPlayView:self.playerView playCofig:self.rtcPlayConfig]; [rtcPlayer startPlayWithURL:@"artc://live.aliyun.com/play/456?timestamp=1661596947&token=XXX&userId=456&sdkAppId=XXX"]; // The streaming URL of Streamer B.
Sample code for Windows:
player_ = AlivcLivePlayer::Create(""); if (player_) { AlivcLivePlayConfig config; player_->setupWithConfig(config); player_->startPlay("artc://live.aliyun.com/play/456?timestamp=1661596947&token=XXX&userId=456&sdkAppId=XXX"); player_->setPlayView(hwnd, width, height); }
Streamer B:
Sample code for Android:
AlivcLivePlayConfig config = new AlivcLivePlayConfig(); config.isFullScreen = isAnchor; AlivcLivePlayer alivcLivePlayer = new AlivcLivePlayerImpl(context, AlivcLiveMode.AlivcLiveInteractiveMode); alivcLivePlayer.setPlayInfoListener(new AlivcLivePlayInfoListener() {}); mAlivcLivePlayer.setupWithConfig(config); mAlivcLivePlayer.setPlayView(frameLayout); alivcLivePlayer.startPlay("artc://live.aliyun.com/play/123?timestamp=1661596947&token=XXX&userId=123&sdkAppId=XXX"); // The streaming URL of Streamer A.
Sample code for iOS:
AlivcLivePlayer *rtcPlayer = [[AlivcLivePlayer alloc] init]; [rtcPlayer setLivePlayerDelegate:self]; [rtcPlayer setPlayView:self.playerView playCofig:self.rtcPlayConfig]; [rtcPlayer startPlayWithURL:@"artc://live.aliyun.com/play/123?timestamp=1661596947&token=XXX&userId=123&sdkAppId=XXX"]; // The streaming URL of Streamer A.
Sample code for Windows:
player_ = AlivcLivePlayer::Create(""); if (player_) { AlivcLivePlayConfig config; player_->setupWithConfig(config); player_->startPlay("artc://live.aliyun.com/play/123?timestamp=1661596947&token=XXX&userId=123&sdkAppId=XXX"); player_->setPlayView(hwnd, width, height); }
Streamer A and Streamer B update the mixed stream, respectively.
In order to ensure that Viewer C and Viewer D can watch the stream in which Streamer A and Streamer B battle, Streamer A and Streamer B must perform the stream mixing operation, respectively. This way, the streams of Streamer A and Streamer B can be mixed into a single stream, which the viewers can pull from CDN for playback.
Specifically, Streamer A and Streamer B call the setLiveMixTranscodingConfig method to start a mixed-stream transcoding task and specify the streams to be mixed, respectively. Parameters such as the resolution and frame rate of the mixed stream are those that the streamers configured in AlivcLivePushConfig when creating the AlivcLivePusher object. For example, if Streamer A set the resolution to 720p in AlivcLivePushConfig when creating the AlivcLivePusher object, the resolution of the mixed stream for Streamer A is 720p.
Sample code for Android:
AlivcLiveTranscodingConfig transcodingConfig = new AlivcLiveTranscodingConfig(); AlivcLiveMixStream anchorMixStream = new AlivcLiveMixStream(); anchorMixStream.setUserId(123); anchorMixStream.setX(0); anchorMixStream.setY(0); anchorMixStream.setWidth(mAlivcLivePushConfig.getWidth()); anchorMixStream.setHeight(mAlivcLivePushConfig.getHeight()); anchorMixStream.setZOrder(1); AlivcLiveMixStream audienceMixStream = new AlivcLiveMixStream(); audienceMixStream.setUserId(456); audienceMixStream.setX((int) mAudienceFrameLayout.getX() / 3); audienceMixStream.setY((int) mAudienceFrameLayout.getY() / 3); audienceMixStream.setWidth(mAudienceFrameLayout.getWidth() / 2); audienceMixStream.setHeight(mAudienceFrameLayout.getHeight() / 2); audienceMixStream.setZOrder(2); ArrayList<AlivcLiveMixStream> mixStreams = new ArrayList<>(); mixStreams.add(anchorMixStream); mixStreams.add(audienceMixStream); transcodingConfig.setMixStreams(mixStreams); alivcLivePusher.setLiveMixTranscodingConfig(transcodingConfig);
Sample code for iOS:
AlivcLiveTranscodingConfig *liveTranscodingConfig = [[AlivcLiveTranscodingConfig alloc] init]; AlivcLiveMixStream *anchorMixStream = [[AlivcLiveMixStream alloc] init]; anchorMixStream.userId = 123; anchorMixStream.x = 0; anchorMixStream.y = 0; anchorMixStream.width = [self.rtcPushConfig getPushResolution].width; anchorMixStream.height = [self.rtcPushConfig getPushResolution].height; anchorMixStream.zOrder = 1; AlivcLiveMixStream *audienceMixStream = [[AlivcLiveMixStream alloc] init]; audienceMixStream.userId = 456; audienceMixStream.x = 100; audienceMixStream.y = 200; audienceMixStream.width = 200; audienceMixStream.height = 300; audienceMixStream.zOrder = 2; liveTranscodingConfig.mixStreams = [NSArray arrayWithObjects:anchorMixStream, audienceMixStream, nil]; [self.rtcPusher setLiveMixTranscodingConfig:liveTranscodingConfig];
Sample code for Windows:
AlivcLiveTranscodingConfig liveTranscodingConfig; AlivcLiveMixStream anchorMixStream ; anchorMixStream.userId = userA; anchorMixStream.x = 0; anchorMixStream.y = 0; anchorMixStream.width = width; anchorMixStream.height = height; anchorMixStream.zOrder = 1; AlivcLiveMixStream audienceMixStream; audienceMixStream.userId = userD; audienceMixStream.x = 100; audienceMixStream.y = 200; audienceMixStream.width = 200; audienceMixStream.height = 300; audienceMixStream.zOrder = 2; liveTranscodingConfig.mixStreams.Add(anchorMixStream); liveTranscodingConfig.mixStreams.Add(audienceMixStream); pusher_->setLiveMixTranscodingConfig(&liveTranscodingConfig);
Step 6: End the battle
Streamer A and Streamer B stop playing the stream of each other.
The battle is stopped. Streamer A and Streamer B are ingesting their own separate stream again.
Sample code for Android:
alivcLivePlayer.stopPlay(); alivcLivePlayer.destroy(); alivcLivePlayer = null;
Sample code for iOS:
[self.rtcPlayer stopPlay]; self.rtcPlayer = nil;
Sample code for Windows:
if (player_) { player_->stopPlay(); player_->destroy(); player_ = nullptr; }
Streamer A and Streamer B update the mixed stream, respectively.
After the battle ends, Streamer A and Streamer B need to call the setLiveMixTranscodingConfig method and pass in an empty value, respectively. This way, stream mixing is stopped.
Take note that if Viewer C or D only wants to watch the stream of Streamer A or B, Streamer A or B can also call the setLiveMixTranscodingConfig method and pass in an empty value to stop stream mixing.
WarningIf the streamer is still in the room but does not require stream mixing, make sure that stream mixing is stopped. If stream mixing is not stopped, the stream mixing module that continues to run will incur unnecessary fees.
Sample code for Android:
alivcLivePusher.setLiveMixTranscodingConfig(null);
Sample code for iOS:
[self.rtcPusher setLiveMixTranscodingConfig:nil];
Sample code for Windows:
pusher_->setLiveMixTranscodingConfig(nullptr);