IRSOL
C++ code implementing socket server for interacting with Baumer camera.
irsol::server::frame_collector::FrameCollector Class Reference

Coordinates frame acquisition from a camera and distributes frames to registered clients. More...

#include <collector.hpp>

Public Types

using frame_queue_t = ClientCollectionParams::frame_queue_t
 

Public Member Functions

 FrameCollector (irsol::camera::Interface &camera)
 Constructs a FrameCollector for the given camera interface.
 
 ~FrameCollector ()
 Destructor. Stops any running threads and cleans up resources.
 
void start ()
 Starts the frame collection and distribution thread.
 
void stop ()
 Stops the frame collector and joins worker threads.
 
bool isBusy () const
 Checks whether the collector is currently serving any clients.
 
void registerClient (irsol::types::client_id_t clientId, double fps, std::shared_ptr< frame_queue_t > queue, int64_t frameCount=-1)
 Registers a client to receive frames at a specified frame rate.
 
void deregisterClient (irsol::types::client_id_t clientId)
 Deregisters a client and stops frame delivery.
 

Static Public Member Functions

static std::shared_ptr< frame_queue_tmakeQueuePtr ()
 Utility static function to create a shared pointer to a frame queue.
 

Static Public Attributes

static constexpr irsol::types::duration_t SLACK = std::chrono::milliseconds(50)
 Slack window for batching frame delivery to clients.
 

Private Member Functions

void run ()
 Runs the frame distribution loop in a background thread.
 
void deregisterClientNonThreadSafe (irsol::types::client_id_t clientId)
 Deregisters a client and stops frame delivery (not thread-safe).
 
std::pair< std::vector< irsol::types::client_id_t >, std::vector< irsol::types::timepoint_t > > collectReadyClients (irsol::types::timepoint_t now, irsol::types::duration_t slack) const
 Collects clients who are scheduled to receive a frame at the given time.
 
void cleanUpSchedule (const std::vector< irsol::types::timepoint_t > schedules)
 Removes schedules from the schedule map.
 
std::optional< std::pair< FrameMetadata, std::vector< irsol::types::byte_t > > > grabImageData () const
 Captures an image and returns it along with associated metadata.
 
bool schedule (const irsol::types::client_id_t &clientId, irsol::types::timepoint_t nextFrameDue)
 Schedules the next frame delivery for a client.
 

Private Attributes

irsol::camera::Interfacem_cam
 Reference to the camera interface used for capturing.
 
std::mutex m_clientsMutex
 Protects access to m_clients and m_scheduleMap.
 
std::unordered_map< irsol::types::client_id_t, ClientCollectionParamsm_clients
 Stores parameters for each registered client.
 
std::map< irsol::types::timepoint_t, std::vector< irsol::types::client_id_t > > m_scheduleMap
 Maps timestamps to client IDs scheduled at that time.
 
std::condition_variable m_scheduleCondition
 Signals when a new client is scheduled.
 
std::thread m_distributorThread
 Thread responsible for frame distribution.
 
std::atomic< bool > m_stop
 Indicates whether the collector is stopping due to an external request.
 

Detailed Description

Coordinates frame acquisition from a camera and distributes frames to registered clients.

The FrameCollector class is responsible for orchestrating the acquisition of frames from a camera device and distributing those frames to multiple registered clients. Each client can specify its desired frame rate and the number of frames it wishes to receive. The collector ensures that frames are delivered to clients according to their schedules, batching requests within a configurable "slack" window to optimize camera usage and minimize redundant captures. For instance, if multiple clients are scheduled to receive frames at similar times, a single frame image is requested from the camera and distributed to all those clients.

The collector maintains a background thread that monitors client schedules, captures frames just-in-time, and pushes them into client-specific irsol::utils::SafeQueue. Clients can consume frames from these queues at their own pace. When a client has received its requested number of frames, it is automatically deregistered and its queue is marked as complete.

