diff --git a/vav2/platforms/windows/applications/vav2player/Vav2Player/App.xaml.cpp b/vav2/platforms/windows/applications/vav2player/Vav2Player/App.xaml.cpp
index dd4479f..b31063c 100644
--- a/vav2/platforms/windows/applications/vav2player/Vav2Player/App.xaml.cpp
+++ b/vav2/platforms/windows/applications/vav2player/Vav2Player/App.xaml.cpp
@@ -2,6 +2,7 @@
#include "App.xaml.h"
#include "MainWindow.xaml.h"
#include "VavCore/VavCore.h"
+#include "src/Logger/SimpleLogger.h"
using namespace winrt;
using namespace winrt::Microsoft::UI::Xaml;
@@ -16,28 +17,26 @@ namespace winrt::Vav2Player::implementation
if (result == VAVCORE_SUCCESS)
{
const char* version = vavcore_get_version_string();
- OutputDebugStringA("VavCore initialized successfully, version: ");
- OutputDebugStringA(version);
- OutputDebugStringA("\n");
+ LOGF_INFO("[App] VavCore initialized successfully, version: %s", version);
// Test creating a player
VavCorePlayer* player = vavcore_create_player();
if (player)
{
- OutputDebugStringA("VavCore player created successfully\n");
+ LOGF_INFO("[App] VavCore player created successfully");
vavcore_destroy_player(player);
}
// C++ wrapper test disabled for now (only using C API)
// VavCore::VideoPlayer cppPlayer;
- // OutputDebugStringA("VavCore C++ player created successfully\n");
+ // LOGF_INFO("[App] VavCore C++ player created successfully");
vavcore_cleanup();
- OutputDebugStringA("VavCore cleanup completed\n");
+ LOGF_INFO("[App] VavCore cleanup completed");
}
else
{
- OutputDebugStringA("Failed to initialize VavCore\n");
+ LOGF_ERROR("[App] Failed to initialize VavCore");
}
}
diff --git a/vav2/platforms/windows/applications/vav2player/Vav2Player/Vav2Player.vcxproj b/vav2/platforms/windows/applications/vav2player/Vav2Player/Vav2Player.vcxproj
index 83127b5..df5eab7 100644
--- a/vav2/platforms/windows/applications/vav2player/Vav2Player/Vav2Player.vcxproj
+++ b/vav2/platforms/windows/applications/vav2player/Vav2Player/Vav2Player.vcxproj
@@ -234,6 +234,7 @@
+
diff --git a/vav2/platforms/windows/applications/vav2player/Vav2Player/VideoPlayerControl2.xaml.cpp b/vav2/platforms/windows/applications/vav2player/Vav2Player/VideoPlayerControl2.xaml.cpp
index dbedd9e..725ce23 100644
--- a/vav2/platforms/windows/applications/vav2player/Vav2Player/VideoPlayerControl2.xaml.cpp
+++ b/vav2/platforms/windows/applications/vav2player/Vav2Player/VideoPlayerControl2.xaml.cpp
@@ -298,7 +298,18 @@ namespace winrt::Vav2Player::implementation
// 2. Load the video file. VavCore will now see the pending D3D device
// and initialize the decoder in the correct GPU-accelerated mode.
std::wstring filePathStr(filePath.c_str());
+ LogMgr::GetInstance().LogInfo(L"VideoPlayerControl2", L"About to call LoadVideo with path: " + std::wstring(filePath));
+
+ // Safety check: Verify m_playbackController is valid
+ if (!m_playbackController) {
+ LogMgr::GetInstance().LogError(L"VideoPlayerControl2", L"CRITICAL: m_playbackController is nullptr!");
+ UpdateStatus(L"Load failed - internal error");
+ return;
+ }
+
bool success = m_playbackController->LoadVideo(filePathStr);
+ LogMgr::GetInstance().LogInfo(L"VideoPlayerControl2",
+ success ? L"LoadVideo returned TRUE" : L"LoadVideo returned FALSE");
if (success) {
// 3. Get video dimensions (which are now available).
@@ -550,7 +561,8 @@ namespace winrt::Vav2Player::implementation
return;
}
- processor->ProcessFrame(player, [weakThis = get_weak(), &consecutiveErrors](bool success) {
+ processor->ProcessFrame(player, [weakThis = get_weak()](bool success) {
+ // Static variable is already in function scope, no need to capture
if (auto strongThis = weakThis.get()) {
if (!success) {
consecutiveErrors++;
diff --git a/vav2/platforms/windows/applications/vav2player/Vav2Player/src/Decoder/AV1Decoder.cpp b/vav2/platforms/windows/applications/vav2player/Vav2Player/src/Decoder/AV1Decoder.cpp
deleted file mode 100644
index f2a684c..0000000
--- a/vav2/platforms/windows/applications/vav2player/Vav2Player/src/Decoder/AV1Decoder.cpp
+++ /dev/null
@@ -1,277 +0,0 @@
-#include "pch.h"
-#include "AV1Decoder.h"
-#include
-#include
-
-namespace Vav2Player {
-
-// Dummy free callback for zero-copy decoding
-static void DummyFreeCallback(const uint8_t* data, void* user_data) {
- // Do nothing - packet data is managed externally
-}
-
-AV1Decoder::AV1Decoder()
- : m_dav1d_context(nullptr)
- , m_initialized(false) {
- // Initialize default AV1 settings
- m_av1_settings.max_frame_delay = 1;
- m_av1_settings.num_threads = std::min(4, (int)std::thread::hardware_concurrency());
- m_av1_settings.apply_grain = true;
- m_av1_settings.all_layers = false;
-}
-
-AV1Decoder::~AV1Decoder() {
- Cleanup();
-}
-
-bool AV1Decoder::Initialize(const VideoMetadata& metadata) {
- if (m_initialized) {
- LogError("Decoder already initialized");
- return false;
- }
-
- if (metadata.codec_type != VideoCodecType::AV1) {
- LogError("Invalid codec type for AV1 decoder");
- return false;
- }
-
- // Initialize dav1d settings
- Dav1dSettings settings;
- dav1d_default_settings(&settings);
-
- settings.max_frame_delay = m_av1_settings.max_frame_delay;
- settings.n_threads = m_av1_settings.num_threads;
- settings.apply_grain = m_av1_settings.apply_grain ? 1 : 0;
- settings.all_layers = m_av1_settings.all_layers ? 1 : 0;
-
- // Open dav1d decoder
- int ret = dav1d_open(&m_dav1d_context, &settings);
- if (ret != 0 || !m_dav1d_context) {
- LogError("Failed to open dav1d context: " + std::to_string(ret));
- return false;
- }
-
- m_initialized = true;
-
- std::cout << "[AV1Decoder_Headless] Initialized successfully" << std::endl;
- std::cout << " Resolution: " << metadata.width << "x" << metadata.height << std::endl;
- std::cout << " Frame rate: " << metadata.frame_rate << " fps" << std::endl;
- std::cout << " Threads: " << settings.n_threads << std::endl;
-
- return true;
-}
-
-void AV1Decoder::Cleanup() {
- if (m_dav1d_context) {
- dav1d_close(&m_dav1d_context);
- m_dav1d_context = nullptr;
- }
- m_initialized = false;
-}
-
-bool AV1Decoder::IsInitialized() const {
- return m_initialized;
-}
-
-bool AV1Decoder::DecodeFrame(const VideoPacket& input_packet, VideoFrame& output_frame) {
- if (!input_packet.IsValid()) {
- LogError("Invalid input packet");
- return false;
- }
-
- return DecodeFrame(input_packet.data.get(), input_packet.size, output_frame);
-}
-
-bool AV1Decoder::DecodeFrame(const uint8_t* packet_data, size_t packet_size, VideoFrame& output_frame) {
- if (!m_initialized || !packet_data || packet_size == 0) {
- LogError("Invalid parameters or decoder not initialized");
- return false;
- }
-
- auto decode_start = std::chrono::high_resolution_clock::now();
-
- // Create dav1d data structure with memory copy
- Dav1dData data;
- uint8_t* buffer = dav1d_data_create(&data, packet_size);
- if (!buffer) {
- LogError("Failed to create dav1d data buffer");
- return false;
- }
-
- std::memcpy(buffer, packet_data, packet_size);
-
- // Send data to decoder
- int ret = dav1d_send_data(m_dav1d_context, &data);
- if (ret != 0) {
- LogError("Failed to send data to dav1d: " + std::to_string(ret));
- return false;
- }
-
- // Get decoded picture
- Dav1dPicture picture = {};
- ret = dav1d_get_picture(m_dav1d_context, &picture);
- if (ret != 0) {
- LogError("Failed to get decoded picture: " + std::to_string(ret));
- return false;
- }
-
- // Convert to our VideoFrame format
- bool convert_success = ConvertDav1dPicture(picture, output_frame);
- dav1d_picture_unref(&picture);
-
- if (!convert_success) {
- LogError("Failed to convert dav1d picture");
- m_stats.decode_errors++;
- return false;
- }
-
- // Update statistics
- auto decode_end = std::chrono::high_resolution_clock::now();
- auto decode_duration = std::chrono::duration(decode_end - decode_start);
- UpdateDecodingStats(decode_duration.count(), packet_size);
-
- output_frame.is_valid = true;
- return true;
-}
-
-bool AV1Decoder::DecodeFrameZeroCopy(const uint8_t* packet_data, size_t packet_size, VideoFrame& output_frame) {
- if (!m_initialized || !packet_data || packet_size == 0) {
- LogError("Invalid parameters or decoder not initialized");
- return false;
- }
-
- auto decode_start = std::chrono::high_resolution_clock::now();
-
- // Create dav1d data structure with zero-copy (wrap existing data)
- Dav1dData data;
- int ret = dav1d_data_wrap(&data, const_cast(packet_data), packet_size, DummyFreeCallback, nullptr);
- if (ret != 0) {
- LogError("Failed to wrap packet data: " + std::to_string(ret));
- return false;
- }
-
- // Send data to decoder
- ret = dav1d_send_data(m_dav1d_context, &data);
- if (ret != 0) {
- LogError("Failed to send data to dav1d: " + std::to_string(ret));
- return false;
- }
-
- // Get decoded picture
- Dav1dPicture picture = {};
- ret = dav1d_get_picture(m_dav1d_context, &picture);
- if (ret != 0) {
- LogError("Failed to get decoded picture: " + std::to_string(ret));
- return false;
- }
-
- // Convert to our VideoFrame format
- bool convert_success = ConvertDav1dPicture(picture, output_frame);
- dav1d_picture_unref(&picture);
-
- if (!convert_success) {
- LogError("Failed to convert dav1d picture");
- m_stats.decode_errors++;
- return false;
- }
-
- // Update statistics
- auto decode_end = std::chrono::high_resolution_clock::now();
- auto decode_duration = std::chrono::duration(decode_end - decode_start);
- UpdateDecodingStats(decode_duration.count(), packet_size);
-
- output_frame.is_valid = true;
- return true;
-}
-
-bool AV1Decoder::Reset() {
- if (!m_initialized) {
- return false;
- }
-
- dav1d_flush(m_dav1d_context);
- return true;
-}
-
-bool AV1Decoder::Flush() {
- if (!m_initialized) {
- return false;
- }
-
- dav1d_flush(m_dav1d_context);
- return true;
-}
-
-bool AV1Decoder::ConvertDav1dPicture(const Dav1dPicture& dav1d_picture, VideoFrame& output_frame) {
- // Allocate YUV420P frame
- if (!output_frame.AllocateYUV420P(dav1d_picture.p.w, dav1d_picture.p.h)) {
- LogError("Failed to allocate VideoFrame");
- return false;
- }
-
- // Copy Y plane
- const uint8_t* src_y = static_cast(dav1d_picture.data[0]);
- uint8_t* dst_y = output_frame.y_plane.get();
- for (uint32_t y = 0; y < output_frame.height; y++) {
- std::memcpy(dst_y + y * output_frame.y_stride,
- src_y + y * dav1d_picture.stride[0],
- output_frame.width);
- }
-
- // Copy U plane
- const uint8_t* src_u = static_cast(dav1d_picture.data[1]);
- uint8_t* dst_u = output_frame.u_plane.get();
- for (uint32_t y = 0; y < output_frame.height / 2; y++) {
- std::memcpy(dst_u + y * output_frame.u_stride,
- src_u + y * dav1d_picture.stride[1],
- output_frame.width / 2);
- }
-
- // Copy V plane
- const uint8_t* src_v = static_cast(dav1d_picture.data[2]);
- uint8_t* dst_v = output_frame.v_plane.get();
- for (uint32_t y = 0; y < output_frame.height / 2; y++) {
- std::memcpy(dst_v + y * output_frame.v_stride,
- src_v + y * dav1d_picture.stride[1],
- output_frame.width / 2);
- }
-
- output_frame.color_space = ColorSpace::YUV420P;
- return true;
-}
-
-void AV1Decoder::UpdateDecodingStats(double decode_time_ms, size_t packet_size) {
- m_stats.total_frames_decoded++;
- m_stats.total_bytes_processed += packet_size;
- m_stats.total_decode_time_ms += decode_time_ms;
-}
-
-void AV1Decoder::LogError(const std::string& message) {
- std::cout << "[AV1Decoder_Headless ERROR] " << message << std::endl;
-}
-
-void AV1Decoder::ApplyOptimalSettingsForResolution(uint32_t width, uint32_t height) {
- // Apply resolution-based optimizations
- AV1Settings settings = m_av1_settings;
-
- if (width >= 3840 && height >= 2160) {
- // 4K or higher - use more threads
- settings.num_threads = 8;
- settings.max_frame_delay = 2;
- } else if (width >= 1920 && height >= 1080) {
- // 1080p - balanced settings
- settings.num_threads = 6;
- settings.max_frame_delay = 1;
- } else {
- // Lower resolutions - fewer threads
- settings.num_threads = 4;
- settings.max_frame_delay = 1;
- }
-
- SetAV1Settings(settings);
-
- std::cout << "[AV1Decoder_Headless] Applied optimal settings for " << width << "x" << height
- << " (threads=" << settings.num_threads << ", delay=" << settings.max_frame_delay << ")" << std::endl;
-}
-
-} // namespace Vav2Player
\ No newline at end of file
diff --git a/vav2/platforms/windows/applications/vav2player/Vav2Player/src/Decoder/AV1Decoder.h b/vav2/platforms/windows/applications/vav2player/Vav2Player/src/Decoder/AV1Decoder.h
deleted file mode 100644
index f804c08..0000000
--- a/vav2/platforms/windows/applications/vav2player/Vav2Player/src/Decoder/AV1Decoder.h
+++ /dev/null
@@ -1,101 +0,0 @@
-#pragma once
-#include "IVideoDecoder.h"
-#include
-#include
-#include
-
-namespace Vav2Player {
-
-// AV1 decoder using dav1d library
-class AV1Decoder : public IVideoDecoder {
-public:
- AV1Decoder();
- ~AV1Decoder() override;
-
- // Prevent copying
- AV1Decoder(const AV1Decoder&) = delete;
- AV1Decoder& operator=(const AV1Decoder&) = delete;
-
- // IVideoDecoder interface implementation
- bool Initialize(const VideoMetadata& metadata) override;
- void Cleanup() override;
- bool IsInitialized() const override;
-
- bool DecodeFrame(const VideoPacket& input_packet, VideoFrame& output_frame) override;
- bool DecodeFrame(const uint8_t* packet_data, size_t packet_size, VideoFrame& output_frame) override;
-
- // Zero-copy decoding methods
- bool DecodeFrameZeroCopy(const uint8_t* packet_data, size_t packet_size, VideoFrame& output_frame);
-
- bool Reset() override;
- bool Flush() override;
-
- // IVideoDecoder interface - additional methods
- std::string GetCodecName() const override { return "AV1 (dav1d)"; }
- VideoCodecType GetCodecType() const override { return VideoCodecType::AV1; }
- std::string GetVersion() const override { return "dav1d headless"; }
-
- DecoderStats GetStats() const override {
- DecoderStats stats;
- stats.frames_decoded = m_stats.total_frames_decoded;
- stats.decode_errors = m_stats.decode_errors;
- stats.avg_decode_time_ms = m_stats.GetAverageDecodeTime();
- stats.bytes_processed = m_stats.total_bytes_processed;
- return stats;
- }
-
- void ResetStats() override {
- m_stats = DecodingStatistics{};
- }
-
- // Statistics and performance monitoring
- struct DecodingStatistics {
- uint64_t total_frames_decoded = 0;
- uint64_t total_bytes_processed = 0;
- double total_decode_time_ms = 0.0;
- uint64_t decode_errors = 0;
-
- double GetAverageDecodeTime() const {
- return (total_frames_decoded > 0) ? (total_decode_time_ms / total_frames_decoded) : 0.0;
- }
-
- double GetAverageThroughput() const {
- return (total_decode_time_ms > 0) ? ((total_bytes_processed / 1024.0 / 1024.0) / (total_decode_time_ms / 1000.0)) : 0.0;
- }
- };
-
- const DecodingStatistics& GetStatistics() const { return m_stats; }
-
- // AV1-specific settings
- struct AV1Settings {
- int max_frame_delay = 1;
- int num_threads = 4;
- bool apply_grain = true;
- bool all_layers = false;
- };
-
- void SetAV1Settings(const AV1Settings& settings) { m_av1_settings = settings; }
- const AV1Settings& GetAV1Settings() const { return m_av1_settings; }
-
- // Compatibility method for GUI integration
- void ApplyOptimalSettingsForResolution(uint32_t width, uint32_t height);
-
- // Compatibility methods for pipeline integration
- bool IsWaitingForMoreData() const { return false; } // Simple always ready
-
-private:
- // dav1d context and state
- Dav1dContext* m_dav1d_context;
- bool m_initialized;
-
- // Statistics
- DecodingStatistics m_stats;
- AV1Settings m_av1_settings;
-
- // Helper methods
- bool ConvertDav1dPicture(const Dav1dPicture& dav1d_picture, VideoFrame& output_frame);
- void UpdateDecodingStats(double decode_time_ms, size_t packet_size);
- void LogError(const std::string& message);
-};
-
-} // namespace Vav2Player
\ No newline at end of file
diff --git a/vav2/platforms/windows/applications/vav2player/Vav2Player/src/Decoder/AdaptiveAV1Decoder.cpp b/vav2/platforms/windows/applications/vav2player/Vav2Player/src/Decoder/AdaptiveAV1Decoder.cpp
deleted file mode 100644
index e6b9b71..0000000
--- a/vav2/platforms/windows/applications/vav2player/Vav2Player/src/Decoder/AdaptiveAV1Decoder.cpp
+++ /dev/null
@@ -1,317 +0,0 @@
-#include "pch.h"
-#include "AdaptiveAV1Decoder.h"
-#include "AdaptiveNVDECDecoder.h" // For shared types and utilities
-#include
-#include
-
-namespace Vav2Player {
-
-AdaptiveAV1Decoder::AdaptiveAV1Decoder() : AV1Decoder() {
- // Initialize with balanced configuration
- m_config = AdaptiveUtils::GetBalancedConfig();
- m_metrics.last_update = std::chrono::steady_clock::now();
-}
-
-AdaptiveAV1Decoder::~AdaptiveAV1Decoder() = default;
-
-bool AdaptiveAV1Decoder::Initialize(const VideoMetadata& metadata, const AdaptiveConfig& config) {
- m_config = config;
- return Initialize(metadata);
-}
-
-bool AdaptiveAV1Decoder::Initialize(const VideoMetadata& metadata) {
- // Store original dimensions for scaling calculations
- m_originalWidth = metadata.width;
- m_originalHeight = metadata.height;
- m_targetScaledWidth = metadata.width;
- m_targetScaledHeight = metadata.height;
-
- // Initialize the base dav1d decoder
- bool result = AV1Decoder::Initialize(metadata);
- if (result) {
- OutputDebugStringA("[AdaptiveAV1Decoder] Initialized with adaptive quality control (post-decode scaling)\n");
- }
-
- return result;
-}
-
-bool AdaptiveAV1Decoder::DecodeFrame(const VideoPacket& input_packet, VideoFrame& output_frame) {
- return DecodeFrame(input_packet.data.get(), input_packet.size, output_frame);
-}
-
-bool AdaptiveAV1Decoder::DecodeFrame(const uint8_t* packet_data, size_t packet_size, VideoFrame& output_frame) {
- auto decode_start = std::chrono::high_resolution_clock::now();
-
- // Step 1: Decode frame using dav1d at full resolution
- VideoFrame full_resolution_frame;
- bool decode_success = AV1Decoder::DecodeFrame(packet_data, packet_size, full_resolution_frame);
-
- auto decode_end = std::chrono::high_resolution_clock::now();
- double decode_time = std::chrono::duration(decode_end - decode_start).count();
-
- if (!decode_success) {
- return false;
- }
-
- // Step 2: Apply post-decode scaling if quality level requires it
- if (m_currentQuality != QualityLevel::ULTRA) {
- auto scale_start = std::chrono::high_resolution_clock::now();
-
- bool scale_success = ScaleDecodedFrame(full_resolution_frame, output_frame);
-
- auto scale_end = std::chrono::high_resolution_clock::now();
- double scale_time = std::chrono::duration(scale_end - scale_start).count();
-
- // Add scaling time to decode time for performance analysis
- decode_time += scale_time;
-
- if (!scale_success) {
- // Fallback: use full resolution frame
- output_frame = std::move(full_resolution_frame);
- OutputDebugStringA("[AdaptiveAV1Decoder] Scaling failed, using full resolution\n");
- }
- } else {
- // ULTRA quality: use full resolution frame directly
- output_frame = std::move(full_resolution_frame);
- }
-
- // Step 3: Update performance metrics and potentially adjust quality
- if (m_adaptiveEnabled) {
- UpdatePerformanceMetrics(decode_time, 0.0); // Render time updated separately
- AnalyzePerformanceAndAdjust();
- }
-
- return true;
-}
-
-void AdaptiveAV1Decoder::SetQualityLevel(QualityLevel level) {
- if (level == m_currentQuality) return;
-
- m_targetQuality = level;
- CalculateScaledDimensions(level, m_targetScaledWidth, m_targetScaledHeight);
- m_currentQuality = level; // dav1d doesn't need decoder reconfiguration
-
- OutputDebugStringA(("[AdaptiveAV1Decoder] Quality changed to " +
- QualityLevelToString(level) + "\n").c_str());
-}
-
-void AdaptiveAV1Decoder::UpdatePerformanceMetrics(double decode_time, double render_time) {
- std::lock_guard lock(m_metricsMutex);
-
- // Update moving averages
- if (decode_time > 0) {
- UpdateMovingAverage(m_recentDecodeTimes, decode_time);
- m_metrics.avg_decode_time_ms = CalculateMovingAverage(m_recentDecodeTimes);
- }
-
- if (render_time > 0) {
- UpdateMovingAverage(m_recentRenderTimes, render_time);
- m_metrics.avg_render_time_ms = CalculateMovingAverage(m_recentRenderTimes);
- }
-
- m_metrics.last_update = std::chrono::steady_clock::now();
-}
-
-void AdaptiveAV1Decoder::AnalyzePerformanceAndAdjust() {
- std::lock_guard lock(m_metricsMutex);
-
- double totalFrameTime = m_metrics.avg_decode_time_ms + m_metrics.avg_render_time_ms;
-
- if (ShouldAdjustQuality(m_metrics.avg_decode_time_ms, m_metrics.avg_render_time_ms)) {
- QualityLevel optimalQuality = DetermineOptimalQuality(totalFrameTime);
-
- if (optimalQuality != m_currentQuality) {
- m_stableFrameCount = 0; // Reset stability counter
- SetQualityLevel(optimalQuality);
- } else {
- m_stableFrameCount++;
- }
- } else {
- m_stableFrameCount++;
- }
-}
-
-bool AdaptiveAV1Decoder::ShouldAdjustQuality(double avgDecodeTime, double avgRenderTime) {
- double totalTime = avgDecodeTime + avgRenderTime;
- double targetTime = m_config.target_frame_time_ms;
-
- // Require minimum frames for stability
- if (m_stableFrameCount < m_config.stable_frames_required) {
- return false;
- }
-
- // Check if we're outside acceptable performance range
- bool shouldScaleDown = totalTime > (targetTime * m_config.quality_down_threshold);
- bool shouldScaleUp = totalTime < (targetTime * m_config.quality_up_threshold) &&
- m_currentQuality > QualityLevel::ULTRA;
-
- return shouldScaleDown || shouldScaleUp;
-}
-
-QualityLevel AdaptiveAV1Decoder::DetermineOptimalQuality(double totalFrameTime) {
- double targetTime = m_config.target_frame_time_ms;
- double ratio = totalFrameTime / targetTime;
-
- // Determine quality based on performance ratio
- if (ratio > 2.0) return QualityLevel::MINIMUM; // Extremely slow
- if (ratio > 1.5) return QualityLevel::LOW; // Very slow
- if (ratio > 1.2) return QualityLevel::MEDIUM; // Slow
- if (ratio > 1.0) return QualityLevel::HIGH; // Slightly slow
- return QualityLevel::ULTRA; // Fast enough for full quality
-}
-
-bool AdaptiveAV1Decoder::ScaleDecodedFrame(const VideoFrame& input_frame, VideoFrame& output_frame) {
- // Ensure we have target dimensions calculated
- if (m_targetScaledWidth == 0 || m_targetScaledHeight == 0) {
- return false;
- }
-
- // Create output frame with scaled dimensions
- output_frame.width = m_targetScaledWidth;
- output_frame.height = m_targetScaledHeight;
- output_frame.color_space = input_frame.color_space;
- output_frame.timestamp_seconds = input_frame.timestamp_seconds;
-
- // Allocate scaled frame buffer
- if (!output_frame.AllocateYUV420P(m_targetScaledWidth, m_targetScaledHeight)) {
- return false;
- }
-
- // Perform CPU-based YUV420P scaling
- return ScaleYUV420P_CPU(input_frame, output_frame);
-}
-
-void AdaptiveAV1Decoder::CalculateScaledDimensions(QualityLevel quality, uint32_t& width, uint32_t& height) {
- double scaleFactor = GetQualityScaleFactor(quality);
-
- width = static_cast(m_originalWidth * scaleFactor);
- height = static_cast(m_originalHeight * scaleFactor);
-
- // Ensure dimensions are even (required for YUV420P)
- width = (width + 1) & ~1;
- height = (height + 1) & ~1;
-
- // Ensure minimum dimensions
- width = std::max(width, 64u);
- height = std::max(height, 64u);
-}
-
-bool AdaptiveAV1Decoder::EnsureScalingBuffer(size_t required_size) {
- if (m_scalingBufferSize < required_size) {
- m_scalingBuffer = std::make_unique(required_size);
- m_scalingBufferSize = required_size;
- }
- return m_scalingBuffer != nullptr;
-}
-
-bool AdaptiveAV1Decoder::ScaleYUV420P_CPU(const VideoFrame& input, VideoFrame& output) {
- // Scale Y plane
- if (!ScaleYUVPlane_CPU(
- input.y_plane.get(), input.y_stride, input.width, input.height,
- output.y_plane.get(), output.y_stride, output.width, output.height)) {
- return false;
- }
-
- // Scale U plane (half resolution)
- uint32_t input_uv_width = input.width / 2;
- uint32_t input_uv_height = input.height / 2;
- uint32_t output_uv_width = output.width / 2;
- uint32_t output_uv_height = output.height / 2;
-
- if (!ScaleYUVPlane_CPU(
- input.u_plane.get(), input.u_stride, input_uv_width, input_uv_height,
- output.u_plane.get(), output.u_stride, output_uv_width, output_uv_height)) {
- return false;
- }
-
- // Scale V plane (half resolution)
- if (!ScaleYUVPlane_CPU(
- input.v_plane.get(), input.v_stride, input_uv_width, input_uv_height,
- output.v_plane.get(), output.v_stride, output_uv_width, output_uv_height)) {
- return false;
- }
-
- return true;
-}
-
-bool AdaptiveAV1Decoder::ScaleYUVPlane_CPU(const uint8_t* src_plane, int src_stride, int src_width, int src_height,
- uint8_t* dst_plane, int dst_stride, int dst_width, int dst_height) {
- // Simple bilinear scaling implementation
- for (int dst_y = 0; dst_y < dst_height; dst_y++) {
- for (int dst_x = 0; dst_x < dst_width; dst_x++) {
- // Calculate source coordinates
- float src_x_f = (static_cast(dst_x) * src_width) / dst_width;
- float src_y_f = (static_cast(dst_y) * src_height) / dst_height;
-
- int src_x = static_cast(src_x_f);
- int src_y = static_cast(src_y_f);
-
- // Bounds checking
- src_x = std::min(src_x, src_width - 1);
- src_y = std::min(src_y, src_height - 1);
-
- // Simple nearest neighbor for now (can be upgraded to bilinear later)
- uint8_t pixel_value = src_plane[src_y * src_stride + src_x];
- dst_plane[dst_y * dst_stride + dst_x] = pixel_value;
- }
- }
-
- return true;
-}
-
-void AdaptiveAV1Decoder::UpdateMovingAverage(std::queue& queue, double newValue, size_t maxSize) {
- queue.push(newValue);
- while (queue.size() > maxSize) {
- queue.pop();
- }
-}
-
-double AdaptiveAV1Decoder::CalculateMovingAverage(const std::queue& queue) const {
- if (queue.empty()) return 0.0;
-
- double sum = 0.0;
- std::queue temp = queue;
- while (!temp.empty()) {
- sum += temp.front();
- temp.pop();
- }
- return sum / queue.size();
-}
-
-double AdaptiveAV1Decoder::GetQualityScaleFactor(QualityLevel level) {
- switch (level) {
- case QualityLevel::ULTRA: return 1.0; // 100% resolution
- case QualityLevel::HIGH: return 0.75; // 75% resolution
- case QualityLevel::MEDIUM: return 0.5; // 50% resolution
- case QualityLevel::LOW: return 0.35; // 35% resolution
- case QualityLevel::MINIMUM: return 0.25; // 25% resolution
- default: return 1.0;
- }
-}
-
-std::string AdaptiveAV1Decoder::QualityLevelToString(QualityLevel level) {
- switch (level) {
- case QualityLevel::ULTRA: return "ULTRA";
- case QualityLevel::HIGH: return "HIGH";
- case QualityLevel::MEDIUM: return "MEDIUM";
- case QualityLevel::LOW: return "LOW";
- case QualityLevel::MINIMUM: return "MINIMUM";
- default: return "UNKNOWN";
- }
-}
-
-PerformanceMetrics AdaptiveAV1Decoder::GetPerformanceMetrics() const {
- std::lock_guard lock(m_metricsMutex);
- return m_metrics;
-}
-
-void AdaptiveAV1Decoder::SetTargetFrameRate(double fps) {
- m_config.target_frame_time_ms = 1000.0 / fps;
- m_config.critical_frame_time_ms = 1000.0 / (fps * 0.6); // 60% of target FPS
-}
-
-void AdaptiveAV1Decoder::ForceQualityAdjustment() {
- m_stableFrameCount = m_config.stable_frames_required; // Force next analysis
-}
-
-} // namespace Vav2Player
\ No newline at end of file
diff --git a/vav2/platforms/windows/applications/vav2player/Vav2Player/src/Decoder/AdaptiveAV1Decoder.h b/vav2/platforms/windows/applications/vav2player/Vav2Player/src/Decoder/AdaptiveAV1Decoder.h
deleted file mode 100644
index 63d3409..0000000
--- a/vav2/platforms/windows/applications/vav2player/Vav2Player/src/Decoder/AdaptiveAV1Decoder.h
+++ /dev/null
@@ -1,96 +0,0 @@
-#pragma once
-#include "AV1Decoder.h"
-#include "AdaptiveNVDECDecoder.h" // Include full definitions of shared types
-#include
-#include
-#include
-
-namespace Vav2Player {
-
-// Enhanced AV1 decoder with adaptive quality adjustment using post-decode scaling
-class AdaptiveAV1Decoder : public AV1Decoder {
-public:
- AdaptiveAV1Decoder();
- ~AdaptiveAV1Decoder() override;
-
- // Enhanced initialization with adaptive features
- bool Initialize(const VideoMetadata& metadata) override;
- bool Initialize(const VideoMetadata& metadata, const AdaptiveConfig& config);
-
- // Override decode with adaptive logic and post-decode scaling
- bool DecodeFrame(const VideoPacket& input_packet, VideoFrame& output_frame) override;
- bool DecodeFrame(const uint8_t* packet_data, size_t packet_size, VideoFrame& output_frame) override;
-
- // Adaptive quality control
- void SetQualityLevel(QualityLevel level);
- QualityLevel GetCurrentQualityLevel() const { return m_currentQuality; }
-
- // Performance monitoring
- PerformanceMetrics GetPerformanceMetrics() const;
- void UpdatePerformanceMetrics(double decode_time, double render_time);
-
- // Manual override controls
- void EnableAdaptiveMode(bool enable) { m_adaptiveEnabled = enable; }
- void SetTargetFrameRate(double fps);
- void ForceQualityAdjustment(); // Immediate adjustment trigger
-
- // Configuration management
- void UpdateConfig(const AdaptiveConfig& config) { m_config = config; }
- AdaptiveConfig GetConfig() const { return m_config; }
-
- // Override codec name to indicate adaptive capability
- std::string GetCodecName() const override { return "AV1 (dav1d adaptive)"; }
-
-private:
- // Adaptive control state
- QualityLevel m_currentQuality = QualityLevel::ULTRA;
- QualityLevel m_targetQuality = QualityLevel::ULTRA;
- AdaptiveConfig m_config;
-
- // Performance monitoring
- mutable std::mutex m_metricsMutex;
- PerformanceMetrics m_metrics;
- std::queue m_recentDecodeTimes;
- std::queue m_recentRenderTimes;
-
- // Adaptive decision making
- std::atomic m_adaptiveEnabled{true};
- uint32_t m_stableFrameCount = 0;
-
- // Original video properties for scaling calculations
- uint32_t m_originalWidth = 0;
- uint32_t m_originalHeight = 0;
-
- // Current scaled properties (for post-decode scaling)
- uint32_t m_targetScaledWidth = 0;
- uint32_t m_targetScaledHeight = 0;
-
- // Post-decode scaling buffer
- std::unique_ptr m_scalingBuffer;
- size_t m_scalingBufferSize = 0;
-
- // Adaptive logic methods
- void AnalyzePerformanceAndAdjust();
- bool ShouldAdjustQuality(double avgDecodeTime, double avgRenderTime);
- QualityLevel DetermineOptimalQuality(double totalFrameTime);
-
- // Post-decode scaling methods (dav1d specific approach)
- bool ScaleDecodedFrame(const VideoFrame& input_frame, VideoFrame& output_frame);
- void CalculateScaledDimensions(QualityLevel quality, uint32_t& width, uint32_t& height);
- bool EnsureScalingBuffer(size_t required_size);
-
- // CPU-based scaling implementation
- bool ScaleYUV420P_CPU(const VideoFrame& input, VideoFrame& output);
- bool ScaleYUVPlane_CPU(const uint8_t* src_plane, int src_stride, int src_width, int src_height,
- uint8_t* dst_plane, int dst_stride, int dst_width, int dst_height);
-
- // Performance calculation helpers
- void UpdateMovingAverage(std::queue& queue, double newValue, size_t maxSize = 30);
- double CalculateMovingAverage(const std::queue& queue) const;
-
- // Quality level utilities
- static double GetQualityScaleFactor(QualityLevel level);
- static std::string QualityLevelToString(QualityLevel level);
-};
-
-} // namespace Vav2Player
\ No newline at end of file
diff --git a/vav2/platforms/windows/applications/vav2player/Vav2Player/src/Decoder/AdaptiveDecodingExample.cpp b/vav2/platforms/windows/applications/vav2player/Vav2Player/src/Decoder/AdaptiveDecodingExample.cpp
deleted file mode 100644
index f9c1108..0000000
--- a/vav2/platforms/windows/applications/vav2player/Vav2Player/src/Decoder/AdaptiveDecodingExample.cpp
+++ /dev/null
@@ -1,159 +0,0 @@
-// Example usage and integration guide for AdaptiveNVDECDecoder
-#include "pch.h"
-#include "AdaptiveNVDECDecoder.h"
-
-namespace Vav2Player {
-
-// Example: Basic adaptive decoding setup
-class AdaptiveVideoPlayer {
-public:
- bool InitializeAdaptiveDecoding(const VideoMetadata& metadata, double targetFPS = 30.0) {
- // Create adaptive configuration based on target performance
- AdaptiveConfig config = AdaptiveUtils::CreateConfigForTarget(targetFPS);
-
- // Customize based on system capabilities
- if (IsLowEndSystem()) {
- config = AdaptiveUtils::GetConservativeConfig();
- } else if (IsHighEndSystem()) {
- config = AdaptiveUtils::GetQualityConfig();
- }
-
- // Initialize adaptive decoder
- m_adaptiveDecoder = std::make_unique();
- bool result = m_adaptiveDecoder->Initialize(metadata, config);
-
- if (result) {
- OutputDebugStringA("[AdaptiveVideoPlayer] Adaptive decoding initialized successfully\n");
-
- // Start performance monitoring thread
- StartPerformanceMonitoring();
- }
-
- return result;
- }
-
- void ProcessFrameWithAdaptiveControl(const VideoPacket& packet) {
- auto render_start = std::chrono::high_resolution_clock::now();
-
- // Decode frame with adaptive quality
- VideoFrame frame;
- bool decode_success = m_adaptiveDecoder->DecodeFrame(packet, frame);
-
- if (decode_success) {
- // Render frame (this timing is important for adaptive control)
- RenderFrame(frame);
-
- auto render_end = std::chrono::high_resolution_clock::now();
- double render_time = std::chrono::duration(render_end - render_start).count();
-
- // Update render timing for adaptive algorithm
- m_adaptiveDecoder->UpdatePerformanceMetrics(0.0, render_time);
-
- // Log quality changes
- LogQualityStatus();
- }
- }
-
- // Example: Manual quality override for user preferences
- void SetUserQualityPreference(const std::string& qualityName) {
- QualityLevel level = QualityLevel::ULTRA;
-
- if (qualityName == "high") level = QualityLevel::HIGH;
- else if (qualityName == "medium") level = QualityLevel::MEDIUM;
- else if (qualityName == "low") level = QualityLevel::LOW;
- else if (qualityName == "minimum") level = QualityLevel::MINIMUM;
-
- // Temporarily disable adaptive mode for manual control
- m_adaptiveDecoder->EnableAdaptiveMode(false);
- m_adaptiveDecoder->SetQualityLevel(level);
-
- OutputDebugStringA(("[AdaptiveVideoPlayer] Manual quality set to " + qualityName + "\n").c_str());
- }
-
- // Example: Re-enable adaptive mode after manual override
- void EnableAutoQuality() {
- m_adaptiveDecoder->EnableAdaptiveMode(true);
- m_adaptiveDecoder->ForceQualityAdjustment(); // Immediate analysis
- OutputDebugStringA("[AdaptiveVideoPlayer] Auto quality control re-enabled\n");
- }
-
- // Example: Performance-based target adjustment
- void AdjustForSystemLoad() {
- PerformanceMetrics metrics = m_adaptiveDecoder->GetPerformanceMetrics();
-
- // If system is heavily loaded, reduce target FPS
- if (metrics.cpu_usage_percent > 85.0 || metrics.gpu_usage_percent > 90.0) {
- m_adaptiveDecoder->SetTargetFrameRate(24.0); // Reduce to 24 FPS
- OutputDebugStringA("[AdaptiveVideoPlayer] Reduced target FPS due to high system load\n");
- }
- // If system has headroom, try to increase FPS
- else if (metrics.cpu_usage_percent < 50.0 && metrics.gpu_usage_percent < 60.0) {
- m_adaptiveDecoder->SetTargetFrameRate(60.0); // Increase to 60 FPS
- OutputDebugStringA("[AdaptiveVideoPlayer] Increased target FPS due to available resources\n");
- }
- }
-
-private:
- std::unique_ptr m_adaptiveDecoder;
- QualityLevel m_lastLoggedQuality = QualityLevel::ULTRA;
-
- void StartPerformanceMonitoring() {
- // Background thread for periodic performance adjustment
- std::thread([this]() {
- while (m_adaptiveDecoder && m_adaptiveDecoder->IsInitialized()) {
- std::this_thread::sleep_for(std::chrono::seconds(2));
- AdjustForSystemLoad();
- }
- }).detach();
- }
-
- void LogQualityStatus() {
- QualityLevel currentQuality = m_adaptiveDecoder->GetCurrentQualityLevel();
- if (currentQuality != m_lastLoggedQuality) {
- PerformanceMetrics metrics = m_adaptiveDecoder->GetPerformanceMetrics();
-
- OutputDebugStringA(("[AdaptiveVideoPlayer] Quality: " +
- AdaptiveNVDECDecoder::QualityLevelToString(currentQuality) +
- ", Decode: " + std::to_string(metrics.avg_decode_time_ms) + "ms" +
- ", Render: " + std::to_string(metrics.avg_render_time_ms) + "ms\n").c_str());
-
- m_lastLoggedQuality = currentQuality;
- }
- }
-
- bool IsLowEndSystem() {
- // Example: Detect system capabilities
- // Check GPU memory, compute capability, etc.
- return false; // Placeholder
- }
-
- bool IsHighEndSystem() {
- // Example: High-end system with plenty of resources
- return true; // Placeholder
- }
-
- void RenderFrame(const VideoFrame& frame) {
- // Placeholder for actual rendering
- std::this_thread::sleep_for(std::chrono::milliseconds(5)); // Simulate render time
- }
-};
-
-// Example: Integration with VideoDecoderFactory
-class AdaptiveDecoderFactory {
-public:
- static std::unique_ptr CreateAdaptiveDecoder(
- VideoCodecType codecType,
- const AdaptiveConfig& config = AdaptiveUtils::GetBalancedConfig()) {
-
- if (codecType == VideoCodecType::AV1) {
- auto decoder = std::make_unique();
- // Configuration will be applied during Initialize()
- return std::move(decoder);
- }
-
- // Fallback to regular decoders for non-adaptive cases
- return VideoDecoderFactory::CreateDecoder(codecType, VideoDecoderFactory::DecoderType::AUTO);
- }
-};
-
-} // namespace Vav2Player
\ No newline at end of file
diff --git a/vav2/platforms/windows/applications/vav2player/Vav2Player/src/Decoder/AdaptiveNVDECDecoder.cpp b/vav2/platforms/windows/applications/vav2player/Vav2Player/src/Decoder/AdaptiveNVDECDecoder.cpp
deleted file mode 100644
index 363a7c1..0000000
--- a/vav2/platforms/windows/applications/vav2player/Vav2Player/src/Decoder/AdaptiveNVDECDecoder.cpp
+++ /dev/null
@@ -1,296 +0,0 @@
-#include "pch.h"
-#include "AdaptiveNVDECDecoder.h"
-#include
-#include
-
-namespace Vav2Player {
-
-AdaptiveNVDECDecoder::AdaptiveNVDECDecoder() : NVDECAV1Decoder() {
- // Initialize with balanced configuration
- m_config = AdaptiveUtils::GetBalancedConfig();
- m_metrics.last_update = std::chrono::steady_clock::now();
-}
-
-AdaptiveNVDECDecoder::~AdaptiveNVDECDecoder() = default;
-
-bool AdaptiveNVDECDecoder::Initialize(const VideoMetadata& metadata, const AdaptiveConfig& config) {
- m_config = config;
- return Initialize(metadata);
-}
-
-bool AdaptiveNVDECDecoder::Initialize(const VideoMetadata& metadata) {
- // Store original dimensions for scaling calculations
- m_originalWidth = metadata.width;
- m_originalHeight = metadata.height;
- m_currentScaledWidth = metadata.width;
- m_currentScaledHeight = metadata.height;
-
- // Initialize with maximum resolution support for dynamic scaling
- VideoMetadata adaptiveMetadata = metadata;
- // Ensure maxWidth/maxHeight can support dynamic resolution changes
- m_maxWidth = std::max(metadata.width, 4096u);
- m_maxHeight = std::max(metadata.height, 4096u);
-
- bool result = NVDECAV1Decoder::Initialize(adaptiveMetadata);
- if (result) {
- OutputDebugStringA("[AdaptiveNVDECDecoder] Initialized with adaptive quality control\n");
- }
-
- return result;
-}
-
-bool AdaptiveNVDECDecoder::DecodeFrame(const uint8_t* packet_data, size_t packet_size, VideoFrame& output_frame) {
- auto decode_start = std::chrono::high_resolution_clock::now();
-
- // Check if we need to reconfigure the decoder
- if (m_needsReconfiguration) {
- if (ReconfigureDecoder(m_currentScaledWidth, m_currentScaledHeight)) {
- m_needsReconfiguration = false;
- OutputDebugStringA(("[AdaptiveNVDECDecoder] Reconfigured to " +
- std::to_string(m_currentScaledWidth) + "x" +
- std::to_string(m_currentScaledHeight) + "\n").c_str());
- }
- }
-
- // Perform the actual decode
- bool decode_success = NVDECAV1Decoder::DecodeFrame(packet_data, packet_size, output_frame);
-
- auto decode_end = std::chrono::high_resolution_clock::now();
- double decode_time = std::chrono::duration(decode_end - decode_start).count();
-
- // Update performance metrics and potentially adjust quality
- if (m_adaptiveEnabled) {
- UpdatePerformanceMetrics(decode_time, 0.0); // Render time updated separately
- AnalyzePerformanceAndAdjust();
- }
-
- return decode_success;
-}
-
-void AdaptiveNVDECDecoder::SetQualityLevel(QualityLevel level) {
- if (level == m_currentQuality) return;
-
- m_targetQuality = level;
- CalculateScaledDimensions(level, m_currentScaledWidth, m_currentScaledHeight);
- m_needsReconfiguration = true;
-
- OutputDebugStringA(("[AdaptiveNVDECDecoder] Quality changed to " +
- QualityLevelToString(level) + "\n").c_str());
-}
-
-void AdaptiveNVDECDecoder::UpdatePerformanceMetrics(double decode_time, double render_time) {
- std::lock_guard lock(m_metricsMutex);
-
- // Update moving averages
- if (decode_time > 0) {
- UpdateMovingAverage(m_recentDecodeTimes, decode_time);
- m_metrics.avg_decode_time_ms = CalculateMovingAverage(m_recentDecodeTimes);
- }
-
- if (render_time > 0) {
- UpdateMovingAverage(m_recentRenderTimes, render_time);
- m_metrics.avg_render_time_ms = CalculateMovingAverage(m_recentRenderTimes);
- }
-
- m_metrics.last_update = std::chrono::steady_clock::now();
-}
-
-void AdaptiveNVDECDecoder::AnalyzePerformanceAndAdjust() {
- std::lock_guard lock(m_metricsMutex);
-
- double totalFrameTime = m_metrics.avg_decode_time_ms + m_metrics.avg_render_time_ms;
-
- if (ShouldAdjustQuality(m_metrics.avg_decode_time_ms, m_metrics.avg_render_time_ms)) {
- QualityLevel optimalQuality = DetermineOptimalQuality(totalFrameTime);
-
- if (optimalQuality != m_currentQuality) {
- m_stableFrameCount = 0; // Reset stability counter
- SetQualityLevel(optimalQuality);
- } else {
- m_stableFrameCount++;
- }
- } else {
- m_stableFrameCount++;
- }
-}
-
-bool AdaptiveNVDECDecoder::ShouldAdjustQuality(double avgDecodeTime, double avgRenderTime) {
- double totalTime = avgDecodeTime + avgRenderTime;
- double targetTime = m_config.target_frame_time_ms;
-
- // Require minimum frames for stability
- if (m_stableFrameCount < m_config.stable_frames_required) {
- return false;
- }
-
- // Check if we're outside acceptable performance range
- bool shouldScaleDown = totalTime > (targetTime * m_config.quality_down_threshold);
- bool shouldScaleUp = totalTime < (targetTime * m_config.quality_up_threshold) &&
- m_currentQuality > QualityLevel::ULTRA;
-
- return shouldScaleDown || shouldScaleUp;
-}
-
-QualityLevel AdaptiveNVDECDecoder::DetermineOptimalQuality(double totalFrameTime) {
- double targetTime = m_config.target_frame_time_ms;
- double ratio = totalFrameTime / targetTime;
-
- // Determine quality based on performance ratio
- if (ratio > 2.0) return QualityLevel::MINIMUM; // Extremely slow
- if (ratio > 1.5) return QualityLevel::LOW; // Very slow
- if (ratio > 1.2) return QualityLevel::MEDIUM; // Slow
- if (ratio > 1.0) return QualityLevel::HIGH; // Slightly slow
- return QualityLevel::ULTRA; // Fast enough for full quality
-}
-
-bool AdaptiveNVDECDecoder::ReconfigureDecoder(uint32_t newWidth, uint32_t newHeight) {
- if (!m_decoder) return false;
-
- // Use cuvidReconfigureDecoder for dynamic resolution changes
- CUVIDRECONFIGUREDECODERINFO reconfigInfo = {};
- reconfigInfo.ulWidth = newWidth;
- reconfigInfo.ulHeight = newHeight;
- reconfigInfo.ulTargetWidth = newWidth;
- reconfigInfo.ulTargetHeight = newHeight;
- reconfigInfo.ulNumDecodeSurfaces = m_createInfo.ulNumDecodeSurfaces;
-
- CUresult result = cuvidReconfigureDecoder(m_decoder, &reconfigInfo);
- if (result == CUDA_SUCCESS) {
- // Update our create info for future reference
- m_createInfo.ulWidth = newWidth;
- m_createInfo.ulHeight = newHeight;
- m_createInfo.ulTargetWidth = newWidth;
- m_createInfo.ulTargetHeight = newHeight;
-
- m_width = newWidth;
- m_height = newHeight;
- m_currentQuality = m_targetQuality;
- return true;
- } else {
- LogCUDAError(result, "cuvidReconfigureDecoder");
- return false;
- }
-}
-
-void AdaptiveNVDECDecoder::CalculateScaledDimensions(QualityLevel quality, uint32_t& width, uint32_t& height) {
- double scaleFactor = GetQualityScaleFactor(quality);
-
- width = static_cast(m_originalWidth * scaleFactor);
- height = static_cast(m_originalHeight * scaleFactor);
-
- // Ensure dimensions are even (required for most video formats)
- width = (width + 1) & ~1;
- height = (height + 1) & ~1;
-
- // Ensure minimum dimensions
- width = std::max(width, 64u);
- height = std::max(height, 64u);
-}
-
-void AdaptiveNVDECDecoder::UpdateMovingAverage(std::queue& queue, double newValue, size_t maxSize) {
- queue.push(newValue);
- while (queue.size() > maxSize) {
- queue.pop();
- }
-}
-
-double AdaptiveNVDECDecoder::CalculateMovingAverage(const std::queue& queue) const {
- if (queue.empty()) return 0.0;
-
- double sum = 0.0;
- std::queue temp = queue;
- while (!temp.empty()) {
- sum += temp.front();
- temp.pop();
- }
- return sum / queue.size();
-}
-
-double AdaptiveNVDECDecoder::GetQualityScaleFactor(QualityLevel level) {
- switch (level) {
- case QualityLevel::ULTRA: return 1.0; // 100% resolution
- case QualityLevel::HIGH: return 0.75; // 75% resolution
- case QualityLevel::MEDIUM: return 0.5; // 50% resolution
- case QualityLevel::LOW: return 0.35; // 35% resolution
- case QualityLevel::MINIMUM: return 0.25; // 25% resolution
- default: return 1.0;
- }
-}
-
-std::string AdaptiveNVDECDecoder::QualityLevelToString(QualityLevel level) {
- switch (level) {
- case QualityLevel::ULTRA: return "ULTRA";
- case QualityLevel::HIGH: return "HIGH";
- case QualityLevel::MEDIUM: return "MEDIUM";
- case QualityLevel::LOW: return "LOW";
- case QualityLevel::MINIMUM: return "MINIMUM";
- default: return "UNKNOWN";
- }
-}
-
-PerformanceMetrics AdaptiveNVDECDecoder::GetPerformanceMetrics() const {
- std::lock_guard lock(m_metricsMutex);
- return m_metrics;
-}
-
-void AdaptiveNVDECDecoder::SetTargetFrameRate(double fps) {
- m_config.target_frame_time_ms = 1000.0 / fps;
- m_config.critical_frame_time_ms = 1000.0 / (fps * 0.6); // 60% of target FPS
-}
-
-void AdaptiveNVDECDecoder::ForceQualityAdjustment() {
- m_stableFrameCount = m_config.stable_frames_required; // Force next analysis
-}
-
-// AdaptiveUtils implementation
-namespace AdaptiveUtils {
-
-AdaptiveConfig CreateConfigForTarget(double targetFPS) {
- AdaptiveConfig config;
- config.target_frame_time_ms = 1000.0 / targetFPS;
- config.critical_frame_time_ms = 1000.0 / (targetFPS * 0.6);
- return config;
-}
-
-AdaptiveConfig GetConservativeConfig() {
- AdaptiveConfig config;
- config.quality_down_threshold = 1.1; // Aggressive scaling down
- config.quality_up_threshold = 0.7; // Conservative scaling up
- config.stable_frames_required = 15; // Faster adjustments
- return config;
-}
-
-AdaptiveConfig GetBalancedConfig() {
- AdaptiveConfig config;
- // Use default values - already balanced
- return config;
-}
-
-AdaptiveConfig GetQualityConfig() {
- AdaptiveConfig config;
- config.quality_down_threshold = 1.4; // Tolerate more lag before reducing quality
- config.quality_up_threshold = 0.9; // Quick to restore quality
- config.stable_frames_required = 45; // Slower adjustments for stability
- return config;
-}
-
-double EstimateOptimalFrameRate(const PerformanceMetrics& metrics) {
- double totalFrameTime = metrics.avg_decode_time_ms + metrics.avg_render_time_ms;
- if (totalFrameTime <= 0) return 30.0; // Default
-
- return 1000.0 / totalFrameTime; // Convert ms to FPS
-}
-
-QualityLevel RecommendQualityForTargetFPS(double targetFPS, double currentFPS) {
- double ratio = currentFPS / targetFPS;
-
- if (ratio >= 0.95) return QualityLevel::ULTRA;
- if (ratio >= 0.80) return QualityLevel::HIGH;
- if (ratio >= 0.60) return QualityLevel::MEDIUM;
- if (ratio >= 0.40) return QualityLevel::LOW;
- return QualityLevel::MINIMUM;
-}
-
-} // namespace AdaptiveUtils
-
-} // namespace Vav2Player
\ No newline at end of file
diff --git a/vav2/platforms/windows/applications/vav2player/Vav2Player/src/Decoder/AdaptiveNVDECDecoder.h b/vav2/platforms/windows/applications/vav2player/Vav2Player/src/Decoder/AdaptiveNVDECDecoder.h
deleted file mode 100644
index 7d3d9a6..0000000
--- a/vav2/platforms/windows/applications/vav2player/Vav2Player/src/Decoder/AdaptiveNVDECDecoder.h
+++ /dev/null
@@ -1,143 +0,0 @@
-#pragma once
-#include "NVDECAV1Decoder.h"
-#include
-#include
-#include
-
-namespace Vav2Player {
-
-// Performance monitoring data
-struct PerformanceMetrics {
- double avg_decode_time_ms = 0.0;
- double avg_render_time_ms = 0.0;
- double cpu_usage_percent = 0.0;
- double gpu_usage_percent = 0.0;
- uint64_t dropped_frames = 0;
- std::chrono::steady_clock::time_point last_update;
-};
-
-// Quality levels for adaptive adjustment
-enum class QualityLevel {
- ULTRA = 0, // Original resolution, full quality
- HIGH = 1, // 75% resolution, high quality
- MEDIUM = 2, // 50% resolution, medium quality
- LOW = 3, // 25% resolution, low quality
- MINIMUM = 4 // 12.5% resolution, minimal quality
-};
-
-// Adaptive decoder configuration
-struct AdaptiveConfig {
- // Performance thresholds (milliseconds)
- double target_frame_time_ms = 33.33; // 30 FPS target
- double critical_frame_time_ms = 50.0; // 20 FPS critical
-
- // Quality adjustment thresholds
- double quality_up_threshold = 0.8; // Scale up when < 80% of target time
- double quality_down_threshold = 1.2; // Scale down when > 120% of target time
-
- // Hysteresis to prevent oscillation
- uint32_t stable_frames_required = 30; // Frames to wait before adjustment
-
- // Memory constraints
- uint32_t max_decode_surfaces = 16;
- uint32_t min_decode_surfaces = 4;
-
- // Enable/disable features
- bool enable_dynamic_resolution = true;
- bool enable_dynamic_surfaces = true;
- bool enable_skip_non_reference = true;
-};
-
-// Enhanced NVDEC decoder with adaptive quality adjustment
-class AdaptiveNVDECDecoder : public NVDECAV1Decoder {
-public:
- AdaptiveNVDECDecoder();
- ~AdaptiveNVDECDecoder() override;
-
- // Enhanced initialization with adaptive features
- bool Initialize(const VideoMetadata& metadata) override;
- bool Initialize(const VideoMetadata& metadata, const AdaptiveConfig& config);
-
- // Override decode with adaptive logic
- bool DecodeFrame(const uint8_t* packet_data, size_t packet_size, VideoFrame& output_frame) override;
-
- // Adaptive quality control
- void SetQualityLevel(QualityLevel level);
- QualityLevel GetCurrentQualityLevel() const { return m_currentQuality; }
-
- // Performance monitoring
- PerformanceMetrics GetPerformanceMetrics() const;
- void UpdatePerformanceMetrics(double decode_time, double render_time);
-
- // Manual override controls
- void EnableAdaptiveMode(bool enable) { m_adaptiveEnabled = enable; }
- void SetTargetFrameRate(double fps);
- void ForceQualityAdjustment(); // Immediate adjustment trigger
-
- // Configuration management
- void UpdateConfig(const AdaptiveConfig& config) { m_config = config; }
- AdaptiveConfig GetConfig() const { return m_config; }
-
-private:
- // Adaptive control state
- QualityLevel m_currentQuality = QualityLevel::ULTRA;
- QualityLevel m_targetQuality = QualityLevel::ULTRA;
- AdaptiveConfig m_config;
-
- // Performance monitoring
- mutable std::mutex m_metricsMutex;
- PerformanceMetrics m_metrics;
- std::queue m_recentDecodeTimes;
- std::queue m_recentRenderTimes;
-
- // Adaptive decision making
- std::atomic m_adaptiveEnabled{true};
- uint32_t m_stableFrameCount = 0;
- bool m_needsReconfiguration = false;
-
- // Original video properties for scaling calculations
- uint32_t m_originalWidth = 0;
- uint32_t m_originalHeight = 0;
-
- // Current scaled properties
- uint32_t m_currentScaledWidth = 0;
- uint32_t m_currentScaledHeight = 0;
-
- // Adaptive logic methods
- void AnalyzePerformanceAndAdjust();
- bool ShouldAdjustQuality(double avgDecodeTime, double avgRenderTime);
- QualityLevel DetermineOptimalQuality(double totalFrameTime);
-
- // NVDEC reconfiguration methods
- bool ReconfigureDecoder(uint32_t newWidth, uint32_t newHeight);
- bool UpdateDecodeSurfaces(uint32_t newCount);
- void CalculateScaledDimensions(QualityLevel quality, uint32_t& width, uint32_t& height);
-
- // Performance calculation helpers
- void UpdateMovingAverage(std::queue& queue, double newValue, size_t maxSize = 30);
- double CalculateMovingAverage(const std::queue& queue) const;
-
- // Quality level utilities
- static double GetQualityScaleFactor(QualityLevel level);
- static std::string QualityLevelToString(QualityLevel level);
-};
-
-// Utility functions for adaptive decoding
-namespace AdaptiveUtils {
- // Performance estimation
- double EstimateOptimalFrameRate(const PerformanceMetrics& metrics);
- QualityLevel RecommendQualityForTargetFPS(double targetFPS, double currentFPS);
-
- // System resource monitoring
- double GetCurrentCPUUsage();
- double GetCurrentGPUUsage();
- size_t GetAvailableGPUMemory();
-
- // Configuration presets
- AdaptiveConfig CreateConfigForTarget(double targetFPS);
- AdaptiveConfig GetConservativeConfig(); // Aggressive quality reduction
- AdaptiveConfig GetBalancedConfig(); // Balanced performance/quality
- AdaptiveConfig GetQualityConfig(); // Prioritize quality over performance
-}
-
-} // namespace Vav2Player
\ No newline at end of file
diff --git a/vav2/platforms/windows/applications/vav2player/Vav2Player/src/Decoder/IVideoDecoder.h b/vav2/platforms/windows/applications/vav2player/Vav2Player/src/Decoder/IVideoDecoder.h
deleted file mode 100644
index ac6e46e..0000000
--- a/vav2/platforms/windows/applications/vav2player/Vav2Player/src/Decoder/IVideoDecoder.h
+++ /dev/null
@@ -1,69 +0,0 @@
-#pragma once
-#include "../Common/VideoTypes.h"
-#include
-
-namespace Vav2Player {
-
-// 비디오 디코더 인터페이스
-// 다양한 코덱(AV1, VP9, H.264 등)에 대한 공통 인터페이스 제공
-class IVideoDecoder {
-public:
- virtual ~IVideoDecoder() = default;
-
- // 초기화 및 해제
- virtual bool Initialize(const VideoMetadata& metadata) = 0;
- virtual void Cleanup() = 0;
- virtual bool IsInitialized() const = 0;
-
- // 디코딩 핵심 기능
- virtual bool DecodeFrame(const VideoPacket& input_packet, VideoFrame& output_frame) = 0;
-
- // 추가 디코딩 옵션 (일부 디코더에서 사용)
- virtual bool DecodeFrame(const uint8_t* packet_data, size_t packet_size, VideoFrame& output_frame) = 0;
-
- // 디코더 상태 관리
- virtual bool Reset() = 0; // 디코더 상태 초기화
- virtual bool Flush() = 0; // 남은 프레임들 출력
-
- // 디코더 정보
- virtual std::string GetCodecName() const = 0;
- virtual VideoCodecType GetCodecType() const = 0;
- virtual std::string GetVersion() const = 0;
-
- // 성능 및 통계 정보
- struct DecoderStats {
- uint64_t frames_decoded = 0;
- uint64_t frames_dropped = 0;
- uint64_t decode_errors = 0;
- double avg_decode_time_ms = 0.0;
- uint64_t bytes_processed = 0;
- };
-
- virtual DecoderStats GetStats() const = 0;
- virtual void ResetStats() = 0;
-
- // 디코더별 특화 설정 (옵션)
- virtual bool SetOption(const std::string& key, const std::string& value) {
- return false; // 기본 구현: 설정 미지원
- }
- virtual std::string GetOption(const std::string& key) const {
- return ""; // 기본 구현: 설정 미지원
- }
-
-protected:
- // 파생 클래스에서 통계 업데이트용
- mutable DecoderStats m_stats{};
-
- void UpdateDecodeTime(double decode_time_ms) {
- m_stats.avg_decode_time_ms =
- (m_stats.avg_decode_time_ms * m_stats.frames_decoded + decode_time_ms) /
- (m_stats.frames_decoded + 1);
- }
-
- void IncrementFramesDecoded() { ++m_stats.frames_decoded; }
- void IncrementFramesDropped() { ++m_stats.frames_dropped; }
- void IncrementDecodeErrors() { ++m_stats.decode_errors; }
- void AddBytesProcessed(size_t bytes) { m_stats.bytes_processed += bytes; }
-};
-
-} // namespace Vav2Player
\ No newline at end of file
diff --git a/vav2/platforms/windows/applications/vav2player/Vav2Player/src/Decoder/MediaFoundationAV1Decoder.cpp b/vav2/platforms/windows/applications/vav2player/Vav2Player/src/Decoder/MediaFoundationAV1Decoder.cpp
deleted file mode 100644
index 02d67ac..0000000
--- a/vav2/platforms/windows/applications/vav2player/Vav2Player/src/Decoder/MediaFoundationAV1Decoder.cpp
+++ /dev/null
@@ -1,651 +0,0 @@
-#include "pch.h"
-#include "MediaFoundationAV1Decoder.h"
-#include
-#include
-#include
-
-
-namespace Vav2Player {
-
-MediaFoundationAV1Decoder::MediaFoundationAV1Decoder()
- : m_initialized(false)
- , m_hardwareAccelerated(false)
- , m_hardwareType("Software")
- , m_mftConfigured(false) {
- LogInfo("MediaFoundationAV1Decoder created");
-}
-
-MediaFoundationAV1Decoder::~MediaFoundationAV1Decoder() {
- Cleanup();
- LogInfo("MediaFoundationAV1Decoder destroyed");
-}
-
-bool MediaFoundationAV1Decoder::Initialize(const VideoMetadata& metadata) {
- LogInfo("Initializing MediaFoundationAV1Decoder...");
-
- if (m_initialized) {
- LogInfo("Decoder already initialized");
- return true;
- }
-
- m_metadata = metadata;
-
- if (!InitializeMediaFoundation()) {
- LogError("Failed to initialize Media Foundation");
- return false;
- }
-
- ComPtr testMFT;
- if (!FindAV1DecoderMFT(&testMFT)) {
- LogError("No AV1 decoder MFT found on this system");
- Cleanup();
- return false;
- }
- testMFT.Reset(); // Release the test MFT
-
- if (m_mfSettings.enable_hardware_acceleration && !CreateD3D11Device()) {
- LogError("Failed to create D3D11 device, falling back to software");
- m_mfSettings.enable_hardware_acceleration = false;
- }
-
- if (m_mfSettings.enable_dxva && m_mfSettings.enable_hardware_acceleration) {
- if (!SetupDXVAAcceleration()) {
- LogError("Failed to setup DXVA acceleration");
- m_mfSettings.enable_dxva = false;
- }
- }
-
- if (!CreateSourceReader()) {
- LogError("Failed to create AV1 decoder MFT");
- Cleanup();
- return false;
- }
-
- if (!ConfigureVideoDecoder()) {
- LogError("Failed to configure video decoder");
- Cleanup();
- return false;
- }
-
- DetectHardwareAcceleration();
-
- m_initialized = true;
- ResetStats();
-
- LogInfo("MediaFoundationAV1Decoder initialized successfully");
- LogInfo("Hardware acceleration: " + std::string(m_hardwareAccelerated ? "Enabled" : "Disabled"));
- if (m_hardwareAccelerated) {
- LogInfo("Hardware type: " + m_hardwareType);
- }
- return true;
-}
-
-void MediaFoundationAV1Decoder::Cleanup() {
- if (!m_initialized) {
- return;
- }
- LogInfo("Cleaning up MediaFoundationAV1Decoder...");
- {
- std::lock_guard lock(m_mftMutex);
- m_mftConfigured = false;
- }
- CleanupMediaFoundation();
- m_initialized = false;
- m_hardwareAccelerated = false;
- m_hardwareType = "Software";
- LogInfo("MediaFoundationAV1Decoder cleanup completed");
-}
-
-bool MediaFoundationAV1Decoder::IsInitialized() const {
- return m_initialized;
-}
-
-bool MediaFoundationAV1Decoder::DecodeFrame(const VideoPacket& input_packet, VideoFrame& output_frame) {
- if (!input_packet.IsValid()) {
- LogError("Invalid input packet");
- return false;
- }
- return DecodeFrame(input_packet.data.get(), input_packet.size, output_frame);
-}
-
-bool MediaFoundationAV1Decoder::DecodeFrame(const uint8_t* packet_data, size_t packet_size, VideoFrame& output_frame) {
- if (!m_initialized) {
- LogError("Decoder not initialized");
- return false;
- }
- if (!packet_data || packet_size == 0) {
- LogError("Invalid packet data");
- return false;
- }
- try {
- if (!m_mftConfigured) {
- std::lock_guard lock(m_mftMutex);
- if (!m_mftConfigured && !ConfigureMFTMediaTypes()) {
- LogError("Failed to configure MFT media types in DecodeFrame");
- return false;
- }
- m_mftConfigured = true;
- }
-
- auto start_time = std::chrono::high_resolution_clock::now();
-
- // Always feed input data to the MFT
- if (!ProcessMFTInput(packet_data, packet_size)) {
- return false; // Error is logged inside
- }
-
- // Try to get output, but "need more input" is normal for MediaFoundation
- bool outputAvailable = ProcessMFTOutput(output_frame);
- if (!outputAvailable) {
- // Could be "need more input" (normal) or actual error
- // For now, we'll assume it's normal and wait for more input
- LogInfo("No output available yet - buffering input data");
- return false; // No output frame available yet
- }
-
- // Update timing and statistics only when frame is successfully output
- auto end_time = std::chrono::high_resolution_clock::now();
- auto duration = std::chrono::duration_cast(end_time - start_time);
- UpdateDecodingStats(duration.count() / 1000.0, packet_size);
-
- output_frame.is_valid = true;
- output_frame.timestamp_seconds = 0.0;
- return true;
- }
- catch (const std::exception& e) {
- LogError("Exception in DecodeFrame: " + std::string(e.what()));
- return false;
- }
-}
-
-bool MediaFoundationAV1Decoder::Reset() {
- if (!m_initialized) {
- return false;
- }
-
- LogInfo("Resetting MediaFoundationAV1Decoder...");
-
- try {
- if (m_decoderMFT) {
- HRESULT hr = m_decoderMFT->ProcessMessage(MFT_MESSAGE_COMMAND_FLUSH, 0);
- if (FAILED(hr)) {
- LogError("Failed to flush MFT", hr);
- return false;
- }
-
- hr = m_decoderMFT->ProcessMessage(MFT_MESSAGE_NOTIFY_BEGIN_STREAMING, 0);
- if (FAILED(hr)) {
- LogError("Failed to send BEGIN_STREAMING message after flush", hr);
- return false;
- }
- }
-
- LogInfo("MediaFoundationAV1Decoder reset completed");
- return true;
- }
- catch (const std::exception& e) {
- LogError("Exception in Reset: " + std::string(e.what()));
- return false;
- }
-}
-
-bool MediaFoundationAV1Decoder::Flush() {
- return Reset();
-}
-
-std::string MediaFoundationAV1Decoder::GetCodecName() const {
- return "Media Foundation AV1 Decoder";
-}
-
-VideoCodecType MediaFoundationAV1Decoder::GetCodecType() const {
- return VideoCodecType::AV1;
-}
-
-std::string MediaFoundationAV1Decoder::GetVersion() const {
- return "1.0.0 (Media Foundation)";
-}
-
-IVideoDecoder::DecoderStats MediaFoundationAV1Decoder::GetStats() const {
- std::lock_guard lock(m_statsMutex);
- return m_stats;
-}
-
-void MediaFoundationAV1Decoder::ResetStats() {
- std::lock_guard lock(m_statsMutex);
- m_stats = DecoderStats{};
-}
-
-bool MediaFoundationAV1Decoder::SetOption(const std::string& key, const std::string& value) { return false; }
-std::string MediaFoundationAV1Decoder::GetOption(const std::string& key) const { return ""; }
-void MediaFoundationAV1Decoder::SetMFSettings(const MFSettings& settings) { m_mfSettings = settings; }
-MediaFoundationAV1Decoder::MFSettings MediaFoundationAV1Decoder::GetMFSettings() const { return m_mfSettings; }
-bool MediaFoundationAV1Decoder::IsHardwareAccelerated() const { return m_hardwareAccelerated; }
-std::string MediaFoundationAV1Decoder::GetHardwareAccelerationType() const { return m_hardwareType; }
-
-bool MediaFoundationAV1Decoder::InitializeMediaFoundation() {
- HRESULT hr = MFStartup(MF_VERSION);
- if (FAILED(hr)) {
- LogError("MFStartup failed", hr);
- return false;
- }
- LogInfo("Media Foundation initialized successfully");
- return true;
-}
-
-void MediaFoundationAV1Decoder::CleanupMediaFoundation() {
- m_decoderMFT.Reset();
- m_deviceManager.Reset();
- m_d3d11Context.Reset();
- m_d3d11Device.Reset();
- m_inputMediaType.Reset();
- m_outputMediaType.Reset();
- MFShutdown();
- LogInfo("Media Foundation cleaned up");
-}
-
-bool MediaFoundationAV1Decoder::CreateD3D11Device() {
- D3D_FEATURE_LEVEL featureLevels[] = { D3D_FEATURE_LEVEL_11_1, D3D_FEATURE_LEVEL_11_0, D3D_FEATURE_LEVEL_10_1, D3D_FEATURE_LEVEL_10_0 };
- UINT createDeviceFlags = 0;
-#ifdef _DEBUG
- createDeviceFlags |= D3D11_CREATE_DEVICE_DEBUG;
-#endif
- D3D_FEATURE_LEVEL featureLevel;
- HRESULT hr = D3D11CreateDevice(nullptr, D3D_DRIVER_TYPE_HARDWARE, nullptr, createDeviceFlags, featureLevels, ARRAYSIZE(featureLevels), D3D11_SDK_VERSION, &m_d3d11Device, &featureLevel, &m_d3d11Context);
- if (FAILED(hr)) {
- LogError("D3D11CreateDevice failed", hr);
- return false;
- }
- LogInfo("D3D11 device created successfully");
- return true;
-}
-
-bool MediaFoundationAV1Decoder::SetupDXVAAcceleration() {
- if (!m_d3d11Device) {
- LogError("D3D11 device not available for DXVA");
- return false;
- }
- UINT resetToken = 0;
- HRESULT hr = MFCreateDXGIDeviceManager(&resetToken, &m_deviceManager);
- if (FAILED(hr)) {
- LogError("MFCreateDXGIDeviceManager failed", hr);
- return false;
- }
- hr = m_deviceManager->ResetDevice(m_d3d11Device.Get(), resetToken);
- if (FAILED(hr)) {
- LogError("ResetDevice failed", hr);
- return false;
- }
- LogInfo("DXVA acceleration setup successfully");
- return true;
-}
-
-bool MediaFoundationAV1Decoder::CreateSourceReader() {
- LogInfo("Creating AV1 decoder MFT directly...");
- ComPtr decoder;
- if (!FindAV1DecoderMFT(&decoder)) {
- LogError("Failed to find AV1 decoder MFT");
- return false;
- }
- m_decoderMFT = decoder;
- if (m_deviceManager && m_mfSettings.enable_dxva) {
- if (!SetupMFTForDXVA()) {
- LogError("Failed to setup MFT for DXVA, continuing without hardware acceleration");
- m_hardwareAccelerated = false;
- }
- }
- LogInfo("AV1 decoder MFT created successfully");
- return true;
-}
-
-bool MediaFoundationAV1Decoder::ConfigureVideoDecoder() {
- if (!m_decoderMFT) {
- LogError("Decoder MFT not available");
- return false;
- }
- if (!ConfigureMFTMediaTypes()) {
- LogError("Failed to configure MFT media types");
- return false;
- }
- HRESULT hr = m_decoderMFT->ProcessMessage(MFT_MESSAGE_NOTIFY_BEGIN_STREAMING, 0);
- if (FAILED(hr)) {
- LogError("Failed to send BEGIN_STREAMING message to MFT", hr);
- return false;
- }
- m_mftConfigured = true;
- LogInfo("Video decoder configured successfully");
- return true;
-}
-
-bool MediaFoundationAV1Decoder::ConvertMFSampleToVideoFrame(IMFSample* sample, VideoFrame& output_frame) {
- if (!sample) return false;
- ComPtr buffer;
- HRESULT hr = sample->ConvertToContiguousBuffer(&buffer);
- if (FAILED(hr)) return false;
- if (!m_outputMediaType) return false;
- UINT32 width = 0, height = 0;
- hr = MFGetAttributeSize(m_outputMediaType.Get(), MF_MT_FRAME_SIZE, &width, &height);
- if (FAILED(hr)) return false;
- GUID subtype = {};
- hr = m_outputMediaType->GetGUID(MF_MT_SUBTYPE, &subtype);
- if (FAILED(hr)) return false;
- output_frame.width = width;
- output_frame.height = height;
- output_frame.color_space = GetColorSpaceFromMFFormat(subtype);
- return CopyVideoFrameData(buffer.Get(), output_frame);
-}
-
-bool MediaFoundationAV1Decoder::CopyVideoFrameData(IMFMediaBuffer* buffer, VideoFrame& frame) {
- BYTE* data = nullptr;
- DWORD currentLength = 0;
- HRESULT hr = buffer->Lock(&data, nullptr, ¤tLength);
- if (FAILED(hr)) return false;
- bool success = false;
- try {
- if (frame.color_space == ColorSpace::YUV420P) {
- size_t y_size = frame.width * frame.height;
- size_t uv_size = y_size / 2;
- frame.y_plane = std::make_unique(y_size);
- frame.y_stride = frame.width;
- frame.y_size = static_cast(y_size);
- memcpy(frame.y_plane.get(), data, y_size);
- frame.u_plane = std::make_unique(uv_size / 2);
- frame.v_plane = std::make_unique(uv_size / 2);
- frame.u_stride = frame.width / 2;
- frame.v_stride = frame.width / 2;
- frame.u_size = static_cast(uv_size / 2);
- frame.v_size = static_cast(uv_size / 2);
- const uint8_t* uv_data = data + y_size;
- for (uint32_t i = 0; i < frame.u_size; i++) {
- frame.u_plane[i] = uv_data[i * 2];
- frame.v_plane[i] = uv_data[i * 2 + 1];
- }
- success = true;
- }
- }
- catch (...) { success = false; }
- buffer->Unlock();
- return success;
-}
-
-ColorSpace MediaFoundationAV1Decoder::GetColorSpaceFromMFFormat(const GUID& subtype) {
- if (subtype == MFVideoFormat_NV12 || subtype == MFVideoFormat_YV12) return ColorSpace::YUV420P;
- if (subtype == MFVideoFormat_YUY2) return ColorSpace::YUV422P;
- if (subtype == MFVideoFormat_AYUV) return ColorSpace::YUV444P;
- return ColorSpace::YUV420P;
-}
-
-bool MediaFoundationAV1Decoder::DetectHardwareAcceleration() {
- if (!m_decoderMFT) return false;
- m_hardwareAccelerated = (m_d3d11Device != nullptr && m_deviceManager != nullptr);
- if (m_hardwareAccelerated) {
- m_hardwareType = GetGPUDescription();
- }
- return true;
-}
-
-std::string MediaFoundationAV1Decoder::GetGPUDescription() {
- if (!m_d3d11Device) return "Software";
- ComPtr dxgiDevice;
- HRESULT hr = m_d3d11Device->QueryInterface(__uuidof(IDXGIDevice), reinterpret_cast(dxgiDevice.GetAddressOf()));
- if (FAILED(hr)) return "Unknown Hardware";
- ComPtr adapter;
- hr = dxgiDevice->GetAdapter(&adapter);
- if (FAILED(hr)) return "Unknown Hardware";
- return MFUtils::GetDXGIAdapterDescription(adapter.Get());
-}
-
-void MediaFoundationAV1Decoder::UpdateDecodingStats(double decode_time_ms, size_t input_bytes) {
- std::lock_guard lock(m_statsMutex);
- m_stats.bytes_processed += input_bytes;
- m_stats.frames_decoded++;
- // Simple moving average filter
- if (m_stats.avg_decode_time_ms == 0) {
- m_stats.avg_decode_time_ms = decode_time_ms;
- } else {
- m_stats.avg_decode_time_ms = m_stats.avg_decode_time_ms * 0.95 + decode_time_ms * 0.05;
- }
-}
-
-std::string MediaFoundationAV1Decoder::GetMFErrorString(HRESULT hr) {
- switch (hr) {
- case MF_E_INVALIDMEDIATYPE: return "Invalid media type";
- case MF_E_INVALIDREQUEST: return "Invalid request";
- case MF_E_INVALIDSTREAMNUMBER: return "Invalid stream number";
- case MF_E_NOTACCEPTING: return "Not accepting data";
- case MF_E_NOT_INITIALIZED: return "Not initialized";
- case MF_E_UNSUPPORTED_REPRESENTATION: return "Unsupported representation";
- default: {
- std::ostringstream oss;
- oss << "HRESULT 0x" << std::hex << hr;
- return oss.str();
- }
- }
-}
-
-void MediaFoundationAV1Decoder::LogError(const std::string& message, HRESULT hr) {
- std::string fullMessage = "[MediaFoundationAV1Decoder] ERROR: " + message;
- if (hr != S_OK) {
- fullMessage += " (" + GetMFErrorString(hr) + ")";
- }
- OutputDebugStringA((fullMessage + "\n").c_str());
-}
-
-void MediaFoundationAV1Decoder::LogInfo(const std::string& message) {
- std::string fullMessage = "[MediaFoundationAV1Decoder] INFO: " + message;
- OutputDebugStringA((fullMessage + "\n").c_str());
-}
-
-namespace MFUtils {
- bool InitializeMediaFoundation() { return SUCCEEDED(MFStartup(MF_VERSION)); }
- void ShutdownMediaFoundation() { MFShutdown(); }
- std::string GetDXGIAdapterDescription(IDXGIAdapter* adapter) {
- if (!adapter) return "Unknown Adapter";
- DXGI_ADAPTER_DESC desc;
- if (FAILED(adapter->GetDesc(&desc))) return "Unknown Adapter";
- char buffer[256];
- size_t convertedChars = 0;
- wcstombs_s(&convertedChars, buffer, sizeof(buffer), desc.Description, _TRUNCATE);
- return std::string(buffer);
- }
-}
-
-bool MediaFoundationAV1Decoder::FindAV1DecoderMFT(IMFTransform** decoder) {
- *decoder = nullptr;
- MFT_REGISTER_TYPE_INFO inputType = { MFMediaType_Video, MFVideoFormat_AV1 };
- IMFActivate** activateArray = nullptr;
- UINT32 numActivate = 0;
- HRESULT hr = MFTEnumEx(MFT_CATEGORY_VIDEO_DECODER, MFT_ENUM_FLAG_ALL, &inputType, nullptr, &activateArray, &numActivate);
- if (FAILED(hr) || numActivate == 0) {
- LogError("No AV1 decoder MFT found", hr);
- return false;
- }
- hr = activateArray[0]->ActivateObject(IID_PPV_ARGS(decoder));
- for (UINT32 i = 0; i < numActivate; i++) { activateArray[i]->Release(); }
- CoTaskMemFree(activateArray);
- if (FAILED(hr)) {
- LogError("Failed to activate AV1 decoder MFT", hr);
- return false;
- }
- LogInfo("AV1 decoder MFT found and activated");
- return true;
-}
-
-bool MediaFoundationAV1Decoder::SetupMFTForDXVA() {
- if (!m_decoderMFT || !m_deviceManager) {
- LogError("SetupMFTForDXVA: MFT or Device Manager not available.");
- return false;
- }
- HRESULT hr = m_decoderMFT->ProcessMessage(MFT_MESSAGE_SET_D3D_MANAGER, reinterpret_cast(m_deviceManager.Get()));
- if (FAILED(hr)) {
- LogError("Failed to set D3D manager on MFT", hr);
- return false;
- }
- LogInfo("DXVA setup for MFT completed");
- return true;
-}
-
-bool MediaFoundationAV1Decoder::ConfigureMFTMediaTypes() {
- if (!m_decoderMFT) { return false; }
- HRESULT hr;
- hr = MFCreateMediaType(&m_inputMediaType);
- if (FAILED(hr)) return false;
- hr = m_inputMediaType->SetGUID(MF_MT_MAJOR_TYPE, MFMediaType_Video);
- if (FAILED(hr)) return false;
- hr = m_inputMediaType->SetGUID(MF_MT_SUBTYPE, MFVideoFormat_AV1);
- if (FAILED(hr)) return false;
- hr = m_decoderMFT->SetInputType(0, m_inputMediaType.Get(), 0);
- if (FAILED(hr)) {
- LogError("Failed to set input type on MFT", hr);
- return false;
- }
- for (DWORD i = 0; ; i++) {
- ComPtr availableType;
- hr = m_decoderMFT->GetOutputAvailableType(0, i, &availableType);
- if (FAILED(hr)) {
- if (hr == MF_E_NO_MORE_TYPES) { LogError("MFT does not support a suitable output type (NV12)."); }
- break;
- }
- GUID subtype = {};
- availableType->GetGUID(MF_MT_SUBTYPE, &subtype);
- if (subtype == MFVideoFormat_NV12) {
- m_outputMediaType = availableType;
- break;
- }
- }
- if (!m_outputMediaType) {
- LogError("Failed to find a suitable output media type.");
- return false;
- }
- if (m_metadata.width > 0 && m_metadata.height > 0) {
- hr = MFSetAttributeSize(m_outputMediaType.Get(), MF_MT_FRAME_SIZE, m_metadata.width, m_metadata.height);
- if (FAILED(hr)) {
- LogError("Failed to set frame size on output type", hr);
- return false;
- }
- }
- hr = m_decoderMFT->SetOutputType(0, m_outputMediaType.Get(), 0);
- if (FAILED(hr)) {
- LogError("Failed to set output type on MFT after negotiation", hr);
- return false;
- }
- LogInfo("MFT media types configured successfully after negotiation");
- return true;
-}
-
-bool MediaFoundationAV1Decoder::ProcessMFTInput(const uint8_t* packet_data, size_t packet_size) {
- HRESULT hr;
- ComPtr inputSample;
- ComPtr inputBuffer;
- hr = MFCreateSample(&inputSample);
- if (FAILED(hr)) return false;
- hr = MFCreateMemoryBuffer(static_cast(packet_size), &inputBuffer);
- if (FAILED(hr)) return false;
- hr = inputSample->AddBuffer(inputBuffer.Get());
- if (FAILED(hr)) return false;
- BYTE* bufferData = nullptr;
- hr = inputBuffer->Lock(&bufferData, nullptr, nullptr);
- if (FAILED(hr)) return false;
- memcpy(bufferData, packet_data, packet_size);
- inputBuffer->Unlock();
- hr = inputBuffer->SetCurrentLength(static_cast(packet_size));
- if (FAILED(hr)) return false;
- hr = inputSample->SetUINT32(MFSampleExtension_CleanPoint, TRUE);
- if (FAILED(hr)) {
- LogError("Failed to set CleanPoint attribute", hr);
- return false;
- }
- hr = inputSample->SetSampleTime(0);
- if (FAILED(hr)) {
- LogError("Failed to set sample time", hr);
- return false;
- }
- hr = m_decoderMFT->ProcessInput(0, inputSample.Get(), 0);
- if (FAILED(hr)) {
- LogError("ProcessInput failed", hr);
- return false;
- }
- return true;
-}
-
-bool MediaFoundationAV1Decoder::ProcessMFTOutput(VideoFrame& output_frame) {
- if (!m_decoderMFT) {
- LogError("ProcessMFTOutput: m_decoderMFT is null");
- return false;
- }
-
- // Check MFT output stream info
- DWORD outputStreamId = 0;
- MFT_OUTPUT_STREAM_INFO outputStreamInfo = {};
- HRESULT hr = m_decoderMFT->GetOutputStreamInfo(outputStreamId, &outputStreamInfo);
- if (FAILED(hr)) {
- LogError("GetOutputStreamInfo failed", hr);
- return false;
- }
-
- LogInfo("MFT Output Stream Info - Flags: " + std::to_string(outputStreamInfo.dwFlags) +
- ", Size: " + std::to_string(outputStreamInfo.cbSize) +
- ", Alignment: " + std::to_string(outputStreamInfo.cbAlignment));
-
- MFT_OUTPUT_DATA_BUFFER outputDataBuffer = {};
- outputDataBuffer.dwStreamID = 0;
- outputDataBuffer.pEvents = nullptr;
-
- // Check if we need to provide the output sample
- if (outputStreamInfo.dwFlags & MFT_OUTPUT_STREAM_PROVIDES_SAMPLES) {
- LogInfo("MFT provides output samples");
- outputDataBuffer.pSample = nullptr;
- } else {
- LogInfo("Client must provide output sample");
-
- // Create output sample
- ComPtr outputSample;
- hr = MFCreateSample(&outputSample);
- if (FAILED(hr)) {
- LogError("Failed to create output sample", hr);
- return false;
- }
-
- // Create output media buffer
- UINT32 bufferSize = outputStreamInfo.cbSize > 0 ? outputStreamInfo.cbSize : (3840 * 2160 * 4);
- ComPtr outputBuffer;
- hr = MFCreateMemoryBuffer(bufferSize, &outputBuffer);
- if (FAILED(hr)) {
- LogError("Failed to create output buffer", hr);
- return false;
- }
-
- hr = outputSample->AddBuffer(outputBuffer.Get());
- if (FAILED(hr)) {
- LogError("Failed to add buffer to sample", hr);
- return false;
- }
-
- outputDataBuffer.pSample = outputSample.Get();
- }
-
- DWORD status = 0;
- hr = m_decoderMFT->ProcessOutput(0, 1, &outputDataBuffer, &status);
- if (hr == MF_E_TRANSFORM_NEED_MORE_INPUT) {
- LogInfo("ProcessOutput: Need more input (normal for MediaFoundation)");
- return false; // Normal case - need more input packets
- }
- if (FAILED(hr)) {
- LogError("ProcessOutput failed", hr);
- return false;
- }
- if (!outputDataBuffer.pSample) {
- LogError("ProcessOutput: No output sample returned");
- return false;
- }
-
- bool result = ConvertMFSampleToVideoFrame(outputDataBuffer.pSample, output_frame);
-
- // Clean up
- if (outputDataBuffer.pEvents) {
- outputDataBuffer.pEvents->Release();
- }
-
- return result;
-}
-
-} // namespace Vav2Player
\ No newline at end of file
diff --git a/vav2/platforms/windows/applications/vav2player/Vav2Player/src/Decoder/MediaFoundationAV1Decoder.h b/vav2/platforms/windows/applications/vav2player/Vav2Player/src/Decoder/MediaFoundationAV1Decoder.h
deleted file mode 100644
index e5b8a5e..0000000
--- a/vav2/platforms/windows/applications/vav2player/Vav2Player/src/Decoder/MediaFoundationAV1Decoder.h
+++ /dev/null
@@ -1,166 +0,0 @@
-#pragma once
-#include "IVideoDecoder.h"
-// Simplified architecture - removed FramePool
-#include
-#include
-#include
-
-// Media Foundation headers
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-
-// Additional headers for Media Foundation Transform
-#include
-
-// AV1 format GUID declaration (supported in Windows 10 2004+)
-// AV01: {31304156-0000-0010-8000-00AA00389B71}
-extern const GUID MFVideoFormat_AV01;
-
-#pragma comment(lib, "mfplat.lib")
-#pragma comment(lib, "mf.lib")
-#pragma comment(lib, "mfreadwrite.lib")
-#pragma comment(lib, "mfuuid.lib")
-#pragma comment(lib, "dxgi.lib")
-#pragma comment(lib, "d3d11.lib")
-
-using Microsoft::WRL::ComPtr;
-
-namespace Vav2Player {
-
-// Media Foundation-based AV1 hardware accelerated decoder
-// Performs hardware accelerated decoding using Source Reader
-class MediaFoundationAV1Decoder : public IVideoDecoder {
-public:
- MediaFoundationAV1Decoder();
- ~MediaFoundationAV1Decoder() override;
-
- // Prevent copying
- MediaFoundationAV1Decoder(const MediaFoundationAV1Decoder&) = delete;
- MediaFoundationAV1Decoder& operator=(const MediaFoundationAV1Decoder&) = delete;
-
- // IVideoDecoder interface implementation
- bool Initialize(const VideoMetadata& metadata) override;
- void Cleanup() override;
- bool IsInitialized() const override;
-
- bool DecodeFrame(const VideoPacket& input_packet, VideoFrame& output_frame) override;
- bool DecodeFrame(const uint8_t* packet_data, size_t packet_size, VideoFrame& output_frame) override;
-
- bool Reset() override;
- bool Flush() override;
-
- std::string GetCodecName() const override;
- VideoCodecType GetCodecType() const override;
- std::string GetVersion() const override;
-
- DecoderStats GetStats() const override;
- void ResetStats() override;
-
- // Media Foundation specific options
- bool SetOption(const std::string& key, const std::string& value) override;
- std::string GetOption(const std::string& key) const override;
-
- // Media Foundation exclusive methods
- struct MFSettings {
- bool enable_hardware_acceleration = true; // Enable hardware acceleration
- bool enable_dxva = true; // Use DXVA 2.0/D3D11VA
- bool enable_low_latency = true; // Low latency mode
- uint32_t max_buffer_size = 16; // Maximum buffer size (number of frames)
- };
-
- void SetMFSettings(const MFSettings& settings);
- MFSettings GetMFSettings() const;
-
- // Check hardware acceleration status
- bool IsHardwareAccelerated() const;
- std::string GetHardwareAccelerationType() const;
-
-private:
- // Media Foundation related members
- ComPtr m_decoderMFT;
- ComPtr m_deviceManager;
- ComPtr m_d3d11Device;
- ComPtr m_d3d11Context;
- ComPtr m_inputMediaType;
- ComPtr m_outputMediaType;
-
- // Settings and state
- MFSettings m_mfSettings;
- bool m_initialized;
- bool m_hardwareAccelerated;
- std::string m_hardwareType;
- VideoMetadata m_metadata;
-
- // MFT processing
- std::mutex m_mftMutex;
- bool m_mftConfigured = false;
-
- // Members for performance measurement
- std::chrono::high_resolution_clock::time_point m_decode_start_time;
- mutable std::mutex m_statsMutex;
-
- // Internal helper methods
- bool InitializeMediaFoundation();
- void CleanupMediaFoundation();
- bool CreateD3D11Device();
- bool SetupDXVAAcceleration();
- bool CreateSourceReader();
- bool ConfigureVideoDecoder();
-
- // MFT related methods
- bool FindAV1DecoderMFT(IMFTransform** decoder);
- bool SetupMFTForDXVA();
- bool ConfigureMFTMediaTypes();
- bool ProcessMFTInput(const uint8_t* packet_data, size_t packet_size);
- bool ProcessMFTOutput(VideoFrame& output_frame);
-
- // Frame conversion
- bool ConvertMFSampleToVideoFrame(IMFSample* sample, VideoFrame& output_frame);
- bool CopyVideoFrameData(IMFMediaBuffer* buffer, VideoFrame& frame);
- ColorSpace GetColorSpaceFromMFFormat(const GUID& subtype);
-
- // Hardware acceleration detection
- bool DetectHardwareAcceleration();
- std::string GetGPUDescription();
-
- // Statistics update
- void UpdateDecodingStats(double decode_time_ms, size_t input_bytes);
-
- // Error handling
- std::string GetMFErrorString(HRESULT hr);
- void LogError(const std::string& message, HRESULT hr = S_OK);
- void LogInfo(const std::string& message);
-};
-
-// Media Foundation related utility functions
-namespace MFUtils {
- // Media Foundation initialization/cleanup
- bool InitializeMediaFoundation();
- void ShutdownMediaFoundation();
-
- // GUID conversion utility
- std::string GUIDToString(const GUID& guid);
- bool StringToGUID(const std::string& str, GUID& guid);
-
- // Media type utility
- std::string GetMediaTypeDescription(IMFMediaType* mediaType);
- bool IsAV1MediaType(IMFMediaType* mediaType);
-
- // Hardware detection
- std::vector EnumerateVideoDecoders();
- bool IsHardwareDecoderAvailable(const std::string& codecName);
-
- // D3D11 utility
- std::string GetD3D11FeatureLevel(ID3D11Device* device);
- std::string GetDXGIAdapterDescription(IDXGIAdapter* adapter);
-}
-
-} // namespace Vav2Player
\ No newline at end of file
diff --git a/vav2/platforms/windows/applications/vav2player/Vav2Player/src/Decoder/NVDECAV1Decoder.cpp b/vav2/platforms/windows/applications/vav2player/Vav2Player/src/Decoder/NVDECAV1Decoder.cpp
deleted file mode 100644
index 96c2085..0000000
--- a/vav2/platforms/windows/applications/vav2player/Vav2Player/src/Decoder/NVDECAV1Decoder.cpp
+++ /dev/null
@@ -1,366 +0,0 @@
-#include "pch.h"
-// Include NVDEC decoder header with TIMECODE protection
-#include "NVDECAV1Decoder.h"
-#include
-#include
-#include
-
-namespace Vav2Player {
-
-NVDECAV1Decoder::NVDECAV1Decoder()
- : m_initialized(false) {
-}
-
-NVDECAV1Decoder::~NVDECAV1Decoder() {
- Cleanup();
-}
-
-bool NVDECAV1Decoder::Initialize(const VideoMetadata& metadata) {
- if (m_initialized) {
- LogError("Decoder already initialized");
- return false;
- }
-
- if (metadata.codec_type != VideoCodecType::AV1) {
- LogError("Invalid codec type for NVDEC AV1 decoder");
- return false;
- }
-
- // Check NVDEC availability
- if (!IsNVDECAvailable()) {
- LogError("NVDEC not available on this system");
- return false;
- }
-
- // Initialize CUDA context
- if (!InitializeCUDA()) {
- LogError("Failed to initialize CUDA");
- return false;
- }
-
- // Store video properties
- m_width = metadata.width;
- m_height = metadata.height;
- m_maxWidth = std::max(m_width, 4096u);
- m_maxHeight = std::max(m_height, 4096u);
-
- // Create decoder
- if (!CreateDecoder()) {
- LogError("Failed to create NVDEC decoder");
- Cleanup();
- return false;
- }
-
- // Create parser
- if (!CreateParser()) {
- LogError("Failed to create NVDEC parser");
- Cleanup();
- return false;
- }
-
- m_initialized = true;
-
- std::cout << "[NVDECAV1Decoder] Initialized successfully" << std::endl;
- std::cout << " Resolution: " << m_width << "x" << m_height << std::endl;
- std::cout << " Max Resolution: " << m_maxWidth << "x" << m_maxHeight << std::endl;
-
- return true;
-}
-
-void NVDECAV1Decoder::Cleanup() {
- if (m_parser) {
- cuvidDestroyVideoParser(m_parser);
- m_parser = nullptr;
- }
-
- if (m_decoder) {
- cuvidDestroyDecoder(m_decoder);
- m_decoder = nullptr;
- }
-
- CleanupCUDA();
- m_initialized = false;
-}
-
-bool NVDECAV1Decoder::IsInitialized() const {
- return m_initialized;
-}
-
-bool NVDECAV1Decoder::DecodeFrame(const VideoPacket& input_packet, VideoFrame& output_frame) {
- if (!input_packet.IsValid()) {
- LogError("Invalid input packet");
- return false;
- }
-
- return DecodeFrame(input_packet.data.get(), input_packet.size, output_frame);
-}
-
-bool NVDECAV1Decoder::DecodeFrame(const uint8_t* packet_data, size_t packet_size, VideoFrame& output_frame) {
- if (!m_initialized || !packet_data || packet_size == 0) {
- LogError("Invalid parameters or decoder not initialized");
- return false;
- }
-
- auto decode_start = std::chrono::high_resolution_clock::now();
-
- // Prepare packet for parser
- CUVIDSOURCEDATAPACKET packet = {};
- packet.payload_size = static_cast(packet_size);
- packet.payload = packet_data;
- packet.flags = 0;
-
- // Parse packet
- CUresult result = cuvidParseVideoData(m_parser, &packet);
- if (result != CUDA_SUCCESS) {
- LogCUDAError(result, "cuvidParseVideoData");
- m_decodeErrors++;
- return false;
- }
-
- // For GUI mode, we can copy pixel data to the VideoFrame
- // TODO: Implement actual frame data copying when needed
- output_frame.width = m_width;
- output_frame.height = m_height;
- output_frame.format = PixelFormat::YUV420P;
-
- // Update statistics
- auto decode_end = std::chrono::high_resolution_clock::now();
- double decode_time = std::chrono::duration(decode_end - decode_start).count();
-
- m_framesDecoded++;
- m_bytesProcessed += packet_size;
-
- // Update average decode time
- m_avgDecodeTime = (m_avgDecodeTime * (m_framesDecoded - 1) + decode_time) / m_framesDecoded;
-
- return true;
-}
-
-bool NVDECAV1Decoder::Reset() {
- if (!m_initialized) {
- return false;
- }
-
- // Reset statistics
- ResetStats();
- return true;
-}
-
-bool NVDECAV1Decoder::Flush() {
- if (!m_initialized) {
- return false;
- }
-
- // Send end-of-stream packet to flush any remaining frames
- CUVIDSOURCEDATAPACKET packet = {};
- packet.flags = CUVID_PKT_ENDOFSTREAM;
-
- CUresult result = cuvidParseVideoData(m_parser, &packet);
- return (result == CUDA_SUCCESS);
-}
-
-std::string NVDECAV1Decoder::GetVersion() const {
- int driver_version = 0;
- cuDriverGetVersion(&driver_version);
-
- return "NVDEC AV1 (CUDA Driver: " + std::to_string(driver_version) + ")";
-}
-
-bool NVDECAV1Decoder::IsNVDECAvailable() const {
- // Check if CUDA driver is available
- if (cuInit(0) != CUDA_SUCCESS) {
- return false;
- }
-
- // Check device count
- int device_count = 0;
- if (cuDeviceGetCount(&device_count) != CUDA_SUCCESS || device_count == 0) {
- return false;
- }
-
- // Check decode capabilities for AV1
- CUdevice device;
- if (cuDeviceGet(&device, 0) != CUDA_SUCCESS) {
- return false;
- }
-
- CUVIDDECODECAPS decode_caps = {};
- decode_caps.eCodecType = cudaVideoCodec_AV1;
- decode_caps.eChromaFormat = cudaVideoChromaFormat_420;
- decode_caps.nBitDepthMinus8 = 0;
-
- if (cuvidGetDecoderCaps(&decode_caps) != CUDA_SUCCESS) {
- return false;
- }
-
- return decode_caps.bIsSupported != 0;
-}
-
-bool NVDECAV1Decoder::InitializeCUDA() {
- // Initialize CUDA driver
- CUresult result = cuInit(0);
- if (result != CUDA_SUCCESS) {
- LogCUDAError(result, "cuInit");
- return false;
- }
-
- // Get device
- CUdevice device;
- result = cuDeviceGet(&device, 0);
- if (result != CUDA_SUCCESS) {
- LogCUDAError(result, "cuDeviceGet");
- return false;
- }
-
- // Create context - use correct API signature for CUDA 13.0
- CUctxCreateParams createParams = {};
- createParams.execAffinityParams = nullptr;
- result = cuCtxCreate_v4(&m_cuContext, &createParams, 0, device);
- if (result != CUDA_SUCCESS) {
- LogCUDAError(result, "cuCtxCreate");
- return false;
- }
-
- // Create stream
- result = cuStreamCreate(&m_stream, CU_STREAM_DEFAULT);
- if (result != CUDA_SUCCESS) {
- LogCUDAError(result, "cuStreamCreate");
- return false;
- }
-
- return CheckCUDACapability();
-}
-
-bool NVDECAV1Decoder::CheckCUDACapability() {
- // Get device properties
- int major, minor;
- CUresult result = cuDeviceGetAttribute(&major, CU_DEVICE_ATTRIBUTE_COMPUTE_CAPABILITY_MAJOR, 0);
- if (result != CUDA_SUCCESS) {
- LogCUDAError(result, "cuDeviceGetAttribute");
- return false;
- }
-
- result = cuDeviceGetAttribute(&minor, CU_DEVICE_ATTRIBUTE_COMPUTE_CAPABILITY_MINOR, 0);
- if (result != CUDA_SUCCESS) {
- LogCUDAError(result, "cuDeviceGetAttribute");
- return false;
- }
-
- std::cout << "[NVDECAV1Decoder] CUDA Compute Capability: " << major << "." << minor << std::endl;
-
- // NVDEC requires compute capability 3.0 or higher
- return (major >= 3);
-}
-
-bool NVDECAV1Decoder::CreateDecoder() {
- memset(&m_createInfo, 0, sizeof(m_createInfo));
-
- m_createInfo.CodecType = cudaVideoCodec_AV1;
- m_createInfo.ChromaFormat = cudaVideoChromaFormat_420;
- m_createInfo.OutputFormat = cudaVideoSurfaceFormat_NV12;
- m_createInfo.bitDepthMinus8 = 0;
- m_createInfo.DeinterlaceMode = cudaVideoDeinterlaceMode_Weave;
- m_createInfo.ulNumOutputSurfaces = 8;
- m_createInfo.ulCreationFlags = cudaVideoCreate_PreferCUVID;
- m_createInfo.ulNumDecodeSurfaces = 8;
- m_createInfo.vidLock = nullptr;
- m_createInfo.ulWidth = m_width;
- m_createInfo.ulHeight = m_height;
- m_createInfo.ulMaxWidth = m_maxWidth;
- m_createInfo.ulMaxHeight = m_maxHeight;
- m_createInfo.ulTargetWidth = m_width;
- m_createInfo.ulTargetHeight = m_height;
-
- CUresult result = cuvidCreateDecoder(&m_decoder, &m_createInfo);
- if (result != CUDA_SUCCESS) {
- LogCUDAError(result, "cuvidCreateDecoder");
- return false;
- }
-
- return true;
-}
-
-bool NVDECAV1Decoder::CreateParser() {
- memset(&m_parserParams, 0, sizeof(m_parserParams));
-
- m_parserParams.CodecType = cudaVideoCodec_AV1;
- m_parserParams.ulMaxNumDecodeSurfaces = 8;
- m_parserParams.ulClockRate = 0; // Use default
- m_parserParams.ulErrorThreshold = 100;
- m_parserParams.pUserData = this;
- m_parserParams.pfnSequenceCallback = HandleVideoSequence;
- m_parserParams.pfnDecodePicture = HandlePictureDecode;
- m_parserParams.pfnDisplayPicture = HandlePictureDisplay;
-
- CUresult result = cuvidCreateVideoParser(&m_parser, &m_parserParams);
- if (result != CUDA_SUCCESS) {
- LogCUDAError(result, "cuvidCreateVideoParser");
- return false;
- }
-
- return true;
-}
-
-void NVDECAV1Decoder::CleanupCUDA() {
- if (m_stream) {
- cuStreamDestroy(m_stream);
- m_stream = nullptr;
- }
-
- if (m_cuContext) {
- cuCtxDestroy(m_cuContext);
- m_cuContext = nullptr;
- }
-}
-
-// NVDEC Callbacks
-int CUDAAPI NVDECAV1Decoder::HandleVideoSequence(void* user_data, CUVIDEOFORMAT* format) {
- auto* decoder = static_cast(user_data);
- if (!decoder || !format) {
- return 0;
- }
-
- std::cout << "[NVDECAV1Decoder] Sequence: " << format->coded_width << "x" << format->coded_height
- << " ChromaFormat:" << format->chroma_format << " BitDepth:" << format->bit_depth_luma_minus8 + 8 << std::endl;
-
- return 1; // Success
-}
-
-int CUDAAPI NVDECAV1Decoder::HandlePictureDecode(void* user_data, CUVIDPICPARAMS* pic_params) {
- auto* decoder = static_cast(user_data);
- if (!decoder || !pic_params) {
- return 0;
- }
-
- CUresult result = cuvidDecodePicture(decoder->m_decoder, pic_params);
- if (result != CUDA_SUCCESS) {
- decoder->LogCUDAError(result, "cuvidDecodePicture");
- return 0;
- }
-
- return 1; // Success
-}
-
-int CUDAAPI NVDECAV1Decoder::HandlePictureDisplay(void* user_data, CUVIDPARSERDISPINFO* disp_info) {
- auto* decoder = static_cast(user_data);
- if (!decoder || !disp_info) {
- return 0;
- }
-
- // For GUI mode, we can implement actual frame data copying here
- return 1;
-}
-
-void NVDECAV1Decoder::LogError(const std::string& message) const {
- std::cerr << "[NVDECAV1Decoder] ERROR: " << message << std::endl;
-}
-
-void NVDECAV1Decoder::LogCUDAError(CUresult result, const std::string& operation) const {
- const char* error_string = nullptr;
- cuGetErrorString(result, &error_string);
- std::cerr << "[NVDECAV1Decoder] CUDA ERROR in " << operation << ": "
- << (error_string ? error_string : "Unknown error")
- << " (code: " << result << ")" << std::endl;
-}
-
-} // namespace Vav2Player
\ No newline at end of file
diff --git a/vav2/platforms/windows/applications/vav2player/Vav2Player/src/Decoder/NVDECAV1Decoder.h b/vav2/platforms/windows/applications/vav2player/Vav2Player/src/Decoder/NVDECAV1Decoder.h
deleted file mode 100644
index b8f0b1c..0000000
--- a/vav2/platforms/windows/applications/vav2player/Vav2Player/src/Decoder/NVDECAV1Decoder.h
+++ /dev/null
@@ -1,110 +0,0 @@
-#pragma once
-#include "IVideoDecoder.h"
-#include
-#include
-
-// Prevent TIMECODE conflicts by defining it before Windows headers
-#define WIN32_LEAN_AND_MEAN
-#define NOMINMAX
-
-// Prevent specific Windows header conflicts
-#define TIMECODE TIMECODE_WIN32
-#include
-#include
-#include
-#undef TIMECODE
-
-namespace Vav2Player {
-
-// NVIDIA NVDEC-based AV1 decoder for hardware acceleration
-class NVDECAV1Decoder : public IVideoDecoder {
-public:
- NVDECAV1Decoder();
- ~NVDECAV1Decoder() override;
-
- // Prevent copying
- NVDECAV1Decoder(const NVDECAV1Decoder&) = delete;
- NVDECAV1Decoder& operator=(const NVDECAV1Decoder&) = delete;
-
- // IVideoDecoder interface implementation
- bool Initialize(const VideoMetadata& metadata) override;
- void Cleanup() override;
- bool IsInitialized() const override;
-
- bool DecodeFrame(const VideoPacket& input_packet, VideoFrame& output_frame) override;
- bool DecodeFrame(const uint8_t* packet_data, size_t packet_size, VideoFrame& output_frame) override;
-
- bool Reset() override;
- bool Flush() override;
-
- // IVideoDecoder interface - additional methods
- std::string GetCodecName() const override { return "AV1 (NVDEC)"; }
- VideoCodecType GetCodecType() const override { return VideoCodecType::AV1; }
- std::string GetVersion() const override;
-
- DecoderStats GetStats() const override {
- DecoderStats stats;
- stats.frames_decoded = m_framesDecoded;
- stats.decode_errors = m_decodeErrors;
- stats.avg_decode_time_ms = m_avgDecodeTime;
- stats.bytes_processed = m_bytesProcessed;
- return stats;
- }
-
- void ResetStats() override {
- m_framesDecoded = 0;
- m_decodeErrors = 0;
- m_avgDecodeTime = 0.0;
- m_bytesProcessed = 0;
- }
-
- // NVDEC-specific methods
- bool IsNVDECAvailable() const;
- bool InitializeCUDA();
-
-protected:
- // Protected members for inheritance (AdaptiveNVDECDecoder)
- CUvideodecoder m_decoder = nullptr;
- CUVIDDECODECREATEINFO m_createInfo = {};
- uint32_t m_width = 0;
- uint32_t m_height = 0;
- uint32_t m_maxWidth = 4096;
- uint32_t m_maxHeight = 4096;
-
- // Protected helper methods
- void LogCUDAError(CUresult result, const std::string& operation) const;
-
-private:
- // CUDA and NVDEC objects
- CUcontext m_cuContext = nullptr;
- CUvideoparser m_parser = nullptr;
- CUstream m_stream = nullptr;
-
- // Decoder configuration
- CUVIDPARSERPARAMS m_parserParams = {};
-
- // Statistics
- uint64_t m_framesDecoded = 0;
- uint64_t m_decodeErrors = 0;
- double m_avgDecodeTime = 0.0;
- uint64_t m_bytesProcessed = 0;
-
- // State
- bool m_initialized = false;
-
- // Helper methods
- bool CheckCUDACapability();
- bool CreateDecoder();
- bool CreateParser();
- void CleanupCUDA();
-
- // NVDEC callbacks
- static int CUDAAPI HandleVideoSequence(void* user_data, CUVIDEOFORMAT* format);
- static int CUDAAPI HandlePictureDecode(void* user_data, CUVIDPICPARAMS* pic_params);
- static int CUDAAPI HandlePictureDisplay(void* user_data, CUVIDPARSERDISPINFO* disp_info);
-
- // Error handling
- void LogError(const std::string& message) const;
-};
-
-} // namespace Vav2Player
\ No newline at end of file
diff --git a/vav2/platforms/windows/applications/vav2player/Vav2Player/src/Decoder/VideoDecoderFactory.cpp b/vav2/platforms/windows/applications/vav2player/Vav2Player/src/Decoder/VideoDecoderFactory.cpp
deleted file mode 100644
index d335d0f..0000000
--- a/vav2/platforms/windows/applications/vav2player/Vav2Player/src/Decoder/VideoDecoderFactory.cpp
+++ /dev/null
@@ -1,397 +0,0 @@
-#include "pch.h"
-#include "VideoDecoderFactory.h"
-#include "AV1Decoder.h"
-#include "AdaptiveAV1Decoder.h"
-#include "MediaFoundationAV1Decoder.h"
-// #include "VP9Decoder.h" // TODO: activate when VP9 implemented
-
-#include
-
-// Include NVDEC header (TIMECODE conflicts handled in NVDECAV1Decoder.h)
-#include "NVDECAV1Decoder.h"
-#include "AdaptiveNVDECDecoder.h"
-
-namespace Vav2Player {
-
-// Static member initialization
-bool VideoDecoderFactory::s_av1_available = false;
-bool VideoDecoderFactory::s_vp9_available = false;
-bool VideoDecoderFactory::s_media_foundation_available = false;
-bool VideoDecoderFactory::s_nvdec_available = false;
-bool VideoDecoderFactory::s_factory_initialized = false;
-
-
-std::unique_ptr VideoDecoderFactory::CreateDecoder(VideoCodecType codec_type, DecoderType decoder_type) {
- if (!s_factory_initialized) {
- InitializeFactory();
- }
-
- switch (codec_type) {
- case VideoCodecType::AV1:
- return CreateAV1Decoder(decoder_type);
-
- case VideoCodecType::VP9:
- // TODO: activate when VP9 implemented
- // if (s_vp9_available) {
- // return std::make_unique();
- // }
- break;
-
- default:
- break;
- }
-
- return nullptr;
-}
-
-std::unique_ptr VideoDecoderFactory::CreateAV1Decoder(DecoderType decoder_type) {
- switch (decoder_type) {
- case DecoderType::ADAPTIVE_NVDEC:
- if (s_nvdec_available) {
- OutputDebugStringA("[VideoDecoderFactory] Creating Adaptive NVDEC AV1 decoder\n");
- return std::make_unique();
- }
- OutputDebugStringA("[VideoDecoderFactory] NVDEC not available, falling back to regular NVDEC\n");
- [[fallthrough]];
-
- case DecoderType::NVDEC:
- if (s_nvdec_available) {
- OutputDebugStringA("[VideoDecoderFactory] Creating NVDEC AV1 decoder\n");
- return std::make_unique();
- }
- OutputDebugStringA("[VideoDecoderFactory] NVDEC not available, falling back to Adaptive dav1d\n");
- [[fallthrough]];
-
- case DecoderType::ADAPTIVE_DAV1D:
- if (s_av1_available) {
- OutputDebugStringA("[VideoDecoderFactory] Creating Adaptive dav1d AV1 decoder\n");
- return std::make_unique();
- }
- OutputDebugStringA("[VideoDecoderFactory] dav1d not available, falling back to regular dav1d\n");
- [[fallthrough]];
-
- case DecoderType::DAV1D:
- if (s_av1_available) {
- OutputDebugStringA("[VideoDecoderFactory] Creating dav1d AV1 decoder\n");
- return std::make_unique();
- }
- OutputDebugStringA("[VideoDecoderFactory] dav1d not available, falling back to MediaFoundation\n");
- [[fallthrough]];
-
- case DecoderType::MEDIA_FOUNDATION:
- if (s_media_foundation_available) {
- OutputDebugStringA("[VideoDecoderFactory] Creating MediaFoundation AV1 decoder\n");
- return std::make_unique();
- }
- break;
-
- case DecoderType::AUTO:
- // Try ADAPTIVE_NVDEC first (best user experience), then ADAPTIVE_DAV1D, then NVDEC, then dav1d, finally MediaFoundation
- if (s_nvdec_available) {
- OutputDebugStringA("[VideoDecoderFactory] Auto mode: trying Adaptive NVDEC AV1 decoder first\n");
- auto decoder = std::make_unique();
- if (decoder) {
- return decoder;
- }
- }
-
- if (s_av1_available) {
- OutputDebugStringA("[VideoDecoderFactory] Auto mode: trying Adaptive dav1d AV1 decoder\n");
- auto decoder = std::make_unique();
- if (decoder) {
- return decoder;
- }
- }
-
- if (s_nvdec_available) {
- OutputDebugStringA("[VideoDecoderFactory] Auto mode: trying regular NVDEC AV1 decoder\n");
- auto decoder = std::make_unique();
- if (decoder) {
- return decoder;
- }
- }
-
- if (s_av1_available) {
- OutputDebugStringA("[VideoDecoderFactory] Auto mode: trying regular dav1d AV1 decoder\n");
- auto decoder = std::make_unique();
- if (decoder) {
- return decoder;
- }
- }
-
- // Fallback to MediaFoundation as last resort
- if (s_media_foundation_available) {
- OutputDebugStringA("[VideoDecoderFactory] Auto mode: falling back to MediaFoundation AV1 decoder\n");
- return std::make_unique();
- }
- break;
- }
-
- return nullptr;
-}
-
-std::unique_ptr VideoDecoderFactory::CreateDecoderFromCodecId(const std::string& codec_id, DecoderType decoder_type) {
- VideoCodecType codec_type = DetectCodecTypeFromId(codec_id);
- return CreateDecoder(codec_type, decoder_type);
-}
-
-VideoCodecType VideoDecoderFactory::DetectCodecTypeFromId(const std::string& codec_id) {
- if (codec_id == DecoderUtils::CodecIds::AV1) return VideoCodecType::AV1;
- if (codec_id == DecoderUtils::CodecIds::VP9) return VideoCodecType::VP9;
- if (codec_id == DecoderUtils::CodecIds::VP8) return VideoCodecType::VP8;
- if (codec_id == DecoderUtils::CodecIds::H264) return VideoCodecType::H264;
- if (codec_id == DecoderUtils::CodecIds::H265) return VideoCodecType::H265;
-
- return VideoCodecType::AV1; // Default value
-}
-
-std::vector VideoDecoderFactory::GetSupportedDecoders() {
- if (!s_factory_initialized) {
- InitializeFactory();
- }
-
- std::vector decoders;
-
- // AV1 dav1d decoder
- decoders.push_back({
- VideoCodecType::AV1,
- DecoderType::DAV1D,
- "AV1 (dav1d)",
- "AV1 video decoder using dav1d library",
- s_av1_available
- });
-
- // AV1 MediaFoundation decoder
- decoders.push_back({
- VideoCodecType::AV1,
- DecoderType::MEDIA_FOUNDATION,
- "AV1 (MediaFoundation)",
- "AV1 decoder using Windows Media Foundation",
- s_media_foundation_available
- });
-
- // AV1 NVDEC decoder
- decoders.push_back({
- VideoCodecType::AV1,
- DecoderType::NVDEC,
- "AV1 (NVDEC)",
- "AV1 decoder using NVIDIA NVDEC hardware acceleration",
- s_nvdec_available
- });
-
- decoders.push_back({
- VideoCodecType::VP9,
- DecoderType::DAV1D, // TODO: VP9 needs separate decoder type
- "VP9",
- "VP9 video decoder (TODO: not implemented yet)",
- s_vp9_available
- });
-
- return decoders;
-}
-
-bool VideoDecoderFactory::IsCodecSupported(VideoCodecType codec_type) {
- if (!s_factory_initialized) {
- InitializeFactory();
- }
-
- switch (codec_type) {
- case VideoCodecType::AV1: return s_av1_available;
- case VideoCodecType::VP9: return s_vp9_available;
- default: return false;
- }
-}
-
-bool VideoDecoderFactory::IsCodecSupported(const std::string& codec_id) {
- VideoCodecType codec_type = DetectCodecTypeFromId(codec_id);
- return IsCodecSupported(codec_type);
-}
-
-void VideoDecoderFactory::InitializeFactory() {
- if (s_factory_initialized) return;
-
- OutputDebugStringA("[VideoDecoderFactory] Initializing decoder factory...\n");
-
- // Check availability of each decoder
- s_av1_available = CheckAV1DecoderAvailability();
- s_vp9_available = CheckVP9DecoderAvailability();
- s_media_foundation_available = CheckMediaFoundationAvailability();
- s_nvdec_available = CheckNVDECAvailability();
-
- OutputDebugStringA(("[VideoDecoderFactory] AV1 (dav1d): " + std::string(s_av1_available ? "Available" : "Not available") + "\n").c_str());
- OutputDebugStringA(("[VideoDecoderFactory] VP9: " + std::string(s_vp9_available ? "Available" : "Not available") + "\n").c_str());
- OutputDebugStringA(("[VideoDecoderFactory] Media Foundation: " + std::string(s_media_foundation_available ? "Available" : "Not available") + "\n").c_str());
- OutputDebugStringA(("[VideoDecoderFactory] NVDEC: " + std::string(s_nvdec_available ? "Available" : "Not available") + "\n").c_str());
-
- s_factory_initialized = true;
-}
-
-void VideoDecoderFactory::CleanupFactory() {
- s_factory_initialized = false;
- s_av1_available = false;
- s_vp9_available = false;
- s_media_foundation_available = false;
- s_nvdec_available = false;
-}
-
-std::string VideoDecoderFactory::GetDecoderVersion(VideoCodecType codec_type) {
- switch (codec_type) {
- case VideoCodecType::AV1:
- return "dav1d 1.0+"; // TODO: get actual version information
- case VideoCodecType::VP9:
- return "Not implemented"; // TODO: when VP9 is implemented
- default:
- return "Unknown";
- }
-}
-
-std::string VideoDecoderFactory::GetDecoderDescription(VideoCodecType codec_type) {
- switch (codec_type) {
- case VideoCodecType::AV1:
- return "High-performance AV1 decoder";
- case VideoCodecType::VP9:
- return "VP9 decoder (TODO)";
- default:
- return "Unknown decoder";
- }
-}
-
-bool VideoDecoderFactory::CheckAV1DecoderAvailability() {
- // TODO: Actually check dav1d library loading
- // Currently assumes always available
- return true;
-}
-
-bool VideoDecoderFactory::CheckVP9DecoderAvailability() {
- // TODO: activate after VP9 decoder implementation
- return false;
-}
-
-bool VideoDecoderFactory::CheckMediaFoundationAvailability() {
- try {
- HRESULT hr = MFStartup(MF_VERSION);
- if (FAILED(hr)) {
- OutputDebugStringA("[VideoDecoderFactory] Media Foundation startup failed\n");
- return false;
- }
-
- MFT_REGISTER_TYPE_INFO inputType = { MFMediaType_Video, MFVideoFormat_AV1 };
- IMFActivate** activateArray = nullptr;
- UINT32 numActivate = 0;
-
- hr = MFTEnumEx(
- MFT_CATEGORY_VIDEO_DECODER,
- MFT_ENUM_FLAG_ALL, // Search for software and hardware MFTs
- &inputType,
- nullptr,
- &activateArray,
- &numActivate
- );
-
- bool av1_mft_available = false;
- if (SUCCEEDED(hr) && numActivate > 0) {
- av1_mft_available = true;
- for (UINT32 i = 0; i < numActivate; i++) {
- LPWSTR friendlyName = nullptr;
- UINT32 nameLength = 0;
- if (SUCCEEDED(activateArray[i]->GetAllocatedString(MFT_FRIENDLY_NAME_Attribute, &friendlyName, &nameLength))) {
- OutputDebugStringA("[VideoDecoderFactory] Found AV1 MFT: ");
- OutputDebugStringW(friendlyName);
- OutputDebugStringA("\n");
- CoTaskMemFree(friendlyName);
- }
- activateArray[i]->Release();
- }
- CoTaskMemFree(activateArray);
- }
-
- MFShutdown();
-
- if (av1_mft_available) {
- OutputDebugStringA("[VideoDecoderFactory] Media Foundation AV1 support: AVAILABLE\n");
- return true;
- } else {
- OutputDebugStringA("[VideoDecoderFactory] Media Foundation AV1 support: NOT AVAILABLE\n");
- return false;
- }
- }
- catch (const std::exception& e) {
- OutputDebugStringA(("[VideoDecoderFactory] Media Foundation availability check exception: " + std::string(e.what()) + "\n").c_str());
- return false;
- }
- catch (...) {
- OutputDebugStringA("[VideoDecoderFactory] Media Foundation availability check: Unknown exception\n");
- return false;
- }
-}
-
-bool VideoDecoderFactory::CheckNVDECAvailability() {
- try {
- // Create temporary NVDEC decoder to test availability
- auto nvdec_decoder = std::make_unique();
- bool available = nvdec_decoder->IsNVDECAvailable();
-
- if (available) {
- OutputDebugStringA("[VideoDecoderFactory] NVDEC AV1 support: AVAILABLE\n");
- } else {
- OutputDebugStringA("[VideoDecoderFactory] NVDEC AV1 support: NOT AVAILABLE (No NVIDIA GPU or driver)\n");
- }
-
- return available;
- }
- catch (const std::exception& e) {
- OutputDebugStringA(("[VideoDecoderFactory] NVDEC availability check exception: " + std::string(e.what()) + "\n").c_str());
- return false;
- }
- catch (...) {
- OutputDebugStringA("[VideoDecoderFactory] NVDEC availability check: Unknown exception\n");
- return false;
- }
-}
-
-// DecoderUtils implementation
-namespace DecoderUtils {
-
-std::string GetFriendlyCodecName(const std::string& codec_id) {
- if (codec_id == CodecIds::AV1) return "AV1";
- if (codec_id == CodecIds::VP9) return "VP9";
- if (codec_id == CodecIds::VP8) return "VP8";
- if (codec_id == CodecIds::H264) return "H.264/AVC";
- if (codec_id == CodecIds::H265) return "H.265/HEVC";
- return "Unknown (" + codec_id + ")";
-}
-
-std::string GetFriendlyCodecName(VideoCodecType codec_type) {
- switch (codec_type) {
- case VideoCodecType::AV1: return "AV1";
- case VideoCodecType::VP9: return "VP9";
- case VideoCodecType::VP8: return "VP8";
- case VideoCodecType::H264: return "H.264/AVC";
- case VideoCodecType::H265: return "H.265/HEVC";
- default: return "Unknown";
- }
-}
-
-std::string CodecTypeToString(VideoCodecType codec_type) {
- switch (codec_type) {
- case VideoCodecType::AV1: return "AV1";
- case VideoCodecType::VP9: return "VP9";
- case VideoCodecType::VP8: return "VP8";
- case VideoCodecType::H264: return "H264";
- case VideoCodecType::H265: return "H265";
- default: return "UNKNOWN";
- }
-}
-
-VideoCodecType StringToCodecType(const std::string& codec_string) {
- if (codec_string == "AV1") return VideoCodecType::AV1;
- if (codec_string == "VP9") return VideoCodecType::VP9;
- if (codec_string == "VP8") return VideoCodecType::VP8;
- if (codec_string == "H264") return VideoCodecType::H264;
- if (codec_string == "H265") return VideoCodecType::H265;
- return VideoCodecType::AV1; // Default value
-}
-
-} // namespace DecoderUtils
-
-} // namespace Vav2Player
\ No newline at end of file
diff --git a/vav2/platforms/windows/applications/vav2player/Vav2Player/src/Decoder/VideoDecoderFactory.h b/vav2/platforms/windows/applications/vav2player/Vav2Player/src/Decoder/VideoDecoderFactory.h
deleted file mode 100644
index 88403ee..0000000
--- a/vav2/platforms/windows/applications/vav2player/Vav2Player/src/Decoder/VideoDecoderFactory.h
+++ /dev/null
@@ -1,101 +0,0 @@
-#pragma once
-#include "IVideoDecoder.h"
-#include
-#include
-#include
-#include
-
-namespace Vav2Player {
-
-// Video decoder factory class
-// Creates appropriate decoder instances based on codec type
-class VideoDecoderFactory {
-public:
- // Decoder type enumeration
- enum class DecoderType {
- DAV1D, // dav1d library based decoder
- ADAPTIVE_DAV1D, // Adaptive dav1d with dynamic quality control (post-decode scaling)
- MEDIA_FOUNDATION, // Windows Media Foundation based decoder
- NVDEC, // NVIDIA NVDEC hardware acceleration decoder
- ADAPTIVE_NVDEC, // Adaptive NVDEC with dynamic quality control
- AUTO // Auto selection (ADAPTIVE_NVDEC priority, ADAPTIVE_DAV1D, NVDEC, dav1d, finally MediaFoundation)
- };
-
- // Supported decoder information
- struct DecoderInfo {
- VideoCodecType codec_type;
- DecoderType decoder_type;
- std::string codec_name;
- std::string description;
- bool is_available; // Whether currently available (library load status, etc.)
- };
-
- // Decoder creation (based on codec type)
- static std::unique_ptr CreateDecoder(VideoCodecType codec_type, DecoderType decoder_type = DecoderType::AUTO);
-
- // Decoder creation (based on codec ID string - used in WebM)
- static std::unique_ptr CreateDecoderFromCodecId(const std::string& codec_id, DecoderType decoder_type = DecoderType::AUTO);
-
-
- // Convert codec ID string to VideoCodecType
- static VideoCodecType DetectCodecTypeFromId(const std::string& codec_id);
-
- // Return list of all supported decoders
- static std::vector GetSupportedDecoders();
-
- // Check if specific codec is supported
- static bool IsCodecSupported(VideoCodecType codec_type);
- static bool IsCodecSupported(const std::string& codec_id);
-
- // Check decoder availability (library load status, etc.)
- static void InitializeFactory(); // Called at app startup
- static void CleanupFactory(); // Called at app shutdown
-
- // Additional information per decoder
- static std::string GetDecoderVersion(VideoCodecType codec_type);
- static std::string GetDecoderDescription(VideoCodecType codec_type);
-
-private:
- // Factory is used as a static class
- VideoDecoderFactory() = delete;
- ~VideoDecoderFactory() = delete;
- VideoDecoderFactory(const VideoDecoderFactory&) = delete;
- VideoDecoderFactory& operator=(const VideoDecoderFactory&) = delete;
-
- // Internal helper functions
- static bool CheckAV1DecoderAvailability();
- static bool CheckVP9DecoderAvailability(); // TODO: when VP9 is implemented
- static bool CheckMediaFoundationAvailability();
- static bool CheckNVDECAvailability();
- static std::unique_ptr CreateAV1Decoder(DecoderType decoder_type);
-
- // Decoder availability status cache
- static bool s_av1_available;
- static bool s_vp9_available; // TODO: when VP9 is implemented
- static bool s_media_foundation_available;
- static bool s_nvdec_available;
- static bool s_factory_initialized;
-
-};
-
-// Convenience functions
-namespace DecoderUtils {
- // Convert WebM codec ID to human-readable name
- std::string GetFriendlyCodecName(const std::string& codec_id);
- std::string GetFriendlyCodecName(VideoCodecType codec_type);
-
- // Convert codec type to string
- std::string CodecTypeToString(VideoCodecType codec_type);
- VideoCodecType StringToCodecType(const std::string& codec_string);
-
- // Widely used WebM codec IDs
- namespace CodecIds {
- constexpr const char* AV1 = "V_AV01";
- constexpr const char* VP9 = "V_VP9";
- constexpr const char* VP8 = "V_VP8";
- constexpr const char* H264 = "V_MPEG4/ISO/AVC";
- constexpr const char* H265 = "V_MPEGH/ISO/HEVC";
- }
-}
-
-} // namespace Vav2Player
\ No newline at end of file
diff --git a/vav2/platforms/windows/applications/vav2player/Vav2Player/src/FileIO/IWebMFileReader.h b/vav2/platforms/windows/applications/vav2player/Vav2Player/src/FileIO/IWebMFileReader.h
deleted file mode 100644
index bdbf665..0000000
--- a/vav2/platforms/windows/applications/vav2player/Vav2Player/src/FileIO/IWebMFileReader.h
+++ /dev/null
@@ -1,77 +0,0 @@
-#pragma once
-#include "../Common/VideoTypes.h"
-#include
-#include
-
-namespace Vav2Player {
-
-// Forward declaration for VideoTrackInfo
-struct VideoTrackInfo {
- uint64_t track_number;
- VideoCodecType codec_type;
- std::string codec_id;
- std::string codec_name;
- uint32_t width;
- uint32_t height;
- double frame_rate;
- uint64_t frame_count;
- bool is_default;
- bool is_enabled = true; // For mock compatibility
-};
-
-// Error codes for WebM file operations
-enum class WebMErrorCode {
- Success,
- FileNotFound,
- InvalidFormat,
- UnsupportedCodec,
- NoVideoTrack,
- ReadError,
- SeekError,
- FileNotOpen,
- InvalidTrack,
- SeekFailed,
- Unknown
-};
-
-// Interface for WebM/MKV file readers
-// Provides common abstraction for parsing WebM containers and extracting AV1 video streams
-class IWebMFileReader {
-public:
- virtual ~IWebMFileReader() = default;
-
- // File operations
- virtual bool OpenFile(const std::string& file_path) = 0;
- virtual void CloseFile() = 0;
- virtual bool IsFileOpen() const = 0;
-
- // File and stream information
- virtual const VideoMetadata& GetVideoMetadata() const = 0;
- virtual std::string GetFilePath() const = 0;
-
- // Video track management
- virtual std::vector GetVideoTracks() const = 0;
- virtual bool SelectVideoTrack(uint64_t track_number) = 0;
- virtual uint64_t GetSelectedTrackNumber() const = 0;
-
- // Packet reading
- virtual bool ReadNextPacket(VideoPacket& packet) = 0;
- virtual bool SeekToFrame(uint64_t frame_index) = 0;
- virtual bool SeekToTime(double timestamp_seconds) = 0;
-
- // Position information
- virtual uint64_t GetCurrentFrameIndex() const = 0;
- virtual double GetCurrentTimestamp() const = 0;
- virtual bool IsEndOfFile() const = 0;
-
- // File navigation and statistics
- virtual bool Reset() = 0;
- virtual uint64_t GetTotalFrames() const = 0;
- virtual double GetDuration() const = 0;
-
- // Error handling
- virtual WebMErrorCode GetLastError() const = 0;
- virtual std::string GetLastErrorString() const = 0;
-};
-
-} // namespace Vav2Player
\ No newline at end of file
diff --git a/vav2/platforms/windows/applications/vav2player/Vav2Player/src/FileIO/WebMFileReader.cpp b/vav2/platforms/windows/applications/vav2player/Vav2Player/src/FileIO/WebMFileReader.cpp
deleted file mode 100644
index 16871d6..0000000
--- a/vav2/platforms/windows/applications/vav2player/Vav2Player/src/FileIO/WebMFileReader.cpp
+++ /dev/null
@@ -1,871 +0,0 @@
-#include "pch.h"
-#include "WebMFileReader.h"
-#include
-#include
-#include
-#include
-
-namespace Vav2Player {
-
-// libwebm IMkvReader implementation class
-class WebMFileReader::MkvReader : public mkvparser::IMkvReader {
-public:
- MkvReader() : m_file(nullptr) {}
- ~MkvReader() { Close(); }
-
- bool Open(const std::string& file_path) {
- Close();
-
- errno_t err = fopen_s(&m_file, file_path.c_str(), "rb");
- if (err != 0 || !m_file) {
- return false;
- }
-
- // Calculate file size
- _fseeki64(m_file, 0, SEEK_END);
- m_file_size = _ftelli64(m_file);
- _fseeki64(m_file, 0, SEEK_SET);
-
- return true;
- }
-
- void Close() {
- if (m_file) {
- fclose(m_file);
- m_file = nullptr;
- }
- m_file_size = 0;
- }
-
- // IMkvReader interface implementation
- int Read(long long pos, long len, unsigned char* buf) override {
- std::lock_guard lock(m_file_mutex);
-
- if (!m_file || !buf || len < 0) return -1;
-
- if (_fseeki64(m_file, pos, SEEK_SET) != 0) {
- return -1;
- }
-
- const size_t bytes_read = fread(buf, 1, static_cast(len), m_file);
-
- // libwebm IMkvReader contract:
- // - Return 0: success (read all requested bytes)
- // - Return positive: underflow (insufficient bytes available)
- // - Return negative: error
- if (bytes_read == static_cast(len)) {
- return 0; // Success: read all requested bytes
- } else if (bytes_read < static_cast(len)) {
- return static_cast(len - bytes_read); // Underflow: return insufficient byte count
- } else {
- return -1; // Unexpected situation
- }
- }
-
- int Length(long long* total, long long* available) override {
- std::lock_guard lock(m_file_mutex);
-
- if (!m_file) return -1;
-
- if (total) *total = m_file_size;
- if (available) *available = m_file_size; // Entire file available
- return 0;
- }
-
-private:
- std::FILE* m_file;
- long long m_file_size = 0;
- mutable std::mutex m_file_mutex; // File access synchronization
-};
-
-// WebMFileReader internal state management
-struct WebMFileReader::InternalState {
- std::unique_ptr reader;
- std::unique_ptr segment;
-
- // Current state
- std::string file_path;
- VideoMetadata metadata;
- std::vector video_tracks;
- uint64_t selected_track_number = 0;
-
- // Current reading position
- const mkvparser::Cluster* current_cluster = nullptr;
- const mkvparser::BlockEntry* current_block_entry = nullptr;
- uint64_t current_frame_index = 0;
- double current_timestamp = 0.0;
- bool end_of_file = false;
-
- // Error handling
- WebMErrorCode last_error = WebMErrorCode::Success;
- std::string last_error_message;
-
- InternalState() : reader(std::make_unique()) {}
-};
-
-WebMFileReader::WebMFileReader() : m_state(std::make_unique()) {
-}
-
-WebMFileReader::~WebMFileReader() {
- CloseFile();
-}
-
-bool WebMFileReader::OpenFile(const std::string& file_path) {
- CloseFile();
-
- if (file_path.empty()) {
- SetLastError(WebMErrorCode::FileNotFound, "File path is empty");
- return false;
- }
-
- // Open file
- if (!m_state->reader->Open(file_path)) {
- SetLastError(WebMErrorCode::FileNotFound, "Cannot open file: " + file_path);
- return false;
- }
-
- m_state->file_path = file_path;
-
- // Initialize libwebm parser
- if (!InitializeParser()) {
- CloseFile();
- return false;
- }
-
- // Extract video tracks
- if (!ExtractVideoTracks()) {
- CloseFile();
- SetLastError(WebMErrorCode::NoVideoTrack, "No supported video tracks found");
- return false;
- }
-
- // Automatically select first supported video track
- for (const auto& track : m_state->video_tracks) {
- if (IsVideoCodecSupported(track.codec_id)) {
- SelectVideoTrack(track.track_number);
- break;
- }
- }
-
- if (m_state->selected_track_number == 0) {
- CloseFile();
- SetLastError(WebMErrorCode::UnsupportedCodec, "No supported video codecs found");
- return false;
- }
-
- // Extract metadata
- if (!ExtractVideoMetadata()) {
- CloseFile();
- return false;
- }
-
- // Initialize reading position
- Reset();
-
- SetLastError(WebMErrorCode::Success);
- return true;
-}
-
-void WebMFileReader::CloseFile() {
- if (m_state) {
- m_state->segment.reset();
- m_state->reader->Close();
- m_state->video_tracks.clear();
- m_state->selected_track_number = 0;
- m_state->current_cluster = nullptr;
- m_state->current_block_entry = nullptr;
- m_state->current_frame_index = 0;
- m_state->current_timestamp = 0.0;
- m_state->end_of_file = false;
- m_state->file_path.clear();
- }
-}
-
-bool WebMFileReader::IsFileOpen() const {
- return m_state && m_state->segment && !m_state->file_path.empty();
-}
-
-const VideoMetadata& WebMFileReader::GetVideoMetadata() const {
- return m_state->metadata;
-}
-
-std::string WebMFileReader::GetFilePath() const {
- return m_state ? m_state->file_path : "";
-}
-
-std::vector WebMFileReader::GetVideoTracks() const {
- return m_state ? m_state->video_tracks : std::vector();
-}
-
-bool WebMFileReader::SelectVideoTrack(uint64_t track_number) {
- if (!IsFileOpen()) {
- SetLastError(WebMErrorCode::ReadError, "File not open");
- return false;
- }
-
- // Check track existence
- auto it = std::find_if(m_state->video_tracks.begin(), m_state->video_tracks.end(),
- [track_number](const VideoTrackInfo& info) {
- return info.track_number == track_number;
- });
-
- if (it == m_state->video_tracks.end()) {
- SetLastError(WebMErrorCode::ReadError, "Track not found: " + std::to_string(track_number));
- return false;
- }
-
- m_state->selected_track_number = track_number;
- Reset(); // Initialize reading position
-
- SetLastError(WebMErrorCode::Success);
- return true;
-}
-
-uint64_t WebMFileReader::GetSelectedTrackNumber() const {
- return m_state ? m_state->selected_track_number : 0;
-}
-
-bool WebMFileReader::ReadNextPacket(VideoPacket& packet) {
- std::lock_guard lock(m_access_mutex);
-
- // Enhanced null safety check inside mutex
- if (!m_state) {
- SetLastError(WebMErrorCode::ReadError, "WebMFileReader state is null");
- return false;
- }
-
- if (!IsFileOpen() || m_state->end_of_file) {
- return false;
- }
-
- // Fail if no track selected
- if (m_state->selected_track_number == 0) {
- SetLastError(WebMErrorCode::ReadError, "No video track selected");
- return false;
- }
-
- // Find next block
- if (!AdvanceToNextFrame()) {
- m_state->end_of_file = true;
- return false;
- }
-
- // Read packet from current block
- if (!m_state->current_block_entry || !m_state->current_block_entry->GetBlock()) {
- SetLastError(WebMErrorCode::ReadError, "Invalid block entry");
- return false;
- }
-
- const mkvparser::Block* block = m_state->current_block_entry->GetBlock();
- if (!ReadPacketFromBlock(block, packet)) {
- SetLastError(WebMErrorCode::ReadError, "Failed to read packet from block");
- return false;
- }
-
- // Update current state
- m_state->current_frame_index++;
-
- // Calculate timestamp
- const mkvparser::SegmentInfo* info = m_state->segment->GetInfo();
- if (info && m_state->current_cluster) {
- long long cluster_time = m_state->current_cluster->GetTime();
- long long block_time = block->GetTime(m_state->current_cluster);
- long long timecode_scale = info->GetTimeCodeScale();
-
- m_state->current_timestamp = WebMUtils::TimecodeToSeconds(
- cluster_time + block_time, timecode_scale);
-
- packet.timestamp_seconds = m_state->current_timestamp;
- }
-
- packet.frame_index = m_state->current_frame_index - 1;
- packet.is_keyframe = block->IsKey();
-
- SetLastError(WebMErrorCode::Success);
- return true;
-}
-
-bool WebMFileReader::SeekToFrame(uint64_t frame_index) {
- if (!IsFileOpen()) {
- SetLastError(WebMErrorCode::SeekError, "File not open");
- return false;
- }
-
- std::lock_guard lock(m_access_mutex);
-
- if (m_state->selected_track_number == 0) {
- SetLastError(WebMErrorCode::SeekError, "No video track selected");
- return false;
- }
-
- // Frame 0 is equivalent to reset
- if (frame_index == 0) {
- return Reset();
- }
-
- // Convert frame-based seeking to time-based seeking
- // Calculate approximate time using frame rate
- double estimated_time = 0.0;
- if (m_state->metadata.frame_rate > 0) {
- estimated_time = static_cast(frame_index) / m_state->metadata.frame_rate;
- }
-
- // 시간 기반 탐색 후 정확한 프레임 찾기
- if (!SeekToTime(estimated_time)) {
- return false;
- }
-
- // 정확한 프레임 인덱스까지 순차 탐색
- while (m_state->current_frame_index < frame_index && !m_state->end_of_file) {
- if (!AdvanceToNextFrame()) {
- m_state->end_of_file = true;
- SetLastError(WebMErrorCode::SeekError, "Cannot reach target frame");
- return false;
- }
- m_state->current_frame_index++;
- }
-
- SetLastError(WebMErrorCode::Success);
- return m_state->current_frame_index == frame_index;
-}
-
-bool WebMFileReader::SeekToTime(double timestamp_seconds) {
- if (!IsFileOpen()) {
- SetLastError(WebMErrorCode::SeekError, "File not open");
- return false;
- }
-
- std::lock_guard lock(m_access_mutex);
-
- if (m_state->selected_track_number == 0) {
- SetLastError(WebMErrorCode::SeekError, "No video track selected");
- return false;
- }
-
- if (timestamp_seconds < 0.0) {
- SetLastError(WebMErrorCode::SeekError, "Invalid timestamp");
- return false;
- }
-
- // 시간이 0이면 리셋
- if (timestamp_seconds == 0.0) {
- return Reset();
- }
-
- // 세그먼트 정보에서 타임코드 스케일 가져오기
- const mkvparser::SegmentInfo* info = m_state->segment->GetInfo();
- if (!info) {
- SetLastError(WebMErrorCode::SeekError, "No segment info available");
- return false;
- }
-
- long long timecode_scale = info->GetTimeCodeScale();
- if (timecode_scale <= 0) {
- SetLastError(WebMErrorCode::SeekError, "Invalid timecode scale");
- return false;
- }
-
- // 타겟 타임코드 계산
- long long target_timecode = WebMUtils::SecondsToTimecode(timestamp_seconds, timecode_scale);
-
- // 타겟 시간에 가장 가까운 클러스터 찾기
- const mkvparser::Cluster* target_cluster = FindClusterByTime(timestamp_seconds);
- if (!target_cluster) {
- SetLastError(WebMErrorCode::SeekError, "Cannot find target cluster");
- return false;
- }
-
- // 상태 업데이트
- m_state->current_cluster = target_cluster;
- m_state->current_block_entry = nullptr;
- m_state->current_timestamp = timestamp_seconds;
- m_state->end_of_file = false;
-
- // 정확한 시간의 프레임까지 이동
- while (!m_state->end_of_file) {
- if (!AdvanceToNextFrame()) {
- m_state->end_of_file = true;
- break;
- }
-
- // 현재 블록의 시간 확인
- if (m_state->current_block_entry && m_state->current_block_entry->GetBlock()) {
- const mkvparser::Block* block = m_state->current_block_entry->GetBlock();
- long long cluster_time = m_state->current_cluster->GetTime();
- long long block_time = block->GetTime(m_state->current_cluster);
- double current_time = WebMUtils::TimecodeToSeconds(cluster_time + block_time, timecode_scale);
-
- if (current_time >= timestamp_seconds) {
- m_state->current_timestamp = current_time;
- SetLastError(WebMErrorCode::Success);
- return true;
- }
- }
-
- m_state->current_frame_index++;
- }
-
- SetLastError(WebMErrorCode::SeekError, "Cannot reach target time");
- return false;
-}
-
-uint64_t WebMFileReader::GetCurrentFrameIndex() const {
- return m_state ? m_state->current_frame_index : 0;
-}
-
-double WebMFileReader::GetCurrentTimestamp() const {
- return m_state ? m_state->current_timestamp : 0.0;
-}
-
-bool WebMFileReader::IsEndOfFile() const {
- return m_state ? m_state->end_of_file : true;
-}
-
-bool WebMFileReader::Reset() {
- if (!IsFileOpen()) return false;
-
- std::lock_guard lock(m_access_mutex);
-
- // 첫 번째 클러스터로 이동
- const mkvparser::Cluster* cluster = m_state->segment->GetFirst();
- if (!cluster) {
- SetLastError(WebMErrorCode::ReadError, "No clusters found");
- return false;
- }
-
- m_state->current_cluster = cluster;
- m_state->current_block_entry = nullptr;
- m_state->current_frame_index = 0;
- m_state->current_timestamp = 0.0;
- m_state->end_of_file = false;
-
- return true;
-}
-
-uint64_t WebMFileReader::GetTotalFrames() const {
- return m_state ? m_state->metadata.total_frames : 0;
-}
-
-double WebMFileReader::GetDuration() const {
- return m_state ? m_state->metadata.duration_seconds : 0.0;
-}
-
-WebMErrorCode WebMFileReader::GetLastError() const {
- return m_state ? m_state->last_error : WebMErrorCode::Unknown;
-}
-
-std::string WebMFileReader::GetLastErrorString() const {
- if (!m_state) return "Internal error";
-
- std::string result = ErrorCodeToString(m_state->last_error);
- if (!m_state->last_error_message.empty()) {
- result += ": " + m_state->last_error_message;
- }
- return result;
-}
-
-std::string WebMFileReader::GetLibWebMVersion() {
- int major, minor, build, revision;
- mkvparser::GetVersion(major, minor, build, revision);
- return std::to_string(major) + "." + std::to_string(minor) + "." +
- std::to_string(build) + "." + std::to_string(revision);
-}
-
-std::vector WebMFileReader::GetSupportedCodecs() {
- return { WebMUtils::CodecIds::AV1, WebMUtils::CodecIds::VP9, WebMUtils::CodecIds::VP8 };
-}
-
-// 내부 helper 메서드 구현
-
-bool WebMFileReader::InitializeParser() {
- long long pos = 0;
- mkvparser::EBMLHeader ebml_header;
-
- // EBML 헤더 파싱
- long long ebml_header_size = ebml_header.Parse(m_state->reader.get(), pos);
-
- if (ebml_header_size < 0) {
- std::string detailed_error = "Invalid EBML header - Parse() returned: " + std::to_string(ebml_header_size);
-
- // 추가 디버깅: 파일 시작 부분 읽기 (올바른 Read 메서드 시그니처 사용)
- char buffer[32];
- int read_result = m_state->reader->Read(0, 32, reinterpret_cast(buffer));
- if (read_result == 0) {
- std::string hex_dump = ". First 32 bytes: ";
- for (int i = 0; i < 32; i++) {
- char hex[4];
- sprintf_s(hex, "%02X ", static_cast(buffer[i]));
- hex_dump += hex;
- }
- detailed_error += hex_dump;
- }
-
- SetLastError(WebMErrorCode::InvalidFormat, detailed_error);
- return false;
- }
-
- // WebM 파일 확인
- if (!ebml_header.m_docType || std::string(ebml_header.m_docType) != "webm") {
- std::string doc_type = ebml_header.m_docType ? std::string(ebml_header.m_docType) : "NULL";
- std::string error_msg = "Not a WebM file - doc type is: '" + doc_type + "'";
-
- // 추가 디버깅 정보
- error_msg += ". EBML header details: version=" + std::to_string(ebml_header.m_version) +
- ", docTypeVersion=" + std::to_string(ebml_header.m_docTypeVersion);
-
- SetLastError(WebMErrorCode::InvalidFormat, error_msg);
- return false;
- }
-
- // Segment 파싱
- mkvparser::Segment* segment_ptr = nullptr;
- long long create_status = mkvparser::Segment::CreateInstance(
- m_state->reader.get(), pos, segment_ptr);
-
- if (create_status < 0 || !segment_ptr) {
- SetLastError(WebMErrorCode::InvalidFormat, "Cannot create segment");
- return false;
- }
-
- m_state->segment.reset(segment_ptr);
-
- if (!m_state->segment) {
- SetLastError(WebMErrorCode::InvalidFormat, "Cannot create segment");
- return false;
- }
-
- // Segment 정보 로드
- long load_status = m_state->segment->Load();
- if (load_status < 0) {
- SetLastError(WebMErrorCode::InvalidFormat, "Cannot load segment");
- return false;
- }
-
- return true;
-}
-
-void WebMFileReader::CleanupParser() {
- if (m_state) {
- m_state->segment.reset();
- }
-}
-
-bool WebMFileReader::ExtractVideoTracks() {
- if (!m_state->segment) return false;
-
- m_state->video_tracks.clear();
-
- const mkvparser::Tracks* tracks = m_state->segment->GetTracks();
- if (!tracks) return false;
-
- for (unsigned long i = 0; i < tracks->GetTracksCount(); ++i) {
- const mkvparser::Track* track = tracks->GetTrackByIndex(i);
- if (!track || track->GetType() != mkvparser::Track::kVideo) {
- continue;
- }
-
- const mkvparser::VideoTrack* video_track =
- static_cast(track);
-
- VideoTrackInfo info;
- info.track_number = video_track->GetNumber();
- info.codec_id = video_track->GetCodecId() ? video_track->GetCodecId() : "";
-
- // 디버깅: 실제 코덱 ID를 파일에 출력
- std::string debug_msg = "Found video track #" + std::to_string(info.track_number) +
- " with codec_id: '" + info.codec_id + "'";
- SetLastError(WebMErrorCode::Success, debug_msg); // 임시로 이 메시지를 상태에 표시
-
- // 파일에 코덱 ID 기록
- std::ofstream codec_debug("codec_debug.log", std::ios::app);
- codec_debug << "WebM Codec Detection:" << std::endl;
- codec_debug << " Track #" << info.track_number << std::endl;
- codec_debug << " Codec ID: '" << info.codec_id << "'" << std::endl;
- codec_debug << " Expected AV1 variants:" << std::endl;
- codec_debug << " - V_AV01: '" << WebMUtils::CodecIds::AV1 << "'" << std::endl;
- codec_debug << " - V_AV1: '" << WebMUtils::CodecIds::AV1_ALT1 << "'" << std::endl;
- codec_debug << " - AV01: '" << WebMUtils::CodecIds::AV1_ALT2 << "'" << std::endl;
- codec_debug << " Match AV1: " << (info.codec_id == WebMUtils::CodecIds::AV1 ? "YES" : "NO") << std::endl;
- codec_debug << " Match AV1_ALT1: " << (info.codec_id == WebMUtils::CodecIds::AV1_ALT1 ? "YES" : "NO") << std::endl;
- codec_debug << " Match AV1_ALT2: " << (info.codec_id == WebMUtils::CodecIds::AV1_ALT2 ? "YES" : "NO") << std::endl;
- codec_debug << " Supported: " << (IsVideoCodecSupported(info.codec_id) ? "YES" : "NO") << std::endl;
- codec_debug << std::endl;
- codec_debug.close();
-
- info.codec_type = DetectCodecType(info.codec_id);
- info.codec_name = ExtractCodecName(info.codec_id);
- info.width = static_cast(video_track->GetWidth());
- info.height = static_cast(video_track->GetHeight());
- info.frame_rate = CalculateFrameRate(video_track);
- info.frame_count = 0; // TODO: 실제 계산
- info.is_default = false; // TODO: 트랙의 기본 설정 확인
-
- m_state->video_tracks.push_back(info);
- }
-
- return !m_state->video_tracks.empty();
-}
-
-bool WebMFileReader::ExtractVideoMetadata() {
- if (m_state->selected_track_number == 0) return false;
-
- // 선택된 트랙 찾기
- auto it = std::find_if(m_state->video_tracks.begin(), m_state->video_tracks.end(),
- [this](const VideoTrackInfo& info) {
- return info.track_number == m_state->selected_track_number;
- });
-
- if (it == m_state->video_tracks.end()) return false;
-
- // 메타데이터 설정
- VideoMetadata& meta = m_state->metadata;
- meta.width = it->width;
- meta.height = it->height;
- meta.frame_rate = it->frame_rate;
- meta.codec_type = it->codec_type;
- meta.codec_name = it->codec_name;
- meta.color_space = ColorSpace::YUV420P; // 기본값
-
- // 세그먼트 정보에서 duration 가져오기
- const mkvparser::SegmentInfo* info = m_state->segment->GetInfo();
- if (info) {
- long long duration_ns = info->GetDuration();
- long long timecode_scale = info->GetTimeCodeScale();
- if (duration_ns > 0 && timecode_scale > 0) {
- meta.duration_seconds = static_cast(duration_ns) / timecode_scale / 1000000000.0;
- }
- }
-
- // 총 프레임 수 추정
- if (meta.frame_rate > 0 && meta.duration_seconds > 0) {
- meta.total_frames = static_cast(meta.frame_rate * meta.duration_seconds);
- }
-
- meta.file_path = m_state->file_path;
-
- return meta.IsValid();
-}
-
-VideoCodecType WebMFileReader::DetectCodecType(const std::string& codec_id) const {
- if (codec_id == WebMUtils::CodecIds::AV1 ||
- codec_id == WebMUtils::CodecIds::AV1_ALT1 ||
- codec_id == WebMUtils::CodecIds::AV1_ALT2) {
- return VideoCodecType::AV1;
- }
- if (codec_id == WebMUtils::CodecIds::VP9) return VideoCodecType::VP9;
- if (codec_id == WebMUtils::CodecIds::VP8) return VideoCodecType::VP8;
- return VideoCodecType::AV1; // 기본값
-}
-
-double WebMFileReader::CalculateFrameRate(const mkvparser::VideoTrack* video_track) const {
- if (!video_track) return 0.0;
-
- double frame_rate = video_track->GetFrameRate();
- if (frame_rate > 0) return frame_rate;
-
- // TODO: 클러스터 분석으로 프레임 레이트 추정
- return 30.0; // 임시 기본값
-}
-
-void WebMFileReader::SetLastError(WebMErrorCode error, const std::string& message) {
- if (m_state) {
- m_state->last_error = error;
- m_state->last_error_message = message;
- }
-}
-
-std::string WebMFileReader::ErrorCodeToString(WebMErrorCode error) const {
- switch (error) {
- case WebMErrorCode::Success: return "Success";
- case WebMErrorCode::FileNotFound: return "File not found";
- case WebMErrorCode::InvalidFormat: return "Invalid format";
- case WebMErrorCode::UnsupportedCodec: return "Unsupported codec";
- case WebMErrorCode::NoVideoTrack: return "No video track";
- case WebMErrorCode::ReadError: return "Read error";
- case WebMErrorCode::SeekError: return "Seek error";
- default: return "Unknown error";
- }
-}
-
-bool WebMFileReader::IsVideoCodecSupported(const std::string& codec_id) {
- return codec_id == WebMUtils::CodecIds::AV1 ||
- codec_id == WebMUtils::CodecIds::AV1_ALT1 ||
- codec_id == WebMUtils::CodecIds::AV1_ALT2 ||
- codec_id == WebMUtils::CodecIds::VP9; // VP8 currently unsupported
-}
-
-std::string WebMFileReader::ExtractCodecName(const std::string& codec_id) {
- if (codec_id == WebMUtils::CodecIds::AV1 ||
- codec_id == WebMUtils::CodecIds::AV1_ALT1 ||
- codec_id == WebMUtils::CodecIds::AV1_ALT2) {
- return "AV1";
- }
- if (codec_id == WebMUtils::CodecIds::VP9) return "VP9";
- if (codec_id == WebMUtils::CodecIds::VP8) return "VP8";
- return "Unknown";
-}
-
-bool WebMFileReader::ReadPacketFromBlock(const mkvparser::Block* block, VideoPacket& packet) {
- if (!block) return false;
-
- // 선택된 트랙과 블록의 트랙 번호 확인
- if (static_cast(block->GetTrackNumber()) != m_state->selected_track_number) {
- return false; // 다른 트랙의 블록이므로 스킵
- }
-
- // 블록에서 프레임 수 확인 (일반적으로 1개)
- int frame_count = block->GetFrameCount();
- if (frame_count <= 0) return false;
-
- // 첫 번째 프레임 데이터 가져오기
- const mkvparser::Block::Frame& frame = block->GetFrame(0);
- if (frame.len <= 0) return false;
-
- // VideoPacket 메모리 할당
- if (!packet.AllocateData(static_cast(frame.len))) {
- return false;
- }
-
- // 프레임 데이터 읽기
- if (frame.Read(m_state->reader.get(), packet.data.get()) < 0) {
- packet.data.reset();
- packet.size = 0;
- return false;
- }
-
- return true;
-}
-
-bool WebMFileReader::AdvanceToNextFrame() {
- if (!m_state->current_cluster) {
- // 첫 번째 클러스터로 시작
- m_state->current_cluster = m_state->segment->GetFirst();
- if (!m_state->current_cluster) return false;
- }
-
- // 현재 클러스터에서 다음 블록 엔트리 찾기
- while (m_state->current_cluster) {
- const mkvparser::BlockEntry* block_entry = nullptr;
-
- if (m_state->current_block_entry) {
- // 다음 블록 엔트리로 이동
- long status = m_state->current_cluster->GetNext(m_state->current_block_entry, block_entry);
- if (status < 0) return false;
- } else {
- // 클러스터의 첫 번째 블록 엔트리 가져오기
- long status = m_state->current_cluster->GetFirst(block_entry);
- if (status < 0) return false;
- }
-
- // 블록 엔트리가 있으면서 선택된 트랙의 블록인지 확인
- while (block_entry && !block_entry->EOS()) {
- const mkvparser::Block* block = block_entry->GetBlock();
- if (block && static_cast(block->GetTrackNumber()) == m_state->selected_track_number) {
- m_state->current_block_entry = block_entry;
- return true;
- }
-
- // 다른 트랙의 블록이므로 다음 블록으로 이동
- const mkvparser::BlockEntry* next_entry = nullptr;
- long status = m_state->current_cluster->GetNext(block_entry, next_entry);
- if (status < 0) break;
- block_entry = next_entry;
- }
-
- // 현재 클러스터에서 더 이상 블록이 없으면 다음 클러스터로 이동
- m_state->current_cluster = m_state->segment->GetNext(m_state->current_cluster);
- m_state->current_block_entry = nullptr;
- }
-
- // 더 이상 클러스터가 없음
- return false;
-}
-
-const mkvparser::Cluster* WebMFileReader::FindClusterByTime(double timestamp_seconds) {
- if (!m_state->segment) return nullptr;
-
- const mkvparser::SegmentInfo* info = m_state->segment->GetInfo();
- if (!info) return nullptr;
-
- long long timecode_scale = info->GetTimeCodeScale();
- if (timecode_scale <= 0) return nullptr;
-
- // 타겟 타임코드 계산
- long long target_timecode = WebMUtils::SecondsToTimecode(timestamp_seconds, timecode_scale);
-
- // 첫 번째 클러스터부터 순차 탐색
- const mkvparser::Cluster* cluster = m_state->segment->GetFirst();
- const mkvparser::Cluster* best_cluster = cluster;
-
- while (cluster && !cluster->EOS()) {
- long long cluster_time = cluster->GetTime();
-
- // 타겟 시간에 가장 가까운 클러스터 찾기
- if (cluster_time <= target_timecode) {
- best_cluster = cluster;
- } else {
- break; // 타겟 시간을 넘어섰으므로 이전 클러스터가 최적
- }
-
- cluster = m_state->segment->GetNext(cluster);
- }
-
- return best_cluster;
-}
-
-// WebMUtils 구현
-namespace WebMUtils {
-
-bool IsWebMFile(const std::string& file_path) {
- std::ifstream file(file_path, std::ios::binary);
- if (!file.is_open()) return false;
-
- uint8_t header[32];
- file.read(reinterpret_cast(header), sizeof(header));
-
- return IsValidWebMHeader(header, file.gcount());
-}
-
-bool IsValidWebMHeader(const uint8_t* data, size_t size) {
- if (!data || size < 4) return false;
-
- // EBML 헤더 시그니처 확인 (0x1A45DFA3)
- return data[0] == 0x1A && data[1] == 0x45 &&
- data[2] == 0xDF && data[3] == 0xA3;
-}
-
-std::vector GetAllWebMCodecs() {
- return {
- { CodecIds::AV1, "AV1", VideoCodecType::AV1, true },
- { CodecIds::VP9, "VP9", VideoCodecType::VP9, false }, // TODO: VP9 support
- { CodecIds::VP8, "VP8", VideoCodecType::VP8, false }
- };
-}
-
-CodecInfo GetCodecInfo(const std::string& codec_id) {
- auto codecs = GetAllWebMCodecs();
- auto it = std::find_if(codecs.begin(), codecs.end(),
- [&codec_id](const CodecInfo& info) {
- return info.codec_id == codec_id;
- });
-
- if (it != codecs.end()) {
- return *it;
- }
-
- return { codec_id, "Unknown", VideoCodecType::AV1, false };
-}
-
-double TimecodeToSeconds(long long timecode, long long timecode_scale) {
- if (timecode_scale <= 0) return 0.0;
- return static_cast(timecode) * timecode_scale / 1000000000.0;
-}
-
-long long SecondsToTimecode(double seconds, long long timecode_scale) {
- if (timecode_scale <= 0) return 0;
- return static_cast(seconds * 1000000000.0 / timecode_scale);
-}
-
-} // namespace WebMUtils
-
-} // namespace Vav2Player
\ No newline at end of file
diff --git a/vav2/platforms/windows/applications/vav2player/Vav2Player/src/FileIO/WebMFileReader.h b/vav2/platforms/windows/applications/vav2player/Vav2Player/src/FileIO/WebMFileReader.h
deleted file mode 100644
index 41dcb6b..0000000
--- a/vav2/platforms/windows/applications/vav2player/Vav2Player/src/FileIO/WebMFileReader.h
+++ /dev/null
@@ -1,144 +0,0 @@
-#pragma once
-#include "../Common/VideoTypes.h"
-#include "IWebMFileReader.h"
-#include
-#include
-#include
-#include
-#include
-
-namespace Vav2Player {
-
-// WebM/MKV 파일을 파싱하여 AV1 비디오 스트림을 추출하는 클래스
-// libwebm의 mkvparser를 사용하여 구현
-class WebMFileReader : public IWebMFileReader {
-public:
- WebMFileReader();
- ~WebMFileReader();
-
- // 복사 방지
- WebMFileReader(const WebMFileReader&) = delete;
- WebMFileReader& operator=(const WebMFileReader&) = delete;
-
- // 파일 열기/닫기 (interface implementation)
- bool OpenFile(const std::string& file_path) override;
- void CloseFile() override;
- bool IsFileOpen() const override;
-
- // 파일 및 스트림 정보 (interface implementation)
- const VideoMetadata& GetVideoMetadata() const override;
- std::string GetFilePath() const override;
-
- // 비디오 트랙 관리 (interface implementation)
- std::vector GetVideoTracks() const override;
- bool SelectVideoTrack(uint64_t track_number) override;
- uint64_t GetSelectedTrackNumber() const override;
-
- // 패킷 읽기 (interface implementation)
- bool ReadNextPacket(VideoPacket& packet) override;
- bool SeekToFrame(uint64_t frame_index) override;
- bool SeekToTime(double timestamp_seconds) override;
-
- // 위치 정보 (interface implementation)
- uint64_t GetCurrentFrameIndex() const override;
- double GetCurrentTimestamp() const override;
- bool IsEndOfFile() const override;
-
- // 파일 탐색 및 통계 (interface implementation)
- bool Reset() override;
- uint64_t GetTotalFrames() const override;
- double GetDuration() const override;
-
- // 에러 처리 (interface implementation)
- WebMErrorCode GetLastError() const override;
- std::string GetLastErrorString() const override;
-
- // libwebm 관련 정보
- static std::string GetLibWebMVersion();
- static std::vector GetSupportedCodecs();
-
-private:
- // libwebm 관련 내부 클래스들
- class MkvReader; // mkvparser::IMkvReader 구현체
-
- // 내부 상태 관리
- struct InternalState;
- std::unique_ptr m_state;
-
- // Thread safety for multi-instance file access
- mutable std::mutex m_access_mutex;
-
- // 내부 helper 메서드들
- bool InitializeParser();
- void CleanupParser();
- bool ParseFileHeader();
- bool ExtractVideoTracks();
- bool ValidateSelectedTrack();
-
- // 메타데이터 추출
- bool ExtractVideoMetadata();
- VideoCodecType DetectCodecType(const std::string& codec_id) const;
- double CalculateFrameRate(const mkvparser::VideoTrack* video_track) const;
- uint64_t EstimateFrameCount() const;
-
- // 패킷 읽기 관련
- bool ReadPacketFromBlock(const mkvparser::Block* block, VideoPacket& packet);
- bool AdvanceToNextFrame();
-
- // 탐색 관련
- const mkvparser::Cluster* FindClusterByTime(double timestamp_seconds);
- const mkvparser::Block* FindBlockByFrame(uint64_t frame_index);
-
- // 에러 처리
- void SetLastError(WebMErrorCode error, const std::string& message = "");
- std::string ErrorCodeToString(WebMErrorCode error) const;
-
- // 유틸리티 함수들
- static bool IsVideoCodecSupported(const std::string& codec_id);
- static std::string ExtractCodecName(const std::string& codec_id);
- static ColorSpace GetColorSpaceFromTrack(const mkvparser::VideoTrack* track);
-};
-
-// WebM 관련 유틸리티 함수들
-namespace WebMUtils {
- // WebM 파일 검증
- bool IsWebMFile(const std::string& file_path);
- bool IsValidWebMHeader(const uint8_t* data, size_t size);
-
- // 코덱 정보
- struct CodecInfo {
- std::string codec_id;
- std::string friendly_name;
- VideoCodecType codec_type;
- bool is_supported;
- };
-
- std::vector GetAllWebMCodecs();
- CodecInfo GetCodecInfo(const std::string& codec_id);
-
- // WebM 포맷 상수들
- namespace CodecIds {
- constexpr const char* AV1 = "V_AV01";
- constexpr const char* AV1_ALT1 = "V_AV1"; // 대체 AV1 ID
- constexpr const char* AV1_ALT2 = "AV01"; // 짧은 형태
- constexpr const char* VP9 = "V_VP9";
- constexpr const char* VP8 = "V_VP8";
- }
-
- // 시간/타임스탬프 변환
- double TimecodeToSeconds(long long timecode, long long timecode_scale);
- long long SecondsToTimecode(double seconds, long long timecode_scale);
-
- // 파일 크기 및 정보
- struct FileInfo {
- uint64_t file_size;
- std::string file_format;
- std::string muxing_app;
- std::string writing_app;
- double duration_seconds;
- };
-
- FileInfo GetWebMFileInfo(const std::string& file_path);
-}
-
-} // namespace Vav2Player
\ No newline at end of file
diff --git a/vav2/platforms/windows/applications/vav2player/Vav2Player/src/Logger/SimpleLogger.cpp b/vav2/platforms/windows/applications/vav2player/Vav2Player/src/Logger/SimpleLogger.cpp
new file mode 100644
index 0000000..3333ea4
--- /dev/null
+++ b/vav2/platforms/windows/applications/vav2player/Vav2Player/src/Logger/SimpleLogger.cpp
@@ -0,0 +1,201 @@
+#include "pch.h"
+#include "SimpleLogger.h"
+#include
+#include
+#include
+#include
+#include
+#include
+
+namespace Vav2Player {
+
+SimpleLogger& SimpleLogger::GetInstance() {
+ static SimpleLogger instance;
+ return instance;
+}
+
+SimpleLogger::SimpleLogger() : m_currentLevel(LogLevel::Info) {}
+
+void SimpleLogger::SetLogLevel(LogLevel level) {
+ std::lock_guard lock(m_mutex);
+ m_currentLevel = level;
+}
+
+// ===== wstring overloads (original implementation) =====
+void SimpleLogger::LogMessage(LogLevel level, const std::wstring& message, const std::wstring& source) {
+ if (static_cast(level) < static_cast(m_currentLevel)) {
+ return;
+ }
+
+ std::lock_guard lock(m_mutex);
+
+ auto now = std::chrono::system_clock::now();
+ auto time_t = std::chrono::system_clock::to_time_t(now);
+ auto ms = std::chrono::duration_cast(now.time_since_epoch()) % 1000;
+
+ std::tm tm;
+ localtime_s(&tm, &time_t);
+
+ std::wostringstream oss;
+ oss << std::put_time(&tm, L"%H:%M:%S") << L"."
+ << std::setfill(L'0') << std::setw(3) << ms.count()
+ << L" [" << GetLevelString(level) << L"]";
+
+ if (!source.empty()) {
+ oss << L" (" << source << L")";
+ }
+
+ oss << L": " << message;
+
+ OutputToConsoleAndDebug(oss.str());
+}
+
+void SimpleLogger::LogDebug(const std::wstring& message, const std::wstring& source) {
+ LogMessage(LogLevel::Debug, message, source);
+}
+
+void SimpleLogger::LogInfo(const std::wstring& message, const std::wstring& source) {
+ LogMessage(LogLevel::Info, message, source);
+}
+
+void SimpleLogger::LogWarning(const std::wstring& message, const std::wstring& source) {
+ LogMessage(LogLevel::Warning, message, source);
+}
+
+void SimpleLogger::LogError(const std::wstring& message, const std::wstring& source) {
+ LogMessage(LogLevel::Error, message, source);
+}
+
+// ===== std::string overloads (new implementation) =====
+void SimpleLogger::LogMessage(LogLevel level, const std::string& message, const std::string& source) {
+ // Convert std::string to std::wstring
+ std::wstring wmessage(message.begin(), message.end());
+ std::wstring wsource(source.begin(), source.end());
+ LogMessage(level, wmessage, wsource);
+}
+
+void SimpleLogger::LogDebug(const std::string& message, const std::string& source) {
+ LogMessage(LogLevel::Debug, message, source);
+}
+
+void SimpleLogger::LogInfo(const std::string& message, const std::string& source) {
+ LogMessage(LogLevel::Info, message, source);
+}
+
+void SimpleLogger::LogWarning(const std::string& message, const std::string& source) {
+ LogMessage(LogLevel::Warning, message, source);
+}
+
+void SimpleLogger::LogError(const std::string& message, const std::string& source) {
+ LogMessage(LogLevel::Error, message, source);
+}
+
+// ===== printf-style variadic overloads (new implementation) =====
+void SimpleLogger::LogDebugF(const char* format, ...) {
+ va_list args;
+ va_start(args, format);
+
+ char buffer[4096];
+ vsnprintf(buffer, sizeof(buffer), format, args);
+
+ va_end(args);
+
+ LogDebug(std::string(buffer), "");
+}
+
+void SimpleLogger::LogInfoF(const char* format, ...) {
+ va_list args;
+ va_start(args, format);
+
+ char buffer[4096];
+ vsnprintf(buffer, sizeof(buffer), format, args);
+
+ va_end(args);
+
+ LogInfo(std::string(buffer), "");
+}
+
+void SimpleLogger::LogWarningF(const char* format, ...) {
+ va_list args;
+ va_start(args, format);
+
+ char buffer[4096];
+ vsnprintf(buffer, sizeof(buffer), format, args);
+
+ va_end(args);
+
+ LogWarning(std::string(buffer), "");
+}
+
+void SimpleLogger::LogErrorF(const char* format, ...) {
+ va_list args;
+ va_start(args, format);
+
+ char buffer[4096];
+ vsnprintf(buffer, sizeof(buffer), format, args);
+
+ va_end(args);
+
+ LogError(std::string(buffer), "");
+}
+
+// ===== Video-specific logging methods =====
+void SimpleLogger::LogVideoLoad(const std::wstring& filename, bool success) {
+ if (success) {
+ LogInfo(L"Video loaded: " + filename, L"VideoPlayer");
+ } else {
+ LogError(L"Failed to load video: " + filename, L"VideoPlayer");
+ }
+}
+
+void SimpleLogger::LogVideoPlay(const std::wstring& filename) {
+ LogInfo(L"Playing: " + filename, L"VideoPlayer");
+}
+
+void SimpleLogger::LogVideoPause(const std::wstring& filename) {
+ LogInfo(L"Paused: " + filename, L"VideoPlayer");
+}
+
+void SimpleLogger::LogVideoStop(const std::wstring& filename) {
+ LogInfo(L"Stopped: " + filename, L"VideoPlayer");
+}
+
+void SimpleLogger::LogVideoError(const std::wstring& error, const std::wstring& filename) {
+ std::wstring msg = error;
+ if (!filename.empty()) {
+ msg += L" (File: " + filename + L")";
+ }
+ LogError(msg, L"VideoPlayer");
+}
+
+void SimpleLogger::LogFrameInfo(int currentFrame, int totalFrames, double fps) {
+ std::wostringstream oss;
+ oss << L"Frame " << currentFrame << L"/" << totalFrames
+ << L" @ " << std::fixed << std::setprecision(1) << fps << L" FPS";
+ LogDebug(oss.str(), L"VideoPlayer");
+}
+
+void SimpleLogger::LogDecoderInfo(const std::wstring& decoderName, const std::wstring& info) {
+ LogInfo(decoderName + L": " + info, L"Decoder");
+}
+
+// ===== Private helper methods =====
+std::wstring SimpleLogger::GetLevelString(LogLevel level) {
+ switch (level) {
+ case LogLevel::Debug: return L"DEBUG";
+ case LogLevel::Info: return L"INFO";
+ case LogLevel::Warning: return L"WARN";
+ case LogLevel::Error: return L"ERROR";
+ default: return L"UNKNOWN";
+ }
+}
+
+void SimpleLogger::OutputToConsoleAndDebug(const std::wstring& logLine) {
+ // Output to console
+ std::wcout << logLine << std::endl;
+
+ // Output to Visual Studio Output window
+ OutputDebugStringW((logLine + L"\n").c_str());
+}
+
+} // namespace Vav2Player
diff --git a/vav2/platforms/windows/applications/vav2player/Vav2Player/src/Logger/SimpleLogger.h b/vav2/platforms/windows/applications/vav2player/Vav2Player/src/Logger/SimpleLogger.h
index f8a09c9..9e91329 100644
--- a/vav2/platforms/windows/applications/vav2player/Vav2Player/src/Logger/SimpleLogger.h
+++ b/vav2/platforms/windows/applications/vav2player/Vav2Player/src/Logger/SimpleLogger.h
@@ -1,11 +1,6 @@
#pragma once
#include
-#include
-#include
#include
-#include
-#include
-#include
namespace Vav2Player {
@@ -16,132 +11,87 @@ enum class LogLevel {
Error = 3
};
+/**
+ * Simple logger with support for both wstring and variadic arguments
+ * Thread-safe singleton logger for both console and WinUI3 environments
+ *
+ * Features:
+ * - wstring support (native WinUI3)
+ * - std::string support (native C++)
+ * - printf-style variadic arguments
+ * - OutputDebugString for Visual Studio
+ * - Console output for debugging
+ */
class SimpleLogger {
public:
- static SimpleLogger& GetInstance() {
- static SimpleLogger instance;
- return instance;
- }
+ static SimpleLogger& GetInstance();
- void SetLogLevel(LogLevel level) {
- std::lock_guard lock(m_mutex);
- m_currentLevel = level;
- }
+ void SetLogLevel(LogLevel level);
- void LogMessage(LogLevel level, const std::wstring& message, const std::wstring& source = L"") {
- if (static_cast(level) < static_cast(m_currentLevel)) {
- return; // Skip messages below current log level
- }
+ // ===== wstring overloads (original) =====
+ void LogMessage(LogLevel level, const std::wstring& message, const std::wstring& source = L"");
+ void LogDebug(const std::wstring& message, const std::wstring& source = L"");
+ void LogInfo(const std::wstring& message, const std::wstring& source = L"");
+ void LogWarning(const std::wstring& message, const std::wstring& source = L"");
+ void LogError(const std::wstring& message, const std::wstring& source = L"");
- std::lock_guard lock(m_mutex);
+ // ===== std::string overloads (new) =====
+ void LogMessage(LogLevel level, const std::string& message, const std::string& source = "");
+ void LogDebug(const std::string& message, const std::string& source = "");
+ void LogInfo(const std::string& message, const std::string& source = "");
+ void LogWarning(const std::string& message, const std::string& source = "");
+ void LogError(const std::string& message, const std::string& source = "");
- auto now = std::chrono::system_clock::now();
- auto time_t = std::chrono::system_clock::to_time_t(now);
- auto ms = std::chrono::duration_cast(now.time_since_epoch()) % 1000;
+ // ===== printf-style variadic overloads (new) =====
+ void LogDebugF(const char* format, ...);
+ void LogInfoF(const char* format, ...);
+ void LogWarningF(const char* format, ...);
+ void LogErrorF(const char* format, ...);
- std::tm tm;
- localtime_s(&tm, &time_t);
-
- std::wostringstream oss;
- oss << std::put_time(&tm, L"%H:%M:%S") << L"."
- << std::setfill(L'0') << std::setw(3) << ms.count()
- << L" [" << GetLevelString(level) << L"]";
-
- if (!source.empty()) {
- oss << L" (" << source << L")";
- }
-
- oss << L": " << message;
-
- std::wstring logLine = oss.str();
-
- // Output to console
- std::wcout << logLine << std::endl;
-
- // Also output to debug console in Windows
-#ifdef _WIN32
- OutputDebugStringW((logLine + L"\n").c_str());
-#endif
- }
-
- // Convenience methods
- void LogDebug(const std::wstring& message, const std::wstring& source = L"") {
- LogMessage(LogLevel::Debug, message, source);
- }
-
- void LogInfo(const std::wstring& message, const std::wstring& source = L"") {
- LogMessage(LogLevel::Info, message, source);
- }
-
- void LogWarning(const std::wstring& message, const std::wstring& source = L"") {
- LogMessage(LogLevel::Warning, message, source);
- }
-
- void LogError(const std::wstring& message, const std::wstring& source = L"") {
- LogMessage(LogLevel::Error, message, source);
- }
-
- // Video-specific logging methods
- void LogVideoLoad(const std::wstring& filename, bool success = true) {
- if (success) {
- LogInfo(L"Video loaded: " + filename, L"VideoPlayer");
- } else {
- LogError(L"Failed to load video: " + filename, L"VideoPlayer");
- }
- }
-
- void LogVideoPlay(const std::wstring& filename) {
- LogInfo(L"Playing: " + filename, L"VideoPlayer");
- }
-
- void LogVideoPause(const std::wstring& filename) {
- LogInfo(L"Paused: " + filename, L"VideoPlayer");
- }
-
- void LogVideoStop(const std::wstring& filename) {
- LogInfo(L"Stopped: " + filename, L"VideoPlayer");
- }
-
- void LogVideoError(const std::wstring& error, const std::wstring& filename = L"") {
- std::wstring msg = error;
- if (!filename.empty()) {
- msg += L" (File: " + filename + L")";
- }
- LogError(msg, L"VideoPlayer");
- }
-
- void LogFrameInfo(int currentFrame, int totalFrames, double fps) {
- std::wostringstream oss;
- oss << L"Frame " << currentFrame << L"/" << totalFrames
- << L" @ " << std::fixed << std::setprecision(1) << fps << L" FPS";
- LogDebug(oss.str(), L"VideoPlayer");
- }
-
- void LogDecoderInfo(const std::wstring& decoderName, const std::wstring& info) {
- LogInfo(decoderName + L": " + info, L"Decoder");
- }
+ // ===== Video-specific logging methods =====
+ void LogVideoLoad(const std::wstring& filename, bool success = true);
+ void LogVideoPlay(const std::wstring& filename);
+ void LogVideoPause(const std::wstring& filename);
+ void LogVideoStop(const std::wstring& filename);
+ void LogVideoError(const std::wstring& error, const std::wstring& filename = L"");
+ void LogFrameInfo(int currentFrame, int totalFrames, double fps);
+ void LogDecoderInfo(const std::wstring& decoderName, const std::wstring& info);
private:
- SimpleLogger() : m_currentLevel(LogLevel::Info) {}
+ SimpleLogger();
+ ~SimpleLogger() = default;
+ SimpleLogger(const SimpleLogger&) = delete;
+ SimpleLogger& operator=(const SimpleLogger&) = delete;
- std::wstring GetLevelString(LogLevel level) {
- switch (level) {
- case LogLevel::Debug: return L"DEBUG";
- case LogLevel::Info: return L"INFO";
- case LogLevel::Warning: return L"WARN";
- case LogLevel::Error: return L"ERROR";
- default: return L"UNKNOWN";
- }
- }
+ std::wstring GetLevelString(LogLevel level);
+ void OutputToConsoleAndDebug(const std::wstring& logLine);
std::mutex m_mutex;
LogLevel m_currentLevel;
};
-// Global convenience macros for easier usage
+// ===== Logging Macros =====
+// Use these macros instead of creating new logging functions
+
+// LOG_* : For std::wstring messages with optional source parameter
+// Usage: LOG_INFO(L"Video loaded", L"VideoPlayer")
#define LOG_DEBUG(msg, source) Vav2Player::SimpleLogger::GetInstance().LogDebug(msg, source)
#define LOG_INFO(msg, source) Vav2Player::SimpleLogger::GetInstance().LogInfo(msg, source)
#define LOG_WARNING(msg, source) Vav2Player::SimpleLogger::GetInstance().LogWarning(msg, source)
#define LOG_ERROR(msg, source) Vav2Player::SimpleLogger::GetInstance().LogError(msg, source)
-} // namespace Vav2Player
\ No newline at end of file
+// LOGF_* : For printf-style formatted messages (const char* format, ...)
+// Usage: LOGF_INFO("[Component] Value: %d", value)
+#define LOGF_DEBUG(...) Vav2Player::SimpleLogger::GetInstance().LogDebugF(__VA_ARGS__)
+#define LOGF_INFO(...) Vav2Player::SimpleLogger::GetInstance().LogInfoF(__VA_ARGS__)
+#define LOGF_WARNING(...) Vav2Player::SimpleLogger::GetInstance().LogWarningF(__VA_ARGS__)
+#define LOGF_ERROR(...) Vav2Player::SimpleLogger::GetInstance().LogErrorF(__VA_ARGS__)
+
+// LOGS_* : For std::string messages with optional source parameter
+// Usage: LOGS_INFO(errorMessage, "NetworkModule")
+#define LOGS_DEBUG(msg, source) Vav2Player::SimpleLogger::GetInstance().LogDebug(msg, source)
+#define LOGS_INFO(msg, source) Vav2Player::SimpleLogger::GetInstance().LogInfo(msg, source)
+#define LOGS_WARNING(msg, source) Vav2Player::SimpleLogger::GetInstance().LogWarning(msg, source)
+#define LOGS_ERROR(msg, source) Vav2Player::SimpleLogger::GetInstance().LogError(msg, source)
+
+} // namespace Vav2Player
diff --git a/vav2/platforms/windows/applications/vav2player/Vav2Player/src/Playback/FrameProcessor.cpp b/vav2/platforms/windows/applications/vav2player/Vav2Player/src/Playback/FrameProcessor.cpp
index 8651994..29b541d 100644
--- a/vav2/platforms/windows/applications/vav2player/Vav2Player/src/Playback/FrameProcessor.cpp
+++ b/vav2/platforms/windows/applications/vav2player/Vav2Player/src/Playback/FrameProcessor.cpp
@@ -1,5 +1,6 @@
#include "pch.h"
#include "FrameProcessor.h"
+#include "../Logger/SimpleLogger.h"
#include
#include
@@ -31,7 +32,7 @@ bool FrameProcessor::ProcessFrame(VavCorePlayer* player,
std::function onComplete)
{
if (!player || !m_renderer || !m_dispatcherQueue) {
- OutputDebugStringA("[FrameProcessor] Invalid state: missing player/renderer/dispatcherQueue\n");
+ LOGF_ERROR("[FrameProcessor] Invalid state: missing player/renderer/dispatcherQueue");
return false;
}
@@ -39,31 +40,27 @@ bool FrameProcessor::ProcessFrame(VavCorePlayer* player,
bool expected = false;
if (!m_frameProcessing.compare_exchange_strong(expected, true)) {
m_framesDropped++;
- char buf[256];
- sprintf_s(buf, "[FrameProcessor] Frame dropped (#%llu) - previous frame still processing (decoded: %llu)\n",
+ LOGF_INFO("[FrameProcessor] Frame dropped (#%llu) - previous frame still processing (decoded: %llu)",
m_framesDropped.load(), m_framesDecoded.load());
- OutputDebugStringA(buf);
return false;
}
- char buf2[256];
- sprintf_s(buf2, "[FrameProcessor] ProcessFrame START (decoded: %llu, dropped: %llu)\n",
+ LOGF_INFO("[FrameProcessor] ProcessFrame START (decoded: %llu, dropped: %llu)",
m_framesDecoded.load(), m_framesDropped.load());
- OutputDebugStringA(buf2);
// Get NV12 texture from renderer
- OutputDebugStringA("[FrameProcessor] Getting NV12 texture...\n");
+ LOGF_INFO("[FrameProcessor] Getting NV12 texture...");
ID3D12Resource* nv12Texture = m_renderer->GetNV12TextureForCUDAInterop();
if (!nv12Texture) {
- OutputDebugStringA("[FrameProcessor] ERROR: Failed to get NV12 texture - clearing flag\n");
+ LOGF_ERROR("[FrameProcessor] Failed to get NV12 texture - clearing flag");
m_frameProcessing.store(false);
if (onComplete) onComplete(false);
return false;
}
- OutputDebugStringA("[FrameProcessor] NV12 texture acquired\n");
+ LOGF_INFO("[FrameProcessor] NV12 texture acquired");
// Decode frame to D3D12 surface (blocking)
- OutputDebugStringA("[FrameProcessor] Starting vavcore_decode_to_surface (BLOCKING)...\n");
+ LOGF_INFO("[FrameProcessor] Starting vavcore_decode_to_surface (BLOCKING)...");
VavCoreVideoFrame vavFrame = {};
VavCoreResult result = vavcore_decode_to_surface(
player,
@@ -71,16 +68,14 @@ bool FrameProcessor::ProcessFrame(VavCorePlayer* player,
nv12Texture,
&vavFrame
);
- OutputDebugStringA("[FrameProcessor] vavcore_decode_to_surface COMPLETED\n");
+ LOGF_INFO("[FrameProcessor] vavcore_decode_to_surface COMPLETED");
if (result != VAVCORE_SUCCESS) {
if (result != VAVCORE_END_OF_STREAM) {
m_decodeErrors++;
- char errbuf[256];
- sprintf_s(errbuf, "[FrameProcessor] Decode ERROR: result=%d - clearing flag\n", result);
- OutputDebugStringA(errbuf);
+ LOGF_ERROR("[FrameProcessor] Decode ERROR: result=%d - clearing flag", result);
} else {
- OutputDebugStringA("[FrameProcessor] End of stream reached - clearing flag\n");
+ LOGF_INFO("[FrameProcessor] End of stream reached - clearing flag");
}
m_frameProcessing.store(false);
if (onComplete) onComplete(false);
@@ -88,47 +83,43 @@ bool FrameProcessor::ProcessFrame(VavCorePlayer* player,
}
m_framesDecoded++;
- OutputDebugStringA("[FrameProcessor] Decode SUCCESS - frame decoded\n");
+ LOGF_INFO("[FrameProcessor] Decode SUCCESS - frame decoded");
// Enqueue render on UI thread with fence value for GPU sync
uint64_t fenceValue = vavFrame.sync_fence_value;
- char fencebuf[256];
- sprintf_s(fencebuf, "[FrameProcessor] Attempting to enqueue render (fenceValue=%llu)...\n", fenceValue);
- OutputDebugStringA(fencebuf);
+ LOGF_INFO("[FrameProcessor] Attempting to enqueue render (fenceValue=%llu)...", fenceValue);
bool enqueued = m_dispatcherQueue.TryEnqueue([this, fenceValue, onComplete]() {
- OutputDebugStringA("[FrameProcessor] *** UI THREAD CALLBACK STARTED ***\n");
+ LOGF_INFO("[FrameProcessor] *** UI THREAD CALLBACK STARTED ***");
HRESULT hr = m_renderer->RenderNV12TextureToBackBuffer(fenceValue);
bool renderSuccess = SUCCEEDED(hr);
if (!renderSuccess) {
m_renderErrors++;
- char buf[256];
- sprintf_s(buf, "[FrameProcessor] Render error: HRESULT = 0x%08X\n", hr);
- OutputDebugStringA(buf);
+ LOGF_ERROR("[FrameProcessor] Render error: HRESULT = 0x%08X", hr);
} else {
- OutputDebugStringA("[FrameProcessor] Render succeeded\n");
+ LOGF_INFO("[FrameProcessor] Render succeeded");
}
// Mark frame processing complete
- OutputDebugStringA("[FrameProcessor] CLEARING m_frameProcessing flag\n");
+ LOGF_INFO("[FrameProcessor] CLEARING m_frameProcessing flag");
m_frameProcessing.store(false);
- OutputDebugStringA("[FrameProcessor] Flag cleared - ready for next frame\n");
+ LOGF_INFO("[FrameProcessor] Flag cleared - ready for next frame");
if (onComplete) {
onComplete(renderSuccess);
}
- OutputDebugStringA("[FrameProcessor] *** UI THREAD CALLBACK ENDED ***\n");
+ LOGF_INFO("[FrameProcessor] *** UI THREAD CALLBACK ENDED ***");
});
if (!enqueued) {
- OutputDebugStringA("[FrameProcessor] ERROR: TryEnqueue FAILED - clearing flag\n");
+ LOGF_ERROR("[FrameProcessor] TryEnqueue FAILED - clearing flag");
m_frameProcessing.store(false);
if (onComplete) onComplete(false);
return false;
}
- OutputDebugStringA("[FrameProcessor] TryEnqueue SUCCESS - callback queued, ProcessFrame returning true\n");
+ LOGF_INFO("[FrameProcessor] TryEnqueue SUCCESS - callback queued, ProcessFrame returning true");
return true;
}
diff --git a/vav2/platforms/windows/applications/vav2player/Vav2Player/src/Playback/PlaybackController.cpp b/vav2/platforms/windows/applications/vav2player/Vav2Player/src/Playback/PlaybackController.cpp
index fe03704..8bdffa8 100644
--- a/vav2/platforms/windows/applications/vav2player/Vav2Player/src/Playback/PlaybackController.cpp
+++ b/vav2/platforms/windows/applications/vav2player/Vav2Player/src/Playback/PlaybackController.cpp
@@ -1,7 +1,8 @@
#include "pch.h"
#include "PlaybackController.h"
+#include "../Logger/SimpleLogger.h"
#include
-#include
+#include
namespace Vav2Player {
@@ -9,12 +10,12 @@ PlaybackController::PlaybackController()
{
// NOTE: VavCore is initialized globally in App.xaml.cpp
// Do NOT call vavcore_initialize() here to avoid duplicate initialization
- OutputDebugStringA("[PlaybackController] Constructor called (VavCore already initialized)\n");
+ LOGF_INFO("[PlaybackController] Constructor called (VavCore already initialized)");
}
PlaybackController::~PlaybackController()
{
- OutputDebugStringA("[PlaybackController] Destructor called\n");
+ LOGF_INFO("[PlaybackController] Destructor called");
Stop();
Unload();
// NOTE: VavCore cleanup is handled globally in App.xaml.cpp
@@ -25,18 +26,18 @@ bool PlaybackController::InitializeVavCore()
{
VavCoreResult result = vavcore_initialize();
if (result != VAVCORE_SUCCESS) {
- std::cerr << "[PlaybackController] Failed to initialize VavCore: " << result << std::endl;
+ LOGF_ERROR("[PlaybackController] Failed to initialize VavCore: %d", result);
return false;
}
- std::cout << "[PlaybackController] VavCore initialized" << std::endl;
+ LOGF_INFO("[PlaybackController] VavCore initialized");
return true;
}
void PlaybackController::CleanupVavCore()
{
vavcore_cleanup();
- std::cout << "[PlaybackController] VavCore cleaned up" << std::endl;
+ LOGF_INFO("[PlaybackController] VavCore cleaned up");
}
void PlaybackController::SetD3DDevice(void* d3d_device, VavCoreSurfaceType type)
@@ -47,54 +48,52 @@ void PlaybackController::SetD3DDevice(void* d3d_device, VavCoreSurfaceType type)
bool PlaybackController::LoadVideo(const std::wstring& filePath)
{
- std::cout << "[PlaybackController] LoadVideo START" << std::endl;
+ LOGF_INFO("[PlaybackController] LoadVideo START");
// Unload previous video if any
if (m_isLoaded) {
- std::cout << "[PlaybackController] Unloading previous video..." << std::endl;
+ LOGF_INFO("[PlaybackController] Unloading previous video...");
Unload();
}
// Create VavCore player
- std::cout << "[PlaybackController] Creating VavCore player..." << std::endl;
+ LOGF_INFO("[PlaybackController] Creating VavCore player...");
m_vavCorePlayer = vavcore_create_player();
if (!m_vavCorePlayer) {
- std::cerr << "[PlaybackController] ERROR: Failed to create VavCore player" << std::endl;
+ LOGF_ERROR("[PlaybackController] Failed to create VavCore player");
return false;
}
- std::cout << "[PlaybackController] VavCore player created successfully" << std::endl;
+ LOGF_INFO("[PlaybackController] VavCore player created successfully");
// Set decoder type before opening file
- std::cout << "[PlaybackController] Setting decoder type: " << m_decoderType << std::endl;
+ LOGF_INFO("[PlaybackController] Setting decoder type: %d", m_decoderType);
vavcore_set_decoder_type(m_vavCorePlayer, m_decoderType);
// Set D3D device before opening file, if it was provided
if (m_d3dDevice) {
- std::cout << "[PlaybackController] Setting D3D12 device (surface type: " << m_d3dSurfaceType << ")" << std::endl;
+ LOGF_INFO("[PlaybackController] Setting D3D12 device (surface type: %d)", m_d3dSurfaceType);
vavcore_set_d3d_device(m_vavCorePlayer, m_d3dDevice, m_d3dSurfaceType);
} else {
- std::cout << "[PlaybackController] No D3D12 device to set (will use CPU decoding)" << std::endl;
+ LOGF_INFO("[PlaybackController] No D3D12 device to set (will use CPU decoding)");
}
// Convert wstring to UTF-8 string
- std::cout << "[PlaybackController] Converting file path to UTF-8..." << std::endl;
int size_needed = WideCharToMultiByte(CP_UTF8, 0, filePath.c_str(), -1, nullptr, 0, nullptr, nullptr);
std::string utf8FilePath(size_needed - 1, 0);
WideCharToMultiByte(CP_UTF8, 0, filePath.c_str(), -1, &utf8FilePath[0], size_needed, nullptr, nullptr);
- std::cout << "[PlaybackController] UTF-8 path: " << utf8FilePath << std::endl;
// Open video file (this will initialize the decoder)
- std::cout << "[PlaybackController] Calling vavcore_open_file..." << std::endl;
+ LOGF_INFO("[PlaybackController] Calling vavcore_open_file...");
VavCoreResult result = vavcore_open_file(m_vavCorePlayer, utf8FilePath.c_str());
- std::cout << "[PlaybackController] vavcore_open_file returned: " << result << std::endl;
+ LOGF_INFO("[PlaybackController] vavcore_open_file returned: %d", result);
if (result != VAVCORE_SUCCESS) {
- std::cerr << "[PlaybackController] ERROR: Failed to open file with result code: " << result << std::endl;
+ LOGF_ERROR("[PlaybackController] Failed to open file with result code: %d", result);
vavcore_destroy_player(m_vavCorePlayer);
m_vavCorePlayer = nullptr;
return false;
}
- std::cout << "[PlaybackController] File opened successfully" << std::endl;
+ LOGF_INFO("[PlaybackController] File opened successfully");
// Get video metadata
VavCoreVideoMetadata metadata;
@@ -106,10 +105,8 @@ bool PlaybackController::LoadVideo(const std::wstring& filePath)
m_totalFrames = metadata.total_frames;
m_duration = metadata.duration_seconds;
- std::cout << "[PlaybackController] Video loaded: "
- << m_videoWidth << "x" << m_videoHeight
- << " @ " << m_frameRate << "fps"
- << ", Duration: " << m_duration << "s" << std::endl;
+ LOGF_INFO("[PlaybackController] Video loaded: %dx%d @ %.1ffps, Duration: %.1fs",
+ m_videoWidth, m_videoHeight, m_frameRate, m_duration);
}
m_currentFilePath = filePath;
@@ -144,18 +141,18 @@ void PlaybackController::Unload()
m_currentFilePath.clear();
m_isLoaded = false;
- std::cout << "[PlaybackController] Video unloaded" << std::endl;
+ LOGF_INFO("[PlaybackController] Video unloaded");
}
void PlaybackController::Play(std::function onFrameReady)
{
if (!m_isLoaded) {
- std::cerr << "[PlaybackController] Cannot play: video not loaded" << std::endl;
+ LOGF_ERROR("[PlaybackController] Cannot play: video not loaded");
return;
}
if (m_isPlaying) {
- std::cout << "[PlaybackController] Already playing" << std::endl;
+ LOGF_INFO("[PlaybackController] Already playing");
return;
}
@@ -164,7 +161,7 @@ void PlaybackController::Play(std::function onFrameReady)
StartTimingThread();
- std::cout << "[PlaybackController] Playback started" << std::endl;
+ LOGF_INFO("[PlaybackController] Playback started");
}
void PlaybackController::Pause()
@@ -174,7 +171,7 @@ void PlaybackController::Pause()
}
m_isPlaying = false;
- std::cout << "[PlaybackController] Playback paused" << std::endl;
+ LOGF_INFO("[PlaybackController] Playback paused");
}
void PlaybackController::Stop()
@@ -189,7 +186,7 @@ void PlaybackController::Stop()
m_currentFrame = 0;
m_currentTime = 0.0;
- std::cout << "[PlaybackController] Playback stopped" << std::endl;
+ LOGF_INFO("[PlaybackController] Playback stopped");
}
void PlaybackController::Seek(double timeSeconds)
@@ -201,9 +198,9 @@ void PlaybackController::Seek(double timeSeconds)
VavCoreResult result = vavcore_seek_to_time(m_vavCorePlayer, timeSeconds);
if (result == VAVCORE_SUCCESS) {
m_currentTime = timeSeconds;
- std::cout << "[PlaybackController] Seeked to " << timeSeconds << "s" << std::endl;
+ LOGF_INFO("[PlaybackController] Seeked to %.2fs", timeSeconds);
} else {
- std::cerr << "[PlaybackController] Seek failed: " << result << std::endl;
+ LOGF_ERROR("[PlaybackController] Seek failed: %d", result);
}
}
@@ -213,7 +210,7 @@ void PlaybackController::SetDecoderType(VavCoreDecoderType type)
if (m_vavCorePlayer) {
vavcore_set_decoder_type(m_vavCorePlayer, type);
- std::cout << "[PlaybackController] Decoder type set to " << static_cast(type) << std::endl;
+ LOGF_INFO("[PlaybackController] Decoder type set to %d", static_cast(type));
}
}
@@ -229,7 +226,7 @@ void PlaybackController::StartTimingThread()
TimingThreadLoop();
});
- std::cout << "[PlaybackController] Timing thread started" << std::endl;
+ LOGF_INFO("[PlaybackController] Timing thread started");
}
void PlaybackController::StopTimingThread()
@@ -246,7 +243,7 @@ void PlaybackController::StopTimingThread()
m_timingThread.reset();
- std::cout << "[PlaybackController] Timing thread stopped" << std::endl;
+ LOGF_INFO("[PlaybackController] Timing thread stopped");
}
void PlaybackController::TimingThreadLoop()
@@ -270,7 +267,7 @@ void PlaybackController::TimingThreadLoop()
std::this_thread::sleep_until(nextFrame);
}
- std::cout << "[PlaybackController] Timing thread loop exited" << std::endl;
+ LOGF_INFO("[PlaybackController] Timing thread loop exited");
}
} // namespace Vav2Player
diff --git a/vav2/platforms/windows/applications/vav2player/Vav2Player/src/Rendering/D3D12VideoRenderer.cpp b/vav2/platforms/windows/applications/vav2player/Vav2Player/src/Rendering/D3D12VideoRenderer.cpp
index a01cfb8..ca445a5 100644
--- a/vav2/platforms/windows/applications/vav2player/Vav2Player/src/Rendering/D3D12VideoRenderer.cpp
+++ b/vav2/platforms/windows/applications/vav2player/Vav2Player/src/Rendering/D3D12VideoRenderer.cpp
@@ -8,6 +8,7 @@
#include
#include
#include
+#include "../Logger/SimpleLogger.h"
namespace Vav2Player {
@@ -1332,9 +1333,8 @@ HRESULT D3D12VideoRenderer::CreateRingBuffers(uint32_t yWidth, uint32_t yHeight,
UINT optimalBufferCount = CalculateOptimalBufferCount(yWidth * 2, yHeight); // Use Y plane dimensions
if (optimalBufferCount != m_dynamicRingBufferCount) {
- std::cout << "[D3D12VideoRenderer] Adjusting ring buffer count from "
- << m_dynamicRingBufferCount << " to " << optimalBufferCount
- << " for " << yWidth * 2 << "x" << yHeight << " video" << std::endl;
+ LOGF_INFO("[D3D12VideoRenderer] Adjusting ring buffer count from %d to %d for %dx%d video",
+ m_dynamicRingBufferCount, optimalBufferCount, yWidth * 2, yHeight);
hr = ResizeRingBuffers(optimalBufferCount, yWidth, yHeight, uvWidth, uvHeight);
if (FAILED(hr)) return hr;
@@ -1666,7 +1666,7 @@ HRESULT D3D12VideoRenderer::ExecuteRingBufferTextureUpdate(uint32_t bufferIndex)
// Check if YUV textures are initialized before creating barriers
if (!m_yTexture || !m_uTexture || !m_vTexture) {
- std::cout << "[D3D12VideoRenderer] ERROR: YUV textures not initialized. Skipping ResourceBarrier." << std::endl;
+ LOGF_ERROR("[D3D12VideoRenderer] YUV textures not initialized. Skipping ResourceBarrier.");
return E_FAIL;
}
@@ -2511,9 +2511,8 @@ UINT D3D12VideoRenderer::CalculateOptimalBufferCount(uint32_t videoWidth, uint32
optimalCount = std::max(MIN_RING_BUFFER_COUNT,
std::min(MAX_RING_BUFFER_COUNT, optimalCount));
- std::cout << "[DynamicRingBuffer] Calculated optimal buffer count: " << optimalCount
- << " for " << videoWidth << "x" << videoHeight
- << " (frame size: " << frameSize / 1024 << "KB)" << std::endl;
+ LOGF_INFO("[DynamicRingBuffer] Calculated optimal buffer count: %d for %dx%d (frame size: %zuKB)",
+ optimalCount, videoWidth, videoHeight, frameSize / 1024);
return optimalCount;
}
@@ -2543,9 +2542,9 @@ UINT64 D3D12VideoRenderer::GetAvailableVideoMemory()
// Return 80% of dedicated video memory as available
UINT64 availableMemory = static_cast(adapterDesc.DedicatedVideoMemory * 0.8);
- std::cout << "[DynamicRingBuffer] Detected video memory: "
- << adapterDesc.DedicatedVideoMemory / (1024 * 1024) << "MB, "
- << "Available: " << availableMemory / (1024 * 1024) << "MB" << std::endl;
+ LOGF_INFO("[DynamicRingBuffer] Detected video memory: %lluMB, Available: %lluMB",
+ adapterDesc.DedicatedVideoMemory / (1024 * 1024),
+ availableMemory / (1024 * 1024));
return availableMemory;
}
@@ -2576,8 +2575,8 @@ HRESULT D3D12VideoRenderer::ResizeRingBuffers(UINT newBufferCount, uint32_t yWid
// Reset current buffer index
m_currentBufferIndex = 0;
- std::cout << "[DynamicRingBuffer] Resized ring buffer array to " << newBufferCount
- << " buffers for " << yWidth * 2 << "x" << yHeight << " video" << std::endl;
+ LOGF_INFO("[DynamicRingBuffer] Resized ring buffer array to %d buffers for %dx%d video",
+ newBufferCount, yWidth * 2, yHeight);
return S_OK;
}
diff --git a/vav2/platforms/windows/applications/vav2player/Vav2Player/src/Rendering/SimpleGPURenderer.cpp b/vav2/platforms/windows/applications/vav2player/Vav2Player/src/Rendering/SimpleGPURenderer.cpp
index 291972a..4894418 100644
--- a/vav2/platforms/windows/applications/vav2player/Vav2Player/src/Rendering/SimpleGPURenderer.cpp
+++ b/vav2/platforms/windows/applications/vav2player/Vav2Player/src/Rendering/SimpleGPURenderer.cpp
@@ -1,7 +1,7 @@
#include "pch.h"
#include "SimpleGPURenderer.h"
+#include "../Logger/SimpleLogger.h"
#include
-#include
#pragma comment(lib, "d3d12.lib")
#pragma comment(lib, "dxgi.lib")
@@ -248,7 +248,7 @@ HRESULT SimpleGPURenderer::Present()
HRESULT hr = m_swapChain->Present(1, 0);
if (FAILED(hr))
{
- std::cout << "[SimpleGPURenderer] Present failed: 0x" << std::hex << hr << std::endl;
+ LOGF_ERROR("[SimpleGPURenderer] Present failed: 0x%x", hr);
return hr;
}
@@ -272,7 +272,7 @@ HRESULT SimpleGPURenderer::CreateDevice()
HRESULT hr = D3D12CreateDevice(nullptr, D3D_FEATURE_LEVEL_11_0, IID_PPV_ARGS(&m_device));
if (FAILED(hr))
{
- std::cout << "[SimpleGPURenderer] Failed to create D3D12 device: 0x" << std::hex << hr << std::endl;
+ LOGF_ERROR("[SimpleGPURenderer] Failed to create D3D12 device: 0x%x", hr);
return hr;
}
@@ -288,7 +288,7 @@ HRESULT SimpleGPURenderer::CreateCommandQueue()
HRESULT hr = m_device->CreateCommandQueue(&queueDesc, IID_PPV_ARGS(&m_commandQueue));
if (FAILED(hr))
{
- std::cout << "[SimpleGPURenderer] Failed to create command queue: 0x" << std::hex << hr << std::endl;
+ LOGF_ERROR("[SimpleGPURenderer] Failed to create command queue: 0x%x", hr);
return hr;
}
@@ -352,7 +352,7 @@ HRESULT SimpleGPURenderer::CreateDescriptorHeaps()
HRESULT hr = m_device->CreateDescriptorHeap(&rtvHeapDesc, IID_PPV_ARGS(&m_rtvHeap));
if (FAILED(hr))
{
- std::cout << "[SimpleGPURenderer] Failed to create RTV heap: 0x" << std::hex << hr << std::endl;
+ LOGF_ERROR("[SimpleGPURenderer] Failed to create RTV heap: 0x%x", hr);
return hr;
}
@@ -367,7 +367,7 @@ HRESULT SimpleGPURenderer::CreateDescriptorHeaps()
hr = m_device->CreateDescriptorHeap(&srvUavHeapDesc, IID_PPV_ARGS(&m_srvUavHeap));
if (FAILED(hr))
{
- std::cout << "[SimpleGPURenderer] Failed to create SRV/UAV heap: 0x" << std::hex << hr << std::endl;
+ LOGF_ERROR("[SimpleGPURenderer] Failed to create SRV/UAV heap: 0x%x", hr);
return hr;
}
@@ -386,7 +386,7 @@ HRESULT SimpleGPURenderer::CreateRenderTargets()
HRESULT hr = m_swapChain->GetBuffer(i, IID_PPV_ARGS(&m_renderTargets[i]));
if (FAILED(hr))
{
- std::cout << "[SimpleGPURenderer] Failed to get swap chain buffer " << i << ": 0x" << std::hex << hr << std::endl;
+ LOGF_ERROR("[SimpleGPURenderer] Failed to get swap chain buffer %d: 0x%x", i, hr);
return hr;
}
@@ -403,7 +403,7 @@ HRESULT SimpleGPURenderer::CreateSynchronizationObjects()
HRESULT hr = m_device->CreateFence(0, D3D12_FENCE_FLAG_NONE, IID_PPV_ARGS(&m_fence));
if (FAILED(hr))
{
- std::cout << "[SimpleGPURenderer] Failed to create fence: 0x" << std::hex << hr << std::endl;
+ LOGF_ERROR("[SimpleGPURenderer] Failed to create fence: 0x%x", hr);
return hr;
}
@@ -417,7 +417,7 @@ HRESULT SimpleGPURenderer::CreateSynchronizationObjects()
hr = m_device->CreateCommandAllocator(D3D12_COMMAND_LIST_TYPE_DIRECT, IID_PPV_ARGS(&m_commandAllocators[i]));
if (FAILED(hr))
{
- std::cout << "[SimpleGPURenderer] Failed to create command allocator " << i << ": 0x" << std::hex << hr << std::endl;
+ LOGF_ERROR("[SimpleGPURenderer] Failed to create command allocator %d: 0x%x", i, hr);
return hr;
}
}
@@ -426,7 +426,7 @@ HRESULT SimpleGPURenderer::CreateSynchronizationObjects()
hr = m_device->CreateCommandList(0, D3D12_COMMAND_LIST_TYPE_DIRECT, m_commandAllocators[0].Get(), nullptr, IID_PPV_ARGS(&m_commandList));
if (FAILED(hr))
{
- std::cout << "[SimpleGPURenderer] Failed to create command list: 0x" << std::hex << hr << std::endl;
+ LOGF_ERROR("[SimpleGPURenderer] Failed to create command list: 0x%x", hr);
return hr;
}
@@ -434,7 +434,7 @@ HRESULT SimpleGPURenderer::CreateSynchronizationObjects()
hr = m_commandList->Close();
if (FAILED(hr))
{
- std::cout << "[SimpleGPURenderer] Failed to close initial command list: 0x" << std::hex << hr << std::endl;
+ LOGF_ERROR("[SimpleGPURenderer] Failed to close initial command list: 0x%x", hr);
return hr;
}
@@ -478,7 +478,7 @@ HRESULT SimpleGPURenderer::CreateComputeShaderResources()
D3D12_RESOURCE_STATE_GENERIC_READ, nullptr, IID_PPV_ARGS(&m_constantBuffer));
if (FAILED(hr))
{
- std::cout << "[SimpleGPURenderer] Failed to create constant buffer: 0x" << std::hex << hr << std::endl;
+ LOGF_ERROR("[SimpleGPURenderer] Failed to create constant buffer: 0x%x", hr);
return hr;
}
@@ -517,7 +517,7 @@ HRESULT SimpleGPURenderer::CreateVideoTextures(uint32_t videoWidth, uint32_t vid
D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE, nullptr, IID_PPV_ARGS(&m_yTextures[i]));
if (FAILED(hr))
{
- std::cout << "[SimpleGPURenderer] Failed to create Y texture " << i << ": 0x" << std::hex << hr << std::endl;
+ LOGF_ERROR("[SimpleGPURenderer] Failed to create Y texture %d: 0x%x", i, hr);
return hr;
}
@@ -528,7 +528,7 @@ HRESULT SimpleGPURenderer::CreateVideoTextures(uint32_t videoWidth, uint32_t vid
D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE, nullptr, IID_PPV_ARGS(&m_uTextures[i]));
if (FAILED(hr))
{
- std::cout << "[SimpleGPURenderer] Failed to create U texture " << i << ": 0x" << std::hex << hr << std::endl;
+ LOGF_ERROR("[SimpleGPURenderer] Failed to create U texture %d: 0x%x", i, hr);
return hr;
}
@@ -537,7 +537,7 @@ HRESULT SimpleGPURenderer::CreateVideoTextures(uint32_t videoWidth, uint32_t vid
D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE, nullptr, IID_PPV_ARGS(&m_vTextures[i]));
if (FAILED(hr))
{
- std::cout << "[SimpleGPURenderer] Failed to create V texture " << i << ": 0x" << std::hex << hr << std::endl;
+ LOGF_ERROR("[SimpleGPURenderer] Failed to create V texture %d: 0x%x", i, hr);
return hr;
}
}
@@ -557,7 +557,7 @@ HRESULT SimpleGPURenderer::CreateVideoTextures(uint32_t videoWidth, uint32_t vid
D3D12_RESOURCE_STATE_UNORDERED_ACCESS, nullptr, IID_PPV_ARGS(&m_rgbTextures[i]));
if (FAILED(hr))
{
- std::cout << "[SimpleGPURenderer] Failed to create RGB texture " << i << ": 0x" << std::hex << hr << std::endl;
+ LOGF_ERROR("[SimpleGPURenderer] Failed to create RGB texture %d: 0x%x", i, hr);
return hr;
}
}
@@ -584,7 +584,7 @@ HRESULT SimpleGPURenderer::CreateVideoTextures(uint32_t videoWidth, uint32_t vid
D3D12_RESOURCE_STATE_GENERIC_READ, nullptr, IID_PPV_ARGS(&m_yUploadBuffers[i]));
if (FAILED(hr))
{
- std::cout << "[SimpleGPURenderer] Failed to create Y upload buffer " << i << ": 0x" << std::hex << hr << std::endl;
+ LOGF_ERROR("[SimpleGPURenderer] Failed to create Y upload buffer %d: 0x%x", i, hr);
return hr;
}
@@ -641,7 +641,7 @@ HRESULT SimpleGPURenderer::CreateVideoTextures(uint32_t videoWidth, uint32_t vid
}
else
{
- std::cout << "[SimpleGPURenderer] Warning: SRV/UAV heap not available for descriptor creation" << std::endl;
+ LOGF_INFO("[SimpleGPURenderer] Warning: SRV/UAV heap not available for descriptor creation");
}
return S_OK;
@@ -650,7 +650,7 @@ HRESULT SimpleGPURenderer::CreateVideoTextures(uint32_t videoWidth, uint32_t vid
HRESULT SimpleGPURenderer::CreateNV12TextureR8Layout(uint32_t videoWidth, uint32_t videoHeight)
{
if (!m_device) {
- std::cout << "[SimpleGPURenderer::CreateNV12Texture] ERROR: Device not initialized" << std::endl;
+ LOGF_INFO("[SimpleGPURenderer::CreateNV12Texture] ERROR: Device not initialized");
return E_FAIL;
}
@@ -680,38 +680,38 @@ HRESULT SimpleGPURenderer::CreateNV12TextureR8Layout(uint32_t videoWidth, uint32
UINT64 requiredBytes = 0;
m_device->GetCopyableFootprints(&nv12TextureDesc, 0, 2, 0, layouts, numRows, rowSizes, &requiredBytes);
- std::cout << "[SimpleGPURenderer::CreateNV12Texture] GetCopyableFootprints requires: "
+ LOGF_INFO("[SimpleGPURenderer::CreateNV12Texture] GetCopyableFootprints requires: ");
<< requiredBytes << " bytes" << std::endl;
// Query actual allocation size for this descriptor
D3D12_RESOURCE_ALLOCATION_INFO allocInfo = m_device->GetResourceAllocationInfo(0, 1, &nv12TextureDesc);
- std::cout << "[SimpleGPURenderer::CreateNV12Texture] GetResourceAllocationInfo returns: "
+ LOGF_INFO("[SimpleGPURenderer::CreateNV12Texture] GetResourceAllocationInfo returns: ");
<< allocInfo.SizeInBytes << " bytes" << std::endl;
// If allocation is too small, increase texture height until we have enough space
if (allocInfo.SizeInBytes < requiredBytes)
{
- std::cout << "[SimpleGPURenderer::CreateNV12Texture] Allocation too small, increasing height..." << std::endl;
+ LOGF_INFO("[SimpleGPURenderer::CreateNV12Texture] Allocation too small, increasing height...");
for (UINT extraHeight = 64; extraHeight <= videoHeight; extraHeight += 64)
{
nv12TextureDesc.Height = videoHeight + extraHeight;
allocInfo = m_device->GetResourceAllocationInfo(0, 1, &nv12TextureDesc);
- std::cout << "[SimpleGPURenderer::CreateNV12Texture] Testing height " << nv12TextureDesc.Height
+ LOGF_INFO("[SimpleGPURenderer::CreateNV12Texture] Testing height %d", nv12TextureDesc.Height);
<< ": " << allocInfo.SizeInBytes << " bytes" << std::endl;
if (allocInfo.SizeInBytes >= requiredBytes)
{
- std::cout << "[SimpleGPURenderer::CreateNV12Texture] Found sufficient height: "
+ LOGF_INFO("[SimpleGPURenderer::CreateNV12Texture] Found sufficient height: ");
<< nv12TextureDesc.Height << std::endl;
break;
}
}
}
- std::cout << "[SimpleGPURenderer::CreateNV12Texture] Final NV12 texture: " << videoWidth << "x"
+ LOGF_INFO("[SimpleGPURenderer::CreateNV12Texture] Final NV12 texture: %dx", videoWidth);
<< nv12TextureDesc.Height << ", allocation: " << allocInfo.SizeInBytes << " bytes" << std::endl;
D3D12_HEAP_PROPERTIES sharedHeapProps = {};
@@ -728,12 +728,12 @@ HRESULT SimpleGPURenderer::CreateNV12TextureR8Layout(uint32_t videoWidth, uint32
if (FAILED(hr))
{
- std::cout << "[SimpleGPURenderer::CreateNV12Texture] Failed to create NV12 shared texture: 0x"
+ LOGF_ERROR("[SimpleGPURenderer::CreateNV12Texture] Failed to create NV12 shared texture: 0x");
<< std::hex << hr << std::endl;
return hr;
}
- std::cout << "[SimpleGPURenderer::CreateNV12Texture] Created NV12 shared texture ("
+ LOGF_INFO("[SimpleGPURenderer::CreateNV12Texture] Created NV12 shared texture (");
<< videoWidth << "x" << videoHeight << ") for VavCore zero-copy decode" << std::endl;
return S_OK;
@@ -824,7 +824,7 @@ float4 PSMain(PSInput input) : SV_TARGET
"VSMain", "vs_5_0", compileFlags, 0, &m_nv12VertexShaderBlob, &errorBlob);
if (FAILED(hr)) {
if (errorBlob) {
- std::cout << "[SimpleGPURenderer] NV12 Vertex shader compilation error: "
+ LOGF_INFO("[SimpleGPURenderer] NV12 Vertex shader compilation error: ");
<< (char*)errorBlob->GetBufferPointer() << std::endl;
}
return hr;
@@ -835,13 +835,13 @@ float4 PSMain(PSInput input) : SV_TARGET
"PSMain", "ps_5_0", compileFlags, 0, &m_nv12PixelShaderBlob, &errorBlob);
if (FAILED(hr)) {
if (errorBlob) {
- std::cout << "[SimpleGPURenderer] NV12 Pixel shader compilation error: "
+ LOGF_INFO("[SimpleGPURenderer] NV12 Pixel shader compilation error: ");
<< (char*)errorBlob->GetBufferPointer() << std::endl;
}
return hr;
}
- std::cout << "[SimpleGPURenderer] NV12 shaders compiled successfully" << std::endl;
+ LOGF_INFO("[SimpleGPURenderer] NV12 shaders compiled successfully");
return S_OK;
}
@@ -900,7 +900,7 @@ HRESULT SimpleGPURenderer::CreateNV12RootSignature()
HRESULT hr = D3D12SerializeVersionedRootSignature(&rootSigDesc, &signature, &error);
if (FAILED(hr)) {
if (error) {
- std::cout << "[SimpleGPURenderer] NV12 Root signature serialization error: "
+ LOGF_INFO("[SimpleGPURenderer] NV12 Root signature serialization error: ");
<< (char*)error->GetBufferPointer() << std::endl;
}
return hr;
@@ -909,7 +909,7 @@ HRESULT SimpleGPURenderer::CreateNV12RootSignature()
hr = m_device->CreateRootSignature(0, signature->GetBufferPointer(), signature->GetBufferSize(),
IID_PPV_ARGS(&m_nv12RootSignature));
if (FAILED(hr)) {
- std::cout << "[SimpleGPURenderer] Failed to create NV12 root signature: 0x" << std::hex << hr << std::endl;
+ LOGF_ERROR("[SimpleGPURenderer] Failed to create NV12 root signature: 0x%x", hr);
return hr;
}
@@ -935,11 +935,11 @@ HRESULT SimpleGPURenderer::CreateNV12PipelineState()
HRESULT hr = m_device->CreateGraphicsPipelineState(&psoDesc, IID_PPV_ARGS(&m_nv12PipelineState));
if (FAILED(hr)) {
- std::cout << "[SimpleGPURenderer] Failed to create NV12 pipeline state: 0x" << std::hex << hr << std::endl;
+ LOGF_ERROR("[SimpleGPURenderer] Failed to create NV12 pipeline state: 0x%x", hr);
return hr;
}
- std::cout << "[SimpleGPURenderer] NV12 graphics pipeline created successfully" << std::endl;
+ LOGF_INFO("[SimpleGPURenderer] NV12 graphics pipeline created successfully");
return S_OK;
}
@@ -953,7 +953,7 @@ HRESULT SimpleGPURenderer::CreateNV12SrvHeap()
HRESULT hr = m_device->CreateDescriptorHeap(&heapDesc, IID_PPV_ARGS(&m_nv12SrvHeap));
if (FAILED(hr)) {
- std::cout << "[SimpleGPURenderer] Failed to create NV12 SRV heap: 0x" << std::hex << hr << std::endl;
+ LOGF_ERROR("[SimpleGPURenderer] Failed to create NV12 SRV heap: 0x%x", hr);
return hr;
}
@@ -1005,19 +1005,19 @@ HRESULT SimpleGPURenderer::CreateNV12GraphicsPipeline()
if (FAILED(hr))
{
- std::cout << "[SimpleGPURenderer] Failed to create NV12 constant buffer: 0x"
+ LOGF_ERROR("[SimpleGPURenderer] Failed to create NV12 constant buffer: 0x");
<< std::hex << hr << std::endl;
return hr;
}
- std::cout << "[SimpleGPURenderer] NV12 graphics pipeline initialized successfully" << std::endl;
+ LOGF_INFO("[SimpleGPURenderer] NV12 graphics pipeline initialized successfully");
return S_OK;
}
HRESULT SimpleGPURenderer::RenderNV12TextureToBackBuffer(uint64_t fenceValue)
{
if (!m_nv12Texture || !m_initialized) {
- std::cout << "[SimpleGPURenderer::RenderNV12Texture] ERROR: NV12 texture or renderer not initialized" << std::endl;
+ LOGF_INFO("[SimpleGPURenderer::RenderNV12Texture] ERROR: NV12 texture or renderer not initialized");
return E_FAIL;
}
@@ -1035,7 +1035,7 @@ HRESULT SimpleGPURenderer::RenderNV12TextureToBackBuffer(uint64_t fenceValue)
char buf[256];
sprintf_s(buf, "[SimpleGPURenderer::RenderNV12Texture] Failed to create NV12 graphics pipeline: HRESULT = 0x%08X\n", hr);
OutputDebugStringA(buf);
- std::cout << "[SimpleGPURenderer::RenderNV12Texture] Failed to create NV12 graphics pipeline" << std::endl;
+ LOGF_INFO("[SimpleGPURenderer::RenderNV12Texture] Failed to create NV12 graphics pipeline");
return hr;
}
OutputDebugStringA("[SimpleGPURenderer::RenderNV12Texture] NV12 graphics pipeline created successfully\n");
@@ -1065,7 +1065,7 @@ HRESULT SimpleGPURenderer::RenderNV12TextureToBackBuffer(uint64_t fenceValue)
m_device->CreateShaderResourceView(m_nv12Texture.Get(), &uvPlaneSrvDesc, srvHandle);
OutputDebugStringA("[SimpleGPURenderer::RenderNV12Texture] NV12 SRVs created (Y + UV planes)\n");
- std::cout << "[SimpleGPURenderer::RenderNV12Texture] NV12 native format SRVs created (Y + UV planes)" << std::endl;
+ LOGF_INFO("[SimpleGPURenderer::RenderNV12Texture] NV12 native format SRVs created (Y + UV planes)");
}
// CRITICAL: Do NOT wait for current frame - this blocks UI thread!
@@ -1196,7 +1196,7 @@ HRESULT SimpleGPURenderer::RenderNV12TextureToBackBuffer(uint64_t fenceValue)
char buf[256];
sprintf_s(buf, "[SimpleGPURenderer::RenderNV12Texture] Present failed: HRESULT = 0x%08X\n", hr);
OutputDebugStringA(buf);
- std::cout << "[SimpleGPURenderer::RenderNV12Texture] Present failed: 0x" << std::hex << hr << std::endl;
+ LOGF_ERROR("[SimpleGPURenderer::RenderNV12Texture] Present failed: 0x%x", hr);
return hr;
}
OutputDebugStringA("[SimpleGPURenderer::RenderNV12Texture] Present succeeded\n");
@@ -1218,13 +1218,13 @@ HRESULT SimpleGPURenderer::ExecuteGPUPipeline(const VavCoreVideoFrame& frame)
// Step 1: Reset command allocator and list ONCE per frame
hr = m_commandAllocators[m_frameIndex]->Reset();
if (FAILED(hr)) {
- std::cout << "[SimpleGPURenderer] Failed to reset command allocator: 0x" << std::hex << hr << std::endl;
+ LOGF_ERROR("[SimpleGPURenderer] Failed to reset command allocator: 0x%x", hr);
return hr;
}
hr = m_commandList->Reset(m_commandAllocators[m_frameIndex].Get(), nullptr);
if (FAILED(hr)) {
- std::cout << "[SimpleGPURenderer] Failed to reset command list: 0x" << std::hex << hr << std::endl;
+ LOGF_ERROR("[SimpleGPURenderer] Failed to reset command list: 0x%x", hr);
return hr;
}
@@ -1244,7 +1244,7 @@ HRESULT SimpleGPURenderer::ExecuteGPUPipeline(const VavCoreVideoFrame& frame)
// Step 5: Close and execute command list ONCE
hr = m_commandList->Close();
if (FAILED(hr)) {
- std::cout << "[SimpleGPURenderer] Failed to close command list: 0x" << std::hex << hr << std::endl;
+ LOGF_ERROR("[SimpleGPURenderer] Failed to close command list: 0x%x", hr);
return hr;
}
@@ -1280,7 +1280,7 @@ HRESULT SimpleGPURenderer::UpdateVideoTexturesInternal(const VavCoreVideoFrame&
// Validate device and buffer state
if (!m_device) {
- std::cout << "[SimpleGPURenderer] D3D12 device is null" << std::endl;
+ LOGF_INFO("[SimpleGPURenderer] D3D12 device is null");
return E_FAIL;
}
@@ -1298,7 +1298,7 @@ HRESULT SimpleGPURenderer::UpdateVideoTexturesInternal(const VavCoreVideoFrame&
// Additional safety: Check if upload buffer is valid before mapping
if (!yUploadBuffer)
{
- std::cout << "[SimpleGPURenderer] Y upload buffer is null!" << std::endl;
+ LOGF_INFO("[SimpleGPURenderer] Y upload buffer is null!");
return E_FAIL;
}
@@ -1307,20 +1307,20 @@ HRESULT SimpleGPURenderer::UpdateVideoTexturesInternal(const VavCoreVideoFrame&
hr = yUploadBuffer->Map(0, &readRange, &yMappedData);
if (FAILED(hr)) {
- std::cout << "[SimpleGPURenderer] Failed to map Y upload buffer: 0x" << std::hex << hr << std::endl;
+ LOGF_ERROR("[SimpleGPURenderer] Failed to map Y upload buffer: 0x%x", hr);
return hr;
}
if (!yMappedData)
{
- std::cout << "[SimpleGPURenderer] Y upload buffer mapping returned null pointer!" << std::endl;
+ LOGF_INFO("[SimpleGPURenderer] Y upload buffer mapping returned null pointer!");
yUploadBuffer->Unmap(0, nullptr);
return E_FAIL;
}
// Validate frame data
if (!frame.y_plane || frame.width == 0 || frame.height == 0) {
- std::cout << "[SimpleGPURenderer] Invalid frame data" << std::endl;
+ LOGF_INFO("[SimpleGPURenderer] Invalid frame data");
yUploadBuffer->Unmap(0, nullptr);
return E_FAIL;
}
@@ -1455,7 +1455,7 @@ HRESULT SimpleGPURenderer::ExecuteComputeShaderInternal()
{
if (!m_computePipelineState || !m_computeRootSignature)
{
- std::cout << "[SimpleGPURenderer] Compute pipeline not ready" << std::endl;
+ LOGF_INFO("[SimpleGPURenderer] Compute pipeline not ready");
return E_FAIL;
}
@@ -1618,7 +1618,7 @@ HRESULT SimpleGPURenderer::CreateComputeRootSignature()
{
if (error)
{
- std::cout << "[SimpleGPURenderer] Root signature serialization error: "
+ LOGF_INFO("[SimpleGPURenderer] Root signature serialization error: ");
<< (char*)error->GetBufferPointer() << std::endl;
}
return hr;
@@ -1628,7 +1628,7 @@ HRESULT SimpleGPURenderer::CreateComputeRootSignature()
IID_PPV_ARGS(&m_computeRootSignature));
if (FAILED(hr))
{
- std::cout << "[SimpleGPURenderer] Failed to create compute root signature: 0x" << std::hex << hr << std::endl;
+ LOGF_ERROR("[SimpleGPURenderer] Failed to create compute root signature: 0x%x", hr);
return hr;
}
@@ -1716,7 +1716,7 @@ void main(uint3 id : SV_DispatchThreadID)
{
if (errorBlob)
{
- std::cout << "[SimpleGPURenderer] Shader compilation failed: "
+ LOGF_ERROR("[SimpleGPURenderer] Shader compilation failed: ");
<< (char*)errorBlob->GetBufferPointer() << std::endl;
}
return hr;
@@ -1732,7 +1732,7 @@ HRESULT SimpleGPURenderer::CreateComputePipelineState()
{
if (!m_computeShaderBlob)
{
- std::cout << "[SimpleGPURenderer] Compute shader blob not available" << std::endl;
+ LOGF_INFO("[SimpleGPURenderer] Compute shader blob not available");
return E_FAIL;
}
@@ -1745,7 +1745,7 @@ HRESULT SimpleGPURenderer::CreateComputePipelineState()
HRESULT hr = m_device->CreateComputePipelineState(&psoDesc, IID_PPV_ARGS(&m_computePipelineState));
if (FAILED(hr))
{
- std::cout << "[SimpleGPURenderer] Failed to create compute pipeline state: 0x"
+ LOGF_ERROR("[SimpleGPURenderer] Failed to create compute pipeline state: 0x");
<< std::hex << hr << std::endl;
return hr;
}
@@ -1781,7 +1781,7 @@ HRESULT SimpleGPURenderer::CreateGraphicsShaderResources()
hr = m_device->CreateDescriptorHeap(&srvHeapDesc, IID_PPV_ARGS(&m_graphicsSrvHeap));
if (FAILED(hr))
{
- std::cout << "[SimpleGPURenderer] Failed to create graphics SRV heap" << std::endl;
+ LOGF_INFO("[SimpleGPURenderer] Failed to create graphics SRV heap");
return hr;
}
@@ -1795,7 +1795,7 @@ HRESULT SimpleGPURenderer::CreateGraphicsShaderResources()
if (FAILED(hr))
{
- std::cout << "[SimpleGPURenderer] Failed to create AspectFit constant buffer" << std::endl;
+ LOGF_INFO("[SimpleGPURenderer] Failed to create AspectFit constant buffer");
return hr;
}
@@ -1896,7 +1896,7 @@ float4 PSMain(VSOutput input) : SV_TARGET
if (FAILED(hr))
{
if (errorBlob)
- std::cout << "[SimpleGPURenderer] VS compilation failed: " << (char*)errorBlob->GetBufferPointer() << std::endl;
+ LOGF_ERROR("[SimpleGPURenderer] VS compilation failed: %d", (char*)errorBlob->GetBufferPointer());
return hr;
}
@@ -1908,7 +1908,7 @@ float4 PSMain(VSOutput input) : SV_TARGET
if (FAILED(hr))
{
if (errorBlob)
- std::cout << "[SimpleGPURenderer] PS compilation failed: " << (char*)errorBlob->GetBufferPointer() << std::endl;
+ LOGF_ERROR("[SimpleGPURenderer] PS compilation failed: %d", (char*)errorBlob->GetBufferPointer());
return hr;
}
@@ -1967,7 +1967,7 @@ HRESULT SimpleGPURenderer::CreateGraphicsRootSignature()
if (FAILED(hr))
{
if (error)
- std::cout << "[SimpleGPURenderer] Root signature serialization failed: " << (char*)error->GetBufferPointer() << std::endl;
+ LOGF_ERROR("[SimpleGPURenderer] Root signature serialization failed: %d", (char*)error->GetBufferPointer());
return hr;
}
@@ -1976,7 +1976,7 @@ HRESULT SimpleGPURenderer::CreateGraphicsRootSignature()
if (FAILED(hr))
{
- std::cout << "[SimpleGPURenderer] Failed to create graphics root signature" << std::endl;
+ LOGF_INFO("[SimpleGPURenderer] Failed to create graphics root signature");
return hr;
}
@@ -1987,7 +1987,7 @@ HRESULT SimpleGPURenderer::CreateGraphicsPipelineState()
{
if (!m_vertexShaderBlob || !m_pixelShaderBlob)
{
- std::cout << "[SimpleGPURenderer] Graphics shader blobs not available" << std::endl;
+ LOGF_INFO("[SimpleGPURenderer] Graphics shader blobs not available");
return E_FAIL;
}
@@ -2025,7 +2025,7 @@ HRESULT SimpleGPURenderer::CreateGraphicsPipelineState()
HRESULT hr = m_device->CreateGraphicsPipelineState(&psoDesc, IID_PPV_ARGS(&m_graphicsPipelineState));
if (FAILED(hr))
{
- std::cout << "[SimpleGPURenderer] Failed to create graphics pipeline state: 0x" << std::hex << hr << std::endl;
+ LOGF_ERROR("[SimpleGPURenderer] Failed to create graphics pipeline state: 0x%x", hr);
return hr;
}
diff --git a/vav2/platforms/windows/vavcore/src/Decoder/NVDECAV1Decoder.cpp b/vav2/platforms/windows/vavcore/src/Decoder/NVDECAV1Decoder.cpp
index 6c5bde9..337c64c 100644
--- a/vav2/platforms/windows/vavcore/src/Decoder/NVDECAV1Decoder.cpp
+++ b/vav2/platforms/windows/vavcore/src/Decoder/NVDECAV1Decoder.cpp
@@ -913,7 +913,9 @@ bool NVDECAV1Decoder::DecodeToSurface(const uint8_t* packet_data, size_t packet_
}
// CRITICAL: Set CUDA context for current thread (may be background thread)
- // Without this, cuvidDecodePicture will fail when called from non-main thread
+ // Thread safety: Lock mutex to prevent context conflicts in multi-threaded scenarios
+ std::lock_guard contextLock(m_cudaContextMutex);
+
CUresult ctxResult = cuCtxSetCurrent(m_cuContext);
if (ctxResult != CUDA_SUCCESS) {
sprintf_s(debug_buf, "[NVDECAV1Decoder::DecodeToSurface] ERROR: cuCtxSetCurrent failed with code %d\n", ctxResult);
@@ -999,13 +1001,25 @@ bool NVDECAV1Decoder::DecodeToSurface(const uint8_t* packet_data, size_t packet_
return false;
}
- // Create D3D12 surface handler on-demand
- if (!m_d3d12Handler) {
+ // Create D3D12 surface handler on-demand, or recreate if device/context changed
+ if (!m_d3d12Handler ||
+ m_cachedD3D12Device != m_d3d12Device ||
+ m_cachedCuContext != m_cuContext) {
+
+ // Release old handler if exists (cleans up external memory cache)
+ m_d3d12Handler.reset();
+
+ // Create new handler with current device/context
m_d3d12Handler = std::make_unique(
static_cast(m_d3d12Device),
m_cuContext
);
- OutputDebugStringA("[NVDECAV1Decoder::DecodeToSurface] D3D12SurfaceHandler created\n");
+
+ // Update cache
+ m_cachedD3D12Device = m_d3d12Device;
+ m_cachedCuContext = m_cuContext;
+
+ OutputDebugStringA("[NVDECAV1Decoder::DecodeToSurface] D3D12SurfaceHandler (re)created with current device/context\n");
}
// Wait for decoded frame to be available (blocks until callback completes)
diff --git a/vav2/platforms/windows/vavcore/src/Decoder/NVDECAV1Decoder.h b/vav2/platforms/windows/vavcore/src/Decoder/NVDECAV1Decoder.h
index c39fd7d..ec38899 100644
--- a/vav2/platforms/windows/vavcore/src/Decoder/NVDECAV1Decoder.h
+++ b/vav2/platforms/windows/vavcore/src/Decoder/NVDECAV1Decoder.h
@@ -127,6 +127,13 @@ private:
std::mutex m_frameQueueMutex;
std::condition_variable m_frameAvailable; // Notify when frame is ready
+ // CUDA context thread safety
+ std::mutex m_cudaContextMutex; // Protects cuCtxSetCurrent calls
+
+ // D3D12SurfaceHandler recreation tracking
+ void* m_cachedD3D12Device = nullptr; // Last used D3D12 device
+ CUcontext m_cachedCuContext = nullptr; // Last used CUDA context
+
// Helper methods
bool CheckCUDACapability();
bool CreateDecoder();
diff --git a/vav2/platforms/windows/vavcore/src/VavCore.cpp b/vav2/platforms/windows/vavcore/src/VavCore.cpp
index b5373b7..9fcc424 100644
--- a/vav2/platforms/windows/vavcore/src/VavCore.cpp
+++ b/vav2/platforms/windows/vavcore/src/VavCore.cpp
@@ -697,17 +697,25 @@ VAVCORE_API VavCoreResult vavcore_set_d3d_device(VavCorePlayer* player, void* d3
return VAVCORE_ERROR_INVALID_PARAM;
}
- // If decoder doesn't exist yet, store for later use
- if (!player->impl->decoder) {
- player->impl->pendingD3DDevice = d3d_device;
- player->impl->pendingD3DSurfaceType = type;
- OutputDebugStringA("[vavcore_set_d3d_device] Decoder not created yet, storing D3D device for later\n");
+ // Always store for pending use (in case decoder is recreated)
+ player->impl->pendingD3DDevice = d3d_device;
+ player->impl->pendingD3DSurfaceType = type;
+
+ // If decoder exists, also apply immediately
+ if (player->impl->decoder) {
+ bool success = player->impl->decoder->SetD3DDevice(d3d_device, type);
+ if (success) {
+ OutputDebugStringA("[vavcore_set_d3d_device] D3D device applied to existing decoder\n");
+ return VAVCORE_SUCCESS;
+ } else {
+ OutputDebugStringA("[vavcore_set_d3d_device] WARNING: Failed to apply D3D device to existing decoder (will retry on next decode)\n");
+ // Still return success - device is stored for later use
+ return VAVCORE_SUCCESS;
+ }
+ } else {
+ OutputDebugStringA("[vavcore_set_d3d_device] Decoder not created yet, D3D device stored for later\n");
return VAVCORE_SUCCESS;
}
-
- // If decoder exists, set it immediately
- bool success = player->impl->decoder->SetD3DDevice(d3d_device, type);
- return success ? VAVCORE_SUCCESS : VAVCORE_ERROR_NOT_SUPPORTED;
}
VAVCORE_API void* vavcore_get_sync_fence(VavCorePlayer* player) {