Files
2026-10-12 18:31:34 +02:00

184 lines
7.6 KiB
C

/* Capture scheduler and histogram processing. */
static int tick(void *unused) {
(void)unused;
uint64_t now = milliseconds();
unsigned long acknowledged = receiver_runtime.output.acknowledgements;
output_tick(&receiver_runtime.output, &playback_json, now);
if (receiver_runtime.output.stage == OUTPUT_FAILED &&
(capture_ctx.handle || receiver_runtime.starting)) {
char error[128];
snprintf(error, sizeof(error), "%s", receiver_runtime.output.error);
release_capture(false);
set_error(error);
retry_update(&retries, boot_enabled() && geteuid() == 0, false, now);
retry_failure(&retries, now);
}
if (receiver_runtime.output.stage == OUTPUT_FAILED && receiver_runtime.clear_pending) {
receiver_runtime.clear_pending = false;
output_close(&receiver_runtime.output);
}
if (receiver_runtime.starting && (piccap_owned || capture_source != SOURCE_HISTOGRAM)) {
receiver_runtime.starting = false;
output_close(&receiver_runtime.output);
}
if (receiver_runtime.output.acknowledgements > acknowledged && capture_ctx.handle) {
capture_ctx.frames += receiver_runtime.output.acknowledgements - acknowledged;
capture_ctx.last_output_at = now;
last_error[0] = 0;
}
if (receiver_runtime.clear_pending && receiver_runtime.output.stage == OUTPUT_READY) {
receiver_runtime.clear_pending = false;
output_stop(&receiver_runtime.output, now, true);
} else if (receiver_runtime.starting && receiver_runtime.output.stage == OUTPUT_READY) {
free(receiver_layout.blends);
receiver_layout.blends = receiver_runtime.output.mapping;
receiver_runtime.output.mapping = NULL;
receiver_layout.count = receiver_runtime.output.count;
receiver_runtime.starting = false;
initialize_capture(NULL, NULL);
if (!capture_ctx.handle)
retry_failure(&retries, now);
}
model_tick(now);
test_tick(&playback_json, now);
layout_refresh_tick(&playback_json, now, capture_ctx.handle || capture_source == SOURCE_PICCAP,
&receiver_layout);
stream_tick(&playback_json, now, handover_enabled && !retries.paused && !retries.blocked);
bool owned = handover_tick();
bridge_tick(now, &capture_ctx);
if (owned)
return 1;
if (!capture_ctx.handle) {
if (receiver_runtime.starting || receiver_runtime.clear_pending ||
receiver_runtime.output.closing)
return 1;
retry_update(&retries, boot_enabled() && geteuid() == 0, false, milliseconds());
if (retry_take(&retries, milliseconds())) {
start(NULL, NULL, NULL);
if (!capture_ctx.handle && !receiver_runtime.starting)
retry_failure(&retries, milliseconds());
}
return 1;
}
if (capture_ctx.revoked) {
set_error("LG revoked capture; recovering after source transition");
capture_ctx.recoveries++;
capture_ctx.recovery_started = milliseconds();
release_capture(false);
retry_source_transition(&retries, milliseconds());
return 1;
}
if (capture_ctx.settling) {
capture_ctx.settling--;
return 1;
}
if (!CALIBRATION_ACTIVE && capture_ctx.letterbox_mode && capture_ctx.preset == 1 &&
milliseconds() >= capture_ctx.next_border_probe) {
if (vt_preset(capture_ctx.handle, 0) != 0) {
set_error("Border probe preset failed");
stop_capture();
return 1;
}
capture_ctx.preset = 0;
capture_ctx.settling = 2;
return 1;
}
if (capture_ctx.sample_at && now - capture_ctx.sample_at < target_capture_interval(&target))
return 1;
Histogram hist[16] = {0};
int rc = vt_get(capture_ctx.handle, hist);
bool populated = !rc && histogram_populated(hist);
if (!populated) {
unsigned char held[16][3];
capture_ctx.color_valid = false;
if (!continuity_frame(&capture_ctx.continuity, false, capture_ctx.rgb, now, held)) {
set_error("Histogram data unavailable beyond hold window; recovering capture");
release_capture(false);
retry_source_transition(&retries, now);
return 1;
}
char *line = region_command(held, receiver_layout.blends, receiver_layout.count,
(unsigned)(1500 - (now - capture_ctx.continuity.missing_at)));
if (!line || (!output_frame(&receiver_runtime.output, line))) {
free(line);
set_error("Receiver connection lost while holding colors");
stop_capture();
return 1;
}
free(line);
return 1;
}
capture_ctx.sample_at = now;
capture_ctx.sample_sequence++;
unsigned aggregate[3] = {0};
for (int i = 0; i < 16; i++) {
capture_ctx.black_percent[i] = 100;
for (int c = 0; c < 3; c++) {
uint64_t total = 0, black = 0;
for (int b = 0; b < 256; b++) {
total += hist[i].bins[c][b];
if (b <= 12)
black += hist[i].bins[c][b];
}
unsigned fraction = total ? (unsigned)(100 * black / total) : 0;
if (fraction < capture_ctx.black_percent[i])
capture_ctx.black_percent[i] = fraction;
}
}
for (int i = 0; i < 16; i++)
for (int c = 0; c < 3; c++) {
capture_ctx.rgb[i][c] = histogram_mean(hist[i].bins[c]);
aggregate[c] += capture_ctx.rgb[i][c];
}
if (!CALIBRATION_ACTIVE && capture_ctx.preset == 0) {
int candidate = letterbox_candidate(capture_ctx.black_percent, capture_ctx.rgb);
uint64_t now = milliseconds();
if (candidate < 0)
capture_ctx.pending_letterbox = -1;
else if (candidate != capture_ctx.pending_letterbox) {
capture_ctx.pending_letterbox = candidate;
capture_ctx.candidate_since = now;
} else if ((unsigned)candidate != capture_ctx.letterbox_mode &&
now - capture_ctx.candidate_since >= 2000)
capture_ctx.letterbox_mode = (unsigned)candidate;
if (capture_ctx.letterbox_mode) {
if (vt_preset(capture_ctx.handle, 1) != 0) {
set_error("Letterbox preset failed");
stop_capture();
return 1;
}
capture_ctx.preset = 1;
capture_ctx.settling = 2;
capture_ctx.next_border_probe = now + 2000;
return 1;
}
}
for (int c = 0; c < 3; c++)
capture_ctx.sent_rgb[c] = (unsigned char)(aggregate[c] / 16);
dark_color_filter(capture_ctx.sent_rgb);
unsigned char boosted_mean[3];
boost_color(capture_ctx.sent_rgb, boosted_mean);
memcpy(capture_ctx.sent_rgb, boosted_mean, 3);
unsigned char mapped_rgb[16][3], stable_rgb[16][3];
calibration_colors(capture_ctx.rgb, mapped_rgb);
for (unsigned i = 0; i < 16; i++)
dark_color_filter(mapped_rgb[i]);
continuity_frame(&capture_ctx.continuity, true, mapped_rgb, now, stable_rgb);
receiver_layout.interpolation = receiver_layout.count && calibration_effective() != CAL_AVERAGE;
char *line = region_command(stable_rgb, receiver_layout.blends, receiver_layout.count, 1500);
if (!line) {
set_error("Color buffer allocation failed");
stop_capture();
return 1;
}
int failed = (!output_frame(&receiver_runtime.output, line));
free(line);
if (failed) {
set_error("Receiver connection lost");
stop_capture();
return 1;
}
capture_ctx.color_valid = true;
return 1;
}