299 lines
10 KiB
C++
299 lines
10 KiB
C++
#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<IInitializeWithWindow>();
|
|
|
|
// 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<uint8_t>(b);
|
|
bgra_buffer[pixel_offset + 1] = static_cast<uint8_t>(g);
|
|
bgra_buffer[pixel_offset + 2] = static_cast<uint8_t>(r);
|
|
bgra_buffer[pixel_offset + 3] = 255;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void MainVideoPage::RenderFrameToScreen(const VavCoreVideoFrame& frame, winrt::Microsoft::UI::Xaml::Media::Imaging::WriteableBitmap& bitmap, std::vector<uint8_t>& 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
|
|
}
|
|
}
|
|
} |