The scheduling strategy is as follows:

  • Each client registers with a desired frame rate (fps) and frame count.
  • The collector maintains a schedule of when each client is next due to receive a frame.
  • When the earliest scheduled time arrives (or is within the allowed slack), the collector captures a single frame and distributes it to all clients whose schedules fall within the slack window.
  • This batching reduces redundant camera captures and ensures efficient resource usage.
  • The collector supports dynamic registration and deregistration of clients at runtime.
Note
The current scheduling strategy can be extended or modified. For example, one might restrict allowed FPS values to multiples of 0.125s (i.e., FPS = 0.125, 0.25, 0.5, 1, 2, 4, 8, 16). In this approach, the FrameCollector would continuously capture frames at the highest frame rate required by any connected client, without scheduling individual capture times. A delivery selection mechanism could then be implemented to determine which captured frames are delivered to which clients, simplifying the scheduling logic and potentially improving efficiency.

Thread safety: All public methods are thread-safe unless otherwise noted.

Definition at line 69 of file collector.hpp.

Member Typedef Documentation

◆ frame_queue_t

Constructor & Destructor Documentation

◆ FrameCollector()

irsol::server::frame_collector::FrameCollector::FrameCollector ( irsol::camera::Interface camera)

Constructs a FrameCollector for the given camera interface.

Parameters
cameraReference to a camera interface for capturing frames.

Definition at line 21 of file collector.cpp.

21 : m_cam(camera)
22{
23 start();
24}
void start()
Starts the frame collection and distribution thread.
Definition collector.cpp:32
irsol::camera::Interface & m_cam
Reference to the camera interface used for capturing.

◆ ~FrameCollector()

irsol::server::frame_collector::FrameCollector::~FrameCollector ( )

Destructor. Stops any running threads and cleans up resources.

Definition at line 26 of file collector.cpp.

27{
28 stop();
29}
void stop()
Stops the frame collector and joins worker threads.
Definition collector.cpp:43

Member Function Documentation

◆ cleanUpSchedule()

void irsol::server::frame_collector::FrameCollector::cleanUpSchedule ( const std::vector< irsol::types::timepoint_t schedules)
private

Removes schedules from the schedule map.

Parameters
schedulesVector of schedule times to clean up.

Definition at line 342 of file collector.cpp.

343{
344 // Erase all the schedules in the input vector
345 for(auto schedule : schedules) {
346 m_scheduleMap.erase(schedule);
347 }
348}
std::map< irsol::types::timepoint_t, std::vector< irsol::types::client_id_t > > m_scheduleMap
Maps timestamps to client IDs scheduled at that time.
bool schedule(const irsol::types::client_id_t &clientId, irsol::types::timepoint_t nextFrameDue)
Schedules the next frame delivery for a client.

◆ collectReadyClients()

std::pair< std::vector< irsol::types::client_id_t >, std::vector< irsol::types::timepoint_t > > irsol::server::frame_collector::FrameCollector::collectReadyClients ( irsol::types::timepoint_t  now,
irsol::types::duration_t  slack 
) const
private

Collects clients who are scheduled to receive a frame at the given time.

Parameters
nowCurrent timestamp.
slackAllowed slack between now and client's schedule for considering a client to be ready for receiving data.
Returns
A pair of vectors: the first is a list of ready client IDs, the second contains the actual schedule times of the returned clients.

Definition at line 314 of file collector.cpp.

316{
318 "frame_collector",
319 "Collecting clients for time {}, with slack of {}",
322 std::vector<irsol::types::client_id_t> readyClients;
323 std::vector<irsol::types::timepoint_t> clientsSchedule;
324
325 for(const auto& [scheduleTime, clients] : m_scheduleMap) {
326 // As soon as the scheduleTime of the client is above now+slack, break the loop.
327 // We can break early, as m_scheduleMap is an ordered container, that is iterated over
328 // in a way that smaller keys (scheduledTimes) are always iterated over at the beginning.
329 if(scheduleTime > now + slack) {
330 break;
331 }
332 for(const auto& client : clients) {
333 readyClients.push_back(client);
334 clientsSchedule.push_back(scheduleTime);
335 }
336 }
337
338 return {readyClients, clientsSchedule};
339}
#define IRSOL_NAMED_LOG_DEBUG(name,...)
Logs a debug-level message using a named logger.
Definition logging.hpp:174
std::string timestampToString(irsol::types::timepoint_t tp)
Converts a steady_clock time point to a human-readable string.
Definition utils.cpp:111
std::string durationToString(irsol::types::duration_t dr)
Converts a duration to a human-readable string.
Definition utils.cpp:133

