# MediaCodec 프라이밍 시스템 및 안정성 개선 설계 **작성일**: 2025년 9월 30일 **상태**: 설계 완료 - 구현 준비 **카테고리**: Android MediaCodec 최적화, 하드웨어 가속 안정성 --- ## 🎯 **프로젝트 개요** Android MediaCodec AV1 디코더의 출력 버퍼 타이밍 문제를 해결하기 위한 종합적인 안정성 개선 시스템입니다. 하드웨어 디코더의 비동기 특성과 초기화 지연을 고려한 3단계 해결책을 제시합니다. ### **핵심 문제** - MediaCodec 하드웨어 디코더의 첫 프레임 출력 버퍼 지연 (`No output buffer ready`) - 비동기 입출력 버퍼 처리로 인한 타이밍 불일치 - 하드웨어 초기화 시간으로 인한 재생 시작 지연 - MediaCodec 실패 시 자동 복구 메커니즘 부재 ### **해결 목표** - **즉시 재생 시작**: 프라이밍을 통한 버퍼 준비 상태 확보 - **안정성 보장**: 하드웨어 실패 시 소프트웨어 폴백 - **성능 최적화**: 하드웨어 가속 우선, 필요 시 자동 전환 --- ## 🏗️ **1. 프라이밍 시스템 (Priming System)** ### **1.1 설계 원리** MediaCodec 하드웨어 디코더는 비동기적으로 작동하며, 첫 번째 출력 버퍼가 준비되기까지 여러 입력 프레임이 필요합니다. 프라이밍 시스템은 재생 시작 전에 파이프라인을 미리 채워서 즉시 출력이 가능한 상태로 만듭니다. ```cpp // AndroidMediaCodecAV1Decoder.h 추가 멤버 class AndroidMediaCodecAV1Decoder : public IVideoDecoder { private: // Priming system state bool m_is_primed = false; int m_priming_frame_count = 3; // Prime with 3 frames std::queue> m_primed_frames; // Priming methods bool PrimeDecoder(); bool IsPrimed() const { return m_is_primed; } void ResetPriming(); }; ``` ### **1.2 프라이밍 프로세스** ```cpp bool AndroidMediaCodecAV1Decoder::PrimeDecoder() { if (m_is_primed) { return true; // Already primed } LogInfo("Starting MediaCodec priming process..."); // Reset any existing state ResetPriming(); // Prime with initial frames for (int i = 0; i < m_priming_frame_count; i++) { // Get next packet from file reader (via callback or parameter) VideoPacket priming_packet; if (!GetNextPrimingPacket(priming_packet)) { LogWarning("Not enough packets for full priming"); break; } // Submit to MediaCodec input buffer if (!ProcessInputBuffer(priming_packet.data.get(), priming_packet.size)) { LogError("Failed to submit priming packet " + std::to_string(i)); continue; } // Try to get output buffer (non-blocking) auto primed_frame = std::make_unique(); if (ProcessOutputBuffer(*primed_frame)) { LogInfo("Primed frame " + std::to_string(i) + " ready"); m_primed_frames.push(std::move(primed_frame)); } // Small delay to allow hardware processing std::this_thread::sleep_for(std::chrono::milliseconds(10)); } bool success = !m_primed_frames.empty(); if (success) { LogInfo("MediaCodec priming completed with " + std::to_string(m_primed_frames.size()) + " frames"); m_is_primed = true; } else { LogWarning("MediaCodec priming failed - no frames ready"); } return success; } ``` ### **1.3 프라이밍된 프레임 사용** ```cpp bool AndroidMediaCodecAV1Decoder::DecodeFrame(const uint8_t* packet_data, size_t packet_size, VideoFrame& output_frame) { if (!m_initialized) { LogError("Decoder not initialized"); return false; } // Use primed frame if available if (!m_primed_frames.empty()) { LogInfo("Using primed frame"); output_frame = *m_primed_frames.front(); m_primed_frames.pop(); // Continue normal processing for next frames ProcessInputBuffer(packet_data, packet_size); return true; } // Normal decoding process if (!ProcessInputBuffer(packet_data, packet_size)) { LogError("Failed to process input buffer"); return false; } if (!ProcessOutputBuffer(output_frame)) { LogError("Failed to process output buffer"); return false; } return true; } ``` --- ## 🔄 **2. 폴백 메커니즘 (Fallback System)** ### **2.1 설계 원리** MediaCodec 하드웨어 디코더 실패 시 자동으로 dav1d 소프트웨어 디코더로 전환하여 재생 연속성을 보장합니다. ```cpp // AndroidMediaCodecAV1Decoder.h 폴백 관련 멤버 class AndroidMediaCodecAV1Decoder : public IVideoDecoder { private: // Fallback system std::unique_ptr m_fallback_decoder; // dav1d decoder bool m_use_fallback = false; int m_consecutive_failures = 0; static const int MAX_FAILURES_BEFORE_FALLBACK = 5; // Fallback methods bool InitializeFallback(); bool ShouldUseFallback() const; void TriggerFallback(); }; ``` ### **2.2 자동 폴백 트리거** ```cpp bool AndroidMediaCodecAV1Decoder::DecodeFrame(const uint8_t* packet_data, size_t packet_size, VideoFrame& output_frame) { // Check if we should use fallback if (m_use_fallback) { return m_fallback_decoder->DecodeFrame(packet_data, packet_size, output_frame); } // Try MediaCodec decoding bool success = false; // Use primed frame if available if (!m_primed_frames.empty()) { output_frame = *m_primed_frames.front(); m_primed_frames.pop(); ProcessInputBuffer(packet_data, packet_size); // Queue next frame success = true; } else { // Normal MediaCodec processing if (ProcessInputBuffer(packet_data, packet_size)) { success = ProcessOutputBuffer(output_frame); } } // Handle failure if (!success) { m_consecutive_failures++; LogWarning("MediaCodec decode failure " + std::to_string(m_consecutive_failures)); if (ShouldUseFallback()) { LogInfo("Triggering fallback to dav1d decoder"); TriggerFallback(); return m_fallback_decoder->DecodeFrame(packet_data, packet_size, output_frame); } return false; } // Reset failure counter on success m_consecutive_failures = 0; return true; } bool AndroidMediaCodecAV1Decoder::ShouldUseFallback() const { return m_consecutive_failures >= MAX_FAILURES_BEFORE_FALLBACK; } void AndroidMediaCodecAV1Decoder::TriggerFallback() { LogInfo("Switching to software decoder (dav1d) fallback"); if (!m_fallback_decoder) { InitializeFallback(); } if (m_fallback_decoder && m_fallback_decoder->Initialize()) { m_use_fallback = true; LogInfo("Fallback decoder initialized successfully"); } else { LogError("Failed to initialize fallback decoder"); } } ``` ### **2.3 폴백 초기화** ```cpp bool AndroidMediaCodecAV1Decoder::InitializeFallback() { LogInfo("Initializing dav1d fallback decoder"); m_fallback_decoder = std::make_unique(); // Configure dav1d with same settings AV1Settings fallback_settings; fallback_settings.threads = std::thread::hardware_concurrency(); fallback_settings.max_frame_delay = 1; // Low latency if (!m_fallback_decoder->SetAV1Settings(fallback_settings)) { LogError("Failed to configure fallback decoder settings"); return false; } LogInfo("Fallback decoder configured successfully"); return true; } ``` --- ## 🔄 **3. 상태 관리 개선 (Lifecycle Management)** ### **3.1 설계 원리** MediaCodec와 VavCore의 상태를 정확히 동기화하여 생명주기 불일치로 인한 문제를 방지합니다. ```cpp // AndroidMediaCodecAV1Decoder.h 상태 관리 멤버 class AndroidMediaCodecAV1Decoder : public IVideoDecoder { private: enum class DecoderState { UNINITIALIZED, INITIALIZING, CONFIGURED, PRIMING, READY, DECODING, FLUSHING, ERROR, FALLBACK_ACTIVE }; DecoderState m_current_state = DecoderState::UNINITIALIZED; std::mutex m_state_mutex; // State management methods bool TransitionState(DecoderState from, DecoderState to); void SetState(DecoderState new_state); DecoderState GetState() const; bool IsValidTransition(DecoderState from, DecoderState to) const; }; ``` ### **3.2 상태 전환 관리** ```cpp bool AndroidMediaCodecAV1Decoder::Initialize() { std::lock_guard lock(m_state_mutex); if (m_current_state != DecoderState::UNINITIALIZED) { LogError("Invalid state for initialization: " + StateToString(m_current_state)); return false; } SetState(DecoderState::INITIALIZING); // Hardware initialization if (DetectHardwareCapabilities() && InitializeMediaCodec()) { SetState(DecoderState::CONFIGURED); LogInfo("Hardware decoder initialized successfully"); // Start priming process SetState(DecoderState::PRIMING); if (PrimeDecoder()) { SetState(DecoderState::READY); m_initialized = true; return true; } else { LogWarning("Priming failed, decoder still usable"); SetState(DecoderState::READY); m_initialized = true; return true; } } // Hardware initialization failed SetState(DecoderState::ERROR); LogWarning("Hardware decoder initialization failed"); return false; } bool AndroidMediaCodecAV1Decoder::DecodeFrame(const uint8_t* packet_data, size_t packet_size, VideoFrame& output_frame) { std::lock_guard lock(m_state_mutex); // State validation if (m_current_state == DecoderState::FALLBACK_ACTIVE) { return m_fallback_decoder->DecodeFrame(packet_data, packet_size, output_frame); } if (m_current_state != DecoderState::READY && m_current_state != DecoderState::DECODING) { LogError("Invalid state for decoding: " + StateToString(m_current_state)); return false; } SetState(DecoderState::DECODING); bool success = DecodeFrameInternal(packet_data, packet_size, output_frame); if (success) { // Stay in DECODING state for continuous playback } else { // Handle failure if (ShouldUseFallback()) { TriggerFallback(); SetState(DecoderState::FALLBACK_ACTIVE); return m_fallback_decoder->DecodeFrame(packet_data, packet_size, output_frame); } else { SetState(DecoderState::ERROR); } } return success; } ``` ### **3.3 정리 및 리셋** ```cpp void AndroidMediaCodecAV1Decoder::Cleanup() { std::lock_guard lock(m_state_mutex); LogInfo("Cleaning up MediaCodec decoder, current state: " + StateToString(m_current_state)); // Flush any remaining frames if (m_current_state == DecoderState::DECODING) { SetState(DecoderState::FLUSHING); FlushDecoder(); } // Clean up primed frames ResetPriming(); // Clean up MediaCodec CleanupMediaCodec(); // Clean up fallback decoder if (m_fallback_decoder) { m_fallback_decoder->Cleanup(); m_fallback_decoder.reset(); } SetState(DecoderState::UNINITIALIZED); m_initialized = false; m_use_fallback = false; m_consecutive_failures = 0; LogInfo("MediaCodec decoder cleanup completed"); } void AndroidMediaCodecAV1Decoder::Reset() { std::lock_guard lock(m_state_mutex); LogInfo("Resetting MediaCodec decoder"); if (m_current_state == DecoderState::FALLBACK_ACTIVE) { if (m_fallback_decoder) { m_fallback_decoder->Reset(); } } else { // Reset MediaCodec state if (m_codec) { AMediaCodec_flush(m_codec); } } // Reset priming state ResetPriming(); m_consecutive_failures = 0; // Try to return to READY state if (m_initialized) { SetState(DecoderState::READY); } LogInfo("MediaCodec decoder reset completed"); } ``` --- ## 📊 **4. 통합 구현 가이드** ### **4.1 초기화 순서** ```cpp // 1. Hardware detection and initialization bool success = androidDecoder->Initialize(); // 2. Priming is automatically triggered during initialization // 3. Fallback decoder is prepared but not initialized // 4. Ready for decoding if (success) { LogInfo("Decoder ready with priming: " + std::to_string(androidDecoder->GetPrimedFrameCount())); } ``` ### **4.2 재생 시작** ```cpp // VavCoreVulkanBridge::Play() modification bool VavCoreVulkanBridge::Play() { // ... existing code ... // Start continuous playback with primed pipeline StartContinuousPlayback(); return true; } // PlaybackThreadMain에서 첫 프레임은 즉시 사용 가능 void VavCoreVulkanBridge::PlaybackThreadMain() { while (ShouldContinuePlayback()) { // ProcessNextFrame()은 이제 primed frame을 먼저 사용 bool success = ProcessNextFrame(); if (!success) { // Automatic fallback handling in decoder LogWarning("Frame processing failed, decoder handling fallback"); } // Timing control remains the same auto sleepTime = m_frameDurationUs - frameProcessTime; if (sleepTime.count() > 0) { std::this_thread::sleep_for(sleepTime); } } } ``` --- ## 🎯 **5. 예상 효과 및 성능 개선** ### **5.1 즉시 재생 시작** - **Before**: 첫 프레임까지 100-200ms 지연 - **After**: 프라이밍으로 즉시 재생 시작 (<10ms) ### **5.2 안정성 보장** - **Hardware failure recovery**: 자동 소프트웨어 폴백 - **Continuous playback**: 디코더 실패 시에도 재생 중단 없음 ### **5.3 사용자 경험** - **Smooth startup**: 버퍼링 없는 즉시 재생 - **Reliable playback**: 하드웨어 문제 시 자동 복구 - **Optimal performance**: 가능한 한 하드웨어 가속 유지 --- ## 🛠️ **6. 구현 단계** ### **Phase 1: 프라이밍 시스템** (1-2일) 1. PrimeDecoder() 메서드 구현 2. 프라이밍 상태 관리 추가 3. DecodeFrame() 수정하여 프라이밍 사용 ### **Phase 2: 폴백 메커니즘** (1일) 1. AV1Decoder 폴백 통합 2. 자동 전환 로직 구현 3. 실패 카운터 및 트리거 조건 ### **Phase 3: 상태 관리** (1일) 1. DecoderState enum 추가 2. 상태 전환 검증 로직 3. Thread-safe 상태 관리 ### **Phase 4: 테스트 및 최적화** (1일) 1. 통합 테스트 2. 성능 측정 및 튜닝 3. 로그 정리 및 문서화 --- ## 💡 **7. 추가 최적화 아이디어** ### **7.1 적응형 프라이밍** - 디바이스 성능에 따라 프라이밍 프레임 수 조정 - 네트워크 스트리밍 시 대역폭 고려 ### **7.2 지능형 폴백** - 특정 해상도/코덱에서만 하드웨어 사용 - 사용자 설정 기반 폴백 정책 ### **7.3 성능 모니터링** - 실시간 디코딩 성능 추적 - 자동 품질 조정 시스템 연동 --- **문서 완료일**: 2025년 9월 30일 **작성자**: Claude Code **상태**: ✅ **설계 완료** - 구현 준비 *이 설계를 바탕으로 단계별 구현을 진행하면 MediaCodec의 안정성과 성능을 크게 개선할 수 있습니다.* 🚀