Talk to us
Talk to us
menu

How to Implement High-Monetization PK battles in Live Streams?

How to Implement High-Monetization PK battles in Live Streams?

Live streaming, as a typical scene of entertainment and social interaction going global, has been developing for many years and is facing increasingly serious problems of product homogenisation. How can social applications find new breakthroughs in the market, increase business revenue and establish their own differentiating advantages? Adding more ways to play in live streaming scenarios is one of the important methods.

PK gameplay, a key tool to boost the atmosphere in live streams, is highly popular among various interaction methods. It drives host popularity, fosters fan engagement, and allows hosts to redirect followers to each other. Adding PK enhances content diversity, enriches user-host interaction, and makes live streams more entertaining. This article will show you how to quickly implement this feature in Android live streams using ZEGO Express SDK.

Prerequisites

Before you begin, make sure you complete the following:

  • Complete SDK integration by referring to Quick Start doc.
  • Download the demo that comes with this doc.
  • Please contact technical support to activate the Stream Mixing service.
  • Activate the In-app Chat service
service management

Preview the effect

You can achieve the following effect with the demo provided in this doc:https://www.zegocloud.com/docs/live-streaming/implement-pk-battles?platform=android&language=java

display of the effect

Send PK battle invitations

A similar approach to call invitations is used here to implement PK battle invitations:

Based on the call invitation (signaling) feature provided by the In-app Chat (referred to as ZIM SDK), which provides the capability of call invitation, allowing you to send, cancel, accept, and reject an invitation, you can achieve PK battle invitations, room invitations, and similar functions – you can use the extendedData field provided by ZIMCallInviteConfig, which allows you to customize the type of this invitation, thus achieving different functions.

For example, you can encode the business agreement into JSON and attach it to the extendedData:

{
    "room_id": "Room10001",
    "user_name": "Alice",
    "type": "start_pkbattles" // or "video_call" , "voice_call"
}

In this way, the receiving user can judge and execute different business logic based on the type field after receiving the invitation.

The process of implementing call invitation based on this is as follows: (taking “Alice invites Bob to a PK battle, Bob accepts and connects the PK battle” as an example)

example of PK battle

For the specific usage of these interfaces, please refer to Call invitation (signaling).

  1. When using this interface to implement PK battle invitations, it is important to note that in the extendedData field of the invitation interface, both parties’ information needs to be passed: A When initiating a PK battle invitation, in addition to the mentioned type, it is also necessary to pass one’s own roomID and userName to the other party – so that the other party knows the relevant information to start the PK battle logic. B When accepting a PK battle invitation, similarly, in addition to the type, one’s own roomID and userName need to be passed.
  2. When inviting for the first time, please call callInvite and set it to advanced mode. In this mode, you need to use callEnd or callQuit to end or quit the PK battle. And you can continue to invite others to join the PK by using callingInvite.
/** Send call invitation to users - Advanced mode */
// Send call invitation
List<String> invitees;  // List of invitees
invitees.add("421234");       // ID of the invitee
ZIMCallInviteConfig config = new ZIMCallInviteConfig(); 
config.timeout = 200; // Timeout for invitation in seconds, range 1-600
// mode represents the call invitation mode, ADVANCED represents setting to advanced mode.
config.mode = ADVANCED;

zim.callInvite(invitees, config, new ZIMCallInvitationSentCallback() {
    @Override
    public void onCallInvitationSent(String callID, ZIMCallInvitationSentInfo info, ZIMError errorInfo) {
        // The callID here is generated by the SDK internally to uniquely identify 
        a call invitation after the user initiates a call; 
        // later, when the initiator cancels the call, or the invitee accepts/rejects the call, this callID will be used.
    }
 });

For more details on these two aspects, refer to the following sections.

Stream publishing & playing for PK

Before starting, please make sure you are familiar with the following concepts:

