MayaFlux 0.5.0
Digital-First Multimedia Processing Framework
Loading...
Searching...
No Matches
CameraReader.hpp
Go to the documentation of this file.
1#pragma once
2
5
6#include <condition_variable>
7
8namespace MayaFlux::Kakshya {
9class CameraContainer;
10}
11
13class IOService;
14}
15
16namespace MayaFlux::IO {
17
18/**
19 * @brief Platform-specific FFmpeg input format string for camera devices.
20 */
21#if defined(MAYAFLUX_PLATFORM_LINUX)
22inline constexpr std::string_view CAMERA_FORMAT = "v4l2";
23#elif defined(MAYAFLUX_PLATFORM_MACOS)
24inline constexpr std::string_view CAMERA_FORMAT = "avfoundation";
25#elif defined(MAYAFLUX_PLATFORM_WINDOWS)
26inline constexpr std::string_view CAMERA_FORMAT = "dshow";
27#endif
28
29/**
30 * @struct CameraConfig
31 * @brief Configuration for opening a camera device via FFmpeg.
32 *
33 * Platform device string conventions:
34 * - Linux: "/dev/video0", "/dev/video1", …
35 * - macOS: "0" (AVFoundation device index) or "FaceTime HD Camera"
36 * - Windows: "video=Integrated Camera" (DirectShow filter name)
37 *
38 * Resolution and frame rate values are hints passed to the device driver
39 * via AVDictionary options. The device may negotiate different parameters;
40 * the actual negotiated values are available from CameraReader after open().
41 */
42struct MAYAFLUX_API CameraConfig {
43 std::string device_name; ///< Platform device string.
44 uint32_t target_width { 1920 }; ///< Requested width in pixels.
45 uint32_t target_height { 1080 }; ///< Requested height in pixels.
46 double target_fps { 30.0 }; ///< Hint only; device may ignore.
47 std::string format_override; ///< Leave empty to use CAMERA_FORMAT for current platform.
48 int pixel_format { -1 }; ///< Target AVPixelFormat as int; negative selects AV_PIX_FMT_RGBA.
49};
50
51/**
52 * @class CameraReader
53 * @brief FFmpeg device reader for live camera input with background decode.
54 *
55 * Owns the FFmpeg demux and video codec contexts for a single camera device.
56 * Decodes frames on a dedicated thread signalled by IOService::request_frame,
57 * writing RGBA pixels directly into CameraContainer::mutable_frame_ptr() and
58 * marking the container READY. The graphics thread is never blocked by device
59 * I/O.
60 *
61 * Two integration paths:
62 * - Managed: IOManager::open_camera() handles registration, reader_id
63 * assignment, container wiring, and avdevice initialisation.
64 * - Standalone: open() → create_container() → setup_io_service(id) →
65 * set_container() → close(). Caller is responsible for
66 * avdevice_register_all() before open().
67 *
68 * Unlike VideoFileReader there is no ring buffer, no seek, and no batch
69 * decode — the device is a live unbounded source. One frame is pulled per
70 * process cycle, demand-driven by CameraContainer::process_default().
71 */
72class MAYAFLUX_API CameraReader {
73public:
76
77 CameraReader(const CameraReader&) = delete;
81
82 /**
83 * @brief Open a camera device using the supplied config.
84 * @param config Device name, resolution hint, fps hint, format override.
85 * @return True on success.
86 */
87 [[nodiscard]] bool open(const CameraConfig& config);
88
89 /**
90 * @brief Release codec, demux, and scratch buffer resources.
91 */
92 void close();
93
94 /**
95 * @brief True if the device is open and codec is ready.
96 */
97 [[nodiscard]] bool is_open() const;
98
99 /**
100 * @brief Create a CameraContainer sized to the negotiated device resolution.
101 * @return Initialised container with slot-0 allocated, ready for pull_frame().
102 */
103 [[nodiscard]] std::shared_ptr<Kakshya::CameraContainer> create_container() const;
104
105 /**
106 * @brief Decode one frame from the device into the container's m_data[0].
107 *
108 * Pumps packets until one decoded frame is available, converts to RGBA
109 * via swscale into container->mutable_frame_ptr(), then calls
110 * container->mark_ready_for_processing(true). Returns false on EAGAIN
111 * (no frame yet) — not an error, just no data available this cycle.
112 *
113 * The caller is responsible for invoking this once per graphics cycle,
114 * before the container's process_default() is triggered by downstream
115 * consumers. Typical integration: register as a graphics pre_process_hook
116 * or call explicitly before buffer processing.
117 *
118 * @param container Target CameraContainer.
119 * @return True if a new frame was written, false if no frame was available.
120 */
121 bool pull_frame(const std::shared_ptr<Kakshya::CameraContainer>& container);
122
123 /** @brief Negotiated output width in pixels. */
124 [[nodiscard]] uint32_t width() const;
125
126 /** @brief Negotiated output height in pixels. */
127 [[nodiscard]] uint32_t height() const;
128
129 /** @brief Negotiated frame rate in fps. */
130 [[nodiscard]] double frame_rate() const;
131
132 /**
133 * @brief Store a weak reference to the container for IOService dispatch.
134 *
135 * Called by IOManager::open_camera() after create_container(). Enables
136 * pull_frame_all() to resolve the container without an explicit argument.
137 *
138 * @param container CameraContainer created by this reader.
139 */
140 void set_container(const std::shared_ptr<Kakshya::CameraContainer>& container);
141
142 /**
143 * @brief Signal the background decode thread to pull one frame.
144 *
145 * Non-blocking. Called by IOManager::dispatch_frame_request() or the
146 * standalone IOService lambda. The decode thread wakes, calls pull_frame(),
147 * writes pixels into the container, marks it READY, then sleeps until the
148 * next signal. Safe to call from any thread.
149 */
150 void pull_frame_all();
151
152 /** @brief Last error string, empty if no error. */
153 [[nodiscard]] const std::string& last_error() const;
154
155 /**
156 * @brief Setup an IOService for this reader with the given reader_id.
157 * @param reader_id Globally unique ID assigned to this reader.
158 *
159 * This is method is called when working outside of IOManager for self registration.
160 * IOManager::open_camera() handles this automatically for managed readers.
161 */
162 void setup_io_service(uint64_t reader_id);
163
164private:
165 std::shared_ptr<FFmpegDemuxContext> m_demux;
166 std::shared_ptr<VideoStreamContext> m_video;
167 mutable std::shared_mutex m_ctx_mutex;
168 std::vector<uint8_t> m_sws_buf;
169 mutable std::string m_last_error;
170 std::weak_ptr<Kakshya::CameraContainer> m_container_ref;
171
172 std::shared_ptr<Registry::Service::IOService> m_standalone_service;
173 uint64_t m_standalone_reader_id {};
174 bool m_owns_service {};
175 bool m_scaler_ready {};
176
177 std::thread m_decode_thread;
178 std::mutex m_decode_mutex;
179 std::condition_variable m_decode_cv;
180 std::atomic<bool> m_decode_stop { false };
181 std::atomic<bool> m_decode_active { false };
182 std::atomic<bool> m_frame_requested { false };
183
184 void start_decode_thread();
185 void stop_decode_thread();
186 void decode_thread_func();
187
188 /**
189 * @brief Pixel format requested at open() time, as an AVPixelFormat int.
190 *
191 * Negative selects RGBA, matching VideoStreamContext::setup_scaler's
192 * default. Read by create_container(), which runs before the scaler is
193 * built and therefore cannot consult the negotiated format.
194 */
195 int m_requested_pixel_format { -1 };
196};
197
198} // namespace MayaFlux::IO
uint32_t width
uint32_t height
CameraReader(const CameraReader &)=delete
CameraReader & operator=(const CameraReader &)=delete
std::shared_ptr< Registry::Service::IOService > m_standalone_service
std::vector< uint8_t > m_sws_buf
std::shared_mutex m_ctx_mutex
CameraReader & operator=(CameraReader &&)=delete
std::weak_ptr< Kakshya::CameraContainer > m_container_ref
std::shared_ptr< VideoStreamContext > m_video
std::condition_variable m_decode_cv
std::shared_ptr< FFmpegDemuxContext > m_demux
CameraReader(CameraReader &&)=delete
FFmpeg device reader for live camera input with background decode.
auto create_container(Args &&... args) -> std::shared_ptr< ContainerType >
creates a new container of the specified type
Definition Depot.hpp:54
std::string device_name
Platform device string.
std::string format_override
Leave empty to use CAMERA_FORMAT for current platform.
Platform-specific FFmpeg input format string for camera devices.