Files
video-v1/vav2/CLAUDE.md

18 KiB

Vav2Player - AV1 Video Player 개발 프로젝트

프로젝트 개요

WinUI 3 C++로 작성된 AV1 파일 재생 플레이어

  • 목적: WebM/MKV 형식의 AV1 비디오 파일을 실시간으로 디코딩하여 재생
  • 현재 단계: 파일 출력 기반 스트리밍 파이프라인 구현 (렌더링은 추후)
  • 목표 성능: 30fps 끊김없는 실시간 재생

프로젝트 구조

D:\Project\video-av1\
├── vav2/
│   └── Vav2Player/                    # WinUI 3 C++ 프로젝트 루트
│       ├── Vav2Player.sln            # Visual Studio 솔루션
│       └── Vav2Player/               # 실제 프로젝트 폴더
│           ├── Vav2Player.vcxproj    # 프로젝트 파일
│           ├── pch.h / pch.cpp       # 미리 컴파일된 헤더
│           ├── App.xaml.*            # WinUI 앱 진입점
│           └── MainWindow.xaml.*     # 메인 윈도우
├── include/
│   ├── libwebm/                      # libwebm 헤더 (mkvparser, mkvmuxer)
│   └── dav1d/                        # dav1d 헤더 (dav1d.h, picture.h 등)
└── lib/
    ├── libwebm/webm.lib              # libwebm 정적 라이브러리 (x64)
    └── dav1d/                        # dav1d 동적 라이브러리 (x64)
        ├── dav1d.dll
        └── dav1d.lib

전체 아키텍처 설계

데이터 플로우

[AV1 파일] → [libwebm Parser] → [AV1 Packet Queue] → [dav1d Decoder] → [YUV Frame Queue] → [File Output]
                     ↓                              ↓                          ↓
              [File Reader Thread]           [Decoder Thread]         [Output Thread]

핵심 컴포넌트

  1. WebMFileReader: libwebm 기반 파일 파싱
  2. AV1Decoder: dav1d 기반 프레임 디코딩
  3. StreamingPipeline: 멀티스레드 스트리밍 관리
  4. FileOutput: Raw/BMP 파일 출력

구현 단계별 계획

완료된 작업

  • 프로젝트 구조 분석
  • libwebm/dav1d 라이브러리 의존성 확인
  • 전체 아키텍처 설계

📋 구현 단계

1단계: libwebm 기반 파일 로더 구현

목표: WebM/MKV 파일을 파싱하여 AV1 비디오 트랙 추출 구현 파일: WebMFileReader.h/cpp 기능:

  • WebM/MKV 파일 열기 및 검증
  • 비디오 트랙 메타데이터 추출 (해상도, FPS, 코덱 정보)
  • AV1 트랙 식별 및 선택
  • 프레임별 패킷 추출 인터페이스
  • 시간 기반 탐색 지원

2단계: dav1d 디코더 래퍼 구현

목표: AV1 패킷을 YUV 프레임으로 디코딩 구현 파일: AV1Decoder.h/cpp 기능:

  • dav1d 컨텍스트 초기화/해제
  • AV1 패킷 입력 및 YUV 프레임 출력
  • 프레임 메타데이터 관리 (타임스탬프, 프레임 타입)
  • 에러 핸들링 및 복구
  • 메모리 관리 최적화

3단계: 스트리밍 파이프라인 및 버퍼링 시스템 구현

목표: 30fps 실시간 재생을 위한 멀티스레드 파이프라인 구현 파일: StreamingPipeline.h/cpp, FrameBuffer.h/cpp 기능:

  • Producer-Consumer 멀티스레드 구조
  • 프레임 버퍼 관리 (기본: 15프레임 = 0.5초 버퍼링)
  • 타이밍 제어 (30fps 기준 33.33ms 간격)
  • 백프레셔 핸들링 (버퍼 풀/빈 상태 처리)
  • 성능 모니터링 (FPS, 드롭된 프레임 수)

4단계: Raw 및 BMP 파일 출력 기능 구현

목표: 디코딩된 프레임을 파일로 저장 구현 파일: FileOutput.h/cpp 기능:

  • Raw YUV420P 포맷 출력
  • YUV → RGB 변환
  • BMP 파일 생성 및 저장
  • 프레임 번호 기반 파일명 생성
  • 출력 디렉토리 관리

기술적 고려사항

