421 lines
15 KiB
Markdown
421 lines
15 KiB
Markdown
|
|
# 등록 기반 팩토리 패턴 (Registration-Based Factory Pattern) 설계
|
||
|
|
|
||
|
|
## 📋 **현재 문제점 분석**
|
||
|
|
|
||
|
|
### **기존 VideoDecoderFactory의 문제**
|
||
|
|
```cpp
|
||
|
|
// 현재 VideoDecoderFactory.h - 모든 디코더 헤더를 직접 포함
|
||
|
|
#include "AV1Decoder.h"
|
||
|
|
#include "MediaFoundationAV1Decoder.h"
|
||
|
|
#include "NVDECAV1Decoder.h" // CUDA/NVDEC 헤더 충돌 가능
|
||
|
|
#include "AMFAV1Decoder.h" // AMF 헤더 충돌 가능
|
||
|
|
#include "VPLAV1Decoder.h" // VPL 헤더 충돌 가능
|
||
|
|
|
||
|
|
class VideoDecoderFactory {
|
||
|
|
// 새로운 디코더 추가시마다 코드 수정 필요
|
||
|
|
case DecoderType::NVDEC: return std::make_unique<NVDECAV1Decoder>();
|
||
|
|
case DecoderType::AMF: return std::make_unique<AMFAV1Decoder>();
|
||
|
|
case DecoderType::VPL: return std::make_unique<VPLAV1Decoder>();
|
||
|
|
};
|
||
|
|
```
|
||
|
|
|
||
|
|
### **핵심 문제들**
|
||
|
|
1. **개방-폐쇄 원칙 위반**: 새 디코더 추가시 팩토리 클래스 수정 필요
|
||
|
|
2. **헤더 의존성 충돌**: 서로 다른 SDK 헤더들이 같은 심볼 정의로 충돌
|
||
|
|
3. **컴파일 시간 증가**: 모든 디코더 헤더가 팩토리에 포함됨
|
||
|
|
4. **빌드 환경 의존성**: 특정 SDK가 없으면 전체 빌드 실패
|
||
|
|
|
||
|
|
## 🎯 **등록 기반 팩토리 패턴 솔루션**
|
||
|
|
|
||
|
|
### **핵심 아이디어**
|
||
|
|
- **등록자(Registrar)**: 각 디코더가 자신을 팩토리에 등록
|
||
|
|
- **팩토리(Factory)**: 등록된 생성자 함수들을 통해 객체 생성
|
||
|
|
- **자동 등록**: 디코더 구현 파일에서 자동으로 등록 수행
|
||
|
|
- **헤더 분리**: 팩토리는 구체 디코더 헤더를 포함하지 않음
|
||
|
|
|
||
|
|
## 🏗️ **단순화된 아키텍처 설계**
|
||
|
|
|
||
|
|
### **핵심 원칙**
|
||
|
|
- **복잡한 DecoderRegistry 제거** → VideoDecoderFactory 내부 배열 사용
|
||
|
|
- **템플릿 클래스 제거** → 간단한 함수 포인터 등록
|
||
|
|
- **매크로 최소화** → 가독성 있는 등록 함수 사용
|
||
|
|
|
||
|
|
### **1. 단순화된 등록 시스템**
|
||
|
|
```cpp
|
||
|
|
// VideoDecoderFactory.h - 구체 디코더 헤더 포함 없음!
|
||
|
|
#include "IVideoDecoder.h"
|
||
|
|
#include <functional>
|
||
|
|
#include <vector>
|
||
|
|
#include <string>
|
||
|
|
|
||
|
|
namespace VavCore {
|
||
|
|
|
||
|
|
// 디코더 등록 정보 구조체
|
||
|
|
struct DecoderRegistration {
|
||
|
|
std::string name;
|
||
|
|
std::string description;
|
||
|
|
int priority; // 0=최고 우선순위
|
||
|
|
std::function<bool()> isAvailable; // 가용성 체크
|
||
|
|
std::function<std::unique_ptr<IVideoDecoder>()> creator; // 생성 함수
|
||
|
|
};
|
||
|
|
|
||
|
|
enum class DecoderType {
|
||
|
|
AUTO, // 가장 적합한 디코더 자동 선택 (우선순위 기반)
|
||
|
|
NVDEC, // NVIDIA NVDEC 하드웨어 디코더 강제 사용
|
||
|
|
VPL, // Intel VPL 하드웨어 디코더 강제 사용
|
||
|
|
AMF, // AMD AMF 하드웨어 디코더 강제 사용
|
||
|
|
DAV1D, // dav1d 소프트웨어 디코더 강제 사용
|
||
|
|
MEDIA_FOUNDATION // Media Foundation 디코더 강제 사용
|
||
|
|
};
|
||
|
|
|
||
|
|
class VideoDecoderFactory {
|
||
|
|
public:
|
||
|
|
// 디코더 생성
|
||
|
|
static std::unique_ptr<IVideoDecoder> CreateDecoder(VideoCodecType codec_type, DecoderType type = DecoderType::AUTO);
|
||
|
|
static std::unique_ptr<IVideoDecoder> CreateDecoder(const std::string& decoder_name);
|
||
|
|
|
||
|
|
// 사용 가능한 디코더 목록
|
||
|
|
static std::vector<std::string> GetAvailableDecoders(VideoCodecType codec_type);
|
||
|
|
|
||
|
|
// 디코더 등록 (각 디코더 cpp에서 호출)
|
||
|
|
static void RegisterAV1Decoder(const DecoderRegistration& registration);
|
||
|
|
static void RegisterVP9Decoder(const DecoderRegistration& registration);
|
||
|
|
|
||
|
|
// 팩토리 초기화
|
||
|
|
static void InitializeFactory();
|
||
|
|
|
||
|
|
private:
|
||
|
|
// 코덱별 등록된 디코더 배열
|
||
|
|
static std::vector<DecoderRegistration> s_av1_decoders;
|
||
|
|
static std::vector<DecoderRegistration> s_vp9_decoders;
|
||
|
|
|
||
|
|
// 헬퍼 함수
|
||
|
|
static std::vector<DecoderRegistration>& GetDecoderList(VideoCodecType codec_type);
|
||
|
|
static bool IsHardwareDecoder(const std::string& decoder_name);
|
||
|
|
static bool IsSoftwareDecoder(const std::string& decoder_name);
|
||
|
|
};
|
||
|
|
|
||
|
|
} // namespace VavCore
|
||
|
|
```
|
||
|
|
|
||
|
|
### **2. VideoDecoderFactory 구현**
|
||
|
|
```cpp
|
||
|
|
// VideoDecoderFactory.cpp - 헤더 포함 없음!
|
||
|
|
#include "pch.h"
|
||
|
|
#include "VideoDecoderFactory.h"
|
||
|
|
#include <algorithm>
|
||
|
|
#include <iostream>
|
||
|
|
|
||
|
|
namespace VavCore {
|
||
|
|
|
||
|
|
// 정적 멤버 초기화
|
||
|
|
std::vector<DecoderRegistration> VideoDecoderFactory::s_av1_decoders;
|
||
|
|
std::vector<DecoderRegistration> VideoDecoderFactory::s_vp9_decoders;
|
||
|
|
|
||
|
|
std::unique_ptr<IVideoDecoder> VideoDecoderFactory::CreateDecoder(VideoCodecType codec_type, DecoderType type) {
|
||
|
|
auto& decoders = GetDecoderList(codec_type);
|
||
|
|
|
||
|
|
// 가용성 필터링
|
||
|
|
std::vector<DecoderRegistration> available;
|
||
|
|
for (const auto& decoder : decoders) {
|
||
|
|
if (decoder.isAvailable()) {
|
||
|
|
available.push_back(decoder);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
if (available.empty()) {
|
||
|
|
return nullptr;
|
||
|
|
}
|
||
|
|
|
||
|
|
// 우선순위 정렬
|
||
|
|
std::sort(available.begin(), available.end(), [](const auto& a, const auto& b) {
|
||
|
|
return a.priority < b.priority;
|
||
|
|
});
|
||
|
|
|
||
|
|
switch (type) {
|
||
|
|
case DecoderType::AUTO:
|
||
|
|
return available[0].creator();
|
||
|
|
|
||
|
|
case DecoderType::NVDEC:
|
||
|
|
for (const auto& decoder : available) {
|
||
|
|
if (decoder.name == "nvdec") {
|
||
|
|
return decoder.creator();
|
||
|
|
}
|
||
|
|
}
|
||
|
|
break;
|
||
|
|
|
||
|
|
case DecoderType::VPL:
|
||
|
|
for (const auto& decoder : available) {
|
||
|
|
if (decoder.name == "vpl") {
|
||
|
|
return decoder.creator();
|
||
|
|
}
|
||
|
|
}
|
||
|
|
break;
|
||
|
|
|
||
|
|
case DecoderType::AMF:
|
||
|
|
for (const auto& decoder : available) {
|
||
|
|
if (decoder.name == "amf") {
|
||
|
|
return decoder.creator();
|
||
|
|
}
|
||
|
|
}
|
||
|
|
break;
|
||
|
|
|
||
|
|
case DecoderType::DAV1D:
|
||
|
|
for (const auto& decoder : available) {
|
||
|
|
if (decoder.name == "dav1d") {
|
||
|
|
return decoder.creator();
|
||
|
|
}
|
||
|
|
}
|
||
|
|
break;
|
||
|
|
|
||
|
|
case DecoderType::MEDIA_FOUNDATION:
|
||
|
|
for (const auto& decoder : available) {
|
||
|
|
if (decoder.name == "media_foundation") {
|
||
|
|
return decoder.creator();
|
||
|
|
}
|
||
|
|
}
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
|
||
|
|
return nullptr;
|
||
|
|
}
|
||
|
|
|
||
|
|
void VideoDecoderFactory::RegisterAV1Decoder(const DecoderRegistration& registration) {
|
||
|
|
s_av1_decoders.push_back(registration);
|
||
|
|
|
||
|
|
// 우선순위 순으로 정렬
|
||
|
|
std::sort(s_av1_decoders.begin(), s_av1_decoders.end(), [](const auto& a, const auto& b) {
|
||
|
|
return a.priority < b.priority;
|
||
|
|
});
|
||
|
|
|
||
|
|
std::cout << "[VideoDecoderFactory] Registered AV1 decoder: " << registration.name
|
||
|
|
<< " (priority: " << registration.priority << ")" << std::endl;
|
||
|
|
}
|
||
|
|
|
||
|
|
} // namespace VavCore
|
||
|
|
```
|
||
|
|
|
||
|
|
### **3. 단순화된 디코더별 자동 등록**
|
||
|
|
|
||
|
|
#### **AV1Decoder.cpp (dav1d 소프트웨어 디코더)**
|
||
|
|
```cpp
|
||
|
|
#include "pch.h"
|
||
|
|
#include "AV1Decoder.h"
|
||
|
|
#include "VideoDecoderFactory.h"
|
||
|
|
|
||
|
|
// AV1Decoder 구현...
|
||
|
|
|
||
|
|
namespace VavCore {
|
||
|
|
|
||
|
|
// 자동 등록 함수
|
||
|
|
void RegisterAV1Decoders() {
|
||
|
|
VideoDecoderFactory::RegisterAV1Decoder({
|
||
|
|
"dav1d", // 이름
|
||
|
|
"Software AV1 decoder using dav1d library", // 설명
|
||
|
|
50, // 우선순위 (중간)
|
||
|
|
[]() { return true; }, // 가용성 체크 (항상 사용 가능)
|
||
|
|
[]() { return std::make_unique<AV1Decoder>(); } // 생성 함수
|
||
|
|
});
|
||
|
|
}
|
||
|
|
|
||
|
|
// 정적 초기화를 통한 자동 등록
|
||
|
|
static bool s_av1_registered = (RegisterAV1Decoders(), true);
|
||
|
|
|
||
|
|
} // namespace VavCore
|
||
|
|
```
|
||
|
|
|
||
|
|
#### **NVDECAV1Decoder.cpp (NVDEC 하드웨어 디코더)**
|
||
|
|
```cpp
|
||
|
|
#include "pch.h"
|
||
|
|
#include "NVDECAV1Decoder.h"
|
||
|
|
#include "VideoDecoderFactory.h"
|
||
|
|
|
||
|
|
// NVDECAV1Decoder 구현...
|
||
|
|
|
||
|
|
namespace VavCore {
|
||
|
|
|
||
|
|
// 자동 등록 함수
|
||
|
|
void RegisterNVDECDecoders() {
|
||
|
|
VideoDecoderFactory::RegisterAV1Decoder({
|
||
|
|
"nvdec", // 이름
|
||
|
|
"Hardware AV1 decoder using NVIDIA NVDEC", // 설명
|
||
|
|
10, // 우선순위 (높음)
|
||
|
|
[]() { // 가용성 체크
|
||
|
|
return NVDECAV1Decoder::CheckNVDECAvailability();
|
||
|
|
},
|
||
|
|
[]() { return std::make_unique<NVDECAV1Decoder>(); } // 생성 함수
|
||
|
|
});
|
||
|
|
}
|
||
|
|
|
||
|
|
// 정적 초기화를 통한 자동 등록
|
||
|
|
static bool s_nvdec_registered = (RegisterNVDECDecoders(), true);
|
||
|
|
|
||
|
|
} // namespace VavCore
|
||
|
|
```
|
||
|
|
|
||
|
|
#### **VPLAV1Decoder.cpp (Intel VPL 하드웨어 디코더)**
|
||
|
|
```cpp
|
||
|
|
#include "pch.h"
|
||
|
|
#include "VPLAV1Decoder.h"
|
||
|
|
#include "VideoDecoderFactory.h"
|
||
|
|
|
||
|
|
// VPLAV1Decoder 구현...
|
||
|
|
|
||
|
|
namespace VavCore {
|
||
|
|
|
||
|
|
// 자동 등록 함수
|
||
|
|
void RegisterVPLDecoders() {
|
||
|
|
VideoDecoderFactory::RegisterAV1Decoder({
|
||
|
|
"vpl", // 이름
|
||
|
|
"Hardware AV1 decoder using Intel VPL", // 설명
|
||
|
|
20, // 우선순위 (높음)
|
||
|
|
[]() { // 가용성 체크
|
||
|
|
return VPLAV1Decoder::CheckVPLSystemAvailability();
|
||
|
|
},
|
||
|
|
[]() { return std::make_unique<VPLAV1Decoder>(); } // 생성 함수
|
||
|
|
});
|
||
|
|
}
|
||
|
|
|
||
|
|
// 정적 초기화를 통한 자동 등록
|
||
|
|
static bool s_vpl_registered = (RegisterVPLDecoders(), true);
|
||
|
|
|
||
|
|
} // namespace VavCore
|
||
|
|
```
|
||
|
|
|
||
|
|
#### **AMFAV1Decoder.cpp (AMD AMF 하드웨어 디코더)**
|
||
|
|
```cpp
|
||
|
|
#include "pch.h"
|
||
|
|
#include "AMFAV1Decoder.h"
|
||
|
|
#include "VideoDecoderFactory.h"
|
||
|
|
|
||
|
|
// AMFAV1Decoder 구현...
|
||
|
|
|
||
|
|
namespace VavCore {
|
||
|
|
|
||
|
|
// 자동 등록 함수
|
||
|
|
void RegisterAMFDecoders() {
|
||
|
|
VideoDecoderFactory::RegisterAV1Decoder({
|
||
|
|
"amf", // 이름
|
||
|
|
"Hardware AV1 decoder using AMD AMF", // 설명
|
||
|
|
15, // 우선순위 (높음)
|
||
|
|
[]() { // 가용성 체크
|
||
|
|
return AMFAV1Decoder::IsAMFAvailable();
|
||
|
|
},
|
||
|
|
[]() { return std::make_unique<AMFAV1Decoder>(); } // 생성 함수
|
||
|
|
});
|
||
|
|
}
|
||
|
|
|
||
|
|
// 정적 초기화를 통한 자동 등록
|
||
|
|
static bool s_amf_registered = (RegisterAMFDecoders(), true);
|
||
|
|
|
||
|
|
} // namespace VavCore
|
||
|
|
```
|
||
|
|
|
||
|
|
## 🔄 **단순화된 사용 예시**
|
||
|
|
|
||
|
|
### **기본 사용법**
|
||
|
|
```cpp
|
||
|
|
// 1. 자동 선택 (가장 적합한 디코더, 우선순위 기반)
|
||
|
|
auto decoder = VideoDecoderFactory::CreateDecoder(VideoCodecType::AV1, DecoderType::AUTO);
|
||
|
|
|
||
|
|
// 2. NVIDIA NVDEC 하드웨어 디코더 강제 사용
|
||
|
|
auto nvdecDecoder = VideoDecoderFactory::CreateDecoder(VideoCodecType::AV1, DecoderType::NVDEC);
|
||
|
|
|
||
|
|
// 3. Intel VPL 하드웨어 디코더 강제 사용
|
||
|
|
auto vplDecoder = VideoDecoderFactory::CreateDecoder(VideoCodecType::AV1, DecoderType::VPL);
|
||
|
|
|
||
|
|
// 4. AMD AMF 하드웨어 디코더 강제 사용
|
||
|
|
auto amfDecoder = VideoDecoderFactory::CreateDecoder(VideoCodecType::AV1, DecoderType::AMF);
|
||
|
|
|
||
|
|
// 5. dav1d 소프트웨어 디코더 강제 사용
|
||
|
|
auto dav1dDecoder = VideoDecoderFactory::CreateDecoder(VideoCodecType::AV1, DecoderType::DAV1D);
|
||
|
|
|
||
|
|
// 6. Media Foundation 디코더 강제 사용
|
||
|
|
auto mfDecoder = VideoDecoderFactory::CreateDecoder(VideoCodecType::AV1, DecoderType::MEDIA_FOUNDATION);
|
||
|
|
|
||
|
|
// 7. 사용 가능한 디코더 목록 조회
|
||
|
|
auto available = VideoDecoderFactory::GetAvailableDecoders(VideoCodecType::AV1);
|
||
|
|
for (const auto& name : available) {
|
||
|
|
std::cout << "Available AV1 decoder: " << name << std::endl;
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
### **WebM 파일 처리 예시**
|
||
|
|
```cpp
|
||
|
|
// WebM 코덱 ID에서 자동 디코더 생성
|
||
|
|
std::string codecId = "V_AV01"; // WebM의 AV1 코덱 ID
|
||
|
|
VideoCodecType codecType = VideoDecoderFactory::DetectCodecTypeFromId(codecId);
|
||
|
|
auto decoder = VideoDecoderFactory::CreateDecoder(codecType, DecoderType::AUTO);
|
||
|
|
|
||
|
|
if (decoder) {
|
||
|
|
std::cout << "Created decoder for: " << codecId << std::endl;
|
||
|
|
} else {
|
||
|
|
std::cout << "No suitable decoder found for: " << codecId << std::endl;
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
## 🚀 **단순화된 설계의 장점**
|
||
|
|
|
||
|
|
### **1. 개방-폐쇄 원칙 준수**
|
||
|
|
- ✅ **개방**: 새로운 디코더 추가 용이 (각자의 cpp 파일에서 등록)
|
||
|
|
- ✅ **폐쇄**: VideoDecoderFactory 헤더/구현 수정 불필요
|
||
|
|
|
||
|
|
### **2. 헤더 의존성 완전 분리**
|
||
|
|
- ✅ **충돌 제거**: VideoDecoderFactory가 구체 디코더 헤더를 포함하지 않음
|
||
|
|
- ✅ **빌드 시간 단축**: 불필요한 헤더 컴파일 제거
|
||
|
|
- ✅ **선택적 빌드**: 특정 SDK 없어도 나머지 디코더는 정상 빌드
|
||
|
|
|
||
|
|
### **3. 구현 단순성**
|
||
|
|
- ✅ **복잡한 DecoderRegistry 제거**: 간단한 std::vector 사용
|
||
|
|
- ✅ **템플릿/매크로 최소화**: 가독성 높은 함수 기반 등록
|
||
|
|
- ✅ **디버깅 용이**: 복잡한 싱글톤/템플릿 없이 직관적인 구조
|
||
|
|
|
||
|
|
### **4. 성능 최적화**
|
||
|
|
- ✅ **우선순위 기반 선택**: 시스템 환경에 따른 최적 디코더 자동 선택
|
||
|
|
- ✅ **가용성 기반 필터링**: 사용 불가능한 디코더 자동 제외
|
||
|
|
- ✅ **정적 초기화 활용**: 런타임 오버헤드 최소화
|
||
|
|
|
||
|
|
### **5. 유지보수성 향상**
|
||
|
|
- ✅ **관심사 분리**: 각 디코더가 자신의 등록 로직만 관리
|
||
|
|
- ✅ **코드 가독성**: 복잡한 등록 시스템 없이 명확한 구조
|
||
|
|
- ✅ **확장성**: 새로운 코덱 타입 추가 시에도 동일 패턴 적용 가능
|
||
|
|
|
||
|
|
## 📋 **단순화된 구현 계획**
|
||
|
|
|
||
|
|
### **Phase 1: 단순화된 VideoDecoderFactory 구현**
|
||
|
|
1. 기존 복잡한 VideoDecoderFactory.h/.cpp 교체
|
||
|
|
2. `DecoderRegistration` 구조체 기반 등록 시스템 구현
|
||
|
|
3. 정적 std::vector를 사용한 디코더 저장소 구현
|
||
|
|
|
||
|
|
### **Phase 2: 디코더별 등록 변환**
|
||
|
|
1. `AV1Decoder.cpp`에 단순화된 등록 로직 추가
|
||
|
|
2. `NVDECAV1Decoder.cpp`에 등록 로직 추가
|
||
|
|
3. `VPLAV1Decoder.cpp`에 등록 로직 추가
|
||
|
|
4. `AMFAV1Decoder.cpp`에 등록 로직 추가 (AMF SDK 사용 가능 시)
|
||
|
|
|
||
|
|
### **Phase 3: 빌드 및 테스트**
|
||
|
|
1. VavCore 라이브러리 빌드 테스트
|
||
|
|
2. 헤더 의존성 제거 확인
|
||
|
|
3. 디코더 자동 등록 및 우선순위 동작 검증
|
||
|
|
|
||
|
|
### **Phase 4: 기존 코드와 통합**
|
||
|
|
1. WebMFileReader에서 새로운 팩토리 사용 확인
|
||
|
|
2. VideoPlayerControl에서 디코더 생성 로직 업데이트
|
||
|
|
3. 전체 애플리케이션 통합 테스트
|
||
|
|
|
||
|
|
## 🎯 **단순화된 설계의 결론**
|
||
|
|
|
||
|
|
**함수 포인터 기반 등록 패턴**을 통해:
|
||
|
|
- ✅ **PIMPL 패턴의 복잡성 없이** 헤더 의존성 문제 완전 해결
|
||
|
|
- ✅ **개방-폐쇄 원칙 준수**로 확장 가능한 아키텍처 구현
|
||
|
|
- ✅ **복잡한 싱글톤/템플릿 제거**로 코드 가독성 향상
|
||
|
|
- ✅ **정적 초기화 활용**으로 성능 최적화
|
||
|
|
- ✅ **직관적인 등록 시스템**으로 디버깅 및 유지보수 용이
|
||
|
|
|
||
|
|
**기존 복잡한 등록 시스템 대비 장점:**
|
||
|
|
- DecoderRegistry 싱글톤 제거 → 간단한 정적 배열 사용
|
||
|
|
- DecoderRegistrar 템플릿 제거 → 명확한 함수 기반 등록
|
||
|
|
- 복잡한 매크로 제거 → 가독성 높은 구조체 초기화 방식
|
||
|
|
- 범용적인 문자열 기반 API 제거 → 타입 안전한 enum 기반 접근법
|
||
|
|
|
||
|
|
이 단순화된 설계는 Vav2Player의 디코더 생태계를 더욱 **유연하고 유지보수하기 쉽게** 만들어, 미래의 새로운 디코더 추가나 SDK 변경에도 robust하게 대응할 수 있습니다.
|