impl UpstreamMediaRuntime {
/// Rebase one upstream video packet timestamp onto the shared session clock.
#[must_use]
pub fn map_video_pts(&self, remote_pts_us: u64, frame_step_us: u64) -> Option<u64> {
        match self.plan_video_pts(remote_pts_us, frame_step_us.max(1)) {
            UpstreamPlanDecision::Play(plan) => Some(plan.local_pts_us),
            _ => None,
        }
    }

    /// Rebase one upstream audio packet timestamp onto the shared session clock.
    #[must_use]
    pub fn map_audio_pts(&self, remote_pts_us: u64) -> Option<u64> {
        match self.plan_audio_pts(remote_pts_us) {
            UpstreamPlanDecision::Play(plan) => Some(plan.local_pts_us),
            _ => None,
        }
    }

    /// Rebase and schedule one upstream video packet on the shared playout epoch.
    #[must_use]
    pub fn plan_video_pts(&self, remote_pts_us: u64, frame_step_us: u64) -> UpstreamPlanDecision {
        self.plan_pts(
            UpstreamMediaKind::Camera,
            remote_pts_us,
            frame_step_us.max(1),
        )
    }

    /// Rebase and schedule one upstream audio packet on the shared playout epoch.
    #[must_use]
    pub fn plan_audio_pts(&self, remote_pts_us: u64) -> UpstreamPlanDecision {
        self.plan_pts(UpstreamMediaKind::Microphone, remote_pts_us, 1)
    }

