540 lines
16 KiB
Markdown
540 lines
16 KiB
Markdown
|
|
# 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의 안정성과 성능을 크게 개선할 수 있습니다.* 🚀
|