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

326 lines
12 KiB
Markdown
Raw 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.

# Отладка: Приложение отправляет видео на сервер
## Проблема
**Симптомы:**
- ✓ Приложение подключается к серверу по WebSocket
-На экране видна картинка с камеры (превью работает)
-На сервере не получается видео
-В logcat нет сообщений об отправке видеофреймов
**Из logcat (2025-12-03 20:27:19):**
```
WebSocket: Connected! Response code=101
WebSocket: Header: Upgrade=websocket
WebSocket: Message received: {"error": "Invalid room or password"}
StreamViewModel: Status: Подключено к серверу ✓
StreamViewModel: Status: Получено: {"error": "Invalid room or password"}
```
**Но видеофреймы вообще не отправлялись!**
---
## Анализ
### Кто виноват?
Проблема была **в архитектуре приложения**, а не в WebSocket подключении.
#### 1. **CameraManager** - не захватывал фреймы
```kotlin
// ❌ ДО: только превью, никакой обработки фреймов
val preview = Preview.Builder().build()
imageCapture = ImageCapture.Builder().build()
// ✅ ПОСЛЕ: добавлен 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) // Обработка каждого фрейма
}
}
```
#### 2. **MainActivity** - не передавал фреймы в ViewModel
```kotlin
// ❌ ДО: просто запускает камеру
cameraManager.startCamera(lifecycleOwner, pv.surfaceProvider, onError)
// ✅ ПОСЛЕ: передаёт фреймы в ViewModel
cameraManager.startCamera(
lifecycleOwner,
pv.surfaceProvider,
onError = { err -> Log.e("CameraManager", "Camera error: $err") },
onFrame = { frameData ->
viewModel.sendVideoFrame(frameData) // ← КЛЮЧЕВАЯ СТРОКА!
}
)
```
#### 3. **WebSocketManager** - использовал сложную рефлексию для бинарных данных
```kotlin
// ❌ ДО: сложная и хрупкая рефлексия
val byteStringClass = Class.forName("okhttp3.ByteString")
val ofMethod = byteStringClass.getMethod("of", ByteArray::class.java)
val byteString = ofMethod.invoke(null, data)
val sendMethod = WebSocket::class.java.getMethod("send", byteStringClass)
sendMethod.invoke(webSocket, byteString)
// ✅ ПОСЛЕ: простой и надёжный API
import okio.ByteString.Companion.toByteString
val byteString = data.toByteString()
webSocket?.send(byteString)
```
---
## Решение
### Файл 1: CameraManager.kt
**Что изменилось:**
1. Добавлены импорты для `ImageAnalysis` и `ImageProxy`
2. Добавлены новые поля:
- `analysisExecutor` - выполняет анализ фреймов в отдельном потоке
- `onFrameAvailable` - callback для отправки фреймов
3. Изменена сигнатура `startCamera()`:
```kotlin
fun startCamera(
lifecycleOwner: LifecycleOwner,
previewSurfaceProvider: Preview.SurfaceProvider,
onError: (String) -> Unit,
onFrame: ((ByteArray) -> Unit)? = null // ← НОВЫЙ ПАРАМЕТР
)
```
4. Добавлен `ImageAnalysis` при привязке к камере
5. Новый метод `processFrame()`:
- Извлекает пиксельные данные из каждого фрейма
- Отправляет через callback `onFrameAvailable`
- Логирует количество фреймов в секунду
### Файл 2: MainActivity.kt
**Что изменилось:**
```kotlin
// Добавлена передача callback функции
cameraManager.startCamera(
lifecycleOwner,
pv.surfaceProvider,
onError = { err -> Log.e("CameraManager", "Camera error: $err") },
onFrame = { frameData -> // ← НОВЫЙ CALLBACK
viewModel.sendVideoFrame(frameData)
}
)
```
**Как это работает:**
1. Камера захватывает фрейм
2. `CameraManager` преобразует его в ByteArray
3. Вызывает callback `onFrame` с данными фрейма
4. ViewModel получает фрейм и отправляет на сервер через WebSocket
### Файл 3: WebSocketManager.kt
**Что изменилось:**
**Импорты:**
```kotlin
// ❌ ДО
import okhttp3.ByteString
// ✅ ПОСЛЕ
import okio.ByteString.Companion.toByteString
```
**Метод sendBinary():**
```kotlin
// ✅ НОВАЯ РЕАЛИЗАЦИЯ
fun sendBinary(data: ByteArray) {
try {
val byteString = data.toByteString()
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: StreamViewModel.kt
**Улучшено логирование:**
```kotlin
fun sendVideoFrame(frameData: ByteArray) {
try {
wsManager?.sendBinary(frameData)
// ... обновление статистики ...
if (currentTime - lastFpsTime >= 1000) {
Log.d("StreamViewModel", "FPS: $frameCount, Total bytes sent: $totalBytesTransferred")
_fps.value = frameCount
frameCount = 0
lastFpsTime = currentTime
}
} catch (e: Exception) {
Log.e("StreamViewModel", "Failed to send frame: ${e.message}", e)
}
}
```
### Файл 5: AndroidManifest.xml
**Удалены ненужные разрешения:**
```xml
<!-- ❌ УДАЛЕНЫ -->
<uses-permission android:name="android.permission.SEND_SMS" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<!-- ✅ ОСТАЛИСЬ -->
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
```
---
## Проверка в logcat
**После исправлений ожидаются следующие логи:**
### При подключении:
```
WebSocket: Connecting to: ws://192.168.0.112:8000/ws/client/HhfoHArOGcT/1
WebSocket: Connected! Response code=101
CameraManager: Camera started successfully with video streaming
StreamViewModel: Connected to server
```
### При потоке видео:
```
CameraManager: Processing 25 frames/5s, sending to server
WebSocket: Binary data sent: 12345 bytes
StreamViewModel: FPS: 25, Total bytes sent: 308640
WebSocket: Binary data sent: 12340 bytes
StreamViewModel: FPS: 25, Total bytes sent: 617280
```
---
## Результат
✅ **Проект успешно скомпилирован:**
```
BUILD SUCCESSFUL in 6s
97 actionable tasks: 15 executed, 82 up-to-date
```
✅ **Теперь приложение:**
1. Подключается к серверу
2. Захватывает видеофреймы с камеры
3. Отправляет их на сервер через WebSocket
4. Логирует скорость передачи (FPS и байты)
---
## Технические детали
### ImageAnalysis в CameraX
```
┌─────────────────────────────────────────┐
│ Camera Hardware │
└────────────┬────────────────────────────┘
┌────────┴──────────┬─────────────┐
│ │ │
▼ ▼ ▼
┌─────────┐ ┌──────────┐ ┌──────────┐
│ Preview │ │ImageCapture ImageAnalysis│
│(Display)│ │(Photos) │ │(Frames) │
└─────────┘ └──────────┘ └──────────┘
│ │ │
└──────────────────┬┴─────────────┘
┌──────▼──────┐
│ GPU/Display │
└──────────────┘
CameraManager привязывает ВСЕ три use case одновременно:
bindToLifecycle(lifecycleOwner, cameraSelector,
preview, imageCapture, imageAnalysis)
```
### Поток видео
```
Camera → ImageAnalysis → processFrame()
ByteArray → onFrameAvailable callback
MainActivity → viewModel.sendVideoFrame()
StreamViewModel → wsManager.sendBinary()
WebSocketManager → okhttp3 WebSocket
Server
```
---
## Файлы изменены
| Файл | Изменения |
|------|-----------|
| `CameraManager.kt` | +ImageAnalysis, +processFrame(), обновлена startCamera() |
| `MainActivity.kt` | +onFrame callback при запуске камеры |
| `WebSocketManager.kt` | Исправлен sendBinary(), заменена рефлексия на okio API |
| `StreamViewModel.kt` | Улучшено логирование FPS и размера данных |
| `AndroidManifest.xml` | Удалены ненужные разрешения |
---
## Как тестировать
1. **Запустите приложение** на устройстве/эмуляторе
2. **Введите параметры подключения:**
- IP сервера: `192.168.0.112`
- Порт: `8000`
- Room ID: `HhfoHArOGcT`
- Password: `1`
3. **Нажмите "Подключиться"**
4. **Откройте logcat** и отфильтруйте по:
- `CameraManager` - статус камеры
- `WebSocket` - отправка видео
- `StreamViewModel` - статистика FPS
5. **На сервере** должна появиться видео-трансляция
---
## Потенциальные улучшения
1. **Кодирование видео** - сейчас отправляются raw RGBA фреймы (очень большой размер)
- Используйте H.264 или VP9 кодирование
- Это уменьшит пропускную способность в 10-100 раз
2. **Качество и масштабирование**
- Добавить регулировку разрешения камеры
- Масштабировать фреймы перед отправкой
3. **Обработка ошибок**
- Переподключение при разрыве соединения
- Буферизация фреймов при медленной сети
4. **Производительность**
- Использовать `STRATEGY_BLOCK_CAPTURE_SESSION` если нужна синхронизация
- Оптимизировать работу потоков