自定义音视频基类五

本章内容主要是简述音视频硬编码模块和部分逻辑改动

使用示例

    override fun init() {
        //开启surfaceTextureListener监听
        textureView.surfaceTextureListener = surfaceTextureListener


        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
            //创建预览的回调
            setOnCreateCaptureSession(onCreateCaptureSession)
            //音视频编码数据回调
            setOnCodingVideoCallback(onCodingVideoCallback)
        }else{
            //老相机数据回调
            setOnOldVideoCallback(onOldVideoCallback)
        }

         //切换相机
        switchCamera.setOnClickListener {
            switchCamera()
        }
    }

初始化主要分为三个部分,首先是设置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()
                //设置预览尺寸
                previewSize = setPreviewSize(surfaceTexture, cameraCharacteristics)

                previewSurface = Surface(surfaceTexture)
            }
        }

        override fun onSurfaceTextureUpdated(surface: SurfaceTexture?) {

        }

        override fun onSurfaceTextureDestroyed(surfaceTexture: SurfaceTexture?): Boolean {
            //关闭老相机API预览
            if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
                stopPreview()
            }else{
                previewSurface.release()
            }
            return true
        }

        @SuppressLint("Recycle")
        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()

                //设置预览尺寸
                previewSize = setPreviewSize(surfaceTexture, cameraCharacteristics)

                //获取预览Surface
                previewSurface = Surface(surfaceTexture)

                //是否开启视频编码
                createSurfaceVideoEncoder()

                //是否开启音频编码
                createAudioEncoder()

                //开启摄像头
                openCamera()
            } else {
                oldSurfaceTexture = surfaceTexture
                //开启老相机API预览
                startPreview()
            }
        }
    }

然后是创建预览的回调,从基类代码中可以看到,可以使用设置好的参数,也可以自定义参数,自定义参数需要使用setOnCreateCaptureSession(onCreateCaptureSession)

    /**
     * 打开摄像头的回调
     */
    @RequiresApi(Build.VERSION_CODES.LOLLIPOP)
    private val callback = object : CameraDevice.StateCallback() {
        //成功打开时的回调,可以得到一个 CameraDevice 实例
        override fun onOpened(camera: CameraDevice) {
            cameraDevice = camera

            outputs = ArrayList()

            if (onCreateCaptureSession == null){
                //创建一个录像的CaptureRequest
                previewCaptureRequest = camera.createCaptureRequest(CameraDevice.TEMPLATE_RECORD)
                previewCaptureRequest.addTarget(previewSurface)
                previewCaptureRequest.addTarget(inputSurface)

                outputs.add(previewSurface)
                outputs.add(inputSurface)
            }else{
                previewCaptureRequest = onCreateCaptureSession!!.createCaptureSession(camera,previewSurface,outputs)
            }

            //创建一个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()
        }
    }

自定义预览参数示例

    /**
     * 创建预览的回调
     */
    private val onCreateCaptureSession = object : OnCreateCaptureSession {
        @RequiresApi(Build.VERSION_CODES.LOLLIPOP)
        override fun createCaptureSession(
            camera: CameraDevice,
            previewSurface: Surface,
            outputs: ArrayList
        ): CaptureRequest.Builder {
            val previewCaptureRequest = camera.createCaptureRequest(CameraDevice.TEMPLATE_RECORD)
            previewCaptureRequest.addTarget(previewSurface)
            outputs.add(previewSurface)
            return previewCaptureRequest
        }
    }

最后是设置数据回调,从代码中可以看到有两种回调,一种是5.0以上版本使用Camera2开启摄像头,一种是兼容5.0以下的Camera开启摄像头;音频方面没有什么限制,只是5.0以上回调的是编码后的音频数据,5.0以下是未编码的音频数据

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
            //音视频编码数据回调
            setOnCodingVideoCallback(onCodingVideoCallback)
        }else{
            //老相机数据回调
            setOnOldVideoCallback(onOldVideoCallback)
        }

完整代码

class LiveBroadcastActivity : BaseVideoActivity() {

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