What is Room/Stream & Stream-Mixing?

  1. What is Room and Stream ?
  • ZEGO Express SDK: the real-time Audio and Video Call SDK provided by ZEGOCLOUD. It can help you provide audio and video services that feature convenient access, high-definition and fluency, cross-platform communication, low latency, and high concurrency.
  • Stream publishing: the process of publishing the audio and video data streams that are captured and packaged to ZEGOCLOUD real-time audio and video cloud.
  • Stream playing: the process of receiving and playing audio and video data streams from ZEGOCLOUD real-time audio and video cloud.
  • Room: the service provided by ZEGOCLOUD for organizing user groups and allowing users in the same room to receive and send real-time audio, video, and messages to each other.
    • Users can publish or play streams only after logging in to a room.
    • Users can receive notifications about changes (such as users joining or leaving a room, and audio and video stream changes) in the room where they are in.
  1. What is Stream-Mixing?

Through the stream-mixing service, multiple published media streams can compose a single stream , which allows audiences to play just one stream to improve quality and reduce performance cost.

For more details, refer to stream mixing.

The PK Battles solution requires the use of stream mixing: stream mixing refers to combining multiple streams into one, so that audiences only need to play this stream to watch the footage of multiple hosts. The necessity and significant advantages of using stream mixing are as follows:

  • Necessity: It ensures relatively real-time synchronization of audio and video when audiences watch multiple hosts, avoiding issues where two hosts have inconsistent delays, resulting in a poor interactive experience.
  • Advantages: The client does not need to decode and play multiple streams, which can save bandwidth on the audience’s side and prevent overheating on low-end devices, further enhancing the viewing experience for audiences.

Before the PK battle starts, each host will publish a stream, and the audience can directly play the stream of the host. However, after the PK battle starts, the method of playing the stream will change:

  1. In addition to publishing their own stream, each host also needs to play the stream of the other host: to achieve real-time audio and video interaction between hosts.
  2. The audience temporarily mutes the single stream of the host: using mute can save bandwidth and quickly restore the single stream image of the host after the PK ends.
  3. Start mixing the streams of the two hosts together: client or server can be used to manage the mixing, which will be explained in detail later.
  4. The audience plays the mixed stream: to watch the interaction between the two hosts.

The above is the basic framework of stream publishing & playing in the PK scenario. In the following sections, we will provide a detailed explanation of this solution based on the logic of the hosts and the audience.

Start PK battle – Host logic

When the host is ready to start the PK battle, the following operations need to be performed:

  1. Play the single stream of the each host

Generally, the stream ID is related to the room ID and user ID. For example, in the accompanying demo of this doc, the stream ID rule is "${roomID}_${userID}_main_host". Therefore, you can concatenate each host’s stream ID using this rule and then call startPlayingStream to play the stream of the opponent. The information of both sides can be obtained from the callback of the invitation interface and the extendedData passed between both sides.

If your PK battle is scheduled and matched by the server and the start PK signal is sent down by the server, you need to include the userID, roomID, userName, and other information of the opponent host when sending the PK start notification to the host on the server side.

  1. Start the stream mixing task

Stream mixing can be initiated by the client or the server, and you can choose accordingly:

  • Initiating stream mixing on the client side has a simpler architecture but requires managing client-side issues like complex networks and potential app exits.
  • In contrast, server-side stream mixing is less affected by client abnormalities, but it involves more complex client-server interactions and requires stronger server-side development skills.

The accompanying demo of this doc uses the “Manual stream mixing initiated by the host client” approach.

Initiating stream mixing from the client

There are some details to note about the stream mixing parameters:

  • Stream mixing layout: During a PK battle, each audience member sees their room’s host on the left. After the PK starts, each host must initiate a stream mixing task, placing their video on the left side of the layout. The layout parameter can be referred to in the following code or you can refer to the Mix the live streams doc for more details on stream mixing layouts.
  • Stream mixing resolution: Taking the default 540p resolution of the host as an example, each single stream has a resolution of width=540, height=960. After combining the two streams side by side, the resolution of the mixed stream should be width=540*2, height=960. If you want to lower the resolution of the mixed stream, you can maintain this aspect ratio and reduce the stream mixing resolution, for example, using a 540*480 resolution with width=540*2/2, height=960/2. Note that if you need to lower the stream mixing resolution, you also need to make corresponding adjustments to the layout parameter. Our demo uses a mixed stream resolution of width=810 and height=720.
  • Stream mixing task ID and stream ID: Usually, each stream mixing task has only one output stream, and the same applies to the PK battle scenario. Therefore, you can use the same ID for both the stream mixing task ID and the stream ID, such as '${roomID}__mix'. This makes it easier to manage the stream mixing tasks in the future.

