Toggle file output

This commit is contained in:
2025-09-20 04:47:43 +09:00
parent c98913b0fa
commit b1bd2e8bb4
4 changed files with 337 additions and 74 deletions

View File

@@ -55,10 +55,10 @@ Vav2Player 에서 성능의 저하가 발생될 우려가 있는 부분을 진
>>> WORK
https://www.youtube.com/@%EC%84%9C%ED%98%84SeoHyeon1/shorts
● 주요 미완성 부분들:
1. VP9 디코더 (미래 확장)

View File

@@ -34,44 +34,80 @@
</Border>
<!-- Player Controls -->
<StackPanel Grid.Row="1" Orientation="Horizontal" HorizontalAlignment="Center" Margin="10">
<Button x:Name="OpenFileButton"
Content="Open AV1 File"
Width="120"
Height="40"
Margin="5"
Click="OpenFileButton_Click"/>
<StackPanel Grid.Row="1" Orientation="Vertical" HorizontalAlignment="Center" Margin="10">
<!-- Main Control Buttons -->
<StackPanel Orientation="Horizontal" HorizontalAlignment="Center" Margin="0,0,0,10">
<Button x:Name="OpenFileButton"
Content="Open AV1 File"
Width="120"
Height="40"
Margin="5"
Click="OpenFileButton_Click"/>
<Button x:Name="TestDecodeButton"
Content="Test Decode"
Width="100"
Height="40"
Margin="5"
Click="TestDecodeButton_Click"/>
<Button x:Name="TestDecodeButton"
Content="Test Decode"
Width="100"
Height="40"
Margin="5"
Click="TestDecodeButton_Click"/>
<Button x:Name="PlayButton"
Content="Play"
Width="80"
Height="40"
Margin="5"
IsEnabled="False"
Click="PlayButton_Click"/>
<Button x:Name="PlayButton"
Content="Play"
Width="80"
Height="40"
Margin="5"
IsEnabled="False"
Click="PlayButton_Click"/>
<Button x:Name="PauseButton"
Content="Pause"
Width="80"
Height="40"
Margin="5"
IsEnabled="False"
Click="PauseButton_Click"/>
<Button x:Name="PauseButton"
Content="Pause"
Width="80"
Height="40"
Margin="5"
IsEnabled="False"
Click="PauseButton_Click"/>
<Button x:Name="StopButton"
Content="Stop"
Width="80"
Height="40"
Margin="5"
IsEnabled="False"
Click="StopButton_Click"/>
<Button x:Name="StopButton"
Content="Stop"
Width="80"
Height="40"
Margin="5"
IsEnabled="False"
Click="StopButton_Click"/>
</StackPanel>
<!-- File Output Options -->
<StackPanel Orientation="Horizontal" HorizontalAlignment="Center">
<CheckBox x:Name="EnableFileOutputCheckBox"
Content="Save frames to files"
IsChecked="False"
VerticalAlignment="Center"
Margin="10,0,20,0"
Checked="EnableFileOutputCheckBox_Checked"
Unchecked="EnableFileOutputCheckBox_Unchecked"/>
<ComboBox x:Name="OutputFormatComboBox"
Width="120"
Height="32"
VerticalAlignment="Center"
Margin="5,0,20,0"
IsEnabled="False"
SelectionChanged="OutputFormatComboBox_SelectionChanged">
<ComboBoxItem Content="BMP Format" IsSelected="True"/>
<ComboBoxItem Content="Raw YUV"/>
</ComboBox>
<TextBlock Text="Output folder:"
VerticalAlignment="Center"
Margin="0,0,5,0"/>
<TextBlock x:Name="OutputFolderText"
Text="Not set"
VerticalAlignment="Center"
FontStyle="Italic"
Foreground="Gray"
MaxWidth="200"
TextTrimming="CharacterEllipsis"/>
</StackPanel>
</StackPanel>
<!-- Status and Info Panel -->

View File

