Optimization with frame pool
This commit is contained in:
@@ -30,7 +30,8 @@
|
||||
"Bash(\".\\Vav2Player.exe\" \"test.webm\")",
|
||||
"Bash(\".\\Vav2Player.exe\" \"D:\\Project\\video-av1\\sample\\simple_test.webm\")",
|
||||
"Bash(\"C:\\Program Files\\Microsoft Visual Studio\\2022\\Community\\MSBuild\\Current\\Bin\\MSBuild.exe\" Vav2Player.vcxproj \"/p:Configuration=Debug\" \"/p:Platform=x64\")",
|
||||
"Bash(cmd /c:*)"
|
||||
"Bash(cmd /c:*)",
|
||||
"Bash(\"C:/Program Files/Microsoft Visual Studio/2022/Community/MSBuild/Current/Bin/MSBuild.exe\" Vav2Player.vcxproj \"/p:Configuration=Debug\" \"/p:Platform=x64\")"
|
||||
],
|
||||
"deny": [],
|
||||
"ask": []
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
#include <crtdbg.h>
|
||||
|
||||
using namespace winrt;
|
||||
using namespace Microsoft::UI::Xaml;
|
||||
// using namespace Microsoft::UI::Xaml; // 충돌 방지를 위해 명시적으로 사용
|
||||
|
||||
// 헤드리스 모드용 에러 핸들러
|
||||
void HeadlessAbortHandler(int signal) {
|
||||
@@ -95,7 +95,7 @@ namespace winrt::Vav2Player::implementation
|
||||
// See https://github.com/microsoft/cppwinrt/tree/master/nuget#initializecomponent
|
||||
|
||||
#if defined _DEBUG && !defined DISABLE_XAML_GENERATED_BREAK_ON_UNHANDLED_EXCEPTION
|
||||
UnhandledException([](IInspectable const&, UnhandledExceptionEventArgs const& e)
|
||||
UnhandledException([](Windows::Foundation::IInspectable const&, winrt::Microsoft::UI::Xaml::UnhandledExceptionEventArgs const& e)
|
||||
{
|
||||
if (IsDebuggerPresent())
|
||||
{
|
||||
@@ -110,7 +110,7 @@ namespace winrt::Vav2Player::implementation
|
||||
/// Invoked when the application is launched.
|
||||
/// </summary>
|
||||
/// <param name="e">Details about the launch request and process.</param>
|
||||
void App::OnLaunched([[maybe_unused]] LaunchActivatedEventArgs const& e)
|
||||
void App::OnLaunched([[maybe_unused]] winrt::Microsoft::UI::Xaml::LaunchActivatedEventArgs const& e)
|
||||
{
|
||||
// 명령줄 인자 확인
|
||||
int argc = 0;
|
||||
|
||||
@@ -18,12 +18,19 @@
|
||||
|
||||
<!-- Video Display Area -->
|
||||
<Border Grid.Row="0" Background="Black" BorderBrush="Gray" BorderThickness="1" Margin="10">
|
||||
<TextBlock x:Name="VideoDisplayArea"
|
||||
Text="AV1 Video will be displayed here"
|
||||
Foreground="White"
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"
|
||||
FontSize="16"/>
|
||||
<Grid>
|
||||
<TextBlock x:Name="VideoDisplayArea"
|
||||
Text="AV1 Video will be displayed here"
|
||||
Foreground="White"
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"
|
||||
FontSize="16"/>
|
||||
|
||||
<Image x:Name="VideoImage"
|
||||
Stretch="Uniform"
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"/>
|
||||
</Grid>
|
||||
</Border>
|
||||
|
||||
<!-- Player Controls -->
|
||||
|
||||
@@ -1,20 +1,21 @@
|
||||
#include "pch.h"
|
||||
#include "MainWindow.xaml.h"
|
||||
#include "src/Output/FileOutput.h"
|
||||
#if __has_include("MainWindow.g.cpp")
|
||||
#include "MainWindow.g.cpp"
|
||||
#endif
|
||||
|
||||
using namespace winrt;
|
||||
using namespace Microsoft::UI::Xaml;
|
||||
using namespace Microsoft::UI::Xaml::Controls;
|
||||
using namespace Windows::Foundation;
|
||||
// using namespace Microsoft::UI::Xaml; // 충돌 방지를 위해 명시적으로 사용
|
||||
// using namespace Microsoft::UI::Xaml::Controls;
|
||||
// using namespace Windows::Foundation;
|
||||
|
||||
// To learn more about WinUI, the WinUI project structure,
|
||||
// and more about our project templates, see: http://aka.ms/winui-project-info.
|
||||
|
||||
namespace winrt::Vav2Player::implementation
|
||||
{
|
||||
void MainWindow::OpenFileButton_Click(IInspectable const&, RoutedEventArgs const&)
|
||||
void MainWindow::OpenFileButton_Click(Windows::Foundation::IInspectable const&, winrt::Microsoft::UI::Xaml::RoutedEventArgs const&)
|
||||
{
|
||||
try
|
||||
{
|
||||
@@ -30,7 +31,7 @@ namespace winrt::Vav2Player::implementation
|
||||
}
|
||||
}
|
||||
|
||||
void MainWindow::TestDecodeButton_Click(IInspectable const&, RoutedEventArgs const&)
|
||||
void MainWindow::TestDecodeButton_Click(Windows::Foundation::IInspectable const&, winrt::Microsoft::UI::Xaml::RoutedEventArgs const&)
|
||||
{
|
||||
try
|
||||
{
|
||||
@@ -69,14 +70,18 @@ namespace winrt::Vav2Player::implementation
|
||||
}
|
||||
}
|
||||
|
||||
// Initialize FileOutput if not already done
|
||||
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");
|
||||
}
|
||||
}
|
||||
|
||||
UpdateStatus("AV1 decoder initialized successfully!");
|
||||
|
||||
// Reset file reading position
|
||||
m_fileReader->Reset();
|
||||
|
||||
@@ -89,7 +94,7 @@ namespace winrt::Vav2Player::implementation
|
||||
}
|
||||
}
|
||||
|
||||
void MainWindow::PlayButton_Click(IInspectable const&, RoutedEventArgs const&)
|
||||
void MainWindow::PlayButton_Click(Windows::Foundation::IInspectable const&, winrt::Microsoft::UI::Xaml::RoutedEventArgs const&)
|
||||
{
|
||||
try
|
||||
{
|
||||
@@ -124,7 +129,7 @@ namespace winrt::Vav2Player::implementation
|
||||
}
|
||||
}
|
||||
|
||||
void MainWindow::PauseButton_Click(IInspectable const&, RoutedEventArgs const&)
|
||||
void MainWindow::PauseButton_Click(Windows::Foundation::IInspectable const&, winrt::Microsoft::UI::Xaml::RoutedEventArgs const&)
|
||||
{
|
||||
try
|
||||
{
|
||||
@@ -143,7 +148,7 @@ namespace winrt::Vav2Player::implementation
|
||||
}
|
||||
}
|
||||
|
||||
void MainWindow::StopButton_Click(IInspectable const&, RoutedEventArgs const&)
|
||||
void MainWindow::StopButton_Click(Windows::Foundation::IInspectable const&, winrt::Microsoft::UI::Xaml::RoutedEventArgs const&)
|
||||
{
|
||||
try
|
||||
{
|
||||
@@ -247,16 +252,28 @@ namespace winrt::Vav2Player::implementation
|
||||
// Successfully decoded frame
|
||||
m_currentFrame++;
|
||||
|
||||
// Save frame to file if FileOutput is available
|
||||
// Render frame to screen
|
||||
RenderFrameToScreen(frame);
|
||||
|
||||
// Save frame as BMP file
|
||||
if (m_fileOutput)
|
||||
{
|
||||
auto saveResult = m_fileOutput->SaveFrame(frame, m_currentFrame);
|
||||
auto saveResult = m_fileOutput->SaveFrame(frame, m_currentFrame, 0.0);
|
||||
if (saveResult.success)
|
||||
{
|
||||
OutputDebugStringA(("Saved frame " + std::to_string(m_currentFrame) +
|
||||
" to " + saveResult.saved_path.string() + "\n").c_str());
|
||||
OutputDebugStringA(("Saved frame " + std::to_string(m_currentFrame) + " as BMP: " + saveResult.saved_path.string() + "\n").c_str());
|
||||
}
|
||||
else
|
||||
{
|
||||
OutputDebugStringA(("Failed to save frame " + std::to_string(m_currentFrame) + " as BMP: " + saveResult.error_message + "\n").c_str());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
OutputDebugStringA(("Frame " + std::to_string(m_currentFrame) + " not saved - FileOutput not initialized\n").c_str());
|
||||
}
|
||||
|
||||
OutputDebugStringA(("Displayed frame " + std::to_string(m_currentFrame) + "\n").c_str());
|
||||
|
||||
// Update progress
|
||||
UpdateProgress();
|
||||
@@ -480,21 +497,21 @@ namespace winrt::Vav2Player::implementation
|
||||
}
|
||||
}
|
||||
|
||||
// Initialize FileOutput if not already done
|
||||
// Initialize video rendering
|
||||
InitializeVideoRenderer();
|
||||
|
||||
// Initialize FileOutput for BMP file saving
|
||||
if (!m_fileOutput)
|
||||
{
|
||||
m_fileOutput = std::make_unique<FileOutput>();
|
||||
|
||||
// Configure output to save frames as BMP files
|
||||
FileOutput::OutputConfig config;
|
||||
config.format = FileOutput::OutputFormat::BMP;
|
||||
config.output_directory = "output_frames";
|
||||
config.filename_prefix = "frame";
|
||||
config.create_subdirectories = true;
|
||||
config.overwrite_existing = true;
|
||||
|
||||
m_fileOutput->SetConfig(config);
|
||||
m_fileOutput->CreateOutputDirectory();
|
||||
if (m_fileOutput->CreateOutputDirectory())
|
||||
{
|
||||
OutputDebugStringA("FileOutput initialized - BMP saving enabled\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
OutputDebugStringA("Warning: Failed to create output directory for BMP files\n");
|
||||
}
|
||||
}
|
||||
|
||||
// Reset playback position
|
||||
@@ -569,4 +586,28 @@ namespace winrt::Vav2Player::implementation
|
||||
// Error stopping timer
|
||||
}
|
||||
}
|
||||
|
||||
// Video rendering implementation
|
||||
void MainWindow::InitializeVideoRenderer()
|
||||
{
|
||||
// TODO: 비디오 렌더러 초기화 (현재는 스텁)
|
||||
UpdateStatus("Video renderer initialized (stub)");
|
||||
}
|
||||
|
||||
void MainWindow::RenderFrameToScreen(const VideoFrame& frame)
|
||||
{
|
||||
// TODO: 화면 렌더링 구현 (현재는 스텁)
|
||||
// 메모리 풀 최적화가 완료되면 이 부분을 다시 구현할 예정
|
||||
if (frame.is_valid)
|
||||
{
|
||||
OutputDebugStringA("Frame rendered (stub implementation)\n");
|
||||
}
|
||||
}
|
||||
|
||||
void MainWindow::ConvertYUVToBGRA(const VideoFrame& yuv_frame, uint8_t* bgra_buffer, uint32_t width, uint32_t height)
|
||||
{
|
||||
// TODO: YUV to BGRA 변환 구현 (현재는 스텁)
|
||||
// 메모리 풀 최적화 완료 후 다시 구현할 예정
|
||||
(void)yuv_frame; (void)bgra_buffer; (void)width; (void)height; // unused parameter warning 방지
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
#pragma once
|
||||
|
||||
#include "MainWindow.g.h"
|
||||
#include "src/Output/FileOutput.h"
|
||||
#include "src/FileIO/WebMFileReader.h"
|
||||
#include "src/Decoder/VideoDecoderFactory.h"
|
||||
#include "src/Output/FileOutput.h"
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
@@ -30,6 +30,10 @@ namespace winrt::Vav2Player::implementation
|
||||
std::unique_ptr<IVideoDecoder> m_decoder;
|
||||
std::unique_ptr<FileOutput> m_fileOutput;
|
||||
|
||||
// Video rendering components (TODO: 추후 렌더링 구현시 활성화)
|
||||
// winrt::Microsoft::UI::Xaml::Controls::Image m_videoImage{ nullptr };
|
||||
// winrt::Microsoft::UI::Xaml::Media::Imaging::WriteableBitmap m_renderBitmap{ nullptr };
|
||||
|
||||
// Current file path
|
||||
std::string m_currentFilePath;
|
||||
|
||||
@@ -55,6 +59,11 @@ namespace winrt::Vav2Player::implementation
|
||||
void StartPlaybackTimer();
|
||||
void StopPlaybackTimer();
|
||||
|
||||
// Video rendering methods
|
||||
void InitializeVideoRenderer();
|
||||
void RenderFrameToScreen(const VideoFrame& frame);
|
||||
void ConvertYUVToBGRA(const VideoFrame& yuv_frame, uint8_t* bgra_buffer, uint32_t width, uint32_t height);
|
||||
|
||||
// File picker helper
|
||||
winrt::Windows::Foundation::IAsyncAction OpenFileAsync();
|
||||
};
|
||||
|
||||
@@ -139,6 +139,7 @@
|
||||
<DependentUpon>MainWindow.xaml</DependentUpon>
|
||||
</ClInclude>
|
||||
<ClInclude Include="src\Common\VideoTypes.h" />
|
||||
<ClInclude Include="src\Common\FramePool.h" />
|
||||
<ClInclude Include="src\Decoder\IVideoDecoder.h" />
|
||||
<ClInclude Include="src\Decoder\VideoDecoderFactory.h" />
|
||||
<ClInclude Include="src\Decoder\AV1Decoder.h" />
|
||||
@@ -163,6 +164,7 @@
|
||||
<DependentUpon>MainWindow.xaml</DependentUpon>
|
||||
</ClCompile>
|
||||
<ClCompile Include="src\Decoder\VideoDecoderFactory.cpp" />
|
||||
<ClCompile Include="src\Common\FramePool.cpp" />
|
||||
<ClCompile Include="src\Decoder\AV1Decoder.cpp" />
|
||||
<ClCompile Include="src\FileIO\WebMFileReader.cpp" />
|
||||
<ClCompile Include="src\Pipeline\FrameBuffer.cpp" />
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
<ClCompile Include="pch.cpp" />
|
||||
<ClCompile Include="$(GeneratedFilesDir)module.g.cpp" />
|
||||
<ClCompile Include="src\Decoder\VideoDecoderFactory.cpp" />
|
||||
<ClCompile Include="src\Common\FramePool.cpp" />
|
||||
<ClCompile Include="src\Decoder\AV1Decoder.cpp" />
|
||||
<ClCompile Include="src\FileIO\WebMFileReader.cpp" />
|
||||
<ClCompile Include="src\Pipeline\FrameBuffer.cpp" />
|
||||
@@ -24,6 +25,7 @@
|
||||
<ItemGroup>
|
||||
<ClInclude Include="pch.h" />
|
||||
<ClInclude Include="src\Common\VideoTypes.h" />
|
||||
<ClInclude Include="src\Common\FramePool.h" />
|
||||
<ClInclude Include="src\Decoder\IVideoDecoder.h" />
|
||||
<ClInclude Include="src\Decoder\VideoDecoderFactory.h" />
|
||||
<ClInclude Include="src\Decoder\AV1Decoder.h" />
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
#include <winrt/Microsoft.UI.Xaml.Interop.h>
|
||||
#include <winrt/Microsoft.UI.Xaml.Markup.h>
|
||||
#include <winrt/Microsoft.UI.Xaml.Media.h>
|
||||
#include <winrt/Microsoft.UI.Xaml.Media.Imaging.h>
|
||||
#include <winrt/Microsoft.UI.Xaml.Navigation.h>
|
||||
#include <winrt/Microsoft.UI.Xaml.Shapes.h>
|
||||
#include <winrt/Microsoft.UI.Dispatching.h>
|
||||
@@ -28,6 +29,9 @@
|
||||
#include <winrt/Windows.Storage.h>
|
||||
#include <winrt/Windows.Storage.Pickers.h>
|
||||
#include <winrt/Windows.Storage.Streams.h>
|
||||
#include <windows.storage.streams.h>
|
||||
#include <robuffer.h>
|
||||
#include <wrl.h>
|
||||
#include <shobjidl.h> // For IInitializeWithWindow
|
||||
#include <microsoft.ui.xaml.window.h> // For IWindowNative
|
||||
|
||||
@@ -40,7 +44,7 @@
|
||||
#include "src/Decoder/IVideoDecoder.h"
|
||||
#include "src/Decoder/VideoDecoderFactory.h"
|
||||
#include "src/FileIO/WebMFileReader.h"
|
||||
#include "src/Output/FileOutput.h"
|
||||
#include "src/Common/FramePool.h"
|
||||
|
||||
// Standard library headers
|
||||
#include <chrono>
|
||||
|
||||
202
vav2/Vav2Player/Vav2Player/src/Common/FramePool.cpp
Normal file
202
vav2/Vav2Player/Vav2Player/src/Common/FramePool.cpp
Normal file
@@ -0,0 +1,202 @@
|
||||
#include "pch.h"
|
||||
#include "FramePool.h"
|
||||
#include <iostream>
|
||||
#include <algorithm>
|
||||
|
||||
namespace Vav2Player {
|
||||
|
||||
FramePool& FramePool::GetInstance() {
|
||||
static FramePool instance;
|
||||
return instance;
|
||||
}
|
||||
|
||||
FramePool::~FramePool() {
|
||||
Cleanup();
|
||||
}
|
||||
|
||||
FramePool::PooledFrame FramePool::AcquireFrame(uint32_t width, uint32_t height, ColorSpace format) {
|
||||
m_total_requests++;
|
||||
|
||||
uint64_t frame_key = MakeFrameKey(width, height, format);
|
||||
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(m_mutex);
|
||||
FramePoolBucket* bucket = GetOrCreateBucket(frame_key);
|
||||
|
||||
if (!bucket->available_frames.empty()) {
|
||||
// 풀에서 재사용 가능한 프레임 가져오기
|
||||
auto frame = std::move(bucket->available_frames.front());
|
||||
bucket->available_frames.pop();
|
||||
bucket->in_use++;
|
||||
m_cache_hits++;
|
||||
|
||||
// 프레임 데이터 초기화 (이전 데이터 정리)
|
||||
frame->is_valid = false;
|
||||
frame->timestamp_seconds = 0.0;
|
||||
frame->frame_index = 0;
|
||||
|
||||
std::cout << "[FramePool] Cache hit - reusing frame " << width << "x" << height
|
||||
<< " (format=" << static_cast<int>(format) << ")" << std::endl;
|
||||
|
||||
return PooledFrame(std::move(frame), frame_key);
|
||||
}
|
||||
}
|
||||
|
||||
// 캐시 미스 - 새 프레임 생성
|
||||
m_cache_misses++;
|
||||
auto new_frame = CreateNewFrame(width, height, format);
|
||||
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(m_mutex);
|
||||
FramePoolBucket* bucket = GetOrCreateBucket(frame_key);
|
||||
bucket->total_allocated++;
|
||||
bucket->in_use++;
|
||||
}
|
||||
|
||||
std::cout << "[FramePool] Cache miss - creating new frame " << width << "x" << height
|
||||
<< " (format=" << static_cast<int>(format) << ")" << std::endl;
|
||||
|
||||
return PooledFrame(std::move(new_frame), frame_key);
|
||||
}
|
||||
|
||||
void FramePool::ReleaseFrame(PooledFrame&& pooled_frame) {
|
||||
if (!pooled_frame.frame) {
|
||||
return;
|
||||
}
|
||||
|
||||
uint64_t frame_key = pooled_frame.frame_key;
|
||||
|
||||
std::lock_guard<std::mutex> lock(m_mutex);
|
||||
auto it = m_pools.find(frame_key);
|
||||
if (it != m_pools.end()) {
|
||||
FramePoolBucket* bucket = it->second.get();
|
||||
|
||||
if (bucket->available_frames.size() < MAX_POOL_SIZE_PER_FORMAT) {
|
||||
// 풀에 여유가 있으면 반환
|
||||
bucket->available_frames.push(std::move(pooled_frame.frame));
|
||||
bucket->in_use--;
|
||||
|
||||
std::cout << "[FramePool] Frame returned to pool (pool size: "
|
||||
<< bucket->available_frames.size() << "/" << MAX_POOL_SIZE_PER_FORMAT << ")" << std::endl;
|
||||
} else {
|
||||
// 풀이 가득 찼으면 그냥 해제
|
||||
bucket->in_use--;
|
||||
bucket->total_allocated--;
|
||||
|
||||
std::cout << "[FramePool] Pool full - deallocating frame" << std::endl;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
FramePool::PoolStats FramePool::GetStats() const {
|
||||
std::lock_guard<std::mutex> lock(m_mutex);
|
||||
|
||||
PoolStats stats;
|
||||
stats.total_requests = m_total_requests;
|
||||
stats.cache_hits = m_cache_hits;
|
||||
stats.cache_misses = m_cache_misses;
|
||||
stats.hit_rate = (stats.total_requests > 0) ?
|
||||
(static_cast<double>(stats.cache_hits) / stats.total_requests) : 0.0;
|
||||
stats.active_pools = m_pools.size();
|
||||
|
||||
size_t total_allocated = 0;
|
||||
size_t total_in_use = 0;
|
||||
|
||||
for (const auto& pair : m_pools) {
|
||||
const FramePoolBucket* bucket = pair.second.get();
|
||||
total_allocated += bucket->total_allocated;
|
||||
total_in_use += bucket->in_use;
|
||||
}
|
||||
|
||||
stats.total_frames_allocated = total_allocated;
|
||||
stats.frames_in_use = total_in_use;
|
||||
|
||||
return stats;
|
||||
}
|
||||
|
||||
void FramePool::Cleanup() {
|
||||
std::lock_guard<std::mutex> lock(m_mutex);
|
||||
|
||||
size_t total_freed = 0;
|
||||
|
||||
for (auto& pair : m_pools) {
|
||||
FramePoolBucket* bucket = pair.second.get();
|
||||
size_t bucket_size = bucket->available_frames.size();
|
||||
|
||||
// 풀의 모든 프레임 해제
|
||||
while (!bucket->available_frames.empty()) {
|
||||
bucket->available_frames.pop();
|
||||
bucket->total_allocated--;
|
||||
total_freed++;
|
||||
}
|
||||
}
|
||||
|
||||
std::cout << "[FramePool] Cleanup completed - freed " << total_freed << " frames" << std::endl;
|
||||
}
|
||||
|
||||
void FramePool::PreallocatePool(uint32_t width, uint32_t height, ColorSpace format, size_t count) {
|
||||
uint64_t frame_key = MakeFrameKey(width, height, format);
|
||||
|
||||
std::lock_guard<std::mutex> lock(m_mutex);
|
||||
FramePoolBucket* bucket = GetOrCreateBucket(frame_key);
|
||||
|
||||
// 이미 할당된 수를 고려하여 필요한 만큼만 추가 할당
|
||||
size_t current_size = bucket->available_frames.size();
|
||||
if (current_size >= count) {
|
||||
return; // 이미 충분히 할당됨
|
||||
}
|
||||
|
||||
size_t needed = count - current_size;
|
||||
size_t max_available = MAX_POOL_SIZE_PER_FORMAT - current_size;
|
||||
size_t to_allocate = (needed < max_available) ? needed : max_available;
|
||||
|
||||
for (size_t i = 0; i < to_allocate; ++i) {
|
||||
auto frame = CreateNewFrame(width, height, format);
|
||||
if (frame) {
|
||||
bucket->available_frames.push(std::move(frame));
|
||||
bucket->total_allocated++;
|
||||
}
|
||||
}
|
||||
|
||||
std::cout << "[FramePool] Preallocated " << to_allocate << " frames for "
|
||||
<< width << "x" << height << " (format=" << static_cast<int>(format) << ")" << std::endl;
|
||||
}
|
||||
|
||||
uint64_t FramePool::MakeFrameKey(uint32_t width, uint32_t height, ColorSpace format) {
|
||||
return static_cast<uint64_t>(width) |
|
||||
(static_cast<uint64_t>(height) << 16) |
|
||||
(static_cast<uint64_t>(format) << 32);
|
||||
}
|
||||
|
||||
std::unique_ptr<VideoFrame> FramePool::CreateNewFrame(uint32_t width, uint32_t height, ColorSpace format) {
|
||||
auto frame = std::make_unique<VideoFrame>();
|
||||
|
||||
// 현재는 YUV420P만 지원
|
||||
if (format == ColorSpace::YUV420P) {
|
||||
if (!frame->AllocateYUV420P(width, height)) {
|
||||
std::cout << "[FramePool Error] Failed to allocate frame memory" << std::endl;
|
||||
return nullptr;
|
||||
}
|
||||
} else {
|
||||
std::cout << "[FramePool Error] Unsupported color space: " << static_cast<int>(format) << std::endl;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return frame;
|
||||
}
|
||||
|
||||
FramePool::FramePoolBucket* FramePool::GetOrCreateBucket(uint64_t frame_key) {
|
||||
auto it = m_pools.find(frame_key);
|
||||
if (it != m_pools.end()) {
|
||||
return it->second.get();
|
||||
}
|
||||
|
||||
// 새 버킷 생성
|
||||
auto bucket = std::make_unique<FramePoolBucket>();
|
||||
FramePoolBucket* bucket_ptr = bucket.get();
|
||||
m_pools[frame_key] = std::move(bucket);
|
||||
|
||||
return bucket_ptr;
|
||||
}
|
||||
|
||||
} // namespace Vav2Player
|
||||
168
vav2/Vav2Player/Vav2Player/src/Common/FramePool.h
Normal file
168
vav2/Vav2Player/Vav2Player/src/Common/FramePool.h
Normal file
@@ -0,0 +1,168 @@
|
||||
#pragma once
|
||||
#include "VideoTypes.h"
|
||||
#include <memory>
|
||||
#include <queue>
|
||||
#include <mutex>
|
||||
#include <unordered_map>
|
||||
#include <atomic>
|
||||
|
||||
namespace Vav2Player {
|
||||
|
||||
class FramePool {
|
||||
public:
|
||||
struct PooledFrame {
|
||||
std::unique_ptr<VideoFrame> frame;
|
||||
uint64_t frame_key; // width + (height << 16) + (format << 32)
|
||||
|
||||
PooledFrame() = default;
|
||||
PooledFrame(std::unique_ptr<VideoFrame> f, uint64_t key)
|
||||
: frame(std::move(f)), frame_key(key) {}
|
||||
|
||||
PooledFrame(PooledFrame&& other) noexcept
|
||||
: frame(std::move(other.frame)), frame_key(other.frame_key) {}
|
||||
|
||||
PooledFrame& operator=(PooledFrame&& other) noexcept {
|
||||
if (this != &other) {
|
||||
frame = std::move(other.frame);
|
||||
frame_key = other.frame_key;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
// 복사 방지
|
||||
PooledFrame(const PooledFrame&) = delete;
|
||||
PooledFrame& operator=(const PooledFrame&) = delete;
|
||||
};
|
||||
|
||||
private:
|
||||
struct FramePoolBucket {
|
||||
std::queue<std::unique_ptr<VideoFrame>> available_frames;
|
||||
std::atomic<size_t> total_allocated{0};
|
||||
std::atomic<size_t> in_use{0};
|
||||
|
||||
FramePoolBucket() = default;
|
||||
FramePoolBucket(const FramePoolBucket&) = delete;
|
||||
FramePoolBucket& operator=(const FramePoolBucket&) = delete;
|
||||
};
|
||||
|
||||
mutable std::mutex m_mutex;
|
||||
std::unordered_map<uint64_t, std::unique_ptr<FramePoolBucket>> m_pools;
|
||||
|
||||
// 풀 설정
|
||||
static constexpr size_t MAX_POOL_SIZE_PER_FORMAT = 10; // 포맷당 최대 10개 프레임
|
||||
static constexpr size_t INITIAL_POOL_SIZE = 3; // 초기 할당 크기
|
||||
|
||||
// 통계
|
||||
std::atomic<uint64_t> m_total_requests{0};
|
||||
std::atomic<uint64_t> m_cache_hits{0};
|
||||
std::atomic<uint64_t> m_cache_misses{0};
|
||||
|
||||
public:
|
||||
static FramePool& GetInstance();
|
||||
|
||||
~FramePool();
|
||||
|
||||
// 프레임 할당 - 풀에서 재사용 또는 새로 생성
|
||||
PooledFrame AcquireFrame(uint32_t width, uint32_t height, ColorSpace format);
|
||||
|
||||
// 프레임 반환 - 풀로 되돌리기
|
||||
void ReleaseFrame(PooledFrame&& pooled_frame);
|
||||
|
||||
// 풀 상태 조회
|
||||
struct PoolStats {
|
||||
uint64_t total_requests;
|
||||
uint64_t cache_hits;
|
||||
uint64_t cache_misses;
|
||||
double hit_rate;
|
||||
size_t active_pools;
|
||||
size_t total_frames_allocated;
|
||||
size_t frames_in_use;
|
||||
};
|
||||
|
||||
PoolStats GetStats() const;
|
||||
|
||||
// 풀 정리 - 사용하지 않는 프레임 해제
|
||||
void Cleanup();
|
||||
|
||||
// 특정 포맷의 풀 미리 할당
|
||||
void PreallocatePool(uint32_t width, uint32_t height, ColorSpace format, size_t count = INITIAL_POOL_SIZE);
|
||||
|
||||
private:
|
||||
FramePool() = default;
|
||||
|
||||
// 프레임 키 생성 (width, height, format 조합)
|
||||
static uint64_t MakeFrameKey(uint32_t width, uint32_t height, ColorSpace format);
|
||||
|
||||
// 새 프레임 생성
|
||||
std::unique_ptr<VideoFrame> CreateNewFrame(uint32_t width, uint32_t height, ColorSpace format);
|
||||
|
||||
// 특정 풀의 버킷 가져오기 (없으면 생성)
|
||||
FramePoolBucket* GetOrCreateBucket(uint64_t frame_key);
|
||||
};
|
||||
|
||||
// RAII 방식의 프레임 관리자
|
||||
class ScopedFrame {
|
||||
private:
|
||||
FramePool::PooledFrame m_pooled_frame;
|
||||
bool m_released = false;
|
||||
|
||||
public:
|
||||
explicit ScopedFrame(FramePool::PooledFrame&& pooled_frame)
|
||||
: m_pooled_frame(std::move(pooled_frame)) {}
|
||||
|
||||
~ScopedFrame() {
|
||||
if (!m_released && m_pooled_frame.frame) {
|
||||
FramePool::GetInstance().ReleaseFrame(std::move(m_pooled_frame));
|
||||
}
|
||||
}
|
||||
|
||||
// 이동 생성자
|
||||
ScopedFrame(ScopedFrame&& other) noexcept
|
||||
: m_pooled_frame(std::move(other.m_pooled_frame)), m_released(other.m_released) {
|
||||
other.m_released = true;
|
||||
}
|
||||
|
||||
// 이동 대입 연산자
|
||||
ScopedFrame& operator=(ScopedFrame&& other) noexcept {
|
||||
if (this != &other) {
|
||||
if (!m_released && m_pooled_frame.frame) {
|
||||
FramePool::GetInstance().ReleaseFrame(std::move(m_pooled_frame));
|
||||
}
|
||||
m_pooled_frame = std::move(other.m_pooled_frame);
|
||||
m_released = other.m_released;
|
||||
other.m_released = true;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
// 복사 방지
|
||||
ScopedFrame(const ScopedFrame&) = delete;
|
||||
ScopedFrame& operator=(const ScopedFrame&) = delete;
|
||||
|
||||
// 프레임 액세스
|
||||
VideoFrame* operator->() { return m_pooled_frame.frame.get(); }
|
||||
const VideoFrame* operator->() const { return m_pooled_frame.frame.get(); }
|
||||
VideoFrame& operator*() { return *m_pooled_frame.frame; }
|
||||
const VideoFrame& operator*() const { return *m_pooled_frame.frame; }
|
||||
|
||||
VideoFrame* get() { return m_pooled_frame.frame.get(); }
|
||||
const VideoFrame* get() const { return m_pooled_frame.frame.get(); }
|
||||
|
||||
// 수동으로 풀에 반환 (소멸자에서 자동 반환 방지)
|
||||
void Release() {
|
||||
if (!m_released && m_pooled_frame.frame) {
|
||||
FramePool::GetInstance().ReleaseFrame(std::move(m_pooled_frame));
|
||||
m_released = true;
|
||||
}
|
||||
}
|
||||
|
||||
// 프레임 소유권 이전 (풀 반환 없이)
|
||||
FramePool::PooledFrame ExtractFrame() {
|
||||
m_released = true;
|
||||
return std::move(m_pooled_frame);
|
||||
}
|
||||
|
||||
bool IsValid() const { return !m_released && m_pooled_frame.frame != nullptr; }
|
||||
};
|
||||
|
||||
} // namespace Vav2Player
|
||||
@@ -399,4 +399,93 @@ std::string GetDav1dCopyright() {
|
||||
|
||||
} // namespace AV1Utils
|
||||
|
||||
// 메모리 풀을 사용하는 최적화된 디코딩 메서드들
|
||||
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();
|
||||
|
||||
// dav1d 데이터 구조 준비
|
||||
Dav1dData data;
|
||||
uint8_t* buffer = dav1d_data_create(&data, packet_size);
|
||||
if (!buffer) {
|
||||
m_stats.decode_errors++;
|
||||
return ScopedFrame(FramePool::PooledFrame{});
|
||||
}
|
||||
|
||||
// 패킷 데이터 복사 (향후 zero-copy로 최적화 가능)
|
||||
memcpy(buffer, packet_data, packet_size);
|
||||
|
||||
// 디코더에 데이터 전송
|
||||
int result = dav1d_send_data(m_dav1d_context, &data);
|
||||
if (result < 0 && result != -EAGAIN) {
|
||||
m_stats.decode_errors++;
|
||||
return ScopedFrame(FramePool::PooledFrame{});
|
||||
}
|
||||
|
||||
// 디코딩된 프레임 가져오기
|
||||
Dav1dPicture picture;
|
||||
result = dav1d_get_picture(m_dav1d_context, &picture);
|
||||
if (result < 0) {
|
||||
if (result != -EAGAIN) {
|
||||
m_stats.decode_errors++;
|
||||
}
|
||||
return ScopedFrame(FramePool::PooledFrame{});
|
||||
}
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
// 메모리 풀에서 프레임 할당
|
||||
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{});
|
||||
}
|
||||
|
||||
// Dav1dPicture에서 VideoFrame으로 데이터 복사
|
||||
if (!ConvertDav1dPicture(picture, *pooled_frame.frame)) {
|
||||
dav1d_picture_unref(&picture);
|
||||
m_stats.decode_errors++;
|
||||
return ScopedFrame(FramePool::PooledFrame{});
|
||||
}
|
||||
|
||||
// 메타데이터 설정
|
||||
pooled_frame.frame->is_valid = true;
|
||||
pooled_frame.frame->timestamp_seconds = 0.0; // 호출자에서 설정 필요
|
||||
pooled_frame.frame->frame_index = 0; // 호출자에서 설정 필요
|
||||
|
||||
// 디코딩 시간 측정
|
||||
auto decode_end = std::chrono::high_resolution_clock::now();
|
||||
auto decode_duration = std::chrono::duration<double, std::milli>(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;
|
||||
|
||||
// dav1d 리소스 정리
|
||||
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));
|
||||
}
|
||||
|
||||
} // namespace Vav2Player
|
||||
@@ -1,5 +1,6 @@
|
||||
#pragma once
|
||||
#include "IVideoDecoder.h"
|
||||
#include "../Common/FramePool.h"
|
||||
#include <dav1d.h>
|
||||
#include <memory>
|
||||
#include <chrono>
|
||||
@@ -25,6 +26,10 @@ public:
|
||||
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;
|
||||
|
||||
// 메모리 풀을 사용하는 최적화된 디코딩 메서드
|
||||
ScopedFrame DecodeFramePooled(const VideoPacket& input_packet);
|
||||
ScopedFrame DecodeFramePooled(const uint8_t* packet_data, size_t packet_size);
|
||||
|
||||
bool Reset() override;
|
||||
bool Flush() override;
|
||||
|
||||
@@ -62,6 +67,7 @@ private:
|
||||
|
||||
// 성능 측정을 위한 멤버들
|
||||
std::chrono::high_resolution_clock::time_point m_decode_start_time;
|
||||
double m_total_decode_time_ms = 0.0;
|
||||
|
||||
// 내부 helper 메서드들
|
||||
bool InitializeDav1d();
|
||||
|
||||
Reference in New Issue
Block a user