> ## Documentation Index
> Fetch the complete documentation index at: https://red5.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# Red5 Android SDK — Native WebRTC Streaming for Android

> Build native Android streaming apps with sub-second WebRTC latency. Covers installation, publish and subscribe, Picture-in-Picture, conferencing, and chat.

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](https://red5-cloud-sdk.cachefly.net/index.html) and place it in your `app/libs` folder.

Add the following dependencies to your app's `build.gradle`:

```gradle theme={null}
// 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`:

```xml theme={null}
<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

<Steps>
  <Step title="Add a Red5Renderer to your layout">
    `Red5Renderer` extends WebRTC's `SurfaceViewRenderer` and displays the local camera preview.

    ```xml theme={null}
    <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:

    ```java theme={null}
    private Red5Renderer surfaceView;
    surfaceView = findViewById(R.id.surface_view);
    ```
  </Step>

  <Step title="Request camera and microphone permissions">
    ```java theme={null}
    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);
        }
    }
    ```
  </Step>

  <Step title="Build the client">
    Use `IRed5WebrtcClient.builder()` and chain your configuration. Use `setStreamManagerHost()` for Red5 Cloud or `setServerIp()` for a standalone server.

    <Tabs>
      <Tab title="Java">
        ```java theme={null}
        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();
        ```
      </Tab>

      <Tab title="Kotlin">
        ```kotlin theme={null}
        val webrtcClient: IRed5WebrtcClient? = IRed5WebrtcClient.builder()
            .setActivity(this.requireActivity())
            .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()
        ```
      </Tab>
    </Tabs>
  </Step>

  <Step title="Start preview after license validates">
    ```java theme={null}
    @Override
    public void onLicenseValidated(boolean validated, String message) {
        if (validated) {
            webrtcClient.startPreview();
        } else {
            Toast.makeText(this, "License check failed: " + message, Toast.LENGTH_SHORT).show();
        }
    }
    ```
  </Step>

  <Step title="Publish">
    ```java theme={null}
    webrtcClient.publish("myStreamName");
    ```
  </Step>
</Steps>

## Minimal subscribe

```java theme={null}
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.

```java theme={null}
// 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:**

```java theme={null}
@Override
protected void onUserLeaveHint() {
    super.onUserLeaveHint();
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O
            && isPublishing
            && !isInPictureInPictureMode()) {
        enterPictureInPictureMode();
    }
}
```

**Manually enter PiP:**

```java theme={null}
@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).

```java theme={null}
IRed5WebrtcClient webrtcClient = IRed5WebrtcClient.builder()
    // ... other config ...
    .setStatsCollectorEnabled(true)
    .setStatsPollingIntervalMs(2000)
    .build();
```

Implement `onRtcStats()` in your `Red5EventListener`:

```java theme={null}
@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.

<Note>
  Conferencing requires a Red5 Cloud (Stream Manager) deployment. It does not work with standalone servers.
</Note>

<Steps>
  <Step title="Set a ConferenceListener and build the client">
    ```java theme={null}
    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();
    ```
  </Step>

  <Step title="Join the room">
    ```java theme={null}
    red5Client.join(
        "my-conference-room",  // roomId
        "john_" + System.currentTimeMillis(), // userId
        "",                    // token (optional)
        "publisher",           // role: "publisher" or "subscriber"
        "{\"name\":\"John Doe\"}" // metadata (optional JSON)
    );
    ```
  </Step>

  <Step title="Leave the room">
    ```java theme={null}
    red5Client.release();
    ```
  </Step>
</Steps>

## Chat integration

The SDK includes built-in PubNub chat. Pass your PubNub keys when building the client:

```java theme={null}
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:

```java theme={null}
// 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`:

```java theme={null}
@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.

<AccordionGroup>
  <Accordion title="Streaming events">
    | Callback                          | Description               |
    | --------------------------------- | ------------------------- |
    | `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  |
  </Accordion>

  <Accordion title="Lifecycle events">
    | Callback                                                | Description                                  |
    | ------------------------------------------------------- | -------------------------------------------- |
    | `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) |
  </Accordion>

  <Accordion title="Chat events">
    | Callback                                                     | Description              |
    | ------------------------------------------------------------ | ------------------------ |
    | `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           |
  </Accordion>
</AccordionGroup>

## Configuration reference

Key `IRed5WebrtcClient.builder()` methods:

| Method                                      | Default  | Description                        |
| ------------------------------------------- | -------- | ---------------------------------- |
| `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)`                   | `5080`   | Standalone server port             |
| `setAppName(String name)`                   | `"live"` | Application scope                  |
| `setVideoEnabled(boolean enabled)`          | `true`   | Enable or disable video            |
| `setAudioEnabled(boolean enabled)`          | `true`   | Enable or disable audio            |
| `setVideoWidth(int width)`                  | `1280`   | Capture width in pixels            |
| `setVideoHeight(int height)`                | `720`    | Capture height in pixels           |
| `setVideoFps(int fps)`                      | `30`     | Frame rate                         |
| `setVideoBitrate(int kbps)`                 | `1500`   | Bitrate in kbps                    |
| `setStatsCollectorEnabled(boolean enabled)` | `true`   | Enable stats collection            |
| `setStatsPollingIntervalMs(int ms)`         | `2000`   | Stats polling interval             |

For the complete API reference including all methods, enums, and data models, see the [Android SDK API reference](https://www.red5.net/docs/red5-cloud/development/sdks/android-sdk/api-reference/).