◆ deregisterClient()

void irsol::server::frame_collector::FrameCollector::deregisterClient ( irsol::types::client_id_t  clientId)

Deregisters a client and stops frame delivery.

Parameters
clientIdThe client's unique identifier.
Note
Removes all state registered associated with the client in the collector.
This method is thread-safe.

Definition at line 106 of file collector.cpp.

107{
108 std::scoped_lock<std::mutex> lock(m_clientsMutex);
110}
void deregisterClientNonThreadSafe(irsol::types::client_id_t clientId)
Deregisters a client and stops frame delivery (not thread-safe).
std::mutex m_clientsMutex
Protects access to m_clients and m_scheduleMap.

◆ deregisterClientNonThreadSafe()

void irsol::server::frame_collector::FrameCollector::deregisterClientNonThreadSafe ( irsol::types::client_id_t  clientId)
private

Deregisters a client and stops frame delivery (not thread-safe).

Parameters
clientIdThe client's unique identifier.
Note
Removes all state registered associated with the client in the collector.
This method is NOT thread-safe and should only be called with appropriate locking.

Definition at line 264 of file collector.cpp.

265{
266 // Mark the client's queue as finished, so the client is notified that
267 // no more data will be pushed to him
268 IRSOL_NAMED_LOG_INFO("frame_collector", "Deregistering client {}", clientId);
269
270 if(m_clients.find(clientId) == m_clients.end()) {
272 "frame_collector", "Client {} was already deregistered, ignoring request.", clientId);
273 return;
274 }
275 auto& clientParams = m_clients.at(clientId);
276 clientParams.queue->producerFinished();
277
278 // Removes the client from the storage
279 auto dueNext = clientParams.nextFrameDue;
280
281 m_clients.erase(clientId);
283 "frame_collector", "Removed client {}, now remaining {} clients", clientId, m_clients.size());
284
285 if(m_scheduleMap.find(dueNext) == m_scheduleMap.end()) {
287 "frame_collector",
288 "Scheduled time {} was already deregistered. Ignoring request.",
290 return;
291 }
292 auto& clientsAtDue = m_scheduleMap.at(dueNext);
293
295 "frame_collector",
296 "Deregistering client {} from next schedule at {}",
297 clientId,
299 clientsAtDue.erase(
300 std::remove(clientsAtDue.begin(), clientsAtDue.end(), clientId), clientsAtDue.end());
301
302 if(clientsAtDue.empty()) {
304 "frame_collector",
305 "No more clients for schedule {}. Removing schedule.",
307 m_scheduleMap.erase(dueNext);
308 }
309
310 m_scheduleCondition.notify_one();
311}
std::condition_variable m_scheduleCondition
Signals when a new client is scheduled.
std::unordered_map< irsol::types::client_id_t, ClientCollectionParams > m_clients
Stores parameters for each registered client.
#define IRSOL_NAMED_LOG_INFO(name,...)
Logs an info-level message using a named logger.
Definition logging.hpp:176
#define IRSOL_NAMED_LOG_WARN(name,...)
Logs a warning-level message using a named logger.
Definition logging.hpp:178

◆ grabImageData()

std::optional< std::pair< FrameMetadata, std::vector< irsol::types::byte_t > > > irsol::server::frame_collector::FrameCollector::grabImageData ( ) const
private

Captures an image and returns it along with associated metadata.

Returns
A pair containing frame metadata and the image data buffer if image acquisition was successful, std::nullopt otherwise.

Definition at line 351 of file collector.cpp.

