Files
video-v1/vav2/docs/completed/android/MediaCodec_Priming_System_Design.md

540 lines
16 KiB
Markdown
Raw Normal View History

2025-09-30 02:32:41 +09:00
# 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<std::unique_ptr<VideoFrame>> 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<VideoFrame>();
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<AV1Decoder> 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<AV1Decoder>();
// 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<std::mutex> 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<std::mutex> 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<std::mutex> 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<std::mutex> 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의 안정성과 성능을 크게 개선할 수 있습니다.* 🚀