Here is an example code snippet with the complete stream mixing parameters:


public static final int MIX_VIDEO_WIDTH = 720;
public static final int MIX_VIDEO_HEIGHT = 810;
public static final int MIX_VIDEO_BITRATE = 1500;
public static final int MIX_VIDEO_FPS = 15;

private void updatePKMixTask(IZegoMixerStartCallback callback) {
    if (pkBattleInfo != null) {
        List<String> pkUserStreamList = new ArrayList<>();
        for (PKUser pkUser : pkBattleInfo.pkUserList) {
            if (pkUser.getCallUserState() == ZIMCallUserState.ACCEPTED) {
                pkUserStreamList.add(pkUser.getPKUserStream());
            }
        }

        ZegoMixerVideoConfig videoConfig = new ZegoMixerVideoConfig();
        videoConfig.width = MIX_VIDEO_WIDTH;
        videoConfig.height = MIX_VIDEO_HEIGHT;
        videoConfig.bitrate = MIX_VIDEO_BITRATE;
        videoConfig.fps = MIX_VIDEO_FPS;

        MixLayoutProvider mixLayoutProvider = ZEGOLiveStreamingManager.getInstance().getMixLayoutProvider();
        ArrayList<ZegoMixerInput> mixVideoInputs;
        if (mixLayoutProvider == null) {
            mixVideoInputs = getMixVideoInputs(pkUserStreamList, videoConfig);
        } else {
            mixVideoInputs = mixLayoutProvider.getMixVideoInputs(pkUserStreamList, videoConfig);
        }

        if (task == null) {
            String mixStreamID = ZEGOSDKManager.getInstance().expressService.getCurrentRoomID() + "_mix";

            task = new ZegoMixerTask(mixStreamID);
            task.videoConfig = videoConfig;

            task.setInputList(mixVideoInputs);

            ZegoMixerOutput mixerOutput = new ZegoMixerOutput(mixStreamID);
            ArrayList<ZegoMixerOutput> mixerOutputList = new ArrayList<>();
            mixerOutputList.add(mixerOutput);
            task.setOutputList(mixerOutputList);

            task.enableSoundLevel(true);
        } else {
            task.inputList = mixVideoInputs;
        }

        ZEGOSDKManager.getInstance().expressService.startMixerTask(task, new IZegoMixerStartCallback() {
            @Override
            public void onMixerStartResult(int errorCode, JSONObject data) {
                // 1005026 non_exists_stream_list
                if (errorCode == 0) {
                    updatePKRoomAttributes();
                }
                if (callback != null) {
                    callback.onMixerStartResult(errorCode, data);
                }

            }
        });
    }
}

In the client-initiated stream mixing approach, it is important to check the error code returned when calling the stream mixing interface at this step. If the error code is not 0, it means that the stream mixing has failed. In this case, appropriate actions should be taken on the client-side, such as retrying the stream mixing task, to ensure the normal progress of the PK battle.

customize the mix steam layout

If you want to set the layout for mixing the streams, you can customize the layout by using the setInputList method of ZegoMixerTask. Here we show some simple setting rules.

For example, if you have two people, you can set the layout to have each person occupying half of the screen. You can set it like this:

private ArrayList<ZegoMixerInput> getMixVideoInputs(List<String> streamList, ZegoMixerVideoConfig videoConfig) {
    ArrayList<ZegoMixerInput> inputList = new ArrayList<>();
    if (streamList.size() == 2) {
        for (int i = 0; i < streamList.size(); i++) {
            int left = (videoConfig.width / streamList.size()) * i;
            int top = 0;
            int right = (videoConfig.width / streamList.size()) * (i + 1);
            int bottom = videoConfig.height;
            ZegoMixerInput input = new ZegoMixerInput(streamList.get(i), ZegoMixerInputContentType.VIDEO,
                new Rect(left, top, right, bottom));
            input.renderMode = ZegoMixRenderMode.FILL;
            inputList.add(input);
        }
    } else {
        //...
    }

    return inputList;
}

