media: fix issues with video profile levels am: 2fc9c6925a

am: 9e56ec51b0

* commit '9e56ec51b09803982f2c60075ab6523b69bd0933':
  media: fix issues with video profile levels

Change-Id: I15f18757181186524c63185e26536deddb774eb5
This commit is contained in:
Lajos Molnar
2016-06-02 04:03:58 +00:00
committed by android-build-merger

View File

@@ -651,6 +651,27 @@ public final class MediaCodecInfo {
continue; continue;
} }
} }
// MPEG4 levels are not completely ordered:
// Level1 support only implies Level0 (and not Level0b) support
if (mMime.equalsIgnoreCase(MediaFormat.MIMETYPE_VIDEO_MPEG4)) {
if (pl.level != level && pl.level == CodecProfileLevel.MPEG4Level1
&& level > CodecProfileLevel.MPEG4Level0) {
continue;
}
}
// HEVC levels incorporate both tiers and levels. Verify tier support.
if (mMime.equalsIgnoreCase(MediaFormat.MIMETYPE_VIDEO_HEVC)) {
boolean supportsHighTier =
(pl.level & CodecProfileLevel.HEVCHighTierLevels) != 0;
boolean checkingHighTier = (level & CodecProfileLevel.HEVCHighTierLevels) != 0;
// high tier levels are only supported by other high tier levels
if (checkingHighTier && !supportsHighTier) {
continue;
}
}
if (pl.level >= level) { if (pl.level >= level) {
// if we recognize the listed profile/level, we must also recognize the // if we recognize the listed profile/level, we must also recognize the
// profile/level arguments. // profile/level arguments.
@@ -1132,6 +1153,8 @@ public final class MediaCodecInfo {
private int mHeightAlignment; private int mHeightAlignment;
private int mSmallerDimensionUpperLimit; private int mSmallerDimensionUpperLimit;
private boolean mAllowMbOverride; // allow XML to override calculated limits
/** /**
* Returns the range of supported bitrates in bits/second. * Returns the range of supported bitrates in bits/second.
*/ */
@@ -1696,7 +1719,7 @@ public final class MediaCodecInfo {
Long.MAX_VALUE, blockSize.getWidth(), blockSize.getHeight(), Long.MAX_VALUE, blockSize.getWidth(), blockSize.getHeight(),
alignment.getWidth(), alignment.getHeight()); alignment.getWidth(), alignment.getHeight());
if ((mParent.mError & ERROR_UNSUPPORTED) != 0) { if ((mParent.mError & ERROR_UNSUPPORTED) != 0 || mAllowMbOverride) {
// codec supports profiles that we don't know. // codec supports profiles that we don't know.
// Use supplied values clipped to platform limits // Use supplied values clipped to platform limits
if (widths != null) { if (widths != null) {
@@ -1728,7 +1751,12 @@ public final class MediaCodecInfo {
mFrameRateRange = FRAME_RATE_RANGE.intersect(frameRates); mFrameRateRange = FRAME_RATE_RANGE.intersect(frameRates);
} }
if (bitRates != null) { if (bitRates != null) {
mBitrateRange = BITRATE_RANGE.intersect(bitRates); // only allow bitrate override if unsupported profiles were encountered
if ((mParent.mError & ERROR_UNSUPPORTED) != 0) {
mBitrateRange = BITRATE_RANGE.intersect(bitRates);
} else {
mBitrateRange = mBitrateRange.intersect(bitRates);
}
} }
} else { } else {
// no unsupported profile/levels, so restrict values to known limits // no unsupported profile/levels, so restrict values to known limits
@@ -1883,6 +1911,19 @@ public final class MediaCodecInfo {
int maxBlocks, long maxBlocksPerSecond, int maxBlocks, long maxBlocksPerSecond,
int blockWidth, int blockHeight, int blockWidth, int blockHeight,
int widthAlignment, int heightAlignment) { int widthAlignment, int heightAlignment) {
applyMacroBlockLimits(
1 /* minHorizontalBlocks */, 1 /* minVerticalBlocks */,
maxHorizontalBlocks, maxVerticalBlocks,
maxBlocks, maxBlocksPerSecond,
blockWidth, blockHeight, widthAlignment, heightAlignment);
}
private void applyMacroBlockLimits(
int minHorizontalBlocks, int minVerticalBlocks,
int maxHorizontalBlocks, int maxVerticalBlocks,
int maxBlocks, long maxBlocksPerSecond,
int blockWidth, int blockHeight,
int widthAlignment, int heightAlignment) {
applyAlignment(widthAlignment, heightAlignment); applyAlignment(widthAlignment, heightAlignment);
applyBlockLimits( applyBlockLimits(
blockWidth, blockHeight, Range.create(1, maxBlocks), blockWidth, blockHeight, Range.create(1, maxBlocks),
@@ -1892,10 +1933,12 @@ public final class MediaCodecInfo {
new Rational(maxHorizontalBlocks, 1))); new Rational(maxHorizontalBlocks, 1)));
mHorizontalBlockRange = mHorizontalBlockRange =
mHorizontalBlockRange.intersect( mHorizontalBlockRange.intersect(
1, maxHorizontalBlocks / (mBlockWidth / blockWidth)); Utils.divUp(minHorizontalBlocks, (mBlockWidth / blockWidth)),
maxHorizontalBlocks / (mBlockWidth / blockWidth));
mVerticalBlockRange = mVerticalBlockRange =
mVerticalBlockRange.intersect( mVerticalBlockRange.intersect(
1, maxVerticalBlocks / (mBlockHeight / blockHeight)); Utils.divUp(minVerticalBlocks, (mBlockHeight / blockHeight)),
maxVerticalBlocks / (mBlockHeight / blockHeight));
} }
private void applyLevelLimits() { private void applyLevelLimits() {
@@ -2005,7 +2048,7 @@ public final class MediaCodecInfo {
case CodecProfileLevel.MPEG2ProfileSimple: case CodecProfileLevel.MPEG2ProfileSimple:
switch (profileLevel.level) { switch (profileLevel.level) {
case CodecProfileLevel.MPEG2LevelML: case CodecProfileLevel.MPEG2LevelML:
FR = 30; W = 45; H = 36; MBPS = 48600; FS = 1620; BR = 15000; break; FR = 30; W = 45; H = 36; MBPS = 40500; FS = 1620; BR = 15000; break;
default: default:
Log.w(TAG, "Unrecognized profile/level " Log.w(TAG, "Unrecognized profile/level "
+ profileLevel.profile + "/" + profileLevel.profile + "/"
@@ -2018,7 +2061,7 @@ public final class MediaCodecInfo {
case CodecProfileLevel.MPEG2LevelLL: case CodecProfileLevel.MPEG2LevelLL:
FR = 30; W = 22; H = 18; MBPS = 11880; FS = 396; BR = 4000; break; FR = 30; W = 22; H = 18; MBPS = 11880; FS = 396; BR = 4000; break;
case CodecProfileLevel.MPEG2LevelML: case CodecProfileLevel.MPEG2LevelML:
FR = 30; W = 45; H = 36; MBPS = 48600; FS = 1620; BR = 15000; break; FR = 30; W = 45; H = 36; MBPS = 40500; FS = 1620; BR = 15000; break;
case CodecProfileLevel.MPEG2LevelH14: case CodecProfileLevel.MPEG2LevelH14:
FR = 60; W = 90; H = 68; MBPS = 183600; FS = 6120; BR = 60000; break; FR = 60; W = 90; H = 68; MBPS = 183600; FS = 6120; BR = 60000; break;
case CodecProfileLevel.MPEG2LevelHL: case CodecProfileLevel.MPEG2LevelHL:
@@ -2068,16 +2111,19 @@ public final class MediaCodecInfo {
maxBps = 64000; maxBps = 64000;
for (CodecProfileLevel profileLevel: profileLevels) { for (CodecProfileLevel profileLevel: profileLevels) {
int MBPS = 0, FS = 0, BR = 0, FR = 0, W = 0, H = 0; int MBPS = 0, FS = 0, BR = 0, FR = 0, W = 0, H = 0;
boolean strict = false; // true: W, H and FR are individual max limits
boolean supported = true; boolean supported = true;
switch (profileLevel.profile) { switch (profileLevel.profile) {
case CodecProfileLevel.MPEG4ProfileSimple: case CodecProfileLevel.MPEG4ProfileSimple:
switch (profileLevel.level) { switch (profileLevel.level) {
case CodecProfileLevel.MPEG4Level0: case CodecProfileLevel.MPEG4Level0:
strict = true;
FR = 15; W = 11; H = 9; MBPS = 1485; FS = 99; BR = 64; break; FR = 15; W = 11; H = 9; MBPS = 1485; FS = 99; BR = 64; break;
case CodecProfileLevel.MPEG4Level1: case CodecProfileLevel.MPEG4Level1:
FR = 30; W = 11; H = 9; MBPS = 1485; FS = 99; BR = 64; break; FR = 30; W = 11; H = 9; MBPS = 1485; FS = 99; BR = 64; break;
case CodecProfileLevel.MPEG4Level0b: case CodecProfileLevel.MPEG4Level0b:
FR = 30; W = 11; H = 9; MBPS = 1485; FS = 99; BR = 128; break; strict = true;
FR = 15; W = 11; H = 9; MBPS = 1485; FS = 99; BR = 128; break;
case CodecProfileLevel.MPEG4Level2: case CodecProfileLevel.MPEG4Level2:
FR = 30; W = 22; H = 18; MBPS = 5940; FS = 396; BR = 128; break; FR = 30; W = 22; H = 18; MBPS = 5940; FS = 396; BR = 128; break;
case CodecProfileLevel.MPEG4Level3: case CodecProfileLevel.MPEG4Level3:
@@ -2125,11 +2171,16 @@ public final class MediaCodecInfo {
case CodecProfileLevel.MPEG4ProfileCore: // 1-2 case CodecProfileLevel.MPEG4ProfileCore: // 1-2
case CodecProfileLevel.MPEG4ProfileAdvancedCore: // 1-4 case CodecProfileLevel.MPEG4ProfileAdvancedCore: // 1-4
case CodecProfileLevel.MPEG4ProfileSimpleScalable: // 0-2 case CodecProfileLevel.MPEG4ProfileSimpleScalable: // 0-2
case CodecProfileLevel.MPEG4ProfileAdvancedScalable: // 1-3
case CodecProfileLevel.MPEG4ProfileHybrid: // 1-2 case CodecProfileLevel.MPEG4ProfileHybrid: // 1-2
// Studio profiles are not supported by our codecs.
// Only profiles that can decode simple object types are considered.
// The following profiles are not able to.
case CodecProfileLevel.MPEG4ProfileBasicAnimated: // 1-2 case CodecProfileLevel.MPEG4ProfileBasicAnimated: // 1-2
case CodecProfileLevel.MPEG4ProfileScalableTexture: // 1 case CodecProfileLevel.MPEG4ProfileScalableTexture: // 1
case CodecProfileLevel.MPEG4ProfileSimpleFace: // 1-2 case CodecProfileLevel.MPEG4ProfileSimpleFace: // 1-2
case CodecProfileLevel.MPEG4ProfileAdvancedScalable: // 1-3
case CodecProfileLevel.MPEG4ProfileSimpleFBA: // 1-2 case CodecProfileLevel.MPEG4ProfileSimpleFBA: // 1-2
Log.i(TAG, "Unsupported profile " Log.i(TAG, "Unsupported profile "
+ profileLevel.profile + " for " + mime); + profileLevel.profile + " for " + mime);
@@ -2147,9 +2198,17 @@ public final class MediaCodecInfo {
maxBlocksPerSecond = Math.max(MBPS, maxBlocksPerSecond); maxBlocksPerSecond = Math.max(MBPS, maxBlocksPerSecond);
maxBlocks = Math.max(FS, maxBlocks); maxBlocks = Math.max(FS, maxBlocks);
maxBps = Math.max(BR * 1000, maxBps); maxBps = Math.max(BR * 1000, maxBps);
maxWidth = Math.max(W, maxWidth); if (strict) {
maxHeight = Math.max(H, maxHeight); maxWidth = Math.max(W, maxWidth);
maxRate = Math.max(FR, maxRate); maxHeight = Math.max(H, maxHeight);
maxRate = Math.max(FR, maxRate);
} else {
// assuming max 60 fps frame rate and 1:2 aspect ratio
int maxDim = (int)Math.sqrt(FS * 2);
maxWidth = Math.max(maxDim, maxWidth);
maxHeight = Math.max(maxDim, maxHeight);
maxRate = Math.max(Math.max(FR, 60), maxRate);
}
} }
applyMacroBlockLimits(maxWidth, maxHeight, applyMacroBlockLimits(maxWidth, maxHeight,
maxBlocks, maxBlocksPerSecond, maxBlocks, maxBlocksPerSecond,
@@ -2158,34 +2217,47 @@ public final class MediaCodecInfo {
mFrameRateRange = mFrameRateRange.intersect(12, maxRate); mFrameRateRange = mFrameRateRange.intersect(12, maxRate);
} else if (mime.equalsIgnoreCase(MediaFormat.MIMETYPE_VIDEO_H263)) { } else if (mime.equalsIgnoreCase(MediaFormat.MIMETYPE_VIDEO_H263)) {
int maxWidth = 11, maxHeight = 9, maxRate = 15; int maxWidth = 11, maxHeight = 9, maxRate = 15;
int minWidth = maxWidth, minHeight = maxHeight;
int minAlignment = 16;
maxBlocks = 99; maxBlocks = 99;
maxBlocksPerSecond = 1485; maxBlocksPerSecond = 1485;
maxBps = 64000; maxBps = 64000;
for (CodecProfileLevel profileLevel: profileLevels) { for (CodecProfileLevel profileLevel: profileLevels) {
int MBPS = 0, BR = 0, FR = 0, W = 0, H = 0; int MBPS = 0, BR = 0, FR = 0, W = 0, H = 0, minW = minWidth, minH = minHeight;
boolean strict = false; // true: support only sQCIF, QCIF (maybe CIF)
switch (profileLevel.level) { switch (profileLevel.level) {
case CodecProfileLevel.H263Level10: case CodecProfileLevel.H263Level10:
strict = true; // only supports sQCIF & QCIF
FR = 15; W = 11; H = 9; BR = 1; MBPS = W * H * FR; break; FR = 15; W = 11; H = 9; BR = 1; MBPS = W * H * FR; break;
case CodecProfileLevel.H263Level20: case CodecProfileLevel.H263Level20:
// only supports CIF, 0..QCIF strict = true; // only supports sQCIF, QCIF & CIF
FR = 30; W = 22; H = 18; BR = 2; MBPS = W * H * FR; break; FR = 30; W = 22; H = 18; BR = 2; MBPS = W * H * 15; break;
case CodecProfileLevel.H263Level30: case CodecProfileLevel.H263Level30:
// only supports CIF, 0..QCIF strict = true; // only supports sQCIF, QCIF & CIF
FR = 30; W = 22; H = 18; BR = 6; MBPS = W * H * FR; break; FR = 30; W = 22; H = 18; BR = 6; MBPS = W * H * FR; break;
case CodecProfileLevel.H263Level40: case CodecProfileLevel.H263Level40:
// only supports CIF, 0..QCIF strict = true; // only supports sQCIF, QCIF & CIF
FR = 30; W = 22; H = 18; BR = 32; MBPS = W * H * FR; break; FR = 30; W = 22; H = 18; BR = 32; MBPS = W * H * FR; break;
case CodecProfileLevel.H263Level45: case CodecProfileLevel.H263Level45:
// only implies level 10 support // only implies level 10 support
FR = 30; W = 11; H = 9; BR = 2; MBPS = W * H * FR; break; strict = profileLevel.profile == CodecProfileLevel.H263ProfileBaseline
|| profileLevel.profile ==
CodecProfileLevel.H263ProfileBackwardCompatible;
if (!strict) {
minW = 1; minH = 1; minAlignment = 4;
}
FR = 15; W = 11; H = 9; BR = 2; MBPS = W * H * FR; break;
case CodecProfileLevel.H263Level50: case CodecProfileLevel.H263Level50:
// only supports 50fps for H > 15 // only supports 50fps for H > 15
minW = 1; minH = 1; minAlignment = 4;
FR = 60; W = 22; H = 18; BR = 64; MBPS = W * H * 50; break; FR = 60; W = 22; H = 18; BR = 64; MBPS = W * H * 50; break;
case CodecProfileLevel.H263Level60: case CodecProfileLevel.H263Level60:
// only supports 50fps for H > 15 // only supports 50fps for H > 15
minW = 1; minH = 1; minAlignment = 4;
FR = 60; W = 45; H = 18; BR = 128; MBPS = W * H * 50; break; FR = 60; W = 45; H = 18; BR = 128; MBPS = W * H * 50; break;
case CodecProfileLevel.H263Level70: case CodecProfileLevel.H263Level70:
// only supports 50fps for H > 30 // only supports 50fps for H > 30
minW = 1; minH = 1; minAlignment = 4;
FR = 60; W = 45; H = 36; BR = 256; MBPS = W * H * 50; break; FR = 60; W = 45; H = 36; BR = 256; MBPS = W * H * 50; break;
default: default:
Log.w(TAG, "Unrecognized profile/level " + profileLevel.profile Log.w(TAG, "Unrecognized profile/level " + profileLevel.profile
@@ -2208,6 +2280,18 @@ public final class MediaCodecInfo {
+ profileLevel.profile + " for " + mime); + profileLevel.profile + " for " + mime);
errors |= ERROR_UNRECOGNIZED; errors |= ERROR_UNRECOGNIZED;
} }
if (strict) {
// Strict levels define sub-QCIF min size and enumerated sizes. We cannot
// express support for "only sQCIF & QCIF (& CIF)" using VideoCapabilities
// but we can express "only QCIF (& CIF)", so set minimume size at QCIF.
// minW = 8; minH = 6;
minW = 11; minH = 9;
} else {
// any support for non-strict levels (including unrecognized profiles or
// levels) allow custom frame size support beyond supported limits
// (other than bitrate)
mAllowMbOverride = true;
}
errors &= ~ERROR_NONE_SUPPORTED; errors &= ~ERROR_NONE_SUPPORTED;
maxBlocksPerSecond = Math.max(MBPS, maxBlocksPerSecond); maxBlocksPerSecond = Math.max(MBPS, maxBlocksPerSecond);
maxBlocks = Math.max(W * H, maxBlocks); maxBlocks = Math.max(W * H, maxBlocks);
@@ -2215,11 +2299,21 @@ public final class MediaCodecInfo {
maxWidth = Math.max(W, maxWidth); maxWidth = Math.max(W, maxWidth);
maxHeight = Math.max(H, maxHeight); maxHeight = Math.max(H, maxHeight);
maxRate = Math.max(FR, maxRate); maxRate = Math.max(FR, maxRate);
minWidth = Math.min(minW, minWidth);
minHeight = Math.min(minH, minHeight);
} }
applyMacroBlockLimits(maxWidth, maxHeight, // unless we encountered custom frame size support, limit size to QCIF and CIF
// using aspect ratio.
if (!mAllowMbOverride) {
mBlockAspectRatioRange =
Range.create(new Rational(11, 9), new Rational(11, 9));
}
applyMacroBlockLimits(
minWidth, minHeight,
maxWidth, maxHeight,
maxBlocks, maxBlocksPerSecond, maxBlocks, maxBlocksPerSecond,
16 /* blockWidth */, 16 /* blockHeight */, 16 /* blockWidth */, 16 /* blockHeight */,
1 /* widthAlignment */, 1 /* heightAlignment */); minAlignment /* widthAlignment */, minAlignment /* heightAlignment */);
mFrameRateRange = Range.create(1, maxRate); mFrameRateRange = Range.create(1, maxRate);
} else if (mime.equalsIgnoreCase(MediaFormat.MIMETYPE_VIDEO_VP8)) { } else if (mime.equalsIgnoreCase(MediaFormat.MIMETYPE_VIDEO_VP8)) {
maxBlocks = Integer.MAX_VALUE; maxBlocks = Integer.MAX_VALUE;
@@ -2307,6 +2401,8 @@ public final class MediaCodecInfo {
case CodecProfileLevel.VP9Profile1: case CodecProfileLevel.VP9Profile1:
case CodecProfileLevel.VP9Profile2: case CodecProfileLevel.VP9Profile2:
case CodecProfileLevel.VP9Profile3: case CodecProfileLevel.VP9Profile3:
case CodecProfileLevel.VP9Profile2HDR:
case CodecProfileLevel.VP9Profile3HDR:
break; break;
default: default:
Log.w(TAG, "Unrecognized profile " Log.w(TAG, "Unrecognized profile "
@@ -2331,7 +2427,8 @@ public final class MediaCodecInfo {
blockSize, blockSize, blockSize, blockSize,
1 /* widthAlignment */, 1 /* heightAlignment */); 1 /* widthAlignment */, 1 /* heightAlignment */);
} else if (mime.equalsIgnoreCase(MediaFormat.MIMETYPE_VIDEO_HEVC)) { } else if (mime.equalsIgnoreCase(MediaFormat.MIMETYPE_VIDEO_HEVC)) {
maxBlocks = 36864; // CTBs are at least 8x8 so use 8x8 block size
maxBlocks = 36864 >> 6; // 192x192 pixels == 576 8x8 blocks
maxBlocksPerSecond = maxBlocks * 15; maxBlocksPerSecond = maxBlocks * 15;
maxBps = 128000; maxBps = 128000;
for (CodecProfileLevel profileLevel: profileLevels) { for (CodecProfileLevel profileLevel: profileLevels) {
@@ -2339,6 +2436,10 @@ public final class MediaCodecInfo {
int FS = 0; int FS = 0;
int BR = 0; int BR = 0;
switch (profileLevel.level) { switch (profileLevel.level) {
/* The HEVC spec talks only in a very convoluted manner about the
existence of levels 1-3.1 for High tier, which could also be
understood as 'decoders and encoders should treat these levels
as if they were Main tier', so we do that. */
case CodecProfileLevel.HEVCMainTierLevel1: case CodecProfileLevel.HEVCMainTierLevel1:
case CodecProfileLevel.HEVCHighTierLevel1: case CodecProfileLevel.HEVCHighTierLevel1:
FR = 15; FS = 36864; BR = 128; break; FR = 15; FS = 36864; BR = 128; break;
@@ -2409,6 +2510,7 @@ public final class MediaCodecInfo {
else DPB = 6; else DPB = 6;
*/ */
FS >>= 6; // convert pixels to blocks
errors &= ~ERROR_NONE_SUPPORTED; errors &= ~ERROR_NONE_SUPPORTED;
maxBlocksPerSecond = Math.max((int)(FR * FS), maxBlocksPerSecond); maxBlocksPerSecond = Math.max((int)(FR * FS), maxBlocksPerSecond);
maxBlocks = Math.max(FS, maxBlocks); maxBlocks = Math.max(FS, maxBlocks);
@@ -2416,11 +2518,6 @@ public final class MediaCodecInfo {
} }
int maxLengthInBlocks = (int)(Math.sqrt(maxBlocks * 8)); int maxLengthInBlocks = (int)(Math.sqrt(maxBlocks * 8));
// CTBs are at least 8x8
maxBlocks = Utils.divUp(maxBlocks, 8 * 8);
maxBlocksPerSecond = Utils.divUp(maxBlocksPerSecond, 8 * 8);
maxLengthInBlocks = Utils.divUp(maxLengthInBlocks, 8);
applyMacroBlockLimits( applyMacroBlockLimits(
maxLengthInBlocks, maxLengthInBlocks, maxLengthInBlocks, maxLengthInBlocks,
maxBlocks, maxBlocksPerSecond, maxBlocks, maxBlocksPerSecond,
@@ -2834,6 +2931,12 @@ public final class MediaCodecInfo {
public static final int HEVCMainTierLevel62 = 0x1000000; public static final int HEVCMainTierLevel62 = 0x1000000;
public static final int HEVCHighTierLevel62 = 0x2000000; public static final int HEVCHighTierLevel62 = 0x2000000;
private static final int HEVCHighTierLevels =
HEVCHighTierLevel1 | HEVCHighTierLevel2 | HEVCHighTierLevel21 | HEVCHighTierLevel3 |
HEVCHighTierLevel31 | HEVCHighTierLevel4 | HEVCHighTierLevel41 | HEVCHighTierLevel5 |
HEVCHighTierLevel51 | HEVCHighTierLevel52 | HEVCHighTierLevel6 | HEVCHighTierLevel61 |
HEVCHighTierLevel62;
// from OMX_VIDEO_DOLBYVISIONPROFILETYPE // from OMX_VIDEO_DOLBYVISIONPROFILETYPE
public static final int DolbyVisionProfileDvavPer = 0x1; public static final int DolbyVisionProfileDvavPer = 0x1;
public static final int DolbyVisionProfileDvavPen = 0x2; public static final int DolbyVisionProfileDvavPen = 0x2;