这里涉及到了摄像头Camera的使用,和对YUV数据的获取。
这里有一些东西需要格外注意,就是编码格式的选择,以及对宽高的设置
我这里自定义了一个CmeraView 因为摄像头的使用有点复杂,我索性就封装起来,这里一定要注意,宽(prewWidth )高(prewHeight )的设置,很有用,很有用。
import android.app.Activity
import android.content.Context
import android.graphics.ImageFormat
import android.hardware.*
import android.util.AttributeSet
import android.view.SurfaceHolder
import android.view.SurfaceView
import 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 数据的排列格式的数据。