自定义音视频基类一
程序员文章站
2022-07-01 13:53:38
...
安卓音视频开发相关的知识实在是太多了,如果每个都按之前那样写会很烦琐,为了偷懒决定写一个音视频的基类解决这个问题,本章内容为最基础的预览模块,废话不多说先上基类,后面为使用示例
abstract class BaseVideoActivity() : BaseActivity() {
private lateinit var mBackgroundThread: HandlerThread
private var mBackgroundHandler: Handler? = null
//摄像头管理类
lateinit var cameraManager: CameraManager
//摄像头id列表
lateinit var cameraIdList: Array<String>
//第几个摄像头
var index = 0
//当前摄像头id
lateinit var cameraId: String
//当前摄像头
private lateinit var cameraDevice: CameraDevice
//Surface集合
private lateinit var outputs:List<Surface>
//Session
private lateinit var cameraCaptureSession: CameraCaptureSession
/**
* 初始化Camera2
*/
@RequiresApi(Build.VERSION_CODES.LOLLIPOP)
fun initCamera2() {
cameraManager = application.getSystemService(Context.CAMERA_SERVICE) as CameraManager
cameraIdList = cameraManager.cameraIdList
cameraId = cameraIdList[index]
}
/**
* 获取CameraCharacteristics相机属性类
*/
@RequiresApi(Build.VERSION_CODES.LOLLIPOP)
fun getCameraCharacteristics(): CameraCharacteristics {
return cameraManager.getCameraCharacteristics(cameraId)
}
/**
* 设置预设的预览尺寸
*/
@RequiresApi(Build.VERSION_CODES.LOLLIPOP)
fun setPreviewSize(@NotNull surfaceTexture: SurfaceTexture, cameraCharacteristics: CameraCharacteristics): Size {
val aspectRatios = ArrayList<Float>()
aspectRatios.add(16.toFloat() / 9)
aspectRatios.add(4.toFloat() / 3)
aspectRatios.add(18.toFloat() / 9)
val size = getPreviewSize(cameraCharacteristics, aspectRatios)
surfaceTexture.setDefaultBufferSize(size.width, size.height)
return size
}
/**
* 获取预览尺寸
* 参数2:预览尺寸比例的集合,按加入顺序寻找预览尺寸并返回
*/
@RequiresApi(Build.VERSION_CODES.LOLLIPOP)
fun getPreviewSize(@NotNull cameraCharacteristics: CameraCharacteristics, aspectRatios: ArrayList<Float>): Size {
for (aspectRatio in aspectRatios) {
val size = getPreviewSize(cameraCharacteristics, aspectRatio)
if (size != null) {
return size
}
}
return Size(1280, 720)
}
/**
* 获取预览尺寸
* 参数2:预览尺寸比例,如4:3,16:9
*/
@RequiresApi(Build.VERSION_CODES.LOLLIPOP)
fun getPreviewSize(@NotNull cameraCharacteristics: CameraCharacteristics, aspectRatio: Float): Size? {
val streamConfigurationMap =
cameraCharacteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP)
val supportedSizes = streamConfigurationMap.getOutputSizes(SurfaceTexture::class.java)
for (size in supportedSizes) {
if (size.width.toFloat() / size.height == aspectRatio) {
return size
}
}
return null
}
/**
* 打开摄像头
*/
@RequiresApi(Build.VERSION_CODES.LOLLIPOP)
fun openCamera() {
if (ContextCompat.checkSelfPermission(this, Manifest.permission.CAMERA) == PackageManager.PERMISSION_GRANTED) {
cameraManager.openCamera(cameraId, callback, getBackgroundHandler())
} else {
val dialog = AlertDialog.Builder(this)
dialog.setTitle("开启相机失败").setMessage("缺少开启相机的权限").setCancelable(false)
dialog.setNegativeButton("取消") { _, _ ->
}
dialog.setPositiveButton("授权") { _, _ ->
val intent = Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS)
intent.data = Uri.parse("package:$packageName")
startActivity(intent)
}
dialog.show()
}
}
/**
* 打开摄像头的回调
*/
@RequiresApi(Build.VERSION_CODES.LOLLIPOP)
private val callback = object : CameraDevice.StateCallback() {
//成功打开时的回调,可以得到一个 CameraDevice 实例
override fun onOpened(camera: CameraDevice) {
cameraDevice = camera
outputs = createCaptureSession(cameraDevice)
}
//当 camera 不再可用时的回调,通常在该方法中进行资源释放的操作
override fun onDisconnected(camera: CameraDevice) {
showToast("camera不再可用")
}
// 当 camera 打开失败时的回调,error 为具体错误原因,通常在该方法中也要进行资源释放的操作
override fun onError(camera: CameraDevice, error: Int) {
camera.close()
showError(error)
releaseBackgroundThread()
}
//相机关闭时回调
override fun onClosed(camera: CameraDevice) {
super.onClosed(camera)
cameraCaptureSession.close()
}
}
/**
* 创建一个Session
*/
abstract fun createCaptureSession(camera: CameraDevice):List<Surface>
/**
* 创建预览Session的回调
*/
@RequiresApi(Build.VERSION_CODES.LOLLIPOP)
val mSessionCallback = object : CameraCaptureSession.StateCallback() {
override fun onConfigured(session: CameraCaptureSession) {
cameraCaptureSession = session
setRepeatingRequest(cameraCaptureSession)
}
//创建失败
override fun onConfigureFailed(session: CameraCaptureSession) {
showToast("创建Session失败")
}
}
/**
* 开始预览,即设置反复请求
*/
abstract fun setRepeatingRequest(session: CameraCaptureSession)
/**
* Session进度的回调
*/
@RequiresApi(Build.VERSION_CODES.LOLLIPOP)
val captureCallback = object : CameraCaptureSession.CaptureCallback() {
override fun onCaptureCompleted(
session: CameraCaptureSession,
request: CaptureRequest,
result: TotalCaptureResult
) {
super.onCaptureCompleted(session, request, result)
}
override fun onCaptureFailed(
session: CameraCaptureSession,
request: CaptureRequest,
failure: CaptureFailure
) {
super.onCaptureFailed(session, request, failure)
when (failure.reason) {
CaptureFailure.REASON_ERROR -> {
showToast("Capture failed: REASON_ERROR")
cameraCaptureSession.close()
}
CaptureFailure.REASON_FLUSHED -> showToast("Capture failed: REASON_FLUSHED")
else -> showToast("Capture failed: UNKNOWN")
}
}
}
/**
* 切换摄像头
*/
@RequiresApi(Build.VERSION_CODES.LOLLIPOP)
fun switchCamera(){
if (cameraDevice != null){
if (index < cameraIdList.size - 1) {
index++
} else {
index = 0
}
cameraId = cameraIdList[index]
cameraDevice?.close()
openCamera()
}else{
showToast("请先开启摄像头")
}
}
/**
* 获取BackgroundHandler
*/
fun getBackgroundHandler(): Handler {
if (mBackgroundHandler == null) {
//设置摄像头线程
mBackgroundThread = HandlerThread("CameraBackground")
mBackgroundThread.start()
mBackgroundHandler = Handler(mBackgroundThread.looper)
}
return mBackgroundHandler as Handler
}
/**
* 释放线程资源
*/
fun releaseBackgroundThread() {
mBackgroundHandler?.removeCallbacksAndMessages(null)
mBackgroundHandler = null
mBackgroundThread.quitSafely()
mBackgroundThread.join()
}
/**
* 开启摄像头错误提示
*/
fun showError(error: Int) {
when (error) {
CameraDevice.StateCallback.ERROR_CAMERA_IN_USE -> {
showToast("当前相机设备已经在一个更高优先级的地方打开了")
}
CameraDevice.StateCallback.ERROR_MAX_CAMERAS_IN_USE -> {
showToast("已打开相机数量到上限了,无法再打开新的相机了")
}
CameraDevice.StateCallback.ERROR_CAMERA_DISABLED -> {
showToast("由于相关设备策略该相机设备无法打开")
}
CameraDevice.StateCallback.ERROR_CAMERA_DEVICE -> {
showToast("相机设备发生了一个致命错误")
}
CameraDevice.StateCallback.ERROR_CAMERA_SERVICE -> {
showToast("相机服务发生了一个致命错误")
}
}
}
override fun onDestroy() {
super.onDestroy()
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
cameraDevice.close()
for (surface in outputs){
surface.release()
}
releaseBackgroundThread()
}
}
}
使用示例
class LiveBroadcastActivity : BaseVideoActivity() {
//预览Surface
private lateinit var surface: Surface
//相机预览分辨率
private lateinit var size: Size
//预览CaptureRequest.Builder
private lateinit var previewCaptureRequest: CaptureRequest.Builder
override fun getLayoutId(): Int {
return R.layout.activity_live_broadcast;
}
override fun init() {
//开启surfaceTextureListener监听,获取Surface
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
//初始化Camera2
initCamera2()
//开启监听
textureView.surfaceTextureListener = surfaceTextureListener
switchCamera.setOnClickListener {
switchCamera()
}
}
}
/**
* 获取Surface的回调
*/
@RequiresApi(Build.VERSION_CODES.LOLLIPOP)
private val surfaceTextureListener = object : TextureView.SurfaceTextureListener {
//SurfaceTexture大小发生变化时调用
override fun onSurfaceTextureSizeChanged(
surfaceTexture: SurfaceTexture,
width: Int,
height: Int
) {
//获取相机属性类
val cameraCharacteristics = getCameraCharacteristics()
//设置预览尺寸
size = setPreviewSize(surfaceTexture, cameraCharacteristics)
surface = Surface(surfaceTexture)
}
override fun onSurfaceTextureUpdated(surface: SurfaceTexture?) {
}
override fun onSurfaceTextureDestroyed(surfaceTexture: SurfaceTexture?): Boolean {
return true
}
override fun onSurfaceTextureAvailable(
surfaceTexture: SurfaceTexture,
width: Int,
height: Int
) {
//获取相机属性类
val cameraCharacteristics = getCameraCharacteristics()
//设置预览尺寸
size = setPreviewSize(surfaceTexture, cameraCharacteristics)
surface = Surface(surfaceTexture)
//开启摄像头
openCamera()
}
}
/**
* 创建一个Session
*/
@RequiresApi(Build.VERSION_CODES.LOLLIPOP)
override fun createCaptureSession(camera: CameraDevice):List<Surface>{
//创建一个预览的CaptureRequest
previewCaptureRequest = camera.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW)
// 设置预览输出的 Surface
previewCaptureRequest.addTarget(surface)
val outputs = ArrayList<Surface>()
outputs.add(surface)
//创建一个Session
camera.createCaptureSession(
outputs,
mSessionCallback,
getBackgroundHandler()
)
return outputs
}
/**
* 开始预览,即设置反复请求
*/
@RequiresApi(Build.VERSION_CODES.LOLLIPOP)
override fun setRepeatingRequest(session: CameraCaptureSession) {
session.setRepeatingRequest(
previewCaptureRequest.build(),
captureCallback,
getBackgroundHandler()
)
}
/**
* 自动修改textureView宽高以适应不同预览比例
*/
override fun onWindowFocusChanged(hasFocus: Boolean) {
super.onWindowFocusChanged(hasFocus)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
val width = textureView.width
val height = textureView.height
val proportion1 = size.width.toFloat() / size.height
val proportion2 = height.toFloat() / width
if (proportion1 > proportion2) {
val layoutParams = textureView.layoutParams
layoutParams.width = (height * proportion1 + .5).toInt()
textureView.layoutParams = layoutParams
} else if (proportion1 < proportion2) {
val layoutParams = textureView.layoutParams
layoutParams.height = (width * proportion1 + .5).toInt()
textureView.layoutParams = layoutParams
}
}
}
}
上一篇: 仿真中设置断点和查看变量
下一篇: 如何通过搜狐号引流推广?
推荐阅读
-
Python自定义一个类实现字典dict功能的方法
-
Flash怎么自定义类? flash自定义一个类的教程
-
(十七)c#Winform自定义控件-基类窗体
-
实现了一个PHP5的getter/setter基类的代码
-
Python自定义一个数组类,支持数组之间的四则运算和其他常见方法
-
ThinkPHP5.0框架控制器继承基类和自定义类示例
-
WPF自学入门(九)WPF自定义窗口基类
-
python基础第十九课--OOP定义一个接口或抽象基类(小白piao分享)
-
Java自定义一个异常类NoThisSongException和Player类
-
WindowsPhone自定义控件详解(一) - 控件类库分析