1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427
|
#ifndef FRAMEGRABBING_H
#define FRAMEGRABBING_H
#include <glm/fwd.hpp>
#include <list>
#include <map>
#include <string>
#include <gst/gst.h>
#include <glm/ext/vector_float4.hpp>
#include "FrameGrabber.h"
class FrameBuffer;
/**
* @brief Manages all frame grabbers in the application
*
* FrameGrabbing is a singleton that coordinates all FrameGrabber instances,
* handling frame capture from the rendered output and distributing frames
* to active grabbers (recorders, broadcasters, etc.).
*
* The class uses PBO (Pixel Buffer Objects) for efficient GPU-to-CPU frame
* transfers and maintains the pipeline state for all active grabbers.
*
* @note This is a singleton class - use FrameGrabbing::manager() to access
* @note Session calls grabFrame() after each render cycle
*
* Thread Safety: Not thread-safe - should be accessed from render thread only
*/
class FrameGrabbing
{
friend class Mixer;
// Private Constructor (Singleton pattern)
FrameGrabbing();
FrameGrabbing(FrameGrabbing const& copy) = delete;
FrameGrabbing& operator=(FrameGrabbing const& copy) = delete;
public:
/**
* @brief Get the singleton instance
* @return Reference to the FrameGrabbing manager instance
*/
static FrameGrabbing& manager()
{
// The only instance
static FrameGrabbing _instance;
return _instance;
}
/**
* @brief Destructor - cleans up all grabbers and resources
*/
~FrameGrabbing();
/**
* @brief Get the width of captured frames
* @return Frame width in pixels
*/
inline uint width() const { return write_width_; }
/**
* @brief Get the height of captured frames
* @return Frame height in pixels
*/
inline uint height() const { return write_height_; }
/**
* @brief Add a new frame grabber to the active list
* @param rec Pointer to the FrameGrabber to add (takes ownership)
* @param duration Optional duration in seconds (0 = unlimited)
*
* The grabber will start receiving frames on the next render cycle.
* When duration expires or grabber finishes, it will be automatically removed.
*/
void add(FrameGrabber *rec, uint64_t duration = 0);
/**
* @brief Chain a new grabber to start after the current is interrupted
* @param rec Current FrameGrabber that is running
* @param new_rec New FrameGrabber to start
*
* Useful for continuous recording - when one file finishes, another starts.
*/
void chain(FrameGrabber *rec, FrameGrabber *new_rec);
/**
* @brief Check if any frame grabbers are currently active
* @return true if at least one grabber is active, false otherwise
*/
bool busy() const;
/**
* @brief Get a frame grabber by its unique ID
* @param id The unique identifier of the grabber
* @return Pointer to the FrameGrabber, or nullptr if not found
*/
FrameGrabber *get(uint64_t id);
/**
* @brief Get a frame grabber by its type
* @param t The FrameGrabber::Type to search for
* @return Pointer to the first FrameGrabber of this type, or nullptr if none active
*/
FrameGrabber *get(FrameGrabber::Type t);
/**
* @brief Stop all active frame grabbers
*
* Sends stop signal to all grabbers and waits for them to finish cleanly.
*/
void stopAll();
/**
* @brief Clear all finished grabbers from the list
*
* Removes and deletes all grabbers that have completed or failed.
*/
void clearAll();
protected:
/**
* @brief Capture current frame and distribute to all active grabbers
* @param frame_buffer The FrameBuffer containing the rendered frame
*
* Called by Session after each render. Uses PBOs for efficient GPU readback.
* Only accessible to friend class Mixer.
*/
void grabFrame(FrameBuffer *frame_buffer, guint64 dt_millisec);
private:
std::list<FrameGrabber *> grabbers_;
std::map<FrameGrabber *, FrameGrabber *> grabbers_chain_;
std::map<FrameGrabber *, uint64_t> grabbers_duration_;
guint pbo_[2];
guint pbo_index_;
guint pbo_next_index_;
guint read_size_;
guint read_width_;
guint read_height_;
guint write_width_;
guint write_height_;
bool use_alpha_;
GstCaps *read_caps_;
GstCaps *write_caps_;
};
/**
* @brief Global manager for output FrameGrabbers (recordings, broadcasts, loopback, etc.)
*
* Outputs provides a safe interface to launch and stop output FrameGrabbers.
* Since FrameGrabbers can terminate automatically (on completion or failure),
* direct pointers become invalid. This class provides access by FrameGrabber::Type
* instead, ensuring only one active instance per type exists.
*
* Supports all output types:
* - Video recording (GRABBER_VIDEO, GRABBER_GPU)
* - PNG snapshots (GRABBER_PNG)
* - SRT broadcast (GRABBER_BROADCAST)
* - Shared memory (GRABBER_SHM)
* - Loopback camera (GRABBER_LOOPBACK)
*
* @note This is a singleton class - use Outputs::manager() to access
* @note Singleton mechanism ensures only one active instance per FrameGrabber::Type
*
* @example
* @code
* // Start a video recorder
* Outputs::manager().start(new VideoRecorder("output.mp4"));
*
* // Check if any output is active
* if (Outputs::manager().enabled(FrameGrabber::GRABBER_VIDEO,
* FrameGrabber::GRABBER_BROADCAST)) {
* // At least one is running
* }
*
* // Stop recording
* Outputs::manager().stop(FrameGrabber::GRABBER_VIDEO);
* @endcode
*
* Thread Safety: Not thread-safe - should be accessed from main/UI thread only
*/
class Outputs
{
// Private Constructor (Singleton pattern)
Outputs() {};
Outputs(Outputs const& copy) = delete;
Outputs& operator=(Outputs const& copy) = delete;
bool delayed[FrameGrabber::GRABBER_INVALID] = { false };
public:
/**
* @brief Get the singleton instance
* @return Reference to the Outputs manager instance
*/
static Outputs& manager ()
{
// The only instance
static Outputs _instance;
return _instance;
}
/**
* @brief Start a new output FrameGrabber
* @param ptr Pointer to the FrameGrabber to start (takes ownership)
* @param delay Optional delay before starting (default: immediate)
* @param timeout Optional timeout in seconds (0 = unlimited)
*
* The FrameGrabber is registered with FrameGrabbing and will start
* receiving frames. If delay > 0, a countdown timer is shown before starting.
*
* @note Only one instance per FrameGrabber::Type can be active
* @see chain() for starting a new grabber when current one finishes
*/
void start(FrameGrabber *ptr,
std::chrono::seconds delay = std::chrono::seconds(0),
uint64_t timeout = 0);
/**
* @brief Chain a new grabber to start when current one is interrupted
* @param new_rec New FrameGrabber to start after current one completes
*
* Useful for continuous recording - seamlessly starts a new file
* when the current recording is interrupted.
*
* @note The current grabber is determined by new_rec->type()
*/
void chain(FrameGrabber *new_rec);
/**
* @brief Check if a grabber is pending (delayed start)
* @param type The FrameGrabber::Type to check
* @return true if grabber is in delayed countdown, false otherwise
*/
bool pending(FrameGrabber::Type type);
/**
* @brief Check if any of the given grabber types are pending
* @tparam Types Variadic template for multiple FrameGrabber::Type arguments
* @param first First FrameGrabber::Type to check
* @param rest Additional FrameGrabber::Type arguments (0 or more)
* @return true if ANY of the given types are pending, false otherwise
*
* Uses variadic template recursion to check multiple types efficiently.
*
* @example
* @code
* // Check if any output is pending
* if (Outputs::manager().pending(FrameGrabber::GRABBER_VIDEO,
* FrameGrabber::GRABBER_BROADCAST)) {
* // At least one is in countdown
* }
* @endcode
*/
template<typename... Types>
bool pending(FrameGrabber::Type first, Types... rest) {
return pending(first) || pending(rest...);
}
/**
* @brief Check if a grabber is currently active
* @param type The FrameGrabber::Type to check
* @return true if grabber is running, false otherwise
*/
bool enabled(FrameGrabber::Type type);
/**
* @brief Check if any of the given grabber types are active
* @tparam Types Variadic template for multiple FrameGrabber::Type arguments
* @param first First FrameGrabber::Type to check
* @param rest Additional FrameGrabber::Type arguments (0 or more)
* @return true if ANY of the given types are enabled, false otherwise
*
* Uses variadic template recursion for efficient multi-type checking.
* Compiles to equivalent of: enabled(first) || enabled(arg2) || enabled(arg3) || ...
*
* @example
* @code
* // Check if recording or broadcasting
* if (Outputs::manager().enabled(FrameGrabber::GRABBER_VIDEO,
* FrameGrabber::GRABBER_BROADCAST,
* FrameGrabber::GRABBER_SHM)) {
* // Show "output active" indicator
* }
* @endcode
*/
template<typename... Types>
bool enabled(FrameGrabber::Type first, Types... rest) {
return enabled(first) || enabled(rest...);
}
/**
* @brief Check if a grabber is currently processing
* @param type The FrameGrabber::Type to check
* @return true if grabber is actively processing frames, false otherwise
*/
bool busy(FrameGrabber::Type type);
/**
* @brief Check if any of the given grabber types are busy
* @tparam Types Variadic template for multiple FrameGrabber::Type arguments
* @param first First FrameGrabber::Type to check
* @param rest Additional FrameGrabber::Type arguments (0 or more)
* @return true if ANY of the given types are busy, false otherwise
*
* @example
* @code
* // Check if any output is busy processing
* if (Outputs::manager().busy(FrameGrabber::GRABBER_VIDEO,
* FrameGrabber::GRABBER_BROADCAST)) {
* // At least one is actively processing frames
* }
* @endcode
*/
template<typename... Types>
bool busy(FrameGrabber::Type first, Types... rest) {
return busy(first) || busy(rest...);
}
/**
* @brief Get information string about a grabber
* @param type The FrameGrabber::Type to query
* @param extended If true, return detailed info; if false, return brief info
* @return String describing the grabber state (filename, URL, device, etc.)
*
* @example For GRABBER_VIDEO: "Recording: output.mp4"
* @example For GRABBER_BROADCAST: "srt://localhost:9998"
*/
std::string info(bool extended, FrameGrabber::Type type);
/**
* @brief Get concatenated information strings for multiple grabber types
* @tparam Types Variadic template for multiple FrameGrabber::Type arguments
* @param extended If true, return detailed info; if false, return brief info
* @param first First FrameGrabber::Type to query
* @param rest Additional FrameGrabber::Type arguments (0 or more)
* @return Concatenated string with info for all types, separated by newlines
*
* Each enabled grabber's info is on a separate line. Disabled grabbers are skipped.
*
* @example
* @code
* // Get info for all active outputs
* std::string all_info = Outputs::manager().info(false,
* FrameGrabber::GRABBER_VIDEO,
* FrameGrabber::GRABBER_BROADCAST,
* FrameGrabber::GRABBER_SHM);
* // Returns: "Recording: file.mp4\nsrt://localhost:9998\nShared Memory"
* @endcode
*/
template<typename... Types>
std::string info(bool extended, FrameGrabber::Type first, Types... rest) {
std::string result = info(extended, first);
if constexpr (sizeof...(rest) > 0) {
std::string rest_info = info(extended, rest...);
// If current result is "Disabled", replace with rest_info
if (result == "Disabled") {
result = rest_info;
}
// Otherwise, append rest_info if it's not "Disabled" or empty
else if (rest_info != "Disabled" && !rest_info.empty()) {
result += "\n" + rest_info;
}
}
return result;
}
/**
* @brief Stop a running grabber
* @param type The FrameGrabber::Type to stop
*
* Sends stop signal to the grabber, allowing it to finish cleanly
* and finalize output (close file, disconnect stream, etc.).
*/
void stop(FrameGrabber::Type type);
template<typename... Types>
void stop(FrameGrabber::Type first, Types... rest) {
stop(first);
stop(rest...);
}
/**
* @brief Check if a grabber is paused
* @param type The FrameGrabber::Type to check
* @return true if grabber is paused, false if running or not active
*/
bool paused(FrameGrabber::Type type);
template<typename... Types>
bool paused(FrameGrabber::Type first, Types... rest) {
return paused(first) || paused(rest...);
}
/**
* @brief Pause a running grabber
* @param type The FrameGrabber::Type to pause
*
* Temporarily stops frame capture while keeping the grabber active.
* Useful for video recording - pause without stopping the file.
*/
void pause(FrameGrabber::Type type);
template<typename... Types>
void pause(FrameGrabber::Type first, Types... rest) {
pause(first);
pause(rest...);
}
/**
* @brief Resume a paused grabber
* @param type The FrameGrabber::Type to resume
*
* Continues frame capture after pause().
*/
void unpause(FrameGrabber::Type type);
template<typename... Types>
void unpause(FrameGrabber::Type first, Types... rest) {
unpause(first);
unpause(rest...);
}
};
#endif // FRAMEGRABBING_H
|