Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 45 additions & 0 deletions include/rootstream.h
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,34 @@
/* RootStream code format: base64(pubkey) + "@" + hostname */
#define ROOTSTREAM_CODE_MAX_LEN 128

/* Simple fallback selection macro (PHASE 0)
*
* This macro simplifies backend initialization with automatic fallback.
* Reserved for future phases (1-6) when additional fallback backends are added.
*
* Example usage (future):
* int result;
* const char *name;
* TRY_INIT_BACKEND(ctx, primary_init, "Primary", fallback_init, "Fallback", &result, &name);
* if (result) {
* ctx->active_backend.some_name = name;
* }
*/
#define TRY_INIT_BACKEND(ctx, primary_fn, primary_name, fallback_fn, fallback_name, result_ptr, name_ptr) \
do { \
if ((primary_fn)(ctx) == 0) { \
*(name_ptr) = (primary_name); \
*(result_ptr) = 1; /* Mark as initialized */ \
} else if ((fallback_fn) != NULL && (fallback_fn)(ctx) == 0) { \
printf("INFO: Primary failed, using fallback: %s\n", (fallback_name)); \
*(name_ptr) = (fallback_name); \
*(result_ptr) = 1; \
} else { \
printf("ERROR: Both primary and fallback failed\n"); \
*(result_ptr) = 0; \
} \
} while(0)

/* ============================================================================
* CAPTURE - Multi-backend framebuffer capture
* ============================================================================ */
Expand Down Expand Up @@ -458,6 +486,23 @@ typedef struct rootstream_ctx {
bool is_host; /* Host mode (streamer) */
uint64_t last_video_ts_us; /* Last received video timestamp */
uint64_t last_audio_ts_us; /* Last received audio timestamp */

/* Backend tracking (added in PHASE 0) */
struct {
const char *capture_name; /* Name of active capture backend */
const char *encoder_name; /* Name of active encoder backend */
const char *audio_cap_name; /* Name of active audio capture backend */
const char *audio_play_name; /* Name of active audio playback backend */
const char *decoder_name; /* Name of active decoder backend */
const char *display_name; /* Name of active display backend */
} active_backend;

/* User preferences for backend override */
struct {
const char *capture_override; /* User-specified capture backend */
const char *encoder_override; /* User-specified encoder backend */
bool verbose; /* Print fallback attempts */
} backend_prefs;
} rootstream_ctx_t;

/* Encoder backend abstraction for multi-tier fallback */
Expand Down
14 changes: 14 additions & 0 deletions src/core.c
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,20 @@ int rootstream_init(rootstream_ctx_t *ctx) {
printf(" %s\n", ctx->keypair.rootstream_code);
printf("\n");

/* Initialize backend tracking (PHASE 0) */
ctx->active_backend.capture_name = "uninitialized";
ctx->active_backend.encoder_name = "uninitialized";
ctx->active_backend.audio_cap_name = "uninitialized";
ctx->active_backend.audio_play_name = "uninitialized";
ctx->active_backend.decoder_name = "uninitialized";
ctx->active_backend.display_name = "uninitialized";

ctx->backend_prefs.capture_override = NULL;
ctx->backend_prefs.encoder_override = NULL;
ctx->backend_prefs.verbose = false;

printf("INFO: Backend infrastructure initialized\n");

return 0;
}

Expand Down
15 changes: 14 additions & 1 deletion src/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -285,6 +285,7 @@ int main(int argc, char **argv) {
{"no-discovery",no_argument, 0, 'n'},
{"latency-log", no_argument, 0, 'l'},
{"latency-interval", required_argument, 0, 'i'},
{"backend-verbose", no_argument, 0, 0},
{0, 0, 0, 0}
};

