A client comes to us with a task: view a surveillance camera directly in the app. Sounds simple, but in practice there are several issues. RTSP stream from an IP camera cannot be opened by native iOS and Android players directly. RTMP is outdated. WebRTC works but requires a signaling server. Low latency of 200 ms is a completely different architecture than HLS with a 5-second buffer. Over 5+ years, we have developed proven solutions for these scenarios and implemented more than 50 projects of streaming video.
In this article, we will break down real technical approaches: server-side transcoding, native RTSP decoder, and WebRTC client. You will learn how to implement PTZ control, recording, and multi-camera support. Get a consultation for your project.
Why RTSP cannot be opened by a native player?
AVPlayer does not support rtsp:// – only http(s):// and HLS. ExoPlayer officially removed RTSP in Media3 (although support exists via RtspMediaSource, it is unstable on some manufacturers with custom firmwares). On Flutter, there is no mature native RTSP player. As stated in Apple documentation, this is an architectural limitation.
There are two working approaches.
Server-side transcoding (RTSP → HLS/WebRTC)
A media server (MediaMTX, Nginx-RTMP + ffmpeg, Ant Media Server) receives RTSP from the camera and delivers an HLS or WebRTC endpoint to the client. The client receives HLS – AVPlayer / ExoPlayer read it without issues. HLS latency: 3-8 seconds with a standard 2-second chunk. This is acceptable for monitoring. For a door phone – no.
paths: cam1: source: rtsp://admin:[email protected]:554/stream1 hlsAlwaysRemux: yes The client connects to http://media-server/cam1/index.m3u8.
Native RTSP decoder in the app
iOS: VLCKit (MobileVLCKit) – a wrapper over libVLC. Supports RTSP, RTMP, H.264, H.265. VLCMediaPlayer with drawable = UIView renders directly into a view. Latency: 500-800 ms with a network buffer of 300 ms. Downside: binary +30 MB, App Store accepts without issues.
Custom path: FFmpeg via ffmpeg-kit-ios (FFmpegKitConfig.executeAsync), decode the stream and render via AVSampleBufferDisplayLayer. Gives full control over buffering and latency (can be reduced to 100-200 ms with rtsp_transport tcp and minimal analyzeduration). Harder to implement, but better results.
Android: ExoPlayer RtspMediaSource – for simple cases. For complex ones (RTSP over TCP, H.265, multi-stream cameras) – ijkplayer or FFmpegKit. ijkplayer works with Jetpack Compose via AndroidView.
Flutter: flutter_vlc_player (MobileVLCKit / libVLC Android) – cross-platform option. video_player plugin does not support RTSP.
What to choose: RTSP or WebRTC?
If latency is not critical (warehouse monitoring), RTSP via VLCKit/FFmpegKit is enough. If interactivity is important (door phone, PTZ camera control), WebRTC is the right choice with 100-300 ms latency.
WebRTC architecture: IoT camera → WebRTC-compatible media server (Janus, Kurento, MediaSoup, Ant Media) → mobile client via ICE/STUN/TURN.
iOS: WebRTC framework (pod 'GoogleWebRTC' or pod 'WebRTC-SDK'). Create RTCPeerConnection, receive SDP offer from server, answer, get RTCVideoTrack and render via RTCMTLVideoView (Metal rendering, hardware acceleration). Signaling – WebSocket (Starscream, URLSessionWebSocketTask).
Android: io.getstream:stream-webrtc-android or official WebRTC from Google. SurfaceViewRenderer for rendering VideoTrack.
Flutter: flutter_webrtc – uses native WebRTC under the hood. RTCVideoRenderer + RTCVideoView.
For NAT traversal, STUN (free Google stun.l.google.com:19302) and TURN server for symmetric NAT (Coturn on your own server) are mandatory.
How to implement PTZ control?
Pan/Tilt/Zoom via ONVIF or proprietary HTTP API of the camera. On mobile: UIPanGestureRecognizer → compute delta → send ONVIF ContinuousMove request via HTTP. Pinch → AbsoluteMove with zoom coordinate.
Debounce requests: do not send every gesture event – throttle(300ms), otherwise the camera cannot keep up with commands.
Recording and snapshots
Camera snapshot: cheaper to request JPEG snapshot URL directly from the camera (most IP cameras support http://cam/snapshot.jpg) than to capture a frame from the video stream.
Recording the stream on device: AVAssetWriter (iOS) writes CMSampleBuffer from decoded stream to MP4. On Android – MediaMuxer + MediaCodec. For server-side recording – ffmpeg -i rtsp://... -c copy output.mp4 via the media server.
Multi-camera
Camera list + stream thumbnails. Do not play all streams simultaneously – only the active one. Preview: static snapshot, refreshed every 5 seconds (URLSession.dataTask + UIImageView). Saves battery and traffic by 90%.
| Platform | RTSP library | Latency | Notes |
|---|---|---|---|
| iOS | MobileVLCKit | 500-800 ms | +30 MB, App Store approved |
| iOS | ffmpeg-kit | 100-200 ms | Full control |
| Android | ExoPlayer | 300-800 ms | Simple but unstable |
| Android | ijkplayer | 200-500 ms | Works with H.265 |
| Flutter | flutter_vlc_player | 500-800 ms | Cross-platform |
| Protocol | Latency | Client complexity | Notes |
|---|---|---|---|
| HLS (RTSP→HLS on server) | 3-8 sec | Low (native player) | Monitoring |
| RTSP (VLCKit/FFmpegKit) | 300-800 ms | Medium | Universal |
| WebRTC | 100-300 ms | High | Door phone, PTZ |
Our process
- Camera and network analysis – we check protocols, resolution, NAT traversal.
- Protocol and architecture selection – based on latency requirements.
- Player integration and test bench – we run a demo with one camera.
- Buffering and rendering optimization – achieve target latency.
- PTZ, recording, multi-camera – implement additional features.
- Testing on devices – verify on iOS, Android, different firmwares.
- Code and documentation handover – train the client's team.
We help choose the optimal protocol, saving your budget. Development cost is discussed after an audit of your project. Contact us for a free audit of your project. Order video streaming development – get a consultation on your project.
What's included in the work
- Architectural documentation with protocol selection and configurations
- Deployment of a demo environment with one camera for testing
- WebRTC integration with signaling server and TURN
- Training the client's team on code and configs
- Post-launch support: 12-month warranty on the solution
Timeline: approximately 3 days to 3 weeks depending on complexity. We'll evaluate your project within 2 business days. We guarantee functionality on all devices.







