Optimization with frame pool

This commit is contained in:
2025-09-20 00:45:20 +09:00
parent a17a0339f1
commit cbdfa86aac
12 changed files with 570 additions and 39 deletions

View File

@@ -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": []

View File

@@ -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;

View File

@@ -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 -->

View File

@@ -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 방지
}
}

View File

@@ -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();
};

View File

@@ -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" />

View File

@@ -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" />

View File

@@ -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>

View 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

View 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

View File

@@ -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

View File

@@ -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();