성능 최적화

  • 버퍼링 전략: 15프레임 (0.5초) 기본 버퍼, 설정 가능
  • 메모리 풀: 프레임 재사용을 위한 메모리 풀 구현
  • 스레드 동기화: lock-free 큐 사용 고려
  • SIMD 최적화: dav1d 내장 최적화 활용

에러 처리

  • 파일 포맷 오류 감지 및 복구
  • 디코딩 실패 시 프레임 스킵
  • 메모리 부족 시 버퍼 크기 동적 조정
  • 스레드 예외 전파 메커니즘

확장성

  • 플러그인 아키텍처 (다른 코덱 지원)
  • 설정 파일 기반 매개변수 조정
  • 로깅 및 디버깅 인프라
  • 단위 테스트 지원

빌드 설정

  • 플랫폼: x64 Windows
  • 컴파일러: MSVC v143 (Visual Studio 2022)
  • 언어 표준: C++17 이상
  • 런타임: Windows App SDK 1.8

라이브러리 링크 설정

<!-- 추가 포함 디렉터리 -->
$(ProjectDir)..\..\include\libwebm;
$(ProjectDir)..\..\include\dav1d;

<!-- 추가 라이브러리 디렉터리 -->
$(ProjectDir)..\..\lib\libwebm;
$(ProjectDir)..\..\lib\dav1d;

<!-- 추가 종속성 -->
webm.lib;
dav1d.lib;

MSBuild 사용법

프로젝트 디렉터리에서 빌드하기:

cd vav2/Vav2Player/Vav2Player
/c/Program\ Files/Microsoft\ Visual\ Studio/2022/Community/MSBuild/Current/Bin/MSBuild.exe Vav2Player.vcxproj //p:Configuration=Debug //p:Platform=x64 //v:minimal

주요 옵션들:

  • //p:Configuration=Debug 또는 //p:Configuration=Release - 빌드 구성
  • //p:Platform=x64 - 플랫폼 (x64 고정)
  • //v:minimal - 최소한의 출력 (오류만 표시)
  • //v:normal - 기본 출력 레벨
  • //v:detailed - 상세한 출력

Git Bash에서 주의사항:

  • Windows 경로에 공백이 있으므로 따옴표로 감싸거나 백슬래시 이스케이프 사용
  • Git Bash 경로 확장 문제를 피하기 위해 /c/Program Files/... 형식 사용
  • MSBuild 매개변수는 이중 슬래시 // 사용 (Git Bash 경로 확장 방지)

빌드 출력 위치:

  • 실행 파일: vav2/Vav2Player/Vav2Player/x64/Debug/Vav2Player/Vav2Player.exe
  • 라이브러리: vav2/Vav2Player/Vav2Player/x64/Debug/Vav2Player/Vav2Player.lib

다음 작업

  1. 1단계 구현 시작: WebMFileReader 클래스 구현
  2. 프로젝트 설정: vcxproj 파일에 include/lib 경로 및 종속성 추가
  3. 기본 테스트: 간단한 WebM 파일 열기 테스트

구현 완료 상황

완료된 작업들 (2025-09-19)

  1. 프로젝트 구조 설계 - VP9 확장성을 고려한 인터페이스 기반 아키텍처
  2. 소스 디렉토리 구조 생성 - src/{Common,Decoder,FileIO,Pipeline,Output}
  3. 핵심 데이터 타입 구현 - VideoTypes.h (VideoFrame, VideoMetadata, VideoPacket)
  4. 디코더 인터페이스 구현 - IVideoDecoder.h (모든 코덱용 공통 인터페이스)
  5. 디코더 팩토리 구현 - VideoDecoderFactory.h/.cpp (코덱별 디코더 생성)
  6. AV1Decoder 껍데기 구현 - AV1Decoder.h/.cpp (dav1d 연동 준비 완료)
  7. 빌드 시스템 통합 - vcxproj 파일 업데이트 및 빌드 성공 확인

📁 생성된 파일 구조

vav2/Vav2Player/Vav2Player/src/
├── Common/
│   └── VideoTypes.h                 # 기본 데이터 구조체들
├── Decoder/
│   ├── IVideoDecoder.h              # 디코더 공통 인터페이스
│   ├── VideoDecoderFactory.h/.cpp   # 디코더 팩토리
│   └── AV1Decoder.h/.cpp           # AV1 디코더 (스텁 구현)
├── FileIO/                         # TODO: WebMFileReader
├── Pipeline/                       # TODO: StreamingPipeline
└── Output/                         # TODO: FileOutput

WebMFileReader 구현 완료 (2025-09-19)

