WebRTC in Android Explained: as Simple as It Is — cover illustration

Key takeaways

WebRTC Android development is a solved problem in 2026, if you respect the platform. The media path is identical on every SDK: capture → PeerConnection → SDP offer/answer → ICE → media tracks. What breaks builds is Android, not WebRTC.

Google no longer ships an official prebuilt Android library. The community-maintained getstream/webrtc-android fork is what most teams pull for direct libwebrtc work.

Five Android-specific gotchas eat sprints. Foreground service for capture, audio focus, hardware-codec selection, simulcast tuning, and Doze-mode reconnects. Android 15 made the first one stricter.

SDK choice is downstream of scale, compliance, and UI. LiveKit for Compose-first builds, Stream for chat-first, Daily for HIPAA, Agora for APAC volume, libwebrtc for a custom SFU.

We have shipped this at scale. Fora Soft has delivered 250+ projects since 2005, and built the WebRTC infrastructure behind BrainCert: 500M+ classroom minutes across native Android, iOS, and web.

Why Fora Soft wrote this Android WebRTC playbook

We have shipped Android WebRTC clients on every credible engine: libwebrtc directly, the Stream, LiveKit, Daily, Agora, and 100ms SDKs, and bespoke SFU clients under NDA. BrainCert and ProVideoMeeting both run Android video clients we wrote. Fora Soft has delivered 250+ projects since 2005 with a 50-engineer in-house team; real-time video is the core of what we do.

This is the playbook we hand an Android engineer on day one of a WebRTC project. It walks the canonical flow, names the five Android-specific traps that quietly eat sprints, compares the SDKs on the axes that actually decide the pick, and shows the cost math with the arithmetic left in. If you want the conceptual primer first, read our plain-English “What is WebRTC?”; for topology and scaling, the WebRTC architecture guide. This one is about the client on the phone.

Building an Android WebRTC client?

Thirty minutes with our Android leads gets you an SDK pick, a camera and audio-focus plan, a Compose-friendly architecture, and a realistic calendar.

Book a 30-min call → WhatsApp → Email us →

WebRTC Android core concepts every client needs

Every Android WebRTC client reuses the same five primitives, whether you build on libwebrtc directly or through an SDK. Learn them once and most of the head-scratching disappears. Here is the whole media path in one picture, with the Android concern that sits under each stage.

Android WebRTC data flow: Camera2 capture, PeerConnection, signalling to an SFU, then SurfaceViewRenderer in Compose

Figure 1. The Android WebRTC media path is the same on every SDK. The orange row is the platform work you own.

Concept Android API Purpose
PeerConnectionFactory org.webrtc.PeerConnectionFactory Boots libwebrtc, registers codecs, wires the EGL and capture devices
PeerConnection PeerConnection The session: ICE candidates, SDP offer/answer, media tracks
MediaStreamTrack VideoTrack, AudioTrack Wraps camera and mic capture for transmission
DataChannel DataChannel Out-of-band messaging (chat, control, file transfer)
SurfaceViewRenderer SurfaceViewRenderer Renders local or remote video to the UI

One 2026 change trips up teams that last touched this a few years ago: Google stopped publishing an official prebuilt Android AAR. The library most teams pull today is the maintained getstream/webrtc-android fork: the same org.webrtc package, kept current and shipped as a Gradle dependency. The official reference is still worth a read: webrtc.org.

Camera capture — the part Android-specific bugs love

libwebrtc ships two camera enumerators: Camera1Enumerator (legacy) and Camera2Enumerator (API 21+). Pick Camera2 for anything new. The Camera1 path assumes device behaviour that newer handsets and codec paths no longer guarantee.

val enumerator = Camera2Enumerator(context)
val capturer = enumerator.deviceNames.firstOrNull { enumerator.isFrontFacing(it) }
  ?.let { enumerator.createCapturer(it, null) }
  ?: throw IllegalStateException("No front camera")

capturer.initialize(surfaceTextureHelper, context, source.capturerObserver)
capturer.startCapture(1280, 720, 30)

Mind the foreground service. Since Android 14 (API 34), a service that touches the camera or mic must declare its type (android:foregroundServiceType="camera|microphone") and hold the matching runtime permission, or the OS refuses to start it. Since Android 14, you also cannot start a camera or microphone foreground service while the app is in the background (it throws a SecurityException). Android 15 (API 35) added one more limit on top: you cannot start a camera foreground service from a BOOT_COMPLETED receiver. Google’s foreground-service type reference is the source of truth here.

