main fixes
This commit is contained in:
407
CHANGES.md
Normal file
407
CHANGES.md
Normal 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
|
||||
**Проверено:** ✅ Успешно компилируется
|
||||
**Статус:** 🚀 Готово к тестированию
|
||||
|
||||
Reference in New Issue
Block a user