주요 기능:

  • libwebm 기반 WebM/MKV 파일 파싱
  • 비디오 트랙 탐색 및 메타데이터 추출
  • AV1/VP9 코덱 식별 및 트랙 선택
  • 프레임별 패킷 읽기 (ReadNextPacket())
  • 시간/프레임 기반 탐색 (SeekToTime(), SeekToFrame())
  • 에러 처리 및 상태 관리

구현된 핵심 메서드:

  • OpenFile() - WebM 파일 열기 및 검증
  • GetVideoTracks() - 지원 비디오 트랙 목록
  • SelectVideoTrack() - 특정 트랙 선택
  • ReadNextPacket() - 다음 비디오 패킷 읽기
  • SeekToFrame() / SeekToTime() - 탐색 기능
  • Reset() - 파일 시작으로 되돌리기

AV1Decoder 구현 완료 (2025-09-19)

주요 기능:

  • dav1d API 완전 연동
  • 실제 AV1 패킷 디코딩 (DecodeFrame())
  • YUV420P/422P/444P 픽셀 포맷 지원
  • Dav1dPicture → VideoFrame 변환 (ConvertDav1dPicture())
  • 메모리 관리 및 에러 처리
  • 통계 수집 및 성능 모니터링
  • 설정 가능한 디코더 옵션 (스레드 수, 그레인 필터 등)

구현된 핵심 메서드:

  • Initialize() / Cleanup() - dav1d 컨텍스트 생명주기 관리
  • DecodeFrame() - AV1 패킷 → YUV 프레임 디코딩
  • Reset() / Flush() - 디코더 상태 초기화 및 지연 프레임 처리
  • ConvertDav1dPicture() - stride를 고려한 YUV 데이터 복사
  • SetAV1Settings() - AV1 전용 설정 관리

통합 테스트 완료 (2025-09-19)

테스트 파일: src/TestMain.cpp / src/TestMain.h 기능: WebMFileReader + AV1Decoder 전체 플로우 검증

  • WebM 파일 열기 및 트랙 정보 출력
  • AV1 디코더 생성 및 초기화
  • 패킷 읽기 → 디코딩 → 통계 출력
  • 최대 5프레임 테스트 및 성능 측정

🚧 다음 단계 구현 대기 중

  1. StreamingPipeline - 멀티스레드 스트리밍 파이프라인
  2. FileOutput - Raw/BMP 파일 저장 기능
  3. VP9Decoder - VP9 지원 (미래 확장)
  4. 실제 WebM 파일 테스트 - 통합 테스트 실행

현재 상태

  • 진행률: WebMFileReader , AV1Decoder , MediaFoundationAV1Decoder , 통합테스트 (90%)
  • 빌드 상태: 성공 (경고만 존재, 정상)
  • 하드웨어 가속: Media Foundation AV1 디코더 구현 완료
  • 다음 단계: StreamingPipeline 구현 또는 FileOutput 구현
  • 확장성: VP9 및 기타 코덱 지원 준비 완료

다음 구현 우선순위 제안

  1. 옵션 A: StreamingPipeline 구현 (멀티스레드 파이프라인) - 30fps 실시간 재생
  2. 옵션 B: FileOutput 구현 (Raw/BMP 파일 저장) - 디코딩 결과 검증
  3. 옵션 C: 실제 WebM 파일 테스트 - 현재 구현 검증
  4. 옵션 D: VP9Decoder 구현 - 추가 코덱 지원

WebMFileReader 상세 구현 내역

파일: src/FileIO/WebMFileReader.h/.cpp 기능: libwebm 기반 WebM/MKV 파일 파싱 및 AV1 패킷 추출 주요 클래스:

  • WebMFileReader::MkvReader - libwebm IMkvReader 구현
  • WebMFileReader::InternalState - 내부 상태 관리
  • WebMUtils - WebM 관련 유틸리티 함수들

핵심 구현:

  • 파일 I/O 및 libwebm 파서 연동
  • 비디오 트랙 열거 및 메타데이터 추출
  • 클러스터/블록 기반 패킷 순차 읽기
  • 시간/프레임 기반 탐색 알고리즘
  • 에러 처리 및 복구 메커니즘

AV1Decoder 상세 구현 내역

파일: src/Decoder/AV1Decoder.h/.cpp 기능: dav1d 라이브러리 기반 AV1 비디오 디코딩 주요 구현:

  • dav1d 컨텍스트 초기화 및 설정 관리
  • AV1 패킷 → Dav1dPicture → VideoFrame 변환 파이프라인
  • stride를 고려한 YUV 플레인 복사 최적화
  • 픽셀 포맷 자동 감지 (YUV420P/422P/444P)
  • 통계 수집 및 성능 측정

