From 7968c7e0bebf18a52ddc3cc63def3b8bd9815ab9 Mon Sep 17 00:00:00 2001 From: ened Date: Fri, 3 Oct 2025 19:00:15 +0900 Subject: [PATCH] WIP --- .../vav2player/Vav2Player/App.xaml.cpp | 13 +- .../vav2player/Vav2Player/Vav2Player.vcxproj | 1 + .../Vav2Player/VideoPlayerControl2.xaml.cpp | 14 +- .../Vav2Player/src/Decoder/AV1Decoder.cpp | 277 ------ .../Vav2Player/src/Decoder/AV1Decoder.h | 101 -- .../src/Decoder/AdaptiveAV1Decoder.cpp | 317 ------- .../src/Decoder/AdaptiveAV1Decoder.h | 96 -- .../src/Decoder/AdaptiveDecodingExample.cpp | 159 ---- .../src/Decoder/AdaptiveNVDECDecoder.cpp | 296 ------ .../src/Decoder/AdaptiveNVDECDecoder.h | 143 --- .../Vav2Player/src/Decoder/IVideoDecoder.h | 69 -- .../src/Decoder/MediaFoundationAV1Decoder.cpp | 651 ------------- .../src/Decoder/MediaFoundationAV1Decoder.h | 166 ---- .../src/Decoder/NVDECAV1Decoder.cpp | 366 -------- .../Vav2Player/src/Decoder/NVDECAV1Decoder.h | 110 --- .../src/Decoder/VideoDecoderFactory.cpp | 397 -------- .../src/Decoder/VideoDecoderFactory.h | 101 -- .../Vav2Player/src/FileIO/IWebMFileReader.h | 77 -- .../Vav2Player/src/FileIO/WebMFileReader.cpp | 871 ------------------ .../Vav2Player/src/FileIO/WebMFileReader.h | 144 --- .../Vav2Player/src/Logger/SimpleLogger.cpp | 201 ++++ .../Vav2Player/src/Logger/SimpleLogger.h | 178 ++-- .../src/Playback/FrameProcessor.cpp | 51 +- .../src/Playback/PlaybackController.cpp | 69 +- .../src/Rendering/D3D12VideoRenderer.cpp | 23 +- .../src/Rendering/SimpleGPURenderer.cpp | 126 +-- .../vavcore/src/Decoder/NVDECAV1Decoder.cpp | 22 +- .../vavcore/src/Decoder/NVDECAV1Decoder.h | 7 + .../platforms/windows/vavcore/src/VavCore.cpp | 26 +- 29 files changed, 455 insertions(+), 4617 deletions(-) delete mode 100644 vav2/platforms/windows/applications/vav2player/Vav2Player/src/Decoder/AV1Decoder.cpp delete mode 100644 vav2/platforms/windows/applications/vav2player/Vav2Player/src/Decoder/AV1Decoder.h delete mode 100644 vav2/platforms/windows/applications/vav2player/Vav2Player/src/Decoder/AdaptiveAV1Decoder.cpp delete mode 100644 vav2/platforms/windows/applications/vav2player/Vav2Player/src/Decoder/AdaptiveAV1Decoder.h delete mode 100644 vav2/platforms/windows/applications/vav2player/Vav2Player/src/Decoder/AdaptiveDecodingExample.cpp delete mode 100644 vav2/platforms/windows/applications/vav2player/Vav2Player/src/Decoder/AdaptiveNVDECDecoder.cpp delete mode 100644 vav2/platforms/windows/applications/vav2player/Vav2Player/src/Decoder/AdaptiveNVDECDecoder.h delete mode 100644 vav2/platforms/windows/applications/vav2player/Vav2Player/src/Decoder/IVideoDecoder.h delete mode 100644 vav2/platforms/windows/applications/vav2player/Vav2Player/src/Decoder/MediaFoundationAV1Decoder.cpp delete mode 100644 vav2/platforms/windows/applications/vav2player/Vav2Player/src/Decoder/MediaFoundationAV1Decoder.h delete mode 100644 vav2/platforms/windows/applications/vav2player/Vav2Player/src/Decoder/NVDECAV1Decoder.cpp delete mode 100644 vav2/platforms/windows/applications/vav2player/Vav2Player/src/Decoder/NVDECAV1Decoder.h delete mode 100644 vav2/platforms/windows/applications/vav2player/Vav2Player/src/Decoder/VideoDecoderFactory.cpp delete mode 100644 vav2/platforms/windows/applications/vav2player/Vav2Player/src/Decoder/VideoDecoderFactory.h delete mode 100644 vav2/platforms/windows/applications/vav2player/Vav2Player/src/FileIO/IWebMFileReader.h delete mode 100644 vav2/platforms/windows/applications/vav2player/Vav2Player/src/FileIO/WebMFileReader.cpp delete mode 100644 vav2/platforms/windows/applications/vav2player/Vav2Player/src/FileIO/WebMFileReader.h create mode 100644 vav2/platforms/windows/applications/vav2player/Vav2Player/src/Logger/SimpleLogger.cpp 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) {