这里涉及到了摄像头Camera的使用,和对YUV数据的获取。
这里有一些东西需要格外注意,就是编码格式的选择,以及对宽高的设置
我这里自定义了一个CmeraView 因为摄像头的使用有点复杂,我索性就封装起来,这里一定要注意,宽(prewWidth )高(prewHeight )的设置,很有用,很有用。
1import android.app.Activity 2import android.content.Context 3import android.graphics.ImageFormat 4import android.hardware.* 5import android.util.AttributeSet 6import android.view.SurfaceHolder 7import android.view.SurfaceView 8import android.view.Surface 9 10 11/** 12 * Created by xiaolei on 2018/3/26. 13 */ 14 15class CameraView @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0) : SurfaceView(context, attrs, defStyleAttr), SurfaceHolder.Callback 16{ 17 private var cameraId = 0 18 private val camera = Camera.open(cameraId) 19 private var preViewBlock: ((ByteArray, Camera) -> Unit)? = null 20 var prewWidth = 640 21 var prewHeight = 480 22 23 init 24 { 25 holder.addCallback(this) 26 holder.setKeepScreenOn(true) 27 holder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS) 28 camera.setPreviewCallback { data, camera -> 29 preViewBlock?.invoke(data, camera) 30 } 31 val params = camera.parameters 32 val supportSizeList = params.supportedPreviewSizes // 获取摄像头支持的分辨率宽高 33 println(supportSizeList.size) 34 supportSizeList.forEach { size -> 35 if (size.width == prewWidth && size.height == prewHeight) 36 { 37 params.setPreviewSize(prewWidth, prewHeight) 38 } 39 } 40 params.previewFormat = ImageFormat.NV21 // 设置摄像头输出的格式为 YUV420下 的 NV21 41 camera.parameters = params 42 } 43 44 override fun surfaceCreated(holder: SurfaceHolder?) 45 { 46 try 47 { 48 camera.setPreviewDisplay(holder) 49 camera.startPreview() 50 } catch (e: Exception) 51 { 52 e.printStackTrace() 53 } 54 } 55 56 override fun surfaceChanged(holder: SurfaceHolder?, format: Int, width: Int, height: Int) 57 { 58 // If your preview can change or rotate, take care of those events here. 59 // 如果您的预览可以更改或旋转,请在这里处理这些事件。 60 // Make sure to stop the preview before resizing or reformatting it. 61 // 在调整或重新格式化之前,务必停止预览。 62 getHolder() ?: let { 63 return 64 } 65 // stop preview before making changes 66 // 更改前停止预览。 67 try 68 { 69 camera.startPreview() 70 } catch (e: Exception) 71 { 72 e.printStackTrace() 73 } 74 // set preview size and make any resize, rotate or reformatting changes here 75 // 设置预览大小,并在这里进行任何调整、旋转或重新格式化。 76 // start preview with new settings 77 // 使用新的设置开始预览。 78 try 79 { 80 camera.setPreviewDisplay(getHolder()) 81 camera.startPreview() 82 } catch (e: Exception) 83 { 84 e.printStackTrace() 85 } 86 87 } 88 89 override fun surfaceDestroyed(holder: SurfaceHolder?) 90 { 91 // empty. Take care of releasing the Camera preview in your activity. 92 // 空的。注意在你的活动中发布相机预览。 93 camera.setPreviewCallback(null) 94 camera.stopPreview() 95 camera.release() 96 } 97 98 /** 99 * 设置自动旋转 100 */ 101 fun setAutoRotation(activity: Activity) 102 { 103 val rotation = activity.windowManager.defaultDisplay.rotation 104 val info = Camera.CameraInfo() 105 Camera.getCameraInfo(cameraId, info) 106 val degrees = when (rotation) 107 { 108 Surface.ROTATION_0 -> 0 109 Surface.ROTATION_90 -> 90 110 Surface.ROTATION_180 -> 180 111 Surface.ROTATION_270 -> 270 112 else -> 0 113 } 114 val result = if (info.facing === Camera.CameraInfo.CAMERA_FACING_FRONT) 115 { 116 val r = (info.orientation + degrees) % 360 117 (360 - r) % 360 118 } else 119 { 120 (info.orientation - degrees + 360) % 360 121 } 122 camera.setDisplayOrientation(result) 123 } 124 125 /** 126 * 设置预览数据的回调 127 */ 128 fun onPreviewCallback(block: (ByteArray, Camera) -> Unit) 129 { 130 this.preViewBlock = block 131 } 132 133 fun autoFocus() 134 { 135 camera.autoFocus { success, camera -> } 136 } 137 138 139 140 /** 141 * 把 YUV 的界面旋转90度,使得预览正常, 142 * 但是会把,宽 高 旋转 143 */ 144 fun yuv_rotate90(src: ByteArray, width: Int, height: Int): ByteArray 145 { 146 val des = ByteArray(src.size) 147 val wh = width * height 148 //旋转Y 149 var k = 0 150 for (i in 0 until width) 151 { 152 for (j in height - 1 downTo 0) 153 { 154 des[k++] = src[j * width + i] 155 } 156 } 157 //旋转UV 158 val uvHeight = height shr 1 159 val uvWidth = width shr 1 160 val uvWH = uvHeight * uvWidth 161 var i = 0 162 while (i < width) 163 { 164 for (j in uvHeight - 1 downTo 0) 165 { 166 des[k] = src[wh + width * j + i] 167 des[k + 1] = src[wh + width * j + i + 1] 168 k += 2 169 } 170 i += 2 171 } 172 return des 173 } 174}
其实关键代码就是这句:
1camera.setPreviewCallback { data, camera -> 2 preViewBlock?.invoke(data, camera) 3}
这里拿到的数据,就是刚才设置的 宽*高以及对 UV 数据的排列格式的数据。