@@ -31,6 +31,21 @@ public:
namespace winrt::Vav2Player::implementation
{
void MainWindow::InitializeUI()
{
try
{
// Initialize UI state
UpdateFileOutputControls();
UpdateStatus("Ready");
OutputDebugStringA("MainWindow UI initialized\n");
}
catch (const std::exception& e)
{
UpdateStatus("Error initializing UI: " + std::string(e.what()));
}
}
void MainWindow::OpenFileButton_Click(Windows::Foundation::IInspectable const&, winrt::Microsoft::UI::Xaml::RoutedEventArgs const&)
{
try
@@ -88,15 +103,11 @@ namespace winrt::Vav2Player::implementation
UpdateStatus("AV1 decoder initialized successfully!");
// Initialize FileOutput for BMP file saving
if (!m_fileOutput)
{
m_fileOutput = std::make_unique<FileOutput>();
if (!m_fileOutput->CreateOutputDirectory())
{
UpdateStatus("Warning: Failed to create output directory");
}
}
// Initialize video rendering
InitializeVideoRenderer();
// FileOutput is now managed by the checkbox UI - don't create it here
// The checkbox event handlers will manage FileOutput creation/destruction
// Reset file reading position
m_fileReader->Reset();
@@ -193,12 +204,11 @@ namespace winrt::Vav2Player::implementation
// Reset progress bar
ProgressBar().Value(0);
// Clear video display
VideoImage().Source(nullptr);
m_renderBitmap = nullptr;
VideoDisplayArea().Visibility(winrt::Microsoft::UI::Xaml::Visibility::Visible);
// DO NOT destroy video renderer components - keep them for next playback
// The video renderer (m_renderBitmap, VideoImage) should remain intact
// Only reset playback state, not the display components
OutputDebugStringA("Playback stopped and reset\n");
OutputDebugStringA("Playback stopped and reset (renderer preserved)\n");
}
catch (...)
{
@@ -290,23 +300,37 @@ namespace winrt::Vav2Player::implementation
// Successfully decoded frame
m_currentFrame++;
// Render frame to screen
// ALWAYS render frame to screen first (regardless of file output)
OutputDebugStringA(("Processing frame " + std::to_string(m_currentFrame) + " - Rendering to screen\n").c_str());
RenderFrameToScreen(frame);
OutputDebugStringA(("Frame " + std::to_string(m_currentFrame) + " rendered to screen successfully\n").c_str());
// Save frame as BMP file
if (m_fileOutput)
// Save frame to file if enabled (wrapped in try-catch to prevent UI blocking)
if (m_fileOutputEnabled && m_fileOutput)
{
auto saveResult = m_fileOutput->SaveFrame(frame, m_currentFrame, 0.0);
if (saveResult.success)
try
{
OutputDebugStringA(("Saved frame " + std::to_string(m_currentFrame) + " as BMP: " + saveResult.saved_path.string() + "\n").c_str());
OutputDebugStringA(("Frame " + std::to_string(m_currentFrame) + " - Starting file save\n").c_str());
auto saveResult = m_fileOutput->SaveFrame(frame, m_currentFrame, 0.0);
if (saveResult.success)
{
OutputDebugStringA(("Saved frame " + std::to_string(m_currentFrame) + " to file: " + saveResult.saved_path.string() + "\n").c_str());
}
else
{
OutputDebugStringA(("Failed to save frame " + std::to_string(m_currentFrame) + " to file: " + saveResult.error_message + "\n").c_str());
}
}
else
catch (const std::exception& e)
{
OutputDebugStringA(("Failed to save frame " + std::to_string(m_currentFrame) + " as BMP: " + saveResult.error_message + "\n").c_str());
OutputDebugStringA(("Exception during file save for frame " + std::to_string(m_currentFrame) + ": " + e.what() + "\n").c_str());
}
catch (...)
{
OutputDebugStringA(("Unknown exception during file save for frame " + std::to_string(m_currentFrame) + "\n").c_str());
}
}
else
else if (m_fileOutputEnabled)
{
OutputDebugStringA(("Frame " + std::to_string(m_currentFrame) + " not saved - FileOutput not initialized\n").c_str());
}
@@ -537,19 +561,8 @@ namespace winrt::Vav2Player::implementation
// Initialize video rendering
InitializeVideoRenderer();
// Initialize FileOutput for BMP file saving
if (!m_fileOutput)
{
m_fileOutput = std::make_unique<FileOutput>();
if (m_fileOutput->CreateOutputDirectory())
{
OutputDebugStringA("FileOutput initialized - BMP saving enabled\n");
}
else
{
OutputDebugStringA("Warning: Failed to create output directory for BMP files\n");
}
}
// FileOutput is now managed by the checkbox UI - don't create it here
// The checkbox event handlers will manage FileOutput creation/destruction
// Reset playback position
m_currentFrame = 0;
@@ -628,39 +641,86 @@ namespace winrt::Vav2Player::implementation
{
try
{
OutputDebugStringA("InitializeVideoRenderer: Starting video renderer initialization\n");
if (!m_fileReader || !m_fileReader->IsFileOpen()) {
OutputDebugStringA("InitializeVideoRenderer: ERROR - No file reader or file not open\n");
UpdateStatus("Error: No file loaded for video renderer");
return;
}
auto metadata = m_fileReader->GetVideoMetadata();
int width = metadata.width;
int height = metadata.height;
// Create WriteableBitmap with video dimensions
OutputDebugStringA(("InitializeVideoRenderer: Video dimensions: " + std::to_string(width) + "x" + std::to_string(height) + "\n").c_str());
if (width <= 0 || height <= 0) {
OutputDebugStringA("InitializeVideoRenderer: ERROR - Invalid video dimensions\n");
UpdateStatus("Error: Invalid video dimensions");
return;
}
// Always create a fresh bitmap - don't reuse existing one
OutputDebugStringA("InitializeVideoRenderer: Creating new WriteableBitmap\n");
m_renderBitmap = winrt::Microsoft::UI::Xaml::Media::Imaging::WriteableBitmap(width, height);
if (!m_renderBitmap) {
OutputDebugStringA("InitializeVideoRenderer: ERROR - Failed to create WriteableBitmap\n");
UpdateStatus("Error: Failed to create video bitmap");
return;
}
// Allocate buffer for BGRA conversion
m_bgraBuffer.resize(width * height * 4);
OutputDebugStringA(("InitializeVideoRenderer: Allocated BGRA buffer: " + std::to_string(m_bgraBuffer.size()) + " bytes\n").c_str());
// Set the bitmap as the source for the Image control
OutputDebugStringA("InitializeVideoRenderer: Setting bitmap as VideoImage source\n");
VideoImage().Source(m_renderBitmap);
// Hide placeholder text
// Hide placeholder text and show video
OutputDebugStringA("InitializeVideoRenderer: Updating UI visibility\n");
VideoDisplayArea().Visibility(winrt::Microsoft::UI::Xaml::Visibility::Collapsed);
OutputDebugStringA("InitializeVideoRenderer: Video renderer initialized successfully\n");
UpdateStatus("Video renderer initialized");
}
catch (winrt::hresult_error const& ex)
{
OutputDebugStringA(("InitializeVideoRenderer HRESULT error: " + winrt::to_string(ex.message()) + "\n").c_str());
UpdateStatus("Error initializing video renderer: " + winrt::to_string(ex.message()));
}
catch (const std::exception& ex)
{
OutputDebugStringA(("InitializeVideoRenderer std::exception: " + std::string(ex.what()) + "\n").c_str());
UpdateStatus("Error initializing video renderer: " + std::string(ex.what()));
}
catch (...)
{
OutputDebugStringA("InitializeVideoRenderer: Unknown exception occurred\n");
UpdateStatus("Error initializing video renderer: Unknown error");
}
}
void MainWindow::RenderFrameToScreen(const VideoFrame& frame)
{
if (!frame.is_valid || !m_renderBitmap)
if (!frame.is_valid)
{
OutputDebugStringA("RenderFrameToScreen: Frame is not valid\n");
return;
}
if (!m_renderBitmap)
{
OutputDebugStringA("RenderFrameToScreen: m_renderBitmap is null\n");
return;
}
try
{
OutputDebugStringA(("RenderFrameToScreen: Rendering frame " + std::to_string(frame.width) + "x" + std::to_string(frame.height) + "\n").c_str());
// Convert YUV frame to our BGRA buffer
ConvertYUVToBGRA(frame, m_bgraBuffer.data(), frame.width, frame.height);
@@ -670,16 +730,32 @@ namespace winrt::Vav2Player::implementation
uint8_t* dest_pixels = nullptr;
byteAccess->Buffer(&dest_pixels);
if (!dest_pixels)
{
OutputDebugStringA("RenderFrameToScreen: Failed to get pixel buffer\n");
return;
}
// Copy our BGRA data into the WriteableBitmap
memcpy(dest_pixels, m_bgraBuffer.data(), m_bgraBuffer.size());
// Invalidate the bitmap to trigger a UI update
m_renderBitmap.Invalidate();
OutputDebugStringA("RenderFrameToScreen: Frame rendered successfully\n");
}
catch (winrt::hresult_error const& ex)
{
OutputDebugStringA(("Error rendering frame: " + winrt::to_string(ex.message()) + "\n").c_str());
}
catch (const std::exception& ex)
{
OutputDebugStringA(("Error rendering frame (std::exception): " + std::string(ex.what()) + "\n").c_str());
}
catch (...)
{
OutputDebugStringA("Error rendering frame: Unknown exception\n");
}
}
// This function converts an I420 YUV frame to a BGRA frame.
@@ -728,4 +804,142 @@ namespace winrt::Vav2Player::implementation
}
}
}
// File Output Control Event Handlers
void MainWindow::EnableFileOutputCheckBox_Checked(winrt::Windows::Foundation::IInspectable const&, winrt::Microsoft::UI::Xaml::RoutedEventArgs const&)
{
m_fileOutputEnabled = true;
UpdateFileOutputControls();
InitializeFileOutput();
UpdateStatus("File output enabled");
}
void MainWindow::EnableFileOutputCheckBox_Unchecked(winrt::Windows::Foundation::IInspectable const&, winrt::Microsoft::UI::Xaml::RoutedEventArgs const&)
{
OutputDebugStringA("EnableFileOutputCheckBox_Unchecked: Starting\n");
m_fileOutputEnabled = false;
// Completely destroy FileOutput object when disabled
if (m_fileOutput) {
OutputDebugStringA("EnableFileOutputCheckBox_Unchecked: Destroying FileOutput object\n");
m_fileOutput.reset();
}
UpdateFileOutputControls();
UpdateStatus("File output disabled");
OutputDebugStringA("EnableFileOutputCheckBox_Unchecked: Completed\n");
}
void MainWindow::OutputFormatComboBox_SelectionChanged(winrt::Windows::Foundation::IInspectable const&, winrt::Microsoft::UI::Xaml::Controls::SelectionChangedEventArgs const&)
{
if (m_fileOutputEnabled && m_fileOutput) {
auto selectedIndex = OutputFormatComboBox().SelectedIndex();
FileOutput::OutputConfig config = m_fileOutput->GetConfig();
switch (selectedIndex) {
case 0: // BMP Format
config.format = FileOutput::OutputFormat::BMP;
UpdateStatus("Output format changed to BMP");
break;
case 1: // Raw YUV
config.format = FileOutput::OutputFormat::RawYUV;
UpdateStatus("Output format changed to Raw YUV");
break;
default:
config.format = FileOutput::OutputFormat::BMP;
break;
}
m_fileOutput->SetConfig(config);
}
}
// File Output Helper Methods
void MainWindow::UpdateFileOutputControls()
{
try {
// Enable/disable the format combo box based on checkbox state
OutputFormatComboBox().IsEnabled(m_fileOutputEnabled);
// Update output folder display
UpdateOutputFolderDisplay();
}
catch (const std::exception& e) {
UpdateStatus("Error updating file output controls: " + std::string(e.what()));
}
}
void MainWindow::InitializeFileOutput()
{
if (!m_fileOutputEnabled) {
return;
}
try {
OutputDebugStringA("InitializeFileOutput: Starting FileOutput initialization\n");
// Only create new FileOutput if it doesn't exist or needs reconfiguration
if (!m_fileOutput) {
// Create FileOutput with default configuration
FileOutput::OutputConfig config;
config.format = FileOutput::OutputFormat::BMP;
config.output_directory = "output";
config.filename_prefix = "frame";
config.create_subdirectories = true;
config.overwrite_existing = true;
OutputDebugStringA("InitializeFileOutput: Creating new FileOutput object\n");
m_fileOutput = std::make_unique<FileOutput>(config);
// Create output directory
if (!m_fileOutput->CreateOutputDirectory()) {
OutputDebugStringA("InitializeFileOutput: Failed to create output directory\n");
UpdateStatus("Warning: Failed to create output directory");
} else {
OutputDebugStringA("InitializeFileOutput: Output directory created successfully\n");
}
}
// Update the combo box selection to match
OutputFormatComboBox().SelectedIndex(0); // BMP by default
UpdateOutputFolderDisplay();
UpdateStatus("File output initialized successfully");
OutputDebugStringA("InitializeFileOutput: FileOutput initialization completed successfully\n");
}
catch (const std::exception& e) {
OutputDebugStringA(("InitializeFileOutput error: " + std::string(e.what()) + "\n").c_str());
UpdateStatus("Error initializing file output: " + std::string(e.what()));
// Don't automatically turn off the checkbox - let user decide
// m_fileOutputEnabled = false;
// EnableFileOutputCheckBox().IsChecked(false);
// UpdateFileOutputControls();
}
}
void MainWindow::UpdateOutputFolderDisplay()
{
try {
if (m_fileOutput && m_fileOutputEnabled) {
auto config = m_fileOutput->GetConfig();
std::string displayPath = config.output_directory.string();
// Convert to wide string for display
std::wstring widePath(displayPath.begin(), displayPath.end());
OutputFolderText().Text(widePath);
OutputFolderText().Foreground(winrt::Microsoft::UI::Xaml::Media::SolidColorBrush(winrt::Microsoft::UI::Colors::Black()));
} else {
OutputFolderText().Text(L"Not set");
OutputFolderText().Foreground(winrt::Microsoft::UI::Xaml::Media::SolidColorBrush(winrt::Microsoft::UI::Colors::Gray()));
}
}
catch (const std::exception& e) {
OutputFolderText().Text(L"Error");
OutputFolderText().Foreground(winrt::Microsoft::UI::Xaml::Media::SolidColorBrush(winrt::Microsoft::UI::Colors::Red()));
}
}
}

