Files
video-v1/vav2/docs/completed/architecture/Registration_Based_Factory_Design.md
2025-09-28 17:10:41 +09:00

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

핵심 문제들

  1. 개방-폐쇄 원칙 위반: 새 디코더 추가시 팩토리 클래스 수정 필요
  2. 헤더 의존성 충돌: 서로 다른 SDK 헤더들이 같은 심볼 정의로 충돌
  3. 컴파일 시간 증가: 모든 디코더 헤더가 팩토리에 포함됨
  4. 빌드 환경 의존성: 특정 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 구현

  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하게 대응할 수 있습니다.