Skip to main content
The Red5 Android SDK enables native Android applications to publish and subscribe to live streams over WebRTC with sub-second latency. Built on the WHIP/WHEP protocol stack, the SDK handles ICE negotiation, camera and microphone capture, and license validation. It works with both Red5 Cloud (Stream Manager) deployments and standalone Red5 Pro servers, and supports conferencing, PubNub chat, real-time stats collection, and Picture-in-Picture mode.

Requirements

  • A valid Red5 Pro SDK license key
  • Camera and microphone permissions for publishing
  • Internet permission for both publishing and subscribing
  • Android API level compatible with the WebRTC dependency

Installation

Download the Android Archive (.aar) from red5-cloud-sdk.cachefly.net and place it in your app/libs folder. Add the following dependencies to your app’s build.gradle:
// Red5 SDK
implementation fileTree(include: ['*.aar'], dir: 'libs')
implementation 'androidx.annotation:annotation:1.9.1'
implementation 'com.google.code.gson:gson:2.13.2'
implementation 'com.squareup.okhttp3:okhttp:5.1.0'
implementation 'io.github.webrtc-sdk:android:137.7151.03'
implementation 'com.pubnub:pubnub-gson:11.0.0'

Permissions

Add the following to your AndroidManifest.xml:
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />

Minimal publish

1

Add a Red5Renderer to your layout

Red5Renderer extends WebRTC’s SurfaceViewRenderer and displays the local camera preview.
<net.red5.android.core.Red5Renderer
    android:id="@+id/surface_view"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:layout_centerInParent="true" />
In your Activity:
private Red5Renderer surfaceView;
surfaceView = findViewById(R.id.surface_view);
2

Request camera and microphone permissions

private static final String[] REQUIRED_PERMISSIONS = {
    Manifest.permission.CAMERA,
    Manifest.permission.RECORD_AUDIO
};

private void checkPermissions() {
    if (hasAllPermissions()) {
        createWebrtcClient();
    } else {
        ActivityCompat.requestPermissions(this, REQUIRED_PERMISSIONS, PERMISSION_REQUEST_CODE);
    }
}
3

Build the client

Use IRed5WebrtcClient.builder() and chain your configuration. Use setStreamManagerHost() for Red5 Cloud or setServerIp() for a standalone server.
IRed5WebrtcClient webrtcClient = IRed5WebrtcClient.builder()
    .setActivity(this)
    .setLicenseKey(YOUR_SDK_LICENSE_KEY)
    .setStreamManagerHost("userid-xxx.cloud.red5.net") // Red5 Cloud
    // .setServerIp("192.168.1.100")                  // Standalone
    .setVideoEnabled(true)
    .setAudioEnabled(true)
    .setVideoWidth(1280)
    .setVideoHeight(720)
    .setVideoFps(30)
    .setVideoBitrate(1500)
    .setVideoSource(IRed5WebrtcClient.StreamSource.FRONT_CAMERA)
    .setVideoRenderer(surfaceView)
    .setEventListener(this)
    .build();
4

Start preview after license validates

@Override
public void onLicenseValidated(boolean validated, String message) {
    if (validated) {
        webrtcClient.startPreview();
    } else {
        Toast.makeText(this, "License check failed: " + message, Toast.LENGTH_SHORT).show();
    }
}
5

Publish

webrtcClient.publish("myStreamName");

Minimal subscribe

IRed5WebrtcClient webrtcClient = IRed5WebrtcClient.builder()
    .setActivity(this)
    .setLicenseKey(YOUR_SDK_LICENSE_KEY)
    .setStreamManagerHost("userid-xxx.cloud.red5.net")
    .setVideoRenderer(surfaceView)
    .setEventListener(this)
    .build();

webrtcClient.subscribe("myStreamName");

Camera controls

Call these methods on the IRed5WebrtcClient instance at any time after the client is built.
// Switch between front and rear camera
webrtcClient.switchCamera();

// Mute/unmute video during an active session
webrtcClient.toggleSendVideo(false); // mute
webrtcClient.toggleSendVideo(true);  // unmute

// Mute/unmute audio during an active session
webrtcClient.toggleSendAudio(false); // mute
webrtcClient.toggleSendAudio(true);  // unmute

Picture-in-Picture mode

The SDK supports Android’s native PiP mode for both publishing and subscribing. Auto-enter PiP when the user navigates away:
@Override
protected void onUserLeaveHint() {
    super.onUserLeaveHint();
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O
            && isPublishing
            && !isInPictureInPictureMode()) {
        enterPictureInPictureMode();
    }
}
Manually enter PiP:
@TargetApi(Build.VERSION_CODES.O)
public void enterPip() {
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
        Rational aspectRatio = new Rational(surfaceView.getWidth(), surfaceView.getHeight());
        PictureInPictureParams params = new PictureInPictureParams.Builder()
            .setAspectRatio(aspectRatio)
            .build();
        enterPictureInPictureMode(params);
    }
}

Real-time stats collection

Enable the stats collector to receive WebRTC metrics every 2 seconds (configurable).
IRed5WebrtcClient webrtcClient = IRed5WebrtcClient.builder()
    // ... other config ...
    .setStatsCollectorEnabled(true)
    .setStatsPollingIntervalMs(2000)
    .build();
Implement onRtcStats() in your Red5EventListener:
@Override
public void onRtcStats(RTCStats stats) {
    Log.d(TAG, "TX Bitrate: " + stats.txKBitRate + " kbps");
    Log.d(TAG, "RX Bitrate: " + stats.rxKBitRate + " kbps");
    Log.d(TAG, "Packet Loss: " + stats.rxPacketLossRate + "%");

    // Local mic level (0.0 = silence, 1.0 = maximum)
    if (stats.localAudioLevel > 0.05) {
        showMicrophoneActivity();
    }
}

