Files
cam_control_android/PROBLEM_ANALYSIS.md
2025-12-09 21:21:26 +09:00

173 lines
7.7 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

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.

# 🔬 АНАЛИЗ ПРОБЛЕМЫ И РЕШЕНИЕ
## Проблема (как она выглядела в логах)
### На сервере (server logs):
```
[VideoProcessor Process] ⚠️ ece8bb89-458f-4cb6-9dca-af679716cc10: NO FRAMES YET (waiting for 5.0s)
[VideoProcessor Process] ⚠️ ece8bb89-458f-4cb6-9dca-af679716cc10: NO FRAMES YET (waiting for 10.0s)
[VideoProcessor Process] ⚠️ ece8bb89-458f-4cb6-9dca-af679716cc10: NO FRAMES YET (waiting for 15.0s)
...
[VideoProcessor Process] ⚠️ ece8bb89-458f-4cb6-9dca-af679716cc10: NO FRAMES YET (waiting for 30.0s)
```
**Означает:** Сервер подключился, но видеокадры не приходят!
### На приложении (logcat):
```
2025-12-09 21:12:52.249 9634-9998 ImageReader_JNI com.example.camcontrol W Unable to acquire a buffer item
2025-12-09 21:12:52.285 9634-9998 ImageAnalysisAnalyzer com.example.camcontrol E Failed to acquire image.
java.lang.IllegalStateException: maxImages (4) has already been acquired,
call #close before acquiring more.
at android.media.ImageReader.acquireNextImage(ImageReader.java:662)
at android.media.ImageReader.acquireLatestImage(ImageReader.java:542)
at androidx.camera.core.AndroidImageReaderProxy.acquireLatestImage(AndroidImageReaderProxy.java:63)
at androidx.camera.core.SafeCloseImageReaderProxy.acquireLatestImage(SafeCloseImageReaderProxy.java:80)
at androidx.camera.core.ImageProcessingUtil.convertYUVToRGB(ImageProcessingUtil.java:224)
at androidx.camera.core.ImageAnalysisAbstractAnalyzer.analyzeImage(ImageAnalysisAbstractAnalyzer.java:218)
```
**Означает:** ImageAnalysis не может получить буфер для обработки кадров!
## Диагноз
### Что происходило в коде:
```kotlin
// ДО ИСПРАВЛЕНИЯ (неправильно):
cameraProvider?.bindToLifecycle(
lifecycleOwner,
cameraSelector,
preview, // Preview отправляет видео ✅
imageCapture, // ImageCapture готов к снимкам ✅
imageAnalysis // ImageAnalysis ПЕРЕПОЛНЯЕТ буфер ❌
)
```
### Как это работало в runtime:
```
Камера → YUV видеокадры (30 fps)
┌─────────────────────────────────────────────┐
│ CameraX связывает 3 use case одновременно: │
├─────────────────────────────────────────────┤
│ 1. Preview: отправляет в PreviewView │ ← БЫСТРО ✅
│ 2. ImageCapture: готов захватывать │ ← ОК
│ 3. ImageAnalysis: конвертирует YUV→RGBA │ ← МЕДЛЕННО ❌
└─────────────────────────────────────────────┘
ImageReader буфер (максимум 4 изображения одновременно)
❌ ПЕРЕПОЛНЕНИЕ:
- ImageAnalysis не успевает обрабатывать
- Буфер заполнен (4 изображения уже заняты)
- Новые кадры не могут войти
- Preview тоже блокируется
- Видео не отправляется на сервер
```
### Почему ImageAnalysis медленная:
1. **YUV → RGBA конвертация** - сложная операция
- YUV это плоский формат (оптимизированный)
- RGBA требует интерполяции цветов
- На каждый кадр нужны вычисления
2. **В фоновом потоке** - ThreadPoolExecutor
- Конкуренция за ресурсы процессора
- GC (сборка мусора) может прерывать
- При 30 fps нужно обработать 30 кадров в секунду
3. **ImageReader имеет лимит** - максимум 4 одновременно
- Это DESIGN из Android API
- Защита от утечек памяти
- Но ImageAnalysis может потребить все 4!
## Решение
### ПосДА ИСПРАВЛЕНИЯ (правильно):
```kotlin
cameraProvider?.bindToLifecycle(
lifecycleOwner,
cameraSelector,
preview, // Preview отправляет видео ✅
imageCapture // ImageCapture готов к снимкам ✅
// imageAnalysis УДАЛЕНА! 🎉
)
```
### Как работает правильно:
```
Камера → YUV видеокадры (30 fps)
┌──────────────────────────────────┐
│ CameraX связывает 2 use case: │
├──────────────────────────────────┤
│ 1. Preview: отправляет │ ← БЫСТРО ✅
│ 2. ImageCapture: готов │ ← ОК
└──────────────────────────────────┘
✅ ВИДЕОПОТОК СВОБОДЕН:
- Preview может обрабатывать максимум скорость 30 fps
- Буфер ImageReader не переполняется
- Кадры идут напрямую на сервер
- Никакого замедления
- Видео работает в реальном времени!
```
## Почему Preview достаточно
**Preview** - это оптимизированное использование видеопотока:
```kotlin
val preview = Preview.Builder().build()
preview.setSurfaceProvider(previewSurfaceProvider)
```
- **Работает напрямую с GPU** (SurfaceView/TextureView)
- **Не требует конвертации** (остаётся в YUV)
- **Максимальная производительность** - буквально передача данных
- **Автоматически отправляет** на сервер через фоновый процесс
## ImageCapture нужна для:
```kotlin
imageCapture.takePicture(...) // Захватить ОДИН кадр
```
- Снимки (сохранить на диск)
- Видеоконференции (захват скриншота)
- Не нужна для потокового видео
## Итоги
| Аспект | ImageAnalysis | Решение |
|--------|---------------|---------|
| **Производительность** | ❌ Медленная (YUV→RGBA) | ✅ Быстрая (GPU обработка) |
| **CPU использование** | ❌ Высокое | ✅ Низкое |
| **Буфер переполнение** | ❌ Частое | ✅ Никогда |
| **Видеотрансляция** | ❌ Блокируется | ✅ Работает идеально |
| **Сложность кода** | ❌ Лишняя | ✅ Минимальная |
---
## Файлы которые изменились
1. **CameraManager.kt**
- Удалена: `imageAnalysis` use case
- Удалена: `analysisExecutor`
- Изменена: `bindToLifecycle()` (теперь только Preview + ImageCapture)
2. **Результат**
- ✅ Видео поток работает
- ✅ Нет ошибок ImageAnalyzer
- ✅ Сервер получает кадры
- ✅ Нет "NO FRAMES YET" на сервере
---
**Выучено:** Иногда меньше = лучше. Удаление ненужной обработки может быть более эффективным, чем оптимизация существующей!