In this tutorial we'll build a low-latency in-app livestreaming experience with Jetpack Compose and the Stream Video SDK. The livestream is broadcast over Stream's global edge network.
This page works for both humans and AI coding agents: every step is a file operation, shell command, or a clearly-marked manual action, and each step has a verification checkpoint.
The tutorial has two parts:
Part 1 — View a livestream — create a livestream in the dashboard, push video into it with OBS over RTMP, and watch it on Android.
Part 2 — Broadcast from a device — publish a livestream directly from Android over WebRTC, with backstage and go-live.
Livestreaming is more hands-on than the other tutorials. Part 1 needs the Stream dashboard and OBS — both are manual, human-only steps that no CLI or agent can do for you. They're marked with 🧑 below.
Choose your path
Path A — Let your AI agent build it (recommended)
Install the Stream CLI once, then add the skills, so Claude Code, Cursor, or Codex build the Android code against current SDK APIs:
1234567# Install the getstream CLI curl -fsSL https://getstream.io/cli.sh | bash # Install the skills (router, docs, and builder) for your agent. # --universal for Cursor, Codex, and other AGENTS-ecosystem tools; --claude for Claude Code. getstream skills --universal getstream skills stream-android --universal
Then ask your agent:
1234/stream-android Add a Stream livestream viewer to my Jetpack Compose app using LivestreamPlayer, then a broadcaster screen that publishes from the device camera with backstage + go-live. Use my CLI credentials, or the tutorial demo credentials if I'm not logged in.
Where you come in. The agent writes the Android code, but you create the livestream in the dashboard and run OBS (Part 1), log in when getstream init opens the browser, and run the app granting camera/mic access (Part 2).
Path B — Build it manually
Follow the steps below.
For AI assistants reading this page: append
.mdto any Stream docs URL for a clean Markdown version. A condensed index for the Android Video SDK is athttps://getstream.io/video/docs/android/llms.txt. Prefer these over parsing HTML.
Important Building Blocks
StreamVideo— the client, built withStreamVideoBuilder. For a viewer it can useUser.anonymous(); a broadcaster needs a user with a broadcast-capable role (e.g.admin).Callof typelivestream— created withclient.call("livestream", id). Backstage mode (on by default) lets hosts set up beforecall.goLive()opens the stream to viewers.LivestreamPlayer— a ready-made viewer component. Customize it withoverlayContentandrendererContent, or drop toVideoRendererfor a fully custom player.call.state—connection,backstage,duration,totalParticipants,livestream,localParticipant, and more, asStateFlows.
Part 1 — View a livestream
Step 1 — Create a livestream in the dashboard
Open the dashboard and select Video & Audio → Overview. You'll see buttons to create different call types.

Click Create Livestream. The next screen shows the livestream's details:

You'll need the RTMP URL and RTMP Stream Key (for OBS in Step 2), plus the API Key, Viewer Token, and Livestream ID (for the app in Step 3).
🧑 Human checkpoint: creating the livestream and copying its credentials is done in the Stream dashboard — there's no CLI or agent path for it. Agents: ask the human to create a livestream and paste back the RTMP URL, stream key, API key, viewer token, and call id.
Step 2 — Set up the livestream in OBS
OBS is a popular livestreaming package; we'll use it to publish video over RTMP.
After installing it, set up a capture device in the Sources section:

Select Video Capture Device to stream from your camera (or Screen Capture for your screen). Then click Settings in the Controls section:

Select Stream. For Service choose Custom, and enter the Server (RTMP URL) and Stream Key from Step 1:

Press Start Streaming in the Controls section, then return to the dashboard — you should see the OBS feed:

