#include "pch.h" #include "MainVideoPage.xaml.h" #if __has_include("MainVideoPage.g.cpp") #include "MainVideoPage.g.cpp" #endif using namespace winrt; using namespace winrt::Microsoft::UI::Xaml; namespace winrt::Vav2Player::implementation { MainVideoPage::MainVideoPage() { InitializeComponent(); } winrt::Windows::Foundation::IAsyncAction MainVideoPage::OpenFileButton_Click(winrt::Windows::Foundation::IInspectable const&, winrt::Microsoft::UI::Xaml::RoutedEventArgs const&) { try { // Create file picker winrt::Windows::Storage::Pickers::FileOpenPicker picker; picker.ViewMode(winrt::Windows::Storage::Pickers::PickerViewMode::Thumbnail); picker.SuggestedStartLocation(winrt::Windows::Storage::Pickers::PickerLocationId::VideosLibrary); // Add supported video file types picker.FileTypeFilter().Append(L".webm"); picker.FileTypeFilter().Append(L".mkv"); picker.FileTypeFilter().Append(L".mp4"); picker.FileTypeFilter().Append(L".avi"); // Initialize picker with window handle - use simple approach for Page try { auto initializeWithWindow = picker.as(); // Get the active window handle HWND hWnd = GetActiveWindow(); if (hWnd == nullptr) { hWnd = GetForegroundWindow(); } if (hWnd != nullptr) { initializeWithWindow->Initialize(hWnd); } } catch (...) { // If window handle initialization fails, continue without it // FileOpenPicker might still work in some cases } // Show picker and get selected file auto file = co_await picker.PickSingleFileAsync(); if (file != nullptr) { auto filePath = file.Path(); // Load video using VideoPlayerControl if (VideoPlayer()) { VideoPlayer().LoadVideo(filePath); } // Update UI if (StatusText()) { StatusText().Text(L"Loading video: " + filePath); } if (PlayButton()) PlayButton().IsEnabled(true); if (PauseButton()) PauseButton().IsEnabled(true); if (StopButton()) StopButton().IsEnabled(true); } else { if (StatusText()) { StatusText().Text(L"No file selected"); } } } catch (...) { if (StatusText()) { StatusText().Text(L"Failed to open file picker or load video"); } } } void MainVideoPage::TestDecodeButton_Click(winrt::Windows::Foundation::IInspectable const&, winrt::Microsoft::UI::Xaml::RoutedEventArgs const&) { try { if (StatusText()) { StatusText().Text(L"Test decode functionality - Coming soon"); } } catch (...) { // Ignore errors during initialization } } void MainVideoPage::PlayButton_Click(winrt::Windows::Foundation::IInspectable const&, winrt::Microsoft::UI::Xaml::RoutedEventArgs const&) { try { if (VideoPlayer()) { VideoPlayer().Play(); } if (StatusText()) { StatusText().Text(L"Playing video..."); } } catch (...) { if (StatusText()) { StatusText().Text(L"Failed to play video"); } } } void MainVideoPage::PauseButton_Click(winrt::Windows::Foundation::IInspectable const&, winrt::Microsoft::UI::Xaml::RoutedEventArgs const&) { try { if (VideoPlayer()) { VideoPlayer().Pause(); } if (StatusText()) { StatusText().Text(L"Video paused"); } } catch (...) { if (StatusText()) { StatusText().Text(L"Failed to pause video"); } } } void MainVideoPage::StopButton_Click(winrt::Windows::Foundation::IInspectable const&, winrt::Microsoft::UI::Xaml::RoutedEventArgs const&) { try { if (VideoPlayer()) { VideoPlayer().Stop(); } if (StatusText()) { StatusText().Text(L"Video stopped"); } } catch (...) { if (StatusText()) { StatusText().Text(L"Failed to stop video"); } } } void MainVideoPage::DecoderSelectionComboBox_SelectionChanged(winrt::Windows::Foundation::IInspectable const&, winrt::Microsoft::UI::Xaml::Controls::SelectionChangedEventArgs const&) { try { if (StatusText()) { StatusText().Text(L"Decoder selection changed"); } } catch (...) { // Ignore errors during initialization } } void MainVideoPage::EnableFileOutputCheckBox_Checked(winrt::Windows::Foundation::IInspectable const&, winrt::Microsoft::UI::Xaml::RoutedEventArgs const&) { try { if (OutputFormatComboBox()) { OutputFormatComboBox().IsEnabled(true); } if (StatusText()) { StatusText().Text(L"File output enabled"); } } catch (...) { // Ignore errors during initialization } } void MainVideoPage::EnableFileOutputCheckBox_Unchecked(winrt::Windows::Foundation::IInspectable const&, winrt::Microsoft::UI::Xaml::RoutedEventArgs const&) { try { if (OutputFormatComboBox()) { OutputFormatComboBox().IsEnabled(false); } if (StatusText()) { StatusText().Text(L"File output disabled"); } } catch (...) { // Ignore errors during initialization } } void MainVideoPage::OutputFormatComboBox_SelectionChanged(winrt::Windows::Foundation::IInspectable const&, winrt::Microsoft::UI::Xaml::Controls::SelectionChangedEventArgs const&) { try { if (StatusText()) { StatusText().Text(L"Output format changed"); } } catch (...) { // Ignore errors during initialization } } // Helper methods - actual implementations void MainVideoPage::ConvertYUVToBGRA(const VavCoreVideoFrame& yuv_frame, uint8_t* bgra_buffer, uint32_t width, uint32_t height) { const uint8_t* y_plane = yuv_frame.y_plane; const uint8_t* u_plane = yuv_frame.u_plane; const uint8_t* v_plane = yuv_frame.v_plane; const int32_t y_stride = yuv_frame.y_stride; const int32_t u_stride = yuv_frame.u_stride; const int32_t v_stride = yuv_frame.v_stride; // Lookup table optimization static bool lookup_initialized = false; static int16_t r_v_table[256]; static int16_t g_u_table[256]; static int16_t g_v_table[256]; static int16_t b_u_table[256]; if (!lookup_initialized) { for (int i = 0; i < 256; i++) { int uv_val = i - 128; r_v_table[i] = (409 * uv_val + 128) >> 8; g_u_table[i] = (100 * uv_val + 128) >> 8; g_v_table[i] = (208 * uv_val + 128) >> 8; b_u_table[i] = (516 * uv_val + 128) >> 8; } lookup_initialized = true; } // Process 4 pixels at a time for cache efficiency for (uint32_t row = 0; row < height; row += 2) { for (uint32_t col = 0; col < width; col += 2) { // UV values are one per 2x2 block uint32_t uv_row = row / 2; uint32_t uv_col = col / 2; if (uv_row >= height / 2 || uv_col >= width / 2) continue; int u = u_plane[uv_row * u_stride + uv_col]; int v = v_plane[uv_row * v_stride + uv_col]; // Get conversion coefficients from lookup table int r_offset = r_v_table[v]; int g_u_offset = g_u_table[u]; int g_v_offset = g_v_table[v]; int b_offset = b_u_table[u]; // Process 4 pixels in 2x2 block for (int dy = 0; dy < 2 && (row + dy) < height; dy++) { for (int dx = 0; dx < 2 && (col + dx) < width; dx++) { uint32_t pixel_row = row + dy; uint32_t pixel_col = col + dx; int y = y_plane[pixel_row * y_stride + pixel_col]; int y_scaled = (298 * (y - 16) + 128) >> 8; // RGB calculation int r = std::clamp(y_scaled + r_offset, 0, 255); int g = std::clamp(y_scaled - g_u_offset - g_v_offset, 0, 255); int b = std::clamp(y_scaled + b_offset, 0, 255); // Store as BGRA format uint32_t pixel_offset = (pixel_row * width + pixel_col) * 4; bgra_buffer[pixel_offset + 0] = static_cast(b); bgra_buffer[pixel_offset + 1] = static_cast(g); bgra_buffer[pixel_offset + 2] = static_cast(r); bgra_buffer[pixel_offset + 3] = 255; } } } } } void MainVideoPage::RenderFrameToScreen(const VavCoreVideoFrame& frame, winrt::Microsoft::UI::Xaml::Media::Imaging::WriteableBitmap& bitmap, std::vector& bgra_buffer) { // Check for valid frame and bitmap if (!frame.y_plane || !bitmap) { return; } try { // Convert YUV to BGRA ConvertYUVToBGRA(frame, bgra_buffer.data(), frame.width, frame.height); // Get direct access to WriteableBitmap pixel buffer auto buffer = bitmap.PixelBuffer(); auto byteAccess = buffer.as<::IBufferByteAccess>(); uint8_t* dest_pixels = nullptr; byteAccess->Buffer(&dest_pixels); if (dest_pixels) { // Direct memory copy to minimize overhead memcpy(dest_pixels, bgra_buffer.data(), bgra_buffer.size()); // Invalidate bitmap to trigger UI update bitmap.Invalidate(); } } catch (...) { // Ignore rendering errors } } }