Relay Media(PK Host) - Android
Overview​
The Relay Media feature enables hosts to relay their media (audio, video, screen share) to one or multiple other live streams. This creates cross-stream interactions similar to "PK battles" or collaborative broadcasts.
This feature allows audiences in one live stream to view and hear hosts from another live stream without changing rooms, creating powerful interactive experiences.
Key Concepts​
- Media Relay: The process of transmitting a host's audio/video from one live stream to another
- Source Meeting: The original live stream where the host is broadcasting
- Destination Meeting: The target live stream(s) where the host's media will be relayed
- Relay Kinds: The types of media that can be relayed (video, audio, screen share, etc.)
Sequence Diagram​
Implementation Guide​
1. Requesting Media Relay​
requestMediaRelay()
​
Parameters:
destinationMeetingId
(String
, required) – ID of the target meetingtoken
(String
) – Authentication token for the destination meeting.
If you passnull
, the SDK will use the existing authentication token.kinds
(Array<String>
) – Media types to relay. Ifnull
, all media types will be relayed. Options:"video"
– Camera video"audio"
– Microphone audio"share"
– Screen share video
- Kotlin
- Java
import android.os.Bundle
import android.widget.Button
import androidx.appcompat.app.AppCompatActivity
import live.videosdk.rtc.android.Meeting
class LiveStreamActivity : AppCompatActivity() {
//Initialize meeting
liveStream = VideoSDK.initMeeting(... )
//...
val switchButton: Button = findViewById(R.id.switchLiveStreamButton)
switchButton.setOnClickListener {
requestMediaRelayToAnotherMeeting()
}
fun requestMediaRelayToAnotherMeeting() {
val liveStreamId = "liveStream-B"
// Secure token to authenticate the relay meeting
val relayToken = "secureToken123"
// Specify the media types to relay
val mediaTypes = listOf("video", "audio")
// Request media relay
liveStream.requestMediaRelay(liveStreamId, relayToken, mediaTypes)
}
}
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import androidx.appcompat.app.AppCompatActivity;
import live.videosdk.rtc.android.Meeting;
public class LiveStreamActivity extends AppCompatActivity {
// Initialize meeting
Meeting liveStream = VideoSDK.initMeeting( ...);
//...
Button switchButton = findViewById(R.id.switchLiveStreamButton);
switchButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (liveStream != null) {
requestMediaRelayToAnotherMeeting()
}
}
});
public void requestMediaRelayToAnotherMeeting() {
// Meeting ID to which you want to relay
String liveStreamId = "liveStream-B";
// Secure token to authenticate the relay meeting
String relayToken = "secureToken123";
// Specify the media types to relay
String[] mediaTypes = new String[]{"video", "audio"};
// Request media relay
liveStream.requestMediaRelay(liveStreamId, relayToken, Arrays.asList(mediaTypes));
}
}
2. Handling Relay Requests (Destination Meeting)​
In the destination liveStream, hosts (participants with SEND_AND_RECV
mode) will receive relay requests:
- Kotlin
- Java
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import live.videosdk.rtc.android.Meeting
import live.videosdk.rtc.android.listeners.MeetingEventListener
class LiveStreamView() {
init {
// initialize initialize
liveStream = VideoSDK.initMeeting(... )
// ...
// Set up media relay request listener
liveStream.addEventListener(object : MeetingEventListener() {
override fun onMediaRelayRequestReceived(participantId: String, liveStreamId: String, listener:RelayRequestListener) {
showRelayRequest(participantId, liveStreamId, listener)
}
})
}
private fun showRelayRequest(participantId: String,liveStreamId: String,listener: RelayRequestListener) {
MaterialAlertDialogBuilder(this@MainActivity)
.setTitle("Entry Request : $participantId")
.setMessage("$liveStreamId is requesting to join")
.setPositiveButton("Approve") { dialog, _ -> listener.accept() }
.setNegativeButton("Deny") { dialog, _ -> listener.reject() }
.setCancelable(false)
.show()
}
}
import android.app.AlertDialog;
import live.videosdk.rtc.android.Meeting;
import live.videosdk.rtc.android.listeners.MeetingEventListener;
public class LiveStreamView {
//initialize initialize
Meeting liveStream = VideoSDK.initMeeting( ...);
//...
// Set up media relay request listener
liveStream.addEventListener(new MeetingEventListener() {
@Override
public void onMediaRelayRequestReceived(String participantId, String liveStreamId, RelayRequestListener listener) {
showRelayRequest(participantId, liveStreamId, listener);
}
});
private void showRelayRequest(String participantId, String liveStreamId, RelayRequestListener listener) {
new MaterialAlertDialogBuilder(MainActivity.this)
.setTitle("Entry Request :"+ participantId)
.setMessage(liveStreamId + " is requesting to join")
.setPositiveButton("Approve", (dialog, which) -> listener.accept())
.setNegativeButton("Deny", (dialog, which) -> listener.reject())
.setCancelable(false)
.show();
}
}
3. Handling Request Responses (Source Meeting)​
In the source liveStream, track the response to your relay requests:
- Kotlin
- Java
import android.content.Context
import android.widget.Toast
import live.videosdk.rtc.android.Meeting
import live.videosdk.rtc.android.listeners.MeetingEventListener
class LiveStreamView() {
//initialize initialize
liveStream = VideoSDK.initMeeting(... )
init {
// Set up media relay request response listener
liveStream.addEventListener(object : MeetingEventListener() {
override fun onMediaRelayRequestResponse(participantId: String, decision: String) {
handleMediaRelayResponse(participantId, decision)
}
})
}
// Handling the media relay request response
private fun handleMediaRelayResponse(participantId: String, decision: String) {
if (decision == "accepted") {
// Relay request accepted by participant
println("Relay request accepted by $participantId")
} else {
// Relay request rejected by participant
println("Relay request rejected by $participantId")
}
}
}
import android.content.Context;
import android.widget.Toast;
import live.videosdk.rtc.android.Meeting;
import live.videosdk.rtc.android.listeners.MeetingEventListener;
public class LiveStreamView {
// initialize meeting
Meeting liveStream = VideoSDK.initMeeting( ...);
// ...
// Set up media relay request response listener
liveStream.addEventListener(new MeetingEventListener() {
@Override
public void onMediaRelayRequestResponse(String participantId, String decision) {
handleMediaRelayResponse(participantId, decision);
}
});
// Handling the media relay request response
private void handleMediaRelayResponse(String participantId, String decision) {
if ("accepted".equals(decision)) {
// Relay request accepted by participant
System.out.println("Relay request accepted by " + participantId);
} else {
// Relay request rejected by participant
System.out.println("Relay request rejected by " + participantId);
}
}
}
4. Tracking Active Relays​
When a relay successfully starts:
- Kotlin
- Java
import android.util.Log
import live.videosdk.rtc.android.Meeting
import live.videosdk.rtc.android.listeners.MeetingEventListener
class LiveStreamView() {
// initialize meeting
liveStream = VideoSDK.initMeeting(... )
// ...
companion object {
private const val TAG = "LiveStreamView"
}
init {
// Set up media relay started listener
liveStream.addEventListener(object : MeetingEventListener() {
override fun onMediaRelayStarted(liveStreamId: String) {
handleMediaRelayStarted(liveStreamId)
}
})
}
// Called when media relay starts
private fun handleMediaRelayStarted(liveStreamId: String) {
Log.d(TAG, "Media relay started to $liveStreamId")
// You can update UI or handle logic here
}
}
import android.util.Log;
import live.videosdk.rtc.android.Meeting;
import live.videosdk.rtc.android.listeners.MeetingEventListener;
public class LiveStreamView {
// initialize meeting
Meeting liveStream = VideoSDK.initMeeting( ...);
// ...
// Set up media relay started listener
liveStream.addEventListener(new MeetingEventListener() {
@Override
public void onMediaRelayStarted(String liveStreamId) {
handleMediaRelayStarted(liveStreamId);
}
});
// Called when media relay starts
private void handleMediaRelayStarted(String liveStreamId) {
Log.d(TAG, "Media relay started to " + liveStreamId);
//...
}
}
5. Stopping Media Relay​
To stop an ongoing relay:
stopMediaRelay()
​
Parameters:
liveStreamId
(string): ID of the liveStream where the relay should stop
- Kotlin
- Java
import android.os.Bundle
import android.widget.Button
import androidx.appcompat.app.AppCompatActivity
import live.videosdk.rtc.android.Meeting
class LiveStreamActivity : AppCompatActivity() {
// Initialize meeting
liveStream = VideoSDK.initMeeting(... )
//..
val stopRelayButton = findViewById<Button>(R.id.btnStopRelay)
stopRelayButton.setOnClickListener {
handleStopMediaRelay()
}
// Called to stop media relay
private fun handleStopMediaRelay() {
val liveStreamId = "liveStream-B"
liveStream.stopMediaRelay(liveStreamId)
}
}
import android.view.View;
import android.widget.Button;
import live.videosdk.rtc.android.Meeting;
public class LiveStreamView {
private Meeting meeting;
// initialize meeting
Meeting liveStream = VideoSDK.initMeeting( ...);
// ...
// Set click listener for the Stop Media Relay button
stopRelayButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
handleStopMediaRelay();
}
});
// Called to stop media relay
private void handleStopMediaRelay() {
String liveStreamId = "liveStream-B";
liveStream.stopMediaRelay(liveStreamId);
}
}
6. Handling Relay Stop Events​
When a relay stops for any reason:
- Kotlin
- Java
import android.util.Log
import live.videosdk.rtc.android.Meeting
import live.videosdk.rtc.android.listeners.MeetingEventListener
class LiveStreamView() {
// initialize meeting
val liveStream = VideoSDK.initMeeting(... )
// ...
companion object {
private const val TAG = "LiveStreamView"
}
fun setupListeners() {
liveStream.addEventListener(object : MeetingEventListener() {
override fun onMediaRelayStopped(liveStreamId: String, reason: String) {
handleMediaRelayStopped(liveStreamId, reason)
}
})
}
private fun handleMediaRelayStopped(liveStreamId: String, reason: String) {
Log.d(TAG, "Relay to $liveStreamId stopped. Reason: $reason")
// Update UI based on stop reason
when (reason) {
"user_stopped" -> showMessage("You stopped relaying to $liveStreamId")
"connection_lost" -> showMessage("Relay to $liveStreamId ended due to connection issues")
else -> showMessage("Relay to $liveStreamId ended: $reason")
}
}
private fun showMessage(message: String) {
Log.i(TAG, message)
}
}
import android.util.Log;
import live.videosdk.rtc.android.Meeting;
import live.videosdk.rtc.android.listeners.MeetingEventListener;
public class LiveStreamView {
private static final String TAG = "LiveStreamView";
// initialize meeting
Meeting liveStream = VideoSDK.initMeeting( ...);
// ...
public void setupListeners() {
liveStream.addEventListener(new MeetingEventListener() {
@Override
public void onMediaRelayStopped(String liveStreamId, String reason) {
handleMediaRelayStopped(liveStreamId, reason);
}
});
}
private void handleMediaRelayStopped(String liveStreamId, String reason) {
Log.d(TAG, "Relay to " + liveStreamId + " stopped. Reason: " + reason);
// Update UI based on stop reason
switch (reason) {
case "user_stopped":
showMessage("You stopped relaying to " + liveStreamId);
break;
case "connection_lost":
showMessage("Relay to " + liveStreamId + " ended due to connection issues");
break;
default:
showMessage("Relay to " + liveStreamId + " ended: " + reason);
}
}
private void showMessage(String message) {
Log.i(TAG, message);
}
}
7. Handling Relay Errors​
To handle errors that may occur during relay:
- Kotlin
- Java
import android.util.Log
import live.videosdk.rtc.android.Meeting
import live.videosdk.rtc.android.listeners.MeetingEventListener
class LiveStreamView() {
// initialize meeting
val liveStream = VideoSDK.initMeeting(... )
// ...
companion object {
private const val TAG = "LiveStreamView"
}
private fun setupListeners() {
liveStream.addEventListener(object : MeetingEventListener() {
override fun onMediaRelayError(liveStreamId: String, error: String) {
handleMediaRelayError(liveStreamId, error)
}
})
}
private fun handleMediaRelayError(liveStreamId: String, error: String) {
Log.e(TAG, "Relay error to $liveStreamId: $error")
}
}
import android.util.Log;
import live.videosdk.rtc.android.Meeting;
import live.videosdk.rtc.android.listeners.MeetingEventListener;
public class LiveStreamView {
// initialize meeting
Meeting liveStream = VideoSDK.initMeeting( ...);
private static final String TAG = "LiveStreamView";
private void setupListeners() {
liveStream.addEventListener(new MeetingEventListener() {
@Override
public void onMediaRelayError(String liveStreamId, String error) {
handleMediaRelayError(liveStreamId, error);
}
});
}
private void handleMediaRelayError(String liveStreamId, String error) {
Log.e(TAG, "Relay error to " + liveStreamId + ": " + error);
}
}
Use Cases​
- Guest Appearances: Allow popular hosts to make guest appearances in other streams without leaving their audience
- Cross-Stream Competitions: Create "battles" or competitions between hosts in different streams
- Multi-Location Broadcasting: Connect hosts from different physical locations into a shared experience
- Expert Commentary: Bring in subject matter experts to comment on events in another stream
Troubleshooting​
Common Issues​
-
Relay Request Not Received
- Verify both meetings are active
- Check that destination liveStream ID is correct
- Ensure the token has proper permissions
-
Media Not Visible After Acceptance
- Verify network connectivity
- Check that appropriate media kinds were specified
- Ensure the host has enabled their camera/microphone
-
Unexpected Relay Termination
- Check for network connectivity issues
- Verify that both meetings remain active
- Look for error events with specific reasons
API Reference​
Got a Question? Ask us on discord