If you have more than two people, you can set up the layout as you wish. You can set it up like this:

private ArrayList<ZegoMixerInput> getMixVideoInputs(List<String> streamList, ZegoMixerVideoConfig videoConfig) {
    ArrayList<ZegoMixerInput> inputList = new ArrayList<>();
    //...
    if (streamList.size() == 2) {
        for (int i = 0; i < streamList.size(); i++) {
            int left = (videoConfig.width / streamList.size()) * i;
            int top = 0;
            int right = (videoConfig.width / streamList.size()) * (i + 1);
            int bottom = videoConfig.height;
            ZegoMixerInput input = new ZegoMixerInput(streamList.get(i), ZegoMixerInputContentType.VIDEO,
                new Rect(left, top, right, bottom));
            input.renderMode = ZegoMixRenderMode.FILL;
            inputList.add(input);
        }
    } else if (streamList.size() == 3) {
        for (int i = 0; i < streamList.size(); i++) {
            int left, top, right, bottom;
            if (i == 0) {
                left = 0;
                top = 0;
                right = videoConfig.width / 2;
                bottom = videoConfig.height;
            } else if (i == 1) {
                left = videoConfig.width / 2;
                top = 0;
                right = left + videoConfig.width / 2;
                bottom = top + videoConfig.height / 2;
            } else {
                left = videoConfig.width / 2;
                top = videoConfig.height / 2;
                right = left + videoConfig.width / 2;
                bottom = top + videoConfig.height / 2;
            }
            ZegoMixerInput input = new ZegoMixerInput(streamList.get(i), ZegoMixerInputContentType.VIDEO,
                new Rect(left, top, right, bottom));
            input.renderMode = ZegoMixRenderMode.FILL;
            inputList.add(input);
        }
    } else if (streamList.size() == 4 || streamList.size() == 6) {
        int row = 2;
        int maxCellCount = streamList.size() % 2 == 0 ? streamList.size() : (streamList.size() + 1);
        int column = maxCellCount / row;
        int cellWidth = videoConfig.width / column;
        int cellHeight = videoConfig.height / row;
        int left, top, right, bottom;
        for (int i = 0; i < streamList.size(); i++) {
            left = cellWidth * (i % column);
            top = cellHeight * (i < column ? 0 : 1);
            right = left + cellWidth;
            bottom = top + cellHeight;
            ZegoMixerInput input = new ZegoMixerInput(streamList.get(i), ZegoMixerInputContentType.VIDEO,
                new Rect(left, top, right, bottom));
            input.renderMode = ZegoMixRenderMode.FILL;
            inputList.add(input);
        }
    } else if (streamList.size() == 5) {
        for (int i = 0; i < streamList.size(); i++) {
            int left, top, right, bottom;
            if (i == 0) {
                left = 0;
                top = 0;
                right = videoConfig.width / 2;
                bottom = videoConfig.height / 2;
            } else if (i == 1) {
                left = videoConfig.width / 2;
                top = 0;
                right = left + videoConfig.width / 2;
                bottom = top + videoConfig.height / 2;
            } else if (i == 2) {
                left = 0;
                top = videoConfig.height / 2;
                right = left + videoConfig.width / 3;
                bottom = top + videoConfig.height / 2;
            } else if (i == 3) {
                left = videoConfig.width / 3;
                top = videoConfig.height / 2;
                right = left + videoConfig.width / 3;
                bottom = top + videoConfig.height / 2;
            } else {
                left = (videoConfig.width / 3) * 2;
                top = videoConfig.height / 2;
                right = left + videoConfig.width / 3;
                bottom = top + videoConfig.height / 2;
            }
            ZegoMixerInput input = new ZegoMixerInput(streamList.get(i), ZegoMixerInputContentType.VIDEO,
                new Rect(left, top, right, bottom));
            input.renderMode = ZegoMixRenderMode.FILL;
            inputList.add(input);
        }
    } else {
        int row = 3;
        int column = 3;
        int cellWidth = videoConfig.width / column;
        int cellHeight = videoConfig.height / row;
        int left, top, right, bottom;
        for (int i = 0; i < streamList.size(); i++) {
            left = cellWidth * (i % column);
            top = cellHeight * (i < column ? 0 : 1);
            right = left + cellWidth;
            bottom = top + cellHeight;
            ZegoMixerInput input = new ZegoMixerInput(streamList.get(i), ZegoMixerInputContentType.VIDEO,
                new Rect(left, top, right, bottom));
            input.renderMode = ZegoMixRenderMode.FILL;
            inputList.add(input);
        }
    }

    return inputList;
}

