How to check whether Android MediaCodec supports Constant Quality mode
Asked Answered
B

1

9

I have a screen recording app that uses a MediaCodec encoder to encode the video frames. Here's one way I retrieve the video-encoder:

videoCodec = MediaCodec.createEncoderByType(MediaFormat.MIMETYPE_VIDEO_AVC);

I then try to determine the best bitrate-mode that this encoder supports, with my order of preference being "Constant Quality" mode, Variable Bitrate mode, Constant Bitrate mode. This is how I try to do it:

MediaCodecInfo.CodecCapabilities capabilities = videoCodec.getCodecInfo().getCapabilitiesForType(MediaFormat.MIMETYPE_VIDEO_AVC);
MediaCodecInfo.EncoderCapabilities encoderCapabilities = capabilities.getEncoderCapabilities();

if (encoderCapabilities.isBitrateModeSupported(MediaCodecInfo.EncoderCapabilities.BITRATE_MODE_CQ)) {
    Timber.i("Setting bitrate mode to constant quality");
    videoFormat.setInteger(MediaFormat.KEY_BITRATE_MODE, MediaCodecInfo.EncoderCapabilities.BITRATE_MODE_CQ);
} else if (encoderCapabilities.isBitrateModeSupported(MediaCodecInfo.EncoderCapabilities.BITRATE_MODE_VBR)) {
    Timber.w("Setting bitrate mode to variable bitrate");
    videoFormat.setInteger(MediaFormat.KEY_BITRATE_MODE, MediaCodecInfo.EncoderCapabilities.BITRATE_MODE_VBR);
} else if (encoderCapabilities.isBitrateModeSupported(MediaCodecInfo.EncoderCapabilities.BITRATE_MODE_CBR)) {
    Timber.w("Setting bitrate mode to constant bitrate");
    videoFormat.setInteger(MediaFormat.KEY_BITRATE_MODE, MediaCodecInfo.EncoderCapabilities.BITRATE_MODE_CBR);
}

Running this on my Samsung Galaxy S7 ends up selecting VBR mode, i.e. Constant Quality mode is supposedly not supported. However, if I just set the BITRATE_MODE to Constant Quality, it not only works but in fact produces a better quality video than VBR mode.

So, if Constant Quality mode is apparently supported by this encoder, why do I get a false negative from isBitrateModeSupported()? Am I missing something here?

Bellyful answered 8/12, 2017 at 13:21 Comment(1)
In my work with MediaCodec and Samsung devices, I realised that there are some weird behaviours in these devices. I don't know how to fix it, but maybe you need to think in something special to them. So use: if(android.os.Build.MANUFACTURER.toLowerCase().equals("samsumg"))Factorage
R
1

It's super late but maybe i can help people that arrive here in the future.

As far as android 26, MediaCodec class will only accept BITRATE_MODE_CQ for MIMETYPE_AUDIO_FLAC codecs.

I dont know why but this is hard coded into the class:

    /**
             * Query whether a bitrate mode is supported.
             */
            public boolean isBitrateModeSupported(int mode) {
                for (Feature feat: bitrates) {
                    if (mode == feat.mValue) {
                        return (mBitControl & (1 << mode)) != 0;
                    }
                }
                return false;
            }

private void applyLevelLimits() {
            String mime = mParent.getMimeType();
            if (mime.equalsIgnoreCase(MediaFormat.MIMETYPE_AUDIO_FLAC)) {
                mComplexityRange = Range.create(0, 8);
                mBitControl = (1 << BITRATE_MODE_CQ);
            } else if (mime.equalsIgnoreCase(MediaFormat.MIMETYPE_AUDIO_AMR_NB)
                    || mime.equalsIgnoreCase(MediaFormat.MIMETYPE_AUDIO_AMR_WB)
                    || mime.equalsIgnoreCase(MediaFormat.MIMETYPE_AUDIO_G711_ALAW)
                    || mime.equalsIgnoreCase(MediaFormat.MIMETYPE_AUDIO_G711_MLAW)
                    || mime.equalsIgnoreCase(MediaFormat.MIMETYPE_AUDIO_MSGSM)) {
                mBitControl = (1 << BITRATE_MODE_CBR);
            }
        }

considering that BITRATE_MODE_CQ is 0 isBitrateModeSupported will only return true case MIMETYPE_AUDIO_FLAC is selected.

This is the answer why it returns false up to android lvl 26
why it is coded like this i wont know.

I guess a simple way to check is try to create the encoder with the format you wish and catch any possible exception

Radburn answered 14/10, 2019 at 15:52 Comment(1)
Thanks for pointing to the Android source. I don't think you've read it quite correctly though. applyLevelLimits() sets CQ for FLAC and CBR for the other audio codecs as the default values instead of VBR. Then the code goes on to set the passed in parameters on the line mBitControl |= parseBitrateMode(mode) (source). You can set it, if supported.Livingston

© 2022 - 2024 — McMap. All rights reserved.