173 lines
7.7 KiB
Markdown
173 lines
7.7 KiB
Markdown
# 🔬 АНАЛИЗ ПРОБЛЕМЫ И РЕШЕНИЕ
|
||
|
||
## Проблема (как она выглядела в логах)
|
||
|
||
### На сервере (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" на сервере
|
||
|
||
---
|
||
|
||
**Выучено:** Иногда меньше = лучше. Удаление ненужной обработки может быть более эффективным, чем оптимизация существующей!
|
||
|