Skip to content

Commit e0cb83c

Browse files
authored
Merge pull request #119 from BAKAOLC/feat/video-texture
feat(video): add memory-based video loading and refactor stream handling
2 parents 980a808 + fdf4877 commit e0cb83c

5 files changed

Lines changed: 221 additions & 82 deletions

File tree

LuaSTG/LuaSTG/LuaBinding/VideoBindingHelpers.cpp

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

3636
auto array_idx = stack.create_array(list.size());
37-
for (size_t i = 0; i < list.size(); ++i) {
37+
int index = 1;
38+
for (auto const& info : list) {
3839
auto item_idx = stack.create_map(5);
39-
stack.set_map_value(item_idx, "index", list[i].index);
40-
stack.set_map_value(item_idx, "width", list[i].width);
41-
stack.set_map_value(item_idx, "height", list[i].height);
42-
stack.set_map_value(item_idx, "fps", list[i].fps);
43-
stack.set_map_value(item_idx, "duration", list[i].duration_seconds);
44-
stack.set_array_value(array_idx, i + 1, item_idx);
40+
41+
stack.set_map_value(item_idx, "index", info.index);
42+
stack.set_map_value(item_idx, "width", info.width);
43+
stack.set_map_value(item_idx, "height", info.height);
44+
stack.set_map_value(item_idx, "fps", info.fps);
45+
stack.set_map_value(item_idx, "duration", info.duration_seconds);
46+
47+
stack.set_array_value(array_idx, index, item_idx);
48+
stack.pop_value();
49+
++index;
4550
}
4651
}
4752

@@ -60,13 +65,18 @@ namespace luastg::binding::video {
6065
decoder->getAudioStreams(callback, &list);
6166

6267
auto array_idx = stack.create_array(list.size());
63-
for (size_t i = 0; i < list.size(); ++i) {
68+
int index = 1;
69+
for (auto const& info : list) {
6470
auto item_idx = stack.create_map(4);
65-
stack.set_map_value(item_idx, "index", list[i].index);
66-
stack.set_map_value(item_idx, "channels", list[i].channels);
67-
stack.set_map_value(item_idx, "sample_rate", list[i].sample_rate);
68-
stack.set_map_value(item_idx, "duration", list[i].duration_seconds);
69-
stack.set_array_value(array_idx, i + 1, item_idx);
71+
72+
stack.set_map_value(item_idx, "index", info.index);
73+
stack.set_map_value(item_idx, "channels", info.channels);
74+
stack.set_map_value(item_idx, "sample_rate", info.sample_rate);
75+
stack.set_map_value(item_idx, "duration", info.duration_seconds);
76+
77+
stack.set_array_value(array_idx, index, item_idx);
78+
stack.pop_value();
79+
++index;
7080
}
7181
}
7282

LuaSTG/LuaSTG/LuaBinding/modern/VideoDecoder.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -176,7 +176,7 @@ namespace luastg::binding {
176176
return luaL_error(vm, "create VideoDecoder from file '%s' failed", path.data());
177177
}
178178

179-
if (!decoder->open(path)) {
179+
if (!decoder->open(path, opt)) {
180180
return luaL_error(vm, "create VideoDecoder from file '%s' failed", path.data());
181181
}
182182

engine/graphics/d3d11/VideoDecoder.cpp

Lines changed: 94 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -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) {

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; }
@@ -97,8 +99,11 @@ namespace core {
9799
bool initialize(IGraphicsDevice* device);
98100

99101
private:
100-
// File loading
101-
bool loadVideoFile(StringView path);
102+
// Stream creation
103+
bool createStreamFromMemory(void const* data, size_t size);
104+
105+
// Video opening (requires stream to be set)
106+
bool openFromStream(VideoOpenOptions const& options);
102107

103108
// Hardware acceleration setup
104109
bool configureHardwareAcceleration();
@@ -118,6 +123,7 @@ namespace core {
118123

119124
// Property extraction
120125
bool extractVideoProperties();
126+
void cacheStreamInformation();
121127

122128
// Resource creation
123129
bool createResources();
@@ -168,5 +174,9 @@ namespace core {
168174

169175
SmartReference<IGraphicsSampler> m_sampler;
170176
bool m_premultiplied_alpha{ false };
177+
178+
// Cached stream information
179+
std::vector<VideoStreamInfo> m_cached_video_streams;
180+
std::vector<AudioStreamInfo> m_cached_audio_streams;
171181
};
172182
}

0 commit comments

Comments
 (0)