impl LesavkaClientApp {
    /*──────────────── cam stream ───────────────────*/
    #[cfg(not(coverage))]
    /// Keeps `cam_loop` explicit because it sits on the live uplink path, where stale media must be dropped instead of queued into latency.
    /// Inputs are the typed parameters; output is the return value or side effect.
    async fn cam_loop(
        ep: Channel,
        initial_source: Option<String>,
        initial_profile: Option<String>,
        camera_cfg: Option<crate::input::camera::CameraConfig>,
        telemetry: crate::uplink_telemetry::UplinkTelemetryHandle,
        media_controls: crate::live_media_control::LiveMediaControls,
    ) {
        let mut delay = Duration::from_secs(1);

        loop {
            let state = media_controls.refresh();
            if !state.camera {
                telemetry.record_enabled(false);
                tokio::time::sleep(Duration::from_millis(100)).await;
                continue;
            }
            let active_source = state.camera_source.resolve(initial_source.as_deref());
            let active_profile = state.camera_profile.resolve(initial_profile.as_deref());
            let capture_profile = active_profile
                .as_deref()
                .and_then(parse_camera_profile_id);
            let setup_source = active_source.clone();
            let result = tokio::task::spawn_blocking(move || {
                CameraCapture::new_with_capture_profile(
                    setup_source.as_deref(),
                    camera_cfg,
                    capture_profile,
                )
            })
            .await;
            let cam = match result {
                Ok(Ok(cam)) => Arc::new(cam),
                Ok(Err(err)) => {
                    telemetry.record_disconnect(format!("webcam uplink setup failed: {err:#}"));
                    warn!(
                        "📸 webcam uplink setup failed for {:?}: {err:#}",
                        active_source.as_deref().unwrap_or("auto")
                    );
                    abort_if_required_media_source_failed("camera", "📸", active_source.as_deref(), &err);
                    delay = app_support::next_delay(delay);
                    tokio::time::sleep(delay).await;
                    continue;
                }
                Err(err) => {
                    telemetry.record_disconnect(format!("webcam uplink setup task failed: {err}"));
                    warn!("📸 webcam uplink setup task failed before StreamCamera could start: {err}");
                    abort_if_required_media_source_failed(
                        "camera",
                        "📸",
                        active_source.as_deref(),
                        &err,
                    );
                    delay = app_support::next_delay(delay);
                    tokio::time::sleep(delay).await;
                    continue;
                }
            };

            telemetry.record_reconnect_attempt();
            let mut cli = RelayClient::new(ep.clone());
            let queue = crate::uplink_fresh_queue::FreshPacketQueue::new(VIDEO_UPLINK_QUEUE);
            let drop_log =
                Arc::new(std::sync::Mutex::new(UplinkDropLogLimiter::new("camera", "📸")));

            let queue_stream = queue.clone();
            let telemetry_stream = telemetry.clone();
            let drop_log_stream = Arc::clone(&drop_log);
            let outbound = async_stream::stream! {
                loop {
                    let next = queue_stream.pop_fresh().await;
                    if next.dropped_stale > 0 {
                        telemetry_stream.record_stale_drop(next.dropped_stale);
                        log_uplink_drop(
                            &drop_log_stream,
                            UplinkDropReason::Stale,
                            next.dropped_stale,
                            next.queue_depth,
                            duration_ms(next.delivery_age),
                        );
                    }
                    if let Some(mut packet) = next.packet {
                        telemetry_stream.record_streamed(
                            queue_depth_u32(next.queue_depth),
                            duration_ms(next.delivery_age),
                        );
                        attach_video_queue_metadata(
                            &mut packet,
                            next.queue_depth,
                            next.delivery_age,
                        );
                        yield packet;
                        continue;
                    }
                    break;
                }
            };

            match cli.stream_camera(Request::new(outbound)).await {
                Ok(mut resp) => {
                    let (stop_tx, stop_rx) = std::sync::mpsc::channel::<()>();
                    let cam_worker = std::thread::spawn({
                        let cam = cam.clone();
                        let telemetry = telemetry.clone();
                        let queue = queue.clone();
                        let drop_log = Arc::clone(&drop_log);
                        let media_controls = media_controls.clone();
                        let initial_source_thread = initial_source.clone();
                        let active_source_thread = active_source.clone();
                        let initial_profile_thread = initial_profile.clone();
                        let active_profile_thread = active_profile.clone();
                        move || loop {
                            if stop_rx.try_recv().is_ok() {
                                break;
                            }
                            let state = media_controls.refresh();
                            let desired_source =
                                state.camera_source.resolve(initial_source_thread.as_deref());
                            let desired_profile =
                                state.camera_profile.resolve(initial_profile_thread.as_deref());
                            if desired_source != active_source_thread
                                || desired_profile != active_profile_thread
                            {
                                tracing::info!(
                                    from = active_source_thread.as_deref().unwrap_or("auto"),
                                    to = desired_source.as_deref().unwrap_or("auto"),
                                    "📸 webcam source changed; restarting live uplink pipeline"
                                );
                                break;
                            }
                            if !state.camera {
                                telemetry.record_enabled(false);
                                tracing::info!("📸 webcam uplink soft-paused");
                                while stop_rx.try_recv().is_err() {
                                    let state = media_controls.refresh();
                                    let desired_source =
                                        state.camera_source.resolve(initial_source_thread.as_deref());
                                    let desired_profile = state
                                        .camera_profile
                                        .resolve(initial_profile_thread.as_deref());
                                    if desired_source != active_source_thread
                                        || desired_profile != active_profile_thread
                                    {
                                        break;
                                    }
                                    if state.camera {
                                        break;
                                    }
                                    std::thread::sleep(Duration::from_millis(25));
                                }
                                if stop_rx.try_recv().is_ok() {
                                    break;
                                }
                                let state = media_controls.refresh();
                                let desired_source =
                                    state.camera_source.resolve(initial_source_thread.as_deref());
                                let desired_profile = state
                                    .camera_profile
                                    .resolve(initial_profile_thread.as_deref());
                                if desired_source != active_source_thread
                                    || desired_profile != active_profile_thread
                                {
                                    tracing::info!(
                                        from = active_source_thread.as_deref().unwrap_or("auto"),
                                        to = desired_source.as_deref().unwrap_or("auto"),
                                        "📸 webcam source changed while paused; restarting live uplink pipeline"
                                    );
                                    break;
                                }
                                telemetry.record_enabled(true);
                                tracing::info!("📸 webcam uplink resumed");
                            }
                            let Some(mut pkt) = cam.pull() else {
                                std::thread::sleep(Duration::from_millis(5));
                                continue;
                            };
                            static CNT: std::sync::atomic::AtomicU64 =
                                std::sync::atomic::AtomicU64::new(0);
                            let n = CNT.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
                            if n < 10 || n.is_multiple_of(120) {
                                tracing::trace!("📸 cli  frame#{n}  {} B", pkt.data.len());
                            }
                            tracing::trace!("📸⬆️ sent webcam AU pts={} {} B", pkt.pts, pkt.data.len());
                            let enqueue_age = stamp_video_timing_metadata_at_enqueue(&mut pkt);
                            let stats = queue.push(pkt, enqueue_age);
                            if stats.dropped_queue_full > 0 {
                                telemetry.record_queue_full_drop(stats.dropped_queue_full);
                                log_uplink_drop(
                                    &drop_log,
                                    UplinkDropReason::QueueFull,
                                    stats.dropped_queue_full,
                                    stats.queue_depth,
                                    duration_ms(enqueue_age),
                                );
                            }
                            telemetry.record_enqueue(
                                queue_depth_u32(stats.queue_depth),
                                duration_ms(enqueue_age),
                                0.0,
                            );
                        }
                    });
                    delay = Duration::from_secs(1);
                    telemetry.record_connected();
                    while resp.get_mut().message().await.transpose().is_some() {}
                    telemetry.record_disconnect("camera uplink stream ended");
                    queue.close();
                    let _ = stop_tx.send(());
                    let _ = cam_worker.join();
                }
                Err(e) if e.code() == tonic::Code::Unimplemented => {
                    tracing::warn!("📸 server does not support StreamCamera – giving up");
                    telemetry.record_disconnect("camera uplink unavailable on server");
                    queue.close();
                    return;
                }
                Err(e) => {
                    telemetry.record_disconnect(format!("camera uplink connect failed: {e}"));
                    tracing::warn!("❌📸 connect failed: {e:?}");
                    delay = app_support::next_delay(delay);
                }
            }

            queue.close();
            tokio::time::sleep(delay).await;
        }
    }
}
