14 KiB
🎥 CamControl - Исправления Видеопотока
Статус: ✅ ГОТОВО К ТЕСТИРОВАНИЮ
Проект успешно скомпилирован и содержит все исправления для отправки видео на сервер.
📋 Что было исправлено
Проблема
Приложение показывало превью камеры локально, но видео вообще не отправлялось на сервер.
Причины
- CameraManager захватывал только превью, но не обрабатывал видеофреймы
- MainActivity запускал камеру, но не передавал фреймы в ViewModel
- WebSocketManager использовал хрупкую рефлексию для бинарных данных
- Цепь обработки: Камера → Фреймы → Сервер была разорвана
Решение
- ✅ Добавлен
ImageAnalysisв CameraManager для захвата фреймов - ✅ Добавлен callback
onFrameв MainActivity для передачи фреймов в ViewModel - ✅ Исправлена отправка бинарных данных в WebSocketManager (окончательная рефлексия → okio.ByteString)
- ✅ Улучшено логирование для отслеживания процесса
📁 Измененные файлы
1️⃣ CameraManager.kt (Основное изменение)
Добавлено:
- ImageAnalysis для захвата видеофреймов
- Метод processFrame() для обработки каждого фрейма
- Callback onFrame для отправки фреймов наружу
- analysisExecutor для асинхронной обработки
Ключевые строки:
// Захват фреймов
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) {
val frameData = ByteArray(buffer.remaining())
buffer.get(frameData)
onFrameAvailable?.invoke(frameData)
}
2️⃣ MainActivity.kt (Критическое изменение)
Добавлено:
- Callback onFrame при запуске камеры
- Передача фреймов в ViewModel через sendVideoFrame()
Ключевые строки:
cameraManager.startCamera(
lifecycleOwner,
pv.surfaceProvider,
onError = { err -> Log.e("CameraManager", "Camera error: $err") },
onFrame = { frameData ->
viewModel.sendVideoFrame(frameData) // ← ГЛАВНОЕ!
}
)
3️⃣ WebSocketManager.kt (Техническое улучшение)
Изменено:
- Заменена рефлексия на стандартный API okio
- Улучшена надежность и производительность
Ключевые строки:
// Было (рефлексия):
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)
// Стало (clean API):
import okio.ByteString.Companion.toByteString
val byteString = data.toByteString()
webSocket?.send(byteString)
4️⃣ StreamViewModel.kt (Логирование)
Улучшено:
- Логирование FPS (фреймов в секунду)
- Логирование объема переданных данных
- Трассировка исключений при ошибках
5️⃣ AndroidManifest.xml (Очистка)
Удалены ненужные разрешения:
- SEND_SMS
- RECORD_AUDIO
- ACCESS_COARSE_LOCATION
- ACCESS_FINE_LOCATION
🚀 Как тестировать
Шаг 1: Убедитесь, что сервер работает
python manage.py runserver 192.168.0.112:8000
Шаг 2: Запустите приложение
./gradlew installDebug
# Или используйте Android Studio: Run > Run 'app'
Шаг 3: Введите параметры подключения
- Server IP: 192.168.0.112
- Server Port: 8000
- Room ID: HhfoHArOGcT
- Password: 1
Шаг 4: Проверьте логи
adb logcat | grep -E "CameraManager|WebSocket|StreamViewModel"
Ожидаемые логи:
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
Шаг 5: Проверьте, что видео получено на сервере
🔍 Диагностика
Если видео не отправляется
Проверить логи:
# Все логи приложения
adb logcat | grep com.example.camcontrol
# Только ошибки
adb logcat | grep ERROR
# Только WebSocket
adb logcat | grep WebSocket
# Только CameraManager
adb logcat | grep CameraManager
Что проверить:
- ✓ WebSocket подключен:
Connected! Response code=101 - ✓ Камера запущена:
Camera started successfully - ✓ Фреймы отправляются:
Binary data sent: X bytes - ✓ Нет исключений: ошибок в logcat
Если подключение не работает
Проверьте:
- IP адрес сервера доступен:
ping 192.168.0.112 - Сервер слушает на правильном порту:
netstat -ln | grep 8000 - Сетевая конфигурация Android:
app/src/main/res/xml/network_security_config.xml
📊 Структура потока видео
┌──────────────────────────────────────────────────────┐
│ Android Device │
├──────────────────────────────────────────────────────┤
│ │
│ ┌─────────────┐ │
│ │ Camera │ (задняя камера) │
│ └──────┬──────┘ │
│ │ │
│ │ RGBA_8888 frames │
│ ▼ │
│ ┌─────────────────────────────────┐ │
│ │ CameraManager.processFrame() │ │
│ │ - Extract pixel data │ │
│ │ - Convert to ByteArray │ │
│ │ - Invoke onFrameAvailable │ │
│ └──────┬──────────────────────────┘ │
│ │ │
│ │ ByteArray (frame data) │
│ ▼ │
│ ┌──────────────────────────────────────────────┐ │
│ │ MainActivity │ │
│ │ - Receive frame in onFrame callback │ │
│ │ - Call viewModel.sendVideoFrame(frameData) │ │
│ └──────┬───────────────────────────────────────┘ │
│ │ │
│ │ ByteArray (frame data) │
│ ▼ │
│ ┌────────────────────────────────────────────────┐ │
│ │ StreamViewModel │ │
│ │ - sendVideoFrame(frameData) │ │
│ │ - wsManager.sendBinary(frameData) │ │
│ │ - Update statistics (FPS, bytes sent) │ │
│ └────────┬─────────────────────────────────────┘ │
│ │ │
│ │ okio.ByteString │
│ ▼ │
│ ┌────────────────────────────────────────────────┐ │
│ │ WebSocketManager │ │
│ │ - Convert to okio.ByteString │ │
│ │ - webSocket.send(byteString) │ │
│ └────────┬─────────────────────────────────────┘ │
│ │ │
└───────────┼─────────────────────────────────────────┘
│
│ WebSocket Binary Frame
▼
┌──────────────────────────────────────────────────────┐
│ Server (192.168.0.112:8000) │
│ - Receive frame │
│ - Decode frame │
│ - Display video │
└──────────────────────────────────────────────────────┘
💡 Оптимизация (для будущего)
1. Кодирование видео
Сейчас отправляются raw RGBA фреймы (~3 МБ/сек при 30fps, 1920x1080)
Что сделать:
- Использовать H.264 кодирование (видео кодек)
- Или VP9/AV1 для лучшего качества
- Это уменьшит пропускную способность в 10-100 раз
Код:
import androidx.camera.video.VideoCapture
import androidx.camera.video.Recording
// Использовать VideoCapture вместо ImageAnalysis
val videoCapture = VideoCapture.withOutput(...)
2. Масштабирование
- Уменьшить разрешение перед отправкой
- Добавить регулировку качества
3. Буферизация
- Добавить очередь фреймов при медленной сети
- Пропускать фреймы при отставании
📝 История изменений
| Дата | Изменение |
|---|---|
| 2025-12-03 | Найдена проблема: видео не отправляется на сервер |
| 2025-12-03 | Добавлен ImageAnalysis в CameraManager |
| 2025-12-03 | Добавлен callback onFrame в MainActivity |
| 2025-12-03 | Исправлена отправка бинарных данных в WebSocketManager |
| 2025-12-03 | Проект успешно скомпилирован |
❓ FAQ
Q: Почему видео не показывается на сервере? A: Проверьте, что:
- WebSocket подключен (logcat: "Connected! Response code=101")
- Приложение запустило камеру (logcat: "Camera started successfully")
- Логи показывают "Binary data sent" (фреймы отправляются)
Q: Почему столько данных отправляется? A: Отправляются raw RGBA фреймы без кодирования. Это 3-4 МБ/сек. Используйте H.264 видеокодирование для сжатия (см. раздел "Оптимизация").
Q: Можно ли изменить разрешение камеры? A: Да, добавьте в CameraManager:
val imageAnalysis = ImageAnalysis.Builder()
.setTargetResolution(android.util.Size(640, 480)) // Меньшее разрешение
.build()
Q: Как отладить проблемы с WebSocket? A: Используйте фильтр в logcat:
adb logcat | grep "WebSocket"
🎯 Следующие шаги
- ✅ Скомпилировать проект (
./gradlew build) - ✅ Запустить на устройстве
- ✅ Проверить логи в logcat
- ✅ Убедиться, что видео отправляется на сервер
- 📋 Оптимизировать качество видео и битрейт
- 📋 Добавить кодирование видео (H.264)
- 📋 Реализовать переподключение при разрыве
Создано: 2025-12-03
Статус: Готово к тестированию
Контакт: Trevor (тестирующий)