# Cloud Streaming Mode vs Local Streaming in FadCam Remote: Key Differences Explained

> Understand FadCam Remote's Cloud Streaming vs Local Streaming. Choose cloud for global access or local for LAN/Wi-Fi streaming without internet upload. Learn key differences now.

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

---

**Cloud Streaming Mode uploads encrypted video fragments to a FadSec cloud relay for global access via dashboard URL, while Local Streaming runs an HTTP server directly on your Android device serving HLS fragments over LAN/Wi-Fi without internet upload requirements.**

The FadCam Remote feature in the [anonfaded/FadCam](https://github.com/anonfaded/FadCam) repository provides two distinct architectures for live video delivery. Both modes share the same circular fragment buffer and UI controls but diverge fundamentally in transport mechanisms, authentication requirements, and how they report streaming status to the interface.

## Transport and Network Architecture

### Local Streaming HTTP Server

In Local Streaming mode, the app starts `RemoteStreamService` which instantiates a lightweight HTTP server on the device. This server reads HLS fragments from the shared `RemoteStreamManager` buffer and serves them directly to clients on the same network.

The transport operates over standard HTTP on your local Wi-Fi, displaying a root URL in the format:

```

http://<device-ip>:<port>/

```

According to the source in [`RemoteFragment.java`](https://github.com/anonfaded/FadCam/blob/main/RemoteFragment.java), the UI determines active status by checking:

```java
isStreaming = streamService != null && serviceBound && streamService.isServerRunning();

```

This mode requires no cloud authentication and consumes only local bandwidth, making it ideal for low-latency home network viewing.

### Cloud Streaming HTTPS Upload

Cloud Streaming Mode bypasses the local HTTP server entirely. Instead, `CloudStreamUploader` pushes encrypted fragments via HTTPS to `https://live.fadseclab.com:8443`. The implementation uses a dedicated `ExecutorService` with a 2-thread pool to prevent UI blocking during uploads.

Key characteristics include:
- **Encryption**: All segments pass through `SegmentEncryptor` before transmission
- **Endpoint**: Init segment uploads via `PUT /upload/{user_uuid}/{device_id}/init.mp4` and media fragments to `.../seg-{n}.m4s`
- **Dashboard URL**: Viewers access the stream at `https://fadcam.fadseclab.com/stream/<device-id>/`

Unlike Local mode, the UI status relies on `RemoteStreamManager.isStreamingEnabled()` rather than the HTTP server's running state, as implemented in `RemoteFragment.updateUI()`.

## Authentication and Requirements

**Local Streaming** requires no account linkage. As long as the device and viewer share a network, the stream is accessible via the displayed IP address.

**Cloud Streaming** demands a linked FadSec-cloud account with valid JWT and refresh tokens. The mode selection logic in `RemoteFragment` checks:

```java
boolean canUseCloud = cloudAuthManager.isLinked() && (hasValidToken || hasRefreshToken);

```

The `CloudStreamUploader` extracts the user UUID from the JWT's `sub` claim for upload path construction, and the `CloudStatusManager` periodically POSTs status JSON to the relay every 2 seconds (`CLOUD_STATUS_INTERVAL_MS = 2000`) to populate the dashboard with viewer counts and battery statistics.

## UI Behavior and Implementation

### Mode Selection Logic

The streaming mode constants define the integer values used throughout the codebase:

```java
private static final int MODE_LOCAL = 0;
private static final int MODE_CLOUD = 1;

```

When building the mode picker, the app conditionally enables the cloud option based on authentication state. If unavailable, the option appears disabled with a link-required hint. After selection, the legacy cloud streaming row is hidden via `cloudStreamingRow.setVisibility(View.GONE)` to prevent confusion with the new picker interface.

### Status Detection Differences

The critical architectural divergence appears in how the UI confirms streaming status:

```java
boolean isCloudMode = cloudPrefs.getInt(KEY_STREAMING_MODE, MODE_LOCAL) == MODE_CLOUD;
if (isCloudMode) {
    // Cloud: Manager-based status, dashboard URL
    isStreaming = RemoteStreamManager.getInstance().isStreamingEnabled();
    rootUrlText.setText("https://fadcam.fadseclab.com/stream/" + deviceId + "/");
} else {
    // Local: Server-based status, IP address
    isStreaming = streamService != null && serviceBound && streamService.isServerRunning();
    rootUrlText.setText("http://" + streamService.getDeviceIpWithPort() + "/");
}

```

## Code Examples

### Enabling Cloud Mode

To programmatically activate Cloud Streaming:

```java
// Switch preference to cloud mode
fragment.setStreamingMode(RemoteFragment.MODE_CLOUD);

// Enable the streaming manager
RemoteStreamManager.getInstance().setStreamingEnabled(true);

// Start the cloud uploader
CloudStreamUploader.getInstance(context).setEnabled(true);

```

This configuration displays the dashboard URL in the UI and begins uploading encrypted fragments to the relay.

### Switching to Local Mode

To return to Local Streaming:

```java
fragment.setStreamingMode(RemoteFragment.MODE_LOCAL);
RemoteStreamManager.getInstance().setStreamingEnabled(true);
CloudStreamUploader.getInstance(context).setEnabled(false);

```

The UI now shows the device's local IP address, and the `RemoteStreamService` HTTP server serves fragments directly.

### Starting and Stopping Streams

Both modes share the same service lifecycle for background operation:

```java
// Start streaming (common to both modes)
RemoteStreamManager.getInstance().setStreamingEnabled(true);
Intent intent = new Intent(context, RemoteStreamService.class);
context.startForegroundService(intent);
context.bindService(intent, serviceConnection, Context.BIND_AUTO_CREATE);

// Stop streaming
context.unbindService(serviceConnection);
context.stopService(intent);
RemoteStreamManager.getInstance().setStreamingEnabled(false);

```

Note that in Cloud mode, `RemoteStreamService` still runs for background task management, but its HTTP server remains unused for video delivery.

## Performance and Battery Impact

**Local Streaming** minimizes battery consumption by avoiding internet uploads and encryption overhead. It serves fragments directly from the 15-fragment circular buffer maintained in `RemoteStreamManager`.

**Cloud Streaming** incurs additional battery and network costs due to:
- Continuous HTTPS upload operations via `CloudStreamUploader`
- AES encryption via `SegmentEncryptor` for each fragment
- Periodic status pushes every 2 seconds through `CloudStatusManager`

The trade-off provides global accessibility without VPN requirements and removes the need for viewers to know the device's local IP address.

## Summary

- **Local Streaming** runs an HTTP server on `RemoteStreamService` serving HLS fragments via `http://<device-ip>:<port>/` with status determined by `isServerRunning()`
- **Cloud Streaming Mode** uploads encrypted fragments to `https://live.fadseclab.com:8443` via `CloudStreamUploader`, viewable at dashboard URLs, with status tracked via `RemoteStreamManager.isStreamingEnabled()`
- Both modes share the same `RemoteStreamManager` circular buffer (15 fragments) and service lifecycle but diverge in transport, authentication, and UI status detection
- Cloud mode requires FadSec account linkage with valid JWT tokens; Local mode requires only shared network access
- Cloud mode consumes additional battery for encryption and uploads, while Local mode offers lower latency on trusted networks

## Frequently Asked Questions

### Does Cloud Streaming work without an internet connection?

No. Cloud Streaming Mode requires an active internet connection to upload encrypted segments to the FadSec relay at `live.fadseclab.com:8443`. The `CloudStreamUploader` establishes HTTPS connections to this endpoint, and `CloudStatusManager` requires connectivity to push status updates every 2 seconds. For offline scenarios, use Local Streaming over Wi-Fi.

### Can I switch between streaming modes while recording?

Yes. The app stores the mode preference via `cloudPrefs.getInt(KEY_STREAMING_MODE, MODE_LOCAL)` and updates the UI through `updateUI()`. However, you should toggle `CloudStreamUploader.setEnabled(false)` when switching from Cloud to Local to prevent continued background uploads, and ensure `RemoteStreamService` is properly bound to query `isServerRunning()` for local status detection.

### Why does Cloud Streaming hide the local IP address in the UI?

When `isCloudMode` evaluates to true in [`RemoteFragment.java`](https://github.com/anonfaded/FadCam/blob/main/RemoteFragment.java), the UI replaces the local IP display with the dashboard URL format. This occurs because Cloud mode does not use the local HTTP server for client connections. The `cloudStreamingRow` is also set to `View.GONE` to clean up the interface, as the cloud functionality is accessed through the picker rather than legacy toggle switches.

### What happens to the local HTTP server when Cloud Streaming is active?

The `RemoteStreamService` continues running in the background when Cloud Streaming is enabled, as it handles other background tasks, but its HTTP server is not queried for status or video delivery. The UI relies exclusively on `RemoteStreamManager.isStreamingEnabled()` to determine streaming state, and fragments route through `CloudStreamUploader` rather than being served locally.