本章内容主要是简述音视频硬编码模块和部分逻辑改动
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)
}
}
}