Expand All @@ -298,10 +299,19 @@ int main(int argc, char **argv) {
const char *record_file = NULL;
bool latency_log = false;
uint64_t latency_interval_ms = 1000;
bool backend_verbose = false;

int opt;
while ((opt = getopt_long(argc, argv, "hvqLsp:d:b:r:nli:", long_options, NULL)) != -1) {
int option_index = 0;
while ((opt = getopt_long(argc, argv, "hvqLsp:d:b:r:nli:", long_options, &option_index)) != -1) {
switch (opt) {
case 0:
/* Long option without short equivalent */
if (strcmp(long_options[option_index].name, "backend-verbose") == 0) {
backend_verbose = true;
printf("INFO: Backend selection verbose mode enabled\n");
}
break;
case 'h':
print_usage(argv[0]);
return 0;
Expand Down Expand Up @@ -367,6 +377,9 @@ int main(int argc, char **argv) {
return 1;
}

/* Set backend verbose mode if requested */
ctx.backend_prefs.verbose = backend_verbose;

if (latency_init(&ctx.latency, 240, latency_interval_ms, latency_log) < 0) {
fprintf(stderr, "WARNING: Latency logging disabled due to init failure\n");
}
Expand Down
43 changes: 43 additions & 0 deletions src/service.c
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,7 @@ int service_run_host(rootstream_ctx_t *ctx) {
if (backends[backend_idx].init_fn(ctx) == 0) {
printf("✓ Capture backend '%s' initialized successfully\n", backends[backend_idx].name);
ctx->capture_backend = &backends[backend_idx];
ctx->active_backend.capture_name = backends[backend_idx].name;
break;
} else {
printf("WARNING: Capture backend '%s' failed, trying next...\n", backends[backend_idx].name);
Expand Down Expand Up @@ -268,6 +269,7 @@ int service_run_host(rootstream_ctx_t *ctx) {
if (init_result == 0) {
printf("✓ Encoder backend '%s' initialized successfully\n", backend->name);
ctx->encoder_backend = backend;
ctx->active_backend.encoder_name = backend->name;
encoder_initialized = true;

/* Warn if using fallback (software or raw) */
Expand Down Expand Up @@ -345,6 +347,7 @@ int service_run_host(rootstream_ctx_t *ctx) {
if (capture_backends[capture_idx].init_fn(ctx) == 0) {
printf("✓ Audio capture backend '%s' initialized\n", capture_backends[capture_idx].name);
ctx->audio_capture_backend = &capture_backends[capture_idx];
ctx->active_backend.audio_cap_name = capture_backends[capture_idx].name;
break;
} else {
printf("WARNING: Audio capture backend '%s' failed, trying next...\n",
Expand All @@ -355,6 +358,7 @@ int service_run_host(rootstream_ctx_t *ctx) {

if (!ctx->audio_capture_backend) {
printf("WARNING: All audio capture backends failed, streaming video only\n");
ctx->active_backend.audio_cap_name = "disabled";
} else {
/* Initialize Opus encoder */
printf("INFO: Initializing Opus encoder...\n");
Expand All @@ -364,11 +368,13 @@ int service_run_host(rootstream_ctx_t *ctx) {
ctx->audio_capture_backend->cleanup_fn(ctx);
}
ctx->audio_capture_backend = NULL;
ctx->active_backend.audio_cap_name = "disabled";
}
}
} else {
printf("INFO: Audio disabled in settings\n");
ctx->audio_capture_backend = NULL;
ctx->active_backend.audio_cap_name = "disabled";
}

/* Announce service */
Expand All @@ -393,6 +399,17 @@ int service_run_host(rootstream_ctx_t *ctx) {
return -1;
}

/* Report active backends (PHASE 0) */
printf("\n");
printf("╔════════════════════════════════════════════════╗\n");
printf("║ RootStream Backend Status ║\n");
printf("╚════════════════════════════════════════════════╝\n");
printf("Capture: %s\n", ctx->active_backend.capture_name);
printf("Encoder: %s\n", ctx->active_backend.encoder_name);
printf("Audio Cap: %s\n", ctx->active_backend.audio_cap_name);
printf("Audio Play: %s\n", ctx->active_backend.audio_play_name);
printf("\n");

/* Main loop */
while (service_running && ctx->running) {
uint64_t loop_start_us = get_timestamp_us();
Expand Down Expand Up @@ -525,13 +542,25 @@ int service_run_client(rootstream_ctx_t *ctx) {
fprintf(stderr, "ERROR: Decoder initialization failed\n");
return -1;
}
/* Set decoder backend name based on platform
* NOTE: This is a simple platform check. For Phase 0, we assume:
* - Windows uses Media Foundation decoder
* - Linux/Unix uses VA-API decoder
* Future phases could add runtime detection if needed.
*/
#ifdef _WIN32
ctx->active_backend.decoder_name = "Media Foundation";
#else
ctx->active_backend.decoder_name = "VA-API";
#endif

/* Initialize display (SDL2 window) */
if (display_init(ctx, "RootStream Client", 1920, 1080) < 0) {
fprintf(stderr, "ERROR: Display initialization failed\n");
rootstream_decoder_cleanup(ctx);
return -1;
}
ctx->active_backend.display_name = "SDL2";

/* Initialize audio playback with fallback */
if (ctx->settings.audio_enabled) {
Expand Down Expand Up @@ -577,6 +606,7 @@ int service_run_client(rootstream_ctx_t *ctx) {
if (playback_backends[playback_idx].init_fn(ctx) == 0) {
printf("✓ Audio playback backend '%s' initialized\n", playback_backends[playback_idx].name);
ctx->audio_playback_backend = &playback_backends[playback_idx];
ctx->active_backend.audio_play_name = playback_backends[playback_idx].name;
break;
} else {
printf("WARNING: Audio playback backend '%s' failed, trying next...\n",
Expand All @@ -587,6 +617,7 @@ int service_run_client(rootstream_ctx_t *ctx) {

if (!ctx->audio_playback_backend) {
printf("WARNING: All audio playback backends failed, watching video only\n");
ctx->active_backend.audio_play_name = "disabled";
} else {
/* Initialize Opus decoder */
printf("INFO: Initializing Opus decoder...\n");
Expand All @@ -596,11 +627,13 @@ int service_run_client(rootstream_ctx_t *ctx) {
ctx->audio_playback_backend->cleanup_fn(ctx);
}
ctx->audio_playback_backend = NULL;
ctx->active_backend.audio_play_name = "disabled";
}
}
} else {
printf("INFO: Audio disabled in settings\n");
ctx->audio_playback_backend = NULL;
ctx->active_backend.audio_play_name = "disabled";
}

printf("✓ Client initialized - ready to receive video and audio\n");
Expand All @@ -609,6 +642,16 @@ int service_run_client(rootstream_ctx_t *ctx) {
ctx->latency.report_interval_ms, ctx->latency.capacity);
}

/* Report active backends (PHASE 0) */
printf("\n");
printf("╔════════════════════════════════════════════════╗\n");
printf("║ RootStream Client Backend Status ║\n");
printf("╚════════════════════════════════════════════════╝\n");
printf("Decoder: %s\n", ctx->active_backend.decoder_name);
printf("Display: %s\n", ctx->active_backend.display_name);
printf("Audio Play: %s\n", ctx->active_backend.audio_play_name);
printf("\n");

/* Allocate decode buffer */
frame_buffer_t decoded_frame = {0};

Expand Down
Loading