    override fun init() {
        //开启surfaceTextureListener监听
        textureView.surfaceTextureListener = surfaceTextureListener

        //切换相机
        switchCamera.setOnClickListener {
            switchCamera()
        }

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
            //创建预览的回调
            setOnCreateCaptureSession(onCreateCaptureSession)
            //音视频编码数据回调
            setOnCodingVideoCallback(onCodingVideoCallback)
        } else {
            //老相机数据回调
            setOnOldVideoCallback(onOldVideoCallback)
        }
    }

    /**
     * 创建预览的回调
     */
    private val onCreateCaptureSession = object : OnCreateCaptureSession {
        @RequiresApi(Build.VERSION_CODES.LOLLIPOP)
        override fun createCaptureSession(
            camera: CameraDevice,
            previewSurface: Surface,
            outputs: ArrayList
        ): CaptureRequest.Builder {
            val previewCaptureRequest = camera.createCaptureRequest(CameraDevice.TEMPLATE_RECORD)
            previewCaptureRequest.addTarget(previewSurface)
            outputs.add(previewSurface)
            return previewCaptureRequest
        }
    }

    /**
     * 老相机数据回调
     */
    private val onOldVideoCallback = object : OnOldVideoCallback {
        //发送老相机的视频数据
        override fun oldSendVideoData(data: ByteArray) {
            println("")
        }

        //老相机的音频数据
        override fun oldSendAudioData(data: ByteArray, len: Int) {
            println("")
        }
    }

    /**
     * 音视频编码数据回调
     */
    private val onCodingVideoCallback = object : OnCodingVideoCallback {
        //发送编码后的视频数据
        override fun sendVideoPacket(videoBuffers: ByteBuffer) {
            println("")
        }

        //发送编码后的音频数据
        override fun sendAudioPacket(audioBuffer: ByteBuffer) {
            println("")
        }
    }

    /**
     * 自动修改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 = previewSize.width.toFloat() / previewSize.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
            }
        }
    }
}

以上便是使用示例,很简洁,文末为基类完整代码

基类

import android.Manifest
import android.annotation.SuppressLint
import android.content.Context
import android.content.Intent
import android.content.pm.PackageManager
import android.graphics.ImageFormat
import android.graphics.SurfaceTexture
import android.hardware.Camera
import android.hardware.camera2.*
import android.media.*
import android.net.Uri
import android.os.Build
import android.os.Handler
import android.os.HandlerThread
import android.provider.Settings
import android.support.annotation.RequiresApi
import android.support.v4.content.ContextCompat
import android.support.v7.app.AlertDialog
import android.util.Size
import android.view.Surface
import android.view.TextureView
import kotlinx.coroutines.*
import org.jetbrains.annotations.NotNull
import java.nio.ByteBuffer

abstract class BaseVideoActivity : BaseActivity() {

    private lateinit var mBackgroundThread: HandlerThread
    private var mBackgroundHandler: Handler? = null

    //摄像头管理类
    private lateinit var cameraManager: CameraManager

    //摄像头id列表
    private lateinit var cameraIdList: Array

    //第几个摄像头
    private var index = 0

    //当前摄像头id
    private lateinit var cameraId: String

    //Surface集合
    private lateinit var outputs: ArrayList

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

    //Session
    private var cameraCaptureSession: CameraCaptureSession? = null

    //预览Surface
    private lateinit var previewSurface: Surface

    //预览CaptureRequest.Builder
    private lateinit var previewCaptureRequest: CaptureRequest.Builder

    //相机预览分辨率
    lateinit var previewSize: Size

    //视频编码器
    private lateinit var videoEncoder: MediaCodec
    //视频编码器输入Surface
    private lateinit var inputSurface: Surface

    private var onOldVideoCallback: OnOldVideoCallback? = null

    interface OnOldVideoCallback {
        //发送老相机的视频数据
        fun oldSendVideoData(data: ByteArray)

        fun oldSendAudioData(data: ByteArray, len: Int)
    }

    /**
     * 老相机数据回调
     */
    fun setOnOldVideoCallback(onOldVideoCallback: OnOldVideoCallback) {
        this.onOldVideoCallback = onOldVideoCallback
    }

    private var onCodingVideoCallback: OnCodingVideoCallback? = null

    interface OnCodingVideoCallback {
        //发送编码后的视频数据
        fun sendVideoPacket(videoBuffers: ByteBuffer)
        //发送编码后的音频数据
        fun sendAudioPacket(audioBuffer: ByteBuffer)
    }

    @RequiresApi(Build.VERSION_CODES.LOLLIPOP)
    fun setOnCodingVideoCallback(onCodingVideoCallback: OnCodingVideoCallback) {
        this.onCodingVideoCallback = onCodingVideoCallback
    }

    private var onCreateCaptureSession: OnCreateCaptureSession? = null

    interface OnCreateCaptureSession {
        fun createCaptureSession(
            camera: CameraDevice,
            previewSurface:Surface,
            outputs: ArrayList
        ):CaptureRequest.Builder
    }

    //创建预览的回调
    @RequiresApi(Build.VERSION_CODES.LOLLIPOP)
    fun setOnCreateCaptureSession(onCreateCaptureSession: OnCreateCaptureSession) {
        this.onCreateCaptureSession = onCreateCaptureSession
    }

    /**
     * 获取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()
                //设置预览尺寸
                previewSize = setPreviewSize(surfaceTexture, cameraCharacteristics)

                previewSurface = Surface(surfaceTexture)
            }
        }

        override fun onSurfaceTextureUpdated(surface: SurfaceTexture?) {

        }

        override fun onSurfaceTextureDestroyed(surfaceTexture: SurfaceTexture?): Boolean {
            //关闭老相机API预览
            if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
                stopPreview()
            }else{
                previewSurface.release()
            }
            return true
        }

        @SuppressLint("Recycle")
        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()

                //设置预览尺寸
                previewSize = setPreviewSize(surfaceTexture, cameraCharacteristics)

                //获取预览Surface
                previewSurface = Surface(surfaceTexture)

                //是否开启视频编码
                createSurfaceVideoEncoder()

                //是否开启音频编码
                createAudioEncoder()

                //开启摄像头
                openCamera()
            } else {
                oldSurfaceTexture = surfaceTexture
                //开启老相机API预览
                startPreview()
            }
        }
    }

    /**
     * 初始化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 size = getPreviewSize(cameraCharacteristics)
        surfaceTexture.setDefaultBufferSize(size.width, size.height)
        return size
    }

    /**
     * 获取预览尺寸
     * 参数2:预览尺寸比例的集合,按加入顺序寻找预览尺寸并返回
     */
    @RequiresApi(Build.VERSION_CODES.LOLLIPOP)
    fun getPreviewSize(@NotNull cameraCharacteristics: CameraCharacteristics): Size {
        val aspectRatios = ArrayList()
        aspectRatios.add(16.toFloat() / 9)
        aspectRatios.add(4.toFloat() / 3)
        aspectRatios.add(18.toFloat() / 9)

        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 = ArrayList()

            if (onCreateCaptureSession == null){
                //创建一个录像的CaptureRequest
                previewCaptureRequest = camera.createCaptureRequest(CameraDevice.TEMPLATE_RECORD)
                previewCaptureRequest.addTarget(previewSurface)
                previewCaptureRequest.addTarget(inputSurface)

                outputs.add(previewSurface)
                outputs.add(inputSurface)
            }else{
                previewCaptureRequest = onCreateCaptureSession!!.createCaptureSession(camera,previewSurface,outputs)
            }

            //创建一个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的回调
     */
    @RequiresApi(Build.VERSION_CODES.LOLLIPOP)
    val mSessionCallback = object : CameraCaptureSession.StateCallback() {

        override fun onConfigured(session: CameraCaptureSession) {
            cameraCaptureSession = session

            session.setRepeatingRequest(
                previewCaptureRequest.build(),
                captureCallback,
                getBackgroundHandler()
            )

            //开始录音
            startRecording()
        }

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

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

    /**
     * 创建Camera2编码器,Surface为输入源
     */
    @RequiresApi(Build.VERSION_CODES.LOLLIPOP)
    fun createSurfaceVideoEncoder() {
        //视频编码器
        videoEncoder = MediaCodec.createEncoderByType("video/avc")
        // 创建视频MediaFormat
        val videoFormat =
            MediaFormat.createVideoFormat("video/avc", previewSize.width, previewSize.height)

        // 指定编码器颜色格式
        videoFormat.setInteger(
            MediaFormat.KEY_COLOR_FORMAT,
            MediaCodecInfo.CodecCapabilities.COLOR_FormatSurface
        )
        // 仅编码器指定比特率
        videoFormat.setInteger(MediaFormat.KEY_BIT_RATE, 480000)//4* 1024

        // 编码器必须指定帧率
        videoFormat.setInteger(MediaFormat.KEY_FRAME_RATE, 25)
        // 指定关键帧时间间隔
//        videoFormat.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, 5)
        //BITRATE_MODE_CBR输出码率恒定,BITRATE_MODE_CQ保证图像质量,BITRATE_MODE_VBR图像复杂则码率高,图像简单则码率低
        videoFormat.setInteger(
            MediaFormat.KEY_BITRATE_MODE,
            MediaCodecInfo.EncoderCapabilities.BITRATE_MODE_VBR
        )
        videoFormat.setInteger(
            MediaFormat.KEY_COMPLEXITY,
            MediaCodecInfo.EncoderCapabilities.BITRATE_MODE_VBR
        )

        videoEncoder.configure(videoFormat, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE)

        //获取输入Surface
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
            inputSurface = MediaCodec.createPersistentInputSurface()
            videoEncoder.setInputSurface(inputSurface)
            videoEncoder.setCallback(videoEncoderCallback, getBackgroundHandler())
        } else {
            inputSurface = videoEncoder.createInputSurface()
            videoEncoder.setCallback(videoEncoderCallback)
        }
        videoEncoder.start()
    }

    /**
     * 硬解码的异步回调
     */
    @RequiresApi(Build.VERSION_CODES.LOLLIPOP)
    private val videoEncoderCallback = object : MediaCodec.Callback() {
        override fun onOutputBufferAvailable(
            codec: MediaCodec,
            index: Int,
            info: MediaCodec.BufferInfo
        ) {
            when {
                index == MediaCodec.INFO_TRY_AGAIN_LATER ->
                    showToast("超时")
                index >= 0 -> {
                    try {
                        val encoderOutputBuffers = videoEncoder.getOutputBuffer(index)
                        if (encoderOutputBuffers != null) {
                            onCodingVideoCallback?.sendVideoPacket(encoderOutputBuffers)
                        }
                        videoEncoder.releaseOutputBuffer(index, true)
                    } catch (e: IllegalStateException) {
                        e.printStackTrace()
                    }
                }
            }
        }

        override fun onInputBufferAvailable(codec: MediaCodec, index: Int) {

        }

        override fun onOutputFormatChanged(codec: MediaCodec, format: MediaFormat) {

        }

        override fun onError(codec: MediaCodec, e: MediaCodec.CodecException) {
            showToast("${e.message}")
        }

    }

    /**
     * 切换摄像头
     */
    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()
            for (surface in outputs) {
                surface.release()
            }
            videoEncoder.stop()
            videoEncoder.release()
            audioEncoder.stop()
            audioEncoder.release()
            endRecording()
            releaseBackgroundThread()
        } else {
            stopPreview()
            endRecording()
            oldSurfaceTexture.release()
        }
    }

    //老相机id
    private var oldCameraId = Camera.CameraInfo.CAMERA_FACING_BACK
    //老相机SurfaceTexture
    private lateinit var oldSurfaceTexture: SurfaceTexture
    //老相机数据存储数组
    private lateinit var oldBuffers: ByteArray
    //老相机
    private lateinit var oldCamera: Camera
    //老相机预览尺寸
    lateinit var oldSize: Camera.Size

    /**
     * 老相机API开始预览
     */
    private fun startPreview() {
        // 打开摄像头并将展示方向旋转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)

        startRecording()
        oldCamera.startPreview()
    }

    /**
     * 获取老相机预览数据回调
     */
    private val previewCallback = Camera.PreviewCallback { data, camera ->
        camera?.addCallbackBuffer(oldBuffers)
        onOldVideoCallback?.oldSendVideoData(data)
    }

    /**
     * 停止预览
     */
    fun stopPreview() {
        oldCamera.stopPreview()
        oldCamera.release()
    }

    /**
     * 老camera切换摄像头
     */
    private fun oldSwitchCamera() {
        oldCameraId = if (oldCameraId == Camera.CameraInfo.CAMERA_FACING_BACK) {
            Camera.CameraInfo.CAMERA_FACING_FRONT
        } else {
            Camera.CameraInfo.CAMERA_FACING_BACK
        }
        stopPreview()
        startPreview()
    }

    /**
     * 获取老相机API的预览大小
     */
    private fun getOldSize(sizeList: List): Camera.Size {
        val aspectRatios = ArrayList()
        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]
    }


    // 采样率
    private val sampleRateInHz = 44100

    // 音频通道 立体声:
    val stereo = AudioFormat.CHANNEL_IN_STEREO

    private lateinit var audioRecord: AudioRecord

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

    //录音线程
    private var recordingJob: Job? = null

    //音频编码器
    private lateinit var audioEncoder: MediaCodec
    //BufferInfo配置
    private lateinit var bufferInfo: MediaCodec.BufferInfo

    /**
     * 初始化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) {
                        startAudioEncoder(buffer, len)
                    }
                }
            } else {
                launch(Dispatchers.Main) {
                    showToast("请先初始化AudioRecord类")
                }
            }
        }
    }

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

    /**
     * 配置音频编码格式
     */
    fun createAudioEncoder() {
        bufferInfo = MediaCodec.BufferInfo()

        //音频编码器
        audioEncoder = MediaCodec.createEncoderByType("audio/mp4a-latm")
        // 创建音频MediaFormat,参数2:采样率,参数3:通道
        val audioFormat = MediaFormat.createAudioFormat("audio/mp4a-latm", 44100, 1)
        // 仅编码器指定比特率
        audioFormat.setInteger(MediaFormat.KEY_BIT_RATE, 96000)
        //可选的,输入数据缓冲区的最大大小
        audioFormat.setInteger(MediaFormat.KEY_MAX_INPUT_SIZE, bufferSizeInBytes)

        audioFormat.setInteger(
            MediaFormat.KEY_AAC_PROFILE,
            MediaCodecInfo.CodecProfileLevel.AACObjectLC
        )

        audioEncoder.configure(audioFormat, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE)

        audioEncoder.start()
    }

    /**
     * 开始音频编码
     */
    private fun startAudioEncoder(buffer: ByteArray, len: Int) {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
            try {
                //编码
                val inputIndex = audioEncoder.dequeueInputBuffer(0)
                if (inputIndex >= 0) {
                    val byteBuffer = audioEncoder.getInputBuffer(inputIndex)
                    if (byteBuffer != null) {
                        byteBuffer.clear()
                        byteBuffer.put(buffer)
                        byteBuffer.limit(len)// 设定上限值
                        audioEncoder.queueInputBuffer(
                            inputIndex,
                            0,
                            len,
                            System.nanoTime(),
                            0
                        ); // 第三个参数为时间戳,这里是使用当前
                    }
                }

                val outputIndex = audioEncoder.dequeueOutputBuffer(bufferInfo, 0)
                if (outputIndex >= 0) {
                    val byteBuffer = audioEncoder.getOutputBuffer(outputIndex)
                    if (byteBuffer != null) {
                        //音频编码
                        onCodingVideoCallback?.sendAudioPacket(byteBuffer)
                    }
                    audioEncoder.releaseOutputBuffer(outputIndex, false)
                }
            } catch (e: IllegalStateException) {
                e.printStackTrace()
            }
        } else {
            onOldVideoCallback?.oldSendAudioData(buffer, len)
        }
    }
}

 

你可能感兴趣的:(音视频开发)