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
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 );
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);
}
}
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 ();
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 ()
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 ();
}
}
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.
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 ();
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)
);
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.
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
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)
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
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)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 .