自定義音視頻基類三

前兩章介紹了音視頻的採集,這章主要內容是適配5.0以下手機的視頻採集,首先自然是老Api的基類代碼

//老相機id
    private var oldCameraId = Camera.CameraInfo.CAMERA_FACING_BACK
    //老相機SurfaceTexture
    private var oldSurfaceTexture:SurfaceTexture? = null
    //老相機數據存儲數組
    private var oldBuffers:ByteArray? = null
    //老相機
    private var oldCamera:Camera? = null

    /**
     * 老相機API開始預覽
     */
    fun startPreview(surfaceTexture:SurfaceTexture){
        if (oldSurfaceTexture == null){
            oldSurfaceTexture = surfaceTexture;
        }

        // 打開攝像頭並將展示方向旋轉90度
        oldCamera = Camera.open(oldCameraId)
        oldCamera!!.setDisplayOrientation(90)

        val parameters = oldCamera!!.parameters

        // 選擇合適的預覽尺寸
        val sizeList = parameters.supportedPreviewSizes
        val oldSize = getOldSize(sizeList)

        parameters.previewFormat = ImageFormat.NV21
        //設置預覽圖像參數
        parameters.setPictureSize(oldSize.width,oldSize.height)
        parameters.setPreviewSize(oldSize.width,oldSize.height)

        oldCamera!!.parameters = parameters
        oldCamera!!.setPreviewTexture(oldSurfaceTexture)
        //獲取預覽數據
        oldBuffers = ByteArray(oldSize.width * oldSize.height * 4)
        oldCamera!!.addCallbackBuffer(oldBuffers)
        oldCamera!!.setPreviewCallbackWithBuffer(previewCallback)
        oldCamera!!.startPreview()
    }

    /**
     * 獲取老相機預覽數據回調
     */
    private val previewCallback = Camera.PreviewCallback { data, camera ->
        camera?.addCallbackBuffer(oldBuffers)
        oldSendVideoData(data)
    }

    /**
     * 老相機API預覽數據回調
     */
    abstract fun oldSendVideoData(data:ByteArray)

    /**
     * 停止預覽
     */
    fun stopPreview(){
        if (oldCamera != null){
            oldCamera!!.stopPreview()
            oldCamera!!.release()
            oldCamera = null
        }
    }

    /**
     * 老camera切換攝像頭
     */
    fun oldSwitchCamera(){
        if (oldSurfaceTexture != null){
            oldCameraId = if (oldCameraId == Camera.CameraInfo.CAMERA_FACING_BACK){
                Camera.CameraInfo.CAMERA_FACING_FRONT
            }else{
                Camera.CameraInfo.CAMERA_FACING_BACK
            }
            stopPreview()
            startPreview(oldSurfaceTexture!!)
        }
    }

    /**
     * 獲取老相機API的預覽大小
     */
    private fun getOldSize(sizeList:List<Camera.Size>):Camera.Size{
        val aspectRatios = ArrayList<Float>()
        aspectRatios.add(16.toFloat() / 9)
        aspectRatios.add(4.toFloat() / 3)
        aspectRatios.add(18.toFloat() / 9)
        for (aspectRatio in aspectRatios){
            for (size in sizeList){
                if (size.width.toFloat() / size.height == aspectRatio) {
                    return size
                }
            }
        }
        return sizeList[0]
    }

使用方式很簡單,在onSurfaceTextureAvailable中調用startPreview(surfaceTexture);中onSurfaceTextureDestroyed中調用stopPreview()

/**
     * 獲取Surface的回調
     */
    private val surfaceTextureListener = object : TextureView.SurfaceTextureListener {
        //SurfaceTexture大小發生變化時調用
        override fun onSurfaceTextureSizeChanged(
                surfaceTexture: SurfaceTexture,
                width: Int,
                height: Int
        ) {
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
                //獲取相機屬性類
                val cameraCharacteristics = getCameraCharacteristics()
                //設置預覽尺寸
                size = setPreviewSize(surfaceTexture, cameraCharacteristics)

                surface = Surface(surfaceTexture)
            }
        }

        override fun onSurfaceTextureUpdated(surface: SurfaceTexture?) {

        }

        override fun onSurfaceTextureDestroyed(surfaceTexture: SurfaceTexture?): Boolean {
            //關閉老相機API預覽
            if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
                stopPreview()
            }
            return true
        }

        override fun onSurfaceTextureAvailable(
                surfaceTexture: SurfaceTexture,
                width: Int,
                height: Int
        ) {
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
                //獲取相機屬性類
                val cameraCharacteristics = getCameraCharacteristics()
                //設置預覽尺寸
                size = setPreviewSize(surfaceTexture, cameraCharacteristics)

                surface = Surface(surfaceTexture)
                //開啓攝像頭
                openCamera()
            }else{
                //開啓老相機API預覽
                startPreview(surfaceTexture)
            }
        }
    }

繼承這個基類需要實現oldSendVideoData,視頻編碼是不是很方便

    /**
     * 老相機API數據回調接口
     */
    override fun oldSendVideoData(data: ByteArray) {

    }

使用示例

這裏集成了前面兩章內容

class LiveBroadcastActivity : BaseVideoActivity() {

    /**
     * 老相機API數據回調接口
     */
    override fun oldSendVideoData(data: ByteArray) {

    }

    //預覽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() {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
            //初始化Camera2
            initCamera2()
            //初始化AudioRecord
            initAudioRecord()

            switchCamera.setOnClickListener {
                switchCamera()
            }
        }

        //開啓surfaceTextureListener監聽,獲取Surface
        textureView.surfaceTextureListener = surfaceTextureListener

        switchCamera.setOnClickListener {
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
                switchCamera()
            }else{
                oldSwitchCamera()
            }
        }
    }

    /**
     * 獲取Surface的回調
     */
    private val surfaceTextureListener = object : TextureView.SurfaceTextureListener {
        //SurfaceTexture大小發生變化時調用
        override fun onSurfaceTextureSizeChanged(
                surfaceTexture: SurfaceTexture,
                width: Int,
                height: Int
        ) {
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
                //獲取相機屬性類
                val cameraCharacteristics = getCameraCharacteristics()
                //設置預覽尺寸
                size = setPreviewSize(surfaceTexture, cameraCharacteristics)

                surface = Surface(surfaceTexture)
            }
        }

        override fun onSurfaceTextureUpdated(surface: SurfaceTexture?) {

        }

        override fun onSurfaceTextureDestroyed(surfaceTexture: SurfaceTexture?): Boolean {
            //關閉老相機API預覽
            if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
                stopPreview()
            }
            return true
        }

        override fun onSurfaceTextureAvailable(
                surfaceTexture: SurfaceTexture,
                width: Int,
                height: Int
        ) {
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
                //獲取相機屬性類
                val cameraCharacteristics = getCameraCharacteristics()
                //設置預覽尺寸
                size = setPreviewSize(surfaceTexture, cameraCharacteristics)

                surface = Surface(surfaceTexture)
                //開啓攝像頭
                openCamera()
            }else{
                //開啓老相機API預覽
                startPreview(surfaceTexture)
            }
        }
    }

    /**
     * 創建一個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{
        //開始錄音
        startRecording()
        return previewCaptureRequest.build()
    }

    /**
     * 音頻編碼,注意這裏是在子線程中的
     */
    override fun audioCoding(buffer: ByteArray, len: Int) {

    }

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

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章