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

5.1 KiB
Raw Permalink Blame History

Исправление: Отправка видео на сервер

Проблема

Приложение показывало превью камеры локально, но видео вообще не отправлялось на сервер.

Анализ логов

Из logcat было видно:

  • ✓ WebSocket подключился успешно: Connected! Response code=101
  • ✓ Сервер ответил: Message received: {"error": "Invalid room or password"}
  • Но фреймы не отправлялись: в логах нет сообщений о sendBinary
  • Функция StreamViewModel.sendVideoFrame() вообще не вызывалась

Корень проблемы

1. CameraManager не обрабатывал фреймы

Файл CameraManager.kt только показывал превью, но не захватывал видео фреймы.

  • Использовался только Preview use case для отображения
  • Отсутствовал ImageAnalysis use case для захвата фреймов

2. Нет связи между камерой и ViewModel

MainActivity.kt запускал камеру, но не передавал фреймы в ViewModel:

// ДО: просто стартует камеру без callback
cameraManager.startCamera(lifecycleOwner, pv.surfaceProvider, onError)

// НУЖНО: передать фреймы в ViewModel
cameraManager.startCamera(lifecycleOwner, pv.surfaceProvider, onError,
    onFrame = { frameData -> viewModel.sendVideoFrame(frameData) }
)

3. Проблемы с отправкой бинарных данных

WebSocketManager.sendBinary() использовал рефлексию вместо стандартного API:

// ДО: сложная рефлексия
val byteStringClass = Class.forName("okhttp3.ByteString")
val ofMethod = byteStringClass.getMethod("of", ByteArray::class.java)
// ... и так далее

// ПОСЛЕ: простой и надёжный API
val byteString = ByteString.of(*data)
webSocket?.send(byteString)

Решение

1. Обновлен CameraManager

Добавлен ImageAnalysis для захвата видео фреймов:

val imageAnalysis = ImageAnalysis.Builder()
    .setBackpressureStrategy(ImageAnalysis.STRATEGY_KEEP_ONLY_LATEST)
    .setOutputImageFormat(ImageAnalysis.OUTPUT_IMAGE_FORMAT_RGBA_8888)
    .build()
    .apply {
        setAnalyzer(analysisExecutor) { imageProxy ->
            processFrame(imageProxy)
        }
    }

// Добавлена обработка фреймов
private fun processFrame(imageProxy: ImageProxy) {
    // Преобразование в ByteArray
    val frameData = ByteArray(buffer.remaining())
    buffer.get(frameData)
    
    // Отправка через callback
    onFrameAvailable?.invoke(frameData)
}

2. Обновлена MainActivity

Добавлен callback для передачи фреймов:

cameraManager.startCamera(
    lifecycleOwner,
    pv.surfaceProvider,
    onError = { err -> Log.e("CameraManager", "Camera error: $err") },
    onFrame = { frameData ->
        viewModel.sendVideoFrame(frameData)
    }
)

3. Исправлен WebSocketManager

Заменена рефлексия на стандартный API:

import okhttp3.ByteString

fun sendBinary(data: ByteArray) {
    try {
        val byteString = ByteString.of(*data)
        webSocket?.send(byteString)
        Log.d("WebSocket", "Binary data sent: ${data.size} bytes")
    } catch (e: Exception) {
        Log.e("WebSocket", "Binary send error: ${e.message}")
        onError(e.message ?: "Failed to send binary data")
    }
}

4. Улучшено логирование

Добавлены логи для отслеживания передачи:

  • CameraManager: "Processing X frames/5s, sending to server"
  • StreamViewModel: "FPS: X, Total bytes sent: Y"
  • WebSocket: "Binary data sent: X bytes"

Проверка

После исправления в логах должны появиться:

  1. При подключении:

    WebSocket: Connected! Response code=101
    CameraManager: Camera started successfully with video streaming
    StreamViewModel: Connected to server
    
  2. При потоке видео:

    CameraManager: Processing 25 frames/5s, sending to server
    WebSocket: Binary data sent: 12345 bytes
    StreamViewModel: FPS: 25, Total bytes sent: 308640
    

Файлы изменены

  • CameraManager.kt - Добавлен ImageAnalysis и обработка фреймов
  • MainActivity.kt - Добавлена передача фреймов в ViewModel
  • WebSocketManager.kt - Исправлена отправка бинарных данных
  • StreamViewModel.kt - Улучшено логирование

Результат

Теперь видео из камеры будет отправляться на сервер сразу после успешного подключения WebSocket.