MediaRecorder - java.io.IOException: prepare failed - for 4K resolutions on some devices

227 views Asked by At

So I noticed the following exception on some devices (usually Chinese-brand devices only, especially Realme and Vivo but also Motorola as well and some Samsung devices of A serires) when using 4K resolution 3840x2160 for video recording:

MediaRecorder java.io.IOException: prepare failed. at android.media.MediaRecorder._prepare(Native Method) at android.media.MediaRecorder.prepare(MediaRecorder.java:1356)

I filter camera resolutions by the following max size: MediaCodecInfo.VideoCapabilitiesvideoCapabilities.supportedWidths.upper and MediaCodecInfo.VideoCapabilitiesvideoCapabilities.supportedHeights.upper (for "video/avc" mimeType for example or hevc, depends on which I set for MediaRecorder)

So I don't understand why it doesn't work for such devices.

Didn't notice such issues with Samsung S series (A is very bad with other issues as well, basically a garbage in many cases) or Pixel devices when using 4k resolution for video recording.

Any ideas?

UPDATE

Gathered some info about users' devices (camera, mediacodec, device performance info)

All the data of main back camera ("0" id in Camera 2 API) expect the latest one

Google, Pixel 7 Pro, cheetah, google, Android 13, API 33:

  • Camera max resolution - 3840x2160
  • CamcorderProfile max resolution - 3840x2160
  • MediaCodecInfo.VideoCapabilities max resolution - 3840x3840
  • DevicePerformance: The most premium experience
  • OK. Premium Device Performance. Of course... it's Pixel, not a Chinese-brand device, everything should work fine :)

samsung, SM-S916B (Galaxy S23 Plus), kalama, Android 13, API 33:

  • Camera max resolution - 4080x3060
  • CamcorderProfile max resolution - 3840x2160
  • MediaCodecInfo.VideoCapabilities max resolution - 8192x8192
  • DevicePerformance: Experience functional

OK. Perfomance is just Experience functional and not premium, but still it works even with 4080x3060. Though CamcorderProfile is 3840x2160 but camera resolution max is higher... So both CamcorderProfile and DevicePerformance are useless because this Samsung device can work even with higher resolution.

samsung, SM-S908B (Galaxy S22 Ultra), Android 13, API 33:

  • Camera max resolution - 4000x3000
  • CamcorderProfile max resolution - 3840x2160
  • MediaCodecInfo.VideoCapabilities max resolution - 8192x8192
  • DevicePerformance: Experience functional

OK. Same as with SM-S916B (Galaxy S23 Plus). Also checked "2" camera id (ultra wide lens) and it also has 4000x3000 and MediaRecorder can record such resolution but Camcorder returns 1920x1080 maximum for such camera :D also CamcorderProfile.hasProfile(2, CamcorderProfile.QUALITY_2160P) returns false :)

Xiaomi, M1908C3JGG, biloba, Redmi, Android 13:

  • Camera max resolution - 3840x2160
  • CamcorderProfile max resolution - null
  • MediaCodecInfo.VideoCapabilities max resolution - 3840x2160
  • DevicePerformance: Experience functional

NOT OK! MediaRecorder IOException: prepare failed with 4K! Works with Full HD resolution normally but not with 4K. Camcoder is null.

Xiaomi, Redmi Note 9 Pro Max, Android 12, API 31:

  • Camera max resolution - 3840x2160
  • CamcorderProfile max resolution - 3840x2160
  • MediaCodecInfo.VideoCapabilities max resolution - 4096x2160
  • DevicePerformance: Experience functional

OK, camera max is not higher CamcorderProfile or VideoCapabilities max size

Xiaomi, POCO X2, Android 11, API 30:

  • Camera max resolution - 3840x2160
  • CamcorderProfile max resolution - 3840x2160
  • MediaCodecInfo.VideoCapabilities max resolution - 4096x2160
  • DevicePerformance: Experience functional

OK

Wingtech, TMAF025G, S98122AA1, T-Mobile, Android 12, API 31:

  • Camera max resolution - 2560x1440
  • CamcorderProfile max resolution - 2560x1440
  • MediaCodecInfo.VideoCapabilities max resolution - 2560x1440
  • DevicePerformance: Experience functional

OK

vivo, V2037, k69v1_64, Android 13:

  • Camera max resolution - 3840x2160
  • CamcorderProfile max resolution - null
  • MediaCodecInfo.VideoCapabilities max resolution - 3840x2160
  • DevicePerformance: Experience functional

NOT OK! MediaRecorder IOException: prepare failed with 4K! Works with Full HD resolution normally but not with 4K. Camcoder is null.

realme, RMX3710, RM6769, Android 13, API 33:

  • Camera max resolution - 3840x2160
  • CamcorderProfile max resolution - null
  • MediaCodecInfo.VideoCapabilities max resolution - 3840x2160
  • DevicePerformance: Experience functional

NOT OK! MediaRecorder IOException: prepare failed with 4K! Works with Full HD resolution normally but not with 4K. Camcoder is null.

OnePlus, CPH2487, taro, Android 13, API 33:

  • Camera max resolution - 4096x2160
  • CamcorderProfile max resolution - null
  • MediaCodecInfo.VideoCapabilities max resolution - 8192x8192
  • DevicePerformance: The most premium experience.

OK. Premium Device Performance, interesting... camcoder is null but at least camera size's width is not higher than mediacodec's width

WALTON, ORBIT Y21, Android 12, API 31 ("17" camera id selected by user instead of "0"):

  • Camera max resolution - 1920x1080 (it's not 2K or 4K, but just ineteresting value of CamcorderProfile)
  • CamcorderProfile max resolution - 320x240 ???
  • MediaCodecInfo.VideoCapabilities max resolution - 1920x3840
  • DevicePerformance: Experience functional

Camcoder 320x240, definitely NOT OK, mb it's normal value for "0" camera id, only got info of currently selected camera. Also MediaCodecInfo.VideoCapability' height is higher than width (1920x3840)

I also have older info about many other devices (Xiaomi, OnePlus, Motorola, Samsung A Series, Vivo, Realme) where 4K failed with MediaRecorder Prepare exception, but for that time I didn't include info about camcoder, device performance and mediacodec yet. But Camera2 supports 4K resolution fine for such devices, only MediaRecorder fails.

So the issue is very common. Only Samsung S and Pixel are stable 100% :) so we can use camera max resolution without checking camcorder such devices

to get camera resolutions:

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)
        }
    } catch (e: Throwable) {
        e.printStackTrace()
        emptyList()
    }
    return resolutions
}

generally if Camera API returns 4k then device can use such resolution, didn't notice issue with that, only with MediaRecorder

to get high supported resolution using CamcorderProfile (which I guess is supported by Camera2 API and MediaRecorder?):

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
}

to get max supported resolution for video encoding using MediaCodecInfo (which is supported by MediaRecorder or just MediaCodec, or both?):

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)
        }
    }
}

to get device performance https://developer.android.com/topic/performance/performance-class:

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"
    }
}

So only MediaCodecInfo almost makes sense but still 4K fails with prepare exception with MediaRecorder on some devices, CamcorderProfile often can be null or resolution is lower than camera maximum resolution but still MediaRecorder works fine with such resolution :)

So I don't understand how to make it work.

I guess if a camera has 4K resolution then it should work for video recording as well logically (especially if mediacodec reports it as ok) but phone manufactures make buggy devices and there is nothing we can do as apps developers...

0

There are 0 answers