# FadCam Remote Streaming and Local Network Architecture: HLS Implementation Deep Dive

> Explore FadCam's HLS architecture for remote streaming and local network delivery. Learn how LiveM3U8Server and RemoteStreamManager enable zero-config local streaming with a privacy-first cloud fallback.

- Repository: [Faded/FadCam](https://github.com/anonfaded/FadCam)
- Tags: architecture
- Published: 2026-05-13

---

**FadCam implements a hybrid streaming architecture where LiveM3U8Server exposes HLS endpoints via NanoHTTPD for LAN delivery, while RemoteStreamManager maintains a thread-safe circular buffer of fMP4 fragments that feeds both local HTTP clients and an optional CloudStreamUploader for remote relay, enabling zero-configuration local streaming with privacy-first cloud fallback.**

FadCam (anonfaded/FadCam) is an open-source Android surveillance camera application that implements HTTP Live Streaming (HLS) directly on the device without external server dependencies. The FadCam remote streaming and local network architecture centers on two tightly-coupled Java components that manage segment buffering, HTTP delivery, and optional cloud bridging while maintaining strict privacy controls over viewer metadata.

## Core Architecture Components

The streaming stack consists of two primary classes that handle HTTP serving and state management.

### LiveM3U8Server: The NanoHTTPD Engine

**`LiveM3U8Server`** (located in [`app/src/main/java/com/fadcam/streaming/LiveM3U8Server.java`](https://github.com/anonfaded/FadCam/blob/main/app/src/main/java/com/fadcam/streaming/LiveM3U8Server.java)) extends NanoHTTPD to provide a lightweight HTTP server bound to `0.0.0.0`, making the Android device reachable from any host on the same LAN or Wi-Fi hotspot. It exposes several endpoints: `/live.m3u8` (the HLS playlist), `/init.mp4` (the fMP4 initialization segment), `/seg-*.m4s` (individual fragments), and `/status` (JSON metrics).

The server adds permissive **CORS headers** (`Access-Control-Allow-Origin: *`) to every response, allowing browser-based players like HLS.js to consume the stream without proxy configuration. To avoid parsing errors in browser clients, JSON responses served by this component are intentionally uncompressed.

### RemoteStreamManager: The Circular Buffer State Machine

**`RemoteStreamManager`** (located in [`app/src/main/java/com/fadcam/streaming/RemoteStreamManager.java`](https://github.com/anonfaded/FadCam/blob/main/app/src/main/java/com/fadcam/streaming/RemoteStreamManager.java)) operates as a singleton that owns all streaming state, including a fixed-size **circular buffer** (`BUFFER_SIZE = 15` slots, representing approximately 15 seconds of video). This buffer stores fMP4 fragments generated by the camera encoder.

Thread safety is enforced via a **read-write lock** that protects concurrent access between the encoder thread (`FragmentedMp4MuxerWrapper`) and the HTTP server threads. The manager supports two distinct **streaming modes**: `STREAM_ONLY` (memory-only, no local file persistence) and `STREAM_AND_SAVE` (simultaneous local recording and streaming).

The fragment lifecycle begins when `onInitializationSegment()` receives the `ftyp+moov` init segment, clearing stale fragments and optionally triggering cloud upload. Subsequent calls to `onFragmentComplete()` validate sequence numbers, evict old data to maintain the 15-slot window, update `oldestSequence`, and push the fragment to `CloudStreamUploader` when remote streaming is enabled.

## Local Network Streaming Implementation

FadCam's local streaming requires no router configuration or external DNS. When streaming starts, the application initializes the server on a configurable port (default **8080**), and the device becomes immediately accessible to any client on the network.

### Zero-Configuration Discovery and CORS

Because `LiveM3U8Server` binds to `0.0.0.0` rather than `localhost`, it accepts connections from any network interface. The implementation prints the listening port to logcat, allowing users to connect via `http://<phone_ip>:8080/`.

The server implements **stateless client tracking**, recording only the client IP address for basic metrics (fragment download counts) without persisting personal data. All HTTP responses include the header `Access-Control-Allow-Origin: *`, enabling browser-based players to embed the stream directly.

To start the server programmatically, the application uses:

```java
int httpPort = 8080;                                   // default port – can be changed in UI
LiveM3U8Server server = new LiveM3U8Server(getApplicationContext(), httpPort);
server.start();                                       // starts NanoHTTPD on 0.0.0.0

```

*Source:* [LiveM3U8Server constructor & start-up](https://github.com/anonfaded/FadCam/blob/master/app/src/main/java/com/fadcam/streaming/LiveM3U8Server.java)

### Serving the HLS Playlist

When a client requests `/live.m3u8`, the server invokes `servePlaylist()` to construct a dynamic HLS manifest from the current buffer state. The method reads the initialization segment and available fragments from `RemoteStreamManager`, generating a playlist using HLS version 7 with independent segments.

A typical response looks like:

```text
#EXTM3U
#EXT-X-VERSION:7
#EXT-X-INDEPENDENT-SEGMENTS
#EXT-X-TARGETDURATION:4
#EXT-X-MEDIA-SEQUENCE:123
#EXT-X-MAP:URI="/init.mp4"
#EXTINF:1.000,
/seg-123.m4s
#EXTINF:1.000,
/seg-124.m4s

```

*Source:* [servePlaylist() implementation](https://github.com/anonfaded/FadCam/blob/master/app/src/main/java/com/fadcam/streaming/LiveM3U8Server.java#L274)

## Fragment Ingestion and Buffer Management

The encoder (`FragmentedMp4MuxerWrapper` in the playback package) feeds data into the streaming pipeline through two primary callbacks.

### Initialization and Segment Handling

When recording begins, the encoder calls `onInitializationSegment()` to provide the `ftyp+moov` box. This method clears the circular buffer, stores the init segment, and optionally uploads it to the cloud relay if remote streaming is active.

As the encoder completes each fMP4 fragment (composed of `moof+mdat` boxes), it invokes `onFragmentComplete()`:

```java
RemoteStreamManager manager = RemoteStreamManager.getInstance();
int seq = ...;                     // sequence from the muxer
byte[] fmp4Fragment = ...;        // moof+mdat bytes
manager.onFragmentComplete(seq, fmp4Fragment);

```

*Source:* [onFragmentComplete](https://github.com/anonfaded/FadCam/blob/master/app/src/main/java/com/fadcam/streaming/RemoteStreamManager.java#L442)

This method validates the sequence number against the expected counter, evicts the oldest fragment if the buffer exceeds 15 slots, updates `oldestSequence`, and notifies any cloud uploaders.

## Remote Cloud Integration

While local streaming operates peer-to-peer, FadCam optionally bridges to remote viewers via a cloud relay, implemented without exposing the device's IP address.

### CloudStreamUploader and Relay Mechanics

**`CloudStreamUploader`** (located in [`app/src/main/java/com/fadcam/streaming/CloudStreamUploader.java`](https://github.com/anonfaded/FadCam/blob/main/app/src/main/java/com/fadcam/streaming/CloudStreamUploader.java)) receives the init segment and each subsequent fMP4 fragment from `RemoteStreamManager`. It forwards these to a lightweight cloud relay (typically a Node.js service), which then redistributes the stream to remote clients. This architecture keeps the Android device behind NAT while still enabling global access.

### CloudStatusManager and Privacy-First Metrics

**`CloudStatusManager`** provides heartbeat functionality, pushing a concise status JSON to the relay every **2 seconds** (`CLOUD_STATUS_INTERVAL_MS`). This status includes aggregated viewer counts, recording state, and network health metrics generated by `NetworkMonitor`, but deliberately omits individual client IP addresses to preserve privacy.

The status JSON served locally at `/status` is cached for **1000 milliseconds** (`STATUS_CACHE_MS`) to minimize CPU overhead. It contains fields such as `streaming`, `isRecording`, `fragmentsBuffered`, `torchState`, `batteryDetails`, and `networkHealth`.

To query the status endpoint locally:

```bash
curl http://<phone_ip>:8080/status

```

*Source:* [getStatusJson() method](https://github.com/anonfaded/FadCam/blob/master/app/src/main/java/com/fadcam/streaming/RemoteStreamManager.java#L718)

## Client Integration Examples

The permissive CORS policy allows any web client to consume the stream. A minimal browser implementation using HLS.js requires no special configuration:

```html
<video id="player" controls autoplay></video>
<script src="https://cdn.jsdelivr.net/npm/hls.js@latest"></script>
<script>
  if (Hls.isSupported()) {
    const hls = new Hls();
    hls.loadSource('http://<phone_ip>:8080/live.m3u8');
    hls.attachMedia(document.getElementById('player'));
  }
</script>

```

Because the server sends `Access-Control-Allow-Origin: *`, this code functions when opened from any origin, including local files or external domains.

## Security Considerations

While the base implementation prioritizes ease of use, **`RemoteAuthManager`** (located in [`app/src/main/java/com/fadcam/streaming/RemoteAuthManager.java`](https://github.com/anonfaded/FadCam/blob/main/app/src/main/java/com/fadcam/streaming/RemoteAuthManager.java)) provides optional token-based authentication for the `/auth/*` routes. When enabled, clients must present valid tokens to access streaming endpoints, protecting the video feed on untrusted networks.

## Summary

- **FadCam** implements a self-contained HLS server using **NanoHTTPD** via [`LiveM3U8Server.java`](https://github.com/anonfaded/FadCam/blob/main/LiveM3U8Server.java), binding to `0.0.0.0` for LAN-wide accessibility.
- **RemoteStreamManager** maintains a **thread-safe circular buffer** of 15 fMP4 fragments (~15 seconds), coordinating between the encoder and HTTP threads with read-write locks.
- The system supports **dual-mode operation**: `STREAM_ONLY` for ephemeral viewing or `STREAM_AND_SAVE` for simultaneous local recording and streaming.
- **Zero-configuration local streaming** is achieved through permissive CORS headers and stateless IP tracking, while **cloud integration** uses `CloudStreamUploader` and `CloudStatusManager` to relay fragments and metrics without exposing the device IP.
- Optional **token-based authentication** via `RemoteAuthManager` secures endpoints when operating on public networks.

## Frequently Asked Questions

### How does FadCam achieve zero-configuration LAN streaming?

FadCam binds the `LiveM3U8Server` to `0.0.0.0` rather than `localhost`, allowing it to accept connections from any device on the same network segment. The server adds `Access-Control-Allow-Origin: *` headers to all responses, eliminating the need for browser proxies or CORS workarounds.

### What is the purpose of the 15-fragment buffer limit?

The `BUFFER_SIZE = 15` constant in `RemoteStreamManager` creates a rolling window of approximately 15 seconds of video. This balances latency against memory usage, ensuring that live viewers remain near real-time while preventing unbounded memory growth on the Android device.

### How does remote cloud mode protect user privacy?

According to the anonfaded/FadCam source code, the `CloudStatusManager` sends only aggregated metrics to the relay server every 2 seconds. Individual client IP addresses are never transmitted, and the `CloudStreamUploader` forwards fragments without embedding device identification metadata.

### Which component handles concurrent access between recording and streaming?

`RemoteStreamManager` uses a **read-write lock** to synchronize access between the encoder thread (writing via `FragmentedMp4MuxerWrapper`) and multiple HTTP server threads (reading fragments for `/seg-*.m4s` requests). This prevents race conditions when the circular buffer is simultaneously updated and read.