129 lines
4.7 KiB
C++
129 lines
4.7 KiB
C++
#ifndef CMVR_ES_MANAGER_MEDIA_SOURCE_HUB_H
|
|
#define CMVR_ES_MANAGER_MEDIA_SOURCE_HUB_H
|
|
|
|
#pragma once
|
|
|
|
#include <chrono>
|
|
#include <cstddef>
|
|
#include <cstdint>
|
|
#include <functional>
|
|
#include <memory>
|
|
#include <optional>
|
|
#include <string>
|
|
#include <vector>
|
|
|
|
#include "common/base/ring_buffer.h"
|
|
#include "common/media/media_frame.h"
|
|
|
|
namespace cmvr::media {
|
|
|
|
// MediaSourceHub owns no protocol-specific state. A device or capture adapter registers
|
|
// start/stop callbacks and receives a sink callback when the first consumer subscribes.
|
|
class MediaSourceHub final {
|
|
public:
|
|
using FrameRing = BroadcastFrameRing<MediaFrame>;
|
|
using FrameReadResult = FrameRing::ReadResult;
|
|
using StartPosition = FrameRing::StartPosition;
|
|
using FrameSink = std::function<void(MediaFramePtr)>;
|
|
// Cancellation checks run while MediaSourceHub protects source lifecycle
|
|
// state. Predicates must therefore be fast, non-blocking and must not call
|
|
// back into the same hub.
|
|
using CancelPredicate = std::function<bool()>;
|
|
|
|
struct SourceCallbacks {
|
|
// start() may run asynchronously. It must observe cancelled during any
|
|
// potentially blocking startup work and return false promptly once set.
|
|
// MediaSourceHub retains the callback state until a non-cooperative start
|
|
// eventually returns, so late completion cannot access destroyed state.
|
|
std::function<bool(
|
|
const FrameSink& sink,
|
|
const CancelPredicate& cancelled)> start;
|
|
// stop() is the synchronous publication barrier for the last lease and
|
|
// must unblock and join the source producer before returning.
|
|
std::function<void()> stop;
|
|
std::function<bool()> request_key_frame;
|
|
};
|
|
|
|
private:
|
|
struct SourceState;
|
|
|
|
public:
|
|
class Subscription final {
|
|
public:
|
|
Subscription() = default;
|
|
~Subscription();
|
|
|
|
Subscription(const Subscription&) = delete;
|
|
Subscription& operator=(const Subscription&) = delete;
|
|
Subscription(Subscription&& other) noexcept;
|
|
Subscription& operator=(Subscription&& other) noexcept;
|
|
|
|
// A Subscription owns one reader cursor and is single-consumer. Moving,
|
|
// resetting, or reading the same object concurrently is unsupported; use
|
|
// one independent subscription per consumer thread.
|
|
bool valid() const;
|
|
explicit operator bool() const { return valid(); }
|
|
|
|
// Returns the most recently observed immutable descriptor. A callback source may
|
|
// replace the initially registered UNKNOWN codec/config descriptor with the first
|
|
// real frame descriptor without invalidating existing subscriptions.
|
|
TrackDescriptorPtr descriptor() const;
|
|
|
|
std::optional<FrameReadResult> tryRead();
|
|
std::optional<FrameReadResult> waitRead(std::chrono::milliseconds timeout);
|
|
uint64_t discardPendingIfExceeds(size_t maximum_pending_frames);
|
|
uint64_t droppedCount() const noexcept;
|
|
void reset();
|
|
|
|
private:
|
|
friend class MediaSourceHub;
|
|
Subscription(std::shared_ptr<SourceState> source, FrameRing::Cursor cursor);
|
|
|
|
std::shared_ptr<SourceState> source_;
|
|
FrameRing::Cursor cursor_;
|
|
bool active_{false};
|
|
};
|
|
|
|
MediaSourceHub();
|
|
~MediaSourceHub();
|
|
|
|
MediaSourceHub(const MediaSourceHub&) = delete;
|
|
MediaSourceHub& operator=(const MediaSourceHub&) = delete;
|
|
|
|
bool registerSource(
|
|
TrackDescriptorPtr initial_descriptor,
|
|
SourceCallbacks callbacks,
|
|
size_t ring_capacity = 64);
|
|
|
|
// Active sources cannot be unregistered. Destroy/reset their subscriptions first.
|
|
bool unregisterSource(const std::string& track_id);
|
|
|
|
bool hasSource(const std::string& track_id) const;
|
|
std::vector<TrackDescriptorPtr> listTracks() const;
|
|
size_t subscriberCount(const std::string& track_id) const;
|
|
|
|
// Protocol adapters can request an IDR after a discontinuity without knowing the
|
|
// concrete camera implementation. Returns false when unsupported or not running.
|
|
bool requestKeyFrame(const std::string& track_id) const;
|
|
|
|
Subscription subscribe(
|
|
const std::string& track_id,
|
|
StartPosition start_position = StartPosition::NEXT_PUBLISHED,
|
|
CancelPredicate cancelled = {});
|
|
|
|
// Stops all registered sources and invalidates outstanding subscriptions. The
|
|
// subscriptions remain destructible and their waitRead calls are awakened.
|
|
// A cooperative in-progress start is cancelled; a callback that violates the
|
|
// cancellation contract is quarantined with retained state rather than blocking
|
|
// shutdown or risking a use-after-free.
|
|
void shutdown();
|
|
|
|
private:
|
|
struct Impl;
|
|
std::shared_ptr<Impl> impl_;
|
|
};
|
|
|
|
} // namespace cmvr::media
|
|
|
|
#endif // CMVR_ES_MANAGER_MEDIA_SOURCE_HUB_H
|