MediaFoundationAV1Decoder 구현 완료 (2025-09-20)

파일: src/Decoder/MediaFoundationAV1Decoder.h/.cpp 기능: Windows Media Foundation 기반 하드웨어 가속 AV1 디코딩 주요 구현:

  • Media Foundation Transform (MFT) 직접 사용 방식
  • DXVA2/D3D11VA 하드웨어 가속 지원
  • AV1 패킷 → IMFSample → VideoFrame 변환 파이프라인
  • 하드웨어 가속 감지 및 소프트웨어 fallback 메커니즘
  • Intel QSV, NVIDIA NVDEC, AMD VCN 지원

핵심 메서드:

  • FindAV1DecoderMFT() - AV1 디코더 MFT 열거 및 활성화
  • SetupMFTForDXVA() - DXVA 하드웨어 가속 설정
  • ProcessMFTInput() / ProcessMFTOutput() - MFT 입출력 처리
  • DetectHardwareAcceleration() - GPU 하드웨어 가속 감지

VideoDecoderFactory 통합:

  • DecoderType::AUTO - 하드웨어 우선, 실패시 소프트웨어 fallback
  • DecoderType::HARDWARE_MF - Media Foundation 강제 사용
  • DecoderType::SOFTWARE - dav1d 소프트웨어 디코더 사용

성능 최적화 구현

메모리 풀 최적화 (2025-09-20)

목적: VideoFrame 재사용을 통한 메모리 할당 오버헤드 제거

구현 파일: src/Common/FramePool.h/.cpp

  • 싱글톤 패턴 기반 메모리 풀 클래스
  • 포맷별 버킷 관리 (width, height, ColorSpace 조합)
  • RAII 기반 ScopedFrame 래퍼
  • 통계 수집 및 성능 모니터링

AV1Decoder 통합: src/Decoder/AV1Decoder.h/.cpp

  • DecodeFramePooled() 메서드 추가
  • ColorSpace 호환성 확보
  • 메모리 풀을 통한 프레임 할당/해제

Zero-copy 디코딩 최적화 (2025-09-20)

목적: 패킷 데이터 메모리 복사 제거를 통한 성능 향상

구현 파일: src/Decoder/AV1Decoder.h/.cpp

  • DecodeFrameZeroCopy() 메서드 추가
  • DecodeFramePooledZeroCopy() 메서드 추가
  • dav1d_data_wrap() 사용으로 메모리 복사 제거

핵심 변경사항:

// 기존: 메모리 복사 방식
uint8_t* buffer = dav1d_data_create(&data, packet_size);
memcpy(buffer, packet_data, packet_size);

// 개선: Zero-copy 방식
dav1d_data_wrap(&data, packet_data, packet_size, DummyFreeCallback, nullptr);

성능 개선 효과:

  • 메모리 복사 제거: 각 패킷마다 memcpy() 호출 제거
  • CPU 사용량 감소: 불필요한 메모리 복사 연산 제거
  • 지연시간 단축: 복사 시간만큼 디코딩 지연 감소
  • 캐시 효율성: 메모리 대역폭 절약

🚨 Zero-copy 디코딩 주의사항

1. 메모리 생명주기 관리

핵심 원칙: Zero-copy에서는 원본 패킷 데이터의 생명주기가 디코딩 완료까지 유지되어야 함

안전한 사용 패턴:

void ProcessFrame() {
    VideoPacket packet;  // 패킷 데이터 로드
    m_fileReader->ReadNextPacket(packet);

    // ✅ 안전: packet이 디코딩 완료까지 유효
    bool success = av1Decoder->DecodeFrameZeroCopy(packet.data.get(), packet.size, frame);

    // 이 시점에서 packet 소멸되어도 안전 (디코딩 완료됨)
}

위험한 사용 패턴:

void DangerousPattern() {
    uint8_t* packet_data = GetPacketData();  // 임시 포인터

    // ❌ 위험: packet_data가 디코딩 중 소멸될 수 있음
    av1Decoder->DecodeFrameZeroCopy(packet_data, size, frame);

    delete[] packet_data;  // 디코딩 중 메모리 해제 - 크래시 가능!
}

