MayaFlux 0.5.0
Digital-First Multimedia Processing Framework
Loading...
Searching...
No Matches
VideoContainerBuffer.cpp
Go to the documentation of this file.
2
6
8
9namespace MayaFlux::Buffers {
10
11VideoStreamReader::VideoStreamReader(const std::shared_ptr<Kakshya::StreamContainer>& container)
12 : m_container(container)
13{
14 if (container) {
15 container->register_state_change_callback(
16 [this](const auto& c, auto s) {
17 this->on_container_state_change(c, s);
18 });
19 }
20}
21
22// =========================================================================
23// Lifecycle
24// =========================================================================
25
26void VideoStreamReader::on_attach(const std::shared_ptr<Buffer>& buffer)
27{
28 if (!m_container || !buffer) {
29 return;
30 }
31
32 m_reader_id = m_container->register_dimension_reader(0);
33
34 if (!m_container->is_ready_for_processing()) {
36 std::source_location::current(),
37 "VideoStreamReader: Container not ready for processing");
38 }
39
40 try {
41 auto texture_buffer = std::dynamic_pointer_cast<TextureBuffer>(buffer);
42 if (!texture_buffer) {
44 std::source_location::current(),
45 "VideoStreamReader: Buffer must be a TextureBuffer");
46 }
47
48 extract_frame_data(texture_buffer);
49
50 if (m_update_flags) {
51 buffer->mark_for_processing(true);
52 }
53
54 } catch (const std::exception& e) {
56 "VideoStreamReader: Error during on_attach: {}", e.what());
57 }
58}
59
60void VideoStreamReader::on_detach(const std::shared_ptr<Buffer>& /*buffer*/)
61{
62 if (m_container) {
63 m_container->unregister_dimension_reader(0);
64 m_container->unregister_state_change_callback();
65 }
66}
67
68// =========================================================================
69// Processing
70// =========================================================================
71
72void VideoStreamReader::processing_function(const std::shared_ptr<Buffer>& buffer)
73{
74 if (!m_container || !buffer) {
75 return;
76 }
77
78 if (m_container->is_at_end()) {
79 buffer->mark_for_removal();
80 return;
81 }
82
83 try {
84 auto state = m_container->get_processing_state();
85
87 if (m_update_flags) {
88 buffer->mark_for_removal();
89 }
90 return;
91 }
92
95 if (m_container->try_acquire_processing_token(0)) {
96 m_container->process_default();
97 }
98 }
99 }
100
101 auto texture_buffer = std::dynamic_pointer_cast<TextureBuffer>(buffer);
102 if (!texture_buffer) {
103 return;
104 }
105
106 extract_frame_data(texture_buffer);
107
108 if (m_update_flags) {
109 buffer->mark_for_processing(true);
110 }
111
112 m_container->mark_dimension_consumed(0, m_reader_id);
113
114 if (m_container->all_dimensions_consumed()) {
115 m_container->update_processing_state(Kakshya::ProcessingState::READY);
116 m_container->reset_processing_token();
117 }
118
119 } catch (const std::exception& e) {
121 "VideoStreamReader: Error during processing: {}", e.what());
122 }
123}
124
125// =========================================================================
126// Frame extraction
127// =========================================================================
128
129void VideoStreamReader::extract_frame_data(const std::shared_ptr<TextureBuffer>& texture_buffer)
130{
131 if (!m_container) {
132 return;
133 }
134
135 auto& processed_data = m_container->get_processed_data();
136 if (processed_data.empty()) {
137 return;
138 }
139
140 const auto* pixel_vec = std::get_if<std::vector<uint8_t>>(&processed_data[0]);
141 if (!pixel_vec || pixel_vec->empty()) {
142 return;
143 }
144
145 uint64_t expected_size = static_cast<uint64_t>(texture_buffer->get_width())
146 * texture_buffer->get_height()
147 * Portal::Graphics::TextureLoom::get_bytes_per_pixel(texture_buffer->get_format());
148
149 size_t copy_size = std::min(pixel_vec->size(), static_cast<size_t>(expected_size));
150
151 if (copy_size < expected_size) {
153 "VideoStreamReader: processed frame is {} bytes, texture expects {}; "
154 "uploading a partial surface",
155 pixel_vec->size(), expected_size);
156 }
157
158 texture_buffer->set_pixel_data(pixel_vec->data(), copy_size);
159}
160
161// =========================================================================
162// Container management
163// =========================================================================
164
165void VideoStreamReader::set_container(const std::shared_ptr<Kakshya::StreamContainer>& container)
166{
167 if (m_container) {
168 m_container->unregister_dimension_reader(0);
169 m_container->unregister_state_change_callback();
170 }
171
172 m_container = container;
173
174 if (container) {
175 m_reader_id = container->register_dimension_reader(0);
176 container->register_state_change_callback(
177 [this](const auto& c, auto s) {
178 this->on_container_state_change(c, s);
179 });
180 }
181}
182
183// =========================================================================
184// State callback
185// =========================================================================
186
188 const std::shared_ptr<Kakshya::SignalSourceContainer>& /*container*/,
190{
191 switch (state) {
194 "VideoStreamReader: Container marked for removal");
195 break;
196
199 "VideoStreamReader: Container entered ERROR state");
200 break;
201
202 default:
203 break;
204 }
205}
206
207// =========================================================================
208// VideoContainerBuffer implementation
209// =========================================================================
210
211namespace {
212
213 Portal::Graphics::ImageFormat resolve_container_format(
214 const std::shared_ptr<Kakshya::StreamContainer>& container,
216 {
217 if (auto vsc = std::dynamic_pointer_cast<Kakshya::VideoStreamContainer>(container))
218 return vsc->get_format();
219 return fallback;
220 }
221
222} // namespace
223
225 const std::shared_ptr<Kakshya::StreamContainer>& container,
228 static_cast<uint32_t>(container->get_structure().get_width()),
229 static_cast<uint32_t>(container->get_structure().get_height()),
230 resolve_container_format(container, format))
231 , m_container(container)
232{
233 if (!m_container) {
234 error<std::invalid_argument>(Journal::Component::Buffers, Journal::Context::Init,
235 std::source_location::current(),
236 "VideoContainerBuffer: container must not be null");
237 }
238
239 m_video_reader = std::make_shared<VideoStreamReader>(m_container);
240
242 "VideoContainerBuffer created: {}x{} from container",
243 get_width(), get_height());
244}
245
247{
248 auto self = std::dynamic_pointer_cast<VideoContainerBuffer>(shared_from_this());
249
250 m_video_reader = std::make_shared<VideoStreamReader>(m_container);
251 m_video_reader->set_processing_token(token);
254
255 auto texture_proc = std::make_shared<TextureProcessor>();
256 texture_proc->set_streaming_mode(true);
257 texture_proc->set_processing_token(token);
258 set_texture_processor(texture_proc);
259
260 auto chain = get_processing_chain();
261 if (!chain) {
262 chain = std::make_shared<BufferProcessingChain>();
264 }
265 chain->set_preferred_token(token);
266 chain->add_preprocessor(texture_proc, self);
267
269 "VideoContainerBuffer setup_processors: VideoStreamReader as default, "
270 "TextureProcessor as preprocessor");
271}
272
273void VideoContainerBuffer::set_container(const std::shared_ptr<Kakshya::StreamContainer>& container)
274{
275 m_container = container;
276
277 if (m_video_reader) {
278 m_video_reader->set_container(container);
279 }
280}
281
282} // namespace MayaFlux::Buffers
#define MF_INFO(comp, ctx,...)
#define MF_ERROR(comp, ctx,...)
#define MF_WARN(comp, ctx,...)
void set_texture_processor(const std::shared_ptr< TextureProcessor > &processor)
Allow inherited classes to set the TextureProcessor directly.
A hybrid buffer managing both a textured quad geometry and its pixel data.
std::shared_ptr< Buffers::BufferProcessingChain > get_processing_chain() override
Access the buffer's processing chain.
Definition VKBuffer.cpp:263
void set_default_processor(const std::shared_ptr< BufferProcessor > &processor) override
Set the buffer's default processor.
Definition VKBuffer.cpp:247
void set_processing_chain(const std::shared_ptr< BufferProcessingChain > &chain, bool force=false) override
Replace the buffer's processing chain.
Definition VKBuffer.cpp:268
void enforce_default_processing(bool should_process) override
Controls whether the buffer should use default processing.
Definition VKBuffer.hpp:241
std::shared_ptr< Kakshya::StreamContainer > m_container
std::shared_ptr< VideoStreamReader > m_video_reader
void set_container(const std::shared_ptr< Kakshya::StreamContainer > &container)
Replace the backing container at runtime.
VideoContainerBuffer(const std::shared_ptr< Kakshya::StreamContainer > &container, Portal::Graphics::ImageFormat format=Portal::Graphics::ImageFormat::RGBA8)
Construct a VideoContainerBuffer from a video container.
void setup_processors(ProcessingToken token) override
Override to wire VideoStreamReader as default and TextureProcessor as preprocessor.
void on_attach(const std::shared_ptr< Buffer > &buffer) override
Attach the reader to a TextureBuffer.
void on_detach(const std::shared_ptr< Buffer > &buffer) override
Detach the reader from its TextureBuffer.
void set_container(const std::shared_ptr< Kakshya::StreamContainer > &container)
Replace the backing container.
void extract_frame_data(const std::shared_ptr< TextureBuffer > &texture_buffer)
Extract frame pixel data from processed_data into the TextureBuffer.
void processing_function(const std::shared_ptr< Buffer > &buffer) override
Extract the current frame from the container into the TextureBuffer.
void on_container_state_change(const std::shared_ptr< Kakshya::SignalSourceContainer > &container, Kakshya::ProcessingState state)
Respond to container state changes.
std::shared_ptr< Kakshya::StreamContainer > m_container
VideoStreamReader(const std::shared_ptr< Kakshya::StreamContainer > &container)
Construct a VideoStreamReader for the given container.
static size_t get_bytes_per_pixel(ImageFormat format)
Get bytes per pixel for a format.
ProcessingToken
Bitfield enum defining processing characteristics and backend requirements for buffer operations.
@ BufferProcessing
Buffer processing (Buffers::BufferManager, processing chains)
@ Init
Engine/subsystem initialization.
@ Buffers
Buffers, Managers, processors and processing chains.
ProcessingState
Represents the current processing lifecycle state of a container.
@ READY
Container has data loaded and is ready for processing.
@ NEEDS_REMOVAL
Container is marked for removal from the system.
@ ERROR
Container is in an error state and cannot proceed.
@ PROCESSED
Container has completed processing and results are available.
ImageFormat
User-friendly image format enum.