自定义音视频基类一

安卓音视频开发相关的知识实在是太多了,如果每个都按之前那样写会很烦琐,为了偷懒决定写一个音视频的基类解决这个问题,本章内容为最基础的预览模块,废话不多说先上基类,后面为使用示例

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

    //Surface集合
    private lateinit var outputs: List<Surface>

    //当前摄像头
    private var cameraDevice: CameraDevice? = null

    //Session
    private var cameraCaptureSession: CameraCaptureSession? = null

    /**
     * 初始化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(camera)

            //创建一个Session
            camera.createCaptureSession(
                outputs,
                mSessionCallback,
                getBackgroundHandler()
            )
        }

        //当 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
            val captureRequest = setRepeatingRequest(session)

            session.setRepeatingRequest(
                captureRequest,
                captureCallback,
                getBackgroundHandler()
            )
        }

        //创建失败
        override fun onConfigureFailed(session: CameraCaptureSession) {
            showToast("创建Session失败")
        }
    }

    /**
     * 开始预览,即设置反复请求
     */
    abstract fun setRepeatingRequest(session: CameraCaptureSession):CaptureRequest

    /**
     * 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)

        return outputs
    }

    /**
     * 返回预览的CaptureRequest
     */
    @RequiresApi(Build.VERSION_CODES.LOLLIPOP)
    override fun setRepeatingRequest(session: CameraCaptureSession): CaptureRequest{
        return previewCaptureRequest.build()
    }

    /**
     * 自动修改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
            }
        }
    }
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值