[refactor] use MiniRTC APIs for DeskHub media pause controls

This commit is contained in:
dijunkun
2026-10-13 15:39:34 +08:00
parent 3bd0676672
commit de16eb3b92
6 changed files with 41 additions and 23 deletions
@@ -165,11 +165,11 @@ void PeerEventHandler::OnReceiveDataBuffer(
return;
const auto subscription = PreviewSubscription::Parse(preview_json);
if (!subscription) return;
const auto previous = runtime->preview_subscriptions_.Get(remote_id);
bool accepted = true;
// Apply encoder quality to this controller only; other controllers retain
// their own demand. Frame gating happens before encoding and transmission.
if (runtime->preview_subscriptions_.IsNewer(remote_id,
subscription->request_id)) {
// their own demand. MiniRTC owns per-peer video/audio pause state.
if (!previous || subscription->IsNewerThan(*previous)) {
if (subscription->fps > 0)
accepted = UpdateVideoSettings(
runtime->peer_, remote_id.data(), remote_id.size(),
@@ -177,13 +177,22 @@ void PeerEventHandler::OnReceiveDataBuffer(
subscription->frame_rate,
static_cast<VideoDegradationPreference>(
subscription->EncoderPreference())) == 0;
if (accepted)
// Audio-only updates must not force a keyframe. The pause API requests
// its own keyframes on resume.
if (accepted && (!previous || previous->width != subscription->width ||
previous->height != subscription->height))
accepted = UpdateVideoResolution(
runtime->peer_, remote_id.data(), remote_id.size(),
subscription->width, subscription->height) == 0;
if (accepted)
accepted = SetVideoSendPaused(runtime->peer_, remote_id.data(),
remote_id.size(), subscription->fps == 0) == 0;
if (accepted)
accepted = SetAudioSendPaused(runtime->peer_, remote_id.data(),
remote_id.size(), !subscription->audio) == 0;
if (accepted && subscription->audio)
runtime->devices_.StartSpeakerCapturer();
}
// The resolution API refreshes only this controller's keyframes, including
// resume requests with unchanged dimensions. Do not disturb other viewers.
if (accepted) runtime->preview_subscriptions_.Set(remote_id, *subscription);
const auto reply = nlohmann::json{
{"type", "deskhub_preview_status"},
@@ -527,9 +536,16 @@ void PeerEventHandler::OnReceiveDataBuffer(
runtime->devices_.SendMouseCommand(remote_action,
runtime->selected_display_);
} else if (remote_action.type == ControlType::audio_capture) {
// A negotiated subscription remains authoritative if a legacy capture
// command arrives after it (e.g. when unmuting an inactive window).
const auto subscription = runtime->preview_subscriptions_.Get(remote_id);
const bool paused = subscription ? !subscription->audio : !remote_action.a;
const int ret = SetAudioSendPaused(runtime->peer_, remote_id.data(),
remote_id.size(), paused);
if (remote_action.a)
runtime->devices_.StartSpeakerCapturer();
else
else if (ret != 0)
// Legacy/web transports without the per-peer API retain their fallback.
runtime->devices_.StopSpeakerCapturer();
} else if (remote_action.type == ControlType::keyboard) {
runtime->keyboard_.ApplyRemoteEvent(remote_id, remote_action);
@@ -20,23 +20,21 @@ namespace crossdesk {
class PreviewSubscriptions {
public:
using Clock = std::chrono::steady_clock;
bool IsNewer(const std::string &id, uint32_t request_id) {
std::optional<PreviewSubscription> Get(const std::string &id) {
std::lock_guard lock(mutex_);
const auto found = states_.find(id);
return found == states_.end() ||
static_cast<int32_t>(request_id -
found->second.request.request_id) > 0;
return found == states_.end() ? std::nullopt
: std::optional(found->second.request);
}
bool Set(const std::string &id, const PreviewSubscription &request) {
std::lock_guard lock(mutex_);
const auto found = states_.find(id);
if (found != states_.end() &&
static_cast<int32_t>(request.request_id -
found->second.request.request_id) <= 0)
!request.IsNewerThan(found->second.request))
return false;
auto &state = states_[id];
if (state.request.fps != request.fps) state.next.clear();
state.request = request;
state.next.clear();
return true;
}
bool ShouldSend(const std::string &id, Clock::time_point now = Clock::now(),
@@ -46,8 +44,10 @@ public:
if (found == states_.end())
return true;
auto &state = found->second;
// MiniRTC owns pause/resume. This gate only limits preview cadence, so
// paused input reaches its cheap send gate without dividing by zero here.
if (!state.request.fps)
return false;
return true;
// Interactive streams use the encoder's own 30/60 FPS cadence. A second
// time gate here can drop valid frames because capture timing varies.
if (state.request.fps == 60)
@@ -67,11 +67,6 @@ public:
next = now + std::chrono::milliseconds(1000 / state.request.fps);
return true;
}
bool ShouldSendAudio(const std::string &id) {
std::lock_guard lock(mutex_);
const auto found = states_.find(id);
return found == states_.end() || found->second.request.audio;
}
void Remove(const std::string &id) {
std::lock_guard lock(mutex_);
states_.erase(id);