android解决第三方摄像头调用拍照闪退解决办法 android.hardware.Camera.native_takePicture
android设备 除了前置 和 后置摄像头以外 ,有时会使用 type-c或者usb 连接AR眼镜 再连接到手机或者开发板等在正常情况调用的拍照函数takePicture会报错 用camera 1的api 调用拍照会报错 takePicture failedmCamera = Camera.open()...........mCamera?.takePicture(null, null, Cam
解决方案在文章的最下方
android设备 除了前置和后置摄像头以外 ,有时会使用 type-c或者usb 连接AR眼镜 再连接到手机或者开发板等应用场景
在正常情况用手机调用的拍照底层函数takePicture是不会报错
但是在作为第三方摄像头接入以后用camera 1的api 调用拍照会报错
mCamera = Camera.open()
...........初始化
mCamera?.takePicture(null, null, Camera.PictureCallback { data, camera ->
data?.let {
......生成图片
}
})
camera api会报以下错误
java.lang.RuntimeException: takePicture failed
at android.hardware.Camera.native_takePicture(Native Method)
at android.hardware.Camera.takePicture(Camera.java:1497)
at android.hardware.Camera.takePicture(Camera.java:1439)
at com.afei.camerademo.camera.CameraProxy.takePicture(CameraProxy.java:213)
at com.afei.camerademo.surfaceview.SurfaceCameraActivity.onClick(SurfaceCameraActivity.java:63)
at android.view.View.performClick(View.java:6294)
at android.view.View$PerformClick.run(View.java:24770)
at android.os.Handler.handleCallback(Handler.java:790)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:164)
at android.app.ActivityThread.main(ActivityThread.java:6494)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:438)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:807)
使用camera2的api则会报错
E/AndroidRuntime: FATAL EXCEPTION: RequestThread-0
Process: com.afei.camerademo, PID: 12312
java.lang.UnsupportedOperationException: Unknown error -38
at android.hardware.camera2.legacy.LegacyExceptionUtils.throwOnError(LegacyExceptionUtils.java:77)
at android.hardware.camera2.legacy.LegacyCameraDevice.setSurfaceOrientation(LegacyCameraDevice.java:789)
at android.hardware.camera2.legacy.RequestThreadManager.configureOutputs(RequestThreadManager.java:392)
at android.hardware.camera2.legacy.RequestThreadManager.-wrap0(Unknown Source:0)
at android.hardware.camera2.legacy.RequestThreadManager$5.handleMessage(RequestThreadManager.java:713)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.Looper.loop(Looper.java:164)
at android.os.HandlerThread.run(HandlerThread.java:65)
用TextureView来代替拍照又报错
E/CameraClient: Invalid setDisplayOrientation degrees=215
Process: com.afei.camerademo, PID: 12497
java.lang.RuntimeException: set display orientation failed
at android.hardware.Camera.setDisplayOrientation(Native Method)
at com.afei.camerademo.camera.CameraProxy.setDisplayOrientation(CameraProxy.java:184)
at com.afei.camerademo.camera.CameraProxy.openCamera(CameraProxy.java:55)
at com.afei.camerademo.textureview.CameraTextureView$1.onSurfaceTextureAvailable(CameraTextureView.java:47)
.....
从错误的日志中
只能勉强看到takePicture failed和RequestThread-0 error
问题太偏门了查了很多的文档也没有找到类似相关资料,读源码也找不到解决方案
怀疑是当用usb或者type-c 等AR设备的摄像头接入时
这种第三方既不属于前置摄像头也不属于后置摄像头, 它不属于系统进程
而属于另外一个应用进程,相机可以回传图像这没有问题,但是做一些拍照动作就没有权限
解决办法 用媒体类解锁第三方摄像头使用权限,我们在android 的api中寻找解决方案。
我们回到前面看at android.hardware.Camera.takePicture(Camera.java:1497) 1497的代码是注释是怎么描述
/**
* Triggers an asynchronous image capture. The camera service will initiate
* a series of callbacks to the application as the image capture progresses.
* The shutter callback occurs after the image is captured. This can be used
* to trigger a sound to let the user know that image has been captured. The
* raw callback occurs when the raw image data is available (NOTE: the data
* will be null if there is no raw image callback buffer available or the
* raw image callback buffer is not large enough to hold the raw image).
* The postview callback occurs when a scaled, fully processed postview
* image is available (NOTE: not all hardware supports this). The jpeg
* callback occurs when the compressed image is available. If the
* application does not need a particular callback, a null can be passed
* instead of a callback method.
*
* <p>This method is only valid when preview is active (after
* {@link #startPreview()}). Preview will be stopped after the image is
* taken; callers must call {@link #startPreview()} again if they want to
* re-start preview or take more pictures. This should not be called between
* {@link android.media.MediaRecorder#start()} and
* {@link android.media.MediaRecorder#stop()}.
*
* <p>After calling this method, you must not call {@link #startPreview()}
* or take another picture until the JPEG callback has returned.
*
* @param shutter the callback for image capture moment, or null
* @param raw the callback for raw (uncompressed) image data, or null
* @param postview callback with postview image data, may be null
* @param jpeg the callback for JPEG image data, or null
* @throws RuntimeException if starting picture capture fails; usually this
* would be because of a hardware or other low-level error, or because
* release() has been called on this Camera instance.
*/
翻译一下是
触发异步图像截图,摄像头服务将在图像截图过程中启动一系列回调
(shutter)快门回调发生在图像被截图后,这个可以触发一个声音,让用户知道已被截图.
这个(raw)原始回调发生在原始图像数据可用时(注意:如果没有可用的原始图像回调缓冲区或
原始图像回调缓冲区不够大,无法容纳原始图像)
(postview)当发生缩放动作时,会发生postview回调
(注意:并非所有硬件都支持此功能),(jpeg)当压缩图像可用时发生回调。如果应用程序不需要特定的回调,可以传递null而不是回调方法。
此方法仅在“预览”处于活动状态(在startPreview()方法之后
如果你想要拍更多图片,在图片生成以后必须再次调用startPreview)
也不能在 {@link android.media.MediaRecorder#start()} and {@link android.media.MediaRecorder#stop()}.方法之间
调用此方法后,不能调用{@link\#startPreview()}
或者拍摄另一张照片,直到(JPEG参数)中回调返回。
@param shutter图像捕获时刻的回调,或为null
@param raw原始(未压缩)图像数据的回调,或为null
@带有postview图像数据的postview回调参数可以为null
@param jpeg对jpeg图像数据的回调,或为null
@如果启动图片截图失败,则引发RuntimeException;通常是这样
可能是因为硬件或其他低版本错误,或者
已对此摄影机实例调用release()
这里我们看MediaRecorder.start怎么写的
MediaRecorder需要camera的unlock 方法 unlock api说明
* 解锁一个摄像机进程并访问它
* Unlocks the camera to allow another process to access it.
* Normally, the camera is locked to the process with an active Camera
* object until {@link #release()} is called. To allow rapid handoff
* between processes, you can call this method to release the camera
* temporarily for another process to use; once the other process is done
* you can call {@link #reconnect()} to reclaim the camera.
*
* <p>This must be done before calling
* {@link android.media.MediaRecorder#setCamera(Camera)}. This cannot be
* called after recording starts.
*
* <p>If you are not recording video, you probably do not need this method.
*
* @throws RuntimeException if the camera cannot be unlocked.
---------------------------------解决方案-----------------------------------
首先在startPreview 前调用unLock 解锁相机以允许其他进程访问它
var surfaceHolderCallback=object : SurfaceHolder.Callback {
override fun surfaceChanged(holder: SurfaceHolder?, format: Int, width: Int, height: Int) {
mSurfaceHolder = holder!!
mCamera?.apply {
..........
startPreview()
//添加unlock方法 解锁相机以允许其他进程访问它
unlock()
}
}
override fun surfaceDestroyed(holder: SurfaceHolder?) {
}
override fun surfaceCreated(holder: SurfaceHolder?) {
......
}
}
在拍照前调用创建MediaRecorder的媒体录像工具类的start方法来解锁camera来允许它被当前的应用调用拍照
mRecorder = MediaRecorder().apply {
reset()
setCamera(mCamera)
// 设置音频源与视频源 这两项需要放在setOutputFormat之前
setVideoSource(MediaRecorder.VideoSource.CAMERA)
//设置输出格式
setOutputFormat(MediaRecorder.OutputFormat.MPEG_4)
setVideoEncoder(MediaRecorder.VideoEncoder.H264) //视频编码格式
//设置最终出片分辨率
setVideoSize(1920, 1080)
setVideoFrameRate(30)
setVideoEncodingBitRate(3 * 1024 * 1024)
// setOrientationHint(90)
//设置记录会话的最大持续时间(毫秒)
setMaxDuration(30 * 1000)
}
path = Environment.getExternalStorageDirectory().path + File.separator + "temp.mp4"
try {
mRecorder.apply {
stop()
reset()
release()
}
} catch (e: Exception) {
}
mCamera?.takePicture(null, null, Camera.PictureCallback { data, camera ->
data?.let {
//将data转成文件即可
mCamera?.apply {
lock()
stopPreview()
release()
}
}
})
加上这些方法以后调用拍照就不会报错了?,为什么呢?MediaRecorder的.start方法是这样描述的
“应用程序在使用这个方法以后。应用程序不需要再次锁定摄像头。但是,如果方法失败,应用程序仍应锁定摄像头” 原文:
* Begins capturing and encoding data to the file specified with
* setOutputFile(). Call this after prepare().
*
* <p>Since API level 13, if applications set a camera via
* {@link #setCamera(Camera)}, the apps can use the camera after this method
* call. The apps do not need to lock the camera again. However, if this
* method fails, the apps should still lock the camera back. The apps should
* not start another recording session during recording.
*
* @throws IllegalStateException if it is called before
* prepare() or when the camera is already in use by another app.
拍照可以用了!?就是这么神奇!这个问题真的太偏门了,不知道其他设备遇到类似的错误能不能解决。分享此办法希望略尽绵力。
补充;使用这个方法以后拍摄的照片是缩放的,很模糊
val params = mCamera!!.parameters
val sizes = params.supportedPictureSizes
var size = sizes[0]
for (i in 0 until sizes.size) {
if (sizes[i].width > size.width) size = sizes[i]
}
params.setPictureSize(size.width, size.height)
params.flashMode = Camera.Parameters.FLASH_MODE_AUTO
params.focusMode = Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE
params.sceneMode = Camera.Parameters.SCENE_MODE_AUTO
params.whiteBalance = Camera.Parameters.WHITE_BALANCE_AUTO
params.exposureCompensation = 0
params.pictureFormat = ImageFormat.JPEG
params.jpegQuality = 100
params.setRotation(90)
mCamera?.parameters = params
//部分机型无法正确获取到params.supportedPictureSizes
可以强制使用1920*1080的分辨率
params.setPictureSize(1920, 1080)
更多推荐
所有评论(0)