    /// Schedule a packet from the bundled webcam/microphone transport.
    ///
    /// Inputs: the media kind, client capture timestamp, packet cadence floor,
    /// and the client-owned bundle epoch chosen for this gRPC stream.
    /// Outputs: the server playout decision for that packet.
    /// Why: bundled webcam media has already been synchronized on the client,
    /// so the server should not re-solve cross-stream startup pairing. It only
    /// rebases the shared client clock onto a fresh local playout epoch.
    #[must_use]
    pub fn plan_bundled_pts(
        &self,
        kind: UpstreamMediaKind,
        remote_pts_us: u64,
        min_step_us: u64,
        bundle_base_remote_pts_us: u64,
        bundle_epoch: Instant,
    ) -> UpstreamPlanDecision {
        let mut state = self
            .state
            .lock()
            .expect("upstream media state mutex poisoned");
        let session_id = state.session_id;
        match kind {
            UpstreamMediaKind::Camera => {
                state.camera_packet_count = state.camera_packet_count.saturating_add(1);
                state
                    .first_camera_remote_pts_us
                    .get_or_insert(remote_pts_us);
                state.camera_startup_ready = true;
            }
            UpstreamMediaKind::Microphone => {
                state.microphone_packet_count = state.microphone_packet_count.saturating_add(1);
                state
                    .first_microphone_remote_pts_us
                    .get_or_insert(remote_pts_us);
            }
        }
        update_latest_remote_pts(&mut state, kind, remote_pts_us);
        if state.session_base_remote_pts_us.is_none() {
            state.session_base_remote_pts_us = Some(bundle_base_remote_pts_us);
            state.playout_epoch = Some(bundle_epoch);
            state.pairing_anchor_deadline = Some(bundle_epoch);
            state.phase = UpstreamSyncPhase::Syncing;
            state.last_reason = "client-bundled upstream media epoch established".to_string();
            self.pairing_state_notify.notify_waiters();
            info!(
                session_id,
                bundle_base_remote_pts_us, "client-bundled upstream media epoch established"
            );
        }

        let session_base_remote_pts_us = state
            .session_base_remote_pts_us
            .unwrap_or(bundle_base_remote_pts_us);
        if remote_pts_us < session_base_remote_pts_us {
            return UpstreamPlanDecision::DropBeforeOverlap;
        }

        let max_live_lag = upstream_max_live_lag();
        let source_lag = source_lag_for_kind(&state, kind, remote_pts_us);
        if source_lag > max_live_lag {
            match kind {
                UpstreamMediaKind::Camera => {
                    state.stale_video_drops = state.stale_video_drops.saturating_add(1);
                    state.video_freezes = state.video_freezes.saturating_add(1);
                    state.last_reason =
                        "dropped stale bundled video beyond max live lag".to_string();
                }
                UpstreamMediaKind::Microphone => {
                    state.stale_audio_drops = state.stale_audio_drops.saturating_add(1);
                    state.last_reason =
                        "dropped stale bundled audio beyond max live lag".to_string();
                }
            }
            state.phase = UpstreamSyncPhase::Healing;
            return UpstreamPlanDecision::DropStale("bundled packet exceeded max live lag");
        }

        let mut local_pts_us = remote_pts_us.saturating_sub(session_base_remote_pts_us);
        let last_slot = match kind {
            UpstreamMediaKind::Camera => &mut state.last_video_local_pts_us,
            UpstreamMediaKind::Microphone => &mut state.last_audio_local_pts_us,
        };
        if let Some(last_pts_us) = *last_slot
            && local_pts_us <= last_pts_us
        {
            local_pts_us = last_pts_us.saturating_add(min_step_us.max(1));
        }
        *last_slot = Some(local_pts_us);

        let sink_offset_us = self.bundled_playout_offset_us(kind);
        let epoch = state.playout_epoch.unwrap_or(bundle_epoch);
        let mut due_at =
            apply_playout_offset(epoch + Duration::from_micros(local_pts_us), sink_offset_us);
        let now = Instant::now();
        let mut late_by = now.checked_duration_since(due_at).unwrap_or_default();
        let playout_delay = upstream_bundled_playout_delay().min(max_live_lag);
        let reanchor_threshold = upstream_reanchor_late_threshold(playout_delay);
        let max_future_wait = max_live_lag.saturating_sub(source_lag);
        let output_offset = if sink_offset_us >= 0 {
            Duration::from_micros(sink_offset_us as u64)
        } else {
            Duration::ZERO
        };
        let due_future_wait = due_at.saturating_duration_since(now);
        let due_future_wait_before_output_compensation =
            due_future_wait.saturating_sub(output_offset);
        if late_by > reanchor_threshold
            || due_future_wait_before_output_compensation > max_future_wait
        {
            let desired_delay = playout_delay.min(max_future_wait);
            let desired_due_at = now + desired_delay;
            let unoffset_due_at = apply_playout_offset(desired_due_at, -sink_offset_us);
            let recovered_epoch = unoffset_due_at
                .checked_sub(Duration::from_micros(local_pts_us))
                .unwrap_or(unoffset_due_at);
            state.playout_epoch = Some(recovered_epoch);
            state.pairing_anchor_deadline = Some(desired_due_at);
            state.freshness_reanchors = state.freshness_reanchors.saturating_add(1);
            state.phase = UpstreamSyncPhase::Healing;
            state.last_reason =
                "reanchored bundled upstream playhead to preserve freshness".to_string();
            due_at = apply_playout_offset(
                recovered_epoch + Duration::from_micros(local_pts_us),
                sink_offset_us,
            );
            late_by = now.checked_duration_since(due_at).unwrap_or_default();
            info!(
                session_id,
                ?kind,
                local_pts_us,
                remote_pts_us,
                recovery_buffer_ms = desired_delay.as_millis(),
                max_live_lag_ms = max_live_lag.as_millis(),
                source_lag_ms = source_lag.as_millis(),
                output_offset_ms = output_offset.as_millis(),
                "bundled upstream media playhead reanchored to preserve freshness"
            );
        }
        let predicted_lag_before_output_compensation = source_lag.saturating_add(
            due_at
                .saturating_duration_since(now)
                .saturating_sub(output_offset),
        );
        if predicted_lag_before_output_compensation > max_live_lag {
            match kind {
                UpstreamMediaKind::Camera => {
                    state.stale_video_drops = state.stale_video_drops.saturating_add(1);
                    state.video_freezes = state.video_freezes.saturating_add(1);
                    state.last_reason = "dropped bundled video that would exceed max live lag before output compensation".to_string();
                }
                UpstreamMediaKind::Microphone => {
                    state.stale_audio_drops = state.stale_audio_drops.saturating_add(1);
                    state.last_reason = "dropped bundled audio that would exceed max live lag before output compensation".to_string();
                }
            }
            state.phase = UpstreamSyncPhase::Healing;
            return UpstreamPlanDecision::DropStale(
                "bundled packet would exceed max live lag before output compensation",
            );
        }

        if kind == UpstreamMediaKind::Microphone {
            self.audio_progress_notify.notify_waiters();
        }
        UpstreamPlanDecision::Play(PlannedUpstreamPacket {
            local_pts_us,
            due_at,
            late_by,
            source_lag,
        })
    }

    /// Hold video until the audio master has at least reached the same capture
    /// moment, or until the bounded sync grace is exhausted.
    pub async fn wait_for_audio_master(&self, video_local_pts_us: u64, due_at: Instant) -> bool {
        let slack_us = upstream_pairing_master_slack()
            .as_micros()
            .min(u64::MAX as u128) as u64;
        let audio_delay_allowance_us = self.positive_audio_delay_allowance_us();
        let deadline = due_at + upstream_audio_master_wait_grace();
        loop {
            let notified = self.audio_progress_notify.notified();
            {
                let state = self
                    .state
                    .lock()
                    .expect("upstream media state mutex poisoned");
                if state.active_microphone_generation.is_none() {
                    return true;
                }
                let audio_presented_pts_us = state.last_audio_presented_pts_us.unwrap_or(0);
                if audio_presented_pts_us
                    .saturating_add(slack_us)
                    .saturating_add(audio_delay_allowance_us)
                    >= video_local_pts_us
                {
                    return true;
                }
            }
            if Instant::now() >= deadline {
                return false;
            }
            tokio::select! {
                _ = notified => {}
                _ = tokio::time::sleep_until(deadline) => return false,
            }
        }
    }
}
