这里涉及到了摄像头Camera的使用,和对YUV数据的获取。
这里有一些东西需要格外注意,就是编码格式的选择,以及对宽高的设置
我这里自定义了一个CmeraView 因为摄像头的使用有点复杂,我索性就封装起来,这里一定要注意,宽(prewWidth )高(prewHeight )的设置,很有用,很有用。
import android.app.Activityimport android.content.Contextimport android.graphics.ImageFormatimport android.hardware.*import android.util.AttributeSetimport android.view.SurfaceHolderimport android.view.SurfaceViewimport android.view.Surface/** * Created by xiaolei on 2018/3/26. */class CameraView @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0) : SurfaceView(context, attrs, defStyleAttr), SurfaceHolder.Callback{ private var cameraId = 0 private val camera = Camera.open(cameraId) private var preViewBlock: ((ByteArray, Camera) -> Unit)? = null var prewWidth = 640 var prewHeight = 480 init { holder.addCallback(this) holder.setKeepScreenOn(true) holder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS) camera.setPreviewCallback { data, camera -> preViewBlock?.invoke(data, camera) } val params = camera.parameters val supportSizeList = params.supportedPreviewSizes // 获取摄像头支持的分辨率宽高 println(supportSizeList.size) supportSizeList.forEach { size -> if (size.width == prewWidth && size.height == prewHeight) { params.setPreviewSize(prewWidth, prewHeight) } } params.previewFormat = ImageFormat.NV21 // 设置摄像头输出的格式为 YUV420下 的 NV21 camera.parameters = params } override fun surfaceCreated(holder: SurfaceHolder?) { try { camera.setPreviewDisplay(holder) camera.startPreview() } catch (e: Exception) { e.printStackTrace() } } override fun surfaceChanged(holder: SurfaceHolder?, format: Int, width: Int, height: Int) { // If your preview can change or rotate, take care of those events here. // 如果您的预览可以更改或旋转,请在这里处理这些事件。 // Make sure to stop the preview before resizing or reformatting it. // 在调整或重新格式化之前,务必停止预览。 getHolder() ?: let { return } // stop preview before making changes // 更改前停止预览。 try { camera.startPreview() } catch (e: Exception) { e.printStackTrace() } // set preview size and make any resize, rotate or reformatting changes here // 设置预览大小,并在这里进行任何调整、旋转或重新格式化。 // start preview with new settings // 使用新的设置开始预览。 try { camera.setPreviewDisplay(getHolder()) camera.startPreview() } catch (e: Exception) { e.printStackTrace() } } override fun surfaceDestroyed(holder: SurfaceHolder?) { // empty. Take care of releasing the Camera preview in your activity. // 空的。注意在你的活动中发布相机预览。 camera.setPreviewCallback(null) camera.stopPreview() camera.release() } /** * 设置自动旋转 */ fun setAutoRotation(activity: Activity) { val rotation = activity.windowManager.defaultDisplay.rotation val info = Camera.CameraInfo() Camera.getCameraInfo(cameraId, info) val degrees = when (rotation) { Surface.ROTATION_0 -> 0 Surface.ROTATION_90 -> 90 Surface.ROTATION_180 -> 180 Surface.ROTATION_270 -> 270 else -> 0 } val result = if (info.facing === Camera.CameraInfo.CAMERA_FACING_FRONT) { val r = (info.orientation + degrees) % 360 (360 - r) % 360 } else { (info.orientation - degrees + 360) % 360 } camera.setDisplayOrientation(result) } /** * 设置预览数据的回调 */ fun onPreviewCallback(block: (ByteArray, Camera) -> Unit) { this.preViewBlock = block } fun autoFocus() { camera.autoFocus { success, camera -> } } /** * 把 YUV 的界面旋转90度,使得预览正常, * 但是会把,宽 高 旋转 */ fun yuv_rotate90(src: ByteArray, width: Int, height: Int): ByteArray { val des = ByteArray(src.size) val wh = width * height //旋转Y var k = 0 for (i in 0 until width) { for (j in height - 1 downTo 0) { des[k++] = src[j * width + i] } } //旋转UV val uvHeight = height shr 1 val uvWidth = width shr 1 val uvWH = uvHeight * uvWidth var i = 0 while (i < width) { for (j in uvHeight - 1 downTo 0) { des[k] = src[wh + width * j + i] des[k + 1] = src[wh + width * j + i + 1] k += 2 } i += 2 } return des }}
其实关键代码就是这句:
camera.setPreviewCallback { data, camera -> preViewBlock?.invoke(data, camera)}
这里拿到的数据,就是刚才设置的 宽*高以及对 UV 数据的排列格式的数据。