mirror of
https://github.com/kunkundi/crossdesk.git
synced 2026-10-17 09:23:10 +08:00
87 lines
2.9 KiB
C++
87 lines
2.9 KiB
C++
/*
|
|
* @Author: DI JUNKUN
|
|
* @Date: 2026-10-12
|
|
* Copyright (c) 2026 by DI JUNKUN, All Rights Reserved.
|
|
*/
|
|
|
|
#ifndef _PREVIEW_SUBSCRIPTIONS_H_
|
|
#define _PREVIEW_SUBSCRIPTIONS_H_
|
|
|
|
#include <preview_subscription.h>
|
|
|
|
#include <chrono>
|
|
#include <mutex>
|
|
#include <string>
|
|
#include <unordered_map>
|
|
|
|
namespace crossdesk {
|
|
// Accessed by the network callback and screen-capture callback. Legacy
|
|
// controllers have no entry and continue receiving their normal stream.
|
|
class PreviewSubscriptions {
|
|
public:
|
|
using Clock = std::chrono::steady_clock;
|
|
std::optional<PreviewSubscription> Get(const std::string &id) {
|
|
std::lock_guard lock(mutex_);
|
|
const auto found = states_.find(id);
|
|
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() &&
|
|
!request.IsNewerThan(found->second.request))
|
|
return false;
|
|
auto &state = states_[id];
|
|
if (state.request.fps != request.fps) state.next.clear();
|
|
state.request = request;
|
|
return true;
|
|
}
|
|
bool ShouldSend(const std::string &id, Clock::time_point now = Clock::now(),
|
|
const std::string &stream = {}) {
|
|
std::lock_guard lock(mutex_);
|
|
const auto found = states_.find(id);
|
|
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 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)
|
|
return true;
|
|
auto &next = state.next[stream];
|
|
if (state.request.fps == 15) {
|
|
// Keep a stable 15 FPS deadline across capture jitter. Rebasing every
|
|
// frame can turn small timing errors into extra dropped capture frames.
|
|
const auto interval = std::chrono::nanoseconds(1'000'000'000 / 15);
|
|
if (now + std::chrono::milliseconds(1) < next) return false;
|
|
next = next == Clock::time_point{} || now - next > interval
|
|
? now + interval : next + interval;
|
|
return true;
|
|
}
|
|
if (now < next)
|
|
return false;
|
|
next = now + std::chrono::milliseconds(1000 / state.request.fps);
|
|
return true;
|
|
}
|
|
void Remove(const std::string &id) {
|
|
std::lock_guard lock(mutex_);
|
|
states_.erase(id);
|
|
}
|
|
|
|
private:
|
|
struct State {
|
|
PreviewSubscription request;
|
|
// One display must not consume another display's preview frame budget.
|
|
std::unordered_map<std::string, Clock::time_point> next;
|
|
};
|
|
std::mutex mutex_;
|
|
std::unordered_map<std::string, State> states_;
|
|
};
|
|
} // namespace crossdesk
|
|
|
|
#endif
|