So the demo has implemented the default layout for mixing streams: when there are 2 people in a PK, it is a side-by-side layout. When there are more than 2 people, the screen will be divided into two or three rows.

If you need a more complex custom layout, please refer to this complete document on mixing layouts to understand the way of mixing layouts and use the ZEGOLiveStreamingManager.setMixLayoutProvider() in the Demo to modify the layout:

ZEGOLiveStreamingManager.getInstance().setMixLayoutProvider(new MixLayoutProvider() {
    @Override
    public ArrayList<ZegoMixerInput> getMixVideoInputs(List<String> streamList,
        ZegoMixerVideoConfig videoConfig) {
        ArrayList<ZegoMixerInput> inputList = new ArrayList<>();
        // ... your logic
        return inputList;
    }
});

Initiating stream mixing from the server

If you want to manage stream mixing from the server, you need to start these two stream mixing tasks on the server side when the PK battle begins. Refer to the above instructions for setting the stream mixing parameters when initiating from the client.

For details on how to manage stream mixing tasks from the server side, refer to the server-side API:

Note:

  • In the server-initiated stream mixing approach, the server needs to use Callback on logged out room to monitor the client status of the host. If it detects an abnormal exit, it promptly stops the stream mixing task and notifies the host to end the PK battle.
  • If your server does not have a signaling channel to send notifications to the client, we recommend using the Command message (signaling message) in the Send in-room messages server-side API of ZIM to achieve this.
  1. Notifying the audience of the start of PK battle

When the PK battle starts, it is necessary to notify the audience that the PK battle has begun. After receiving the notification, the audience can handle the logic of watching the PK. How to notify the audience? We recommend using the room attribute feature of the ZIM SDK to achieve this.

If you haven’t used this feature before, you can click here to see an introduction

When the PK starts, the host needs to call setRoomAttributes to set the additional attributes of the room, indicating that the room has entered the PK state. It is recommended to include the following information in the room’s additional attributes:

  1. host_user_id: The userID of the host of this room.
  2. request_id: The requestID of this PK.
  3. pk_users: Participating anchors in PK.

When setting the room attributes, make sure to set the isDeleteAfterOwnerLeft parameter to false. This is to prevent the room’s additional attributes from being deleted when the host exits the room abnormally, which would cause the PK battle to be unable to be resumed.

Example code snippet for generating room attributes:

    private void updatePKRoomAttributes() {
        HashMap<String, String> hashMap = new HashMap<>();

        if (ZEGOLiveStreamingManager.getInstance().getHostUser() != null) {
            hashMap.put("host_user_id", ZEGOLiveStreamingManager.getInstance().getHostUser().userID);
        }

        hashMap.put("request_id", pkBattleInfo.requestID);

        List<PKUser> acceptedUsers = new ArrayList<>();
        for (PKUser pkUser : pkBattleInfo.pkUserList) {
            if (pkUser.hasAccepted()) {
                acceptedUsers.add(pkUser);
            }
        }
        for (PKUser pkUser : acceptedUsers) {
            for (ZegoMixerInput zegoMixerInput : task.inputList) {
                if (Objects.equals(pkUser.getPKUserStream(), zegoMixerInput.streamID)) {
                    pkUser.rect = zegoMixerInput.layout;
                }
            }
        }
        hashMap.put("pk_users", acceptedUsers.toString());

        ZIMRoomAttributesSetConfig config = new ZIMRoomAttributesSetConfig();
        config.isDeleteAfterOwnerLeft = false;
        ZEGOSDKManager.getInstance().zimService.setRoomAttributes(hashMap, config,
            new ZIMRoomAttributesOperatedCallback() {
                @Override
                public void onRoomAttributesOperated(String roomID, ArrayList<String> errorKeys, ZIMError errorInfo) {

                }
            });
    }