352{
353 // Capture the frame just-in-time
354 IRSOL_MAYBE_UNUSED auto t0 = irsol::types::clock_t::now();
355 auto image = m_cam.captureImage();
356 IRSOL_MAYBE_UNUSED auto t1 = irsol::types::clock_t::now();
358 "frame_collector",
359 "Capture image: start: {}, stop: {}, duration: {}",
363
364 // Extract the image data from the image buffer, and copy it into an
365 // owning structure. In this way, when `image` is destroyed at the end of the
366 // execution of this function, it can return into the pool of NeoAPI::Images
367 // for next frames to be written to the buffer.
368 auto* imageData = image.GetImageData();
369 auto numBytes = image.GetSize();
370
371 if(numBytes == 0) {
372 return std::nullopt;
373 }
374
375 std::vector<irsol::types::byte_t> rawData(numBytes);
376 std::memcpy(rawData.data(), imageData, numBytes);
377
378 return std::make_pair<FrameMetadata, std::vector<irsol::types::byte_t>>(
379 {irsol::types::clock_t::now(), image.GetImageID(), image.GetHeight(), image.GetWidth()},
380 std::move(rawData));
381}
image_t captureImage(std::optional< irsol::types::duration_t > timeout=std::nullopt)
Capture a single image from the camera.
#define IRSOL_MAYBE_UNUSED
Suppresses compiler warnings about unused variables or parameters.
Definition macros.hpp:39

◆ isBusy()

bool irsol::server::frame_collector::FrameCollector::isBusy ( ) const

Checks whether the collector is currently serving any clients.

Returns
true if at least one client is registered, false otherwise.

Definition at line 52 of file collector.cpp.

53{
54 return !m_clients.empty();
55}

◆ makeQueuePtr()

std::shared_ptr< FrameCollector::frame_queue_t > irsol::server::frame_collector::FrameCollector::makeQueuePtr ( )
static

Utility static function to create a shared pointer to a frame queue.

Clients may use this to obtain a queue prior to registering with the collector.

Returns
Shared pointer to a new frame queue.
See also
irsol::utils::SafeQueue
irsol::server::handlers::CommandGIHandler
irsol::server::handlers::CommandGISHandler

Definition at line 16 of file collector.cpp.

17{
18 return std::make_shared<FrameCollector::frame_queue_t>();
19}

◆ registerClient()

void irsol::server::frame_collector::FrameCollector::registerClient ( irsol::types::client_id_t  clientId,
double  fps,
std::shared_ptr< frame_queue_t queue,
int64_t  frameCount = -1 
)

Registers a client to receive frames at a specified frame rate.

Parameters
clientIdUnique identifier for the client.
fpsDesired frame rate (frames per second). If fps <= 0 and frameCount == 1, the client will receive a single frame immediately. Tipically used in gi command
See also
irsol::server::handlers::CommandGIHandler.
Parameters
queueShared pointer to the client's frame queue. The collector will push frame data into this queue at the schedule defined by the client.
frameCountNumber of frames to deliver; -1 means unlimited. After the last frame has been pushed to the client, the client is automatically deregistered and its queue marked as complete.
Note
This method is thread-safe.

Definition at line 58 of file collector.cpp.

63{
64
65 std::scoped_lock<std::mutex> lock(m_clientsMutex);
66
67 std::chrono::microseconds interval;
68 bool immediate = false;
69 if(frameCount == 1 && fps <= 0.0) {
70 // Client is requesting a single frame immediately
71 interval = std::chrono::microseconds(1);
72 immediate = true;
73 frameCount = 1;
74 fps = 0.0;
75 } else {
76 interval = std::chrono::microseconds(static_cast<uint64_t>(1000000.0 / fps));
78 "frame_collector",
79 "registering client with interval of {}",
81 }
82
83 // Registers the client so that the next due time is in SLACK ms.
84 // This is to allow the collector thread to batch multiple clients, that desire a frame at a
85 // specific timestamp, but all within the SLACK duration and to serve them all with the same frame
86 // image.
87 auto nextDue = irsol::types::clock_t::now() + FrameCollector::SLACK;
88
90 "frame_collector",
91 "Registering client {} with frame rate {} fps (interval {}), "
92 "next frame due at {} #frames {}, immediate {}",
93 clientId,
94 fps,
97 frameCount,
98 immediate);
99
100 m_clients.emplace(
101 clientId, ClientCollectionParams(fps, interval, nextDue, queue, frameCount, immediate));
102 schedule(clientId, nextDue);
103}
static constexpr irsol::types::duration_t SLACK
Slack window for batching frame delivery to clients.
Definition collector.hpp:91

