camview

Live camera feed from the 4Brains Camera Controller over WebRTC, in one Flutter widget. Point it at your server and room with an API key — signaling is handled for you, and the video flows peer-to-peer (the server only brokers the handshake, so it never becomes a latency bottleneck).

Works on any Flutter target flutter_webrtc supports — iPad, phone, kiosk, TV.

Install

dependencies:
  camview: ^0.2.0

This pulls in flutter_webrtc, which needs a little native setup in your app.

iOS

ios/Podfile:

platform :ios, '13.0'

ios/Runner/Info.plist — allow the LAN connection and satisfy the WebRTC engine (the app only receives, but the plugin links these APIs):

<key>NSAppTransportSecurity</key>
<dict><key>NSAllowsLocalNetworking</key><true/></dict>
<key>NSCameraUsageDescription</key>
<string>Required by the video engine; this app only receives the live view.</string>
<key>NSMicrophoneUsageDescription</key>
<string>Required by the video engine; this app only receives the live view.</string>

Android

android/app/src/main/AndroidManifest.xml:

<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>

Set minSdkVersion 23 (or higher) in android/app/build.gradle.

Use

import 'package:camview/camview.dart';

FourBrainsCameraView(
  server: 'http://192.168.1.42:3000', // the server's LAN URL
  room: 'camera',                     // the room the controller broadcasts to
  apiKey: 'your-api-key',             // your 4Brains API key
  fit: CameraFit.cover,               // contain (default) or cover
  onStatus: (status) {
    // connecting | live | reconnecting | unauthorized
  },
  onSize: (w, h) => debugPrint('feed is ${w}x$h'),
)

Drop it into any layout — a bounded box, Positioned.fill, or an AspectRatio. It reconnects on its own if the network drops or the broadcaster restarts.

Status

Status Meaning
connecting Reaching the server, or waiting for the broadcaster's offer.
live Frames are arriving.
reconnecting Transport dropped, or the broadcaster went away. Retrying.
unauthorized The server rejected apiKey. Terminal — retrying cannot fix a bad key, so the widget stops.

Worth handling unauthorized distinctly in your UI: it is the one failure a user can act on, and it is otherwise indistinguishable from a weak network.

API

Property
server String Signaling server base URL.
room String Room shared with the broadcaster.
apiKey String Sent in the socket handshake. Required — the server refuses the connection without a valid key.
fit CameraFit contain (letterbox) or cover (crop).
iceServers List<Map> STUN/TURN. Empty for LAN; add for cross-network.
onStatus callback Connection state changes.
onSize callback Video pixel size.
placeholder WidgetBuilder Shown before the first frame.

How it fits together

Camera → 4Brains Camera Controller (broadcaster)      your app (this widget)
                    │  SDP/ICE via signaling server        │
                    └──────────────► server ◄──────────────┘
                       video is peer-to-peer, not via the server

The desktop controller captures the camera and broadcasts; your app is a viewer. Run the server locally (see the server's DOCKER.md). The API key is whatever you set in the server's API_KEYS — the same string goes in the controller and here.

LAN vs. internet

Default iceServers is empty — correct for devices on the same Wi-Fi (they connect directly via host candidates). To stream across networks you need a TURN server; pass it via iceServers.

Libraries

camview
Live camera feed from the 4Brains Camera Controller over WebRTC.