MayaFlux 1.0.0
Digital-First Multimedia Processing Framework
Loading...
Searching...
No Matches
DynamicVideoStream.hpp
Go to the documentation of this file.
1#pragma once
2
4
5namespace MayaFlux::Core {
6class VKImage;
7}
8
9namespace MayaFlux::Buffers {
10class VKBuffer;
11}
12
13namespace MayaFlux::Kakshya {
14
15struct ImageData;
16class FrameSeekProcessor;
17
18/**
19 * @struct VideoStreamSpec
20 * @brief Describes a DynamicVideoStream so it can be built by designated initializer.
21 *
22 * Sizes and format are those of the frames that will be appended. The frame rate
23 * should be the rate they are appended at, which is also the rate playback readers
24 * count in. A ring size above zero enables circular mode.
25 */
27 uint32_t width {};
28 uint32_t height {};
30 double frame_rate {};
31 uint64_t ring_frames {};
32};
33
34/**
35 * @class DynamicVideoStream
36 * @brief Video container that frames are appended to at runtime, the counterpart
37 * of DynamicSoundStream.
38 *
39 * Frames arrive through append_frame instead of being decoded from a file. In
40 * linear mode the stream grows with each frame. In circular mode
41 * (enable_circular_buffer) it keeps only the most recent frames.
42 *
43 * Frame indices are absolute. Frame i is stored in slot i % capacity and stays
44 * readable until a later frame overwrites it, so get_write_head keeps counting
45 * past the capacity and is_frame_available says whether an older frame is still
46 * there. Reading is unchanged: get_frame_pixels and FrameAccessProcessor work as
47 * for any VideoStreamContainer. A FrameAccessProcessor playhead that falls more
48 * than the capacity behind the head finds its frames overwritten.
49 *
50 * Pixels are stored as bytes whatever the format.
51 */
52class MAYAFLUX_API DynamicVideoStream : public VideoStreamContainer {
53public:
54 /**
55 * @brief Build a stream from a spec, enabling the ring when it asks for one.
56 * @param spec Frame size, format, rate and optional ring size. The rate is
57 * used by readers that auto advance; zero leaves it unset.
58 */
59 explicit DynamicVideoStream(const VideoStreamSpec& spec);
60
61 /**
62 * @brief Write one frame at the write head and advance it.
63 * @param pixels Exactly get_frame_byte_size() bytes.
64 * @return True if the frame was stored.
65 */
66 bool append_frame(std::span<const uint8_t> pixels);
67
68 /**
69 * @brief Write one image, for example one read back from a GPU image.
70 *
71 * Width, height and format must match the stream.
72 *
73 * @return True if the frame was stored.
74 */
75 bool append_frame(const ImageData& image);
76
77 /**
78 * @brief Download a GPU image straight into the next frame slot.
79 *
80 * Blocking, like TextureContainer::from_image. The image must match the
81 * stream's width, height and format. Pass a persistent staging buffer
82 * (Buffers::create_image_staging_buffer(get_frame_byte_size())) to avoid a
83 * per-call Vulkan allocation.
84 *
85 * @param image Initialised source image.
86 * @param staging Optional host-visible staging buffer.
87 * @return True if the frame was stored.
88 */
89 bool append_frame(const std::shared_ptr<Core::VKImage>& image,
90 const std::shared_ptr<Buffers::VKBuffer>& staging = nullptr);
91
92 /**
93 * @brief Index of the next frame append_frame will write.
94 *
95 * Absolute: in circular mode it keeps counting past the capacity.
96 */
97 [[nodiscard]] uint64_t get_write_head() const { return m_write_head; }
98
99 /**
100 * @brief Keep only the most recent @p capacity frames.
101 *
102 * Allocates the ring and discards any frames already held, so call it before
103 * appending. Use is_ring_mode and get_ring_capacity to query it.
104 *
105 * @param capacity Number of frames to keep.
106 */
107 void enable_circular_buffer(uint64_t capacity);
108
109 /**
110 * @brief Pre-allocate room for @p frames in linear mode.
111 *
112 * Appends then reuse that storage instead of growing. Does nothing in
113 * circular mode, which allocates its ring up front.
114 */
115 void ensure_capacity(uint64_t frames);
116
117 /**
118 * @brief Copy the most recent frames into a new linear stream, oldest first.
119 *
120 * Frames that have already been overwritten come out black. The copy is
121 * independent of this stream.
122 *
123 * @param frames Frames to keep, capped at the ring capacity, or at the
124 * frames written for a linear stream.
125 * @return New stream with the same size, format and rate, or null if empty.
126 */
127 [[nodiscard]] std::shared_ptr<DynamicVideoStream> snapshot(uint64_t frames) const;
128
129 /**
130 * @brief Drop every frame and restart the write head at zero.
131 *
132 * A circular stream keeps its capacity.
133 */
134 void clear() override;
135
136 /**
137 * @brief Install a FrameSeekProcessor as the default processor.
138 *
139 * It reads like FrameAccessProcessor until a time map is set on it. Its clock
140 * runs at the stream's frame rate, which should be the rate frames are
141 * appended at.
142 */
143 void create_default_processor() override;
144
145 /**
146 * @brief The FrameSeekProcessor that reads this stream, installed on first use.
147 *
148 * Hooking the stream to a display buffer installs it too.
149 */
150 [[nodiscard]] std::shared_ptr<FrameSeekProcessor> seek_processor();
151
152 /**
153 * @brief Play back a fixed number of frames behind the write head.
154 *
155 * Call it after enable_circular_buffer and before frames are appended, and
156 * keep @p frames below the ring capacity. Playback holds the first frame
157 * until the lag has elapsed.
158 *
159 * @param frames Distance behind the head in frames.
160 */
161 void lag_behind_head(double frames);
162
163private:
164 uint64_t m_write_head {};
165
166 std::vector<uint8_t>& flat_bytes();
167 uint8_t* claim_frame_slot();
168 void finish_frame();
169 void publish_frame_count();
170};
171
172} // namespace MayaFlux::Kakshya
IO::ImageData image
Definition Decoder.cpp:47
const std::vector< float > * pixels
Definition Decoder.cpp:48
uint64_t get_write_head() const
Index of the next frame append_frame will write.
Video container that frames are appended to at runtime, the counterpart of DynamicSoundStream.
Concrete base implementation for streaming video containers.
Kakshya::ImageData ImageData
ImageFormat
User-friendly image format enum.
Definition ImageData.hpp:12
@ RGBA8
Four channel 8-bit.
Raw image data loaded from file or read back from a GPU image.
Definition ImageData.hpp:55
Describes a DynamicVideoStream so it can be built by designated initializer.