You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用Camera2 Kotlin API创建MediaRecorder并关联已有TextureView

Android Camera2 结合 TextureView 实现视频录制

下面是基于Camera2 API结合TextureView实现视频录制的完整方案,解决你关联MediaRecorder的问题:

一、先搞定权限

首先在AndroidManifest.xml里声明必要权限:

<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<!-- Android 13+ 用这两个替代旧存储权限 -->
<uses-permission android:name="android.permission.WRITE_MEDIA_VIDEO" />
<uses-permission android:name="android.permission.READ_MEDIA_VIDEO" />
<!-- Android 12及以下保留 -->
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" android:maxSdkVersion="32" />

注意:必须动态申请这些权限,确保调用相机前权限已授予。

二、初始化MediaRecorder

写一个方法配置录制参数,指定输出路径、编码格式等:

private fun initMediaRecorder() {
    mediaRecorder = MediaRecorder().apply {
        setAudioSource(MediaRecorder.AudioSource.MIC)
        setVideoSource(MediaRecorder.VideoSource.SURFACE)
        setOutputFormat(MediaRecorder.OutputFormat.MPEG_4)
        
        // 生成视频输出路径(示例:外部存储Movies目录)
        val outputFile = File(
            Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_MOVIES),
            "record_${System.currentTimeMillis()}.mp4"
        )
        setOutputFile(outputFile.absolutePath)
        
        setVideoEncoder(MediaRecorder.VideoEncoder.H264)
        setAudioEncoder(MediaRecorder.AudioEncoder.AAC)
        setVideoSize(1920, 1080) // 建议和相机支持的分辨率匹配
        setVideoFrameRate(30)
        setVideoEncodingBitRate(10 * 1024 * 1024) // 10Mbps保证视频质量
        
        prepare() // 必须调用prepare完成初始化
    }
}

三、修改相机会话,同时关联预览和录制Surface

修改你原来的open_camera方法,创建CaptureSession时要同时加入TextureView的预览Surface和MediaRecorder的录制Surface:

@SuppressLint("MissingPermission")
fun openCamera() {
    cameraManager.openCamera(camIdWithFlash, object : CameraDevice.StateCallback() {
        override fun onOpened(camera: CameraDevice) {
            cameraDevice = camera

            // 先初始化MediaRecorder
            initMediaRecorder()

            // 获取两个关键Surface:预览(TextureView)、录制(MediaRecorder)
            val previewSurface = Surface(textureView.surfaceTexture)
            val recordSurface = mediaRecorder!!.surface

            // 构建预览请求
            capReq = cameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW)
            capReq.addTarget(previewSurface)

            // 创建包含双Surface的相机会话
            cameraDevice.createCaptureSession(
                listOf(previewSurface, recordSurface),
                object : CameraCaptureSession.StateCallback() {
                    override fun onConfigured(session: CameraCaptureSession) {
                        cameraCaptureSession = session
                        // 启动预览
                        cameraCaptureSession.setRepeatingRequest(
                            capReq.build(),
                            null,
                            handler
                        )
                    }

                    override fun onConfigureFailed(session: CameraCaptureSession) {
                        // 配置失败的处理,比如弹个提示
                    }
                },
                handler
            )
        }

        override fun onDisconnected(camera: CameraDevice) {
            camera.close()
            cameraDevice = null
        }

        override fun onError(camera: CameraDevice, error: Int) {
            camera.close()
            cameraDevice = null
        }
    }, handler)
}

四、实现开始/停止录制的逻辑

添加两个方法控制录制状态切换:

private var isRecording = false

fun startRecording() {
    if (isRecording || cameraDevice == null || cameraCaptureSession == null) return
    isRecording = true

    // 构建录制专用的CaptureRequest,用TEMPLATE_RECORD模板
    val recordReq = cameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_RECORD).apply {
        addTarget(Surface(textureView.surfaceTexture!!))
        addTarget(mediaRecorder!!.surface)
        // 设置自动曝光,保证录制时光线正常
        set(CaptureRequest.CONTROL_AE_MODE, CaptureRequest.CONTROL_AE_MODE_ON_AUTO_FLASH)
        // 需要开闪光灯的话,加这行:
        // set(CaptureRequest.FLASH_MODE, CaptureRequest.FLASH_MODE_TORCH)
    }

    // 启动录制
    mediaRecorder?.start()
    // 切换到录制请求
    cameraCaptureSession?.setRepeatingRequest(
        recordReq.build(),
        null,
        handler
    )
}

fun stopRecording() {
    if (!isRecording || mediaRecorder == null) return
    isRecording = false

    // 停止并重置MediaRecorder
    mediaRecorder?.apply {
        stop()
        reset()
        release()
    }
    mediaRecorder = null

    // 切回预览模式
    val previewReq = cameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW).apply {
        addTarget(Surface(textureView.surfaceTexture!!))
    }
    cameraCaptureSession?.setRepeatingRequest(
        previewReq.build(),
        null,
        handler
    )
    // 重新初始化MediaRecorder,为下次录制做准备
    initMediaRecorder()
}

五、关键注意事项

  • 要在TextureView.SurfaceTextureListener的onSurfaceTextureAvailable回调里调用openCamera,确保Surface已就绪。
  • 页面销毁时记得释放资源:
override fun onDestroy() {
    super.onDestroy()
    cameraCaptureSession?.stopRepeating()
    cameraCaptureSession?.close()
    cameraDevice?.close()
    mediaRecorder?.release()
}
  • Android 10+建议用MediaStore保存视频,避免存储权限问题。
  • 如果遇到分辨率不兼容,可以通过CameraCharacteristics获取相机支持的最佳分辨率。

内容的提问来源于stack exchange,提问作者Akigomon505

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.26 22:28:20