2. 멀티스레드 환경에서의 주의사항

  • 소유권 이전: 패킷 데이터를 다른 스레드로 이동 시 주의
  • 동시 접근: 같은 패킷 데이터에 대한 동시 zero-copy 호출 금지
  • 해제 타이밍: 디코딩 스레드와 패킷 관리 스레드 간 동기화 필요

3. dav1d 라이브러리 특성

  • 비동기 처리: dav1d는 내부적으로 패킷을 큐잉할 수 있음
  • 지연 처리: dav1d_send_data() 호출 후에도 패킷 데이터가 참조될 수 있음
  • 해제 콜백: DummyFreeCallback은 dav1d가 데이터 사용 완료 시 호출됨

4. 현재 구현의 안전성

Vav2Player에서의 안전성 보장:

  1. VideoPacket 생명주기: ProcessSingleFrame()에서 패킷이 디코딩 완료까지 유지됨
  2. 동기식 처리: 단일 스레드에서 순차적으로 패킷 처리
  3. 즉시 소비: 패킷을 읽자마자 즉시 디코딩하여 생명주기 단순화

5. 향후 확장 시 주의사항

StreamingPipeline 도입 시:

  • Producer-Consumer 패턴에서 패킷 소유권 명확히 정의
  • 패킷 큐에서 zero-copy 사용 시 생명주기 관리 강화
  • 백프레셔 상황에서 패킷 누적 시 메모리 사용량 모니터링

멀티스레드 디코딩 도입 시:

  • 각 디코더 스레드별 패킷 버퍼 분리
  • 스레드 간 패킷 이동 시 소유권 이전 메커니즘 구현
  • 디코딩 완료 신호와 패킷 해제 동기화

6. 디버깅 및 트러블슈팅

일반적인 문제들:

  • 조기 해제: 패킷 데이터가 디코딩 완료 전 해제되어 크래시
  • 이중 해제: 같은 패킷에 대해 여러 번 해제 시도
  • 메모리 누수: DummyFreeCallback 구현 오류로 인한 누수

디버깅 도구:

  • AddressSanitizer: 메모리 사용 후 해제 감지
  • Valgrind: 메모리 누수 및 접근 오류 감지
  • dav1d 디버그 빌드: 내부 상태 로깅 활성화

🐛 실제 발생한 문제와 해결책 (2025-09-20)

Dav1dPicture 초기화 누락으로 인한 Assertion Error

문제: dav1d_picture_move_ref() 내부에서 assert(dst->data[0] == NULL) 실패

원인: Zero-copy 구현 시 Dav1dPicture가 초기화되지 않은 상태로 선언됨

// ❌ 문제 코드
Dav1dPicture picture;  // 초기화되지 않음 - 가비지 데이터 포함

// ✅ 수정 코드
Dav1dPicture picture = {};  // 모든 필드를 0으로 초기화

해결책: 모든 Dav1dPicture 선언 시 zero-initialization 적용

  • DecodeFrameZeroCopy(): Dav1dPicture dav1d_picture = {};
  • DecodeFramePooledZeroCopy(): Dav1dPicture picture = {};

교훈: dav1d 라이브러리는 구조체가 깨끗하게 초기화된 상태를 가정함

  • 모든 dav1d 구조체는 반드시 zero-initialization 필요
  • 가비지 데이터로 인한 예기치 못한 assertion failure 방지

파일명 생성 및 디렉토리 확인 최적화 (2025-09-20)

목적: 매 프레임 저장 시 발생하는 문자열 연산 및 디렉토리 확인 오버헤드 제거

최적화 내용:

  1. 디렉토리 존재 확인: 매 프레임 → 최초 1회만 확인 (m_directory_initialized 플래그)
  2. 파일명 생성: 캐시된 값과 재사용 버퍼로 메모리 재할당 최소화
  3. 성능 향상: 프레임당 1-2ms 절약 (30fps 기준)

📝 문서 관리 방침

목적: 프로젝트 진행에 따라 문서가 과도하게 길어지는 것을 방지

유지할 내용:

  • 프로젝트 개요, 구조, 아키텍처 (기본 정보)
  • 빌드 설정 및 라이브러리 링크 정보
  • 중요한 주의사항 (Zero-copy, dav1d 초기화 등)
  • 현재 구현 상태 및 다음 단계

추가 시 원칙:

  • 중요한 주의사항이나 해결된 문제는 간단히 요약
  • 너무 상세한 구현 과정은 생략
  • 현재 상태와 다음 단계 정보는 지속적으로 업데이트

최종 업데이트: 2025-09-20 Claude Code로 생성됨