VLink 2.0.0
A high-performance communication middleware
Loading...
Searching...
No Matches
camera_frame.h
Go to the documentation of this file.
1/*
2 * Copyright (C) 2026 by Thun Lu. All rights reserved.
3 * Author: Thun Lu <thun.lu@zohomail.cn>
4 * Repo: https://github.com/thun-res/vlink
5 * _ __ __ _ __
6 * | | / / / / (_) ____ / /__
7 * | | / / / / / / / __ \ / //_/
8 * | |/ / / /___ / / / / / / / ,<
9 * |___/ /_____/ /_/ /_/ /_/ /_/|_|
10 *
11 * Licensed under the Apache License, Version 2.0 (the "License");
12 * you may not use this file except in compliance with the License.
13 * You may obtain a copy of the License at
14 *
15 * http://www.apache.org/licenses/LICENSE-2.0
16 *
17 * Unless required by applicable law or agreed to in writing, software
18 * distributed under the License is distributed on an "AS IS" BASIS,
19 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
20 * See the License for the specific language governing permissions and
21 * limitations under the License.
22 */
23
24/**
25 * @file camera_frame.h
26 * @brief Zero-copy camera image frame container for VLink transport.
27 *
28 * @details
29 * @c CameraFrame carries a single image frame -- either uncompressed (YUV, RGB)
30 * or compressed (JPEG, H.264, H.265) -- together with a @c Header for sequencing
31 * and timestamping. The struct is exactly 80 bytes on 64-bit platforms.
32 *
33 * @par Pixel formats
34 * | Value | Description |
35 * | ------------------- | ------------------------------------------ |
36 * | kFormatYuv420 | Planar 4:2:0 (I420) |
37 * | kFormatYuv422 | Planar 4:2:2 |
38 * | kFormatYuv444 | Planar 4:4:4 |
39 * | kFormatNv12 | Semi-planar YUV 4:2:0 (Y + interleaved UV) |
40 * | kFormatNv21 | Semi-planar YUV 4:2:0 (Y + interleaved VU) |
41 * | kFormatYuyv | Packed YUYV 4:2:2 |
42 * | kFormatYvyu | Packed YVYU 4:2:2 |
43 * | kFormatUyvy | Packed UYVY 4:2:2 |
44 * | kFormatVyuy | Packed VYUY 4:2:2 |
45 * | kFormatBgr888Packed | Packed 24-bit BGR |
46 * | kFormatRgb888Packed | Packed 24-bit RGB |
47 * | kFormatRgb888Planar | Planar 24-bit RGB |
48 * | kFormatJpeg | JPEG compressed |
49 * | kFormatH264 | H.264 / AVC compressed |
50 * | kFormatH265 | H.265 / HEVC compressed |
51 *
52 * @par Video stream types
53 * | Value | Description |
54 * | ------------ | ---------------------- |
55 * | kStreamI | I-frame (key frame) |
56 * | kStreamP | P-frame (predicted) |
57 * | kStreamB | B-frame (bi-predicted) |
58 *
59 * @par Binary wire format
60 * @code
61 * [ magic_begin (4) | CameraFrame struct (80) | pixel data (N) | magic_end (4) ]
62 * @endcode
63 *
64 * @par Usage
65 * @code
66 * vlink::zerocopy::CameraFrame frame;
67 * frame.set_width(1920);
68 * frame.set_height(1080);
69 * frame.set_format(vlink::zerocopy::CameraFrame::kFormatNv12);
70 * frame.create(1920 * 1080 * 3 / 2);
71 * // fill frame.data() ...
72 *
73 * vlink::Bytes wire;
74 * frame >> wire; // serialise
75 *
76 * vlink::zerocopy::CameraFrame frame2;
77 * frame2 << wire; // deserialise (zero-copy, borrows wire)
78 * @endcode
79 *
80 * @note
81 * - 32-bit architectures emit a compile-time warning and are not supported.
82 * - After @c operator<<, the data pointer references memory inside the source
83 * @c Bytes object. The @c Bytes must outlive the @c CameraFrame.
84 * - @c fill_data is an alias for @c deep_copy(uint8_t*, size_t).
85 */
86
87#pragma once
88
89#include <cstdint>
90
91#include "../base/bytes.h"
92#include "./header.h"
93
94namespace vlink {
95
96namespace zerocopy {
97
98/**
99 * @struct CameraFrame
100 * @brief Zero-copy camera image frame with format metadata and Header.
101 *
102 * @details
103 * Carries one complete image frame (any @c Format) along with resolution,
104 * channel count, capture frequency, and video stream-type metadata.
105 * The struct size is fixed at 80 bytes on 64-bit targets.
106 */
108 /**
109 * @brief Pixel/codec encoding format of the image payload.
110 *
111 * @details
112 * Values 1-12 describe uncompressed planar/packed formats.
113 * Values 101-103 describe compressed codec formats.
114 * Pass to @c set_format() and read via @c format().
115 */
116 enum Format : uint8_t {
117 kFormatUnknown = 0, ///< Unknown or uninitialised format.
118
119 kFormatYuv420 = 1, ///< Planar YUV 4:2:0 (I420).
120 kFormatYuv422 = 2, ///< Planar YUV 4:2:2.
121 kFormatYuv444 = 3, ///< Planar YUV 4:4:4.
122 kFormatNv12 = 4, ///< Semi-planar YUV 4:2:0 (Y + interleaved UV).
123 kFormatNv21 = 5, ///< Semi-planar YUV 4:2:0 (Y + interleaved VU).
124 kFormatYuyv = 6, ///< Packed YUYV 4:2:2.
125 kFormatYvyu = 7, ///< Packed YVYU 4:2:2.
126 kFormatUyvy = 8, ///< Packed UYVY 4:2:2.
127 kFormatVyuy = 9, ///< Packed VYUY 4:2:2.
128 kFormatBgr888Packed = 10, ///< Packed 24-bit BGR (3 bytes/pixel).
129 kFormatRgb888Packed = 11, ///< Packed 24-bit RGB (3 bytes/pixel).
130 kFormatRgb888Planar = 12, ///< Planar 24-bit RGB (separate R, G, B planes).
131
132 kFormatJpeg = 101, ///< JPEG compressed image.
133 kFormatH264 = 102, ///< H.264 / AVC compressed video frame.
134 kFormatH265 = 103, ///< H.265 / HEVC compressed video frame.
135 };
136
137 /**
138 * @brief Video stream frame type for compressed formats.
139 *
140 * @details
141 * Relevant when @c format() is @c kFormatH264 or @c kFormatH265.
142 * Set via @c set_stream(); read via @c stream().
143 */
144 enum Stream : uint8_t {
145 kStreamUnknown = 0, ///< Unknown stream type.
146 kStreamI, ///< I-frame (intra-coded, key frame).
147 kStreamP, ///< P-frame (predicted from previous frame).
148 kStreamB, ///< B-frame (bi-directionally predicted).
149 };
150
151 /**
152 * @brief Default constructor.
153 *
154 * @details
155 * Verifies via @c static_assert that the struct is exactly 80 bytes on
156 * 64-bit platforms. 32-bit architectures emit a compile-time warning.
157 */
158 CameraFrame() noexcept;
159
160 /**
161 * @brief Destructor -- frees the owned pixel buffer if @c is_owner() is @c true.
162 */
163 ~CameraFrame() noexcept;
164
165 /**
166 * @brief Copy constructor -- performs a deep copy of @p target.
167 *
168 * @param target Source frame to copy.
169 */
170 CameraFrame(const CameraFrame& target) noexcept;
171
172 /**
173 * @brief Move constructor -- transfers ownership from @p target.
174 *
175 * @details
176 * After the call @p target is empty and does not own any buffer.
177 *
178 * @param target Source frame to move from.
179 */
180 CameraFrame(CameraFrame&& target) noexcept;
181
182 /**
183 * @brief Copy-assignment operator -- deep-copies @p target.
184 *
185 * @param target Source frame to copy. Self-assignment is a no-op.
186 * @return Reference to @c *this.
187 */
188 CameraFrame& operator=(const CameraFrame& target) noexcept;
189
190 /**
191 * @brief Move-assignment operator -- transfers ownership from @p target.
192 *
193 * @param target Source frame to move. Self-assignment is a no-op.
194 * @return Reference to @c *this.
195 */
196 CameraFrame& operator=(CameraFrame&& target) noexcept;
197
198 /**
199 * @brief Deserialises a @c CameraFrame from a @c Bytes wire buffer.
200 *
201 * @details
202 * Validates the magic-number envelope, copies the struct header, and sets
203 * the pixel data pointer to reference memory inside @p bytes (zero-copy).
204 * @c is_owner() will be @c false after a successful call; @p bytes must
205 * outlive this @c CameraFrame.
206 *
207 * @param bytes Buffer produced by @c operator>>.
208 * @return @c true on success, @c false if the buffer fails validation
209 * or the total size is inconsistent.
210 */
211 bool operator<<(const Bytes& bytes) noexcept;
212
213 /**
214 * @brief Serialises this @c CameraFrame into a @c Bytes wire buffer.
215 *
216 * @details
217 * Writes the magic-number envelope, struct fields, and pixel payload into
218 * @p bytes, resizing it if necessary.
219 *
220 * @param bytes Output buffer (reallocated automatically if needed).
221 * @return Always @c true.
222 */
223 bool operator>>(Bytes& bytes) const noexcept;
224
225 /**
226 * @brief Checks whether @p bytes contains a valid @c CameraFrame wire buffer.
227 *
228 * @details
229 * Verifies minimum size and both magic-number sentinels.
230 *
231 * @param bytes Buffer to validate.
232 * @return @c true if the sentinels match and the size is sufficient.
233 */
234 [[nodiscard]] static bool check_valid(const Bytes& bytes) noexcept;
235
236 /**
237 * @brief Returns the total serialised byte count for this frame.
238 *
239 * @details
240 * Equals: @c sizeof(magic_begin) + @c sizeof(CameraFrame) + @c size() + @c sizeof(magic_end)
241 *
242 * @return Total bytes written by @c operator>>.
243 */
244 [[nodiscard]] size_t get_serialized_size() const noexcept;
245
246 /**
247 * @brief Returns @c true when the pixel buffer is present and non-empty.
248 *
249 * @return @c true if @c data() is non-null and @c size() > 0.
250 */
251 [[nodiscard]] bool is_valid() const noexcept;
252
253 /**
254 * @brief Borrows the pixel buffer from @p target without copying.
255 *
256 * @details
257 * Sets metadata and data pointer to match @p target; @c is_owner() becomes
258 * @c false. Any previously owned buffer is freed first.
259 *
260 * @param target Source frame to borrow from.
261 * @return @c false if @p target == @c *this, otherwise @c true.
262 */
263 bool shallow_copy(const CameraFrame& target) noexcept;
264
265 /**
266 * @brief Deep-copies the pixel buffer from @p target.
267 *
268 * @details
269 * If @c *this already owns a same-size buffer the data is copied in-place;
270 * otherwise a new buffer is allocated.
271 *
272 * @param target Source frame to copy.
273 * @return @c false if @p target == @c *this, otherwise @c true.
274 */
275 bool deep_copy(const CameraFrame& target) noexcept;
276
277 /**
278 * @brief Transfers ownership from @p target to @c *this.
279 *
280 * @details
281 * After the call @p target is empty. Self-move is a no-op.
282 *
283 * @param target Source frame to move from.
284 * @return @c false if @p target == @c *this, otherwise @c true.
285 */
286 bool move_copy(CameraFrame& target) noexcept;
287
288 /**
289 * @brief Allocates an owned pixel buffer of @p size bytes.
290 *
291 * @details
292 * Frees any existing owned buffer before allocating the new one.
293 * Buffer content is uninitialised after the call.
294 *
295 * @param size Number of bytes to allocate. Must be non-zero.
296 * @return @c false if @p size is zero, otherwise @c true.
297 */
298 bool create(size_t size) noexcept;
299
300 /**
301 * @brief Releases all resources and zeroes all fields including @c header.
302 */
303 void clear() noexcept;
304
305 /**
306 * @brief Borrows an external raw pixel pointer without copying.
307 *
308 * @details
309 * Sets the internal data pointer to @p data with @c is_owner() == false.
310 * The caller is responsible for the buffer lifetime.
311 *
312 * @param data Non-null pointer to pixel data.
313 * @param size Size of the buffer in bytes.
314 * @return @c false on invalid arguments or if the pointer is unchanged.
315 */
316 bool shallow_copy(uint8_t* data, size_t size) noexcept;
317
318 /**
319 * @brief Deep-copies pixel data from a raw pointer.
320 *
321 * @details
322 * Allocates or reuses an owned buffer and copies @p size bytes from @p data.
323 *
324 * @param data Source pixel data pointer. Must be non-null.
325 * @param size Number of bytes to copy. Must be non-zero.
326 * @return @c false on invalid arguments, otherwise @c true.
327 */
328 bool deep_copy(uint8_t* data, size_t size) noexcept;
329
330 /**
331 * @brief Alias for @c deep_copy(uint8_t*, size_t).
332 *
333 * @param data Source pointer.
334 * @param size Number of bytes.
335 * @return Result of the underlying @c deep_copy call.
336 */
337 bool fill_data(uint8_t* data, size_t size) noexcept;
338
339 /**
340 * @brief Returns the camera channel (sensor index).
341 *
342 * @return Channel number set by @c set_channel().
343 */
344 [[nodiscard]] uint32_t channel() const noexcept;
345
346 /**
347 * @brief Returns the image width in pixels.
348 *
349 * @return Width set by @c set_width().
350 */
351 [[nodiscard]] uint32_t width() const noexcept;
352
353 /**
354 * @brief Returns the image height in pixels.
355 *
356 * @return Height set by @c set_height().
357 */
358 [[nodiscard]] uint32_t height() const noexcept;
359
360 /**
361 * @brief Returns the capture frequency in Hz.
362 *
363 * @return Frequency set by @c set_freq().
364 */
365 [[nodiscard]] uint32_t freq() const noexcept;
366
367 /**
368 * @brief Returns the pixel/codec encoding format.
369 *
370 * @return @c Format value set by @c set_format().
371 */
372 [[nodiscard]] Format format() const noexcept;
373
374 /**
375 * @brief Returns the video stream frame type.
376 *
377 * @return @c Stream value set by @c set_stream().
378 */
379 [[nodiscard]] Stream stream() const noexcept;
380
381 /**
382 * @brief Returns a read-only pointer to the pixel data buffer.
383 *
384 * @return Pointer to pixel bytes, or @c nullptr if the frame is empty.
385 */
386 [[nodiscard]] const uint8_t* data() const noexcept;
387
388 /**
389 * @brief Returns the pixel buffer size in bytes.
390 *
391 * @return Number of payload bytes, or 0 if empty.
392 */
393 [[nodiscard]] size_t size() const noexcept;
394
395 /**
396 * @brief Returns @c true if this object owns its pixel buffer.
397 *
398 * @details
399 * An owned buffer is freed in the destructor. Borrowed buffers (created
400 * by @c shallow_copy or @c operator<<) are not freed.
401 *
402 * @return @c true when memory ownership is held.
403 */
404 [[nodiscard]] bool is_owner() const noexcept;
405
406 /**
407 * @brief Sets the camera channel (sensor index).
408 *
409 * @param channel Channel identifier.
410 */
411 void set_channel(uint32_t channel) noexcept;
412
413 /**
414 * @brief Sets the image width in pixels.
415 *
416 * @param width Pixel width of the image.
417 */
418 void set_width(uint32_t width) noexcept;
419
420 /**
421 * @brief Sets the image height in pixels.
422 *
423 * @param height Pixel height of the image.
424 */
425 void set_height(uint32_t height) noexcept;
426
427 /**
428 * @brief Sets the capture frequency in Hz.
429 *
430 * @param freq Capture rate in frames per second.
431 */
432 void set_freq(uint32_t freq) noexcept;
433
434 /**
435 * @brief Sets the pixel/codec encoding format.
436 *
437 * @param format One of the @c Format enum values.
438 */
439 void set_format(Format format) noexcept;
440
441 /**
442 * @brief Sets the video stream frame type.
443 *
444 * @param stream One of the @c Stream enum values.
445 */
446 void set_stream(Stream stream) noexcept;
447
448 /**
449 * @brief Gets the reserved field.
450 *
451 * @return Reference to the reserved field.
452 */
453 uint32_t& get_reserved() noexcept { return reserved_; }
454
455 Header header; ///< Sequencing and timestamp metadata for this frame.
456
457 static constexpr bool kZerocopyTypes{true}; /// Internal
458
459 private:
460 uint8_t* data_{nullptr};
461 size_t size_{0};
462 uint32_t channel_{0};
463 uint32_t width_{0};
464 uint32_t height_{0};
465 uint32_t freq_{0};
466 Format format_{kFormatUnknown};
467 Stream stream_{kStreamUnknown};
468 bool is_owner_{false};
469 uint32_t reserved_{0};
470
471 static constexpr uint32_t kMagicNumberBegin{0x98B7F15A};
472 static constexpr uint32_t kMagicNumberEnd{0x98B7F15F};
473};
474
475} // namespace zerocopy
476
477} // namespace vlink
Versatile byte buffer with small-buffer optimisation, ownership semantics and compression.
Common timestamp and sequencing metadata header for VLink zero-copy data types.
#define VLINK_EXPORT_AND_ALIGNED(align_num)
Marks a class or variable as both exported and aligned to the given byte boundary.
Definition macros.h:103
Zero-copy camera image frame with format metadata and Header.
Fixed-size (40-byte) metadata header embedded in all VLink zero-copy data types.