Camera2 or CameraX? WebRTC’s capturer is built on Camera2, so that is what you feed the pipeline. CameraX is great for photo and preview apps, but for a live track you want the lower-level control Camera2 exposes: resolution, frame rate, and torch state that the SFU and your simulcast config actually depend on.

Signalling — SDP offer/answer and ICE candidates

WebRTC does not define the signalling channel; you bring your own. Most products use Socket.IO, plain WebSocket, gRPC, or a vendor SDK’s built-in signalling. Whatever the transport, it carries three message types between peers (or between a peer and the SFU): the SDP offer (the offerer’s media capabilities), the SDP answer (the answerer’s), and ICE candidates (network-path candidates, exchanged as each side discovers them).

peerConnection.createOffer(object : SdpObserver {
  override fun onCreateSuccess(sdp: SessionDescription) {
    peerConnection.setLocalDescription(this, sdp)
    signalingClient.sendOffer(sdp)      // your transport carries it to the peer/SFU
  }
  override fun onSetSuccess() {}
  override fun onCreateFailure(reason: String?) { /* surface, retry */ }
  override fun onSetFailure(reason: String?) { /* surface, retry */ }
}, MediaConstraints())

If you are choosing a topology behind that signalling layer — peer-to-peer, SFU, or MCU — our P2P vs SFU vs MCU comparison covers the trade-offs the Android client will inherit.

Android WebRTC SDKs compared — libwebrtc, LiveKit, Stream, Daily, Agora, 100ms

Most teams ship through an SDK rather than libwebrtc directly. An SDK buys you room concepts, adaptive bitrate, and a maintained path through Android API churn, not new physics. Here is the honest lay of the land, and a decision tree that gets you to a pick in about a minute.

Android WebRTC SDK decision tree: libwebrtc, LiveKit, Stream, Daily, Agora, 100ms by control, Compose, chat, HIPAA, scale

Figure 2. Walk the spine top to bottom — the first “yes” is usually your SDK.

SDK License Where it wins Where it breaks
libwebrtc (getstream fork) BSD 3 Total control, no per-minute fee, custom SFU You own signalling, scaling, and every upgrade
LiveKit Android Apache 2 Same code Cloud ↔ OSS, Compose-friendly, AI agents No prebuilt UI — you build the room screens
Stream Video Android Mixed (free + paid) Ready-made Compose UI, fast time-to-MVP Per-MAU pricing climbs with a chat-heavy base
Daily Android Proprietary BAA-friendly, strong docs, ~$0.004/min Recording fees add up if you budget only minutes
Agora Android Proprietary APAC infrastructure, HD video ~$3.99/1k min Each service billed separately; costs surprise you
100ms Android Proprietary Native polls, engagement analytics for live Smaller ecosystem than the majors

Reach for libwebrtc directly when: you are building or migrating to a custom SFU and need full control of the codec list, packetisation, and transport. Pull the getstream fork and expect to own signalling and scaling yourself. Otherwise an SDK saves weeks.

Reach for LiveKit when: you are Compose-first, want the same client code against LiveKit Cloud or your own OSS deployment, and value a modern API — especially if AI voice or video agents are on the roadmap.

Reach for Stream or Daily when: Stream if a chat-first product needs rooms and a ready-made Compose UI fast; Daily if you are healthcare-adjacent and want a BAA plus documentation that assumes compliance from the start.

Reach for Agora or a self-hosted SFU when: Agora if you need APAC reach and volume pricing; a self-hosted SFU once you are past roughly two million participant-minutes a month and can staff a 24/7 on-call. See the cost math below before you commit.

Stuck choosing an Android WebRTC SDK?

We have shipped Android clients on all of them. We will match the SDK to your scale, compliance, and language mix in one call — no vendor allegiance.

Book a 30-min scoping call → WhatsApp → Email us →

Five Android-specific gotchas that eat sprints

These five are where Android, not WebRTC, burns your calendar. None are hard once you know them; all are painful when you find them in QA. Build for them from sprint one.

Five Android WebRTC gotchas: foreground service, audio focus, codec choice, simulcast tuning, Doze reconnects

Figure 3. The five traps, with the one-line fix for each — and how Android 14 and 15 changed the rules.

1. Foreground service for camera and mic. Wrap capture in a camera/microphone foreground service and hold the full set (FOREGROUND_SERVICE_CAMERA, FOREGROUND_SERVICE_MICROPHONE, plus the runtime CAMERA and RECORD_AUDIO grants), or the OS kills your session the moment the user leaves the screen.

2. Audio focus and mode. WebRTC needs MODE_IN_COMMUNICATION on the AudioManager plus an AudioFocusRequest with USAGE_VOICE_COMMUNICATION. Skip it and you get echo, low volume, and Bluetooth-routing chaos.