◆ run()

void irsol::server::frame_collector::FrameCollector::run ( )
private

Runs the frame distribution loop in a background thread.

This method monitors client schedules, captures frames as needed, and distributes them to all clients whose scheduled delivery times fall within the current slack window. It also handles automatic deregistration of clients who have received their requested number of frames.

Definition at line 113 of file collector.cpp.

114{
115 std::unique_lock<std::mutex> lock(m_clientsMutex);
116
117 while(!m_stop) {
118 if(m_scheduleMap.empty()) {
119 // Wait until at least one new client is registered in the map, or if a stop request has
120 // arrived.
121 m_scheduleCondition.wait(lock, [this]() {
123 "frame_collector",
124 "Waiting until a client is scheduled (clients size: {}, schedule size: {})",
125 m_clients.size(),
126 m_scheduleMap.size());
127 return m_stop || !m_scheduleMap.empty();
128 });
129 }
130
131 if(m_stop.load()) {
132 IRSOL_NAMED_LOG_INFO("frame_collector", "Requested frame-collection stop");
133 break;
134 }
135
136 // Retrieve the nextDue time from the schedule map.
137 // This map is always sorted from small to high, as it's an ordered container.
138 irsol::types::timepoint_t nextDue = m_scheduleMap.begin()->first;
140 "frame_collector",
141 "Running for frame collection due at {}",
143
144 // Wait at most until the `nextDue` time.
145 // Allow for early break in case of:
146 // - m_stop is set to true due to a stop-request
147 // - a new client is registered with a nextDue time that is smaller than the current nextDue
148 // time
149 m_scheduleCondition.wait_until(lock, nextDue, [this, currentNextDue = nextDue]() {
150 if(m_stop.load()) {
151 // stopped externally, exit the wait
152 return true;
153 }
154 if(m_scheduleMap.empty()) {
155 // Don't wake up early unnecessarily, as there's no schedules in the map
156 return false;
157 }
158
159 // Check if a new schedule has been inserted into the map, which happens
160 // earlier than the due time we captured prior to sleeping.
161 auto newNextDue = m_scheduleMap.begin()->first;
162
164 "frame_collector",
165 "Current first timestamp in the map: {}, current nextDue: {}",
167 irsol::utils::timestampToString(currentNextDue));
168 return newNextDue < currentNextDue;
169 });
170
171 if(m_stop.load()) {
173 "frame_collector", "Frame collection stop request received, breaking loop");
174 return;
175 }
176
177 // Refresh the nextDue, as the above condition might have finished due to a new client being
178 // registered earlier than the 'nextDue' time that was initially selected.
179 nextDue = m_scheduleMap.begin()->first;
180
181 // Clients due now or earlier
182 irsol::types::timepoint_t now = irsol::types::clock_t::now();
184 "frame_collector",
185 "Woken up at timestamp {}, with next due at {}",
188
189 const auto slack = m_clients.size() == 1 ? std::chrono::milliseconds(0) : FrameCollector::SLACK;
190 auto [readyClients, clientsSchedules] = collectReadyClients(now, slack);
192 "frame_collector", "Found {} clients that need an image now!", readyClients.size());
193 {
194 auto uniqueSchedules =
195 std::set<irsol::types::timepoint_t>(clientsSchedules.begin(), clientsSchedules.end());
196 for(auto uniqueSchedule : uniqueSchedules) {
197 auto numClientsWithSchedule = std::count_if(
198 clientsSchedules.begin(),
199 clientsSchedules.end(),
200 [&uniqueSchedule](const auto& schedule) { return schedule == uniqueSchedule; });
201
202 if(uniqueSchedule != nextDue) {
204 "frame_collector_slack",
205 "Actual schedule: {}, also considering {} clients with schedule {} due to allowed "
206 "slack of {}",
208 numClientsWithSchedule,
209 irsol::utils::timestampToString(uniqueSchedule),
211 }
212 }
213 }
214 cleanUpSchedule(clientsSchedules);
216 "frame_collector",
217 "After schedule cleanup, there are still {} schedules",
218 m_scheduleMap.size());
219
220 auto grabResult = grabImageData();
221 if(!grabResult) {
222 IRSOL_NAMED_LOG_WARN("frame_collector", "Image acquisition failed.");
223 continue;
224 }
225 auto& [frameMetadata, imageRawBuffer] = *grabResult;
226
227 // Deliver the frame to clients
228 std::vector<irsol::types::client_id_t> finishedClient;
229 for(auto clientId : readyClients) {
230 IRSOL_NAMED_LOG_DEBUG("frame_collector", "Notifying client {} for new image data", clientId);
231 auto& clientParams = m_clients.at(clientId);
232 // Push a new frame created on the fly to the current client's queue.
233 // This creates a copy of the image data into the queue, and this is wanted, so that if a
234 // consumer modifies the image, it doesn't affect other clients.
235 clientParams.queue->push(std::make_unique<Frame>(
236 frameMetadata,
238 {imageRawBuffer.begin(), imageRawBuffer.end()},
239 {frameMetadata.height, frameMetadata.width},
240 {irsol::protocol::BinaryDataAttribute("imageId", static_cast<int>(frameMetadata.frameId)),
242 "timestamp", irsol::utils::timestampToString(frameMetadata.timestamp))})));
243
244 // Try to schedule the client, if no longer needed, register it in the finishedClients
245 if(!schedule(clientId, clientParams.nextFrameDue + clientParams.interval)) {
246 finishedClient.push_back(clientId);
247 }
248 }
249
250 // Unlock the clients so we can remove finished clients
251 for(const auto& clientId : finishedClient) {
253 "frame_collector",
254 "Deregistering client {}, as it has consumed all the frames it needed.",
255 clientId);
257 }
258
259 IRSOL_NAMED_LOG_DEBUG("frame_collector", "Loop finished, restarting loop");
260 }
261}
std::atomic< bool > m_stop
Indicates whether the collector is stopping due to an external request.
std::pair< std::vector< irsol::types::client_id_t >, std::vector< irsol::types::timepoint_t > > collectReadyClients(irsol::types::timepoint_t now, irsol::types::duration_t slack) const
Collects clients who are scheduled to receive a frame at the given time.
FrameCollector(irsol::camera::Interface &camera)
Constructs a FrameCollector for the given camera interface.
Definition collector.cpp:21
std::optional< std::pair< FrameMetadata, std::vector< irsol::types::byte_t > > > grabImageData() const
Captures an image and returns it along with associated metadata.
void cleanUpSchedule(const std::vector< irsol::types::timepoint_t > schedules)
Removes schedules from the schedule map.
clock_t::time_point timepoint_t
Alias for a point in time as defined by clock_t.
Definition types.hpp:120
Represents a single binary data attribute within the protocol.
Definition binary.hpp:35
Represents a binary data object within the protocol.
Definition binary.hpp:107

