如何解决Android应用程序在物理设备上崩溃,但未在模拟器上崩溃:“包裹:dup在包裹::读取错误:错误:打开的文件太多” 根本原因可能是...
我正在尝试将旧手机变成网络安全摄像头,因为在所有这些骚乱期间,我所在地区的犯罪率急剧上升(而且我不想依靠别人的应用来控制对我私人时刻的访问) )。
我正在分块发送,因此摄像机将视频记录几秒钟,然后停止,将捕获文件的二进制编码到Base64中,然后通过POST请求将其拍摄到家庭服务器中,无休止的循环。服务器解包+解码+将其作为原始二进制“ MP4”保存到其自己的磁盘上(TODO:有趣的运动检测后处理)。
在目标手机的OS版本和屏幕尺寸(及周围)使用各种虚拟设备,所有这些都可以长时间使用。我用了60秒的块进行了15分钟以上的处理,再加上6秒的块进行了一个多小时的处理。我始终收到模拟器生成的愚蠢的虚拟房间视频到我的服务器上。
但是在运行Android 6.0.1的三星Galaxy S5梦想成为安全摄像机的情况下,通常会在应用程序崩溃之前发送2或3个视频...除非您将分辨率设置得过高,然后您才能运行变成另一种症状。
症状0:在区块末尾崩溃
E / Parcel:dup()在Parcel :: read中失败,我为1,fds [i]为-1,fd_count为2,错误:打开的文件太多
E /表面:dequeueBuffer:IGraphicBufferProducer :: requestBuffer失败:-22
W / Adreno-EGLSUB:DequeueBuffer:721:使本机缓冲区出队失败:参数无效,buffer = 0x0,handle = 0x0
W / Adreno-EGL:
:EGL_BAD_SURFACE
紧接着是第二个错误:
E / CameraDeviceGLThread-1:在GL渲染线程上收到异常:
java.lang.IllegalStateException: swapBuffers: EGL error: 0x300d
然后,最后,一旦块时间到了并且相机再次进行记录,就会发生最后的错误,导致整个应用崩溃:
I / CameraDeviceState:旧版摄像头服务转换为错误状态
E / AndroidRuntime:严重例外:CameraThread
Process: com.example.roselawncam,PID: 14639
android.hardware.camera2.CameraAccessException:摄像头设备遇到严重错误
症状1:由于对自己的好处太“高分辨率”而导致块中崩溃
这些警告清楚表明资源紧张会导致此症状。它们会在应用崩溃时发生,更高的分辨率会导致更快的崩溃。我给这些坏男孩打钟:
- 1920x1080(30 FPS):5秒
- 1280x720(30 FPS):9秒
- 800x480(30 FPS):15秒
前后摄像头的时间相似。在较低的分辨率下,除非您增加了块时间,否则您将开始遇到“症状0”。无论如何:
W / Adreno-GSL:
: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:致命错误:(空)
A / Adreno-GSL:从功能SyncBackBuffer和第3131行退出com.example.roselawncam进程
A / libc:致命信号6(SIGABRT),在tid 19618(CameraDeviceGLT)中的代码-6
最后,相关的科特琳作品:
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)
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)
}
解决方法
我有一些建议:
-
在需要
file lock
的地方强制使用同步函数,以便线程按顺序执行该代码块,以有组织的方式打开和关闭file stream
,例如访问到文件。这样可以避免out of memory
和file already in use
错误。 -
是否可以不将文件编码为
Base 64
?字符串太大,请检查是否避免任何错误。
这是创建您自己的应用的好方法。
,打开的文件太多
症状#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
并发现了a similar question。它具有详细的错误日志和答案。两者都再次表示文件描述符。
根本原因可能是...
params["videodata"] = convertToBase64(outputFile)
请检查convertToBase64()
的实现。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。