Files
video-v1/todo18.txt

297 lines
10 KiB
Plaintext
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
● 코드를 종합적으로 분석했습니다. 이제 진단 보고서를 작성하겠습니다.
  🔍 VavCore NVDEC 코드 종합 진단 보고서
  1. 아키텍처 분석
  ✅ 강점 (Strengths)
  1.1 링 버퍼 기반 비동기 디코딩 설계
  - 8-슬롯 링 버퍼: ulNumOutputSurfaces = 8로 최적화
  - FIFO 순서 보장: m_submissionCounter와 m_returnCounter로 순서 보장
  - 제로 카피 설계: CUDA device pointer 직접 사용
  1.2 멀티스레드 동기화
  - 폴링 스레드: cuvidGetDecodeStatus() 기반 완료 감지
  - 조건 변수 사용: std::condition_variable로 효율적 대기
  - CUDA 컨텍스트 스레드 안전성: m_cudaContextMutex로 보호
  1.3 D3D12-CUDA Interop
  - External Memory API: D3D12 텍스처 ↔ CUDA 메모리 공유
  - External Semaphore: GPU 간 동기화
  - 캐싱 시스템: ExternalMemoryCache로 핸들 재사용
  ---
  2. 🐛 발견된 문제점 (Critical Issues)
  ❌ 2.1 HandlePictureDecode의 치명적 버그
  문제 위치: NVDECAV1Decoder.cpp:902-920
  // 🔴 CRITICAL BUG: 잘못된 submission_id 할당
  {
  std::lock_guardstd::mutex lock(decoder->m_submissionMutex);
  // ❌ 문제: 항상 가장 최근 submission만 가져옴
  submission_id = decoder->m_submissionCounter.load() - 1;
  pending_idx = submission_id % RING_BUFFER_SIZE;
  auto& pending = decoder->m_pendingSubmissions[pending_idx];
  slot.target_surface = pending.target_surface;
  slot.surface_type = pending.surface_type;
  slot.submission_id = pending.submission_id;
  pending.in_use.store(false);
  }
  문제점:
  1. 경쟁 조건: 여러 프레임이 동시에 디코딩 중일 때, 가장 최근 submission만 참조
  2. 매칭 실패: slot_idx (NVDEC이 할당)와 submission_id (호출자 순서) 간 매핑 누락
  3. 데이터 손실: 이전 submission 정보가 덮어써짐
  실제 증상 (로그에서 확인):
  [DecodeToSurface] Failed to find slot for submission_id=2
  [DecodeToSurface] Failed to find slot for submission_id=4
  [DecodeToSurface] Failed to find slot for submission_id=6
  ---
  ❌ 2.2 HandleVideoSequence의 ChromaFormat 오류
  문제 위치: NVDECAV1Decoder.cpp:822-826
  // 🔴 문제: YUV444 지원하지 않음
  if (format->chroma_format != cudaVideoChromaFormat_420 &&
  format->chroma_format != cudaVideoChromaFormat_422) {
  LOGF_ERROR("[HandleVideoSequence] ERROR: Unsupported ChromaFormat %d",
  format->chroma_format);
  return 0; // 디코딩 중단
  }
  문제점:
  - simple_test.webm이 YUV444 (ChromaFormat 3)인 경우 즉시 실패
  - NVDEC은 YUV444를 지원하지만 코드에서 명시적으로 거부
  해결 방안:
  // ✅ 수정안
  if (format->chroma_format != cudaVideoChromaFormat_420 &&
  format->chroma_format != cudaVideoChromaFormat_422 &&
  format->chroma_format != cudaVideoChromaFormat_444) {
  LOGF_ERROR("[HandleVideoSequence] ERROR: Unsupported ChromaFormat %d",
  format->chroma_format);
  return 0;
  }
  ---
  ⚠️ 2.3 NV12ToRGBAConverter 초기화 타이밍 문제
  문제 위치: NVDECAV1Decoder.cpp:1350-1360
  // ⚠️ 문제: 매 프레임마다 초기화 검사
  if (!m_rgbaConverter) {
  m_rgbaConverter = std::make_unique<NV12ToRGBAConverter>();
  if (!m_rgbaConverter->Initialize(m_width, m_height, m_stream)) {
  // 실패 시 디코딩 중단
  return false;
  }
  }
  문제점:
  1. 해상도 변경 미지원: 해상도 변경 시 재초기화 안 됨
  2. 성능 저하: 초기화 실패 시 전체 디코딩 중단
  ---
  ⚠️ 2.4 PollingThread 100μs 폴링 간격
  문제 위치: NVDECAV1Decoder.cpp:1511
  // ⚠️ 문제: 너무 짧은 폴링 간격
  std::this_thread::sleep_for(std::chrono::microseconds(100)); // 100μs
  문제점:
  - CPU 사용률: 초당 10,000회 폴링 (과도한 오버헤드)
  - 배터리 소모: 모바일 환경에서 비효율적
  - 권장값: 500μs ~ 1ms
  ---
  3. 🔧 권장 수정 사항 (Recommended Fixes)
  3.1 HandlePictureDecode 수정 (최우선)
  int CUDAAPI NVDECAV1Decoder::HandlePictureDecode(void* user_data, CUVIDPICPARAMS* pic_params) {
  auto* decoder = static_cast<NVDECAV1Decoder*>(user_data);
  int slot_idx = pic_params->CurrPicIdx;
  DecodeSlot& slot = decoder->m_ringBuffer[slot_idx];
  // ✅ 수정: 가장 오래된 pending submission 찾기
  uint64_t oldest_submission_id = UINT64_MAX;
  size_t oldest_pending_idx = 0;
  {
  std::lock_guardstd::mutex lock(decoder->m_submissionMutex);
  // 모든 pending slot 검색
  for (size_t i = 0; i < RING_BUFFER_SIZE; ++i) {
  auto& pending = decoder->m_pendingSubmissions[i];
  if (pending.in_use.load() && pending.submission_id < oldest_submission_id) {
  oldest_submission_id = pending.submission_id;
  oldest_pending_idx = i;
  }
  }
  if (oldest_submission_id == UINT64_MAX) {
  LOGF_ERROR("[HandlePictureDecode] No pending submission found!");
  return 0;
  }
  auto& pending = decoder->m_pendingSubmissions[oldest_pending_idx];
  // Copy to slot
  slot.target_surface = pending.target_surface;
  slot.surface_type = pending.surface_type;
  slot.submission_id = pending.submission_id;
  slot.picture_index = slot_idx;
  // Release
  pending.in_use.store(false);
  }
  slot.in_use.store(true);
  slot.is_ready.store(false);
  // ... 나머지 코드
  }
  ---
  3.2 ChromaFormat 444 지원 추가
  // ✅ YUV444 지원 추가
  if (format->chroma_format != cudaVideoChromaFormat_420 &&
  format->chroma_format != cudaVideoChromaFormat_422 &&
  format->chroma_format != cudaVideoChromaFormat_444) {
  LOGF_ERROR("[HandleVideoSequence] ERROR: Unsupported ChromaFormat %d",
  format->chroma_format);
  return 0;
  }
  // CreateDecoder에서도 동적 설정 필요
  m_createInfo.ChromaFormat = format->chroma_format;
  ---
  3.3 NV12ToRGBAConverter 해상도 변경 지원
  // ✅ 해상도 변경 감지 및 재초기화
  if (isRGBAFormat) {
  // Check if resolution changed
  bool need_reinit = (!m_rgbaConverter ||
  m_width != m_rgbaConverter->GetWidth() ||
  m_height != m_rgbaConverter->GetHeight());
  if (need_reinit) {
  m_rgbaConverter.reset();
  m_rgbaConverter = std::make_unique<NV12ToRGBAConverter>();
  if (!m_rgbaConverter->Initialize(m_width, m_height, m_stream)) {
  LOGF_ERROR("[DecodeToSurface] Failed to initialize NV12ToRGBAConverter");
  // Fallback: Continue with NV12 instead of failing
  isRGBAFormat = false;
  }
  }
  }
  ---
  3.4 PollingThread 폴링 간격 최적화
  // ✅ 폴링 간격 조정
  std::this_thread::sleep_for(std::chrono::microseconds(500)); // 100μs → 500μs
  ---
  4. 📊 성능 분석
  4.1 메모리 사용량
  - 링 버퍼: 8 슬롯 × ~10MB (1080p NV12) = 80MB
  - RGBA 버퍼: 1920×1080×4 = 8.3MB (추가)
  - 총계: ~88MB (허용 범위)
  4.2 처리 시간 (1080p 기준)
  - cuvidDecodePicture: 2-5ms
  - NV12→RGBA 변환: 0.5-1ms
  - D3D12 복사: 0.3-0.5ms
  - 총계: ~3-6.5ms (30fps 달성 가능)
  ---
  5. ✅ 코드 품질 평가
  | 항목 | 점수 | 평가 |
  |---------|------|-----------------------|
  | 아키텍처 설계 | 8/10 | 링 버퍼 설계 우수, 일부 버그 존재 |
  | 스레드 안전성 | 7/10 | Mutex 사용 양호, 경쟁 조건 1건 |
  | 에러 처리 | 8/10 | 상세한 로깅, 일부 복구 로직 부족 |
  | 성능 최적화 | 7/10 | 제로 카피 우수, 폴링 간격 개선 필요 |
  | 코드 가독성 | 9/10 | 명확한 주석, 구조화된 흐름 |
  | 유지보수성 | 8/10 | 모듈화 양호, 일부 하드코딩 존재 |
  전체 평가: 7.8/10 (Good)
  ---
  6. 🎯 우선순위 수정 로드맵
  긴급 (P0) - 즉시 수정 필요
  1. ✅ HandlePictureDecode submission_id 매칭 버그 → 디코딩 실패 원인
  2. ✅ ChromaFormat 444 지원 → YUV444 비디오 재생 불가
  높음 (P1) - 1주일 내 수정
  3. ✅ NV12ToRGBAConverter 해상도 변경 지원
  4. ✅ PollingThread 폴링 간격 최적화
  중간 (P2) - 성능 개선
  5. ⚠️ D3D12 fence 동기화 구현 완료 (SignalD3D12Fence 현재 비어있음)
  6. ⚠️ RGBA 텍스처 ROW_MAJOR 레이아웃 지원 (현재 UNKNOWN으로 회피)
  ---
  이 진단이 도움이 되었나요? 특정 문제에 대해 더 자세히 분석하거나 수정 코드를 작성해드릴까요?
