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

408 lines
11 KiB
Markdown
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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.

и# 📝 Полный список изменений
## Дата: 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
**Проверено:** ✅ Успешно компилируется
**Статус:** 🚀 Готово к тестированию