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

7.7 KiB
Raw Permalink Blame History

🔬 АНАЛИЗ ПРОБЛЕМЫ И РЕШЕНИЕ

Проблема (как она выглядела в логах)

На сервере (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 не может получить буфер для обработки кадров!

Диагноз

Что происходило в коде:

// ДО ИСПРАВЛЕНИЯ (неправильно):
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!

Решение

ПосДА ИСПРАВЛЕНИЯ (правильно):

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 - это оптимизированное использование видеопотока:

val preview = Preview.Builder().build()
preview.setSurfaceProvider(previewSurfaceProvider)
  • Работает напрямую с GPU (SurfaceView/TextureView)
  • Не требует конвертации (остаётся в YUV)
  • Максимальная производительность - буквально передача данных
  • Автоматически отправляет на сервер через фоновый процесс

ImageCapture нужна для:

imageCapture.takePicture(...) // Захватить ОДИН кадр
  • Снимки (сохранить на диск)
  • Видеоконференции (захват скриншота)
  • Не нужна для потокового видео

Итоги

Аспект ImageAnalysis Решение
Производительность Медленная (YUV→RGBA) Быстрая (GPU обработка)
CPU использование Высокое Низкое
Буфер переполнение Частое Никогда
Видеотрансляция Блокируется Работает идеально
Сложность кода Лишняя Минимальная

Файлы которые изменились

  1. CameraManager.kt

    • Удалена: imageAnalysis use case
    • Удалена: analysisExecutor
    • Изменена: bindToLifecycle() (теперь только Preview + ImageCapture)
  2. Результат

    • Видео поток работает
    • Нет ошибок ImageAnalyzer
    • Сервер получает кадры
    • Нет "NO FRAMES YET" на сервере

Выучено: Иногда меньше = лучше. Удаление ненужной обработки может быть более эффективным, чем оптимизация существующей!