# ๐ŸŽฌ ะ’ะธะทัƒะฐะปัŒะฝะพะต ะพะฑัŠััะฝะตะฝะธะต ะฟั€ะพะฑะปะตะผั‹ ะธ ั€ะตัˆะตะฝะธั ## ะŸะ ะžะ‘ะ›ะ•ะœะ: ะ’ะธะดะตะพ ะฝะต ะพั‚ะฟั€ะฐะฒะปัะตั‚ัั ะฝะฐ ัะตั€ะฒะตั€ ``` โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ ะะ ะฅะ˜ะขะ•ะšะขะฃะ ะ ะ”ะž ะ˜ะกะŸะ ะะ’ะ›ะ•ะะ˜ะฏ โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ ๐Ÿ“ท ะšะะœะ•ะ ะ โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚ (RGBA frames) โ–ผ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ CameraManager โ”‚ โ”‚ โ€ข Preview ั‚ะพะปัŒะบะพ โ”‚ โ† ะŸะ ะžะ‘ะ›ะ•ะœะ: ImageAnalysis ะพั‚ััƒั‚ัั‚ะฒัƒะตั‚! โ”‚ โ€ข ะะตั‚ ะพะฑั€ะฐะฑะพั‚ะบะธ โ”‚ โ”‚ โ€ข ะะตั‚ callback โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โŒ ะ ะะ—ะžะ ะ’ะะะž โŒ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ MainActivity โ”‚ โ”‚ โ€ข ะะต ะฟะตั€ะตะดะฐะตั‚ โ”‚ โ† ะŸะ ะžะ‘ะ›ะ•ะœะ: ะะตั‚ callback ะดะปั ะฟะตั€ะตะดะฐั‡ะธ ั„ั€ะตะนะผะพะฒ โ”‚ ั„ั€ะตะนะผั‹ โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โŒ ะ ะะ—ะžะ ะ’ะะะž โŒ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ StreamViewModel โ”‚ โ”‚ โ€ข sendVideoFrame() โ”‚ โ† ะะ˜ะšะžะ“ะ”ะ ะะ• ะ’ะซะ—ะซะ’ะะ•ะขะกะฏ! โ”‚ ััƒั‰ะตัั‚ะฒัƒะตั‚ โ”‚ โ”‚ โ€ข ะะพ ะฝะธะบั‚ะพ ะฝะต โ”‚ โ”‚ ะฒั‹ะทั‹ะฒะฐะตั‚ โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โŒ ะ ะะ—ะžะ ะ’ะะะž โŒ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ WebSocketManager โ”‚ โ”‚ โ€ข sendBinary() ะตัั‚ัŒ โ”‚ โ† ะะ˜ะงะ•ะ“ะž ะะ• ะžะขะŸะ ะะ’ะ›ะฏะ•ะขะกะฏ โ”‚ โ€ข ะะพ ะดะฐะฝะฝั‹ั… ะฝะตั‚ โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โŒ ะ ะะ—ะžะ ะ’ะะะž โŒ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ ๐Ÿ–ฅ๏ธ ะกะ•ะ ะ’ะ•ะ  โ”‚ โ”‚ โ€ข ะะตั‚ ะฒะธะดะตะพ โŒ โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ• ะŸะžะขะžะš: ๐Ÿ“ท โ†’ โœ— โ†’ ? โ†’ โœ— โ†’ ? โ†’ โœ— โ†’ ๐Ÿ–ฅ๏ธ โ†‘ โ†‘ ะ ะะ—ะžะ ะ’ะะะž ะ ะะ—ะžะ ะ’ะะะž ``` --- ## ะ ะ•ะจะ•ะะ˜ะ•: ะกะพะตะดะธะฝัะตะผ ั†ะตะฟัŒ ะพะฑั€ะฐะฑะพั‚ะบะธ ะฒะธะดะตะพ ``` โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ ะะ ะฅะ˜ะขะ•ะšะขะฃะ ะ ะŸะžะกะ›ะ• ะ˜ะกะŸะ ะะ’ะ›ะ•ะะ˜ะฏ โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ ๐Ÿ“ท ะšะะœะ•ะ ะ โ”‚ โ”‚ (30 fps, 1920x1080) โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚ RGBA frames โ–ผ โ•”โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•— โ•‘ โœ… CameraManager โ•‘ โ•‘ โ”œโ”€ Preview (ะดะปั ัะบั€ะฐะฝะฐ) โ•‘ โ† ะะžะ’ะžะ•: ImageAnalysis โ•‘ โ”œโ”€ ImageCapture (ัะฝะธะผะบะธ) โ•‘ โ•‘ โ””โ”€ โœจ ImageAnalysis (ะฟะพั‚ะพะบะพะฒะพะต) โ•‘ โ† ะะžะ’ะžะ•: processFrame() โ•‘ โ”œโ”€ Analyzer: processFrame() โ•‘ โ† ะะžะ’ะžะ•: onFrameAvailable callback โ•‘ โ”œโ”€ Convert โ†’ ByteArray โ•‘ โ•‘ โ””โ”€ Invoke onFrameAvailable() โ•‘ โ•šโ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•คโ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ• โ”‚ ByteArray (ั„ั€ะตะนะผ) โ”‚ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ–ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ โœ… MainActivity โ”‚ โ”‚ โ”œโ”€ startCamera(...) โ”‚ โ”‚ โ””โ”€ onFrame = { frameData โ†’ โ”‚ โ† ะะžะ’ะžะ•: callback ั„ัƒะฝะบั†ะธั โ”‚ viewModel.sendVideoFrame(...) โ”‚ โ”‚ } โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚ ByteArray โ”‚ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ–ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ โœ… StreamViewModel โ”‚ โ”‚ โ”œโ”€ sendVideoFrame(frameData) โ”‚ โ”‚ โ”œโ”€ wsManager.sendBinary(frameData) โ”‚ โ”‚ โ”œโ”€ frameCount++ โ”‚ โ”‚ โ”œโ”€ totalBytesTransferred += size โ”‚ โ”‚ โ””โ”€ Log: "FPS: X, bytes sent: Y" โ† ะฃะ›ะฃะงะจะ•ะะž โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚ ByteArray โ”‚ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ–ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ โœ… WebSocketManager โ”‚ โ”‚ โ”œโ”€ sendBinary(data) โ”‚ โ”‚ โ”œโ”€ ByteString.toByteString() โ† ะ˜ะกะŸะ ะะ’ะ›ะ•ะะž โ”‚ โ”‚ โ”œโ”€ webSocket.send(byteString) โ”‚ โ”‚ โ””โ”€ Log: "Binary data sent: X bytes" โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚ WebSocket Binary Frame โ”‚ (ั‡ะตั€ะตะท ะธะฝั‚ะตั€ะฝะตั‚) โ–ผ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ ๐Ÿ–ฅ๏ธ ะกะ•ะ ะ’ะ•ะ  โ”‚ โ”‚ โœ… ะ’ะธะดะตะพ ะฟะพะปัƒั‡ะตะฝะพ! โ”‚ โ”‚ โ€ข ะคั€ะตะนะผั‹ ะฟั€ะธั…ะพะดัั‚ โ”‚ โ”‚ โ€ข ะžั‚ะพะฑั€ะฐะถะฐัŽั‚ัั โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ• ะŸะžะขะžะš: ๐Ÿ“ท โœ… โ†’ CameraManager โ†’ MainActivity โ†’ ViewModel โ†’ WebSocket โ†’ ๐Ÿ–ฅ๏ธ โœ… โœ… โœ… โœ… ``` --- ## ะ”ะตั‚ะฐะปัŒะฝะฐั ะดะธะฐะณั€ะฐะผะผะฐ CameraManager ### ะ‘ะซะ›ะž โŒ ``` โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ startCamera() โ”‚ โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค โ”‚ 1. Get ProcessCameraProvider โ”‚ โ”‚ 2. Create Preview (for display) โ”‚ โ”‚ 3. Create ImageCapture (for photos) โ”‚ โ”‚ 4. Select back camera โ”‚ โ”‚ 5. bindToLifecycle( โ”‚ โ”‚ preview, โ”‚ โ”‚ imageCapture โ”‚ โ”‚ โ† ะะ• ะฅะ’ะะขะะ•ะข ะะะะ›ะ˜ะ—ะะขะžะ ะ! โ”‚ โ”‚ ) โ”‚ โ”‚ 6. Log "Camera started" โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ ะ ะ•ะ—ะฃะ›ะฌะขะะข: ๐Ÿ“ท โ†’ [Preview] โ†’ ๐Ÿ–ฅ๏ธ โ†’ (ะฝะธั‡ะตะณะพ ะฝะต ะฒั‹ั…ะพะดะธั‚) ``` ### ะกะขะะ›ะž โœ… ``` โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ startCamera() โ”‚ โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค โ”‚ 1. Get ProcessCameraProvider โ”‚ โ”‚ 2. Create Preview (for display) โ”‚ โ”‚ 3. Create ImageCapture (for photos) โ”‚ โ”‚ 4. โœจ Create ImageAnalysis (for streaming) โ”‚ โ”‚ โ”œโ”€ setBackpressureStrategy(KEEP_ONLY_LATEST) โ”‚ โ”‚ โ”œโ”€ setOutputImageFormat(RGBA_8888) โ”‚ โ”‚ โ””โ”€ setAnalyzer(processFrame) โ”‚ โ”‚ 5. Select back camera โ”‚ โ”‚ 6. bindToLifecycle( โ”‚ โ”‚ preview, โ”‚ โ”‚ imageCapture, โ”‚ โ”‚ imageAnalysis โœจ โ”‚ โ”‚ ) โ”‚ โ”‚ 7. Log "Camera started with video streaming" โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ ะ ะ•ะ—ะฃะ›ะฌะขะะข: ๐Ÿ“ท โ†’ [Preview] โ†’ ๐Ÿ–ฅ๏ธ โ†’ [ImageAnalysis] โ†’ processFrame() โ†’ ByteArray โ†’ onFrameAvailable() ``` --- ## ะ”ะธะฐะณั€ะฐะผะผะฐ processFrame() ``` ImageAnalysis ะฟะพะปัƒั‡ะฐะตั‚ ั„ั€ะตะนะผ ะบะฐะถะดั‹ะต ~33ะผั (30 FPS) โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ processFrame(imageProxy: ImageProxy) โ”‚ โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค โ”‚ โ”‚ โ”‚ frameCount++ โ”‚ โ”‚ currentTime = System.currentTimeMillis() โ”‚ โ”‚ โ”‚ โ”‚ if (currentTime - lastLogTime > 5000) { โ”‚ โ”‚ Log "Processing frameCount frames/5s" โ”‚ โ”‚ frameCount = 0 โ”‚ โ”‚ lastLogTime = currentTime โ”‚ โ”‚ } โ”‚ โ”‚ โ”‚ โ”‚ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ โ”‚ โ”‚ ะžะ‘ะ ะะ‘ะžะขะšะ ะคะ ะ•ะ™ะœะ: โ”‚ โ”‚ โ”‚ โ”‚ 1. buffer = imageProxy.planes[0] โ”‚ โ”‚ โ”‚ โ”‚ 2. buffer.rewind() โ”‚ โ”‚ โ”‚ โ”‚ 3. frameData = ByteArray(size) โ”‚ โ”‚ โ”‚ โ”‚ 4. buffer.get(frameData) โ”‚ โ”‚ โ”‚ โ”‚ = ะšะพะฟะธั€ัƒะตะผ ะฟะธะบัะตะปะธ ะฒ ByteArray โ”‚ โ”‚ โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚ โ”‚ โ”‚ โ”‚ onFrameAvailable?.invoke(frameData) โ”‚ โ”‚ โ†“ ะžั‚ะฟั€ะฐะฒะปัะตะผ ั„ั€ะตะนะผ ั‡ะตั€ะตะท callback โ†“ โ”‚ โ”‚ (ะฟะพะฟะฐะดะฐะตั‚ ะฒ MainActivity.onFrame) โ”‚ โ”‚ โ”‚ โ”‚ imageProxy.close() โ”‚ โ”‚ (ะžัะฒะพะฑะพะถะดะฐะตะผ ั€ะตััƒั€ัั‹) โ”‚ โ”‚ โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ ะŸะžะขะžะš ะ”ะะะะซะฅ: ImageProxy โ†’ Buffer โ†’ ByteArray โ†’ Callback โ†’ ViewModel โ†’ Server ``` --- ## ะ”ะธะฐะณั€ะฐะผะผะฐ MainActivity callback ### ะ‘ะซะ›ะž โŒ ``` DisposableEffect(previewViewRef, isCameraRunning) { if (pv != null && isCameraRunning) { cameraManager.startCamera( lifecycleOwner, pv.surfaceProvider, onError = { err โ†’ ... } โ† ะะ• ะŸะ•ะ ะ•ะ”ะะ•ะœ ะคะ ะ•ะ™ะœะซ! ) } onDispose { cameraManager.stopCamera() } } ะ ะ•ะ—ะฃะ›ะฌะขะะข: ะšะฐะผะตั€ะฐ ั€ะฐะฑะพั‚ะฐะตั‚, ะฝะพ ั„ั€ะตะนะผั‹ ั‚ะตั€ััŽั‚ัั! ``` ### ะกะขะะ›ะž โœ… ``` DisposableEffect(previewViewRef, isCameraRunning) { if (pv != null && isCameraRunning) { cameraManager.startCamera( lifecycleOwner, pv.surfaceProvider, onError = { err โ†’ ... }, onFrame = { frameData โ†’ โ† ะะžะ’ะžะ•! viewModel.sendVideoFrame(frameData) } ) } onDispose { cameraManager.stopCamera() } } ะ ะ•ะ—ะฃะ›ะฌะขะะข: ะšะฐะถะดั‹ะน ั„ั€ะตะนะผ โ†’ ViewModel โ†’ Server โœ… ``` --- ## ะ”ะธะฐะณั€ะฐะผะผะฐ StreamViewModel ``` โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ sendVideoFrame(frameData: ByteArray) โ”‚ โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค โ”‚ โ”‚ โ”‚ wsManager.sendBinary(frameData) โ”‚ โ”‚ โ†“ ะžั‚ะฟั€ะฐะฒะปัะตะผ ั‡ะตั€ะตะท WebSocket โ†“ โ”‚ โ”‚ โ”‚ โ”‚ frameCount++ โ”‚ โ”‚ totalBytesTransferred += frameData.size โ”‚ โ”‚ _bytesTransferred.value = totalBytesTransferredโ”‚ โ”‚ โ”‚ โ”‚ if (currentTime - lastFpsTime >= 1000) { โ”‚ โ”‚ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ โ”‚ โ”‚ ะšะะ–ะ”ะฃะฎ ะกะ•ะšะฃะะ”ะฃ: โ”‚ โ”‚ โ”‚ โ”‚ Log "FPS: $frameCount, Bytes: $total" โ”‚ โ”‚ โ”‚ _fps.value = frameCount โ”‚ โ”‚ โ”‚ โ”‚ frameCount = 0 โ”‚ โ”‚ โ”‚ โ”‚ lastFpsTime = currentTime โ”‚ โ”‚ โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚ โ”‚ } โ”‚ โ”‚ โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ ะกะขะะขะ˜ะกะขะ˜ะšะ ะšะะ–ะ”ะฃะฎ ะกะ•ะšะฃะะ”ะฃ: - FPS: ะบะพะปะธั‡ะตัั‚ะฒะพ ั„ั€ะตะนะผะพะฒ ะฒ ัะตะบัƒะฝะดัƒ - Total bytes: ะพะฑัŠะตะผ ะฟะตั€ะตะดะฐะฝะฝั‹ั… ะดะฐะฝะฝั‹ั… ``` --- ## ะ”ะธะฐะณั€ะฐะผะผะฐ WebSocketManager ### ะ‘ะซะ›ะž โŒ (ั€ะตั„ะปะตะบัะธั) ``` fun sendBinary(data: ByteArray) { 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) } ะŸะ ะžะ‘ะ›ะ•ะœะซ: - ะœะตะดะปะตะฝะฝะพ (ั€ะตั„ะปะตะบัะธั) - ะฅั€ัƒะฟะบะพ (ะทะฐะฒัะทะฐะฝะพ ะฝะฐ ะธะผะตะฝะฐ ะผะตั‚ะพะดะพะฒ) - ะกะปะพะถะฝะพ (ะผะฝะพะณะพ ะฟั€ะพะผะตะถัƒั‚ะพั‡ะฝั‹ั… ะพะฑัŠะตะบั‚ะพะฒ) - ะœะพะถะตั‚ ัะปะพะผะฐั‚ัŒัั ะฟั€ะธ ะพะฑะฝะพะฒะปะตะฝะธะธ OkHttp ``` ### ะกะขะะ›ะž โœ… (Clean API) ``` import okio.ByteString.Companion.toByteString fun sendBinary(data: ByteArray) { val byteString = data.toByteString() โ† ะžะ”ะ˜ะ ะ’ะซะ—ะžะ’ webSocket?.send(byteString) โ† ะŸะ ะฏะœะžะ™ API } ะŸะ ะ•ะ˜ะœะฃะฉะ•ะกะขะ’ะ: - ะ‘ั‹ัั‚ั€ะพ (ะฟั€ัะผะพะน ะฒั‹ะทะพะฒ) - ะะฐะดะตะถะฝะพ (ัั‚ะฐะฝะดะฐั€ั‚ะฝั‹ะน API) - ะŸั€ะพัั‚ะพ (ะดะฒะต ัั‚ั€ะพะบะธ) - ะกั‚ะฐะฑะธะปัŒะฝะพ (ั‡ะฐัั‚ัŒ OkHttp) ``` --- ## ะขะฐะฑะปะธั†ะฐ ัั€ะฐะฒะฝะตะฝะธั | ะัะฟะตะบั‚ | ะ‘ะซะ›ะž โŒ | ะกะขะะ›ะž โœ… | |--------|---------|----------| | **ะ—ะฐั…ะฒะฐั‚ ะฒะธะดะตะพ** | ะขะพะปัŒะบะพ ะฟั€ะตะฒัŒัŽ | ImageAnalysis + Preview | | **ะžะฑั€ะฐะฑะพั‚ะบะฐ ั„ั€ะตะนะผะพะฒ** | ะะตั‚ | processFrame() | | **ะŸะตั€ะตะดะฐั‡ะฐ ั„ั€ะตะนะผะพะฒ** | ะ ะฐะทะพั€ะฒะฐะฝะพ | onFrame callback | | **ะžั‚ะฟั€ะฐะฒะบะฐ ะฝะฐ ัะตั€ะฒะตั€** | ะะธะบะพะณะดะฐ | ะšะฐะถะดั‹ะน ั„ั€ะตะนะผ | | **ะžั‚ะฟั€ะฐะฒะบะฐ ะฑะธะฝะฐั€ะฝั‹ั… ะดะฐะฝะฝั‹ั…** | ะ ะตั„ะปะตะบัะธั | okio.ByteString | | **ะ›ะพะณะธั€ะพะฒะฐะฝะธะต** | ะœะธะฝะธะผะฐะปัŒะฝะพะต | ะŸะพะดั€ะพะฑะฝะพะต (FPS, bytes) | | **ะ ะตะทัƒะปัŒั‚ะฐั‚** | ะ’ะธะดะตะพ ะฝะต ะธะดะตั‚ | โœ… ะ’ะธะดะตะพ ะธะดะตั‚ | --- ## ะ’ั€ะตะผะตะฝะฝะฐั ัˆะบะฐะปะฐ ะพะฑั€ะฐะฑะพั‚ะบะธ ั„ั€ะตะนะผะฐ ``` 0ms โ†’ ะšะฐะผะตั€ะฐ ะทะฐั…ะฒะฐั‚ั‹ะฒะฐะตั‚ ั„ั€ะตะนะผ (30fps = ะบะฐะถะดั‹ะต 33ะผั) 2ms โ†’ ImageAnalysis ะฟะพะปัƒั‡ะฐะตั‚ ั„ั€ะตะนะผ 3ms โ†’ processFrame() ะฟั€ะตะพะฑั€ะฐะทัƒะตั‚ ะฒ ByteArray 4ms โ†’ ะ’ั‹ะทั‹ะฒะฐะตั‚ัั callback onFrameAvailable() 4ms โ†’ MainActivity ะฟะพะปัƒั‡ะฐะตั‚ frameData 5ms โ†’ viewModel.sendVideoFrame() ะฒั‹ะทั‹ะฒะฐะตั‚ัั 6ms โ†’ StreamViewModel ะพั‚ะฟั€ะฐะฒะปัะตั‚ ั‡ะตั€ะตะท WebSocket 7ms โ†’ WebSocket ะพั‚ะฟั€ะฐะฒะปัะตั‚ ะฝะฐ ัะตั€ะฒะตั€ 50ms โ†’ ะกะตั€ะฒะตั€ ะฟะพะปัƒั‡ะฐะตั‚ ั„ั€ะตะนะผ ``` **ะ˜ั‚ะพะณะพ:** ~50ะผั ะทะฐะดะตั€ะถะบะฐ (ะฝะพั€ะผะฐะปัŒะฝะพ ะดะปั ะฟะพั‚ะพะบะพะฒะพะน ะฟะตั€ะตะดะฐั‡ะธ ะฒะธะดะตะพ) --- ## ะŸะพั‚ะพะบ ะดะฐะฝะฝั‹ั… ะฒ ะฟะฐะผัั‚ะธ ``` 1. ImageProxy (ะฒ ะฟะฐะผัั‚ะธ GPU) โ”‚ โ”œโ”€ ~10.7 ะœะ‘ (1920ร—1080ร—4 ะฑะฐะนั‚ะฐ ะดะปั RGBA) โ”‚ โ–ผ 2. ByteArray (ะฒ ะฟะฐะผัั‚ะธ RAM) โ”‚ โ”œโ”€ ~10.7 ะœะ‘ โ”‚ โ–ผ 3. okio.ByteString โ”‚ โ”œโ”€ ~10.7 ะœะ‘ (ะฒั€ะตะผะตะฝะฝะพ) โ”‚ โ–ผ 4. WebSocket ะฑัƒั„ะตั€ โ”‚ โ”œโ”€ ะคั€ะฐะณะผะตะฝั‚ะธั€ัƒะตั‚ัั ะฟะพ 16KB ะฑะปะพะบะฐะผ โ”‚ โ–ผ 5. Socket ะพั‚ะฟั€ะฐะฒะปัะตั‚ โ”‚ โ”œโ”€ ~10.7 ะœะ‘/ัะตะบ ะฟั€ะธ 30fps โ”‚ (100 ะœะฑะธั‚/ัะตะบ ั‚ั€ะตะฑัƒะตั‚ัั!) โ”‚ โ–ผ 6. ะกะตั€ะฒะตั€ ะฟะพะปัƒั‡ะฐะตั‚ ะžะŸะขะ˜ะœะ˜ะ—ะะฆะ˜ะฏ: ะ˜ัะฟะพะปัŒะทะพะฒะฐั‚ัŒ H.264 ะบะพะดะธั€ะพะฒะฐะฝะธะต - 10.7 ะœะ‘ โ†’ 0.1-0.5 ะœะ‘ (100x ัะถะฐั‚ะธะต) - 10 ะœะฑะธั‚/ัะตะบ (ะฒะผะตัั‚ะพ 100) ``` --- **ะ ะธััƒะฝะพะบ ัะพะทะดะฐะฝ:** 2025-12-03 **ะ”ะปั:** ะžะฑัŠััะฝะตะฝะธั ะฐั€ั…ะธั‚ะตะบั‚ัƒั€ั‹ ะฒะธะดะตะพะฟะพั‚ะพะบะฐ ะฒ CamControl **ะกั‚ะฐั‚ัƒั:** โœ… ะ’ัะต ะบะพะผะฟะพะฝะตะฝั‚ั‹ ะธัะฟั€ะฐะฒะปะตะฝั‹ ะธ ัะพะตะดะธะฝะตะฝั‹