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

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 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, the UI determines active status by checking:

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:

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:

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:

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:

// 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:

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:

// 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, 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.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →