Android 应用在物理设备上崩溃,但模拟器无法崩溃:"Parcel: dup() failed in Parcel::read [...] error: Too many open files"



我正试图把一部旧手机变成一个联网的安全摄像头,因为在所有这些骚乱期间,我所在地区的犯罪率急剧上升(我不想依赖别人的应用程序来控制对我私人时刻的访问)。

我正在进行分块和发送,所以相机会记录几秒钟的视频,停止,将捕获的文件的二进制编码到Base64中,然后通过POST请求将其发送到家庭服务器,所有这些都是无休止的循环。服务器将其展开+解码+保存为原始二进制"0";MP4";到自己的磁盘上(TODO:用于运动检测的有趣的后处理)。

在我的目标手机的操作系统版本&屏幕大小,这一切都适用于长时间。我已经用了60秒的时间段15分钟以上,再加上6秒的时间长达一个多小时。我一直收到模拟器在我的服务器上制作的愚蠢的虚拟房间视频。

但在运行安卓6.0.1的三星Galaxy S5上,它梦想成为一款安全摄像头,通常需要发送2到3个视频,然后应用程序才会崩溃。。。除非你的决心设定得太高,否则你会遇到不同的症状。

症状#0:在Chunk结束时崩溃

E/Parcel:dup()在Parcel::read中失败,i为1,fds[i]为-1,fd_count为2,错误:打开的文件太多

E/Surface:dequeueBuffer:IGraphicBufferProducer::requestBuffer失败:-22

W/Adreno EGLSUB:出队缓冲区:721:出队本机缓冲区失败:无效参数,缓冲区=0x0,句柄=0x0

W/Adreno EGL:<qeglDrvAPI_eglSwapBuffers:3800>:EGL_BAD_SURFACE

紧接着是第二个错误:

E/CameraDeviceGLThread-1:在GL渲染线程上接收到异常:

java.lang.IllegalStateException: swapBuffers: EGL error: 0x300d

最后,一旦区块时间到了,相机再次进行记录,就会出现最终错误,导致整个应用程序崩溃:

I/CameraDeviceState:传统相机服务转换到状态ERROR

E/AndroidRuntime:致命异常:CameraThread

Process: com.example.roselawncam, PID: 14639

android.hardware.camera2.CameraAccessException:相机设备遇到严重错误

症状#1:因为你为了自己的利益太高而在Chunk中间崩溃

这些警告清楚地表明,资源紧张导致了这种症状。它们发生在应用程序崩溃时,更高的分辨率会导致更快的崩溃。我在记录了这些坏男孩

  • 1920x1080(30 FPS):5秒
  • 1280x720(30 FPS):9秒
  • 800x480(30 FPS):15秒

前后摄像头的时间相似。在较低的分辨率下,除非你的大块时间增加,否则你会开始遇到症状#0。总之:

W/Adreno GSL:<gsl_ldd_control:475>:ioctl fd 28代码0xc01c0915(ioctl_KGSL_MAP_USER_MEM)失败:errno 12内存不足

W/Adreno EGLSUB:SyncBackBuffer:3130:未能映射fd=281 offs=0的内存

E/Adreno EGLSUB:SyncBackBuffer:3131:SyncBackBuffer:致命错误:(null)

A/Adreno GSL:从函数SyncBackBuffer和第3131行退出进程com.example.roselawncam

A/libc:致命信号6(SIGABRT),tid 19618中的代码-6(CameraDeviceGLT)

最后,相关的Kotlin作品:

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)
}

打开的文件太多

症状#0的第一条错误消息是

E/Parcel: dup() failed in Parcel::read, i is 1, fds[i] is -1, fd_count is 2, error: Too many open files

来自Parcel.cpp:

status_t err = NO_ERROR;
for (size_t i=0 ; i<fd_count && err==NO_ERROR ; i++) {
fds[i] = dup(this->readFileDescriptor());
if (fds[i] < 0) {
err = BAD_VALUE;
ALOGE("dup() failed in Parcel::read, i is %zu, fds[i] is %d, fd_count is %zu, error: %s",
i, fds[i], fd_count, strerror(errno));
}
}

这表明上述错误发生在

fds[i] = dup(this->readFileDescriptor());

我还搜索了Too many open files,发现了一个类似的问题。它有一个详细的错误日志和一个答案。两者都再次指示文件描述符

根本原因可能是

params["videodata"] = convertToBase64(outputFile)

请检查convertToBase64()的执行情况。

根据这个和这个,文件描述符可能会泄漏。

我有一些建议:

  • 在需要file lock的地方强制使用同步函数,以便线程顺序执行该代码块,以有组织的方式打开和关闭file stream,例如访问文件。这将避免out of memoryfile already in use错误。

  • 是否可以不将文件编码为Base 64?字符串太大,请检查它是否避免任何错误。

还有一个很好的计划来开发你自己的应用程序。

最新更新