I'm trying to turn an old phone into a networked security camera, since crime has increased dramatically in my area during all these riots (and I don't want to rely on someone else's app to control access to my private moments).
I'm chunking-and-sending, so the camera records video for a number of seconds, stops, encodes the binary of the captured file into Base64, then shoots it off to a home server via POST request, all in an endless loop. The server unwraps + decodes + saves it as the original binary "MP4" onto its own disk (TODO: fun post-processing for motion detection).
Using a variety of virtual devices at (and around) my target phone's OS version & screen size, this all works for extended periods of time. I've used 60-second chunks for 15+ minutes, plus 6-second chunks for over an hour. I consistently receive the goofy virtual room videos the emulator produces onto my server.
But on the Samsung Galaxy S5 running Android 6.0.1 that dreams of becoming a security camera, it usually takes 2 or 3 videos being sent before the app crashes... except when your resolution is set too high, then you run into a different symptom.
Symptom #0: Crashing at the End of a Chunk
E/Parcel: dup() failed in Parcel::read, i is 1, fds[i] is -1, fd_count is 2, error: Too many open files
E/Surface: dequeueBuffer: IGraphicBufferProducer::requestBuffer failed: -22
W/Adreno-EGLSUB: DequeueBuffer:721: dequeue native buffer fail: Invalid argument, buffer=0x0, handle=0x0
W/Adreno-EGL: <qeglDrvAPI_eglSwapBuffers:3800>: EGL_BAD_SURFACE
Followed immediately by this second error:
E/CameraDeviceGLThread-1: Received exception on GL render thread:
java.lang.IllegalStateException: swapBuffers: EGL error: 0x300d
Then finally, once the chunk time is up and the camera goes to record again, the final error occurs to crash the entire app:
I/CameraDeviceState: Legacy camera service transitioning to state ERROR
E/AndroidRuntime: FATAL EXCEPTION: CameraThread
Process: com.example.roselawncam, PID: 14639
android.hardware.camera2.CameraAccessException: The camera device has encountered a serious error
Symptom #1: Crashing in the Middle of a Chunk Because You Went Too 🌈High-Res🌈 For Your Own Good
These warnings make it clear that resource strain causes this symptom. They occur as the app crashes, with higher resolutions causing sooner crashes. I clocked these bad boys at:
- 1920x1080 (30 FPS): 5 seconds
- 1280x720 (30 FPS): 9 seconds
- 800x480 (30 FPS): 15 seconds
Times were similar for both the front and back cameras. At lower resolutions, you start running into Symptom #0 unless you bump your chunk time way up. Anyway:
W/Adreno-GSL: <gsl_ldd_control:475>: ioctl fd 28 code 0xc01c0915 (IOCTL_KGSL_MAP_USER_MEM) failed: errno 12 Out of memory
W/Adreno-EGLSUB: SyncBackBuffer:3130: failed to map the memory for fd=281 offs=0
E/Adreno-EGLSUB: SyncBackBuffer:3131: SyncBackBuffer: FATAL ERROR : (null)
A/Adreno-GSL: Exiting the process com.example.roselawncam from function SyncBackBuffer and line 3131
A/libc: Fatal signal 6 (SIGABRT), code -6 in tid 19618 (CameraDeviceGLT)
And finally, the pertinent Kotlin pieces:
private fun createRecorder(surface: Surface) = MediaRecorder().apply {
setAudioSource(MediaRecorder.AudioSource.MIC)
setVideoSource(MediaRecorder.VideoSource.SURFACE)
setOutputFormat(MediaRecorder.OutputFormat.MPEG_4)
setOutputFile(outputFile.absolutePath)
setVideoEncodingBitRate(RECORDER_VIDEO_BITRATE)
if (args_fps > 0) setVideoFrameRate(args_fps)
setVideoSize(args_width, args_height)
setVideoEncoder(MediaRecorder.VideoEncoder.H264)
setAudioEncoder(MediaRecorder.AudioEncoder.AAC)
setInputSurface(surface)
}
private fun recordIt(cameraManager: CameraManager, cameraThread: HandlerThread) {
val cameraHandler = Handler(cameraThread.looper)
val stateCallback: CameraDevice.StateCallback = object: CameraDevice.StateCallback() {
override fun onOpened(camera: CameraDevice) {
camera.createCaptureSession(
listOf<Surface>(recorderSurface),
object : CameraCaptureSession.StateCallback() {
override fun onConfigured(session: CameraCaptureSession) {
val recTimeSeconds = findViewById<TextView>(R.id.recTimeSeconds)
val chunkTimeMilliseconds = recTimeSeconds.text.toString().toLong() * 1000
// Boolean "stopREC" (e.g. "Stop Recording") is false at this point
while (!stopREC) {
// // // This loop should run forever, but crashes after a few times // // //
val recorder: MediaRecorder by lazy { createRecorder(recorderSurface) }
val recordRequest: CaptureRequest by lazy {
session.device.createCaptureRequest(CameraDevice.TEMPLATE_RECORD).apply {
addTarget(recorderSurface)
set(CaptureRequest.CONTROL_AE_TARGET_FPS_RANGE, Range(args_fps, args_fps))
}.build()
}
session.setRepeatingRequest(recordRequest, null, cameraHandler)
recorder.apply { prepare(); start() }
Thread.sleep(chunkTimeMilliseconds)
recorder.apply { stop(); release() }
// Send the video file across the network in JSON via POST request:
val params = HashMap<String, String>()
params["videodata"] = convertToBase64(outputFile)
val jsonObject = JSONObject(params as Map<*, *>)
val request = JsonObjectRequest(Request.Method.POST, url, jsonObject, null, null)
queue.add(request)
// // // End of loop that should've ran forever, but crashes occasionally instead // // //
}
camera.close()
}
override fun onConfigureFailed(session: CameraCaptureSession) {}
},
cameraHandler
)
}
override fun onDisconnected(camera: CameraDevice) { recorder.stop(); recorder.release() }
override fun onError(camera: CameraDevice, error:Int) { camera.close() }
}
cameraManager.openCamera(args_cameraId, stateCallback, cameraHandler)
}
from Android App Crashes on Physical Device But Not Emulator: "Parcel: dup() failed in Parcel::read [...] error: Too many open files"
No comments:
Post a Comment