After the settings are completed, the audience will receive the onRoomAttributesUpdated callback. Now let’s explain the logic on the audience side.

Start PK battle – Audience logic

After receiving the onRoomAttributesUpdated callback, if the audience finds that there are newly added fields related to PK battle, they can start processing the logic of watching PK.

  1. Audience needs to play the mixed stream according to the stream ID rule

In the accompanying demo of this doc, the stream ID rule for mixing streams is ${currentRoomID}__mix. It is recommended that you also design and use this kind of rule related to the room ID.

The method for playing normal streams and mixed streams is the same. The audience can call startPlayingStream to start playing the mixed stream.

There are two details that need to be handled:

  1. It is necessary to listen for the onPlayerStateUpdate callback to determine whether the stream playing is successful. If playing the mixed stream fails, the user needs to be prompted with a loading message and corresponding retry logic should be implemented.
  2. Since it takes some time to generate the mixed stream, the audience may not be able to play the mixed stream immediately. In order to optimize the user experience and avoid black screens, after playing the mixed stream, it is necessary to listen for the onPlayerRecvVideoFirstFrame and onPlayerRecvAudioFirstFrame callbacks. After receiving either of these callbacks, the mixed stream can be rendered. This can avoid black screens.
  3. During PK, the audience needs to mute the single stream of the host

Once the audience successfully plays the mixed stream, they can start watching the PK battle. Since the mixed stream already contains the audio and video of both hosts, the audience doesn’t need to render the single stream of the host during the PK.

Therefore, the audience can use mutePlayStreamAudio and mutePlayStreamVideo to temporarily stop playing the audio and video of the host’s single stream. This can further reduce costs and avoid unnecessary traffic consumption and performance loss on the audience’s devices.

It is not recommended to use stopPlayingStream to stop playing the single stream of the host at this time. If this is done, the audience will need to re-play the stream after the PK ends, and the speed of stream switching will be much slower compared to using mute. This will result in a poor user experience.

End PK battle && Quit PK battle

In the demo, the host can manually click the end button to terminate the PK battle.

When the host clicks the end button, they also need to notify the other host that the PK has ended. This can be achieved by endPKBattle method in ZEGOLivesSreamingManager.When the other host receives this notification, they also need to handle the logic for ending the PK. The difference between quitPKBattle and endPKBattle is that the former only allows the player to quit the PK, while the latter will make everyone stop PK. The following operations need to be performed to end the PK battle:

Host:

  1. Stop playing the single stream of the other host.
  2. End the mixed stream task.
  3. Remove the PK-related attributes from the room attribute.
  4. Adjust the UI to return to the state of a single host’s live stream.

Audience:

When the audience receives the callback onRoomAttributesUpdated indicating that the PK-related attributes have been removed, they can start handling the following logic:

  1. Call stopPlayingStream to stop playing the mixed stream and return to the state of a single host’s live stream.
  2. Since the audience muted the single stream of the host when the PK started, you need to call mutePlayStreamAudio and mutePlayStreamVideo again to unmute.
  3. Adjust the UI to return to the state of a single host’s live stream.

Detect abnormal situations in a PK battle

By leveraging the periodic sending of SEI messages, it can be treated as a “heartbeat” To detect abnormalities during a PK, use a heartbeat mechanism. When SEI messages from the host aren’t received for a set period, it indicates a potential issue.

Logic:

  1. Start a 2-second timer after the PK begins.
  2. Record the last time each host sent an SEI message.
  3. On each timer trigger, check if the time since the last SEI message exceeds a threshold (e.g., 5 seconds). If so, the host’s livestream is deemed abnormal.

If an anomaly is detected, display a “host reconnecting” prompt on the host’s video screen.

See PKBattleView.java

public void onTimeOut(boolean timeout) {
    if (timeout) {
        connectTipsView.setVisibility(VISIBLE);
    } else {
        connectTipsView.setVisibility(GONE);
    }
}

