部分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" } }
解决方案建议
优先依赖CamcorderProfile而非原始硬件能力:
部分厂商的硬件规格和实际录像支持存在脱节,CamcorderProfile是系统提供的官方可用录像规格集合,即使相机和Codec标称支持4K,若CamcorderProfile中无对应规格,应放弃使用4K。- 对于
CamcorderProfile为null的设备,直接降级到次高可用分辨率(如1080P); - Android 12及以上版本,使用
CamcorderProfile.getAll()获取所有可用录像配置,优先选择其中最高分辨率的规格,而非直接使用相机输出的4K尺寸。
- 对于
添加分辨率兼容性预验证:
在尝试设置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 } }若预测试失败,自动降级到更低分辨率。
厂商特定兼容性处理:
- 针对Realme、Vivo等品牌设备,额外检查
CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES是否包含CAMERA_CAPABILITY_VIDEO_HIGH_PROFILE,若不包含则禁用4K选项; - 避免直接使用
supportedWidths.upper和supportedHeights.upper组合的尺寸,应从相机输出的MediaRecorder专用尺寸列表中筛选4K规格,而非任意组合宽高上限。
- 针对Realme、Vivo等品牌设备,额外检查
异常降级策略:
在捕获prepare failed异常时,直接切换到1080P分辨率重试,同时记录设备品牌、型号、系统版本等信息,用于后续兼容性适配优化。
内容的提问来源于stack exchange,提问作者user924
相关产品推荐
相关产品推荐