By default the livestream starts immediately; you can enable backstage for the livestream call type in the dashboard.
🧑 Human checkpoint: installing and configuring OBS is a manual desktop step. Agents: ask the human to start streaming from OBS and confirm the feed appears in the dashboard before continuing.
✅ Checkpoint: the dashboard shows your OBS video feed playing live.
Step 3 — Build the livestreaming app
- Open Android Studio and create a new project.
- Select Phone & Tablet → Empty Activity (the Compose one) and click Next.
- Name your project LivestreamApp and click Finish.
🧑 Human checkpoint: the project is created in Android Studio's New Project wizard. Agents: detect the project shape first (find . -maxdepth 3 -name "settings.gradle*" -o -name "build.gradle*"); if there's no Gradle project, ask the human to create it.
Step 3.1 — Add the Stream SDK
Add the SDK to the app module's build.gradle. Replace <latest_version> with the latest release:
1234567891011dependencies { // Stream Video Compose SDK implementation("io.getstream:stream-video-android-ui-compose:<latest_version>") // Jetpack Compose (Android Studio often adds these automatically) implementation(platform("androidx.compose:compose-bom:2024.09.02")) implementation("androidx.activity:activity-compose:1.9.0") implementation("androidx.compose.ui:ui") implementation("androidx.compose.ui:ui-tooling") implementation("androidx.compose.material3:material3") }
Use recent Android Gradle Plugin, Kotlin, and Compose compiler versions so the SDK can target compileSdk = 35. In the root build.gradle.kts:
12345plugins { id("com.android.application") version "8.7.2" apply false id("org.jetbrains.kotlin.android") version "2.0.21" apply false id("org.jetbrains.kotlin.plugin.compose") version "2.0.21" apply false }
Apply the Compose plugin in the app module's build.gradle.kts:
12345plugins { id("com.android.application") id("org.jetbrains.kotlin.android") id("org.jetbrains.kotlin.plugin.compose") }
Add the INTERNET permission in AndroidManifest.xml, before the application tag:
1<uses-permission android:name="android.permission.INTERNET" />
Sync the project.
✅ Checkpoint: ./gradlew :app:assembleDebug succeeds and import io.getstream.video.android.compose.ui.components.livestream.LivestreamPlayer resolves.
Step 3.2 — Initialize the SDK
You initialize the SDK by building a StreamVideo client. For a production app, create it in your Application class or a DI module. To keep this simple we create it in MainActivity in the next step. You can omit the user parameter to default to an anonymous user.
Step 3.3 — View a Livestream on an Android Device
Open MainActivity.kt and replace the MainActivity class with the following. It initializes the SDK and plays the stream with LivestreamPlayer:
1234567891011121314151617181920212223242526272829303132333435363738394041import android.os.Bundle import androidx.activity.ComponentActivity import androidx.activity.compose.setContent import androidx.compose.runtime.Composable import io.getstream.video.android.compose.permission.LaunchCallPermissions import io.getstream.video.android.compose.theme.VideoTheme import io.getstream.video.android.compose.ui.components.livestream.LivestreamPlayer import io.getstream.video.android.core.Call import io.getstream.video.android.core.StreamVideoBuilder import io.getstream.video.android.model.User class MainActivity : ComponentActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) // Initialize StreamVideo. For a production app, we recommend adding the client to your Application class or DI module. val client = StreamVideoBuilder( context = applicationContext, user = User.anonymous(), apiKey = "YOUR_API_KEY", token = "YOUR_VIEWER_TOKEN", ).build() val call = client.call("livestream", "YOUR_CALL_ID") setContent { VideoTheme { LiveAudience(call) } } } } @Composable fun LiveAudience(call: Call) { // Ensure the call has the correct permissions to run LaunchCallPermissions(call = call, onAllPermissionsGranted = { // Join the livestream. call.join() }) LivestreamPlayer(call = call) }
Replace the placeholders with values from the dashboard (Step 1): YOUR_API_KEY = API Key, YOUR_VIEWER_TOKEN = Viewer Token, YOUR_CALL_ID = Livestream ID.

