main fixes

This commit is contained in:
2025-12-09 21:21:26 +09:00
parent 752b2fb1ca
commit 568ca73a11
33 changed files with 4353 additions and 345 deletions

407
CHANGES.md Normal file
View File

@@ -0,0 +1,407 @@
и# 📝 Полный список изменений
## Дата: 2025-12-03
## Статус: ✅ Завершено и скомпилировано
---
## 1⃣ CameraManager.kt
### Добавлены импорты
```kotlin
import androidx.camera.core.ImageAnalysis
import androidx.camera.core.ImageProxy
```
### Добавлены новые поля
```kotlin
private val analysisExecutor = Executors.newSingleThreadExecutor()
private var onFrameAvailable: ((ByteArray) -> Unit)? = null
private var frameCount = 0
private var lastLogTime = System.currentTimeMillis()
```
### Изменена функция startCamera()
**Было:**
```kotlin
fun startCamera(
lifecycleOwner: LifecycleOwner,
previewSurfaceProvider: Preview.SurfaceProvider,
onError: (String) -> Unit
)
```
**Стало:**
```kotlin
fun startCamera(
lifecycleOwner: LifecycleOwner,
previewSurfaceProvider: Preview.SurfaceProvider,
onError: (String) -> Unit,
onFrame: ((ByteArray) -> Unit)? = null // ← НОВЫЙ ПАРАМЕТР
)
```
### Добавлен ImageAnalysis при привязке к камере
```kotlin
// Create image analysis for frame processing
val imageAnalysis = ImageAnalysis.Builder()
.setBackpressureStrategy(ImageAnalysis.STRATEGY_KEEP_ONLY_LATEST)
.setOutputImageFormat(ImageAnalysis.OUTPUT_IMAGE_FORMAT_RGBA_8888)
.build()
.apply {
setAnalyzer(analysisExecutor) { imageProxy ->
processFrame(imageProxy)
}
}
// Bind with imageAnalysis
cameraProvider?.bindToLifecycle(
lifecycleOwner,
cameraSelector,
preview,
imageCapture,
imageAnalysis // ← ДОБАВЛЕН
)
```
### Добавлена новая функция processFrame()
```kotlin
private fun processFrame(imageProxy: ImageProxy) {
try {
frameCount++
val currentTime = System.currentTimeMillis()
// Log every 5 seconds
if (currentTime - lastLogTime > 5000) {
Log.d("CameraManager", "Processing $frameCount frames/5s, sending to server")
frameCount = 0
lastLogTime = currentTime
}
// Convert image to byte array
val buffer = imageProxy.planes[0].buffer
buffer.rewind()
val frameData = ByteArray(buffer.remaining())
buffer.get(frameData)
// Send frame to callback
onFrameAvailable?.invoke(frameData)
imageProxy.close()
} catch (e: Exception) {
Log.e("CameraManager", "Error processing frame: ${e.message}")
imageProxy.close()
}
}
```
### Обновлена функция stopCamera()
```kotlin
fun stopCamera() {
try {
cameraProvider?.unbindAll()
analysisExecutor.shutdown() // ← ДОБАВЛЕНА
cameraExecutor.shutdown()
onFrameAvailable = null // ← ДОБАВЛЕНА
Log.d("CameraManager", "Camera stopped")
} catch (exc: Exception) {
Log.e("CameraManager", "Error stopping camera", exc)
}
}
```
---
## 2⃣ MainActivity.kt
### Изменен вызов startCamera()
**Было:**
```kotlin
cameraManager.startCamera(
lifecycleOwner,
pv.surfaceProvider,
onError = { err -> Log.e("CameraManager", "Camera error: $err") }
)
```
**Стало:**
```kotlin
cameraManager.startCamera(
lifecycleOwner,
pv.surfaceProvider,
onError = { err -> Log.e("CameraManager", "Camera error: $err") },
onFrame = { frameData -> // ← НОВЫЙ CALLBACK
// Send video frame to ViewModel for transmission to server
viewModel.sendVideoFrame(frameData)
}
)
```
---
## 3⃣ WebSocketManager.kt
### Изменены импорты
**Было:**
```kotlin
import okhttp3.OkHttpClient
import okhttp3.Request
import okhttp3.WebSocket
import okhttp3.WebSocketListener
import okhttp3.Response
```
**Стало:**
```kotlin
import android.util.Log
import okio.ByteString.Companion.toByteString // ← ИЗМЕНЕНО
import okhttp3.OkHttpClient
import okhttp3.Request
import okhttp3.WebSocket
import okhttp3.WebSocketListener
import okhttp3.Response
import java.util.concurrent.TimeUnit
```
### Переписана функция sendBinary()
**Было:**
```kotlin
fun sendBinary(data: ByteArray) {
try {
// Create ByteString from ByteArray using reflection to avoid import issues
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)
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")
}
}
```
**Стало:**
```kotlin
fun sendBinary(data: ByteArray) {
try {
val byteString = data.toByteString() // ← CLEAN API
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
### Улучшена функция sendVideoFrame()
**Было:**
```kotlin
fun sendVideoFrame(frameData: ByteArray) {
try {
wsManager?.sendBinary(frameData)
// Update statistics
frameCount++
totalBytesTransferred += frameData.size
_bytesTransferred.value = totalBytesTransferred
// Update FPS every second
val currentTime = System.currentTimeMillis()
if (currentTime - lastFpsTime >= 1000) {
_fps.value = frameCount
frameCount = 0
lastFpsTime = currentTime
}
} catch (e: Exception) {
Log.e("StreamViewModel", "Failed to send frame: ${e.message}")
}
}
```
**Стало:**
```kotlin
fun sendVideoFrame(frameData: ByteArray) {
try {
wsManager?.sendBinary(frameData)
// Update statistics
frameCount++
totalBytesTransferred += frameData.size
_bytesTransferred.value = totalBytesTransferred
// Update FPS every second
val currentTime = System.currentTimeMillis()
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
<!-- Required permissions -->
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<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" />
```
**Стало:**
```xml
<!-- Required permissions -->
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
```
---
## 📊 Статистика изменений
| Файл | Строк добавлено | Строк удалено | Изменено |
|------|-----------------|---------------|----------|
| CameraManager.kt | 45 | 5 | Функция startCamera(), метод processFrame() |
| MainActivity.kt | 5 | 1 | Вызов startCamera() |
| WebSocketManager.kt | 8 | 15 | Метод sendBinary() |
| StreamViewModel.kt | 2 | 1 | Функция sendVideoFrame() |
| AndroidManifest.xml | 0 | 4 | Разрешения |
| **ВСЕГО** | **60** | **26** | **5 файлов** |
---
## 🔍 Детализация по функциям
### CameraManager - Добавлены строки (примерно)
**Новые поля:**
```kotlin
line 18: private val analysisExecutor = Executors.newSingleThreadExecutor()
line 19: private var onFrameAvailable: ((ByteArray) -> Unit)? = null
line 20: private var frameCount = 0
line 21: private var lastLogTime = System.currentTimeMillis()
```
**Параметр startCamera:**
```kotlin
line 24: onFrame: ((ByteArray) -> Unit)? = null
```
**Новый ImageAnalysis:**
```kotlin
line 49-58: val imageAnalysis = ImageAnalysis.Builder()
```
**Новая функция processFrame:**
```kotlin
line 89-110: private fun processFrame(imageProxy: ImageProxy) { ... }
```
**В bindToLifecycle:**
```kotlin
line 76: imageAnalysis
```
**В stopCamera:**
```kotlin
line 124: analysisExecutor.shutdown()
line 125: onFrameAvailable = null
```
### MainActivity - Добавлены строки
**Новый callback в startCamera:**
```kotlin
line X: onFrame = { frameData ->
line X+1: viewModel.sendVideoFrame(frameData)
line X+2: }
```
---
## ✅ Компиляция
```
BUILD SUCCESSFUL in 6s
97 actionable tasks: 15 executed, 82 up-to-date
```
### Никаких ошибок:
- ✅ No compilation errors
- ✅ All imports resolved
- ✅ Type safety verified
---
## 📦 Артефакты
### Созданные файлы документации
1. `VIDEO_STREAMING_FIX.md` - Детальное объяснение
2. `DEBUGGING_SUMMARY.md` - Полный анализ
3. `TESTING_GUIDE.md` - Инструкции тестирования
4. `QUICK_SUMMARY.md` - Краткое резюме
5. `LOGS_COMPARISON.md` - Сравнение логов
6. `CHANGES.md` - Этот файл
### Скомпилированные APK файлы
- `/app/build/outputs/apk/debug/app-debug.apk`
- `/app/build/outputs/apk/release/app-release.apk`
---
## 🎯 Следующие шаги
### Немедленно
1. ✅ Скомпилировать: `./gradlew build` (ГОТОВО)
2. Установить: `./gradlew installDebug`
3. Запустить на устройстве
4. Проверить логи в logcat
### Для оптимизации
1. Добавить H.264 видеокодирование
2. Масштабировать фреймы перед отправкой
3. Реализовать переподключение при разрыве
---
## 🔗 Связанные файлы
- `app/src/main/java/com/example/camcontrol/CameraManager.kt`
- `app/src/main/java/com/example/camcontrol/MainActivity.kt`
- `app/src/main/java/com/example/camcontrol/WebSocketManager.kt`
- `app/src/main/java/com/example/camcontrol/StreamViewModel.kt`
- `app/src/main/AndroidManifest.xml`
---
**Создано:** 2025-12-03
**Проверено:** ✅ Успешно компилируется
**Статус:** 🚀 Готово к тестированию