diff --git a/.claude/settings.local.json b/.claude/settings.local.json
index d7e9d6d..6a13d8f 100644
--- a/.claude/settings.local.json
+++ b/.claude/settings.local.json
@@ -67,7 +67,9 @@
"Bash(\"/c/Program Files/Microsoft Visual Studio/2022/Community/MSBuild/Current/Bin/MSBuild.exe\" Vav2Player.vcxproj \"//p:Configuration=Debug\" \"//p:Platform=x64\" \"//v:minimal\")",
"Bash(python3:*)",
"Bash(find:*)",
- "Bash(\"/c/Program Files/Microsoft Visual Studio/2022/Community/MSBuild/Current/Bin/MSBuild.exe\" Vav2PlayerHeadless.vcxproj \"/p:Configuration=Debug\" \"/p:Platform=x64\" \"/v:minimal\")"
+ "Bash(\"/c/Program Files/Microsoft Visual Studio/2022/Community/MSBuild/Current/Bin/MSBuild.exe\" Vav2PlayerHeadless.vcxproj \"/p:Configuration=Debug\" \"/p:Platform=x64\" \"/v:minimal\")",
+ "Bash(cmd:*)",
+ "Bash(\"C:/Program Files/Microsoft Visual Studio/2022/Community/MSBuild/Current/Bin/MSBuild.exe\" Vav2Player.vcxproj /p:Configuration=Debug /p:Platform=x64 /v:minimal)",
],
"deny": [],
"ask": []
diff --git a/vav2/Vav2Player/Vav2Player/Vav2Player.vcxproj b/vav2/Vav2Player/Vav2Player/Vav2Player.vcxproj
index 0d7cd45..55a74a0 100644
--- a/vav2/Vav2Player/Vav2Player/Vav2Player.vcxproj
+++ b/vav2/Vav2Player/Vav2Player/Vav2Player.vcxproj
@@ -146,7 +146,7 @@
MultiVideoTestWindow.xaml
-
+
@@ -179,7 +179,7 @@
MultiVideoTestWindow.xaml
-
+
diff --git a/vav2/Vav2Player/Vav2Player/Vav2PlayerHeadless.vcxproj b/vav2/Vav2Player/Vav2Player/Vav2PlayerHeadless.vcxproj
index ff2b785..f079b77 100644
--- a/vav2/Vav2Player/Vav2Player/Vav2PlayerHeadless.vcxproj
+++ b/vav2/Vav2Player/Vav2Player/Vav2PlayerHeadless.vcxproj
@@ -97,7 +97,6 @@
-
@@ -106,7 +105,6 @@
-
@@ -122,7 +120,6 @@
-
@@ -131,7 +128,6 @@
-
diff --git a/vav2/Vav2Player/Vav2Player/VideoPlayerControl.xaml.cpp b/vav2/Vav2Player/Vav2Player/VideoPlayerControl.xaml.cpp
index 3412019..b6c9e90 100644
--- a/vav2/Vav2Player/Vav2Player/VideoPlayerControl.xaml.cpp
+++ b/vav2/Vav2Player/Vav2Player/VideoPlayerControl.xaml.cpp
@@ -5,11 +5,17 @@
#endif
#include
+#include
+#include
+#include
#include
#include
#include
#include "headless/AV1Decoder_Headless.h"
#include "src/Rendering/SimpleGPURenderer.h"
+#include "src/Common/VideoTypes.h"
+#include "src/FileIO/WebMFileReader.h"
+#include "src/Decoder/VideoDecoderFactory.h"
using namespace winrt;
using namespace winrt::Microsoft::UI::Xaml;
@@ -19,7 +25,7 @@ using namespace winrt::Microsoft::UI::Dispatching;
namespace winrt::Vav2Player::implementation
{
VideoPlayerControl::VideoPlayerControl()
- : m_useHardwareRendering(true)
+ : m_useHardwareRendering(false) // CPU mode for Phase 1
{
InitializeComponent();
}
@@ -37,12 +43,6 @@ namespace winrt::Vav2Player::implementation
m_isInitialized = true;
UpdateStatus(L"Ready");
- // Set initial UI state
- if (!m_showControls)
- {
- // ControlsGrid().Visibility(winrt::Microsoft::UI::Xaml::Visibility::Collapsed);
- }
-
// Auto load video if source is set
if (!m_videoSource.empty())
{
@@ -58,6 +58,21 @@ namespace winrt::Vav2Player::implementation
});
OutputDebugStringA("VideoPlayerControl loaded successfully\n");
+
+ // Show purple outline placeholder while waiting
+ ShowPurpleOutlinePlaceholder();
+
+ // After 3 seconds, try to load actual video
+ auto delayTimer = winrt::Microsoft::UI::Xaml::DispatcherTimer();
+ delayTimer.Interval(std::chrono::seconds(3));
+ delayTimer.Tick([this, delayTimer](auto&&, auto&&) mutable {
+ delayTimer.Stop();
+ // Try to load test video file
+ auto testVideoPath = L"D:/Project/video-av1/sample/simple_test.webm";
+ OutputDebugStringA("[DEBUG] Attempting to load test video after 3 second delay\n");
+ LoadVideo(testVideoPath);
+ });
+ delayTimer.Start();
}
catch (...)
{
@@ -65,15 +80,86 @@ namespace winrt::Vav2Player::implementation
}
}
+ void VideoPlayerControl::ShowPurpleOutlinePlaceholder()
+ {
+ try
+ {
+ OutputDebugStringA("Showing purple outline placeholder...\n");
+
+ // Get container size for full screen placeholder
+ auto container = VideoDisplayArea();
+ if (!container) return;
+
+ int width = static_cast(container.ActualWidth());
+ int height = static_cast(container.ActualHeight());
+
+ // Use minimum size if container not ready
+ if (width <= 0 || height <= 0) {
+ width = 800;
+ height = 600;
+ }
+
+ m_renderBitmap = winrt::Microsoft::UI::Xaml::Media::Imaging::WriteableBitmap(width, height);
+ VideoImage().Source(m_renderBitmap);
+ VideoImage().Visibility(winrt::Microsoft::UI::Xaml::Visibility::Visible);
+ VideoImage().Width(width);
+ VideoImage().Height(height);
+
+ auto buffer = m_renderBitmap.PixelBuffer();
+ auto bufferByteAccess = buffer.as<::Windows::Storage::Streams::IBufferByteAccess>();
+ uint8_t* bufferData = nullptr;
+ winrt::check_hresult(bufferByteAccess->Buffer(&bufferData));
+
+ // Fill with transparent background
+ for (int i = 0; i < width * height; i++)
+ {
+ bufferData[i * 4 + 0] = 0; // Blue
+ bufferData[i * 4 + 1] = 0; // Green
+ bufferData[i * 4 + 2] = 0; // Red
+ bufferData[i * 4 + 3] = 0; // Alpha (transparent)
+ }
+
+ // Draw purple outline (border thickness = 4 pixels)
+ int borderThickness = 4;
+ for (int y = 0; y < height; y++)
+ {
+ for (int x = 0; x < width; x++)
+ {
+ bool isOutline = (x < borderThickness || x >= width - borderThickness ||
+ y < borderThickness || y >= height - borderThickness);
+
+ if (isOutline)
+ {
+ int index = (y * width + x) * 4;
+ bufferData[index + 0] = 128; // Blue
+ bufferData[index + 1] = 0; // Green
+ bufferData[index + 2] = 128; // Red (Purple = Red + Blue)
+ bufferData[index + 3] = 255; // Alpha (opaque)
+ }
+ }
+ }
+
+ buffer.Length(width * height * 4);
+ m_renderBitmap.Invalidate();
+ OutputDebugStringA("Purple outline placeholder completed\n");
+ }
+ catch (...)
+ {
+ OutputDebugStringA("Purple outline placeholder failed\n");
+ }
+ }
+
void VideoPlayerControl::UserControl_Unloaded(winrt::Windows::Foundation::IInspectable const&, winrt::Microsoft::UI::Xaml::RoutedEventArgs const&)
{
try
{
Stop();
- StopPlaybackTimer();
+ if (m_playbackTimer)
+ {
+ m_playbackTimer.Stop();
+ }
StopControlsHideTimer();
- // Cleanup resources
if (m_gpuRenderer)
{
m_gpuRenderer->Shutdown();
@@ -92,51 +178,6 @@ namespace winrt::Vav2Player::implementation
}
}
- // Control event handlers (commented out as controls are disabled)
- /*
- void VideoPlayerControl::PlayPauseButton_Click(winrt::Windows::Foundation::IInspectable const&, winrt::Microsoft::UI::Xaml::RoutedEventArgs const&)
- {
- if (m_isPlaying)
- {
- Pause();
- }
- else
- {
- Play();
- }
- }
- */
-
- /*
- void VideoPlayerControl::StopButton_Click(winrt::Windows::Foundation::IInspectable const&, winrt::Microsoft::UI::Xaml::RoutedEventArgs const&)
- {
- Stop();
- }
- */
-
- /*
- void VideoPlayerControl::ProgressSlider_ValueChanged(winrt::Windows::Foundation::IInspectable const&, winrt::Microsoft::UI::Xaml::Controls::Primitives::RangeBaseValueChangedEventArgs const& e)
- {
- if (!m_isLoaded || !m_fileReader)
- return;
-
- // Avoid feedback loop during programmatic updates
- if (e.OldValue() == e.NewValue())
- return;
-
- try
- {
- double percentage = e.NewValue();
- double targetTime = (percentage / 100.0) * m_duration;
- Seek(targetTime);
- }
- catch (...)
- {
- // Ignore seek errors
- }
- }
- */
-
void VideoPlayerControl::HoverDetector_PointerEntered(winrt::Windows::Foundation::IInspectable const&, winrt::Microsoft::UI::Xaml::Input::PointerRoutedEventArgs const&)
{
if (m_showControls && m_isLoaded)
@@ -182,14 +223,7 @@ namespace winrt::Vav2Player::implementation
m_showControls = value;
if (m_isInitialized)
{
- if (value && m_isLoaded)
- {
- // ControlsGrid().Visibility(winrt::Microsoft::UI::Xaml::Visibility::Visible);
- }
- else
- {
- // ControlsGrid().Visibility(winrt::Microsoft::UI::Xaml::Visibility::Collapsed);
- }
+ // Update controls visibility based on value and loaded state
}
}
@@ -298,10 +332,12 @@ namespace winrt::Vav2Player::implementation
// Public Methods
void VideoPlayerControl::LoadVideo(winrt::hstring const& filePath)
{
- OutputDebugStringA("[VideoPlayerControl] LoadVideo called\n");
+ OutputDebugStringA("[DEBUG] LoadVideo called\n");
try
{
std::string filePathStr = winrt::to_string(filePath);
+ OutputDebugStringA(("[DEBUG] Loading file: " + filePathStr + "\n").c_str());
+
UpdateStatus(L"Loading video...");
LoadingRing().IsActive(true);
@@ -311,80 +347,98 @@ namespace winrt::Vav2Player::implementation
// Create file reader
if (!m_fileReader)
{
+ OutputDebugStringA("[DEBUG] Creating WebMFileReader\n");
m_fileReader = std::make_unique();
}
// Open file
+ OutputDebugStringA("[DEBUG] Opening file...\n");
if (!m_fileReader->OpenFile(filePathStr))
{
+ OutputDebugStringA("[DEBUG] Failed to open file\n");
UpdateStatus(L"Failed to open video file");
LoadingRing().IsActive(false);
return;
}
+ OutputDebugStringA("[DEBUG] File opened successfully\n");
// Get video tracks
+ OutputDebugStringA("[DEBUG] Getting video tracks...\n");
auto tracks = m_fileReader->GetVideoTracks();
if (tracks.empty())
{
+ OutputDebugStringA("[DEBUG] No video tracks found\n");
UpdateStatus(L"No video tracks found");
LoadingRing().IsActive(false);
return;
}
+ OutputDebugStringA(("[DEBUG] Found " + std::to_string(tracks.size()) + " video tracks\n").c_str());
// Select first video track
+ OutputDebugStringA("[DEBUG] Selecting video track...\n");
if (!m_fileReader->SelectVideoTrack(tracks[0].track_number))
{
+ OutputDebugStringA("[DEBUG] Failed to select video track\n");
UpdateStatus(L"Failed to select video track");
LoadingRing().IsActive(false);
return;
}
+ OutputDebugStringA("[DEBUG] Video track selected successfully\n");
// Get metadata
+ OutputDebugStringA("[DEBUG] Getting video metadata...\n");
auto metadata = m_fileReader->GetVideoMetadata();
m_totalFrames = metadata.total_frames;
m_frameRate = metadata.frame_rate > 0 ? metadata.frame_rate : 30.0;
m_duration = m_totalFrames / m_frameRate;
- // Enable hardware rendering for optimal performance
- UseHardwareRendering(true);
+ OutputDebugStringA(("[DEBUG] Video metadata: " + std::to_string(metadata.width) + "x" + std::to_string(metadata.height) +
+ ", " + std::to_string(m_totalFrames) + " frames, " + std::to_string(m_frameRate) + " fps\n").c_str());
+
+ // Enable CPU rendering for Phase 1 (MAJOR_REFACTORING_GUIDE)
+ UseHardwareRendering(false);
// Create and initialize decoder
- if (!CreateDecoder() || !InitializeDecoder())
+ OutputDebugStringA("[DEBUG] Creating decoder...\n");
+ if (!CreateDecoder())
{
- UpdateStatus(L"Failed to initialize decoder");
+ OutputDebugStringA("[DEBUG] Failed to create decoder\n");
+ UpdateStatus(L"Failed to create decoder");
LoadingRing().IsActive(false);
return;
}
+ OutputDebugStringA("[DEBUG] Initializing decoder...\n");
+ if (!InitializeDecoder())
+ {
+ OutputDebugStringA("[DEBUG] Failed to initialize decoder\n");
+ UpdateStatus(L"Failed to initialize decoder");
+ LoadingRing().IsActive(false);
+ return;
+ }
+ OutputDebugStringA("[DEBUG] Decoder initialized successfully\n");
+
// Initialize video renderer
InitializeVideoRenderer();
- // Simplified initialization - removed complex pipelines
-
// Update UI state
m_isLoaded = true;
LoadingRing().IsActive(false);
PlaceholderText().Visibility(winrt::Microsoft::UI::Xaml::Visibility::Collapsed);
- if (m_showControls)
- {
- // ControlsGrid().Visibility(winrt::Microsoft::UI::Xaml::Visibility::Visible);
- }
-
- // Update duration display
- // DurationText().Text(winrt::to_hstring(FormatTime(m_duration)));
-
UpdateStatus(L"Video loaded successfully");
- OutputDebugStringA(("Video loaded: " + filePathStr + "\n").c_str());
+ OutputDebugStringA(("[DEBUG] Video loaded successfully: " + filePathStr + "\n").c_str());
// Auto play if enabled
if (m_autoPlay)
{
+ OutputDebugStringA("[DEBUG] Auto play enabled - starting playback\n");
Play();
}
}
catch (...)
{
+ OutputDebugStringA("[DEBUG] Exception in LoadVideo\n");
UpdateStatus(L"Error loading video");
LoadingRing().IsActive(false);
}
@@ -392,38 +446,429 @@ namespace winrt::Vav2Player::implementation
void VideoPlayerControl::Play()
{
+ OutputDebugStringA("[DEBUG] Play() called\n");
if (!m_isLoaded || m_isPlaying)
+ {
+ OutputDebugStringA("[DEBUG] Play() - not ready or already playing\n");
return;
-
+ }
+
m_isPlaying = true;
UpdateStatus(L"Playing");
+
+ // Setup playback timer for continuous frame processing
+ if (!m_playbackTimer)
+ {
+ OutputDebugStringA("[DEBUG] Creating playback timer\n");
+ m_playbackTimer = winrt::Microsoft::UI::Xaml::DispatcherTimer();
+
+ // Store weak reference to avoid circular dependency
+ auto weakThis = get_weak();
+ m_playbackTimer.Tick([weakThis](auto&&, auto&&) {
+ if (auto strongThis = weakThis.get())
+ {
+ OutputDebugStringA("[DEBUG] Timer tick - checking conditions\n");
+ OutputDebugStringA(("[DEBUG] m_isPlaying: " + std::string(strongThis->m_isPlaying ? "true" : "false") +
+ ", m_isLoaded: " + std::string(strongThis->m_isLoaded ? "true" : "false") + "\n").c_str());
+
+ if (strongThis->m_isPlaying && strongThis->m_isLoaded)
+ {
+ try {
+ strongThis->ProcessSingleFrame();
+ }
+ catch (...) {
+ OutputDebugStringA("[DEBUG] Exception in timer ProcessSingleFrame\n");
+ strongThis->m_isPlaying = false;
+ strongThis->m_playbackTimer.Stop();
+ }
+ }
+ else
+ {
+ OutputDebugStringA("[DEBUG] Timer tick - conditions not met, skipping frame processing\n");
+ }
+ }
+ else
+ {
+ OutputDebugStringA("[DEBUG] Timer tick - object destroyed, stopping timer\n");
+ }
+ });
+ }
+
+ // Set timer interval based on frame rate (default 30fps = 33.33ms)
+ auto interval = std::chrono::milliseconds(static_cast(1000.0 / m_frameRate));
+ m_playbackTimer.Interval(interval);
+
+ OutputDebugStringA(("[DEBUG] Timer interval set to: " + std::to_string(interval.count()) + "ms\n").c_str());
+ OutputDebugStringA(("[DEBUG] Timer object valid: " + std::string(m_playbackTimer ? "true" : "false") + "\n").c_str());
+
+ m_playbackTimer.Start();
+ OutputDebugStringA("[DEBUG] Timer.Start() called\n");
+
+ // Immediate test: try to process one frame manually to verify the pipeline works
+ OutputDebugStringA("[DEBUG] Testing immediate frame processing...\n");
+ ProcessSingleFrame();
+
+ OutputDebugStringA(("[DEBUG] Started playback timer at " + std::to_string(m_frameRate) + " fps\n").c_str());
+
+ // Backup approach: Create a manual timer that restarts itself
+ OutputDebugStringA("[DEBUG] Setting up backup manual timer approach\n");
+ auto manualTimer = winrt::Microsoft::UI::Xaml::DispatcherTimer();
+ manualTimer.Interval(interval);
+
+ // Manual timer approach with restart
+ manualTimer.Tick([this, manualTimer](auto&&, auto&&) mutable {
+ OutputDebugStringA("[DEBUG] Manual timer tick\n");
+ if (m_isPlaying && m_isLoaded) {
+ ProcessSingleFrame();
+ // Schedule next frame
+ manualTimer.Start();
+ }
+ });
+
+ // Start both timers
+ manualTimer.Start();
+ OutputDebugStringA("[DEBUG] Manual timer also started\n");
}
- // MAJOR_REFACTORING_GUIDE.md: Simplified method stubs for compilation
- void VideoPlayerControl::Pause() { m_isPlaying = false; }
- void VideoPlayerControl::Stop() { m_isPlaying = false; }
- void VideoPlayerControl::ProcessSingleFrame() { /* Simplified */ }
- void VideoPlayerControl::ProcessSingleFrameLegacy() { /* Basic */ }
- void VideoPlayerControl::RenderFrameToScreen(const VideoFrame& frame) { /* Software */ }
- void VideoPlayerControl::RenderFrameSoftware(const VideoFrame& frame) { /* YUV to RGB */ }
- void VideoPlayerControl::UpdateStatus(winrt::hstring const& message) { m_status = message; }
- void VideoPlayerControl::StartPlaybackTimer() { /* Timer */ }
- void VideoPlayerControl::UpdateProgress() { /* Progress */ }
- void VideoPlayerControl::StopPlaybackTimer() { /* Stop Timer */ }
- void VideoPlayerControl::StopControlsHideTimer() { /* Stop Controls Timer */ }
- void VideoPlayerControl::ShowControlsInternal() { /* Show Controls */ }
- void VideoPlayerControl::InitializeVideoRenderer() { /* Init Renderer */ }
- void VideoPlayerControl::ResetVideoState() { /* Reset State */ }
- bool VideoPlayerControl::CreateDecoder() { return true; }
- bool VideoPlayerControl::InitializeDecoder() { return true; }
+ void VideoPlayerControl::Pause()
+ {
+ OutputDebugStringA("[DEBUG] Pause() called\n");
+ m_isPlaying = false;
+ if (m_playbackTimer)
+ {
+ m_playbackTimer.Stop();
+ }
+ UpdateStatus(L"Paused");
+ }
+
+ void VideoPlayerControl::Stop()
+ {
+ OutputDebugStringA("[DEBUG] Stop() called\n");
+ m_isPlaying = false;
+ if (m_playbackTimer)
+ {
+ m_playbackTimer.Stop();
+ }
+
+ // Reset position to beginning
+ m_currentFrame = 0;
+ m_currentTime = 0.0;
+
+ // Reset file reader to beginning for next play
+ if (m_fileReader && m_fileReader->IsFileOpen())
+ {
+ OutputDebugStringA("[DEBUG] Resetting file reader to beginning\n");
+ m_fileReader->Reset();
+ }
+
+ // Reset decoder state
+ if (m_decoder)
+ {
+ OutputDebugStringA("[DEBUG] Resetting decoder state\n");
+ m_decoder->Reset();
+ }
+
+ UpdateStatus(L"Stopped");
+ }
+
+ void VideoPlayerControl::ProcessSingleFrame()
+ {
+ OutputDebugStringA("[DEBUG] ProcessSingleFrame() called\n");
+ if (!m_isLoaded || !m_fileReader || !m_decoder)
+ {
+ OutputDebugStringA("[DEBUG] ProcessSingleFrame() - not ready\n");
+ return;
+ }
+
+ VideoPacket packet;
+ OutputDebugStringA("[DEBUG] Reading next packet...\n");
+ if (!m_fileReader->ReadNextPacket(packet))
+ {
+ OutputDebugStringA("[DEBUG] No more packets - playback completed\n");
+ m_isPlaying = false;
+ if (m_playbackTimer) {
+ m_playbackTimer.Stop();
+ }
+ UpdateStatus(L"Playback completed");
+ return;
+ }
+ OutputDebugStringA(("[DEBUG] Packet read - size: " + std::to_string(packet.size) + "\n").c_str());
+
+ VideoFrame frame;
+ OutputDebugStringA("[DEBUG] Decoding frame...\n");
+ if (!m_decoder->DecodeFrame(packet, frame))
+ {
+ OutputDebugStringA("[DEBUG] Frame decode failed - skipping\n");
+ return;
+ }
+ OutputDebugStringA(("[DEBUG] Frame decoded - size: " + std::to_string(frame.width) + "x" + std::to_string(frame.height) + "\n").c_str());
+
+ RenderFrameToScreen(frame);
+
+ m_currentFrame++;
+ m_currentTime = m_currentFrame / m_frameRate;
+ OutputDebugStringA(("[DEBUG] Frame " + std::to_string(m_currentFrame) + " processed\n").c_str());
+ }
+
+ void VideoPlayerControl::ProcessSingleFrameLegacy()
+ {
+ // Legacy method - calls ProcessSingleFrame for compatibility
+ ProcessSingleFrame();
+ }
+
+ void VideoPlayerControl::RenderFrameToScreen(const VideoFrame& frame)
+ {
+ OutputDebugStringA("[DEBUG] RenderFrameToScreen() called\n");
+ RenderFrameSoftware(frame);
+ }
+
+ void VideoPlayerControl::RenderFrameSoftware(const VideoFrame& frame)
+ {
+ OutputDebugStringA(("[DEBUG] RenderFrameSoftware() called - " + std::to_string(frame.width) + "x" + std::to_string(frame.height) + "\n").c_str());
+ OutputDebugStringA(("[DEBUG] y_plane: " + std::string(frame.y_plane ? "valid" : "null") + "\n").c_str());
+
+ if (!frame.y_plane || frame.width == 0 || frame.height == 0)
+ {
+ OutputDebugStringA("[DEBUG] Invalid frame data - returning\n");
+ return;
+ }
+
+ try {
+ // Create or reuse WriteableBitmap for the frame
+ if (!m_renderBitmap ||
+ static_cast(m_renderBitmap.PixelWidth()) != frame.width ||
+ static_cast(m_renderBitmap.PixelHeight()) != frame.height) {
+
+ OutputDebugStringA("[DEBUG] Creating new WriteableBitmap\n");
+ m_renderBitmap = winrt::Microsoft::UI::Xaml::Media::Imaging::WriteableBitmap(
+ frame.width, frame.height);
+ VideoImage().Source(m_renderBitmap);
+ UpdateVideoImageAspectFit(frame.width, frame.height);
+
+ OutputDebugStringA(("Created WriteableBitmap: " + std::to_string(frame.width) + "x" + std::to_string(frame.height) + "\n").c_str());
+ }
+
+ // Convert YUV to BGRA and render to bitmap
+ auto buffer = m_renderBitmap.PixelBuffer();
+ uint32_t capacity = buffer.Capacity();
+ OutputDebugStringA(("[DEBUG] Buffer capacity: " + std::to_string(capacity) + "\n").c_str());
+
+ if (capacity >= frame.width * frame.height * 4) {
+ OutputDebugStringA("[DEBUG] Converting YUV to BGRA...\n");
+ // Simple approach: create BGRA data and copy to buffer
+ std::vector bgra_data(frame.width * frame.height * 4);
+ ConvertYUVToBGRA(frame, bgra_data.data(), frame.width, frame.height);
+
+ OutputDebugStringA("[DEBUG] Copying to bitmap buffer...\n");
+ // Copy BGRA data directly to bitmap buffer
+ auto bufferByteAccess = buffer.as<::Windows::Storage::Streams::IBufferByteAccess>();
+ uint8_t* bufferData = nullptr;
+ winrt::check_hresult(bufferByteAccess->Buffer(&bufferData));
+ std::memcpy(bufferData, bgra_data.data(), frame.width * frame.height * 4);
+ buffer.Length(frame.width * frame.height * 4);
+
+ // Trigger UI update
+ m_renderBitmap.Invalidate();
+
+ OutputDebugStringA(("[DEBUG] Frame rendered successfully: " + std::to_string(frame.width) + "x" + std::to_string(frame.height) + "\n").c_str());
+ } else {
+ OutputDebugStringA("[DEBUG] Buffer capacity too small\n");
+ }
+
+ } catch (...) {
+ OutputDebugStringA("[DEBUG] Software rendering failed\n");
+ }
+ }
+
+ void VideoPlayerControl::ConvertYUVToBGRA(const VideoFrame& yuv_frame, uint8_t* bgra_buffer, uint32_t width, uint32_t height)
+ {
+ OutputDebugStringA("[DEBUG] ConvertYUVToBGRA() called\n");
+ // YUV420P to BGRA conversion using BT.709 color space
+ const uint8_t* y_plane = yuv_frame.y_plane.get();
+ const uint8_t* u_plane = yuv_frame.u_plane.get();
+ const uint8_t* v_plane = yuv_frame.v_plane.get();
+
+ if (!y_plane || !u_plane || !v_plane) {
+ OutputDebugStringA("[DEBUG] ConvertYUVToBGRA: Invalid plane data\n");
+ return;
+ }
+
+ const uint32_t y_stride = yuv_frame.y_stride;
+ const uint32_t u_stride = yuv_frame.u_stride;
+ const uint32_t v_stride = yuv_frame.v_stride;
+
+ OutputDebugStringA(("[DEBUG] YUV strides: Y=" + std::to_string(y_stride) + " U=" + std::to_string(u_stride) + " V=" + std::to_string(v_stride) + "\n").c_str());
+
+ for (uint32_t y = 0; y < height; y++) {
+ const uint8_t* y_row = y_plane + y * y_stride;
+ const uint8_t* u_row = u_plane + (y / 2) * u_stride;
+ const uint8_t* v_row = v_plane + (y / 2) * v_stride;
+
+ uint8_t* bgra_row = bgra_buffer + y * width * 4;
+
+ for (uint32_t x = 0; x < width; x++) {
+ const uint8_t Y = y_row[x];
+ const uint8_t U = u_row[x / 2];
+ const uint8_t V = v_row[x / 2];
+
+ // BT.709 YUV to RGB conversion
+ const int C = Y - 16;
+ const int D = U - 128;
+ const int E = V - 128;
+
+ int R = (298 * C + 409 * E + 128) >> 8;
+ int G = (298 * C - 100 * D - 208 * E + 128) >> 8;
+ int B = (298 * C + 516 * D + 128) >> 8;
+
+ // Clamp to [0, 255]
+ R = std::max(0, std::min(255, R));
+ G = std::max(0, std::min(255, G));
+ B = std::max(0, std::min(255, B));
+
+ // Store as BGRA
+ bgra_row[x * 4 + 0] = static_cast(B); // Blue
+ bgra_row[x * 4 + 1] = static_cast(G); // Green
+ bgra_row[x * 4 + 2] = static_cast(R); // Red
+ bgra_row[x * 4 + 3] = 255; // Alpha
+ }
+ }
+ OutputDebugStringA("[DEBUG] YUV to BGRA conversion completed\n");
+ }
+
+ void VideoPlayerControl::UpdateStatus(winrt::hstring const& message)
+ {
+ m_status = message;
+ OutputDebugStringA(("[DEBUG] Status: " + winrt::to_string(message) + "\n").c_str());
+ }
+
+ void VideoPlayerControl::ShowControlsInternal()
+ {
+ // Show video controls - simplified implementation
+ }
+
+ void VideoPlayerControl::InitializeVideoRenderer()
+ {
+ OutputDebugStringA("[DEBUG] InitializeVideoRenderer() called\n");
+ // Initialize CPU rendering mode (Phase 1)
+ if (!m_useHardwareRendering)
+ {
+ // Ensure software rendering UI is visible
+ VideoSwapChainPanel().Visibility(winrt::Microsoft::UI::Xaml::Visibility::Collapsed);
+ VideoImage().Visibility(winrt::Microsoft::UI::Xaml::Visibility::Visible);
+ OutputDebugStringA("[DEBUG] Initialized CPU rendering mode\n");
+ }
+ else
+ {
+ // Hardware rendering setup (Phase 2 - future)
+ VideoSwapChainPanel().Visibility(winrt::Microsoft::UI::Xaml::Visibility::Visible);
+ VideoImage().Visibility(winrt::Microsoft::UI::Xaml::Visibility::Collapsed);
+ OutputDebugStringA("[DEBUG] Hardware rendering not implemented yet\n");
+ }
+ }
+
+ void VideoPlayerControl::ResetVideoState()
+ {
+ OutputDebugStringA("[DEBUG] ResetVideoState() called\n");
+ m_currentFrame = 0;
+ m_currentTime = 0.0;
+ m_isLoaded = false;
+ m_isPlaying = false;
+
+ // Stop and reset playback timer
+ if (m_playbackTimer)
+ {
+ m_playbackTimer.Stop();
+ }
+ }
+
+ bool VideoPlayerControl::CreateDecoder()
+ {
+ OutputDebugStringA("[DEBUG] CreateDecoder() called\n");
+ m_decoder = VideoDecoderFactory::CreateDecoder(VideoCodecType::AV1, m_decoderType);
+ bool success = m_decoder != nullptr;
+ OutputDebugStringA(("[DEBUG] Decoder created: " + std::string(success ? "success" : "failed") + "\n").c_str());
+ return success;
+ }
+
+ bool VideoPlayerControl::InitializeDecoder()
+ {
+ OutputDebugStringA("[DEBUG] InitializeDecoder() called\n");
+ if (!m_decoder) {
+ OutputDebugStringA("[DEBUG] No decoder available\n");
+ return false;
+ }
+ auto metadata = m_fileReader->GetVideoMetadata();
+ bool success = m_decoder->Initialize(metadata);
+ OutputDebugStringA(("[DEBUG] Decoder initialized: " + std::string(success ? "success" : "failed") + "\n").c_str());
+ return success;
+ }
+
+ void VideoPlayerControl::UpdateVideoImageAspectFit(int videoWidth, int videoHeight)
+ {
+ OutputDebugStringA(("[DEBUG] UpdateVideoImageAspectFit: " + std::to_string(videoWidth) + "x" + std::to_string(videoHeight) + "\n").c_str());
+ // AspectFit calculation for proper video scaling
+ auto container = VideoDisplayArea();
+ if (!container) return;
+
+ double containerWidth = container.ActualWidth();
+ double containerHeight = container.ActualHeight();
+ if (containerWidth <= 0 || containerHeight <= 0) return;
+
+ double videoAspectRatio = static_cast(videoWidth) / videoHeight;
+ double containerAspectRatio = containerWidth / containerHeight;
+
+ double displayWidth, displayHeight;
+ if (videoAspectRatio > containerAspectRatio) {
+ // Video is wider - fit to container width
+ displayWidth = containerWidth;
+ displayHeight = containerWidth / videoAspectRatio;
+ } else {
+ // Video is taller - fit to container height
+ displayHeight = containerHeight;
+ displayWidth = containerHeight * videoAspectRatio;
+ }
+
+ VideoImage().Width(displayWidth);
+ VideoImage().Height(displayHeight);
+ OutputDebugStringA(("[DEBUG] Video size set to: " + std::to_string(displayWidth) + "x" + std::to_string(displayHeight) + "\n").c_str());
+ }
+
+ void VideoPlayerControl::Seek(double timeSeconds)
+ {
+ OutputDebugStringA(("[DEBUG] Seek to: " + std::to_string(timeSeconds) + "s\n").c_str());
+ if (!m_isLoaded || !m_fileReader) return;
+
+ // Stop playback during seek
+ bool wasPlaying = m_isPlaying;
+ if (m_isPlaying) {
+ Pause();
+ }
+
+ // Seek to the specified time
+ if (m_fileReader->SeekToTime(timeSeconds)) {
+ m_currentTime = timeSeconds;
+ m_currentFrame = static_cast(timeSeconds * m_frameRate);
+
+ // Process one frame to update display
+ ProcessSingleFrame();
+
+ // Resume playback if it was playing before seek
+ if (wasPlaying) {
+ Play();
+ }
+
+ UpdateStatus(L"Seeked");
+ } else {
+ OutputDebugStringA("[DEBUG] Seek operation failed\n");
+ }
+ }
- // Additional required methods for linker
- void VideoPlayerControl::UpdateVideoImageAspectFit(int videoWidth, int videoHeight) { /* AspectFit */ }
- void VideoPlayerControl::Seek(double timeSeconds) { /* Seek */ }
bool VideoPlayerControl::IsVideoPlaying() { return m_isPlaying; }
bool VideoPlayerControl::IsVideoLoaded() { return m_isLoaded; }
double VideoPlayerControl::CurrentTime() { return m_currentTime; }
double VideoPlayerControl::Duration() { return m_duration; }
winrt::hstring VideoPlayerControl::Status() { return m_status; }
- void VideoPlayerControl::StartControlsHideTimer() { /* Start Hide Timer */ }
-}
+
+ void VideoPlayerControl::StartControlsHideTimer() { /* Simplified implementation */ }
+ void VideoPlayerControl::StopControlsHideTimer() { /* Simplified implementation */ }
+}
\ No newline at end of file
diff --git a/vav2/Vav2Player/Vav2Player/VideoPlayerControl.xaml.h b/vav2/Vav2Player/Vav2Player/VideoPlayerControl.xaml.h
index b38ef70..31a766b 100644
--- a/vav2/Vav2Player/Vav2Player/VideoPlayerControl.xaml.h
+++ b/vav2/Vav2Player/Vav2Player/VideoPlayerControl.xaml.h
@@ -4,18 +4,10 @@
#include "src/FileIO/WebMFileReader.h"
#include "src/Decoder/VideoDecoderFactory.h"
#include "src/Common/VideoTypes.h"
-// Simplified architecture - removed FramePool
-#include "src/Rendering/D3D12VideoRenderer.h"
#include "src/Rendering/SimpleGPURenderer.h"
#include
#include
#include
-#include
-
-// Forward declarations
-namespace Vav2Player {
- // Only essential components remain
-}
namespace winrt::Vav2Player::implementation
{
@@ -27,14 +19,10 @@ namespace winrt::Vav2Player::implementation
// Events
void UserControl_Loaded(winrt::Windows::Foundation::IInspectable const& sender, winrt::Microsoft::UI::Xaml::RoutedEventArgs const& e);
void UserControl_Unloaded(winrt::Windows::Foundation::IInspectable const& sender, winrt::Microsoft::UI::Xaml::RoutedEventArgs const& e);
- // Control event handlers (commented out as controls are disabled)
- // void PlayPauseButton_Click(winrt::Windows::Foundation::IInspectable const& sender, winrt::Microsoft::UI::Xaml::RoutedEventArgs const& e);
- // void StopButton_Click(winrt::Windows::Foundation::IInspectable const& sender, winrt::Microsoft::UI::Xaml::RoutedEventArgs const& e);
- // void ProgressSlider_ValueChanged(winrt::Windows::Foundation::IInspectable const& sender, winrt::Microsoft::UI::Xaml::Controls::Primitives::RangeBaseValueChangedEventArgs const& e);
void HoverDetector_PointerEntered(winrt::Windows::Foundation::IInspectable const& sender, winrt::Microsoft::UI::Xaml::Input::PointerRoutedEventArgs const& e);
void HoverDetector_PointerExited(winrt::Windows::Foundation::IInspectable const& sender, winrt::Microsoft::UI::Xaml::Input::PointerRoutedEventArgs const& e);
- // Public Properties (Dependency Properties will be added later)
+ // Public Properties
winrt::hstring VideoSource();
void VideoSource(winrt::hstring const& value);
@@ -47,11 +35,9 @@ namespace winrt::Vav2Player::implementation
Vav2Player::VideoDecoderType DecoderType();
void DecoderType(Vav2Player::VideoDecoderType value);
- // Hardware rendering control
bool UseHardwareRendering();
void UseHardwareRendering(bool value);
- // Internal decoder type management
VideoDecoderFactory::DecoderType GetInternalDecoderType();
void SetInternalDecoderType(VideoDecoderFactory::DecoderType value);
@@ -74,17 +60,13 @@ namespace winrt::Vav2Player::implementation
std::unique_ptr m_fileReader;
std::unique_ptr m_decoder;
- // Simplified processing - no complex pipelines
-
// Video rendering components
winrt::Microsoft::UI::Xaml::Media::Imaging::WriteableBitmap m_renderBitmap{ nullptr };
std::vector m_bgraBuffer;
-
- // D3D12 Hardware rendering (Phase 1)
- std::unique_ptr m_d3d12Renderer;
std::unique_ptr m_gpuRenderer;
bool m_useHardwareRendering = false;
- bool m_hardwareEnvironmentCriticalError = false; // Critical system-level hardware issue
+ // Playback timer for continuous frame processing
+ winrt::Microsoft::UI::Xaml::DispatcherTimer m_playbackTimer;
// Video dimensions
uint32_t m_videoWidth = 0;
@@ -96,10 +78,6 @@ namespace winrt::Vav2Player::implementation
bool m_autoPlay = false;
VideoDecoderFactory::DecoderType m_decoderType = VideoDecoderFactory::DecoderType::AUTO;
- // Decoder reuse optimization to prevent excessive dav1d worker thread creation
- VideoCodecType m_lastCodecType = VideoCodecType::AV1;
- VideoDecoderFactory::DecoderType m_lastDecoderType = VideoDecoderFactory::DecoderType::AUTO;
-
// Playback state
std::atomic m_isPlaying{ false };
std::atomic m_isLoaded{ false };
@@ -111,41 +89,22 @@ namespace winrt::Vav2Player::implementation
double m_duration = 0.0;
winrt::hstring m_status = L"Ready";
- // Timer for playback
- winrt::Microsoft::UI::Dispatching::DispatcherQueueTimer m_playbackTimer{ nullptr };
- winrt::Microsoft::UI::Dispatching::DispatcherQueueTimer m_controlsHideTimer{ nullptr };
-
// Helper methods
void InitializeVideoRenderer();
- void InitializeHardwareRenderer(int width, int height);
- void InitializeSoftwareRenderer(int width, int height);
void ProcessSingleFrame();
void RenderFrameToScreen(const VideoFrame& frame);
- void RenderFrameHardware(const VideoFrame& frame);
void RenderFrameSoftware(const VideoFrame& frame);
- void ProcessSingleFrameSimple(); // Single linear processing path
- void ProcessSingleFrameZeroCopy(); // Zero-copy processing path
- void ProcessSingleFrameRingBuffer(); // Ring buffer processing path
- void ProcessSingleFrameLegacy(); // Legacy CPU processing path
+ void ProcessSingleFrameLegacy();
void ConvertYUVToBGRA(const VideoFrame& yuv_frame, uint8_t* bgra_buffer, uint32_t width, uint32_t height);
-
- // Simplified processing helpers
void UpdateVideoImageAspectFit(int videoWidth, int videoHeight);
void UpdateStatus(winrt::hstring const& message);
- void UpdatePlaybackUI();
- void UpdateProgress();
- void StartPlaybackTimer();
- void StopPlaybackTimer();
void ShowControlsInternal();
- void HideControls();
void StartControlsHideTimer();
void StopControlsHideTimer();
- std::string FormatTime(double seconds);
-
- // Internal state management
void ResetVideoState();
bool CreateDecoder();
bool InitializeDecoder();
+ void ShowPurpleOutlinePlaceholder();
};
}
diff --git a/vav2/Vav2Player/Vav2Player/headless/SimpleHeadlessMain.cpp b/vav2/Vav2Player/Vav2Player/headless/SimpleHeadlessMain.cpp
index 69a8ead..7e5f695 100644
--- a/vav2/Vav2Player/Vav2Player/headless/SimpleHeadlessMain.cpp
+++ b/vav2/Vav2Player/Vav2Player/headless/SimpleHeadlessMain.cpp
@@ -1,121 +1,87 @@
#include "pch.h"
-#include "../src/Rendering/SimpleGPURenderer_Headless.h"
+#include "../src/Decoder/VideoDecoderFactory.h"
+#include "../src/FileIO/WebMFileReader.h"
-// Forward declarations
-int TestSimpleGPUIntegration();
-int TestAV1GPUIntegration(const std::string& videoFile);
-
-// Phase 3: Test SimpleGPURenderer basic functionality
int main(int argc, char* argv[])
{
SetConsoleCP(CP_UTF8);
SetConsoleOutputCP(CP_UTF8);
- std::cout << "=== Vav2Player Phase 3 Headless Test ===" << std::endl;
- std::cout << "Testing SimpleGPURenderer and basic dependencies" << std::endl;
+ std::cout << "=== MAJOR_REFACTORING_GUIDE Phase 3: Basic Video Test ===" << std::endl;
if (argc < 2) {
- std::cout << "Usage: Vav2PlayerHeadless.exe " << std::endl;
+ std::cout << "Usage: " << argv[0] << " " << std::endl;
return 1;
}
- std::string videoFile = argv[1];
- std::cout << "Would test with video file: " << videoFile << std::endl;
-
- // Test 1: Basic library dependencies
- std::cout << "[TEST] Testing basic library dependencies..." << std::endl;
-
- Dav1dSettings settings;
- dav1d_default_settings(&settings);
- std::cout << "[PASS] dav1d library loaded successfully" << std::endl;
+ std::string filePath = argv[1];
+ std::cout << "Testing video file: " << filePath << std::endl;
try {
- Dav1dContext* ctx = nullptr;
- int result = dav1d_open(&ctx, &settings);
-
- if (result == 0 && ctx != nullptr) {
- std::cout << "[PASS] dav1d_open successful" << std::endl;
- dav1d_close(&ctx);
- } else {
- std::cout << "[FAIL] dav1d_open failed with error: " << result << std::endl;
- }
- } catch (...) {
- std::cout << "[FAIL] dav1d_open crashed" << std::endl;
- }
-
- // Test 2: SimpleGPURenderer creation and destruction
- std::cout << "[TEST] Testing SimpleGPURenderer basic functionality..." << std::endl;
-
- try {
- std::unique_ptr renderer =
- std::make_unique();
-
- if (renderer) {
- std::cout << "[PASS] SimpleGPURenderer created successfully" << std::endl;
-
- // Test basic properties
- std::cout << "[INFO] Initial state - Width: " << renderer->GetWidth()
- << ", Height: " << renderer->GetHeight()
- << ", Initialized: " << (renderer->IsInitialized() ? "Yes" : "No") << std::endl;
-
- // Test GPU pipeline initialization
- std::cout << "[TEST] Testing GPU pipeline initialization..." << std::endl;
- HRESULT hr = renderer->Initialize(1920, 1080);
- if (SUCCEEDED(hr)) {
- std::cout << "[PASS] GPU pipeline initialized successfully" << std::endl;
-
- // Test GPU pipeline functionality
- hr = renderer->TestGPUPipeline();
- if (SUCCEEDED(hr)) {
- std::cout << "[PASS] GPU pipeline test completed successfully" << std::endl;
- } else {
- std::cout << "[FAIL] GPU pipeline test failed: 0x" << std::hex << hr << std::endl;
- }
- } else {
- std::cout << "[FAIL] GPU pipeline initialization failed: 0x" << std::hex << hr << std::endl;
- }
-
- std::cout << "[INFO] Testing SimpleGPURenderer destruction..." << std::endl;
- renderer.reset(); // Test destruction
- std::cout << "[PASS] SimpleGPURenderer destroyed successfully" << std::endl;
- } else {
- std::cout << "[FAIL] SimpleGPURenderer creation failed" << std::endl;
+ // Test WebMFileReader
+ auto fileReader = std::make_unique();
+ if (!fileReader->OpenFile(filePath)) {
+ std::cout << "Failed to open video file" << std::endl;
return 1;
}
+
+ auto tracks = fileReader->GetVideoTracks();
+ if (tracks.empty()) {
+ std::cout << "No video tracks found" << std::endl;
+ return 1;
+ }
+
+ std::cout << "Found " << tracks.size() << " video track(s)" << std::endl;
+
+ // Select first track
+ if (!fileReader->SelectVideoTrack(tracks[0].track_number)) {
+ std::cout << "Failed to select video track" << std::endl;
+ return 1;
+ }
+
+ auto metadata = fileReader->GetVideoMetadata();
+ std::cout << "Video: " << metadata.width << "x" << metadata.height
+ << " @ " << metadata.frame_rate << " fps" << std::endl;
+
+ // Test decoder creation
+ auto decoder = VideoDecoderFactory::CreateDecoder(VideoCodecType::AV1, VideoDecoderFactory::DecoderType::AUTO);
+ if (!decoder) {
+ std::cout << "Failed to create AV1 decoder" << std::endl;
+ return 1;
+ }
+
+ if (!decoder->Initialize(metadata)) {
+ std::cout << "Failed to initialize decoder" << std::endl;
+ return 1;
+ }
+
+ std::cout << "Decoder initialized successfully" << std::endl;
+
+ // Test a few frames
+ for (int i = 0; i < 5; i++) {
+ VideoPacket packet;
+ if (!fileReader->ReadNextPacket(packet)) {
+ std::cout << "End of file or read error at frame " << i << std::endl;
+ break;
+ }
+
+ VideoFrame frame;
+ if (decoder->DecodeFrame(packet, frame)) {
+ std::cout << "Frame " << i << ": " << frame.width << "x" << frame.height << std::endl;
+ } else {
+ std::cout << "Frame " << i << ": decode failed" << std::endl;
+ }
+ }
+
+ std::cout << "=== MAJOR_REFACTORING_GUIDE Phase 3: Test completed successfully ===" << std::endl;
+ std::cout << "Basic video decoding pipeline verified!" << std::endl;
+ return 0;
+
} catch (const std::exception& e) {
- std::cout << "[FAIL] Exception during SimpleGPURenderer test: " << e.what() << std::endl;
+ std::cout << "Exception: " << e.what() << std::endl;
return 1;
} catch (...) {
- std::cout << "[FAIL] Unknown exception during SimpleGPURenderer test" << std::endl;
+ std::cout << "Unknown exception occurred" << std::endl;
return 1;
}
-
- std::cout << "[SUCCESS] Phase 3 headless test completed successfully!" << std::endl;
- std::cout << "SimpleGPURenderer infrastructure verified" << std::endl;
-
- // Run simple GPU integration test with mock data
- std::cout << "\n=== Running Simple GPU Integration Test ===" << std::endl;
- int result = TestSimpleGPUIntegration();
- if (result != 0) {
- std::cout << "\n[FAIL] Simple GPU integration test failed" << std::endl;
- return result;
- }
-
- // Run AV1 + GPU integration test with real video file
- if (argc >= 2) {
- std::cout << "\n=== Running AV1 + GPU Integration Test ===" << std::endl;
- result = TestAV1GPUIntegration(videoFile);
- if (result != 0) {
- std::cout << "\n[FAIL] AV1 + GPU integration test failed" << std::endl;
- return result;
- }
- } else {
- std::cout << "\n[INFO] Skipping AV1 + GPU integration test (no video file provided)" << std::endl;
- }
-
- std::cout << "\nGPU pipeline foundation is ready for full implementation" << std::endl;
- std::cout << "Press Enter to exit...";
- std::cin.get();
-
- return 0;
-}
\ No newline at end of file
+}
diff --git a/vav2/Vav2Player/Vav2Player/src/Common/COMWrapper.h b/vav2/Vav2Player/Vav2Player/src/Common/COMWrapper.h
deleted file mode 100644
index a82848d..0000000
--- a/vav2/Vav2Player/Vav2Player/src/Common/COMWrapper.h
+++ /dev/null
@@ -1,87 +0,0 @@
-#pragma once
-
-#include
-
-namespace Vav2Player {
-
-// Simple RAII wrapper for COM objects
-template
-class COMWrapper {
-public:
- COMWrapper() : ptr_(nullptr) {}
-
- explicit COMWrapper(T* ptr) : ptr_(ptr) {
- if (ptr_) ptr_->AddRef();
- }
-
- COMWrapper(const COMWrapper& other) : ptr_(other.ptr_) {
- if (ptr_) ptr_->AddRef();
- }
-
- COMWrapper(COMWrapper&& other) noexcept : ptr_(other.ptr_) {
- other.ptr_ = nullptr;
- }
-
- ~COMWrapper() {
- if (ptr_) ptr_->Release();
- }
-
- COMWrapper& operator=(const COMWrapper& other) {
- if (this != &other) {
- if (ptr_) ptr_->Release();
- ptr_ = other.ptr_;
- if (ptr_) ptr_->AddRef();
- }
- return *this;
- }
-
- COMWrapper& operator=(COMWrapper&& other) noexcept {
- if (this != &other) {
- if (ptr_) ptr_->Release();
- ptr_ = other.ptr_;
- other.ptr_ = nullptr;
- }
- return *this;
- }
-
- // ComPtr-like interface
- T* Get() const { return ptr_; }
- T** GetAddressOf() { Reset(); return &ptr_; }
- T* operator->() const { return ptr_; }
- T& operator*() const { return *ptr_; }
-
- void Reset() {
- if (ptr_) {
- ptr_->Release();
- ptr_ = nullptr;
- }
- }
-
- void Attach(T* ptr) {
- if (ptr_) ptr_->Release();
- ptr_ = ptr;
- }
-
- T* Detach() {
- T* temp = ptr_;
- ptr_ = nullptr;
- return temp;
- }
-
- explicit operator bool() const { return ptr_ != nullptr; }
-
- // QueryInterface helper
- template
- HRESULT As(COMWrapper& result) const {
- return ptr_ ? ptr_->QueryInterface(__uuidof(U), reinterpret_cast(result.GetAddressOf())) : E_POINTER;
- }
-
-private:
- T* ptr_;
-};
-
-// Type alias
-template
-using ComPtr = COMWrapper;
-
-} // namespace Vav2Player
\ No newline at end of file
diff --git a/vav2/Vav2Player/Vav2Player/src/Common/ComPtrReplacements.h b/vav2/Vav2Player/Vav2Player/src/Common/ComPtrReplacements.h
deleted file mode 100644
index 06d8e43..0000000
--- a/vav2/Vav2Player/Vav2Player/src/Common/ComPtrReplacements.h
+++ /dev/null
@@ -1,54 +0,0 @@
-#pragma once
-
-#include
-#include
-
-namespace Vav2Player {
-
-// COM object deleter for std::shared_ptr
-struct COMDeleter {
- template
- void operator()(T* ptr) const {
- if (ptr) {
- ptr->Release();
- }
- }
-};
-
-// Type alias for COM objects with std::shared_ptr
-template
-using com_ptr = std::shared_ptr;
-
-// Helper function to create COM shared_ptr
-template
-com_ptr make_com_ptr(T* ptr) {
- return com_ptr(ptr, COMDeleter{});
-}
-
-// Helper function for QueryInterface
-template
-com_ptr com_cast(const com_ptr& ptr) {
- if (!ptr) return nullptr;
-
- T* result = nullptr;
- HRESULT hr = ptr->QueryInterface(__uuidof(T), reinterpret_cast(&result));
- if (SUCCEEDED(hr) && result) {
- return make_com_ptr(result);
- }
- return nullptr;
-}
-
-// Helper for .Get() equivalent
-template
-T* get_raw_ptr(const com_ptr& ptr) {
- return ptr.get();
-}
-
-// Helper for .GetAddressOf() equivalent
-template
-T** get_address_of(com_ptr& ptr) {
- ptr.reset(); // Release current object
- return reinterpret_cast(&ptr); // This is unsafe but needed for COM APIs
-}
-
-} // namespace Vav2Player
\ No newline at end of file
diff --git a/vav2/Vav2Player/Vav2Player/src/Common/PermissionUtils.cpp b/vav2/Vav2Player/Vav2Player/src/Common/PermissionUtils.cpp
deleted file mode 100644
index 0969dd5..0000000
--- a/vav2/Vav2Player/Vav2Player/src/Common/PermissionUtils.cpp
+++ /dev/null
@@ -1,253 +0,0 @@
-#include "pch.h"
-#include "PermissionUtils.h"
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-
-namespace Vav2Player {
-
-PermissionUtils::PermissionResult PermissionUtils::CheckDirectoryCreatePermission(const std::filesystem::path& directory_path) {
- try {
- // 1. If directory already exists, check write permission
- if (std::filesystem::exists(directory_path)) {
- if (TestDirectoryWrite(directory_path)) {
- OutputDebugStringA(("[PermissionUtils] Directory exists and writable: " + directory_path.string() + "\n").c_str());
- return PermissionResult::Granted;
- } else {
- OutputDebugStringA(("[PermissionUtils] Directory exists but not writable: " + directory_path.string() + "\n").c_str());
- return PermissionResult::Denied;
- }
- }
-
- // 2. Check creation permission in parent directory
- std::filesystem::path parent_path = directory_path.parent_path();
- if (!std::filesystem::exists(parent_path)) {
- // Recursively check if parent directory doesn't exist
- auto parent_result = CheckDirectoryCreatePermission(parent_path);
- if (parent_result != PermissionResult::Granted) {
- return parent_result;
- }
- }
-
- // 3. Actually test temporary directory creation
- std::filesystem::path test_dir = parent_path / ("test_permission_" + std::to_string(GetTickCount64()));
-
- std::error_code ec;
- bool created = std::filesystem::create_directory(test_dir, ec);
-
- if (created && !ec) {
- // Creation successful, cleanup
- std::filesystem::remove(test_dir, ec);
- OutputDebugStringA(("[PermissionUtils] Directory creation test successful: " + directory_path.string() + "\n").c_str());
- return PermissionResult::Granted;
- } else {
- OutputDebugStringA(("[PermissionUtils] Directory creation test failed: " + directory_path.string() + ", Error: " + ec.message() + "\n").c_str());
- return PermissionResult::Denied;
- }
-
- } catch (const std::exception& e) {
- OutputDebugStringA(("[PermissionUtils] Exception in CheckDirectoryCreatePermission: " + std::string(e.what()) + "\n").c_str());
- return PermissionResult::Error;
- }
-}
-
-PermissionUtils::PermissionResult PermissionUtils::CheckFileWritePermission(const std::filesystem::path& file_path) {
- try {
- // 1. Check directory existence
- std::filesystem::path parent_dir = file_path.parent_path();
- if (!std::filesystem::exists(parent_dir)) {
- auto dir_result = CheckDirectoryCreatePermission(parent_dir);
- if (dir_result != PermissionResult::Granted) {
- return dir_result;
- }
- }
-
- // 2. Test temporary file creation
- std::filesystem::path test_file = parent_dir / ("test_write_" + std::to_string(GetTickCount64()) + ".tmp");
-
- std::ofstream test_stream(test_file, std::ios::binary);
- if (test_stream.is_open()) {
- test_stream << "permission test";
- test_stream.close();
-
- // Cleanup
- std::error_code ec;
- std::filesystem::remove(test_file, ec);
-
- OutputDebugStringA(("[PermissionUtils] File write test successful: " + file_path.string() + "\n").c_str());
- return PermissionResult::Granted;
- } else {
- OutputDebugStringA(("[PermissionUtils] File write test failed: " + file_path.string() + "\n").c_str());
- return PermissionResult::Denied;
- }
-
- } catch (const std::exception& e) {
- OutputDebugStringA(("[PermissionUtils] Exception in CheckFileWritePermission: " + std::string(e.what()) + "\n").c_str());
- return PermissionResult::Error;
- }
-}
-
-bool PermissionUtils::IsRunningAsAdmin() {
- BOOL is_admin = FALSE;
- PSID admin_group = nullptr;
- SID_IDENTIFIER_AUTHORITY nt_authority = SECURITY_NT_AUTHORITY;
-
- if (AllocateAndInitializeSid(&nt_authority, 2,
- SECURITY_BUILTIN_DOMAIN_RID,
- DOMAIN_ALIAS_RID_ADMINS,
- 0, 0, 0, 0, 0, 0,
- &admin_group)) {
-
- if (!::CheckTokenMembership(nullptr, admin_group, &is_admin)) {
- is_admin = FALSE;
- }
- FreeSid(admin_group);
- }
-
- OutputDebugStringA(is_admin ? "[PermissionUtils] Running as administrator\n" : "[PermissionUtils] Not running as administrator\n");
- return is_admin == TRUE;
-}
-
-bool PermissionUtils::RequestAdminRestart() {
- wchar_t exe_path[MAX_PATH];
- DWORD path_length = GetModuleFileNameW(nullptr, exe_path, MAX_PATH);
-
- if (path_length == 0 || path_length == MAX_PATH) {
- OutputDebugStringA("[PermissionUtils] Failed to get executable path for restart\n");
- return false;
- }
-
- // Request administrator permission with ShellExecute
- HINSTANCE result = ShellExecuteW(nullptr, L"runas", exe_path, nullptr, nullptr, SW_SHOWNORMAL);
-
- bool success = reinterpret_cast(result) > 32;
- if (success) {
- OutputDebugStringA("[PermissionUtils] Requested admin restart\n");
- // Terminate current process
- PostQuitMessage(0);
- } else {
- OutputDebugStringA("[PermissionUtils] Failed to request admin restart\n");
- }
-
- return success;
-}
-
-bool PermissionUtils::ShowPermissionDialog(const std::wstring& message, const std::wstring& title) {
- int result = MessageBoxW(nullptr, message.c_str(), title.c_str(),
- MB_YESNO | MB_ICONWARNING | MB_TOPMOST);
-
- return result == IDYES;
-}
-
-std::filesystem::path PermissionUtils::GetSafeOutputDirectory() {
- // Try safe directories in priority order
- std::vector safe_directories;
-
- // 1. User documents folder
- wchar_t* documents_path = nullptr;
- if (SUCCEEDED(SHGetKnownFolderPath(FOLDERID_Documents, 0, nullptr, &documents_path))) {
- safe_directories.push_back(std::filesystem::path(documents_path) / "Vav2Player");
- CoTaskMemFree(documents_path);
- }
-
- // 2. User temporary folder
- wchar_t temp_path[MAX_PATH];
- if (GetTempPathW(MAX_PATH, temp_path)) {
- safe_directories.push_back(std::filesystem::path(temp_path) / "Vav2Player");
- }
-
- // 3. Current user profile folder
- wchar_t* profile_path = nullptr;
- if (SUCCEEDED(SHGetKnownFolderPath(FOLDERID_Profile, 0, nullptr, &profile_path))) {
- safe_directories.push_back(std::filesystem::path(profile_path) / "Vav2Player");
- CoTaskMemFree(profile_path);
- }
-
- // 4. Same directory as executable
- wchar_t exe_path[MAX_PATH];
- if (GetModuleFileNameW(nullptr, exe_path, MAX_PATH)) {
- std::filesystem::path exe_dir = std::filesystem::path(exe_path).parent_path();
- safe_directories.push_back(exe_dir / "output");
- }
-
- // Check permissions for each directory
- for (const auto& dir : safe_directories) {
- if (CheckDirectoryCreatePermission(dir) == PermissionResult::Granted) {
- OutputDebugStringA(("[PermissionUtils] Found safe directory: " + dir.string() + "\n").c_str());
- return dir;
- }
- }
-
- // Default value when all attempts fail
- OutputDebugStringA("[PermissionUtils] No safe directory found, using fallback\n");
- return std::filesystem::path("C:\\temp\\Vav2Player");
-}
-
-PermissionUtils::PermissionResult PermissionUtils::CheckAndHandlePermissions(const std::filesystem::path& output_directory) {
- OutputDebugStringA(("[PermissionUtils] Checking permissions for: " + output_directory.string() + "\n").c_str());
-
- // 1. Basic permission check
- auto result = CheckDirectoryCreatePermission(output_directory);
-
- if (result == PermissionResult::Granted) {
- return result;
- }
-
- // 2. Handle case when permission is denied
- std::wstring message;
-
- if (result == PermissionResult::Denied) {
- std::wstring dir_path = std::wstring(output_directory.string().begin(), output_directory.string().end());
- message = L"No write permission to output directory:\n" + dir_path +
- L"\n\nWould you like to restart with administrator privileges?\n(Selecting No will use a safe location)";
- } else {
- std::wstring dir_path = std::wstring(output_directory.string().begin(), output_directory.string().end());
- message = L"An error occurred while checking directory permissions:\n" + dir_path +
- L"\n\nWould you like to restart with administrator privileges?";
- }
-
- // 3. Provide choice to user
- if (ShowPermissionDialog(message, L"Directory Permission Required")) {
- // Request restart with administrator privileges
- if (RequestAdminRestart()) {
- // Restart request successful (app will terminate)
- return PermissionResult::Granted;
- } else {
- // Restart failed - use safe directory
- return PermissionResult::Denied;
- }
- } else {
- // User denied administrator privileges - use safe directory
- return PermissionResult::Denied;
- }
-}
-
-bool PermissionUtils::TestDirectoryWrite(const std::filesystem::path& directory_path) {
- try {
- std::filesystem::path test_file = directory_path / ("write_test_" + std::to_string(GetTickCount64()) + ".tmp");
-
- std::ofstream test_stream(test_file, std::ios::binary);
- if (!test_stream.is_open()) {
- return false;
- }
-
- test_stream << "write permission test";
- test_stream.close();
-
- // Cleanup
- std::error_code ec;
- std::filesystem::remove(test_file, ec);
-
- return true;
-
- } catch (const std::exception&) {
- return false;
- }
-}
-
-
-} // namespace Vav2Player
\ No newline at end of file
diff --git a/vav2/Vav2Player/Vav2Player/src/Common/PermissionUtils.h b/vav2/Vav2Player/Vav2Player/src/Common/PermissionUtils.h
deleted file mode 100644
index 0f960b1..0000000
--- a/vav2/Vav2Player/Vav2Player/src/Common/PermissionUtils.h
+++ /dev/null
@@ -1,44 +0,0 @@
-#pragma once
-#include
-#include
-#include
-
-namespace Vav2Player {
-
-class PermissionUtils {
-public:
- // 권한 체크 결과
- enum class PermissionResult {
- Granted, // 권한 있음
- Denied, // 권한 없음
- Error // 체크 중 오류 발생
- };
-
- // 디렉토리 생성 권한 체크
- static PermissionResult CheckDirectoryCreatePermission(const std::filesystem::path& directory_path);
-
- // 파일 쓰기 권한 체크
- static PermissionResult CheckFileWritePermission(const std::filesystem::path& file_path);
-
- // 관리자 권한으로 실행 중인지 확인
- static bool IsRunningAsAdmin();
-
- // 관리자 권한으로 재시작 요청
- static bool RequestAdminRestart();
-
- // 사용자에게 권한 관련 메시지 표시
- static bool ShowPermissionDialog(const std::wstring& message, const std::wstring& title = L"권한 필요");
-
- // 안전한 출력 디렉토리 제안
- static std::filesystem::path GetSafeOutputDirectory();
-
- // 권한 체크 및 처리 (통합 함수)
- static PermissionResult CheckAndHandlePermissions(const std::filesystem::path& output_directory);
-
-private:
- // 디렉토리에 대한 실제 쓰기 테스트
- static bool TestDirectoryWrite(const std::filesystem::path& directory_path);
-
-};
-
-} // namespace Vav2Player
\ No newline at end of file
diff --git a/vav2/Vav2Player/Vav2Player/src/Common/StdComPtr.h b/vav2/Vav2Player/Vav2Player/src/Common/StdComPtr.h
deleted file mode 100644
index 9ef45b9..0000000
--- a/vav2/Vav2Player/Vav2Player/src/Common/StdComPtr.h
+++ /dev/null
@@ -1,161 +0,0 @@
-#pragma once
-
-#include
-#include
-
-namespace Vav2Player {
-
-// Lightweight std-based ComPtr replacement
-// Maintains Microsoft::WRL::ComPtr compatibility while using std libraries
-template
-class StdComPtr {
-private:
- T* ptr_;
-
-public:
- StdComPtr() : ptr_(nullptr) {}
-
- explicit StdComPtr(T* ptr) : ptr_(ptr) {}
-
- StdComPtr(const StdComPtr& other) : ptr_(other.ptr_) {
- if (ptr_) ptr_->AddRef();
- }
-
- StdComPtr(StdComPtr&& other) noexcept : ptr_(other.ptr_) {
- other.ptr_ = nullptr;
- }
-
- ~StdComPtr() {
- if (ptr_) ptr_->Release();
- }
-
- StdComPtr& operator=(const StdComPtr& other) {
- if (this != &other) {
- if (ptr_) ptr_->Release();
- ptr_ = other.ptr_;
- if (ptr_) ptr_->AddRef();
- }
- return *this;
- }
-
- StdComPtr& operator=(StdComPtr&& other) noexcept {
- if (this != &other) {
- if (ptr_) ptr_->Release();
- ptr_ = other.ptr_;
- other.ptr_ = nullptr;
- }
- return *this;
- }
-
- StdComPtr& operator=(T* ptr) {
- if (ptr_) ptr_->Release();
- ptr_ = ptr;
- if (ptr_) ptr_->AddRef();
- return *this;
- }
-
- // Microsoft::WRL::ComPtr compatible interface
- T* Get() const { return ptr_; }
- T** GetAddressOf() {
- Reset();
- return &ptr_;
- }
- T* const* GetAddressOf() const {
- return &ptr_;
- }
- T** ReleaseAndGetAddressOf() {
- Reset();
- return &ptr_;
- }
- T* operator->() const { return ptr_; }
- T& operator*() const { return *ptr_; }
- explicit operator bool() const { return ptr_ != nullptr; }
-
- // Address-of operator for COM API compatibility
- T** operator&() {
- Reset();
- return &ptr_;
- }
-
- // Comparison operators
- bool operator==(std::nullptr_t) const { return ptr_ == nullptr; }
- bool operator!=(std::nullptr_t) const { return ptr_ != nullptr; }
- bool operator==(const StdComPtr& other) const { return ptr_ == other.ptr_; }
- bool operator!=(const StdComPtr& other) const { return ptr_ != other.ptr_; }
- bool operator<(const StdComPtr& other) const { return ptr_ < other.ptr_; }
-
- void Reset() {
- if (ptr_) {
- ptr_->Release();
- ptr_ = nullptr;
- }
- }
-
- void Attach(T* ptr) {
- if (ptr_) ptr_->Release();
- ptr_ = ptr;
- }
-
- T* Detach() {
- T* temp = ptr_;
- ptr_ = nullptr;
- return temp;
- }
-
- template
- HRESULT As(StdComPtr* result) const {
- return ptr_ ? ptr_->QueryInterface(__uuidof(U), reinterpret_cast(result->GetAddressOf())) : E_POINTER;
- }
-
- // Handle COM QueryInterface with raw pointer-to-pointer
- template
- HRESULT As(U** result) const {
- return ptr_ ? ptr_->QueryInterface(__uuidof(U), reinterpret_cast(result)) : E_POINTER;
- }
-
- // Direct QueryInterface support
- HRESULT QueryInterface(const IID& riid, void** ppvObject) const {
- return ptr_ ? ptr_->QueryInterface(riid, ppvObject) : E_POINTER;
- }
-
- // Support for IID_PPV_ARGS macro compatibility
- template
- HRESULT As(const IID& riid, void** ppvObject) const {
- return ptr_ ? ptr_->QueryInterface(riid, ppvObject) : E_POINTER;
- }
-};
-
-// Type alias for easy migration
-template
-using ComPtr = StdComPtr;
-
-// Helper function to make IID_PPV_ARGS work with StdComPtr
-template
-void** IID_PPV_ARGS_Helper(StdComPtr* pp) {
- return reinterpret_cast(pp->GetAddressOf());
-}
-
-// Global comparison operators for symmetry
-template
-bool operator==(std::nullptr_t, const StdComPtr& ptr) { return ptr == nullptr; }
-
-template
-bool operator!=(std::nullptr_t, const StdComPtr& ptr) { return ptr != nullptr; }
-
-} // namespace Vav2Player
-
-// Allow IID_PPV_ARGS to work with Vav2Player::StdComPtr
-namespace {
- template
- void** IID_PPV_ARGS_Helper(Vav2Player::StdComPtr* pp) {
- return reinterpret_cast(pp->GetAddressOf());
- }
-}
-
-// Macro to easily switch between Microsoft WRL and std version
-#ifdef USE_STD_COMPTR
- #define COMPTR_NAMESPACE Vav2Player
-#else
- #include
- #define COMPTR_NAMESPACE Microsoft::WRL
-#endif
\ No newline at end of file
diff --git a/vav2/Vav2Player/Vav2Player/src/Decoder/AV1Decoder.cpp b/vav2/Vav2Player/Vav2Player/src/Decoder/AV1Decoder.cpp
deleted file mode 100644
index a8daaa6..0000000
--- a/vav2/Vav2Player/Vav2Player/src/Decoder/AV1Decoder.cpp
+++ /dev/null
@@ -1,1371 +0,0 @@
-#include "pch.h"
-#include "AV1Decoder.h"
-#include "../Rendering/D3D12VideoRenderer.h"
-// Note: DirectTextureAllocator include removed during Phase 1 simplification
-#include
-#include
-
-namespace Vav2Player {
-
-AV1Decoder::AV1Decoder()
- : m_dav1d_context(nullptr)
- , m_initialized(false) {
- // Note: m_directTextureAllocator removed during Phase 1 simplification
- // Initialize default AV1 settings with conservative thread usage
- m_av1_settings.max_frame_delay = 1;
- m_av1_settings.num_threads = std::min(4, (int)std::thread::hardware_concurrency()); // Limit to 4 threads max
- m_av1_settings.apply_grain = true;
- m_av1_settings.all_layers = false;
-}
-
-AV1Decoder::~AV1Decoder() {
- Cleanup();
-}
-
-bool AV1Decoder::Initialize(const VideoMetadata& metadata) {
- if (m_initialized) {
- Cleanup();
- }
-
- m_metadata = metadata;
-
- if (!InitializeDav1d()) {
- LogError("Failed to initialize dav1d decoder");
- return false;
- }
-
- m_initialized = true;
- ResetStats();
- return true;
-}
-
-void AV1Decoder::Cleanup() {
- if (!m_initialized) return;
-
- CleanupDav1d();
- m_initialized = false;
-}
-
-bool AV1Decoder::IsInitialized() const {
- return m_initialized;
-}
-
-bool AV1Decoder::DecodeFrame(const VideoPacket& input_packet, VideoFrame& output_frame) {
- if (!input_packet.IsValid()) {
- 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) {
- IncrementDecodeErrors();
- return false;
- }
-
- auto start_time = std::chrono::high_resolution_clock::now();
-
- // Create dav1d input data (using memory copy method)
- Dav1dData dav1d_data;
- uint8_t* buffer = dav1d_data_create(&dav1d_data, packet_size);
- if (!buffer) {
- LogError("Failed to create dav1d data", -1);
- IncrementDecodeErrors();
- return false;
- }
-
- // Copy packet data to dav1d buffer
- memcpy(buffer, packet_data, packet_size);
-
- // Send data to decoder
- int res = dav1d_send_data(m_dav1d_context, &dav1d_data);
- if (res < 0) {
- LogError("Failed to send data to decoder", res);
- IncrementDecodeErrors();
- return false;
- }
-
- // Get decoded picture
- Dav1dPicture dav1d_picture = {};
- res = dav1d_get_picture(m_dav1d_context, &dav1d_picture);
- m_lastDecodeResult = res; // Store result for EAGAIN differentiation
-
- if (res < 0) {
- if (res != DAV1D_ERR(EAGAIN)) {
- LogError("Failed to get decoded picture", res);
- IncrementDecodeErrors();
- }
- // EAGAIN means no frame is ready yet, but it's not an error - just return false to try again
- return false;
- }
-
- // Convert dav1d picture to VideoFrame
- if (!ConvertDav1dPicture(dav1d_picture, output_frame)) {
- dav1d_picture_unref(&dav1d_picture);
- IncrementDecodeErrors();
- return false;
- }
-
- // Release picture reference
- dav1d_picture_unref(&dav1d_picture);
-
- auto end_time = std::chrono::high_resolution_clock::now();
- auto duration = std::chrono::duration_cast(end_time - start_time);
- double decode_time_ms = duration.count() / 1000.0;
-
- UpdateDecodingStats(decode_time_ms, packet_size);
- IncrementFramesDecoded();
-
- return true;
-}
-
-bool AV1Decoder::Reset() {
- if (!m_initialized) return false;
-
- // Flush internal buffers of dav1d context
- dav1d_flush(m_dav1d_context);
-
- // Reset statistics
- ResetStats();
-
- return true;
-}
-
-bool AV1Decoder::Flush() {
- if (!m_initialized) return false;
-
- // Retrieve all remaining frames from decoder
- // Used to process delayed frames at the end of stream
- while (true) {
- Dav1dPicture picture = {};
- int res = dav1d_get_picture(m_dav1d_context, &picture);
- if (res < 0) {
- // Exit when no more frames available or error occurs
- break;
- }
-
- // Release frame reference if available (currently ignored)
- // In actual streaming, these frames should be processed
- dav1d_picture_unref(&picture);
- }
-
- return true;
-}
-
-std::string AV1Decoder::GetCodecName() const {
- return "AV1";
-}
-
-VideoCodecType AV1Decoder::GetCodecType() const {
- return VideoCodecType::AV1;
-}
-
-std::string AV1Decoder::GetVersion() const {
- return AV1Utils::GetDav1dVersion();
-}
-
-IVideoDecoder::DecoderStats AV1Decoder::GetStats() const {
- return m_stats;
-}
-
-void AV1Decoder::ResetStats() {
- m_stats = {};
-}
-
-bool AV1Decoder::SetOption(const std::string& key, const std::string& value) {
- // Process AV1-specific options
- if (key == "max_frame_delay") {
- try {
- m_av1_settings.max_frame_delay = std::stoi(value);
- return true;
- } catch (...) {
- return false;
- }
- }
- else if (key == "num_threads") {
- try {
- m_av1_settings.num_threads = std::stoi(value);
- return true;
- } catch (...) {
- return false;
- }
- }
- else if (key == "apply_grain") {
- m_av1_settings.apply_grain = (value == "true" || value == "1");
- return true;
- }
- else if (key == "all_layers") {
- m_av1_settings.all_layers = (value == "true" || value == "1");
- return true;
- }
-
- return false; // Unsupported option
-}
-
-std::string AV1Decoder::GetOption(const std::string& key) const {
- if (key == "max_frame_delay") {
- return std::to_string(m_av1_settings.max_frame_delay);
- }
- else if (key == "num_threads") {
- return std::to_string(m_av1_settings.num_threads);
- }
- else if (key == "apply_grain") {
- return m_av1_settings.apply_grain ? "true" : "false";
- }
- else if (key == "all_layers") {
- return m_av1_settings.all_layers ? "true" : "false";
- }
-
- return "";
-}
-
-void AV1Decoder::SetAV1Settings(const AV1Settings& settings) {
- m_av1_settings = settings;
-
- if (m_initialized) {
- // Update dav1d settings when runtime configuration changes
- SetupDav1dSettings();
- }
-}
-
-AV1Decoder::AV1Settings AV1Decoder::GetAV1Settings() const {
- return m_av1_settings;
-}
-
-bool AV1Decoder::InitializeDav1d() {
- // Initialize dav1d default settings
- dav1d_default_settings(&m_dav1d_settings);
-
- if (!SetupDav1dSettings()) {
- return false;
- }
-
- // Safety validation: ensure critical settings are within valid ranges
- if (m_dav1d_settings.n_threads < 0 || m_dav1d_settings.n_threads > 64) {
- LogError("Invalid thread count: " + std::to_string(m_dav1d_settings.n_threads));
- m_dav1d_settings.n_threads = 0; // Auto-detect
- }
-
- if (m_dav1d_settings.max_frame_delay < 0 || m_dav1d_settings.max_frame_delay > 256) {
- LogError("Invalid frame delay: " + std::to_string(m_dav1d_settings.max_frame_delay));
- m_dav1d_settings.max_frame_delay = 1; // Safe default
- }
-
- // CRITICAL: Ensure allocator callbacks are properly set (dav1d requires BOTH to be non-NULL)
- if (!m_dav1d_settings.allocator.alloc_picture_callback) {
- LogError("FATAL: alloc_picture_callback is NULL - dav1d_open will fail with EINVAL");
- return false;
- }
- if (!m_dav1d_settings.allocator.release_picture_callback) {
- LogError("FATAL: release_picture_callback is NULL - dav1d_open will fail with EINVAL");
- return false;
- }
-
- std::cout << "[AV1Decoder] Allocator callbacks verified: alloc="
- << (void*)m_dav1d_settings.allocator.alloc_picture_callback
- << " release=" << (void*)m_dav1d_settings.allocator.release_picture_callback << std::endl;
-
- std::cout << "[AV1Decoder] Initializing dav1d with validated settings" << std::endl;
- std::cout << "[AV1Decoder] - n_threads: " << m_dav1d_settings.n_threads << std::endl;
- std::cout << "[AV1Decoder] - max_frame_delay: " << m_dav1d_settings.max_frame_delay << std::endl;
- std::cout << "[AV1Decoder] - apply_grain: " << m_dav1d_settings.apply_grain << std::endl;
- std::cout << "[AV1Decoder] - all_layers: " << m_dav1d_settings.all_layers << std::endl;
-
- // SAFETY: Try to call dav1d_version first to ensure library is accessible
- try {
- const char* version = dav1d_version();
- std::cout << "[AV1Decoder] dav1d version: " << (version ? version : "unknown") << std::endl;
- } catch (...) {
- LogError("Failed to access dav1d library functions");
- return false;
- }
-
- // Create dav1d context with exception handling
- std::cout << "[AV1Decoder] Calling dav1d_open..." << std::endl;
- int res = -1;
- try {
- res = dav1d_open(&m_dav1d_context, &m_dav1d_settings);
- std::cout << "[AV1Decoder] dav1d_open returned: " << res << std::endl;
- } catch (...) {
- LogError("Exception occurred during dav1d_open");
- return false;
- }
-
- if (res < 0) {
- LogError("Failed to open dav1d context", res);
- return false;
- }
-
- return true;
-}
-
-void AV1Decoder::CleanupDav1d() {
- if (m_dav1d_context) {
- dav1d_close(&m_dav1d_context);
- m_dav1d_context = nullptr;
- }
-}
-
-bool AV1Decoder::SetupDav1dSettings() {
- // Convert AV1 settings to dav1d settings
- m_dav1d_settings.max_frame_delay = m_av1_settings.max_frame_delay;
- m_dav1d_settings.n_threads = m_av1_settings.num_threads;
- m_dav1d_settings.apply_grain = m_av1_settings.apply_grain;
- m_dav1d_settings.all_layers = m_av1_settings.all_layers;
-
- // Advanced optimization settings for dav1d (available in current version)
- m_dav1d_settings.strict_std_compliance = m_av1_settings.strict_std_compliance ? 1 : 0;
- m_dav1d_settings.operating_point = m_av1_settings.operating_point ? 1 : 0;
-
- // Set inloop filters based on performance preference
- if (m_av1_settings.enable_inloop_filters) {
- m_dav1d_settings.inloop_filters = DAV1D_INLOOPFILTER_ALL; // Enable for quality
- } else {
- m_dav1d_settings.inloop_filters = DAV1D_INLOOPFILTER_NONE; // Disable for max performance
- }
-
- // SAFETY: Use default allocator - dav1d requires non-NULL allocator callbacks
- // Setting to NULL causes DAV1D_ERR(EINVAL) in dav1d_open
- // Leave allocator as default (initialized by dav1d_default_settings)
-
- // Only set DirectTextureAllocator if it's properly validated
- // DISABLED for now to prevent crashes - enable only after thorough testing
- bool use_direct_texture_allocator = false;
-
- // Note: DirectTextureAllocator integration removed during Phase 1 simplification
- // Use default allocator (safest option)
- std::cout << "[AV1Decoder] Using default allocator (DirectTextureAllocator disabled)" << std::endl;
- // Keep default allocator set by dav1d_default_settings() - do NOT modify
-
- std::cout << "[AV1Decoder] Applied dav1d settings: threads=" << m_dav1d_settings.n_threads
- << ", max_delay=" << m_dav1d_settings.max_frame_delay
- << ", strict_compliance=" << m_dav1d_settings.strict_std_compliance
- << ", inloop_filters=" << static_cast(m_dav1d_settings.inloop_filters)
- << ", apply_grain=" << m_dav1d_settings.apply_grain << std::endl;
-
- return true;
-}
-
-bool AV1Decoder::ConvertDav1dPicture(const Dav1dPicture& dav1d_pic, VideoFrame& output_frame) {
- output_frame.Reset();
-
- // Set basic information
- output_frame.width = static_cast(dav1d_pic.p.w);
- output_frame.height = static_cast(dav1d_pic.p.h);
- output_frame.color_space = ConvertDav1dPixelFormat(dav1d_pic);
-
- // Allocate YUV420P data
- if (!output_frame.AllocateYUV420P(output_frame.width, output_frame.height)) {
- LogError("Failed to allocate YUV420P frame memory");
- return false;
- }
-
- // Get YUV data pointers from dav1d picture
- const uint8_t* src_y = static_cast(dav1d_pic.data[0]);
- const uint8_t* src_u = static_cast(dav1d_pic.data[1]);
- const uint8_t* src_v = static_cast(dav1d_pic.data[2]);
-
- if (!src_y || !src_u || !src_v) {
- LogError("Invalid dav1d picture data pointers");
- return false;
- }
-
- // Get stride information
- ptrdiff_t src_y_stride = dav1d_pic.stride[0];
- ptrdiff_t src_u_stride = dav1d_pic.stride[1];
- ptrdiff_t src_v_stride = dav1d_pic.stride[1]; // U/V have same stride
-
- // Copy Y plane (line by line)
- uint8_t* dst_y = output_frame.y_plane.get();
- for (uint32_t y = 0; y < output_frame.height; ++y) {
- memcpy(dst_y + y * output_frame.y_stride,
- src_y + y * src_y_stride,
- output_frame.width);
- }
-
- // Copy U plane (line by line)
- uint8_t* dst_u = output_frame.u_plane.get();
- uint32_t uv_height = output_frame.height / 2;
- uint32_t uv_width = output_frame.width / 2;
- for (uint32_t y = 0; y < uv_height; ++y) {
- memcpy(dst_u + y * output_frame.u_stride,
- src_u + y * src_u_stride,
- uv_width);
- }
-
- // Copy V plane (line by line)
- uint8_t* dst_v = output_frame.v_plane.get();
- for (uint32_t y = 0; y < uv_height; ++y) {
- memcpy(dst_v + y * output_frame.v_stride,
- src_v + y * src_v_stride,
- uv_width);
- }
-
- // Set frame metadata
- output_frame.is_valid = true;
-
- return true;
-}
-
-void AV1Decoder::UpdateDecodingStats(double decode_time_ms, size_t input_bytes) {
- UpdateDecodeTime(decode_time_ms);
- AddBytesProcessed(input_bytes);
-}
-
-ColorSpace AV1Decoder::ConvertDav1dPixelFormat(const Dav1dPicture& pic) {
- // Convert dav1d pixel format to ColorSpace
- switch (pic.p.layout) {
- case DAV1D_PIXEL_LAYOUT_I420:
- return ColorSpace::YUV420P;
- case DAV1D_PIXEL_LAYOUT_I422:
- return ColorSpace::YUV422P;
- case DAV1D_PIXEL_LAYOUT_I444:
- return ColorSpace::YUV444P;
- default:
- // Default to YUV420P for unsupported formats
- LogError("Unsupported pixel layout: " + std::to_string(static_cast(pic.p.layout)));
- return ColorSpace::YUV420P;
- }
-}
-
-std::string AV1Decoder::GetDav1dErrorString(int error_code) {
- // Convert dav1d error code to meaningful string
- switch (error_code) {
- case DAV1D_ERR(EAGAIN):
- return "EAGAIN (need more data)";
- case DAV1D_ERR(EINVAL):
- return "EINVAL (invalid parameter)";
- case DAV1D_ERR(ENOMEM):
- return "ENOMEM (out of memory)";
- case DAV1D_ERR(EIO):
- return "EIO (input/output error)";
- default:
- return "dav1d error: " + std::to_string(error_code);
- }
-}
-
-void AV1Decoder::LogError(const std::string& message, int error_code) {
- std::string full_message = "[AV1Decoder] " + message;
- if (error_code != 0) {
- full_message += " (" + GetDav1dErrorString(error_code) + ")";
- }
-
- // TODO: Integrate with actual logging system
- std::cerr << full_message << std::endl;
-}
-
-// AV1Utils implementation
-namespace AV1Utils {
-
-OBUType GetOBUType(const uint8_t* data, size_t size) {
- // TODO: Implement AV1 OBU header parsing
- if (!data || size < 1) return OBUType::UNKNOWN;
-
- uint8_t obu_type = (data[0] >> 3) & 0x0F;
- return static_cast(obu_type);
-}
-
-bool IsKeyFrame(const uint8_t* data, size_t size) {
- // TODO: Implement AV1 key frame detection logic
- return false;
-}
-
-std::string GetDav1dVersion() {
- // Get dav1d library version information
- const char* version = dav1d_version();
- return version ? std::string("dav1d ") + version : "dav1d (version unknown)";
-}
-
-std::string GetDav1dCopyright() {
- return "Copyright (c) 2018-2024, VideoLAN and dav1d authors";
-}
-
-} // namespace AV1Utils
-
-/*
-// Optimized decoding methods using memory pool - REMOVED in Phase 1 simplification
-ScopedFrame AV1Decoder::DecodeFramePooled(const VideoPacket& input_packet) {
- return DecodeFramePooled(input_packet.data.get(), input_packet.size);
-}
-
-ScopedFrame AV1Decoder::DecodeFramePooled(const uint8_t* packet_data, size_t packet_size) {
- if (!m_initialized || !packet_data || packet_size == 0) {
- return ScopedFrame(FramePool::PooledFrame{});
- }
-
- auto decode_start = std::chrono::high_resolution_clock::now();
-
- // Prepare dav1d data structure
- Dav1dData data;
- uint8_t* buffer = dav1d_data_create(&data, packet_size);
- if (!buffer) {
- m_stats.decode_errors++;
- return ScopedFrame(FramePool::PooledFrame{});
- }
-
- // Copy packet data (can be optimized with zero-copy in the future)
- memcpy(buffer, packet_data, packet_size);
-
- // Send data to decoder
- int result = dav1d_send_data(m_dav1d_context, &data);
- if (result < 0 && result != -EAGAIN) {
- m_stats.decode_errors++;
- return ScopedFrame(FramePool::PooledFrame{});
- }
-
- // Get decoded frame
- // WARNING: Dav1dPicture initialization required to prevent assertion error
- Dav1dPicture picture = {};
- result = dav1d_get_picture(m_dav1d_context, &picture);
- if (result < 0) {
- if (result != -EAGAIN) {
- m_stats.decode_errors++;
- }
- return ScopedFrame(FramePool::PooledFrame{});
- }
-
- // Determine ColorSpace
- ColorSpace color_space = ColorSpace::YUV420P;
- if (picture.p.layout == DAV1D_PIXEL_LAYOUT_I422) {
- color_space = ColorSpace::YUV422P;
- } else if (picture.p.layout == DAV1D_PIXEL_LAYOUT_I444) {
- color_space = ColorSpace::YUV444P;
- }
-
- // Allocate frame from memory pool
- auto pooled_frame = FramePool::GetInstance().AcquireFrame(
- picture.p.w, picture.p.h, color_space);
-
- if (!pooled_frame.frame) {
- dav1d_picture_unref(&picture);
- m_stats.decode_errors++;
- return ScopedFrame(FramePool::PooledFrame{});
- }
-
- // Copy data from Dav1dPicture to VideoFrame
- if (!ConvertDav1dPicture(picture, *pooled_frame.frame)) {
- dav1d_picture_unref(&picture);
- m_stats.decode_errors++;
- return ScopedFrame(FramePool::PooledFrame{});
- }
-
- // Set metadata
- pooled_frame.frame->is_valid = true;
- pooled_frame.frame->timestamp_seconds = 0.0; // To be set by caller
- pooled_frame.frame->frame_index = 0; // To be set by caller
-
- // Measure decoding time
- auto decode_end = std::chrono::high_resolution_clock::now();
- auto decode_duration = std::chrono::duration(decode_end - decode_start);
-
- // Update statistics
- m_stats.frames_decoded++;
- double decode_time = decode_duration.count();
- m_total_decode_time_ms += decode_time;
- m_stats.avg_decode_time_ms = m_total_decode_time_ms / m_stats.frames_decoded;
-
- // Clean up dav1d resources
- dav1d_picture_unref(&picture);
-
- std::cout << "[AV1Decoder] Pooled decode successful - " << pooled_frame.frame->width
- << "x" << pooled_frame.frame->height << " in " << decode_time << "ms" << std::endl;
-
- return ScopedFrame(std::move(pooled_frame));
-}
-*/
-
-// Zero-copy decoding implementation
-void AV1Decoder::DummyFreeCallback(const uint8_t* data, void* cookie) {
- // WARNING: Critical zero-copy memory management considerations
- //
- // This callback is called when dav1d has finished using the packet data.
- // However, in the current implementation, packet ownership belongs to the caller (VideoPacket),
- // so we do nothing here (dummy callback).
- //
- // Important considerations:
- // 1. Original packet_data must remain valid until this callback is called
- // 2. VideoPacket lifetime must be guaranteed until decoding completion
- // 3. Additional synchronization may be needed in multi-threaded environments
- //
- // Future improvements:
- // - Change callback implementation if actual memory deallocation is needed
- // - Consider using reference counting or smart pointers
-
- (void)data; (void)cookie; // Prevent unused parameter warnings
-}
-
-bool AV1Decoder::DecodeFrameZeroCopy(const uint8_t* packet_data, size_t packet_size, VideoFrame& output_frame) {
- // SAFETY CHECK: Verify essential conditions for zero-copy usage
- if (!m_initialized || !packet_data || packet_size == 0) {
- LogError("Invalid input for zero-copy decoding");
- IncrementDecodeErrors();
- return false;
- }
-
- auto decode_start = std::chrono::high_resolution_clock::now();
-
- // CRITICAL: Zero-copy memory wrapping
- // WARNING: packet_data may still be referenced by dav1d after this function returns!
- // Caller must guarantee the lifetime of packet_data
- Dav1dData dav1d_data;
- int wrap_result = dav1d_data_wrap(&dav1d_data, packet_data, packet_size, DummyFreeCallback, nullptr);
- if (wrap_result < 0) {
- LogError("Failed to wrap data for zero-copy decoding", wrap_result);
- IncrementDecodeErrors();
- return false;
- }
-
- // Send data to decoder
- int res = dav1d_send_data(m_dav1d_context, &dav1d_data);
- if (res < 0) {
- LogError("Failed to send data to decoder", res);
- IncrementDecodeErrors();
- return false;
- }
-
- // Decode frame
- // WARNING: Dav1dPicture initialization required to prevent assertion error
- Dav1dPicture dav1d_picture = {};
- res = dav1d_get_picture(m_dav1d_context, &dav1d_picture);
- if (res < 0) {
- if (res != -EAGAIN) {
- LogError("Failed to get decoded picture", res);
- IncrementDecodeErrors();
- }
- return false;
- }
-
- // Convert dav1d picture to VideoFrame
- bool convert_success = ConvertDav1dPicture(dav1d_picture, output_frame);
-
- // Clean up dav1d resources
- dav1d_picture_unref(&dav1d_picture);
-
- if (!convert_success) {
- LogError("Failed to convert dav1d picture to VideoFrame");
- IncrementDecodeErrors();
- return false;
- }
-
- // Update performance 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);
-
- std::cout << "[AV1Decoder] Zero-copy decode successful - " << output_frame.width
- << "x" << output_frame.height << " in " << decode_duration.count() << "ms" << std::endl;
-
- return true;
-}
-
-/*
-ScopedFrame AV1Decoder::DecodeFramePooledZeroCopy(const uint8_t* packet_data, size_t packet_size) {
- // SAFETY CHECK: Considerations when using zero-copy with memory pool combination
- if (!m_initialized || !packet_data || packet_size == 0) {
- return ScopedFrame(FramePool::PooledFrame{});
- }
-
- auto decode_start = std::chrono::high_resolution_clock::now();
-
- // CRITICAL: Zero-copy + memory pool optimization combination
- // WARNING: Must consider both packet_data lifetime and frame reuse from pool
- Dav1dData data;
- int wrap_result = dav1d_data_wrap(&data, packet_data, packet_size, DummyFreeCallback, nullptr);
- if (wrap_result < 0) {
- m_stats.decode_errors++;
- return ScopedFrame(FramePool::PooledFrame{});
- }
-
- // Send data to decoder
- int result = dav1d_send_data(m_dav1d_context, &data);
- if (result < 0 && result != -EAGAIN) {
- m_stats.decode_errors++;
- return ScopedFrame(FramePool::PooledFrame{});
- }
-
- // Get decoded frame
- // WARNING: Dav1dPicture initialization required to prevent assertion error
- Dav1dPicture picture = {};
- result = dav1d_get_picture(m_dav1d_context, &picture);
- if (result < 0) {
- if (result != -EAGAIN) {
- m_stats.decode_errors++;
- }
- return ScopedFrame(FramePool::PooledFrame{});
- }
-
- // Determine ColorSpace
- ColorSpace color_space = ColorSpace::YUV420P;
- if (picture.p.layout == DAV1D_PIXEL_LAYOUT_I422) {
- color_space = ColorSpace::YUV422P;
- } else if (picture.p.layout == DAV1D_PIXEL_LAYOUT_I444) {
- color_space = ColorSpace::YUV444P;
- }
-
- // Allocate frame from memory pool
- auto pooled_frame = FramePool::GetInstance().AcquireFrame(
- picture.p.w, picture.p.h, color_space);
-
- if (!pooled_frame.frame) {
- dav1d_picture_unref(&picture);
- m_stats.decode_errors++;
- return ScopedFrame(FramePool::PooledFrame{});
- }
-
- // Copy data from Dav1dPicture to VideoFrame
- if (!ConvertDav1dPicture(picture, *pooled_frame.frame)) {
- dav1d_picture_unref(&picture);
- m_stats.decode_errors++;
- return ScopedFrame(FramePool::PooledFrame{});
- }
-
- // Set metadata
- pooled_frame.frame->is_valid = true;
- pooled_frame.frame->timestamp_seconds = 0.0; // To be set by caller
-
- // Update statistics
- auto decode_end = std::chrono::high_resolution_clock::now();
- auto decode_duration = std::chrono::duration(decode_end - decode_start);
-
- m_stats.frames_decoded++;
- double decode_time = decode_duration.count();
- m_total_decode_time_ms += decode_time;
- m_stats.avg_decode_time_ms = m_total_decode_time_ms / m_stats.frames_decoded;
-
- // Clean up dav1d resources
- dav1d_picture_unref(&picture);
-
- std::cout << "[AV1Decoder] Zero-copy pooled decode successful - " << pooled_frame.frame->width
- << "x" << pooled_frame.frame->height << " in " << decode_time << "ms" << std::endl;
-
- return ScopedFrame(std::move(pooled_frame));
-}
-*/
-
-bool AV1Decoder::DecodeFrameToGPU(const uint8_t* packet_data, size_t packet_size,
- uint8_t* yMappedBuffer, uint8_t* uMappedBuffer, uint8_t* vMappedBuffer,
- uint32_t yRowPitch, uint32_t uRowPitch, uint32_t vRowPitch,
- uint32_t videoWidth, uint32_t videoHeight)
-{
- // Safety checks
- if (!m_initialized || !packet_data || packet_size == 0) {
- LogError("Invalid input for GPU direct decoding");
- IncrementDecodeErrors();
- return false;
- }
-
- if (!yMappedBuffer || !uMappedBuffer || !vMappedBuffer) {
- LogError("Invalid GPU mapped buffers provided");
- IncrementDecodeErrors();
- return false;
- }
-
- auto decode_start = std::chrono::high_resolution_clock::now();
-
- // Prepare zero-copy packet (directly referenced by dav1d)
- Dav1dData data = {};
- if (dav1d_data_wrap(&data, packet_data, packet_size, DummyFreeCallback, nullptr) < 0) {
- LogError("Failed to wrap packet data for GPU decoding");
- IncrementDecodeErrors();
- return false;
- }
-
- // Send packet to dav1d
- int ret = dav1d_send_data(m_dav1d_context, &data);
- if (ret < 0 && ret != -EAGAIN) {
- LogError("Failed to send data to dav1d for GPU decoding: " + std::to_string(ret));
- IncrementDecodeErrors();
- return false;
- }
-
- // Get decoded frame
- Dav1dPicture picture = {};
- ret = dav1d_get_picture(m_dav1d_context, &picture);
- if (ret < 0) {
- if (ret != -EAGAIN) {
- LogError("Failed to get decoded picture for GPU decoding: " + std::to_string(ret));
- IncrementDecodeErrors();
- }
- return false;
- }
-
- // Validate frame dimensions
- if (picture.p.w != (int)videoWidth || picture.p.h != (int)videoHeight) {
- LogError("Frame dimension mismatch: expected " + std::to_string(videoWidth) + "x" +
- std::to_string(videoHeight) + ", got " + std::to_string(picture.p.w) + "x" +
- std::to_string(picture.p.h));
- dav1d_picture_unref(&picture);
- IncrementDecodeErrors();
- return false;
- }
-
- // Validate pixel format (must be YUV420P for now)
- if (picture.p.layout != DAV1D_PIXEL_LAYOUT_I420) {
- LogError("Unsupported pixel format for GPU direct decoding. Only YUV420P supported.");
- dav1d_picture_unref(&picture);
- IncrementDecodeErrors();
- return false;
- }
-
- // Calculate UV dimensions
- uint32_t uvWidth = (videoWidth + 1) / 2;
- uint32_t uvHeight = (videoHeight + 1) / 2;
-
- // Direct copy to GPU mapped buffers
- bool copySuccess = true;
-
- try {
- // Copy Y plane
- const uint8_t* ySrc = (const uint8_t*)picture.data[0];
- for (uint32_t y = 0; y < videoHeight && copySuccess; y++) {
- memcpy(yMappedBuffer + y * yRowPitch,
- ySrc + y * picture.stride[0],
- videoWidth);
- }
-
- // Copy U plane
- const uint8_t* uSrc = (const uint8_t*)picture.data[1];
- for (uint32_t y = 0; y < uvHeight && copySuccess; y++) {
- memcpy(uMappedBuffer + y * uRowPitch,
- uSrc + y * picture.stride[1],
- uvWidth);
- }
-
- // Copy V plane
- const uint8_t* vSrc = (const uint8_t*)picture.data[2];
- for (uint32_t y = 0; y < uvHeight && copySuccess; y++) {
- memcpy(vMappedBuffer + y * vRowPitch,
- vSrc + y * picture.stride[1],
- uvWidth);
- }
- }
- catch (...) {
- LogError("Exception during GPU buffer copy");
- copySuccess = false;
- }
-
- // Cleanup
- dav1d_picture_unref(&picture);
-
- if (!copySuccess) {
- IncrementDecodeErrors();
- return false;
- }
-
- // Update statistics
- auto decode_end = std::chrono::high_resolution_clock::now();
- auto decode_duration = std::chrono::duration(decode_end - decode_start);
-
- m_stats.frames_decoded++;
- double decode_time = decode_duration.count();
- m_total_decode_time_ms += decode_time;
- m_stats.avg_decode_time_ms = m_total_decode_time_ms / m_stats.frames_decoded;
-
- std::cout << "[AV1Decoder] GPU direct decode successful - " << videoWidth << "x" << videoHeight
- << " in " << decode_time << "ms (Zero-copy to GPU)" << std::endl;
-
- return true;
-}
-
-bool AV1Decoder::DecodeFrameToRingBuffer(const uint8_t* packet_data, size_t packet_size,
- uint32_t bufferIndex,
- uint8_t* yMappedBuffer, uint8_t* uMappedBuffer, uint8_t* vMappedBuffer,
- uint32_t yRowPitch, uint32_t uRowPitch, uint32_t vRowPitch,
- uint32_t videoWidth, uint32_t videoHeight)
-{
- if (!m_initialized || !packet_data || packet_size == 0) {
- LogError("DecodeFrameToRingBuffer: Invalid parameters or decoder not initialized");
- return false;
- }
-
- if (!yMappedBuffer || !uMappedBuffer || !vMappedBuffer) {
- LogError("DecodeFrameToRingBuffer: Invalid ring buffer pointers for buffer index " + std::to_string(bufferIndex));
- return false;
- }
-
- auto decode_start = std::chrono::high_resolution_clock::now();
-
- // Create dav1d data (zero-copy)
- Dav1dData data;
- if (dav1d_data_wrap(&data, packet_data, packet_size, DummyFreeCallback, nullptr) < 0) {
- LogError("DecodeFrameToRingBuffer: Failed to wrap packet data for ring buffer " + std::to_string(bufferIndex));
- IncrementDecodeErrors();
- return false;
- }
-
- // Send packet to dav1d
- int ret = dav1d_send_data(m_dav1d_context, &data);
- if (ret < 0 && ret != -EAGAIN) {
- LogError("DecodeFrameToRingBuffer: Failed to send data to dav1d for buffer " + std::to_string(bufferIndex) + ": " + std::to_string(ret));
- IncrementDecodeErrors();
- return false;
- }
-
- // Get decoded frame
- Dav1dPicture picture = {};
- ret = dav1d_get_picture(m_dav1d_context, &picture);
- if (ret < 0) {
- if (ret != -EAGAIN) {
- LogError("DecodeFrameToRingBuffer: Failed to get decoded picture for buffer " + std::to_string(bufferIndex) + ": " + std::to_string(ret));
- IncrementDecodeErrors();
- }
- return false;
- }
-
- // Direct copy to ring buffer (GPU memory)
- bool copySuccess = true;
-
- // Copy Y plane to ring buffer
- const uint8_t* ySrc = (const uint8_t*)picture.data[0];
- for (uint32_t y = 0; y < videoHeight && copySuccess; y++) {
- if (ySrc + y * picture.stride[0] + videoWidth <= (const uint8_t*)picture.data[0] + picture.stride[0] * picture.p.h) {
- memcpy(yMappedBuffer + y * yRowPitch,
- ySrc + y * picture.stride[0],
- videoWidth);
- }
- else {
- copySuccess = false;
- LogError("DecodeFrameToRingBuffer: Y plane copy bounds check failed for buffer " + std::to_string(bufferIndex));
- break;
- }
- }
-
- // Copy U plane to ring buffer
- if (copySuccess && picture.data[1]) {
- const uint8_t* uSrc = (const uint8_t*)picture.data[1];
- uint32_t uvWidth = videoWidth / 2;
- uint32_t uvHeight = videoHeight / 2;
-
- for (uint32_t y = 0; y < uvHeight && copySuccess; y++) {
- if (uSrc + y * picture.stride[1] + uvWidth <= (const uint8_t*)picture.data[1] + picture.stride[1] * (picture.p.h / 2)) {
- memcpy(uMappedBuffer + y * uRowPitch,
- uSrc + y * picture.stride[1],
- uvWidth);
- }
- else {
- copySuccess = false;
- LogError("DecodeFrameToRingBuffer: U plane copy bounds check failed for buffer " + std::to_string(bufferIndex));
- break;
- }
- }
- }
-
- // Copy V plane to ring buffer
- if (copySuccess && picture.data[2]) {
- const uint8_t* vSrc = (const uint8_t*)picture.data[2];
- uint32_t uvWidth = videoWidth / 2;
- uint32_t uvHeight = videoHeight / 2;
-
- for (uint32_t y = 0; y < uvHeight && copySuccess; y++) {
- if (vSrc + y * picture.stride[1] + uvWidth <= (const uint8_t*)picture.data[2] + picture.stride[1] * (picture.p.h / 2)) {
- memcpy(vMappedBuffer + y * vRowPitch,
- vSrc + y * picture.stride[1],
- uvWidth);
- }
- else {
- copySuccess = false;
- LogError("DecodeFrameToRingBuffer: V plane copy bounds check failed for buffer " + std::to_string(bufferIndex));
- break;
- }
- }
- }
-
- // Release dav1d picture
- dav1d_picture_unref(&picture);
-
- if (!copySuccess) {
- IncrementDecodeErrors();
- return false;
- }
-
- // Performance measurement and statistics update
- auto decode_end = std::chrono::high_resolution_clock::now();
- double decode_time = std::chrono::duration(decode_end - decode_start).count();
-
- UpdateDecodingStats(decode_time, packet_size);
-
- m_stats.frames_decoded++;
- m_total_decode_time_ms += decode_time;
- m_stats.avg_decode_time_ms = m_total_decode_time_ms / m_stats.frames_decoded;
-
- std::cout << "[AV1Decoder] Ring Buffer decode successful - Buffer[" << bufferIndex << "] "
- << videoWidth << "x" << videoHeight << " in " << decode_time << "ms (Zero-copy to Ring Buffer)"
- << std::endl;
-
- return true;
-}
-
-bool AV1Decoder::DecodeFrameWithGPUCopy(const uint8_t* packet_data, size_t packet_size,
- D3D12VideoRenderer* renderer, uint32_t bufferIndex,
- uint32_t videoWidth, uint32_t videoHeight)
-{
- if (!m_initialized || !packet_data || packet_size == 0 || !renderer) {
- LogError("DecodeFrameWithGPUCopy: Invalid parameters or decoder not initialized");
- return false;
- }
-
- auto decode_start = std::chrono::high_resolution_clock::now();
-
- // Get mapped buffers from ring buffer system
- uint8_t* yMappedBuffer = renderer->GetYMappedBuffer(bufferIndex);
- uint8_t* uMappedBuffer = renderer->GetUMappedBuffer(bufferIndex);
- uint8_t* vMappedBuffer = renderer->GetVMappedBuffer(bufferIndex);
-
- if (!yMappedBuffer || !uMappedBuffer || !vMappedBuffer) {
- LogError("DecodeFrameWithGPUCopy: Failed to get mapped buffers for buffer " + std::to_string(bufferIndex));
- return false;
- }
-
- // First decode to ring buffer (CPU memcpy)
- bool decodeSuccess = DecodeFrameToRingBuffer(packet_data, packet_size, bufferIndex,
- yMappedBuffer, uMappedBuffer, vMappedBuffer,
- renderer->GetYRowPitch(), renderer->GetURowPitch(), renderer->GetVRowPitch(),
- videoWidth, videoHeight);
-
- if (!decodeSuccess) {
- LogError("DecodeFrameWithGPUCopy: Ring buffer decode failed for buffer " + std::to_string(bufferIndex));
- return false;
- }
-
- // Execute GPU copy using compute shader
- HRESULT hr = renderer->CopyYUVPlanesGPU(bufferIndex, videoWidth, videoHeight);
- if (FAILED(hr)) {
- LogError("DecodeFrameWithGPUCopy: GPU copy failed for buffer " + std::to_string(bufferIndex));
- return false;
- }
-
- auto decode_end = std::chrono::high_resolution_clock::now();
- double decode_time = std::chrono::duration(decode_end - decode_start).count();
-
- std::cout << "[AV1Decoder] GPU copy decode successful - Buffer[" << bufferIndex << "] "
- << videoWidth << "x" << videoHeight << " in " << decode_time << "ms (Ring Buffer + GPU Copy)"
- << std::endl;
-
- return true;
-}
-
-bool AV1Decoder::DecodeFrameDirectTexture(const uint8_t* packet_data, size_t packet_size,
- DirectTextureAllocator* textureAllocator)
-{
- if (!m_initialized || !packet_data || packet_size == 0 || !textureAllocator) {
- LogError("DecodeFrameDirectTexture: Invalid parameters or decoder not initialized");
- return false;
- }
-
- auto decode_start = std::chrono::high_resolution_clock::now();
-
- // Store the texture allocator for this decode operation
- m_directTextureAllocator = textureAllocator;
-
- // Decode frame using standard dav1d pipeline first
- Dav1dData data = {};
- dav1d_data_wrap(&data, packet_data, packet_size, DummyFreeCallback, nullptr);
-
- // Send data to decoder
- int ret = dav1d_send_data(m_dav1d_context, &data);
- if (ret < 0) {
- LogError("DecodeFrameDirectTexture: dav1d_send_data failed", ret);
- dav1d_data_unref(&data);
- return false;
- }
-
- // Get decoded picture using default allocator
- Dav1dPicture picture = {};
- ret = dav1d_get_picture(m_dav1d_context, &picture);
- if (ret < 0) {
- if (ret != -11) { // -11 is EAGAIN (no picture available yet)
- LogError("DecodeFrameDirectTexture: dav1d_get_picture failed", ret);
- }
- return false;
- }
-
- // At this point, the picture data should already be in GPU textures via DirectTextureAllocator!
- // No additional memory copy needed if allocator was properly set
-
- // Clean up dav1d picture
- dav1d_picture_unref(&picture);
-
- // Performance measurement
- auto decode_end = std::chrono::high_resolution_clock::now();
- double decode_time = std::chrono::duration(decode_end - decode_start).count();
-
- // Update statistics
- UpdateDecodingStats(decode_time, packet_size);
-
- std::cout << "[AV1Decoder] Direct Texture decode successful - "
- << picture.p.w << "x" << picture.p.h << " in " << decode_time << "ms (Zero-copy to GPU Texture)"
- << std::endl;
-
- // Note: Don't call dav1d_picture_unref here - let the allocator handle lifetime
- // The texture remains valid until the next frame or allocator shutdown
-
- return true;
-}
-
-bool AV1Decoder::SupportsDirectTextureMapping() const
-{
- // Direct Texture Mapping requires:
- // 1. Initialized dav1d context
- // 2. 8-bit YUV420 support (most common format)
- // 3. D3D12 compatible environment
- return m_initialized && m_dav1d_context != nullptr;
-}
-
-// Note: DirectTextureAllocator methods removed during Phase 1 simplification
-
-// Note: PrepareDirectTextureAllocator removed during Phase 1 simplification
-
-// Resolution-based optimized settings implementation
-AV1Decoder::AV1Settings AV1Decoder::GetOptimalSettingsForResolution(uint32_t width, uint32_t height)
-{
- AV1Settings settings;
-
- // Calculate video complexity score
- uint64_t pixelCount = static_cast(width) * height;
- uint32_t maxHWThreads = std::thread::hardware_concurrency();
-
- // 4K and above (3840x2160+): Maximum performance optimization
- if (width >= 3840 && height >= 2160) {
- settings.max_frame_delay = 2; // Allow more delay for better throughput
- settings.num_threads = std::min(16U, maxHWThreads); // Max threads for 4K
- settings.apply_grain = false; // Disable grain for performance
- settings.strict_std_compliance = false; // Allow optimizations
- settings.all_layers = false; // Process only target layer
- settings.operating_point = false; // Disable for performance
- settings.enable_inloop_filters = false; // Disable heavy filters for max performance
-
- std::cout << "[AV1Decoder] Applied 4K optimization settings" << std::endl;
- }
- // 1440p (2560x1440): Balanced high performance
- else if (width >= 2560 && height >= 1440) {
- settings.max_frame_delay = 2;
- settings.num_threads = std::min(12U, maxHWThreads);
- settings.apply_grain = false; // Still disable for performance
- settings.strict_std_compliance = false;
- settings.all_layers = false;
- settings.operating_point = false;
- settings.enable_inloop_filters = false; // Disable for performance
-
- std::cout << "[AV1Decoder] Applied 1440p optimization settings" << std::endl;
- }
- // 1080p (1920x1080): Standard optimization
- else if (width >= 1920 && height >= 1080) {
- settings.max_frame_delay = 1;
- settings.num_threads = std::min(8U, maxHWThreads);
- settings.apply_grain = true; // Enable grain for quality
- settings.strict_std_compliance = false;
- settings.all_layers = false;
- settings.operating_point = false;
- settings.enable_inloop_filters = true; // Enable for balanced quality/performance
-
- std::cout << "[AV1Decoder] Applied 1080p optimization settings" << std::endl;
- }
- // Lower resolutions: Quality-focused
- else {
- settings.max_frame_delay = 1;
- settings.num_threads = std::min(4U, maxHWThreads);
- settings.apply_grain = true; // Maintain quality
- settings.strict_std_compliance = true; // Standard compliance
- settings.all_layers = false;
- settings.operating_point = false;
- settings.enable_inloop_filters = true; // Enable all filters for quality
-
- std::cout << "[AV1Decoder] Applied standard optimization settings" << std::endl;
- }
-
- std::cout << "[AV1Decoder] Optimized for " << width << "x" << height
- << " (" << pixelCount / 1000000.0 << "MP)"
- << " - threads:" << settings.num_threads
- << " delay:" << settings.max_frame_delay
- << " grain:" << (settings.apply_grain ? "on" : "off")
- << " filters:" << (settings.enable_inloop_filters ? "on" : "off") << std::endl;
-
- return settings;
-}
-
-void AV1Decoder::ApplyOptimalSettingsForResolution(uint32_t width, uint32_t height)
-{
- AV1Settings optimalSettings = GetOptimalSettingsForResolution(width, height);
- SetAV1Settings(optimalSettings);
-
- // Re-setup dav1d if already initialized
- if (m_initialized && m_dav1d_context) {
- std::cout << "[AV1Decoder] Re-initializing dav1d with optimized settings..." << std::endl;
-
- // Close existing context
- CleanupDav1d();
-
- // Reinitialize with new settings
- if (!InitializeDav1d()) {
- std::cout << "[AV1Decoder] Warning: Failed to reinitialize with optimized settings, reverting to defaults" << std::endl;
-
- // Fallback to default settings
- dav1d_default_settings(&m_dav1d_settings);
- if (dav1d_open(&m_dav1d_context, &m_dav1d_settings) < 0) {
- m_initialized = false;
- }
- }
- }
-}
-
-ScopedFrame AV1Decoder::DecodeFrameEnhancedZeroCopy(const uint8_t* packet_data, size_t packet_size) {
- // Enhanced zero-copy with automatic packet size prediction and pooling
- if (!m_initialized || !packet_data || packet_size == 0) {
- return ScopedFrame(FramePool::PooledFrame{});
- }
-
- auto decode_start = std::chrono::high_resolution_clock::now();
-
- // Update packet pool size prediction
- PacketPool& packetPool = GetGlobalPacketPool();
- packetPool.UpdateSizePrediction(packet_size);
-
- // Use zero-copy decoding for minimal overhead
- Dav1dData dav1d_data;
- dav1d_data_wrap(&dav1d_data, packet_data, packet_size, DummyFreeCallback, nullptr);
-
- // Send data to decoder
- int res = dav1d_send_data(m_dav1d_context, &dav1d_data);
- if (res < 0) {
- LogError("Failed to send data to decoder", res);
- m_stats.decode_errors++;
- return ScopedFrame(FramePool::PooledFrame{});
- }
-
- // Get decoded picture with proper initialization
- Dav1dPicture picture = {};
- res = dav1d_get_picture(m_dav1d_context, &picture);
- if (res < 0) {
- if (res != -EAGAIN) {
- LogError("Failed to get decoded picture", res);
- m_stats.decode_errors++;
- }
- return ScopedFrame(FramePool::PooledFrame{});
- }
-
- // Determine color space
- ColorSpace color_space = ColorSpace::YUV420P;
- if (picture.p.layout == DAV1D_PIXEL_LAYOUT_I422) {
- color_space = ColorSpace::YUV422P;
- } else if (picture.p.layout == DAV1D_PIXEL_LAYOUT_I444) {
- color_space = ColorSpace::YUV444P;
- }
-
- // Get frame from pool based on picture dimensions
- auto pooled_frame = FramePool::GetInstance().AcquireFrame(
- picture.p.w, picture.p.h, color_space
- );
-
- if (!pooled_frame.frame) {
- LogError("Failed to acquire frame from pool");
- dav1d_picture_unref(&picture);
- m_stats.decode_errors++;
- return ScopedFrame(FramePool::PooledFrame{});
- }
-
- // Convert dav1d picture to pooled frame
- bool convert_success = ConvertDav1dPicture(picture, *pooled_frame.frame);
- dav1d_picture_unref(&picture);
-
- if (!convert_success) {
- LogError("Failed to convert dav1d picture to pooled frame");
- m_stats.decode_errors++;
- return ScopedFrame(FramePool::PooledFrame{});
- }
-
- // Update performance 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);
-
- // Update frame metadata
- pooled_frame.frame->is_valid = true;
- pooled_frame.frame->timestamp_seconds = 0.0; // To be set by caller
-
- return ScopedFrame(std::move(pooled_frame));
-}
-
-ScopedFrame AV1Decoder::DecodePacketFromPool(PacketPool::PooledPacket& pooled_packet) {
- // Optimized decoding using packet pool for enhanced memory management
- if (!m_initialized || !pooled_packet.IsValid()) {
- return ScopedFrame(FramePool::PooledFrame{});
- }
-
- auto decode_start = std::chrono::high_resolution_clock::now();
-
- // Use packet pool data directly with zero-copy
- uint8_t* packet_data = pooled_packet.GetData();
- size_t packet_size = pooled_packet.GetSize();
-
- // Update packet pool statistics
- PacketPool& packetPool = GetGlobalPacketPool();
- packetPool.UpdateSizePrediction(packet_size);
-
- // Zero-copy data wrapping
- Dav1dData dav1d_data;
- dav1d_data_wrap(&dav1d_data, packet_data, packet_size, DummyFreeCallback, nullptr);
-
- // Send data to decoder
- int res = dav1d_send_data(m_dav1d_context, &dav1d_data);
- if (res < 0) {
- LogError("Failed to send pooled packet to decoder", res);
- m_stats.decode_errors++;
- return ScopedFrame(FramePool::PooledFrame{});
- }
-
- // Get decoded picture
- Dav1dPicture picture = {};
- res = dav1d_get_picture(m_dav1d_context, &picture);
- if (res < 0) {
- if (res != -EAGAIN) {
- LogError("Failed to get picture from pooled packet", res);
- m_stats.decode_errors++;
- }
- return ScopedFrame(FramePool::PooledFrame{});
- }
-
- // Determine color space
- ColorSpace color_space = ColorSpace::YUV420P;
- if (picture.p.layout == DAV1D_PIXEL_LAYOUT_I422) {
- color_space = ColorSpace::YUV422P;
- } else if (picture.p.layout == DAV1D_PIXEL_LAYOUT_I444) {
- color_space = ColorSpace::YUV444P;
- }
-
- // Acquire frame from pool
- auto pooled_frame = FramePool::GetInstance().AcquireFrame(
- picture.p.w, picture.p.h, color_space
- );
-
- if (!pooled_frame.frame) {
- LogError("Failed to acquire frame from pool for pooled packet");
- dav1d_picture_unref(&picture);
- m_stats.decode_errors++;
- return ScopedFrame(FramePool::PooledFrame{});
- }
-
- // Convert with optimized path for pooled frames
- bool convert_success = ConvertDav1dPicture(picture, *pooled_frame.frame);
- dav1d_picture_unref(&picture);
-
- if (!convert_success) {
- LogError("Failed to convert pooled packet picture");
- m_stats.decode_errors++;
- return ScopedFrame(FramePool::PooledFrame{});
- }
-
- // Update performance statistics with packet metadata
- 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);
-
- // Update frame metadata from packet pool
- pooled_frame.frame->frame_index = pooled_packet.frame_index;
- pooled_frame.frame->timestamp_seconds = pooled_packet.timestamp_seconds;
- pooled_frame.frame->is_valid = true;
-
- return ScopedFrame(std::move(pooled_frame));
-}
-
-} // namespace Vav2Player
\ No newline at end of file
diff --git a/vav2/Vav2Player/Vav2Player/src/Decoder/AV1Decoder.h b/vav2/Vav2Player/Vav2Player/src/Decoder/AV1Decoder.h
deleted file mode 100644
index acd4048..0000000
--- a/vav2/Vav2Player/Vav2Player/src/Decoder/AV1Decoder.h
+++ /dev/null
@@ -1,156 +0,0 @@
-#pragma once
-#include "IVideoDecoder.h"
-// Simplified architecture - removed complex pools
-#include
-#include
-#include
-
-namespace Vav2Player {
-
-// AV1 video decoder implementation class
-// Decodes AV1 video streams to YUV frames using the 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;
-
- // Note: Pool-based methods removed during Phase 1 simplification
-
- // Zero-copy decoding methods (decode without memory copying)
- // Warning: packet_data lifetime must be maintained until decoding completion!
- // dav1d may queue packets internally, so packet data can be referenced
- // asynchronously even after the call returns immediately
- bool DecodeFrameZeroCopy(const uint8_t* packet_data, size_t packet_size, VideoFrame& output_frame);
- // Note: DecodeFramePooledZeroCopy removed during Phase 1 simplification
-
- // Note: Enhanced zero-copy and packet pool methods removed during Phase 1 simplification
-
- // Direct GPU decoding method (direct output to D3D12 mapped buffers)
- bool DecodeFrameToGPU(const uint8_t* packet_data, size_t packet_size,
- uint8_t* yMappedBuffer, uint8_t* uMappedBuffer, uint8_t* vMappedBuffer,
- uint32_t yRowPitch, uint32_t uRowPitch, uint32_t vRowPitch,
- uint32_t videoWidth, uint32_t videoHeight);
-
- // Ring Buffer supported GPU decoding method
- bool DecodeFrameToRingBuffer(const uint8_t* packet_data, size_t packet_size,
- uint32_t bufferIndex,
- uint8_t* yMappedBuffer, uint8_t* uMappedBuffer, uint8_t* vMappedBuffer,
- uint32_t yRowPitch, uint32_t uRowPitch, uint32_t vRowPitch,
- uint32_t videoWidth, uint32_t videoHeight);
-
- // Compute Shader based GPU copy optimization method
- bool DecodeFrameWithGPUCopy(const uint8_t* packet_data, size_t packet_size,
- class D3D12VideoRenderer* renderer, uint32_t bufferIndex,
- uint32_t videoWidth, uint32_t videoHeight);
-
- // Note: Direct texture mapping methods removed during Phase 1 simplification
-
- bool Reset() override;
- bool Flush() override;
-
- // Check EAGAIN state (when AV1 characteristics require more packets)
- bool IsWaitingForMoreData() const { return m_lastDecodeResult == DAV1D_ERR(EAGAIN); }
-
- std::string GetCodecName() const override;
- VideoCodecType GetCodecType() const override;
- std::string GetVersion() const override;
-
- DecoderStats GetStats() const override;
- void ResetStats() override;
-
- // AV1 specific options
- bool SetOption(const std::string& key, const std::string& value) override;
- std::string GetOption(const std::string& key) const override;
-
- // AV1 dedicated methods
- struct AV1Settings {
- int max_frame_delay = 1; // Maximum frame delay (lower means less delay)
- int num_threads = 0; // Number of decoding threads (0 = auto)
- bool apply_grain = true; // Apply film grain synthesis
- bool all_layers = false; // Decode all spatial/temporal layers
-
- // Advanced dav1d optimization settings
- bool strict_std_compliance = true; // Standard compliance (false = performance optimization)
- bool operating_point = false; // Operating point processing
- bool enable_inloop_filters = true; // Enable in-loop filters (false = performance priority)
- };
-
- void SetAV1Settings(const AV1Settings& settings);
- AV1Settings GetAV1Settings() const;
-
- // Resolution-based optimized settings
- static AV1Settings GetOptimalSettingsForResolution(uint32_t width, uint32_t height);
- void ApplyOptimalSettingsForResolution(uint32_t width, uint32_t height);
-
-private:
- // dav1d related members
- Dav1dContext* m_dav1d_context;
- Dav1dSettings m_dav1d_settings;
- AV1Settings m_av1_settings;
-
- // Note: Direct texture allocator removed during Phase 1 simplification
-
- // Initialization state
- bool m_initialized;
- VideoMetadata m_metadata;
-
- // Track decoding results (for EAGAIN distinction)
- int m_lastDecodeResult = 0;
-
- // Members for performance measurement
- std::chrono::high_resolution_clock::time_point m_decode_start_time;
- double m_total_decode_time_ms = 0.0;
-
- // Internal helper methods
- bool InitializeDav1d();
- void CleanupDav1d();
- bool SetupDav1dSettings();
-
- bool ConvertDav1dPicture(const Dav1dPicture& dav1d_pic, VideoFrame& output_frame);
- void UpdateDecodingStats(double decode_time_ms, size_t input_bytes);
-
- // Convert dav1d pixel format to VideoTypes format
- ColorSpace ConvertDav1dPixelFormat(const Dav1dPicture& pic);
-
- // Error handling
- std::string GetDav1dErrorString(int error_code);
- void LogError(const std::string& message, int error_code = 0);
-
- // Zero-copy decoding support
- static void DummyFreeCallback(const uint8_t* data, void* cookie);
-};
-
-// AV1 related utility functions
-namespace AV1Utils {
- // AV1 OBU (Open Bitstream Unit) type analysis
- enum class OBUType {
- SEQUENCE_HEADER = 1,
- TEMPORAL_DELIMITER = 2,
- FRAME_HEADER = 3,
- FRAME = 6,
- TILE_GROUP = 4,
- METADATA = 5,
- UNKNOWN = -1
- };
-
- OBUType GetOBUType(const uint8_t* data, size_t size);
- bool IsKeyFrame(const uint8_t* data, size_t size);
-
- // dav1d version information
- std::string GetDav1dVersion();
- std::string GetDav1dCopyright();
-}
-
-} // namespace Vav2Player
\ No newline at end of file
diff --git a/vav2/Vav2Player/Vav2Player/src/Rendering/SimpleGPURenderer_Headless.cpp b/vav2/Vav2Player/Vav2Player/src/Rendering/SimpleGPURenderer_Headless.cpp
deleted file mode 100644
index e891f0d..0000000
--- a/vav2/Vav2Player/Vav2Player/src/Rendering/SimpleGPURenderer_Headless.cpp
+++ /dev/null
@@ -1,336 +0,0 @@
-#include "pch.h"
-#include "SimpleGPURenderer_Headless.h"
-#include
-
-#pragma comment(lib, "d3d12.lib")
-#pragma comment(lib, "dxgi.lib")
-
-namespace Vav2Player {
-
-SimpleGPURenderer_Headless::SimpleGPURenderer_Headless()
- : m_fenceValue(1)
-{
- m_fenceEvent = CreateEvent(nullptr, FALSE, FALSE, nullptr);
-}
-
-SimpleGPURenderer_Headless::~SimpleGPURenderer_Headless()
-{
- Shutdown();
- if (m_fenceEvent)
- {
- CloseHandle(m_fenceEvent);
- m_fenceEvent = nullptr;
- }
-}
-
-HRESULT SimpleGPURenderer_Headless::Initialize(uint32_t width, uint32_t height)
-{
- if (m_initialized)
- return S_OK;
-
- m_width = width;
- m_height = height;
-
- HRESULT hr = S_OK;
-
- // 1. Create D3D12 device
- hr = CreateDevice();
- if (FAILED(hr)) return hr;
-
- // 2. Create command queue
- hr = CreateCommandQueue();
- if (FAILED(hr)) return hr;
-
- // 3. Create synchronization objects
- hr = CreateSynchronizationObjects();
- if (FAILED(hr)) return hr;
-
- m_initialized = true;
- std::cout << "[SimpleGPURenderer_Headless] Initialized successfully (" << width << "x" << height << ")" << std::endl;
- return S_OK;
-}
-
-void SimpleGPURenderer_Headless::Shutdown()
-{
- if (!m_initialized)
- return;
-
- // Wait for GPU to finish
- WaitForGPU();
-
- // Reset COM objects
- m_yTexture.Reset();
- m_uTexture.Reset();
- m_vTexture.Reset();
- m_commandList.Reset();
- m_commandAllocator.Reset();
- m_fence.Reset();
- m_commandQueue.Reset();
- m_device.Reset();
-
- m_initialized = false;
- std::cout << "[SimpleGPURenderer_Headless] Shutdown completed" << std::endl;
-}
-
-HRESULT SimpleGPURenderer_Headless::RenderVideoFrame(const VideoFrame& frame)
-{
- if (!m_initialized)
- return E_FAIL;
-
- // Basic frame validation
- if (frame.width == 0 || frame.height == 0)
- return E_INVALIDARG;
-
- std::cout << "[SimpleGPURenderer_Headless] Would render frame ("
- << frame.width << "x" << frame.height << ")" << std::endl;
- return S_OK;
-}
-
-HRESULT SimpleGPURenderer_Headless::TestGPUPipeline()
-{
- if (!m_initialized)
- return E_FAIL;
-
- std::cout << "[SimpleGPURenderer_Headless] Testing GPU pipeline..." << std::endl;
-
- // Test command list recording
- HRESULT hr = m_commandAllocator->Reset();
- if (FAILED(hr))
- {
- std::cout << "[FAIL] Command allocator reset failed: 0x" << std::hex << hr << std::endl;
- return hr;
- }
-
- hr = m_commandList->Reset(m_commandAllocator.Get(), nullptr);
- if (FAILED(hr))
- {
- std::cout << "[FAIL] Command list reset failed: 0x" << std::hex << hr << std::endl;
- return hr;
- }
-
- // Close command list
- hr = m_commandList->Close();
- if (FAILED(hr))
- {
- std::cout << "[FAIL] Command list close failed: 0x" << std::hex << hr << std::endl;
- return hr;
- }
-
- // Execute empty command list (just to test pipeline)
- ID3D12CommandList* commandLists[] = { m_commandList.Get() };
- m_commandQueue->ExecuteCommandLists(1, commandLists);
-
- // Wait for completion
- hr = WaitForGPU();
- if (FAILED(hr))
- {
- std::cout << "[FAIL] GPU wait failed: 0x" << std::hex << hr << std::endl;
- return hr;
- }
-
- std::cout << "[PASS] GPU pipeline test completed successfully" << std::endl;
-
- // Test compute shader compilation
- hr = TestComputeShaderCompilation();
- if (FAILED(hr))
- {
- std::cout << "[FAIL] Compute shader compilation test failed" << std::endl;
- return hr;
- }
-
- return S_OK;
-}
-
-HRESULT SimpleGPURenderer_Headless::CreateDevice()
-{
- // Enable debug layer in debug builds
-#ifdef _DEBUG
- ComPtr debugController;
- if (SUCCEEDED(D3D12GetDebugInterface(IID_PPV_ARGS(&debugController))))
- {
- debugController->EnableDebugLayer();
- }
-#endif
-
- // Create device
- HRESULT hr = D3D12CreateDevice(nullptr, D3D_FEATURE_LEVEL_11_0, IID_PPV_ARGS(&m_device));
- if (FAILED(hr))
- {
- std::cout << "[SimpleGPURenderer_Headless] Failed to create D3D12 device: 0x" << std::hex << hr << std::endl;
- return hr;
- }
-
- std::cout << "[SimpleGPURenderer_Headless] D3D12 device created successfully" << std::endl;
- return S_OK;
-}
-
-HRESULT SimpleGPURenderer_Headless::CreateCommandQueue()
-{
- D3D12_COMMAND_QUEUE_DESC queueDesc = {};
- queueDesc.Flags = D3D12_COMMAND_QUEUE_FLAG_NONE;
- queueDesc.Type = D3D12_COMMAND_LIST_TYPE_DIRECT;
-
- HRESULT hr = m_device->CreateCommandQueue(&queueDesc, IID_PPV_ARGS(&m_commandQueue));
- if (FAILED(hr))
- {
- std::cout << "[SimpleGPURenderer_Headless] Failed to create command queue: 0x" << std::hex << hr << std::endl;
- return hr;
- }
-
- return S_OK;
-}
-
-HRESULT SimpleGPURenderer_Headless::CreateSynchronizationObjects()
-{
- // Create fence
- HRESULT hr = m_device->CreateFence(0, D3D12_FENCE_FLAG_NONE, IID_PPV_ARGS(&m_fence));
- if (FAILED(hr))
- {
- std::cout << "[SimpleGPURenderer_Headless] Failed to create fence: 0x" << std::hex << hr << std::endl;
- return hr;
- }
-
- // Create command allocator
- hr = m_device->CreateCommandAllocator(D3D12_COMMAND_LIST_TYPE_DIRECT, IID_PPV_ARGS(&m_commandAllocator));
- if (FAILED(hr))
- {
- std::cout << "[SimpleGPURenderer_Headless] Failed to create command allocator: 0x" << std::hex << hr << std::endl;
- return hr;
- }
-
- // Create command list
- hr = m_device->CreateCommandList(0, D3D12_COMMAND_LIST_TYPE_DIRECT, m_commandAllocator.Get(), nullptr, IID_PPV_ARGS(&m_commandList));
- if (FAILED(hr))
- {
- std::cout << "[SimpleGPURenderer_Headless] Failed to create command list: 0x" << std::hex << hr << std::endl;
- return hr;
- }
-
- // Close the command list initially
- hr = m_commandList->Close();
- if (FAILED(hr))
- {
- std::cout << "[SimpleGPURenderer_Headless] Failed to close initial command list: 0x" << std::hex << hr << std::endl;
- return hr;
- }
-
- std::cout << "[SimpleGPURenderer_Headless] Synchronization objects created successfully" << std::endl;
- return S_OK;
-}
-
-HRESULT SimpleGPURenderer_Headless::WaitForGPU()
-{
- if (!m_commandQueue || !m_fence || !m_fenceEvent)
- return E_FAIL;
-
- // Signal the fence
- HRESULT hr = m_commandQueue->Signal(m_fence.Get(), m_fenceValue);
- if (FAILED(hr)) return hr;
-
- // Wait for fence completion
- if (m_fence->GetCompletedValue() < m_fenceValue)
- {
- hr = m_fence->SetEventOnCompletion(m_fenceValue, m_fenceEvent);
- if (FAILED(hr)) return hr;
-
- WaitForSingleObject(m_fenceEvent, INFINITE);
- }
-
- m_fenceValue++;
- return S_OK;
-}
-
-HRESULT SimpleGPURenderer_Headless::TestComputeShaderCompilation()
-{
- std::cout << "[SimpleGPURenderer_Headless] Testing compute shader compilation..." << std::endl;
-
- // Define the same YUV-to-RGB compute shader as SimpleGPURenderer
- const char* shaderSource = R"(
-// YUV to RGB conversion compute shader
-// Uses BT.709 color space conversion matrix
-
-// Input Y texture (luminance)
-Texture2D g_yTexture : register(t0);
-
-// Input U texture (chroma)
-Texture2D g_uTexture : register(t1);
-
-// Input V texture (chroma)
-Texture2D g_vTexture : register(t2);
-
-// Output RGB texture
-RWTexture2D g_rgbTexture : register(u0);
-
-[numthreads(8, 8, 1)]
-void main(uint3 id : SV_DispatchThreadID)
-{
- // Get texture dimensions
- uint2 texSize;
- g_yTexture.GetDimensions(texSize.x, texSize.y);
-
- // Bounds check
- if (id.x >= texSize.x || id.y >= texSize.y)
- return;
-
- // Sample Y, U, V values
- float y = g_yTexture[id.xy].r;
-
- // UV coordinates are half resolution (4:2:0 format)
- uint2 uvCoord = id.xy / 2;
- float u = g_uTexture[uvCoord].r;
- float v = g_vTexture[uvCoord].r;
-
- // Convert from [0,1] to YUV ranges
- y = (y * 255.0f - 16.0f) / 219.0f;
- u = (u * 255.0f - 128.0f) / 224.0f;
- v = (v * 255.0f - 128.0f) / 224.0f;
-
- // BT.709 YUV to RGB conversion matrix
- float3 rgb;
- rgb.r = y + 1.5748f * v;
- rgb.g = y - 0.1873f * u - 0.4681f * v;
- rgb.b = y + 1.8556f * u;
-
- // Clamp to [0,1] range
- rgb = saturate(rgb);
-
- // Write RGB result
- g_rgbTexture[id.xy] = float4(rgb, 1.0f);
-}
-)";
-
- // Test compilation
- ComPtr shaderBlob;
- ComPtr errorBlob;
-
- HRESULT hr = D3DCompile(
- shaderSource,
- strlen(shaderSource),
- "YUVToRGB_Compute_Test",
- nullptr,
- nullptr,
- "main",
- "cs_5_0",
- D3DCOMPILE_OPTIMIZATION_LEVEL3,
- 0,
- &shaderBlob,
- &errorBlob
- );
-
- if (FAILED(hr))
- {
- if (errorBlob)
- {
- std::cout << "[FAIL] Compute shader compilation failed: "
- << (char*)errorBlob->GetBufferPointer() << std::endl;
- }
- return hr;
- }
-
- std::cout << "[PASS] Compute shader compiled successfully (Size: "
- << shaderBlob->GetBufferSize() << " bytes)" << std::endl;
-
- return S_OK;
-}
-
-} // namespace Vav2Player
\ No newline at end of file
diff --git a/vav2/Vav2Player/Vav2Player/src/Rendering/SimpleGPURenderer_Headless.h b/vav2/Vav2Player/Vav2Player/src/Rendering/SimpleGPURenderer_Headless.h
deleted file mode 100644
index 069c98e..0000000
--- a/vav2/Vav2Player/Vav2Player/src/Rendering/SimpleGPURenderer_Headless.h
+++ /dev/null
@@ -1,64 +0,0 @@
-#pragma once
-
-#include
-#include
-#include
-#include
-#include "../Common/VideoTypes.h"
-
-using Microsoft::WRL::ComPtr;
-
-namespace Vav2Player {
-
-// Headless version of SimpleGPURenderer for testing
-// Phase 3: GPU pipeline testing without GUI dependencies
-class SimpleGPURenderer_Headless
-{
-public:
- SimpleGPURenderer_Headless();
- ~SimpleGPURenderer_Headless();
-
- // Core lifecycle (headless - no SwapChainPanel)
- HRESULT Initialize(uint32_t width, uint32_t height);
- void Shutdown();
- bool IsInitialized() const { return m_initialized; }
-
- // Video rendering (headless - no actual display)
- HRESULT RenderVideoFrame(const VideoFrame& frame);
- HRESULT TestGPUPipeline(); // Test GPU functionality
-
- // Size management
- uint32_t GetWidth() const { return m_width; }
- uint32_t GetHeight() const { return m_height; }
-
-private:
- // D3D12 core objects (minimal for headless)
- ComPtr m_device;
- ComPtr m_commandQueue;
- ComPtr m_commandAllocator;
- ComPtr m_commandList;
-
- // Synchronization
- ComPtr m_fence;
- UINT64 m_fenceValue;
- HANDLE m_fenceEvent;
-
- // Video textures for testing
- ComPtr m_yTexture;
- ComPtr m_uTexture;
- ComPtr m_vTexture;
-
- // State
- bool m_initialized = false;
- uint32_t m_width = 0;
- uint32_t m_height = 0;
-
- // Helper methods
- HRESULT CreateDevice();
- HRESULT CreateCommandQueue();
- HRESULT CreateSynchronizationObjects();
- HRESULT WaitForGPU();
- HRESULT TestComputeShaderCompilation();
-};
-
-} // namespace Vav2Player
\ No newline at end of file