自定義音視頻基類四:採集篇總結

音視頻開發採集部分需要的知識前三章中已經寫明,這章是對前面三章的整理簡化,首先上實現類,代碼很簡單,文末爲基類代碼

class LiveBroadcastActivity : BaseVideoActivity() {

    //預覽CaptureRequest.Builder
    private lateinit var previewCaptureRequest: CaptureRequest.Builder

    override fun getLayoutId(): Int {
        return R.layout.activity_live_broadcast;
    }

    override fun init() {
        //開啓surfaceTextureListener監聽
        textureView.surfaceTextureListener = surfaceTextureListener

        //切換相機
        switchCamera.setOnClickListener {
            switchCamera()
        }

    }

    /**
     * 創建一個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) {

    }

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

    }

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

是不是感覺瞬間解脫,再也不用去寫那些繁瑣的代碼了,接下來總結一下是如何實現的,開啓定義好的surfaceTextureListener便會自動採集音視頻

textureView.surfaceTextureListener = surfaceTextureListener

使用是不是很簡單,然後來看一下基類是怎麼實現的

    /**
     * 獲取Surface的回調
     */
    val surfaceTextureListener = object : TextureView.SurfaceTextureListener {
        //SurfaceTexture大小發生變化時調用
        @SuppressLint("Recycle")
        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()
            }
            surface.release()
            return true
        }

        override fun onSurfaceTextureAvailable(
            surfaceTexture: SurfaceTexture,
            width: Int,
            height: Int
        ) {
            //初始化AudioRecord
            initAudioRecord()

            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
                //初始化Camera2
                initCamera2()

                //獲取相機屬性類
                val cameraCharacteristics = getCameraCharacteristics()
                //設置預覽尺寸
                size = setPreviewSize(surfaceTexture, cameraCharacteristics)

                surface = Surface(surfaceTexture)

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

在onSurfaceTextureAvailable方法中初始化AudioRecord和Camera,初始化完成後便會開啓攝像頭

/**
     * 打開攝像頭
     */
    @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>

在開啓攝像頭的回調中可以看到使用了一個抽象方法,繼承這個基類需要重寫這個方法,便可以得到CameraDevice,接下來你想怎麼玩就是你的事了

    /**
     * 創建一個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
    }

接下來是創建預覽Session的回調

    /**
     * 創建預覽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

同樣使用了一個抽象方法,定義這個抽象方法主要是靈活性考慮,當然如果你不考慮可以寫在基類中

    /**
     * 返回預覽的CaptureRequest
     */
    @RequiresApi(Build.VERSION_CODES.LOLLIPOP)
    override fun setRepeatingRequest(session: CameraCaptureSession): CaptureRequest{
        //開始錄音
        startRecording()
        return previewCaptureRequest.build()
    }

爲了兼容5.0以下的手機,定義了一個老相機API數據回調接口,只有5.0以下的手機這個方法纔會生效

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

    }

最後是音頻的抽象方法,可以直接在這裏得到音頻數據

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

    }

爲了防止內存泄露做了一些處理

    override fun onDestroy() {
        super.onDestroy()
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
            cameraDevice?.close()
            if(outputs != null){
                for (surface in outputs!!) {
                    surface.release()
                }
            }
            endRecording()
            releaseBackgroundThread()
        }else{
            stopPreview()
        }
    }

最後附上一個自適應比例

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

基類

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 var outputs: List<Surface>? = null

    //當前攝像頭
    private var cameraDevice: CameraDevice? = null

    //Session
    private var cameraCaptureSession: CameraCaptureSession? = null

    //預覽Surface
    lateinit var surface: Surface

    //相機預覽分辨率
    lateinit var size: Size

    /**
     * 獲取Surface的回調
     */
    val surfaceTextureListener = object : TextureView.SurfaceTextureListener {
        //SurfaceTexture大小發生變化時調用
        @SuppressLint("Recycle")
        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()
            }
            surface.release()
            return true
        }

        override fun onSurfaceTextureAvailable(
            surfaceTexture: SurfaceTexture,
            width: Int,
            height: Int
        ) {
            //初始化AudioRecord
            initAudioRecord()

            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
                //初始化Camera2
                initCamera2()

                //獲取相機屬性類
                val cameraCharacteristics = getCameraCharacteristics()
                //設置預覽尺寸
                size = setPreviewSize(surfaceTexture, cameraCharacteristics)

                surface = Surface(surfaceTexture)

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

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

        }

        // 當 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)
        }
    }

    /**
     * 切換攝像頭
     */
    fun switchCamera() {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
            if (cameraDevice != null) {
                if (index < cameraIdList.size - 1) {
                    index++
                } else {
                    index = 0
                }
                cameraId = cameraIdList[index]
                cameraDevice?.close()
                openCamera()
            } else {
                showToast("請先開啓攝像頭")
            }
        }else{
            oldSwitchCamera()
        }
    }

    /**
     * 獲取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()
            if(outputs != null){
                for (surface in outputs!!) {
                    surface.release()
                }
            }
            endRecording()
            releaseBackgroundThread()
        }else{
            stopPreview()
        }
    }

    // 採樣率
    private val sampleRateInHz = 44100

    // 音頻通道 立體聲:
    val stereo = AudioFormat.CHANNEL_IN_STEREO

    lateinit var audioRecord: AudioRecord

    //audioRecord能接受的最小的buffer大小
    private var bufferSizeInBytes: Int = 0

    //錄音線程
    private var recordingJob: Job? = null

    /**
     * 初始化AudioRecord
     */
    fun initAudioRecord(channelConfig: Int = AudioFormat.CHANNEL_IN_MONO) {
        //audioRecord能接受的最小的buffer大小
        bufferSizeInBytes = AudioRecord.getMinBufferSize(sampleRateInHz, channelConfig, AudioFormat.ENCODING_PCM_16BIT)
        audioRecord = AudioRecord(MediaRecorder.AudioSource.MIC, sampleRateInHz, channelConfig, AudioFormat.ENCODING_PCM_16BIT, bufferSizeInBytes)
    }

    /**
     * 開始錄音
     */
    fun startRecording() {
        recordingJob = GlobalScope.launch(Dispatchers.IO) {
            if (bufferSizeInBytes > 0) {
                audioRecord.startRecording()

                while (isActive) {
                    val buffer = ByteArray(bufferSizeInBytes)
                    val len = audioRecord.read(buffer, 0, buffer.size)
                    if (len > 0) {
                        //音頻編碼
                        audioCoding(buffer, len)
                    }
                }
            } else {
                launch(Dispatchers.Main) {
                    showToast("請先初始化AudioRecord類")
                }
            }
        }
    }

    /**
     * 音頻編碼
     */
    abstract fun audioCoding(buffer: ByteArray, len: Int)

    /**
     * 結束錄音
     */
    fun endRecording() {
        if (audioRecord.recordingState == AudioRecord.RECORDSTATE_RECORDING) {
            audioRecord.stop()
        }
        if (audioRecord.state == AudioRecord.STATE_INITIALIZED) {
            audioRecord.release()
        }
        recordingJob?.cancel()
    }

    //老相機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
    //老相機預覽尺寸
    lateinit var oldSize:Camera.Size

    /**
     * 老相機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
        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切換攝像頭
     */
    private 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]
    }
}

 

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