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?

Piet
  • 356
  • 3
  • 10
  • 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")) – Túlio Calazans Oct 14 '19 at 16:03

1 Answers1

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

Rafael Lima
  • 3,079
  • 3
  • 41
  • 105
  • Thanks for pointing to the [Android source](https://android.googlesource.com/platform/frameworks/base/+/72b6e76/media/java/android/media/MediaCodecInfo.java#2285). 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](https://android.googlesource.com/platform/frameworks/base/+/72b6e76/media/java/android/media/MediaCodecInfo.java#2318)). You can set it, if supported. – Emilie Sep 09 '21 at 12:40