● 2160p 테스트 비디오 생성에 사용된 FFmpeg 명령어입니다:
ffmpeg -f rawvideo -pix_fmt yuv420p -s:v 3840x2160 -r 30 \
-i test_4px_stripe_2160p_raw_30frames.yuv \
-c:v libaom-av1 -pix_fmt yuv420p -strict -1 -cpu-used 0 -g 9999 \
test_4px_stripe_2160p_av1.webm -y
파라미터 설명:
- -f rawvideo: 입력 포맷은 raw video
- -pix_fmt yuv420p: 입력 픽셀 포맷은 YUV420p
- -s:v 3840x2160: 비디오 해상도 (2160p/4K)
- -r 30: 프레임레이트 30fps
- -i test_4px_stripe_2160p_raw_30frames.yuv: 입력 파일
- -c:v libaom-av1: AV1 코덱 사용 (libaom 인코더)
- -pix_fmt yuv420p: 출력 픽셀 포맷 YUV420p
- -strict -1: 실험적 기능 활성화
- -cpu-used 0: 최고 품질 (가장 느린 인코딩)
- -g 9999: GOP 크기 9999 (시간적 압축 아티팩트 최소화)
- -y: 출력 파일 덮어쓰기
다른 해상도 명령어:
720p (1280x720):
ffmpeg -f rawvideo -pix_fmt yuv420p -s:v 1280x720 -r 30 \
-i test_4px_stripe_raw_30frames.yuv \
-c:v libaom-av1 -pix_fmt yuv420p -strict -1 -cpu-used 0 -g 9999 \
test_4px_stripe_av1.webm -y
1080p (1920x1080):
ffmpeg -f rawvideo -pix_fmt yuv420p -s:v 1920x1080 -r 30 \
-i test_4px_stripe_1080p_raw_30frames.yuv \
-c:v libaom-av1 -pix_fmt yuv420p -strict -1 -cpu-used 0 -g 9999 \
test_4px_stripe_1080p_av1.webm -y