Conferencing

The SDK provides multi-party conferencing via Red5 Cloud.
Conferencing requires a Red5 Cloud (Stream Manager) deployment. It does not work with standalone servers.
1

Set a ConferenceListener and build the client

IRed5WebrtcClient.ConferenceListener conferenceListener =
    new IRed5WebrtcClient.ConferenceListener() {
        @Override
        public void onJoinRoomSuccess(String roomId, ArrayList participants) {
            Log.d(TAG, "Joined room: " + roomId);
        }

        @Override
        public void onParticipantJoined(String uid, String role, String metaData,
                                        boolean videoEnabled, boolean audioEnabled,
                                        Red5Renderer renderer) {
            if (renderer != null) {
                runOnUiThread(() -> participantContainer.addView(renderer));
            }
        }

        @Override
        public void onParticipantLeft(String uid) {
            Log.d(TAG, "Participant left: " + uid);
        }

        @Override
        public void onParticipantMediaUpdate(String uid, boolean videoEnabled,
                                             boolean audioEnabled, long timestamp) {
            // Update UI indicators for camera/mic state
        }

        @Override
        public void onJoinRoomFailed(int statusCode, String message) {
            Log.e(TAG, "Join failed: " + message);
        }
    };

red5Client = IRed5WebrtcClient.builder()
    .setActivity(this)
    .setLicenseKey(YOUR_SDK_LICENSE_KEY)
    .setStreamManagerHost(YOUR_STREAM_MANAGER_HOST)
    .setVideoEnabled(true)
    .setAudioEnabled(true)
    .setVideoRenderer(localVideoRenderer)
    .setEventListener(this)
    .setConferenceListener(conferenceListener)
    .build();
2

Join the room

red5Client.join(
    "my-conference-room",  // roomId
    "john_" + System.currentTimeMillis(), // userId
    "",                    // token (optional)
    "publisher",           // role: "publisher" or "subscriber"
    "{\"name\":\"John Doe\"}" // metadata (optional JSON)
);
3

Leave the room

red5Client.release();

Chat integration

The SDK includes built-in PubNub chat. Pass your PubNub keys when building the client:
IRed5WebrtcClient webrtcClient = IRed5WebrtcClient.builder()
    .setActivity(this)
    .setLicenseKey(YOUR_SDK_LICENSE_KEY)
    .setChatUserId(USER_ID)
    .setPubnubPublishKey(YOUR_PUBNUB_PUBLISH_KEY)
    .setPubnubSubscribeKey(YOUR_PUBNUB_SUBSCRIBE_KEY)
    .setEventListener(this)
    .build();
Subscribe to a channel and send messages:
// Subscribe to a channel after license validation
webrtcClient.subscribeChatChannel("my-chat-room");

// Send a text message
webrtcClient.sendChatTextMessage("my-chat-room", "Hello, everyone!", null);
Receive messages in your Red5EventListener:
@Override
public void onChatMessageReceived(String channel, JsonElement message) {
    if (message != null && message.isJsonObject()) {
        JsonObject obj = message.getAsJsonObject();
        String text = obj.get("text").getAsString();
        String user = obj.get("userName").getAsString();
        Log.d("Chat", user + ": " + text);
    }
}

Event reference

Implement IRed5WebrtcClient.Red5EventListener in your Activity or Fragment.
CallbackDescription
onPublishStarted()Publish session started
onPublishStopped()Publish session ended
onPublishFailed(String error)Publish session failed
onSubscribeStarted()Subscribe session started
onSubscribeStopped()Subscribe session ended
onSubscribeFailed(String error)Subscribe session failed
CallbackDescription
onLicenseValidated(boolean validated, String message)License check completed — always fires first
onPreviewStarted()Camera preview is running
onPreviewStopped()Camera preview stopped
onIceConnectionStateChanged(IceConnectionState state)ICE connection state changed
onConnectionStateChanged(PeerConnectionState state)Peer connection state changed
onError(String error)Unrecoverable error
onRtcStats(RTCStats stats)WebRTC metrics (fires each polling interval)
CallbackDescription
onChatConnected()Connected to PubNub
onChatDisconnected()Disconnected from PubNub
onChatMessageReceived(String channel, JsonElement message)Message received
onChatSendSuccess(String channel, Long timetoken)Message sent
onChatSendError(String channel, String errorMessage)Message failed

Configuration reference

Key IRed5WebrtcClient.builder() methods:
MethodDefaultDescription
setActivity(Activity activity)Host Activity (required)
setLicenseKey(String key)Red5 Pro license key (required)
setStreamManagerHost(String host)Red5 Cloud Stream Manager hostname
setServerIp(String ip)Standalone server IP
setServerPort(int port)5080Standalone server port
setAppName(String name)"live"Application scope
setVideoEnabled(boolean enabled)trueEnable or disable video
setAudioEnabled(boolean enabled)trueEnable or disable audio
setVideoWidth(int width)1280Capture width in pixels
setVideoHeight(int height)720Capture height in pixels
setVideoFps(int fps)30Frame rate
setVideoBitrate(int kbps)1500Bitrate in kbps
setStatsCollectorEnabled(boolean enabled)trueEnable stats collection
setStatsPollingIntervalMs(int ms)2000Stats polling interval
For the complete API reference including all methods, enums, and data models, see the Android SDK API reference.