15 KiB
15 KiB
등록 기반 팩토리 패턴 (Registration-Based Factory Pattern) 설계
📋 현재 문제점 분석
기존 VideoDecoderFactory의 문제
// 현재 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>();
};
핵심 문제들
- 개방-폐쇄 원칙 위반: 새 디코더 추가시 팩토리 클래스 수정 필요
- 헤더 의존성 충돌: 서로 다른 SDK 헤더들이 같은 심볼 정의로 충돌
- 컴파일 시간 증가: 모든 디코더 헤더가 팩토리에 포함됨
- 빌드 환경 의존성: 특정 SDK가 없으면 전체 빌드 실패
🎯 등록 기반 팩토리 패턴 솔루션
핵심 아이디어
- 등록자(Registrar): 각 디코더가 자신을 팩토리에 등록
- 팩토리(Factory): 등록된 생성자 함수들을 통해 객체 생성
- 자동 등록: 디코더 구현 파일에서 자동으로 등록 수행
- 헤더 분리: 팩토리는 구체 디코더 헤더를 포함하지 않음
🏗️ 단순화된 아키텍처 설계
핵심 원칙
- 복잡한 DecoderRegistry 제거 → VideoDecoderFactory 내부 배열 사용
- 템플릿 클래스 제거 → 간단한 함수 포인터 등록
- 매크로 최소화 → 가독성 있는 등록 함수 사용
1. 단순화된 등록 시스템
// 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 구현
// 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 소프트웨어 디코더)
#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 하드웨어 디코더)
#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 하드웨어 디코더)
#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 하드웨어 디코더)
#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
🔄 단순화된 사용 예시
기본 사용법
// 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 파일 처리 예시
// 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 구현
- 기존 복잡한 VideoDecoderFactory.h/.cpp 교체
DecoderRegistration구조체 기반 등록 시스템 구현- 정적 std::vector를 사용한 디코더 저장소 구현
Phase 2: 디코더별 등록 변환
AV1Decoder.cpp에 단순화된 등록 로직 추가NVDECAV1Decoder.cpp에 등록 로직 추가VPLAV1Decoder.cpp에 등록 로직 추가AMFAV1Decoder.cpp에 등록 로직 추가 (AMF SDK 사용 가능 시)
Phase 3: 빌드 및 테스트
- VavCore 라이브러리 빌드 테스트
- 헤더 의존성 제거 확인
- 디코더 자동 등록 및 우선순위 동작 검증
Phase 4: 기존 코드와 통합
- WebMFileReader에서 새로운 팩토리 사용 확인
- VideoPlayerControl에서 디코더 생성 로직 업데이트
- 전체 애플리케이션 통합 테스트
🎯 단순화된 설계의 결론
함수 포인터 기반 등록 패턴을 통해:
- ✅ PIMPL 패턴의 복잡성 없이 헤더 의존성 문제 완전 해결
- ✅ 개방-폐쇄 원칙 준수로 확장 가능한 아키텍처 구현
- ✅ 복잡한 싱글톤/템플릿 제거로 코드 가독성 향상
- ✅ 정적 초기화 활용으로 성능 최적화
- ✅ 직관적인 등록 시스템으로 디버깅 및 유지보수 용이
기존 복잡한 등록 시스템 대비 장점:
- DecoderRegistry 싱글톤 제거 → 간단한 정적 배열 사용
- DecoderRegistrar 템플릿 제거 → 명확한 함수 기반 등록
- 복잡한 매크로 제거 → 가독성 높은 구조체 초기화 방식
- 범용적인 문자열 기반 API 제거 → 타입 안전한 enum 기반 접근법
이 단순화된 설계는 Vav2Player의 디코더 생태계를 더욱 유연하고 유지보수하기 쉽게 만들어, 미래의 새로운 디코더 추가나 SDK 변경에도 robust하게 대응할 수 있습니다.