3. Hardware codec selection. H.264 hardware encoders on some older Snapdragons mishandle keyframes; VP9 is steadier across a mixed fleet but costs more CPU. Override the codec preference list per device class rather than trusting the default. More on the codec picture in the next section.

4. Simulcast tuning. Default simulcast layers are set for desktops. On phones, drop the top (1080p) layer to hold your CPU and thermal budget, and keep the 360p low layer for receivers on weak networks.

5. Doze and network changes. Phones move between Wi-Fi and cellular constantly, and Doze suspends radios. When iceConnectionState goes DISCONNECTED, wait 2–5 s (many drops self-heal) then call restartIce() with exponential backoff.

Which video codec should you use on Android in 2026?

For real-time on the median Android phone in 2026, default to VP8 for reach and H.264 for hardware-encode efficiency; reach for VP9 when quality matters and you can spend the CPU; treat AV1 as decode-only for now. VP8 and H.264 are both mandatory-to-implement WebRTC video codecs (RFC 7742), so VP8 stays the safe default for reach across a mixed Android fleet.

Android 2026 codec matrix: VP8, VP9, H.264, AV1 across hardware decode, hardware encode, CPU cost, verdict

Figure 4. AV1 hardware decode is limited to recent flagships; real-time AV1 encode on the median phone is not there yet.

The AV1 reality check. AV1 has been in WebRTC since 2021, but its hardware decode is still concentrated on recent flagships (Snapdragon 8 Gen 2 and later from 2023, Google Tensor G3+, Apple A17 Pro and M-series), not the median Android phone. Hardware encode is rarer still, and software AV1 encode is too CPU-hungry for real-time on most handsets. So AV1 is a useful receive-side option and a poor default for the outbound track today. Let the SFU negotiate it only where both sides have hardware encode, and keep VP8 and H.264 as the floor.

Jetpack Compose UI — rendering remote video without recomposition jank

SurfaceViewRenderer is a View, not a composable. Wrap it in AndroidView, remember the renderer instance so it is not re-created on every recomposition, and release it in DisposableEffect.

@Composable
fun RemoteVideoRenderer(track: VideoTrack, modifier: Modifier = Modifier) {
  val renderer = remember {
    SurfaceViewRenderer(context).apply { init(eglBase.eglBaseContext, null) }
  }
  DisposableEffect(track) {
    track.addSink(renderer)
    onDispose { track.removeSink(renderer); renderer.release() }
  }
  AndroidView(factory = { renderer }, modifier = modifier)
}

Recomposition stability got easier in 2026: Strong Skipping is on by default since Kotlin 2.0.20, so restartable composables skip even with unstable params. It helps, but a WebRTC video grid still needs care — stable keys per participant, hoisted state, and no allocation in the composable body. Our Jetpack Compose performance guide covers the recomposition traps that hit video grids specifically.

Recording, screen sharing, and PiP on Android

Recording. Do it on the SFU (cloud or self-hosted), not the phone. The Android client only emits its own MediaStream; client-side MediaRecorder capture is possible but burns battery, disk, and sync budget. Reserve it for offline single-party cases.

Screen share. Use MediaProjection + VirtualDisplay; libwebrtc’s ScreenCapturerAndroid wraps the projection token, and you declare a mediaProjection foreground service. The full walkthrough is in our Android WebRTC screen sharing guide.

Picture-in-picture. Plan PiP on phones (Android 8+) and on Android TV. Pause the low-tier simulcast layers when you enter PiP to save battery — some SDKs do it for you, others need a hook.

Network handling — congestion, ICE restart, and weak-network UX

Three patterns keep sessions alive on a phone that keeps changing networks:

1. ICE restart with backoff. On DISCONNECTED, wait 2–5 s before restartIce(). Many transient drops self-heal, and an immediate restart just thrashes.

2. Let the SFU drive bitrate. Use simulcast and let the server pick the layer per receiver; do not try to out-guess the bandwidth estimator from the app.

3. Honest weak-network UX. Show a low-quality avatar or last-frame still when stats fall below a threshold, rather than freezing on a stale frame. Test it deliberately — our method is in simulating slow network connections.

Testing an Android WebRTC client — unit, instrumented, and load

Unit. Mock the SDP and ICE callbacks and test your signalling state machine independently of libwebrtc — that is where the reconnect logic lives.

Instrumented. Espresso plus UI Automator can drive a real client; pair it with a CI-controlled SFU sandbox to assert join, leave, and freeze metrics on every build.