View File

@@ -16,6 +16,7 @@ namespace winrt::Vav2Player::implementation
{
// Initialize XAML components and event handlers
InitializeComponent();
InitializeUI();
}
// Event handlers
@@ -25,6 +26,11 @@ namespace winrt::Vav2Player::implementation
void PauseButton_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);
// File Output control event handlers
void EnableFileOutputCheckBox_Checked(winrt::Windows::Foundation::IInspectable const& sender, winrt::Microsoft::UI::Xaml::RoutedEventArgs const& e);
void EnableFileOutputCheckBox_Unchecked(winrt::Windows::Foundation::IInspectable const& sender, winrt::Microsoft::UI::Xaml::RoutedEventArgs const& e);
void OutputFormatComboBox_SelectionChanged(winrt::Windows::Foundation::IInspectable const& sender, winrt::Microsoft::UI::Xaml::Controls::SelectionChangedEventArgs const& e);
private:
// Video processing components
std::unique_ptr<WebMFileReader> m_fileReader;
@@ -41,6 +47,7 @@ namespace winrt::Vav2Player::implementation
// UI state
bool m_isPlaying = false;
bool m_isFileLoaded = false;
bool m_fileOutputEnabled = false;
// Playback state
uint64_t m_currentFrame = 0;
@@ -59,6 +66,12 @@ namespace winrt::Vav2Player::implementation
void InitializePlayback();
void StartPlaybackTimer();
void StopPlaybackTimer();
void InitializeUI();
// File Output helper methods
void UpdateFileOutputControls();
void InitializeFileOutput();
void UpdateOutputFolderDisplay();
// Video rendering methods
void InitializeVideoRenderer();