diff options
author | Lajos Molnar <lajos@google.com> | 2014-08-12 18:48:14 -0700 |
---|---|---|
committer | Lajos Molnar <lajos@google.com> | 2014-08-15 17:35:30 -0700 |
commit | 981c344ee7a4472e2a331d66819c132267a681c6 (patch) | |
tree | 39a6ac33f70d3278ed9db0e8daa4d4e97b286d22 /media | |
parent | af059c65a16c7e2f88ecb15645a0acc3f6247356 (diff) | |
download | frameworks_base-981c344ee7a4472e2a331d66819c132267a681c6.zip frameworks_base-981c344ee7a4472e2a331d66819c132267a681c6.tar.gz frameworks_base-981c344ee7a4472e2a331d66819c132267a681c6.tar.bz2 |
media: minor fixes for MediaCodecInfo
- use equals() for Range singularity checks
- parse max-channel-count properly
- add platform defaults to G711 and GSM audio codecs
- allow xml to override platform limits if format,
profile or level is not supported (but recognized to be
possibly valid) by the platform
- set defaut format fields
Bug: 11990470
Change-Id: I06114e2d10dcc205a8a963605a01e6d4f6ecd0f0
Diffstat (limited to 'media')
-rw-r--r-- | media/java/android/media/MediaCodecInfo.java | 129 | ||||
-rw-r--r-- | media/java/android/media/Utils.java | 13 |
2 files changed, 107 insertions, 35 deletions
diff --git a/media/java/android/media/MediaCodecInfo.java b/media/java/android/media/MediaCodecInfo.java index b6e03f5..ae2d024 100644 --- a/media/java/android/media/MediaCodecInfo.java +++ b/media/java/android/media/MediaCodecInfo.java @@ -450,10 +450,16 @@ public final class MediaCodecInfo { } private void applyLevelLimits() { - if (mParent.getMime().equalsIgnoreCase( - MediaFormat.MIMETYPE_AUDIO_FLAC)) { + String mime = mParent.getMime(); + 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); } } @@ -514,11 +520,12 @@ public final class MediaCodecInfo { /** @hide */ public void setDefaultFormat(MediaFormat format) { - if (mQualityRange.getUpper() != mQualityRange.getLower() + // don't list trivial quality/complexity as default for now + if (!mQualityRange.getUpper().equals(mQualityRange.getLower()) && mDefaultQuality != null) { format.setInteger(MediaFormat.KEY_QUALITY, mDefaultQuality); } - if (mComplexityRange.getUpper() != mComplexityRange.getLower() + if (!mComplexityRange.getUpper().equals(mComplexityRange.getLower()) && mDefaultComplexity != null) { format.setInteger(MediaFormat.KEY_COMPLEXITY, mDefaultComplexity); } @@ -985,34 +992,34 @@ public final class MediaCodecInfo { if ((mParent.mError & ERROR_UNSUPPORTED) != 0) { // codec supports profiles that we don't know. - // Extend ranges clipped to platform limits. + // Use supplied values clipped to platform limits if (widths != null) { - mWidthRange = mWidthRange.extend(widths); + mWidthRange = SIZE_RANGE.intersect(widths); } if (heights != null) { - mHeightRange = mHeightRange.extend(heights); + mHeightRange = SIZE_RANGE.intersect(heights); } if (counts != null) { - mBlockCountRange = mBlockCountRange.extend( + mBlockCountRange = POSITIVE_INTEGERS.intersect( Utils.factorRange(counts, mBlockWidth * mBlockHeight / blockSize.getWidth() / blockSize.getHeight())); } if (blockRates != null) { - mBlocksPerSecondRange = mBlocksPerSecondRange.extend( + mBlocksPerSecondRange = POSITIVE_LONGS.intersect( Utils.factorRange(blockRates, mBlockWidth * mBlockHeight / blockSize.getWidth() / blockSize.getHeight())); } if (blockRatios != null) { - mBlockAspectRatioRange = mBlockAspectRatioRange.extend( + mBlockAspectRatioRange = POSITIVE_RATIONALS.intersect( Utils.scaleRange(blockRatios, mBlockHeight / blockSize.getHeight(), mBlockWidth / blockSize.getWidth())); } if (ratios != null) { - mAspectRatioRange = mAspectRatioRange.extend(ratios); + mAspectRatioRange = POSITIVE_RATIONALS.intersect(ratios); } if (frameRates != null) { - mFrameRateRange = mFrameRateRange.extend(frameRates); + mFrameRateRange = FRAME_RATE_RANGE.intersect(frameRates); } } else { // no unsupported profile/levels, so restrict values to known limits @@ -1235,7 +1242,6 @@ public final class MediaCodecInfo { Log.w(TAG, "Unrecognized level " + profileLevel.level + " for " + mime); errors |= ERROR_UNRECOGNIZED; - supported = false; } switch (profileLevel.profile) { case CodecProfileLevel.AVCProfileHigh: @@ -1245,7 +1251,7 @@ public final class MediaCodecInfo { case CodecProfileLevel.AVCProfileExtended: case CodecProfileLevel.AVCProfileHigh422: case CodecProfileLevel.AVCProfileHigh444: - Log.w(TAG, "Unrecognized profile " + Log.w(TAG, "Unsupported profile " + profileLevel.profile + " for " + mime); errors |= ERROR_UNSUPPORTED; supported = false; @@ -1271,7 +1277,9 @@ public final class MediaCodecInfo { int maxLengthInBlocks = (int)(Math.sqrt(maxBlocks * 8)); applyMacroBlockLimits( maxLengthInBlocks, maxLengthInBlocks, - maxBlocks, maxBlocksPerSecond, 16, 16, 1, 1); + maxBlocks, maxBlocksPerSecond, + 16 /* blockWidth */, 16 /* blockHeight */, + 1 /* widthAlignment */, 1 /* heightAlignment */); } else if (mime.equalsIgnoreCase(MediaFormat.MIMETYPE_VIDEO_MPEG4)) { int maxWidth = 11, maxHeight = 9, maxRate = 15; maxBlocks = 99; @@ -1369,7 +1377,9 @@ public final class MediaCodecInfo { maxRate = Math.max(FR, maxRate); } applyMacroBlockLimits(maxWidth, maxHeight, - maxBlocks, maxBlocksPerSecond, 16, 16, 1, 1); + maxBlocks, maxBlocksPerSecond, + 16 /* blockWidth */, 16 /* blockHeight */, + 1 /* widthAlignment */, 1 /* heightAlignment */); mFrameRateRange = mFrameRateRange.intersect(12, maxRate); } else if (mime.equalsIgnoreCase(MediaFormat.MIMETYPE_VIDEO_H263)) { int maxWidth = 11, maxHeight = 9, maxRate = 15; @@ -1432,7 +1442,9 @@ public final class MediaCodecInfo { maxRate = Math.max(FR, maxRate); } applyMacroBlockLimits(maxWidth, maxHeight, - maxBlocks, maxBlocksPerSecond, 16, 16, 1, 1); + maxBlocks, maxBlocksPerSecond, + 16 /* blockWidth */, 16 /* blockHeight */, + 1 /* widthAlignment */, 1 /* heightAlignment */); mFrameRateRange = Range.create(1, maxRate); } else if (mime.equalsIgnoreCase(MediaFormat.MIMETYPE_VIDEO_VP8) || mime.equalsIgnoreCase(MediaFormat.MIMETYPE_VIDEO_VP9)) { @@ -1465,6 +1477,12 @@ public final class MediaCodecInfo { } errors &= ~ERROR_NONE_SUPPORTED; } + + final int blockSize = + mime.equalsIgnoreCase(MediaFormat.MIMETYPE_VIDEO_VP8) ? 16 : 8; + applyMacroBlockLimits(Short.MAX_VALUE, Short.MAX_VALUE, + maxBlocks, maxBlocksPerSecond, blockSize, blockSize, + 1 /* widthAlignment */, 1 /* heightAlignment */); } else if (mime.equalsIgnoreCase(MediaFormat.MIMETYPE_VIDEO_HEVC)) { maxBlocks = 36864; maxBlocksPerSecond = maxBlocks * 15; @@ -1493,17 +1511,17 @@ public final class MediaCodecInfo { FR = 30; FS = 2228224; BR = 12000; break; case CodecProfileLevel.HEVCHighTierLevel4: FR = 30; FS = 2228224; BR = 30000; break; - case CodecProfileLevel.HEVCHighTierLevel41: - FR = 60; FS = 2228224; BR = 20000; break; case CodecProfileLevel.HEVCMainTierLevel41: + FR = 60; FS = 2228224; BR = 20000; break; + case CodecProfileLevel.HEVCHighTierLevel41: FR = 60; FS = 2228224; BR = 50000; break; - case CodecProfileLevel.HEVCHighTierLevel5: - FR = 30; FS = 8912896; BR = 25000; break; case CodecProfileLevel.HEVCMainTierLevel5: + FR = 30; FS = 8912896; BR = 25000; break; + case CodecProfileLevel.HEVCHighTierLevel5: FR = 30; FS = 8912896; BR = 100000; break; - case CodecProfileLevel.HEVCHighTierLevel51: - FR = 60; FS = 8912896; BR = 40000; break; case CodecProfileLevel.HEVCMainTierLevel51: + FR = 60; FS = 8912896; BR = 40000; break; + case CodecProfileLevel.HEVCHighTierLevel51: FR = 60; FS = 8912896; BR = 160000; break; case CodecProfileLevel.HEVCMainTierLevel52: FR = 120; FS = 8912896; BR = 60000; break; @@ -1513,13 +1531,13 @@ public final class MediaCodecInfo { FR = 30; FS = 35651584; BR = 60000; break; case CodecProfileLevel.HEVCHighTierLevel6: FR = 30; FS = 35651584; BR = 240000; break; - case CodecProfileLevel.HEVCHighTierLevel61: - FR = 60; FS = 35651584; BR = 120000; break; case CodecProfileLevel.HEVCMainTierLevel61: + FR = 60; FS = 35651584; BR = 120000; break; + case CodecProfileLevel.HEVCHighTierLevel61: FR = 60; FS = 35651584; BR = 480000; break; - case CodecProfileLevel.HEVCHighTierLevel62: - FR = 120; FS = 35651584; BR = 240000; break; case CodecProfileLevel.HEVCMainTierLevel62: + FR = 120; FS = 35651584; BR = 240000; break; + case CodecProfileLevel.HEVCHighTierLevel62: FR = 120; FS = 35651584; BR = 800000; break; default: Log.w(TAG, "Unrecognized level " @@ -1557,9 +1575,13 @@ public final class MediaCodecInfo { applyMacroBlockLimits( maxLengthInBlocks, maxLengthInBlocks, - maxBlocks, maxBlocksPerSecond, 8, 8, 1, 1); + maxBlocks, maxBlocksPerSecond, + 8 /* blockWidth */, 8 /* blockHeight */, + 1 /* widthAlignment */, 1 /* heightAlignment */); } else { Log.w(TAG, "Unsupported mime " + mime); + // using minimal bitrate here. should be overriden by + // info from media_codecs.xml maxBps = 64000; errors |= ERROR_UNSUPPORTED; } @@ -1628,21 +1650,26 @@ public final class MediaCodecInfo { if (mMime.toLowerCase().startsWith("audio/")) { mAudioCaps = AudioCapabilities.create(info, this); + mAudioCaps.setDefaultFormat(mDefaultFormat); } else if (mMime.toLowerCase().startsWith("video/")) { mVideoCaps = VideoCapabilities.create(info, this); } if (encoder) { mEncoderCaps = EncoderCapabilities.create(info, this); + mEncoderCaps.setDefaultFormat(mDefaultFormat); } for (Feature feat: getValidFeatures()) { - Integer yesNo = (Integer)map.get(MediaFormat.KEY_FEATURE_ + feat.mName); + String key = MediaFormat.KEY_FEATURE_ + feat.mName; + Integer yesNo = (Integer)map.get(key); if (yesNo == null) { continue; } else if (yesNo > 0) { mFlagsRequired |= feat.mValue; + mDefaultFormat.setInteger(key, 1); } else { mFlagsSupported |= feat.mValue; + mDefaultFormat.setInteger(key, 1); } // TODO restrict features by mFlagsVerified once all codecs reliably verify them } @@ -1652,10 +1679,14 @@ public final class MediaCodecInfo { * A class that supports querying the audio capabilities of a codec. */ public static final class AudioCapabilities extends BaseCapabilities { + private static final String TAG = "AudioCapabilities"; + private int[] mSampleRates; private Range<Integer>[] mSampleRateRanges; private int mMaxInputChannelCount; + private static final int MAX_INPUT_CHANNEL_COUNT = 30; + /** * Returns the array of supported sample rates if the codec * supports only discrete values. Otherwise, it returns @@ -1702,7 +1733,7 @@ public final class MediaCodecInfo { } private void initWithPlatformLimits() { - mMaxInputChannelCount = 30; + mMaxInputChannelCount = MAX_INPUT_CHANNEL_COUNT; // mBitrateRange = Range.create(1, 320000); mSampleRateRanges = new Range[] { Range.create(8000, 96000) }; mSampleRates = null; @@ -1758,7 +1789,7 @@ public final class MediaCodecInfo { // check if all values are discrete for (Range<Integer> range: mSampleRateRanges) { - if (range.getLower() != range.getUpper()) { + if (!range.getLower().equals(range.getUpper())) { mSampleRates = null; return; } @@ -1777,7 +1808,7 @@ public final class MediaCodecInfo { 8000, 11025, 12000, 16000, 22050, 24000, 32000, 44100, 48000 }; - bitRates = Range.create(8192, 327680); + bitRates = Range.create(8000, 320000); maxChannels = 2; } else if (mime.equalsIgnoreCase(MediaFormat.MIMETYPE_AUDIO_AMR_NB)) { sampleRates = new int[] { 8000 }; @@ -1807,11 +1838,23 @@ public final class MediaCodecInfo { } else if (mime.equalsIgnoreCase(MediaFormat.MIMETYPE_AUDIO_RAW)) { sampleRateRange = Range.create(1, 96000); bitRates = Range.create(1, 10000000); - maxChannels = 6; + maxChannels = 8; } else if (mime.equalsIgnoreCase(MediaFormat.MIMETYPE_AUDIO_FLAC)) { sampleRateRange = Range.create(1, 655350); // lossless codec, so bitrate is ignored maxChannels = 255; + } else if (mime.equalsIgnoreCase(MediaFormat.MIMETYPE_AUDIO_G711_ALAW) + || mime.equalsIgnoreCase(MediaFormat.MIMETYPE_AUDIO_G711_MLAW)) { + sampleRates = new int[] { 8000 }; + bitRates = Range.create(64000, 64000); + // platform allows multiple channels for this format + } else if (mime.equalsIgnoreCase(MediaFormat.MIMETYPE_AUDIO_MSGSM)) { + sampleRates = new int[] { 8000 }; + bitRates = Range.create(13000, 13000); + maxChannels = 1; + } else { + Log.w(TAG, "Unsupported mime " + mime); + mParent.mError |= ERROR_UNSUPPORTED; } // restrict ranges @@ -1832,7 +1875,7 @@ public final class MediaCodecInfo { } private void parseFromInfo(MediaFormat info) { - int maxInputChannels = 1; + int maxInputChannels = MAX_INPUT_CHANNEL_COUNT; Range<Integer> bitRates = POSITIVE_INTEGERS; if (info.containsKey("sample-rate-ranges")) { @@ -1844,7 +1887,8 @@ public final class MediaCodecInfo { limitSampleRates(rateRanges); } if (info.containsKey("max-channel-count")) { - maxInputChannels = info.getInteger("max-channel-count"); + maxInputChannels = Utils.parseIntSafely( + info.getString("max-channel-count"), maxInputChannels); } if (info.containsKey("bitrate-range")) { bitRates = bitRates.intersect( @@ -1854,6 +1898,21 @@ public final class MediaCodecInfo { } /** @hide */ + public void setDefaultFormat(MediaFormat format) { + // report settings that have only a single choice + if (mBitrateRange.getLower().equals(mBitrateRange.getUpper())) { + format.setInteger(MediaFormat.KEY_BIT_RATE, mBitrateRange.getLower()); + } + if (mMaxInputChannelCount == 1) { + // mono-only format + format.setInteger(MediaFormat.KEY_CHANNEL_COUNT, 1); + } + if (mSampleRates != null && mSampleRates.length == 1) { + format.setInteger(MediaFormat.KEY_SAMPLE_RATE, mSampleRates[0]); + } + } + + /** @hide */ public boolean supportsFormat(MediaFormat format) { Map<String, Object> map = format.getMap(); Integer sampleRate = (Integer)map.get(MediaFormat.KEY_SAMPLE_RATE); diff --git a/media/java/android/media/Utils.java b/media/java/android/media/Utils.java index 12910ec..df0daaf 100644 --- a/media/java/android/media/Utils.java +++ b/media/java/android/media/Utils.java @@ -210,6 +210,19 @@ class Utils { return fallback; } + static int parseIntSafely(Object o, int fallback) { + try { + String s = (String)o; + return Integer.parseInt(s); + } catch (ClassCastException e) { + } catch (NumberFormatException e) { + } catch (NullPointerException e) { + return fallback; + } + Log.w(TAG, "could not parse integer '" + o + "'"); + return fallback; + } + static Range<Integer> parseIntRange(Object o, Range<Integer> fallback) { try { String s = (String)o; |