简介
为了快速使用Android设备的相机 打包了camera2库
该库只需要很少的配置即可获取相机的部分重要参数,并启用其中一个选中的相机
用法
- 使用
init(applicationContext)获取上下文 - 使用
getCamerasList()获取相机参数 - 使用
setFrameCallback()设置回调 - 使用
setCameraParameters()选择目标相机 - 使用
closeCamera()关闭相机
附件
源码:
import android.Manifest
import android.content.Context
import android.graphics.ImageFormat
import android.hardware.camera2.CameraAccessException
import android.hardware.camera2.CameraCaptureSession
import android.hardware.camera2.CameraCharacteristics
import android.hardware.camera2.CameraDevice
import android.hardware.camera2.CameraManager
import android.hardware.camera2.CaptureRequest
import android.media.Image
import android.media.ImageReader
import android.media.MediaRecorder
import android.os.Handler
import android.os.HandlerThread
import android.util.Log
import android.util.Range
import android.util.Size
import androidx.annotation.RequiresPermission
// 分辨率与帧率信息类
data class ResInfo(
val width: Int,
val height: Int,
val fpsRanges: List<Range<Int>> // 帧率范围列表(如[30,30]或[60,120])
)
data class CameraInfo_t (
var id: Int = 0,
var focalLength: Float = 0.0f,
var equalsFocalLength: Float = 0.0f,
var resolutions: List<ResInfo> = emptyList()
)
object CameraController {
private lateinit var applicationContext: Context
fun init(applicationContext: Context) {
this.applicationContext = applicationContext.applicationContext
}
private var CameraInfos: MutableList<CameraInfo_t> = mutableListOf()
private lateinit var cameraManager: CameraManager
private lateinit var cameraDevice: CameraDevice
private lateinit var imageReader: ImageReader
private lateinit var backgroundThread: HandlerThread
private lateinit var backgroundHandler: Handler
private lateinit var captureSession: CameraCaptureSession
private var frameCallback: FrameCallback? = null
private var fpsRange: Range<Int>? = null
fun interface FrameCallback {
fun onFrameAvailable(imageData: Image, width: Int, height: Int)
}
fun setFrameCallback(callback: FrameCallback) {
this.frameCallback = callback
}
// 启动后台线程
private fun startBackgroundThread() {
Log.d("startBackgroundThread","Active")
backgroundThread = HandlerThread("CameraBackground")
backgroundThread.start()
backgroundHandler = Handler(backgroundThread.looper)
}
// 停止后台线程
private fun stopBackgroundThread() {
backgroundThread.quitSafely()
try {
backgroundThread.join()
} catch (e: InterruptedException) {
e.printStackTrace()
}
}
// 获取camera信息列表
public fun getCamerasList():List<CameraInfo_t> {
cameraManager = applicationContext.getSystemService(Context.CAMERA_SERVICE) as CameraManager
try {
val cameraIds = cameraManager.cameraIdList
for (cameraId in cameraIds) {
var cameraInfo = CameraInfo_t()
Log.d("ID",cameraId)
cameraInfo.id = cameraId.toInt()
val characteristics = cameraManager.getCameraCharacteristics(cameraId)
// 1. 获取所有支持的分辨率及帧率范围
val streamMap = characteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP)
streamMap?.let { map ->
val videoSizes = map.getOutputSizes(MediaRecorder::class.java)
cameraInfo.resolutions = videoSizes?.map { size ->
val fpsRanges = if (map.highSpeedVideoSizes.contains(size)) {
// 仅高帧率分辨率可调用此方法
map.getHighSpeedVideoFpsRangesFor(size)?.toList() ?: emptyList()
} else {
// 普通帧率:获取标准帧率范围
map.getOutputMinFrameDuration(MediaRecorder::class.java, size)?.let {
val fps = (1_000_000_000.0 / it).toInt()
listOf(Range(fps, fps)) // 构造固定帧率范围
} ?: emptyList()
}
ResInfo(size.width, size.height, fpsRanges)
} ?: emptyList()
}
cameraInfo.resolutions.forEach { i ->
Log.d("CameraInfo", "分辨率: ${i.width} * ${i.height} 帧率: ${i.fpsRanges} " )
}
val focalLengths = characteristics.get(CameraCharacteristics.LENS_INFO_AVAILABLE_FOCAL_LENGTHS)
val sensorSize = characteristics.get(CameraCharacteristics.SENSOR_INFO_PHYSICAL_SIZE)
val diagonal35mm = 43.27f
val sensorDiagonal = kotlin.math.hypot(sensorSize!!.width, sensorSize.height)
focalLengths?.let { lengths ->
if(lengths.isNotEmpty()) {
val mainFocalLength = lengths[0]
val equivalentFocalLength = mainFocalLength * (diagonal35mm / sensorDiagonal)
Log.d("CameraInfo", "相机焦距: $mainFocalLength mm, 等效焦距: $equivalentFocalLength mm")
cameraInfo.focalLength = mainFocalLength
cameraInfo.equalsFocalLength = equivalentFocalLength
}
}
CameraInfos.add(cameraInfo)
}
} catch (e: CameraAccessException) {
e.printStackTrace()
}
return CameraInfos
}
//设置camera并启动
@RequiresPermission(Manifest.permission.CAMERA)
public fun setCameraParameters(id:Int, width:Int, height:Int, fps:Int) {
val characteristics = cameraManager.getCameraCharacteristics(id.toString())
fpsRange = chooseOptimalFpsRange(
characteristics.get(CameraCharacteristics.CONTROL_AE_AVAILABLE_TARGET_FPS_RANGES)
?: arrayOf(),
fps
)
Log.d("fpsRange","$fpsRange")
try {
startBackgroundThread() //启动后台进程
imageReader = ImageReader.newInstance( //配置ImageReader
width,
height,
ImageFormat.YUV_420_888,
2
).apply {
setOnImageAvailableListener({ reader ->
//Log.d("setOnImageAvailableListener","Active")
val image = reader.acquireLatestImage()
image?.let {
val buffer = it
frameCallback?.onFrameAvailable(buffer,it.width,it.height)
it.close()
}
}, backgroundHandler)
}
cameraManager!!.openCamera(
id.toString(),
object : CameraDevice.StateCallback() {
override fun onOpened(camera: CameraDevice) {
Log.d("cameraManager","onOpened")
cameraDevice = camera
startPreview(width,height,fps)
}
override fun onDisconnected(camera: CameraDevice) {
Log.d("cameraManager","onDisconnected")
closeCamera()
}
override fun onError(
camera: CameraDevice,
error: Int
) {
Log.e("cameraManager","onError:$error")
closeCamera()
}
},
backgroundHandler
)
} catch (e:CameraAccessException) {
Log.e("setCameraParameters",e.toString())
}
}
private fun startPreview(width:Int, height:Int, fps:Int) {
try {
val imageSurface = imageReader.surface
val captureRequestBuilder = cameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW)
captureRequestBuilder.addTarget(imageSurface)
fpsRange?.let {
captureRequestBuilder.set(
CaptureRequest.CONTROL_AE_TARGET_FPS_RANGE,
it
)
}
cameraDevice.createCaptureSession(
listOf(imageSurface),
object : CameraCaptureSession.StateCallback() {
override fun onConfigured(session: CameraCaptureSession) {
captureSession = session
updatePreview(captureRequestBuilder)
}
override fun onConfigureFailed(session: CameraCaptureSession) {
//TODO("Not yet implemented")
}
},
backgroundHandler
)
} catch (e:CameraAccessException) {
Log.e("startPreview",e.toString())
}
}
private fun updatePreview(requestBuilder: CaptureRequest.Builder) {
try {
captureSession?.setRepeatingRequest(
requestBuilder.build(),
null,
backgroundHandler
)
} catch (e: CameraAccessException) {
e.printStackTrace()
}
}
//关闭camera
public fun closeCamera() {
if (::cameraDevice.isInitialized) {
cameraDevice.close()
stopBackgroundThread()
}
}
private fun convertImageToByteArray(image: Image): ByteArray {
val planes = image.planes
val yBuffer = planes[0].buffer
val uBuffer = planes[1].buffer
val vBuffer = planes[2].buffer
val ySize = yBuffer.remaining()
val uSize = uBuffer.remaining()
val vSize = vBuffer.remaining()
val nv21 = ByteArray(ySize + uSize + vSize)
// 复制 Y 数据
yBuffer.get(nv21, 0, ySize)
// 复制 UV 数据
vBuffer.get(nv21, ySize, vSize)
uBuffer.get(nv21, ySize + vSize, uSize)
return nv21
}
// 选择最佳尺寸
private fun chooseOptimalSize(
choices: Array<Size>,
width: Int,
height: Int
): Size {
val desiredRatio = width.toFloat() / height
var optimalSize = choices[0]
var minDiff = Float.MAX_VALUE
for (size in choices) {
val ratio = size.width.toFloat() / size.height
val diff = Math.abs(ratio - desiredRatio)
if (diff < minDiff) {
optimalSize = size
minDiff = diff
}
}
return optimalSize
}
// 选择最佳 FPS 范围
private fun chooseOptimalFpsRange(
ranges: Array<Range<Int>>,
desiredFps: Int
): Range<Int>? {
if (ranges.isEmpty()) return null
// 找到最接近 desiredFps 的范围
var optimalRange: Range<Int>? = null
var minDiff = Int.MAX_VALUE
for (range in ranges) {
// 检查范围是否包含 desiredFps
if (range.lower <= desiredFps && range.upper >= desiredFps) {
// 计算与 desiredFps 的差异
val diff = (range.upper - desiredFps) + (desiredFps - range.lower)
if (diff < minDiff) {
minDiff = diff
optimalRange = range
}
}
}
// 如果没有找到包含 desiredFps 的范围,返回最大范围
return optimalRange ?: ranges.maxByOrNull { it.upper }
}
}
Comments NOTHING