◆ schedule()

bool irsol::server::frame_collector::FrameCollector::schedule ( const irsol::types::client_id_t clientId,
irsol::types::timepoint_t  nextFrameDue 
)
private

Schedules the next frame delivery for a client.

Parameters
clientIdThe client to schedule.
nextFrameDueThe next timestamp when the client should receive a frame.
Returns
true if successfully scheduled, false if the client is no longer active and can later be removed from the frame collector.

Definition at line 384 of file collector.cpp.

387{
389 m_clients.find(clientId) != m_clients.end(), "Impossible to schedule an unregistered client.");
390 // Update the parameters of the client.
391 auto& clientParams = m_clients.at(clientId);
392 if(clientParams.remainingFrames-- == 0 && clientParams.remainingFrames < 0) {
393 // Client no longer expects frames.
394 // This handles also clients that are listening forever, as their 'remainingFrames' is negative
395 // so this¨ condition is never fully met.
397 "frame_collector", "Client {} had no longer frames to produce.", clientId);
398 return false;
399 }
400
401 if(clientParams.nextFrameDue == nextFrameDue) {
402 // This has been called the first time for client-registration
404 "frame_collector",
405 "Client {} has been scheduled for timestamp {}.",
406 clientId,
407 irsol::utils::timestampToString(clientParams.nextFrameDue));
408 } else {
409
411 "frame_collector",
412 "(Rescheduling client {} for next frame, previous due: {}, next due {}, # count {}",
413 clientId,
414 irsol::utils::timestampToString(clientParams.nextFrameDue),
416 clientParams.remainingFrames);
417 }
418
419 // Updates the parameters of the client
420 clientParams.nextFrameDue = nextFrameDue;
421
422 // Registers the client for the scheduled timestamp.
423 m_scheduleMap[nextFrameDue].push_back(clientId);
424
425 // Notify the condition variable, that a new client has been scheduled
426 m_scheduleCondition.notify_one();
427 return true;
428}
#define IRSOL_ASSERT_ERROR
Error-level assertion macro.
Definition assert.hpp:134