Synthetic load. Generate hundreds to thousands of fake clients against the SFU to see how the real Android client behaves in a full room. The quality metrics and thresholds worth asserting are in how to test WebRTC stream quality.

What does an Android WebRTC client cost to build and run?

Build cost splits into two buckets: the one-time engineering to ship the client, and the ongoing per-minute or infrastructure bill to run it. Here are indicative 2026 build ranges, then the running-cost arithmetic with the numbers shown.

Scope Included Indicative range Calendar
SDK-based MVP (LiveKit / Stream) 1:1 + group call, Compose UI, basic recording $15K–$35K 3–6 weeks
Custom UI on top of an SDK Branded grid, custom controls, PiP, in-app chat +$10K–$25K +2–4 weeks
Direct libwebrtc client (custom SFU) Bespoke signalling + capture + render $40K–$80K 8–12 weeks
Screen-sharing pack MediaProjection wiring, FGS, simulcast tuning $8K–$15K 1–2 weeks
PiP + Android TV Picture-in-picture, leanback, remote input $10K–$20K 1–3 weeks

Running cost is where the build-vs-buy line lives. Take 1,000,000 participant-minutes a month of 720p group calls. On a managed SDK like LiveKit’s Ship tier that is a $50 platform fee plus 1,000,000 × $0.0005 = $500, so $550/month. A self-hosted SFU at that volume is roughly two Hetzner AX servers plus TURN egress (~$400) and amortised on-call (~$300), so ~$700/month, and you have taken on the pager. On these numbers the lines cross around 2M minutes/month; only past that does a self-hosted SFU start to pay for the ops burden it adds.

Cost math: managed WebRTC SDK vs self-hosted SFU monthly cost at 100k, 1M and 5M participant-minutes in 2026

Figure 5. Managed minutes stay cheaper until real volume; the crossover only rewards you if you can staff a 24/7 on-call.

Mini-case: the Android WebRTC client behind BrainCert

The situation. BrainCert is the world’s first WebRTC + HTML5 virtual-classroom LMS, and it needed a native Android client that behaved on real hardware, not just a demo that worked on the newest Pixel. Live classes meant camera capture surviving backgrounding, clean audio routing over Bluetooth, and reconnects that did not drop a lesson mid-sentence.

The plan. We built the WebRTC infrastructure from the ground up and engineered the Android client around the five gotchas above: a camera/microphone foreground service so classes survived multitasking, MODE_IN_COMMUNICATION plus explicit audio-focus handling, per-device-class codec selection, phone-tuned simulcast, and restartIce() with backoff for Doze and Wi-Fi/LTE handoffs.

The result. BrainCert has delivered 500M+ real-time classroom minutes across 10 datacenters at 99.995% uptime, serving 100K+ customers on native Android, iOS, and web, and grew revenue from $1.5M (2021) to $1.9M (2023) to $3M (2024, a 58% jump year over year), bootstrapped, on the platform we built. Want a similar assessment of your Android stack? Book a 30-minute call.

A decision framework — pick your Android WebRTC path in five questions

Answer these five in order; the first strong signal usually settles the architecture.

1. Does your team have WebRTC experience? No → use an SDK (LiveKit, Stream, or Daily). Yes → libwebrtc directly is on the table.

2. What is the primary device class? Phones → default to 720p simulcast. Tablets or TV → bigger grid, plan for wide layouts, rotation, and PiP.

3. Compose or XML? Compose for new builds; XML only for legacy codebases or specific OEM constraints.

4. Do you need screen sharing? Yes → budget $8–$15K and plan a mediaProjection foreground service. No → defer to v2.

5. What is your concurrent-user target? Under a few hundred → SDK per-minute fees are fine. Past roughly two million minutes/month → pair the Android client with a self-hosted SFU, and staff the on-call.

Pitfalls we have watched Android WebRTC teams fall into

1. Skipping the foreground service. Sessions die on backgrounding and reviewers reject the app. Build it in sprint one, not after the first crash report.

2. Defaulting to 1080p. Phones overheat and drain. Default to 720p simulcast and opt up only on confirmed hardware.

3. Re-initialising EglBase on every recomposition. A classic Compose mistake; the second instance crashes the renderer. Wrap it in remember.

4. Ignoring AudioManager mode. Without MODE_IN_COMMUNICATION you fight echo, low volume, and Bluetooth routing forever.

5. Shipping Camera1 in 2026. It still runs, but newer devices and codec paths assume Camera2; you will hit obscure, hard-to-repro bugs.

KPIs — what to measure on the Android client

Quality KPIs. p95 join time under 4 s, freeze rate under 1%, audio MOS above 4.0, simulcast layer-up success above 90%.

