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

部分Android设备4K录像MediaRecorder prepare失败IOException问题求助

4K录像MediaRecorder抛出IOException: prepare failed的兼容性问题

在Realme、Vivo等国产机型,以及摩托罗拉、三星A系列部分设备上,使用3840x2160的4K分辨率录像时,MediaRecorder会抛出java.io.IOException: prepare failed异常。

已尝试通过MediaCodecInfo.VideoCapabilities的supportedWidths.upper和supportedHeights.upper筛选相机分辨率,但问题依旧。三星S系列和Pixel设备的4K录像功能则完全正常。

设备测试情况

  • 小米M1908C3JGG、Vivo V2037、Realme RMX3710等设备:相机和MediaCodecInfo均标称支持4K,但CamcorderProfile为null,仅全高清录像可正常工作,4K录像触发异常;
  • 三星S23 Plus等设备:相机最大分辨率高于CamcorderProfile定义的规格,但4K录像仍可正常运行;
  • 仅三星S系列、Pixel设备的4K录像实现100%稳定。

现有相关代码

获取相机分辨率

fun getCameraResolutions(
    context: Context,
    cameraId: String
): List<Size> {
    val characteristics = getCameraCharacteristics(context, cameraId)
    val resolutions = try {
        val map = characteristics.get(
            CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP
        )!!
        var outputSizes = map.getOutputSizes(MediaRecorder::class.java)
        if (outputSizes.isEmpty()) {
            outputSizes = map.getOutputSizes(MediaCodec::class.java)
        }
        if (outputSizes.isEmpty()) {
            outputSizes = map.getOutputSizes(SurfaceTexture::class.java)
        }
        outputSizes.toList()
    } catch (e: Throwable) {
        e.printStackTrace()
        emptyList()
    }
    return resolutions
}

获取CamcorderProfile最大分辨率

fun getCamCoderMaxSize(cameraIdx: String): Size? {
    val size = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
        CamcorderProfile.getAll(
            cameraIdx,
            CamcorderProfile.QUALITY_HIGH
        )?.videoProfiles?.maxBy { it.width * it.height }?.let {
            Size(it.width, it.height)
        }
    } else {
        @Suppress("DEPRECATION")
        CamcorderProfile.get(CamcorderProfile.QUALITY_HIGH)?.let {
            Size(it.videoFrameWidth, it.videoFrameHeight)
        }
    }
    return size
}

获取MediaCodecInfo视频编码能力

private const val VIDEO_MIME_TYPE = "video/avc"

fun getVideoCodecInfo(): MediaCodecInfo.VideoCapabilities? {
    try {
        val codecInfos = MediaCodecList(MediaCodecList.ALL_CODECS).codecInfos
        val codecInfo = codecInfos.find { codecInfo ->
            codecInfo.supportedTypes.any {
                it.equals(VIDEO_MIME_TYPE, true)
            } && codecInfo.getCapabilitiesForType(
                VIDEO_MIME_TYPE
            ).colorFormats.any {
                it == MediaCodecInfo.CodecCapabilities.COLOR_FormatSurface
            }
        }!!
        return codecInfo.getCapabilitiesForType(VIDEO_MIME_TYPE).videoCapabilities
    } catch (e: Throwable) {
        e.printStackTrace()
        // doesn't work on some devices (hello from brand-China devices...)
        return null
    }
}
// and then to get max size:
val videoCapabilitySize: Size? = CodecUtil.getVideoCodecInfo()?.let { codec ->
    codec.supportedWidths?.upper?.let { width ->
        codec.supportedHeights?.upper?.let { height ->
            Size(width, height)
        }
    }
}

获取设备性能

val devicePerformance = DevicePerformance.create(context)
val devicePerformanceText = when {
    devicePerformance.mediaPerformanceClass >= Build.VERSION_CODES.S -> {
        // Performance class level 13 and above
        // Provide the most premium experience for highest performing devices
        "The most premium experience"
    }
    devicePerformance.mediaPerformanceClass == Build.VERSION_CODES.R -> {
        // Performance class level 12
        // Provide a high quality experience
        "High quality experience"
    }
    else -> {
        // Performance class level 11 or undefined
        // Remove extras to keep experience functional
        "Experience functional"
    }
}

解决方案建议

  1. 优先依赖CamcorderProfile而非原始硬件能力:
    部分厂商的硬件规格和实际录像支持存在脱节,CamcorderProfile是系统提供的官方可用录像规格集合,即使相机和Codec标称支持4K,若CamcorderProfile中无对应规格,应放弃使用4K。

    • 对于CamcorderProfile为null的设备,直接降级到次高可用分辨率(如1080P);
    • Android 12及以上版本,使用CamcorderProfile.getAll()获取所有可用录像配置,优先选择其中最高分辨率的规格,而非直接使用相机输出的4K尺寸。
  2. 添加分辨率兼容性预验证:
    在尝试设置4K分辨率前,通过MediaRecorder进行轻量预测试,确认该分辨率是否可正常初始化:

    fun isResolutionSupported(context: Context, cameraId: String, size: Size): Boolean {
        val recorder = MediaRecorder()
        val cameraProvider = ProcessCameraProvider.getInstance(context).get()
        val camera = cameraProvider.bindToLifecycle(context as LifecycleOwner, CameraSelector.DEFAULT_BACK_CAMERA)
        recorder.setCamera(camera)
        return try {
            recorder.setAudioSource(MediaRecorder.AudioSource.MIC)
            recorder.setVideoSource(MediaRecorder.VideoSource.SURFACE)
            recorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4)
            recorder.setVideoEncoder(MediaRecorder.VideoEncoder.H264)
            recorder.setAudioEncoder(MediaRecorder.AudioEncoder.AAC)
            recorder.setVideoSize(size.width, size.height)
            recorder.setOutputFile(File(context.cacheDir, "test.mp4").absolutePath)
            recorder.prepare()
            recorder.release()
            true
        } catch (e: IOException) {
            recorder.release()
            false
        }
    }
    

    若预测试失败,自动降级到更低分辨率。

  3. 厂商特定兼容性处理:

    • 针对Realme、Vivo等品牌设备,额外检查CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES是否包含CAMERA_CAPABILITY_VIDEO_HIGH_PROFILE,若不包含则禁用4K选项;
    • 避免直接使用supportedWidths.upper和supportedHeights.upper组合的尺寸,应从相机输出的MediaRecorder专用尺寸列表中筛选4K规格,而非任意组合宽高上限。
  4. 异常降级策略:
    在捕获prepare failed异常时,直接切换到1080P分辨率重试,同时记录设备品牌、型号、系统版本等信息,用于后续兼容性适配优化。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.14 06:05:55