◆ start()

void irsol::server::frame_collector::FrameCollector::start ( )

Starts the frame collection and distribution thread.

If the collector is already running, this call is ignored.

Definition at line 32 of file collector.cpp.

33{
34 if(m_stop.load()) {
36 "frame_collector", "Collector was requested to stop already. Ignoring re-start request");
37 return;
38 }
39 m_distributorThread = std::thread(&FrameCollector::run, this);
40}
void run()
Runs the frame distribution loop in a background thread.
std::thread m_distributorThread
Thread responsible for frame distribution.

◆ stop()

void irsol::server::frame_collector::FrameCollector::stop ( )

Stops the frame collector and joins worker threads.

After calling stop(), no further frames will be delivered to clients.

Definition at line 43 of file collector.cpp.

44{
45 m_stop.store(true);
46 m_scheduleCondition.notify_all();
47 if(m_distributorThread.joinable())
49}

Member Data Documentation

◆ m_cam

irsol::camera::Interface& irsol::server::frame_collector::FrameCollector::m_cam
private

Reference to the camera interface used for capturing.

Definition at line 214 of file collector.hpp.

◆ m_clients

std::unordered_map<irsol::types::client_id_t, ClientCollectionParams> irsol::server::frame_collector::FrameCollector::m_clients
private

Stores parameters for each registered client.

Definition at line 218 of file collector.hpp.

◆ m_clientsMutex

std::mutex irsol::server::frame_collector::FrameCollector::m_clientsMutex
private

Protects access to m_clients and m_scheduleMap.

Definition at line 216 of file collector.hpp.

◆ m_distributorThread

std::thread irsol::server::frame_collector::FrameCollector::m_distributorThread
private

Thread responsible for frame distribution.

Definition at line 223 of file collector.hpp.

◆ m_scheduleCondition

std::condition_variable irsol::server::frame_collector::FrameCollector::m_scheduleCondition
private

Signals when a new client is scheduled.

Definition at line 222 of file collector.hpp.

◆ m_scheduleMap

std::map<irsol::types::timepoint_t, std::vector<irsol::types::client_id_t> > irsol::server::frame_collector::FrameCollector::m_scheduleMap
private

Maps timestamps to client IDs scheduled at that time.

Definition at line 220 of file collector.hpp.

◆ m_stop

std::atomic<bool> irsol::server::frame_collector::FrameCollector::m_stop
private
Initial value:
{
false}

Indicates whether the collector is stopping due to an external request.

Definition at line 225 of file collector.hpp.

225 {
226 false};

◆ SLACK

constexpr irsol::types::duration_t irsol::server::frame_collector::FrameCollector::SLACK = std::chrono::milliseconds(50)
staticconstexpr

Slack window for batching frame delivery to clients.

Clients whose scheduled delivery times fall within this window of a captured frame will all receive the same frame. This reduces redundant captures and improves efficiency.

Definition at line 91 of file collector.hpp.


The documentation for this class was generated from the following files: