Skip to content

Commit b819434

Browse files
committed
feat(video): add memory-based video loading and refactor stream handling
- Add createVideoTextureFromMemory() method to graphics device interface and D3D11 implementation for loading videos from memory buffers - Add openFromMemory() method to VideoDecoder for opening video streams from memory with source name tracking - Refactor video stream enumeration to use cached stream information instead of querying at runtime - Improve stream info caching with proper initialization and cleanup in VideoDecoder - Refactor openFile() to use new createStreamFromMemory() and openFromStream() helper methods - Simplify Lua binding helpers by using range-based for loops and explicit index tracking instead of array indexing - Add stack value cleanup (pop_value) in video and audio stream Lua binding loops to prevent stack leaks - Improve code readability with better variable naming and consistent formatting in binding helpers
1 parent b4e3bb8 commit b819434

8 files changed

Lines changed: 248 additions & 84 deletions

File tree

LuaSTG/LuaSTG/LuaBinding/VideoBindingHelpers.hpp

Lines changed: 23 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -50,14 +50,19 @@ namespace luastg::binding::video {
5050
decoder->getVideoStreams(callback, &list);
5151

5252
auto array_idx = stack.create_array(list.size());
53-
for (size_t i = 0; i < list.size(); ++i) {
53+
int index = 1;
54+
for (auto const& info : list) {
5455
auto item_idx = stack.create_map(5);
55-
stack.set_map_value(item_idx, "index", list[i].index);
56-
stack.set_map_value(item_idx, "width", list[i].width);
57-
stack.set_map_value(item_idx, "height", list[i].height);
58-
stack.set_map_value(item_idx, "fps", list[i].fps);
59-
stack.set_map_value(item_idx, "duration", list[i].duration_seconds);
60-
stack.set_array_value(array_idx, i + 1, item_idx);
56+
57+
stack.set_map_value(item_idx, "index", info.index);
58+
stack.set_map_value(item_idx, "width", info.width);
59+
stack.set_map_value(item_idx, "height", info.height);
60+
stack.set_map_value(item_idx, "fps", info.fps);
61+
stack.set_map_value(item_idx, "duration", info.duration_seconds);
62+
63+
stack.set_array_value(array_idx, index, item_idx);
64+
stack.pop_value();
65+
++index;
6166
}
6267
}
6368

@@ -77,13 +82,18 @@ namespace luastg::binding::video {
7782
decoder->getAudioStreams(callback, &list);
7883

7984
auto array_idx = stack.create_array(list.size());
80-
for (size_t i = 0; i < list.size(); ++i) {
85+
int index = 1;
86+
for (auto const& info : list) {
8187
auto item_idx = stack.create_map(4);
82-
stack.set_map_value(item_idx, "index", list[i].index);
83-
stack.set_map_value(item_idx, "channels", list[i].channels);
84-
stack.set_map_value(item_idx, "sample_rate", list[i].sample_rate);
85-
stack.set_map_value(item_idx, "duration", list[i].duration_seconds);
86-
stack.set_array_value(array_idx, i + 1, item_idx);
88+
89+
stack.set_map_value(item_idx, "index", info.index);
90+
stack.set_map_value(item_idx, "channels", info.channels);
91+
stack.set_map_value(item_idx, "sample_rate", info.sample_rate);
92+
stack.set_map_value(item_idx, "duration", info.duration_seconds);
93+
94+
stack.set_array_value(array_idx, index, item_idx);
95+
stack.pop_value();
96+
++index;
8797
}
8898
}
8999

engine/graphics/core/GraphicsDevice.hpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,7 @@ namespace core {
111111
virtual bool createTexture(Vector2U size, ITexture2D** out_texture) = 0;
112112
virtual bool createVideoTexture(StringView path, ITexture2D** out_texture) = 0;
113113
virtual bool createVideoTexture(StringView path, VideoOpenOptions const& options, ITexture2D** out_texture) = 0;
114+
virtual bool createVideoTextureFromMemory(void const* data, size_t size, VideoOpenOptions const& options, ITexture2D** out_texture, StringView source_name = "<memory>") = 0;
114115
virtual bool createVideoDecoder(IVideoDecoder** out_decoder) = 0;
115116

116117
virtual bool createSampler(const GraphicsSamplerInfo& info, IGraphicsSampler** out_sampler) = 0;

engine/graphics/d3d11/GraphicsDevice.hpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ namespace core {
2424
bool createTextureFromImage(IImage* image, bool mipmap, ITexture2D** out_texture) override;
2525
bool createVideoTexture(StringView path, ITexture2D** out_texture) override;
2626
bool createVideoTexture(StringView path, VideoOpenOptions const& options, ITexture2D** out_texture) override;
27+
bool createVideoTextureFromMemory(void const* data, size_t size, VideoOpenOptions const& options, ITexture2D** out_texture, StringView source_name = "<memory>") override;
2728
bool createVideoDecoder(IVideoDecoder** out_decoder) override;
2829

2930
bool createSampler(const GraphicsSamplerInfo& info, IGraphicsSampler** out_sampler) override;

engine/graphics/d3d11/VideoDecoder.cpp

Lines changed: 94 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -84,13 +84,14 @@ namespace core {
8484

8585
close();
8686

87-
if (!loadVideoFile(path)) return false;
88-
if (!configureHardwareAcceleration()) return false;
89-
if (!selectVideoStream(options.video_stream_index)) return false;
90-
if (!negotiateOutputFormat(options)) return false;
91-
if (!extractVideoProperties()) return false;
92-
if (!createResources()) return false;
93-
if (!loadFirstFrame()) return false;
87+
SmartReference<IData> file_data;
88+
if (!FileSystemManager::readFile(path, file_data.put())) {
89+
Logger::error("[core] [VideoDecoder] Failed to read video file: {}", path);
90+
return false;
91+
}
92+
93+
if (!createStreamFromMemory(file_data->data(), file_data->size())) return false;
94+
if (!openFromStream(options)) return false;
9495

9596
m_last_open_path.assign(path.data(), path.size());
9697
m_last_open_options = options;
@@ -105,74 +106,105 @@ namespace core {
105106

106107
return true;
107108
}
109+
110+
bool VideoDecoder::openFromMemory(void const* data, size_t size, VideoOpenOptions const& options, StringView source_name) {
111+
if (!m_initialized) {
112+
Logger::error("[core] [VideoDecoder] Not initialized");
113+
return false;
114+
}
115+
116+
close();
117+
118+
if (!createStreamFromMemory(data, size)) return false;
119+
if (!openFromStream(options)) return false;
120+
121+
m_last_open_path.assign(source_name.data(), source_name.size());
122+
m_last_open_options = options;
123+
124+
setLooping(options.looping);
125+
if (options.loop_duration > 0.0) {
126+
setLoopRange(options.loop_end, options.loop_duration);
127+
}
128+
129+
Logger::info("[core] [VideoDecoder] Opened video from memory: {}x{}, duration: {:.2f}s",
130+
m_target_size.x, m_target_size.y, m_duration);
131+
132+
return true;
133+
}
108134

109135
void VideoDecoder::getVideoStreams(void (*callback)(VideoStreamInfo const&, void*), void* userdata) const {
110-
if (!m_source_reader || !callback) return;
136+
if (!callback) return;
111137

112-
wil::unique_prop_variant var;
113-
double duration_sec = 0.0;
114-
if (SUCCEEDED(m_source_reader->GetPresentationAttribute((DWORD)MF_SOURCE_READER_MEDIASOURCE, MF_PD_DURATION, &var))) {
115-
duration_sec = var.hVal.QuadPart / 10000000.0;
116-
}
117-
118-
for (DWORD si = 0; si < Config::kMaxStreams; ++si) {
119-
win32::com_ptr<IMFMediaType> mt;
120-
if (FAILED(m_source_reader->GetNativeMediaType(si, 0, mt.put()))) continue;
121-
122-
GUID major = GUID_NULL;
123-
if (FAILED(mt->GetGUID(MF_MT_MAJOR_TYPE, &major)) || major != MFMediaType_Video) continue;
124-
125-
VideoStreamInfo info{};
126-
info.index = si;
127-
info.duration_seconds = duration_sec;
128-
129-
UINT32 w = 0, h = 0;
130-
if (SUCCEEDED(MFGetAttributeSize(mt.get(), MF_MT_FRAME_SIZE, &w, &h))) {
131-
info.width = w;
132-
info.height = h;
133-
}
134-
135-
UINT32 num = 0, den = 0;
136-
if (SUCCEEDED(MFGetAttributeRatio(mt.get(), MF_MT_FRAME_RATE, &num, &den)) && den > 0) {
137-
info.fps = static_cast<double>(num) / static_cast<double>(den);
138-
}
139-
138+
for (auto const& info : m_cached_video_streams) {
140139
callback(info, userdata);
141140
}
142141
}
143142

144143
void VideoDecoder::getAudioStreams(void (*callback)(AudioStreamInfo const&, void*), void* userdata) const {
145-
if (!m_source_reader || !callback) return;
144+
if (!callback) return;
146145

147-
wil::unique_prop_variant var;
148-
double duration_sec = 0.0;
149-
if (SUCCEEDED(m_source_reader->GetPresentationAttribute((DWORD)MF_SOURCE_READER_MEDIASOURCE, MF_PD_DURATION, &var))) {
150-
duration_sec = var.hVal.QuadPart / 10000000.0;
151-
}
152-
153-
for (DWORD si = 0; si < Config::kMaxStreams; ++si) {
154-
win32::com_ptr<IMFMediaType> mt;
155-
if (FAILED(m_source_reader->GetNativeMediaType(si, 0, mt.put()))) continue;
156-
157-
GUID major = GUID_NULL;
158-
if (FAILED(mt->GetGUID(MF_MT_MAJOR_TYPE, &major)) || major != MFMediaType_Audio) continue;
159-
160-
AudioStreamInfo info{};
161-
info.index = si;
162-
info.duration_seconds = duration_sec;
163-
info.channels = (UINT32)MFGetAttributeUINT32(mt.get(), MF_MT_AUDIO_NUM_CHANNELS, 0);
164-
info.sample_rate = MFGetAttributeUINT32(mt.get(), MF_MT_AUDIO_SAMPLES_PER_SECOND, 0);
165-
146+
for (auto const& info : m_cached_audio_streams) {
166147
callback(info, userdata);
167148
}
168149
}
169150

170151
bool VideoDecoder::reopen(VideoOpenOptions const& options) {
171-
if (m_last_open_path.empty()) {
172-
Logger::error("[core] [VideoDecoder] reopen: no previous path (open was never successful)");
152+
if (!m_byte_stream) {
153+
Logger::error("[core] [VideoDecoder] reopen: no byte stream available");
154+
return false;
155+
}
156+
157+
auto saved_stream = m_byte_stream;
158+
159+
m_source_reader.reset();
160+
m_media_type.reset();
161+
m_texture.reset();
162+
m_shader_resource_view.reset();
163+
m_device_context.reset();
164+
m_video_processor.reset();
165+
m_video_processor_enum.reset();
166+
m_video_context.reset();
167+
m_video_device.reset();
168+
169+
m_output_format = OutputFormat::ARGB32;
170+
m_video_size = Vector2U{};
171+
m_target_size = Vector2U{};
172+
m_duration = 0.0;
173+
m_current_time = 0.0;
174+
m_last_requested_time = -1.0;
175+
m_frame_interval = 1.0 / Config::kDefaultFrameRateNum;
176+
m_frame_rate_num = Config::kDefaultFrameRateNum;
177+
m_frame_rate_den = Config::kDefaultFrameRateDen;
178+
m_frame_pitch = 0;
179+
m_video_stream_index = 0;
180+
m_loop_state = LoopState{};
181+
m_first_texture_update_logged = false;
182+
m_cached_video_streams.clear();
183+
m_cached_audio_streams.clear();
184+
185+
m_byte_stream = saved_stream;
186+
187+
if (FAILED(m_byte_stream->SetCurrentPosition(0))) {
188+
Logger::error("[core] [VideoDecoder] reopen: failed to reset stream position");
173189
return false;
174190
}
175-
return open(StringView(m_last_open_path), options);
191+
192+
if (!openFromStream(options)) {
193+
Logger::error("[core] [VideoDecoder] reopen: failed to reopen");
194+
return false;
195+
}
196+
197+
m_last_open_options = options;
198+
199+
setLooping(options.looping);
200+
if (options.loop_duration > 0.0) {
201+
setLoopRange(options.loop_end, options.loop_duration);
202+
}
203+
204+
Logger::info("[core] [VideoDecoder] Reopened video: {}x{}",
205+
m_target_size.x, m_target_size.y);
206+
207+
return true;
176208
}
177209

178210
void VideoDecoder::close() {
@@ -200,6 +232,9 @@ namespace core {
200232
m_video_stream_index = 0;
201233
m_loop_state = LoopState{};
202234
m_first_texture_update_logged = false;
235+
236+
m_cached_video_streams.clear();
237+
m_cached_audio_streams.clear();
203238
}
204239

205240
void VideoDecoder::setLoopRange(double end_sec, double duration_sec) {

engine/graphics/d3d11/VideoDecoder.hpp

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
#include <mfreadwrite.h>
1111
#include <mutex>
1212
#include <string>
13+
#include <vector>
1314

1415
namespace core {
1516
class VideoDecoder final :
@@ -33,6 +34,7 @@ namespace core {
3334

3435
bool open(StringView path) override;
3536
bool open(StringView path, VideoOpenOptions const& options) override;
37+
bool openFromMemory(void const* data, size_t size, VideoOpenOptions const& options, StringView source_name = "<memory>");
3638
void close() override;
3739

3840
bool hasVideo() const noexcept override { return m_source_reader.get() != nullptr; }
@@ -78,8 +80,11 @@ namespace core {
7880
bool initialize(IGraphicsDevice* device);
7981

8082
private:
81-
// File loading
82-
bool loadVideoFile(StringView path);
83+
// Stream creation
84+
bool createStreamFromMemory(void const* data, size_t size);
85+
86+
// Video opening (requires stream to be set)
87+
bool openFromStream(VideoOpenOptions const& options);
8388

8489
// Hardware acceleration setup
8590
bool configureHardwareAcceleration();
@@ -99,6 +104,7 @@ namespace core {
99104

100105
// Property extraction
101106
bool extractVideoProperties();
107+
void cacheStreamInformation();
102108

103109
// Resource creation
104110
bool createResources();
@@ -146,5 +152,9 @@ namespace core {
146152
VideoOpenOptions m_last_open_options;
147153
bool m_initialized{ false };
148154
bool m_first_texture_update_logged{ false };
155+
156+
// Cached stream information
157+
std::vector<VideoStreamInfo> m_cached_video_streams;
158+
std::vector<AudioStreamInfo> m_cached_audio_streams;
149159
};
150160
}

0 commit comments

Comments
 (0)