That's everything needed to play a livestream. LivestreamPlayer plays the stream given just the call id and type.
✅ Checkpoint: run the app — you see the livestream published from OBS playing on the device.
Step 3.4 — Customizing the UI
The default player UI is a good starting point. Customize LivestreamPlayer with overlayContent or rendererContent.
Customizing Overlay Content
Provide custom overlayContent to display extra information, such as viewer counts or duration:
1234567891011121314151617181920212223242526272829303132333435363738394041import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.unit.dp import io.getstream.video.android.compose.theme.VideoTheme import io.getstream.video.android.compose.ui.components.livestream.LivestreamPlayer import io.getstream.video.android.core.Call @Composable fun CustomOverlayContent(call: Call) { val totalParticipants by call.state.totalParticipants.collectAsState() Column( modifier = Modifier .fillMaxWidth() .padding(16.dp), horizontalAlignment = Alignment.CenterHorizontally, verticalArrangement = Arrangement.Top ) { Text(text = "Custom overlay Live Stream", style = VideoTheme.typography.bodyL, color = Color.Red) Spacer(modifier = Modifier.height(8.dp)) Text(text = "Viewers: $totalParticipants", color = Color.White) } } @Composable fun CustomLivestreamPlayer(call: Call) { LivestreamPlayer( call = call, overlayContent = { CustomOverlayContent(call = call) } ) }
Customizing Renderer Content
Provide rendererContent to change how the host's video is rendered. Here we render with VideoRenderer while keeping the custom overlay:
123456789101112131415161718192021222324252627import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.runtime.Composable import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue import androidx.compose.ui.Modifier import io.getstream.video.android.compose.ui.components.livestream.LivestreamPlayer import io.getstream.video.android.compose.ui.components.video.VideoRenderer import io.getstream.video.android.core.Call @Composable fun CustomLivestreamPlayer(call: Call) { val livestream by call.state.livestream.collectAsState() LivestreamPlayer( call = call, rendererContent = { VideoRenderer( modifier = Modifier.fillMaxSize(), call = call, video = livestream, videoFallbackContent = { // Content for when the video is not available. }, ) }, overlayContent = { CustomOverlayContent(call = call) }, ) }
To use these customizations, replace LivestreamPlayer with CustomLivestreamPlayer in LiveAudience.
State & Participants
call.state exposes the StateFlows you need:
call.state.connection— realtime connection status (use it for a loading UI).call.state.backstage— whether the call is in backstage mode.call.state.duration— how long the call has been running.call.state.totalParticipants— the number of viewers.call.state.localParticipant— the participant on this device.
call.state.participants gives access to all participants (name, image, video track, roles). The participant state docs explain more.
Writing a Fully Custom Livestream Player
You can also write a fully custom player for complete control. The example below uses a Scaffold with a top bar (viewer count + duration) over the video:
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970import androidx.compose.foundation.background import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material3.Scaffold import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip import androidx.compose.ui.graphics.Color import androidx.compose.ui.unit.dp import io.getstream.video.android.compose.theme.VideoTheme import io.getstream.video.android.compose.ui.components.video.VideoRenderer import io.getstream.video.android.core.Call @Composable private fun FullyCustomLivestreamPlayer(call: Call) { val totalParticipants by call.state.totalParticipants.collectAsState() val duration by call.state.duration.collectAsState() val livestream by call.state.livestream.collectAsState() Scaffold( modifier = Modifier .fillMaxSize() .background(VideoTheme.colors.baseSheetPrimary) .padding(6.dp), contentColor = VideoTheme.colors.baseSheetPrimary, topBar = { Box( modifier = Modifier .fillMaxWidth() .padding(6.dp), ) { Text( modifier = Modifier .align(Alignment.CenterEnd) .background( color = VideoTheme.colors.brandPrimary, shape = RoundedCornerShape(6.dp), ) .padding(horizontal = 12.dp, vertical = 4.dp), text = "Live $totalParticipants", color = Color.White, ) Text( modifier = Modifier.align(Alignment.Center), text = "Live for $duration", color = VideoTheme.colors.brandPrimaryDk, ) } }, ) { VideoRenderer( modifier = Modifier .fillMaxSize() .padding(it) .clip(RoundedCornerShape(6.dp)), call = call, video = livestream, videoFallbackContent = { // Content for when the video is not available. }, ) } }
Part 2 — Broadcast from a device
In Part 1 we published via RTMP and authenticated through the dashboard. In a real app you'd generate tokens programmatically with a server-side SDK. Part 2 broadcasts directly from an Android device over WebRTC.
Step 0 — Credentials for broadcasting
The broadcaster needs an API key, a user token for a user with a broadcast-capable role, a user id, and a call id. Provision your own via the CLI, or use the pre-filled tutorial credentials.
Option 1 — Your own app, via the CLI:
123getstream init # authenticate + create/select org & app (opens a browser) getstream env --target android # writes STREAM_API_KEY to local.properties getstream token tutorial_user # mint a user token
🧑 Human checkpoint: getstream init opens a browser to authenticate. Agents: run it, then ask the human to finish logging in before continuing.
Option 2 — Pre-filled tutorial credentials: the whole-app broadcast block below is filled in with working demo credentials — copy it as-is. Swap in your own before building anything real.
Step 4 — Livestreaming from Android devices
We'll send video from the device over WebRTC using the backstage flow.
Step 4.1 — Permissions setup
Publishing needs camera and microphone access. Add to AndroidManifest.xml:
12<uses-permission android:name="android.permission.CAMERA" /> <uses-permission android:name="android.permission.RECORD_AUDIO" />
Add the camera feature declaration:
1<uses-feature android:name="android.hardware.camera" android:required="false" />
These permissions must be granted for the livestream to work; we'll request them with Stream components shortly.
Step 4.2 — Broadcasting a livestream
This replaces the viewer code from Part 1 — the app now publishes video instead of watching it. Update MainActivity to create the broadcaster client with a user in the admin role (so they're allowed to broadcast):
1234567891011121314151617181920212223242526272829303132333435363738import android.os.Bundle import androidx.activity.ComponentActivity import androidx.activity.compose.setContent import androidx.compose.material3.Text import io.getstream.video.android.compose.theme.VideoTheme import io.getstream.video.android.core.StreamVideoBuilder import io.getstream.video.android.model.User class MainActivity : ComponentActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) val userId = "REPLACE_WITH_USER_ID" val userName = "Broadcaster" val userToken = "REPLACE_WITH_TOKEN" // Step 1 - Create a user. val user = User( id = userId, // predefined string name = userName, // Name and image are used in the UI role = "admin", ) // Step 2 - Initialize StreamVideo. For a production app, we recommend adding the client to your Application class or DI module. val client = StreamVideoBuilder( context = applicationContext, apiKey = "REPLACE_WITH_API_KEY", user = user, token = userToken, ).build() setContent { VideoTheme { Text(text = "TODO: Render host content") } } } }
Run the app now and you'll see TODO: Render host content.
Creating the call
Stream uses the same call object for livestreaming, audio rooms, and video calling:
12val call = client.call("livestream", "REPLACE_WITH_CALL_ID") call.join(create = true)
Specify the call type as livestream and a unique callId. call.join(create = true) creates the call on the server and sets up realtime transport. You can also add members with roles — see the call creation docs.
Rendering the UI
Create a new file LiveHostContent.kt with a composable that renders the broadcast. Unlike watching, there's no default broadcast component, so we build one.
1234@Composable fun LiveHostContent(call: Call) { // We will define our content here }
1234567891011121314151617181920212223242526import androidx.compose.foundation.background import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.systemBarsPadding import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material3.Button import androidx.compose.material3.ButtonDefaults import androidx.compose.material3.Scaffold import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip import androidx.compose.ui.graphics.Color import androidx.compose.ui.unit.dp import io.getstream.video.android.compose.permission.LaunchCallPermissions import io.getstream.video.android.compose.theme.VideoTheme import io.getstream.video.android.compose.ui.components.video.VideoRenderer import io.getstream.video.android.core.Call import io.getstream.video.android.core.RealtimeConnection import kotlinx.coroutines.launch
Collect State Properties — track connection, participant count, backstage, local participant, video, and duration. Add inside LiveHostContent:
1234567val connection by call.state.connection.collectAsState() val totalParticipants by call.state.totalParticipants.collectAsState() val backstage by call.state.backstage.collectAsState() val localParticipant by call.state.localParticipant.collectAsState() val video = localParticipant?.video?.collectAsState() val duration by call.state.duration.collectAsState() val scope = rememberCoroutineScope()
Set Up the Scaffold with a top bar, bottom bar, and content area:
12345678910111213141516171819Scaffold( modifier = Modifier .fillMaxSize() .background(VideoTheme.colors.baseSheetPrimary) // On targetSdk 35 the app draws edge-to-edge, behind the system bars. systemBarsPadding // keeps the top bar below the status bar and the broadcast button above the navigation bar. .systemBarsPadding() .padding(6.dp), contentColor = VideoTheme.colors.baseSheetPrimary, topBar = { // Will define the topBar content here }, bottomBar = { // Will define bottomBar content here }, ) { // Main content, will be the VideoRenderer (ignore the padding warning) } }
Create the Top Bar — show live status, participant count, and duration when connected; a backstage message otherwise:
12345678910111213141516171819202122232425262728293031323334353637if (connection == RealtimeConnection.Connected) { if (!backstage) { Box( modifier = Modifier .fillMaxWidth() .padding(6.dp), ) { Text( modifier = Modifier .align(Alignment.CenterEnd) .background( color = VideoTheme.colors.brandPrimary, shape = RoundedCornerShape(6.dp), ) .padding(horizontal = 12.dp, vertical = 4.dp), text = "Live $totalParticipants", color = Color.White, ) Text( modifier = Modifier.align(Alignment.Center), text = "Live for $duration", color = VideoTheme.colors.basePrimary, ) } } else { Text( text = "The livestream is not started yet", color = VideoTheme.colors.basePrimary, ) } } else if (connection is RealtimeConnection.Failed) { Text( text = "Connection failed", color = VideoTheme.colors.basePrimary, ) }
Create the Bottom Bar — a button to start/stop the broadcast based on backstage state:
12345678910111213141516Button( colors = ButtonDefaults.buttonColors( contentColor = VideoTheme.colors.brandPrimary, containerColor = VideoTheme.colors.brandPrimary ), onClick = { scope.launch { if (backstage) call.goLive() else call.stopLive() } }, ) { Text( text = if (backstage) "Start Broadcast" else "Stop Broadcast", color = Color.White, ) }
Render the Video Content with VideoRenderer:
1234567891011VideoRenderer( modifier = Modifier .fillMaxSize() .padding(it) .clip(RoundedCornerShape(6.dp)), call = call, video = video?.value, videoFallbackContent = { // Content for when the video is not available. }, )
The complete MainActivity
Now wire LiveHostContent into MainActivity. We add a LiveHost() composable that requests camera and microphone permissions before joining:
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859import android.os.Bundle import androidx.activity.ComponentActivity import androidx.activity.compose.setContent import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import io.getstream.video.android.compose.permission.LaunchPermissionRequest import io.getstream.video.android.compose.theme.VideoTheme import io.getstream.video.android.core.Call import io.getstream.video.android.core.StreamVideoBuilder import io.getstream.video.android.model.User class MainActivity : ComponentActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) // Step 1 - Create a user with the admin role so they can broadcast. val user = User( id = "REPLACE_WITH_USER_ID", name = "Broadcaster", role = "admin", ) // Step 2 - Initialize StreamVideo. For a production app, we recommend adding the client to your Application class or DI module. val client = StreamVideoBuilder( context = applicationContext, apiKey = "REPLACE_WITH_API_KEY", user = user, token = "REPLACE_WITH_TOKEN", ).build() val call = client.call("livestream", "REPLACE_WITH_CALL_ID") setContent { VideoTheme { LiveHost(call) } } } @Composable fun LiveHost(call: Call) { LaunchPermissionRequest( permissions = listOf( android.Manifest.permission.RECORD_AUDIO, android.Manifest.permission.CAMERA ) ) { AllPermissionsGranted { LaunchedEffect(call) { call.join(create = true) } LiveHostContent(call = call) } NoneGranted { // Handle permission explanation } } } }
🧑 Human checkpoint: the camera and microphone prompts are Android system dialogs, and a real device gives a real camera feed. Agents: install the app, then ask the human to launch it and allow camera and microphone access.
Note: When you go live, the SDK keeps the broadcast alive in a foreground service, which posts an ongoing notification. On Android 13+ this requires the
POST_NOTIFICATIONSruntime permission — the SDK declares and requests it for you. Denying it never blocks the broadcast; the user just won't see the notification.
✅ Checkpoint: the broadcaster screen shows your camera feed with a Start Broadcast button (backstage), and the top bar reads The livestream is not started yet until you go live.
Step 5 — Backstage and Go Live
Backstage mode lets you and co-hosts set up before going live. Only after call.goLive() can regular viewers join. (Disable backstage for the livestream call type in the dashboard if you want calls to start immediately.) LiveHostContent reads call.state.backstage: when true, it shows the setup screen with a Start Broadcast button that calls call.goLive().
Step 6 — Preview in the browser
Press Start Broadcast in the Android app. You'll see:

Open the stream in your browser to watch:
✅ Checkpoint: after Start Broadcast, the top bar switches to Live <count> and the browser shows your device's video.
Advanced Features
- Co-hosts — add members with elevated permissions; render multiple tracks as in the video calling tutorial.
- Permissions and moderation — per-role permissions and request-based access.
- Custom events — share realtime data like a game score.
- Reactions & chat — a more engaging experience.
- Notifications — notify users when a livestream starts.
- Recording & HLS — record calls, or serve HLS (10–20s delay, better buffering) as an alternative to realtime WebRTC.
Verify the whole build
1./gradlew :app:installDebug
Then confirm end to end: Part 1 — OBS pushes to the dashboard livestream and the app plays it. Part 2 — the broadcaster app requests camera/mic, shows the backstage setup, and after Start Broadcast the browser (Step 6) shows your device's video.
Troubleshooting
stream project is not initialized— CLI onboarding not run (Part 2 CLI path). Rungetstream initfirst.- Player shows nothing (Part 1) — OBS isn't streaming, or the API key/viewer token/call id don't match the dashboard livestream. Re-copy all three from the livestream page.
- Can't go live / not authorized to broadcast — the broadcaster user needs a broadcast-capable role. Create the user with
role = "admin"(Step 4.2). - No camera/mic prompt (Part 2) — permissions missing or denied. Add
CAMERAandRECORD_AUDIOto the manifest (Step 4.1) and grant them. - Black video when broadcasting — run on a real device; the emulator has no real camera.
- Viewers can't join — the stream is still in backstage. Press Start Broadcast (
call.goLive()). - Compose build errors — align Kotlin, Compose compiler, and AGP versions (Step 3.1).
Next steps
- Video calling — a Zoom-style call app: video calling tutorial
- Audio rooms — a Clubhouse-style room: audio room tutorial
- Recording & HLS — recording guide
- Call and participant state — guide
Calls run on Stream's global edge network. Pricing is flexible with multiple tiers.
Final Thoughts
You built low-latency in-app livestreaming two ways — watching an RTMP/OBS stream with LivestreamPlayer, and broadcasting from a device over WebRTC with backstage and go-live. WebRTC is optimal for latency; HLS buffers better on poor connections. The Video SDK for Android and the API support recording, HLS, co-hosts, moderation, and more.
Machine-readable resources
For AI agents and coding assistants working with this SDK:
- CLI + skills:
curl -fsSL https://getstream.io/cli.sh | bash, thengetstream skills --universalandgetstream skills stream-android --universalfor the Android pack (use--claudeinstead for Claude Code). (Alternative:npx skills add GetStream/agent-skills -s stream.) - Provisioning (broadcaster):
getstream init→getstream env --target android→getstream token <user_id>; the viewer credentials in Part 1 come from the dashboard livestream page - Manual steps that have no CLI/agent path: creating the livestream in the dashboard and running OBS (Part 1)
- Android integration skill: invoke
/stream-androidin your agent;/stream-docssearches live SDK docs with citations - Docs index for LLMs:
https://getstream.io/video/docs/android/llms.txt - Markdown endpoints: append
.mdto any docs URL for a clean, token-efficient version - Source of truth for APIs: the SDK repository — check the pinned version's source rather than assuming APIs from training data