Business KPIs. Crash-free sessions above 99.5%, day-1 retention movement after a performance release, Play Store “bad behaviour” Vitals under threshold.

Reliability KPIs. Reconnect success above 95%, Doze-mode session resumption under 5 s, foreground-service uptime 100% during active calls.

When NOT to use WebRTC on Android

Skip WebRTC when it is the wrong tool. Three cases: (a) one-way live broadcast to thousands — HLS or LL-HLS scales far cheaper; (b) a latency budget over about five seconds — LL-HLS is simpler and cheaper to run; (c) a team with neither SDK budget nor WebRTC experience and a tight deadline — a managed embed can buy you time while you plan the real build. Honesty here saves months.

Need a stage-matched Android WebRTC plan?

One call gets you an SDK pick, a Compose-friendly architecture, a screen-sharing budget, and an honest build-vs-buy read for your volume.

Book a 30-min call → WhatsApp → Email us →

FAQ

What is the easiest Android WebRTC SDK to start with?

LiveKit Android for Compose-first builds, Stream Video Android for chat-heavy products, and Daily for HIPAA-friendly contexts. All three ship a 1:1 plus group-call MVP in roughly 3–6 weeks.

Does Google still provide an official WebRTC Android library?

Not as a maintained prebuilt AAR. In 2026 most teams pull the community-maintained getstream/webrtc-android fork, which tracks upstream libwebrtc and ships the same org.webrtc package as a Gradle dependency.

Does WebRTC work in the background on Android?

Only if you wrap camera and mic capture in a foreground service of the right type (camera, microphone). Since Android 14 the type is mandatory and you cannot start such a service from the background; Android 15 additionally blocks starting a camera foreground service from BOOT_COMPLETED.

Which codec should an Android WebRTC app use?

Default to VP8 for reach (it is mandatory-to-implement per RFC 7742) and H.264 for efficient hardware encode. Use VP9 when quality matters and you can spend CPU. Treat AV1 as decode-only for now; real-time AV1 encode is limited to recent flagship phones in 2026.

How do we handle network changes from Wi-Fi to LTE?

Watch PeerConnection.iceConnectionState. On DISCONNECTED, wait 2–5 s and call restartIce() with exponential backoff. Most transient changes self-heal before the restart fires.

Compose or XML for the video UI?

Compose for any new build. Wrap SurfaceViewRenderer in AndroidView, remember the renderer to avoid recomposition re-init, and lean on Strong Skipping (default since Kotlin 2.0.20) plus stable keys per participant.

How do we record an Android WebRTC session?

Record on the SFU (cloud or self-hosted), not the device. Client-side MediaRecorder capture is possible but burns battery and disk; server-side recording is the production default.

Has Fora Soft shipped Android WebRTC products at scale?

Yes. We built the WebRTC infrastructure and native Android client behind BrainCert — 500M+ classroom minutes, 100K+ customers — and ship the Android client for ProVideoMeeting, with more under NDA. We are a WebRTC development team with 250+ projects since 2005.

Implementation

Android WebRTC screen sharing — the complete guide

MediaProjection, the foreground service, and simulcast tuning, worked end to end.

Compose performance

Jetpack Compose performance for video grids

The recomposition traps that hit WebRTC video grids specifically.

iOS counterpart

WebRTC in iOS — how to add video calls

The same playbook applied to iOS — useful when you build for both.

Topology

P2P vs MCU vs SFU for video conferencing

The topologies your Android client connects to, compared.

Security

WebRTC security in plain language

DTLS, SRTP, E2EE, and the holes you actually need to close.

Ready to ship a clean Android WebRTC client?

WebRTC on Android in 2026 is a solved problem when you respect the platform: a camera/microphone foreground service, audio focus, per-device codec selection, phone-tuned simulcast, and Doze-aware reconnects. SDK choice comes after those rules, not before — LiveKit for Compose-first, Stream for chat, Daily for HIPAA, Agora for APAC volume, libwebrtc for a custom SFU. Compose is the default UI; VP8 and H.264 are the default codecs; AV1 is decode-only for now.

If you want a stage-matched plan — SDK choice, screen sharing, recording, PiP, and a realistic calendar — the fastest next step is a 30-minute call. We will pick the path, scope the work, and tell you honestly when an SDK MVP beats a custom build. For the broader picture, our custom software development and video streaming Learn hub go deeper.

Talk to our Android engineering leads

Book a 30-minute call. We will scope the Android WebRTC client — SDK, capture, render, screen share, budget, and calendar — in one session.

Book a 30-min call → WhatsApp → Email us →

  • Development