In addition, you also need to define a maximum timeout period. For example, in the Demo, if a PK host has no SEI for more than 60 seconds, all users participating in the PK will remove that exceptional host from the PK.

ZEGOLiveStreamingManager.getInstance().addLiveStreamingListener(new LiveStreamingListener() {
    // ...
    @Override
    public void onPKUserConnecting(String userID, long duration) {
        if (duration >= 60_000) {
            ZEGOSDKUser currentUser = ZEGOSDKManager.getInstance().expressService.getCurrentUser();
            if (!Objects.equals(currentUser.userID, userID)) {
                ZEGOLiveStreamingManager.getInstance().removeUserFromPKBattle(userID);
            } else {
                ZEGOLiveStreamingManager.getInstance().quitPKBattle();
            }
        }
    }
});

Achieve server-side matching process

If you want to challenge a random host to PK, you may need to use the server for matchmaking. For example, the client can send a request to the server, and the server will respond with the user ID of the target host. After receiving this user ID, the client can call the startPKBattle function to send an automatic PK request to the target host using this user ID. When using this interface, the host receiving the PK request will automatically agree to start the PK by default.

Get the device status of the host process

Depending on the specific streaming scenario, different methods are used to obtain the device status.

Scenario 1: During PK, how do hosts obtain the device status of each other?

In this scenario, where hosts are streaming and interacting with each other in real-time, you can use onRemoteCameraStateUpdate and onRemoteMicStateUpdate to obtain the camera and microphone status of the other host.

It is important to note that this feature needs to be enabled by calling setEngineConfig after calling createEngine. Here is an example of the code:

ZegoEngineConfig config = new ZegoEngineConfig();
config.advancedConfig.put("notify_remote_device_unknown_status", "true");
config.advancedConfig.put("notify_remote_device_init_status", "true");
ZegoExpressEngine.setEngineConfig(config);

If your audience is using Interactive Live Streaming to play the stream, you can also use this method. You can further understand the concepts of Live Streaming and Interactive Live Streaming here: Live Streaming vs. Interactive Live Streaming

Scenario 2: Audience obtaining the device status of the host

When playing the stream for Live Streaming or Mixed Stream, it is recommended to use the SEI (Supplemental Enhancement Information) solution to obtain the device status of the host. This includes the following two cases in the PK battle scenario:

  1. In PK mode, the audience plays the mixed stream.
  2. In non-PK mode, the audience plays the host’s single stream.

In this case, the audience cannot receive the callbacks mentioned in “Scenario 1”. Therefore, when the host is publishing the stream, they need to update their device status using sendSEI, and the playing side will receive the callback onPlayerRecvSEI.

What is SEI?

To send SEI, you need to create a timer to periodically send SEI messages. It is recommended to send them every 200ms. In the timer, regularly send the following information to synchronize device status:

{
  'type': 0, // device_state
  'senderID': myUserID, // Due to the mixing of SEI from multiple streams into the mixed stream, you need to add a senderID identifier in the SEI.
  'cam': false, // true:on, false:off
  'mic': true, // true:on, false:off
}

You can refer to the relevant code in the demo for the specific implementation of this part.

Conclusion

Implementing PK battles in the live streaming scenario is a powerful way to enhance user retention and platform revenue.

In addition to the PK game, ZEGOCLOUD addresses the three main pain points of long initial load times, blurred images and lag in live streaming applications, providing a smooth live streaming solution. By integrating interactive live streaming services, it achieves instant loading, seamless ultra-high definition video quality and a one-click video quality enhancement solution. This solution intelligently adapts to the user’s network conditions, phone performance and viewing context to deliver the best video quality. Sign up today and enjoy 10,000 minutes free of charge.

Let’s Build APP Together

Start building with real-time video, voice & chat SDK for apps today!

Talk to us

Take your apps to the next level with our voice, video and chat APIs

Free Trial
  • 10,000 minutes for free
  • 4,000+ corporate clients
  • 3 Billion daily call minutes

Stay updated with us by signing up for our newsletter!

Don't miss out on important news and updates from ZEGOCLOUD!

* You may unsubscribe at any time using the unsubscribe link in the digest email. See our privacy policy for more information.