@@ -117,13 +117,14 @@ namespace core {
117117
118118 close ();
119119
120- if (!loadVideoFile (path)) return false ;
121- if (!configureHardwareAcceleration ()) return false ;
122- if (!selectVideoStream (options.video_stream_index )) return false ;
123- if (!negotiateOutputFormat (options)) return false ;
124- if (!extractVideoProperties ()) return false ;
125- if (!createResources ()) return false ;
126- if (!loadFirstFrame ()) return false ;
120+ SmartReference<IData> file_data;
121+ if (!FileSystemManager::readFile (path, file_data.put ())) {
122+ Logger::error (" [core] [VideoDecoder] Failed to read video file: {}" , path);
123+ return false ;
124+ }
125+
126+ if (!createStreamFromMemory (file_data->data (), file_data->size ())) return false ;
127+ if (!openFromStream (options)) return false ;
127128
128129 m_last_open_path.assign (path.data (), path.size ());
129130 m_last_open_options = options;
@@ -138,74 +139,105 @@ namespace core {
138139
139140 return true ;
140141 }
142+
143+ bool VideoDecoder::openFromMemory (void const * data, size_t size, VideoOpenOptions const & options, StringView source_name) {
144+ if (!m_initialized) {
145+ Logger::error (" [core] [VideoDecoder] Not initialized" );
146+ return false ;
147+ }
148+
149+ close ();
150+
151+ if (!createStreamFromMemory (data, size)) return false ;
152+ if (!openFromStream (options)) return false ;
153+
154+ m_last_open_path.assign (source_name.data (), source_name.size ());
155+ m_last_open_options = options;
156+
157+ setLooping (options.looping );
158+ if (options.loop_duration > 0.0 ) {
159+ setLoopRange (options.loop_end , options.loop_duration );
160+ }
161+
162+ Logger::info (" [core] [VideoDecoder] Opened video from memory: {}x{}, duration: {:.2f}s" ,
163+ m_target_size.x , m_target_size.y , m_duration);
164+
165+ return true ;
166+ }
141167
142168 void VideoDecoder::getVideoStreams (void (*callback)(VideoStreamInfo const &, void *), void* userdata) const {
143- if (!m_source_reader || ! callback) return ;
169+ if (!callback) return ;
144170
145- wil::unique_prop_variant var;
146- double duration_sec = 0.0 ;
147- if (SUCCEEDED (m_source_reader->GetPresentationAttribute ((DWORD )MF_SOURCE_READER_MEDIASOURCE , MF_PD_DURATION , &var))) {
148- duration_sec = var.hVal .QuadPart / 10000000.0 ;
149- }
150-
151- for (DWORD si = 0 ; si < Config::kMaxStreams ; ++si) {
152- win32::com_ptr<IMFMediaType> mt;
153- if (FAILED (m_source_reader->GetNativeMediaType (si, 0 , mt.put ()))) continue ;
154-
155- GUID major = GUID_NULL ;
156- if (FAILED (mt->GetGUID (MF_MT_MAJOR_TYPE , &major)) || major != MFMediaType_Video) continue ;
157-
158- VideoStreamInfo info{};
159- info.index = si;
160- info.duration_seconds = duration_sec;
161-
162- UINT32 w = 0 , h = 0 ;
163- if (SUCCEEDED (MFGetAttributeSize (mt.get (), MF_MT_FRAME_SIZE , &w, &h))) {
164- info.width = w;
165- info.height = h;
166- }
167-
168- UINT32 num = 0 , den = 0 ;
169- if (SUCCEEDED (MFGetAttributeRatio (mt.get (), MF_MT_FRAME_RATE , &num, &den)) && den > 0 ) {
170- info.fps = static_cast <double >(num) / static_cast <double >(den);
171- }
172-
171+ for (auto const & info : m_cached_video_streams) {
173172 callback (info, userdata);
174173 }
175174 }
176175
177176 void VideoDecoder::getAudioStreams (void (*callback)(AudioStreamInfo const &, void *), void* userdata) const {
178- if (!m_source_reader || ! callback) return ;
177+ if (!callback) return ;
179178
180- wil::unique_prop_variant var;
181- double duration_sec = 0.0 ;
182- if (SUCCEEDED (m_source_reader->GetPresentationAttribute ((DWORD )MF_SOURCE_READER_MEDIASOURCE , MF_PD_DURATION , &var))) {
183- duration_sec = var.hVal .QuadPart / 10000000.0 ;
184- }
185-
186- for (DWORD si = 0 ; si < Config::kMaxStreams ; ++si) {
187- win32::com_ptr<IMFMediaType> mt;
188- if (FAILED (m_source_reader->GetNativeMediaType (si, 0 , mt.put ()))) continue ;
189-
190- GUID major = GUID_NULL ;
191- if (FAILED (mt->GetGUID (MF_MT_MAJOR_TYPE , &major)) || major != MFMediaType_Audio) continue ;
192-
193- AudioStreamInfo info{};
194- info.index = si;
195- info.duration_seconds = duration_sec;
196- info.channels = (UINT32 )MFGetAttributeUINT32 (mt.get (), MF_MT_AUDIO_NUM_CHANNELS , 0 );
197- info.sample_rate = MFGetAttributeUINT32 (mt.get (), MF_MT_AUDIO_SAMPLES_PER_SECOND , 0 );
198-
179+ for (auto const & info : m_cached_audio_streams) {
199180 callback (info, userdata);
200181 }
201182 }
202183
203184 bool VideoDecoder::reopen (VideoOpenOptions const & options) {
204- if (m_last_open_path.empty ()) {
205- Logger::error (" [core] [VideoDecoder] reopen: no previous path (open was never successful)" );
185+ if (!m_byte_stream) {
186+ Logger::error (" [core] [VideoDecoder] reopen: no byte stream available" );
187+ return false ;
188+ }
189+
190+ auto saved_stream = m_byte_stream;
191+
192+ m_source_reader.reset ();
193+ m_media_type.reset ();
194+ m_texture.reset ();
195+ m_shader_resource_view.reset ();
196+ m_device_context.reset ();
197+ m_video_processor.reset ();
198+ m_video_processor_enum.reset ();
199+ m_video_context.reset ();
200+ m_video_device.reset ();
201+
202+ m_output_format = OutputFormat::ARGB32 ;
203+ m_video_size = Vector2U{};
204+ m_target_size = Vector2U{};
205+ m_duration = 0.0 ;
206+ m_current_time = 0.0 ;
207+ m_last_requested_time = -1.0 ;
208+ m_frame_interval = 1.0 / Config::kDefaultFrameRateNum ;
209+ m_frame_rate_num = Config::kDefaultFrameRateNum ;
210+ m_frame_rate_den = Config::kDefaultFrameRateDen ;
211+ m_frame_pitch = 0 ;
212+ m_video_stream_index = 0 ;
213+ m_loop_state = LoopState{};
214+ m_first_texture_update_logged = false ;
215+ m_cached_video_streams.clear ();
216+ m_cached_audio_streams.clear ();
217+
218+ m_byte_stream = saved_stream;
219+
220+ if (FAILED (m_byte_stream->SetCurrentPosition (0 ))) {
221+ Logger::error (" [core] [VideoDecoder] reopen: failed to reset stream position" );
206222 return false ;
207223 }
208- return open (StringView (m_last_open_path), options);
224+
225+ if (!openFromStream (options)) {
226+ Logger::error (" [core] [VideoDecoder] reopen: failed to reopen" );
227+ return false ;
228+ }
229+
230+ m_last_open_options = options;
231+
232+ setLooping (options.looping );
233+ if (options.loop_duration > 0.0 ) {
234+ setLoopRange (options.loop_end , options.loop_duration );
235+ }
236+
237+ Logger::info (" [core] [VideoDecoder] Reopened video: {}x{}" ,
238+ m_target_size.x , m_target_size.y );
239+
240+ return true ;
209241 }
210242
211243 void VideoDecoder::close () {
@@ -233,6 +265,9 @@ namespace core {
233265 m_video_stream_index = 0 ;
234266 m_loop_state = LoopState{};
235267 m_first_texture_update_logged = false ;
268+
269+ m_cached_video_streams.clear ();
270+ m_cached_audio_streams.clear ();
236271 }
237272
238273 void VideoDecoder::setLoopRange (double end_sec, double duration_sec) {
0 commit comments