一、概述
视频录制,在一般开发中很少遇到,大部分开发工作都是写写页面,请求接口,展示数据等等。真要遇到,可能采用第三方库实现,一来实现快速,二来可能觉得别人实现的比较好。特别是在开发周期很紧的情况下,一般都不会自己花时间实现。
其实最好是使用手机系统的录制视频,功能完善,稳定。实现起来最简单,简简单单几句代码:
//跳转系统的录制视频页面val intent = Intent(MediaStore.ACTION_VIDEO_CAPTURE)intent.putExtra(MediaStore.EXTRA_VIDEO_QUALITY,1)intent.putExtra(MediaStore.EXTRA_DURATION_LIMIT,30)//录制时长startActivityForResult(intent, 666)//打开手机的选择视频页
// val intent = Intent()
// intent.action = Intent.ACTION_PICK
// intent.data = MediaStore.Video.Media.EXTERNAL_CONTENT_URI
// startActivityForResult(intent,666)
然后在onActiityResult方法中接收录制好的视频路径处理即可。
但如果需求是像微信那样app内的录制视频,就不能使用系统自带录制功能,需要自己实现。
下面将我自己实现的,记录下来。这里也只是实现了一个简单的录制功能,甚至还有问题:前置摄像头录制视频是镜像的。
另外下面的实现不支持在Android6.0以下的手机上使用,因为使用到了API23的方法:MediaCodec.createPersistentInputSurface(),主要是为了能支持横屏录制的视频方向为横屏。
先看看演示效果:
二、实现方案和细节
使用的Camera2 和 MediaRecorder。
如果使用Camera1的话,可能会更简单一些,Camera2用起来确实相对麻烦一点。不过Camera1毕竟已经被弃用了,且使用Camera1打开相机比Camera2要耗时一些。
Camera2使用
- 用CameraManager获取相机Id列表cameraIdList,然后openCamera指定的相机id,打开相机
- 打开成功后,使用 CameraDevice.createCaptureSession 创建CameraCaptureSession
- 创建成功后,使用CameraCaptureSession.setRepeatingRequest 发起预览请求,它需要传入CaptureRequest,通过CameraDevice.captureRequest创建,CaptureRequest可以设置一些参数,对焦、曝光、闪光灯等等
第2步 createCaptureSession 时需要传入Surface列表。
这里传入了两个Surface,一个是预览使用,由SurfaceView提供。
另一个是录制使用,通过MediaCodec.createPersistentInputSurface() 创建,设置给MediaRecorder。
如果预览时不创建MediaRecorder,只传入预览Surface,等到点击录制时,才创建MediaRecorder,需要重新创建createCaptureSession,传入新的Surface,这样虽然可以,但是点击录制时会很慢,预览画面会断一下。
第2步传入的Surface列表,还需要在第3步中使用CaptureRequest.addTarget 添加,两个地方必须对应,不然发起预览请求时会报错。
MediaRecorder配置
因为使用的是Camera2,所以不能使用MediaRecorder.setCamera()。
替代方法是使用MediaRecorder.surface,前提是需要设置数据源为Surface
mediaRecorder.setVideoSource(MediaRecorder.VideoSource.SURFACE)
且需要在mediaRecorder.prepare之后,mediaRecorder.surface 才可用。
后面因为要支持横屏录制,没有采用 mediaRecorder.surface 。
如果进入页面开启相机预览时手机竖屏,点击录制时手机横屏,因为在预览时就创建了mediaRecorder,并且setOrientationHint确定了视频方向,无法再改变(只能prepare之前设置),这时录制的视频方向肯定就不对。
要改变视频方向,只能重新创建 mediaRecorder ,但是重新创建mediaRecorder,同时也重新创建了一个新的Sueface,需要重新createCaptureSession传入新的Sueface。(改成点击录制时,创建mediaRecorder,然后重新createCaptureSession,测试中也发现画面会断一下,效果不好)。
正因如此,最终改为使用 MediaCodec.createPersistentInputSurface() 创建 Surface,然后 setInputSurface 给 mediaRecorder。MediaCodec.createPersistentInputSurface()创建的Surface只有在mediaRecorder.prepare之后才可用。 在点击录制时,重新配置mediaRecorder,设置新的方向。这样虽然mediaRecorder重新配置了,但是Surface还是同一个。
var mediaRecorder = MediaRecorder()recordSurface = MediaCodec.createPersistentInputSurface()var recordSurface = mRecordSurfaceif (recordSurface == null) {recordSurface = MediaCodec.createPersistentInputSurface()mRecordSurface = recordSurface}mediaRecorder.setInputSurface(recordSurface)mediaRecorder.setAudioSource(MediaRecorder.AudioSource.MIC)//数据源来之surfacemediaRecorder.setVideoSource(MediaRecorder.VideoSource.SURFACE)//设置QUALITY_HIGH,可以提高视频的录制质量(文件也会变大),但是不能设置编码格式和帧率等参数,否则报错val profile = CamcorderProfile.get(CamcorderProfile.QUALITY_480P)mediaRecorder.setProfile(profile)mediaRecorder.setMaxFileSize(0)mediaRecorder.setVideoSize(width, height)//视频方向mediaRecorder.setOrientationHint(mRecordVideoOrientation)val parentFile = externalCacheDir ?: cacheDirval fileName = "${System.currentTimeMillis()}.mp4"//不设置setOutputFile prepare时会报错mediaRecorder.setOutputFile(parentFile.absolutePath + File.separator + fileName)//prepare之后recordSurface才能用mediaRecorder.prepare()
三、全部代码
/*** 录制视频* 支持后置、前置摄像头切换(但前置摄像头录制视频是镜像的,需要翻转) TODO* Android 6.0以下不支持* 支持横屏录制* 2023/03/17*/
@RequiresApi(Build.VERSION_CODES.M)
class VideoRecord23Activity : AppCompatActivity(), SurfaceHolder.Callback,View.OnClickListener {companion object {const val DURATION = "duration"const val FILE_NAME = "name"const val FILE_PATH = "path"}private val requestPermissionCode = 52private val requestActivityCode = 10/*** mCountDownMsg 录制进度倒计时msg,一秒钟发送一次* mStartRecordMsg 开始录制* mCameraOpenFailMsg 相机打开失败* mCameraPreviewFailMsg 相机预览失败* mRecordErrorMsg 录制出现错误* 在 mCountDownHandler(主线程的Handler)中处理*/private val mCountDownMsg = 19private val mStartRecordMsg = 20private val mCameraOpenFailMsg = 21private val mCameraPreviewFailMsg = 22private val mRecordErrorMsg = 23private lateinit var mSurfaceView :SurfaceViewprivate lateinit var mRecordProgressBar: ProgressBarprivate lateinit var mRecordStateIv: ImageViewprivate lateinit var mFlashlightIv: ImageViewprivate lateinit var mSwitchCameraIv: ImageView/*** 录制视频文件路径*/@Volatileprivate var mFilePath: String? = null/*** 录制的视频文件名*/@Volatileprivate var mFileName: String? = null/*** 预览画面尺寸,和视频录制尺寸*/@Volatileprivate var mRecordSize: Size? = null/*** 相机方向*/private var mCameraOrientation: Int = 0/*** 录制视频的方向,随着手机方向的改变而改变*/@Volatileprivate var mRecordVideoOrientation: Int = 0/*** 默认打开后置相机 LENS_FACING_BACK* 可以切换为前置相机 LENS_FACING_FRONT*/private var mFensFacing = CameraCharacteristics.LENS_FACING_BACK/*** 预览Surface*/@Volatileprivate var mPreviewSurface: Surface? = null/*** 录制Surface*/@Volatileprivate var mRecordSurface: Surface? = null@Volatileprivate var mCameraDevice: CameraDevice? = null@Volatileprivate var mCameraCaptureSession: CameraCaptureSession? = null@Volatileprivate var mCaptureRequest: CaptureRequest.Builder? = nullprivate var mOrientationEventListener: OrientationEventListener? = null@Volatileprivate var mMediaRecorder: MediaRecorder? = null/*** 是否是录制中的状态* true:录制中*/@Volatileprivate var mRecordingState = false/*** 是否录制完成。从手动点击开始录制到手动点击停止录制(或者录制时长倒计时到了),为录制完成,值为true。其他情况为false*/private var mRecordComplete = false/*** 闪光灯状态* true 开启* false 关闭*/private var mFlashlightState = false/*** 是否可以录制* 录制完成,跳转播放页面后,返回时点击的完成,不能录制* 其他情况都为可以录制*/private var mRecordable = true/*** 录制最大时长,时间到了之后录制完成* 单位:秒*/private var mMaxRecordDuration = 30/*** 已录制的时长*/private var mCurrentRecordDuration = 0override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)setContentView(R.layout.activity_video_record)mSurfaceView = findViewById(R.id.surfaceView)mSurfaceView.holder.addCallback(this)mRecordStateIv = findViewById(R.id.recordStateIv)mRecordProgressBar = findViewById(R.id.recordProgressBar)mFlashlightIv = findViewById(R.id.flashlightIv)mSwitchCameraIv = findViewById(R.id.switchIv)mRecordStateIv.setOnClickListener(this)mFlashlightIv.setOnClickListener(this)mSwitchCameraIv.setOnClickListener(this)initOrientationEventListener()mMaxRecordDuration = intent.getIntExtra(DURATION, 30)mSurfaceView.scaleX = -1f}override fun surfaceCreated(holder: SurfaceHolder?) {if (mRecordable) {mRecordComplete = falsemFlashlightState = falsemFlashlightIv.setImageResource(R.drawable.flashlight_off)checkPermissionAndOpenCamera()mOrientationEventListener?.enable()}}override fun surfaceChanged(holder: SurfaceHolder?, format: Int, width: Int, height: Int) {}override fun surfaceDestroyed(holder: SurfaceHolder?) {}@SuppressLint("MissingPermission")override fun onClick(v: View) {when (v.id) {R.id.recordStateIv -> {if (mRecordingState) {//停止录制stopRecord()mRecordComplete = true//跳转预览页面openPlayActivity()} else {startRecord()mOrientationEventListener?.disable()}}R.id.flashlightIv -> {val captureRequest = mCaptureRequest ?: returnval cameraCaptureSession = mCameraCaptureSession ?: returnif (mFlashlightState) {//闪光灯开启,点击关闭captureRequest[CaptureRequest.FLASH_MODE] = CaptureRequest.FLASH_MODE_OFFmFlashlightIv.setImageResource(R.drawable.flashlight_off)} else {//闪关灯关闭,点击开启captureRequest[CaptureRequest.FLASH_MODE] = CaptureRequest.FLASH_MODE_TORCHmFlashlightIv.setImageResource(R.drawable.flashlight_on)}mFlashlightState = !mFlashlightStatecameraCaptureSession.stopRepeating()mCameraCaptureSession?.setRepeatingRequest(captureRequest.build(), mCameraCaptureSessionCaptureCallback, mRecordThreadHandler)}R.id.switchIv -> {if (mRecordingState) {//正在录制中Toast.makeText(this, "正在录制", Toast.LENGTH_SHORT).show()return}if (mFensFacing == CameraCharacteristics.LENS_FACING_BACK) {//当前打开的是后置摄像头,切换到前置摄像头mFensFacing = CameraCharacteristics.LENS_FACING_FRONTclose()openCamera()} else if (mFensFacing == CameraCharacteristics.LENS_FACING_FRONT) {//当前打开的是前置摄像头,切换到后置摄像头mFensFacing = CameraCharacteristics.LENS_FACING_BACKclose()openCamera()}}}}private fun startRecord() {mRecordThreadHandler.sendEmptyMessage(mStartRecordMsg)mRecordingState = truemRecordStateIv.setImageResource(R.drawable.record_start_state_bg)mCurrentRecordDuration = 0//开始录制倒计时mUiHandler.sendEmptyMessageDelayed(mCountDownMsg, 1000)}private fun stopRecord() {//视图变为 停止录制状态mRecordingState = falsemRecordStateIv.setImageResource(R.drawable.record_stop_state_bg)mRecordProgressBar.progress = 0mUiHandler.removeMessages(mCountDownMsg)val mediaRecorder = mMediaRecorder ?: returntry {mediaRecorder.stop()} catch (t: Throwable) {t.printStackTrace()}}/*** 检查相机和录音与权限,并打开相机*/private fun checkPermissionAndOpenCamera(){//录制音频权限okval audioOk = ContextCompat.checkSelfPermission(this, Manifest.permission.RECORD_AUDIO) == PackageManager.PERMISSION_GRANTED//相机权限okval cameraOk = ContextCompat.checkSelfPermission(this, Manifest.permission.CAMERA)== PackageManager.PERMISSION_GRANTEDif (audioOk && cameraOk) {openCamera()} else if (!audioOk && !cameraOk) {val array = arrayOf(Manifest.permission.RECORD_AUDIO, Manifest.permission.CAMERA)ActivityCompat.requestPermissions(this, array, requestPermissionCode)} else if (!audioOk && cameraOk) {val array = arrayOf(Manifest.permission.RECORD_AUDIO)ActivityCompat.requestPermissions(this, array, requestPermissionCode)} else if (audioOk && !cameraOk) {val array = arrayOf(Manifest.permission.CAMERA)ActivityCompat.requestPermissions(this, array, requestPermissionCode)}}@SuppressLint("MissingPermission")override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<out String>, grantResults: IntArray) {super.onRequestPermissionsResult(requestCode, permissions, grantResults)if (requestCode == requestPermissionCode) {for (i in grantResults) {if (i != PackageManager.PERMISSION_GRANTED) {Toast.makeText(this, "请开启相机和录音权限", Toast.LENGTH_SHORT).show()finish()return}}openCamera()}}override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {super.onActivityResult(requestCode, resultCode, data)if (requestCode == requestActivityCode) {when (resultCode) {//确认Activity.RESULT_OK -> {val fileName = mFileName ?: returnval filePath = mFilePath ?: return//不能再录制mRecordable = falseval intent = Intent()//把录制的视频文件名、文件路径传给外面调用的页面intent.putExtra(FILE_NAME, fileName)intent.putExtra(FILE_PATH, filePath)setResult(Activity.RESULT_OK, intent)finish()}//重新录制Activity.RESULT_CANCELED -> {//删除文件,重新录制deleteRecordFile()}}}}/*** 页面暂停时,关闭相机,停止录制*/override fun onStop() {super.onStop()close()mOrientationEventListener?.disable()mRecordThreadHandler.removeMessages(mStartRecordMsg)}override fun onDestroy() {super.onDestroy()mRecordThreadHandler.looper.quit()mPreviewSurface?.release()mRecordSurface?.release()mMediaRecorder?.release()mMediaRecorder = null}/*** 准备录制相关处理*/@RequiresPermission(Manifest.permission.CAMERA)fun openCamera() {if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {//使用context可能会出现内存泄漏(红米手机上),CameraManager会一直持有contextval cameraManager = applicationContext.getSystemService(CAMERA_SERVICE) as? CameraManagerval cameraIdList = cameraManager?.cameraIdListif (cameraManager == null || cameraIdList == null || cameraIdList.isEmpty()) {Toast.makeText(this, "无法使用设备相机", Toast.LENGTH_SHORT).show()finish()return}for (id in cameraIdList) {var cameraCharacteristics: CameraCharacteristics? = nulltry {cameraCharacteristics = cameraManager.getCameraCharacteristics(id)} catch (t: Throwable) {t.printStackTrace()}if (cameraCharacteristics == null) {continue}val fensFacing = cameraCharacteristics.get(CameraCharacteristics.LENS_FACING)if (fensFacing != mFensFacing) {continue}val level = cameraCharacteristics.get(CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL)val capabilities = cameraCharacteristics.get(CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES) mCameraOrientation = cameraCharacteristics.get(CameraCharacteristics.SENSOR_ORIENTATION) ?: 0val map = cameraCharacteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP) ?: continue
// //获取预览支持的分辨率
// val outputSizes = map.getOutputSizes(SurfaceHolder::class.java)
// Log.i("TAG", "prepareRecord: outputSizes ${Arrays.toString(outputSizes)}")//获取录制支持的分辨率val recorderSizes = map.getOutputSizes(MediaRecorder::class.java)
// Log.i("TAG", "prepareRecord: recorderSizes ${Arrays.toString(recorderSizes)}")val recordSize = getRecordSize(recorderSizes)mRecordSize = recordSizeresizeSurfaceSize(recordSize.width, recordSize.height)mSurfaceView.holder.setFixedSize(recordSize.width, recordSize.height)try {cameraManager.openCamera(id, mCameraDeviceStateCallback, mRecordThreadHandler)} catch (t: Throwable) {t.printStackTrace()Toast.makeText(this, "相机打开失败,请关闭重试", Toast.LENGTH_SHORT).show()}break}} else {//6.0以下Toast.makeText(this, "Android系统版本太低不支持", Toast.LENGTH_SHORT).show()finish()}}private val mCameraDeviceStateCallback: CameraDevice.StateCallback by lazy(LazyThreadSafetyMode.NONE) {object : CameraDevice.StateCallback() {override fun onOpened(camera: CameraDevice) {mCameraDevice = cameraval recordSize = mRecordSize ?: return//预览surfaceval previewSurface = mSurfaceView.holder.surfacemPreviewSurface = previewSurfacesetupMediaRecorder(recordSize.width, recordSize.height, false)val recordSurface = mRecordSurfacemRecordSurface = recordSurfaceval surfaceList = listOf(previewSurface, recordSurface)camera.createCaptureSession(surfaceList, mCameraCaptureSessionStateCallback, mRecordThreadHandler)}override fun onDisconnected(camera: CameraDevice) {//相机连接断开if (mCameraDevice != null) {close()} else {camera.close()}}override fun onError(camera: CameraDevice, error: Int) {camera.close()mUiHandler.sendEmptyMessage(mCameraOpenFailMsg)}}}private val mCameraCaptureSessionStateCallback: CameraCaptureSession.StateCallback by lazy {object : CameraCaptureSession.StateCallback(){override fun onConfigured(session: CameraCaptureSession) {mCameraCaptureSession = sessionval camera = mCameraDevice ?: returnval previewSurface = mPreviewSurface ?: returnval recordSurface = mRecordSurface ?: returnval captureRequest = camera.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW)mCaptureRequest = captureRequestcaptureRequest.addTarget(previewSurface)captureRequest.addTarget(recordSurface)//对焦captureRequest.set(CaptureRequest.CONTROL_AF_MODE,CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_VIDEO)//自动曝光captureRequest.set(CaptureRequest.CONTROL_AE_MODE,CaptureRequest.CONTROL_AE_MODE_ON)//进行重复请求录制预览session.setRepeatingRequest(captureRequest.build(), mCameraCaptureSessionCaptureCallback, mRecordThreadHandler)}override fun onConfigureFailed(session: CameraCaptureSession) {session.close()mUiHandler.sendEmptyMessage(mCameraPreviewFailMsg)}}}private val mCameraCaptureSessionCaptureCallback: CameraCaptureSession.CaptureCallback by lazy(LazyThreadSafetyMode.NONE){object :CameraCaptureSession.CaptureCallback(){override fun onCaptureCompleted(session: CameraCaptureSession, request: CaptureRequest, result: TotalCaptureResult) {super.onCaptureCompleted(session, request, result)//这个方法在预览过长中,会一直被回调
// Log.i("TAG", "onCaptureCompleted thread ${Thread.currentThread().name}")}override fun onCaptureFailed(session: CameraCaptureSession, request: CaptureRequest, failure: CaptureFailure) {super.onCaptureFailed(session, request, failure)}}}/*** 录制线程的Handler*/private val mRecordThreadHandler: Handler by lazy(LazyThreadSafetyMode.NONE) {val recordThread = HandlerThread("RecordVideoThread")recordThread.start()object : Handler(recordThread.looper) {override fun handleMessage(msg: Message) {if (isFinishing || isDestroyed) returnwhen (msg.what) {mStartRecordMsg -> {//开始录制if (!mRecordingState) {//不是开始录制状态,returnreturn}val recordSize = mRecordSize ?: returntry {//重新配置MediaRecorder,因为用户刚打开页面时的手机方向,和点击录制时的手机方向可能不一样,所以重新配置。注意是为了支持横屏录制的视频为横屏视频,不然都是竖屏视频setupMediaRecorder(recordSize.width, recordSize.height, true)//视图变为录制状态mMediaRecorder?.start()} catch (t: Throwable) {t.printStackTrace()//录制出现错误mUiHandler.sendEmptyMessage(mRecordErrorMsg)}}}}}}/*** ui线程Handler 处理录制倒计时,相机打开失败相关消息*/private val mUiHandler: Handler by lazy(LazyThreadSafetyMode.NONE) {object : Handler(Looper.getMainLooper()) {override fun handleMessage(msg: Message?) {if (isFinishing || isDestroyed) {return}when(msg?.what){mCountDownMsg -> {mCurrentRecordDuration += 1val progress = (mCurrentRecordDuration * 1f / mMaxRecordDuration * 100 + 0.5f).toInt()mRecordProgressBar.progress = progressif (mCurrentRecordDuration >= mMaxRecordDuration) {//录制时间到了,停止录制stopRecord()mRecordComplete = true//跳转预览页面openPlayActivity()} else {sendEmptyMessageDelayed(mCountDownMsg, 1000)}}mCameraOpenFailMsg -> {Toast.makeText(this@VideoRecord23Activity, "相机打开失败,请关闭重试", Toast.LENGTH_SHORT).show()}mCameraPreviewFailMsg -> {Toast.makeText(this@VideoRecord23Activity, "相机预览失败,请关闭重试", Toast.LENGTH_SHORT).show()}}}}}/*** 创建并配置 MediaRecorder* @param width 视频宽度* @param height 视频高度* @param outputFileCreated 输出文件是否已经创建;第一次prepare时,文件已经创建了,开始录制时,不用再次创建文件*/private fun setupMediaRecorder(width: Int, height: Int, outputFileCreated: Boolean): MediaRecorder {var mediaRecorder = mMediaRecorderif (mediaRecorder == null) {mediaRecorder = MediaRecorder()mMediaRecorder = mediaRecorder} else {mediaRecorder.reset()}var recordSurface = mRecordSurfaceif (recordSurface == null) {recordSurface = MediaCodec.createPersistentInputSurface()mRecordSurface = recordSurface}mediaRecorder.setInputSurface(recordSurface)mediaRecorder.setAudioSource(MediaRecorder.AudioSource.MIC)//数据源来之surfacemediaRecorder.setVideoSource(MediaRecorder.VideoSource.SURFACE)//设置QUALITY_HIGH,可以提高视频的录制质量(文件也会变大),但是不能设置编码格式和帧率等参数,否则报错val profile = CamcorderProfile.get(CamcorderProfile.QUALITY_480P)mediaRecorder.setProfile(profile)mediaRecorder.setMaxFileSize(0)mediaRecorder.setVideoSize(width, height)//视频方向mediaRecorder.setOrientationHint(mRecordVideoOrientation)//录制文件没有创建,创建文件if (!outputFileCreated) {val parentFile = externalCacheDir ?: cacheDirval fileName = "${System.currentTimeMillis()}.mp4"mFileName = fileNamemFilePath = parentFile.absolutePath + File.separator + fileName}//不设置setOutputFile prepare时会报错mediaRecorder.setOutputFile(mFilePath)//prepare之后recordSurface才能用mediaRecorder.prepare()return mediaRecorder}/*** 页面关闭或不在前台时,停止录制、释放相机*/private fun close() {if (mRecordingState) {//停止录制stopRecord()}if (!mRecordComplete) {//没有录制完成,或者没有开始录制过(MediaRecorder prepare时会创建文件),删除录制的文件deleteRecordFile()}//释放相机val previewSurface = mPreviewSurfaceif (previewSurface != null) {mCaptureRequest?.removeTarget(previewSurface)}val recordSurface = mRecordSurfaceif (recordSurface != null) {mCaptureRequest?.removeTarget(recordSurface)}mCameraCaptureSession?.close()mCameraDevice?.close()mCaptureRequest = nullmCameraCaptureSession = nullmCameraDevice = null}/*** 删除录制的文件*/private fun deleteRecordFile() {val filePath = mFilePath ?: returntry {val file = File(filePath)if (file.exists()) {file.delete()}mFilePath = null} catch (t: Throwable) {t.printStackTrace()}}/*** 获取录制的视频尺寸* @param sizes 支持的尺寸列表*/private fun getRecordSize(sizes: Array<Size>): Size {//参考尺寸 1280*720val compareWidth = 1280val compareHeight = 720var resultSize = sizes[0]var minDiffW = Int.MAX_VALUEvar minDiffH = Int.MAX_VALUEfor (size in sizes) {if (size.width == compareWidth && size.height == compareHeight) {resultSize = sizebreak}//找到最接近 1280*720的sizeval diffW = abs(size.width - compareWidth)val diffH = abs(size.height - compareHeight)if (diffW < minDiffW && diffH < minDiffH) {minDiffW = diffWminDiffH = diffHresultSize = size}}return resultSize}/*** 根据视频宽高,修改surfaceView的宽高,来适应预览尺寸** @param width 预览宽度* @param height 预览高度*/private fun resizeSurfaceSize(height: Int, width: Int) {val displayW: Int = mSurfaceView.widthval displayH: Int = mSurfaceView.heightif (displayW == 0 || displayH == 0) returnvar ratioW = 1fvar ratioH = 1fif (width != displayW) {ratioW = width * 1f / displayW}if (height != displayH) {ratioH = height * 1f / displayH}var finalH = displayHvar finalW = displayWif (ratioW >= ratioH) {finalH = (height / ratioW).toInt()} else {finalW = (width / ratioH).toInt()}val layoutParams = mSurfaceView.layoutParamsif (layoutParams.width == finalW && layoutParams.height == finalH) {return}layoutParams.width = finalWlayoutParams.height = finalHmSurfaceView.layoutParams = layoutParams}/*** 监听手机方向改变,计算录制时的视频方向。横屏录制时,视频横屏。竖屏录制时,视频竖屏*/private fun initOrientationEventListener() {val orientationEventListener = object : OrientationEventListener(this) {override fun onOrientationChanged(orientation: Int) {if (orientation == ORIENTATION_UNKNOWN) returnval rotation = (orientation + 45) / 90 * 90if (mFensFacing == CameraCharacteristics.LENS_FACING_BACK) {//后置摄像头mRecordVideoOrientation = (mCameraOrientation + rotation) % 360} else if (mFensFacing == CameraCharacteristics.LENS_FACING_FRONT) {//前置摄像头mRecordVideoOrientation = mCameraOrientation - rotation}}}mOrientationEventListener = orientationEventListener}/*** 跳转录制视频预览页面*/private fun openPlayActivity() {//val intent = Intent(this, VideoPlayActivity::class.java)//intent.putExtra(VideoPlayActivity.FILE_PATH, mFilePath)//startActivity(intent)}
}