blob: 51a10fca5bbbb6be369a7cd3a10b17c121001d58 [file] [log] [blame]
/*
* Copyright (C) 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.mediav2.cts;
import android.media.MediaCodec;
import android.media.MediaCodecInfo;
import android.media.MediaExtractor;
import android.media.MediaFormat;
import android.media.MediaMuxer;
import android.util.Log;
import android.util.Pair;
import org.junit.Assume;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import java.io.File;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import static android.media.MediaCodecInfo.CodecProfileLevel.*;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
/**
* Validate profile and level configuration for listed encoder components
*/
@RunWith(Parameterized.class)
public class EncoderProfileLevelTest extends CodecEncoderTestBase {
private static final String LOG_TAG = EncoderProfileLevelTest.class.getSimpleName();
private static final HashMap<String, int[]> mProfileMap = new HashMap<>();
private static final HashMap<String, Pair<int[], Integer>> mProfileLevelCdd = new HashMap<>();
private MediaFormat mConfigFormat;
private MediaMuxer mMuxer;
public EncoderProfileLevelTest(String encoder, String mime, int bitrate, int encoderInfo1,
int encoderInfo2, int frameRate) {
super(encoder, mime, new int[]{bitrate}, new int[]{encoderInfo1}, new int[]{encoderInfo2});
if (mIsAudio) {
mSampleRate = encoderInfo1;
mChannels = encoderInfo2;
} else {
mWidth = encoderInfo1;
mHeight = encoderInfo2;
mFrameRate = frameRate;
}
setUpParams(1);
mConfigFormat = mFormats.get(0);
}
@Parameterized.Parameters(name = "{index}({0}_{1})")
public static Collection<Object[]> input() {
final boolean isEncoder = true;
final boolean needAudio = true;
final boolean needVideo = true;
final List<Object[]> exhaustiveArgsList = Arrays.asList(new Object[][]{
// Audio - CodecMime, bit-rate, sample rate, channel count
{MediaFormat.MIMETYPE_AUDIO_AAC, 64000, 48000, 1, -1},
{MediaFormat.MIMETYPE_AUDIO_AAC, 128000, 48000, 2, -1},
// Video - CodecMime, bit-rate, height, width, frame-rate
// TODO (b/151423508)
/*{MediaFormat.MIMETYPE_VIDEO_AVC, 64000, 176, 144, 15},
{MediaFormat.MIMETYPE_VIDEO_AVC, 128000, 176, 144, 15},
{MediaFormat.MIMETYPE_VIDEO_AVC, 192000, 352, 288, 7},
{MediaFormat.MIMETYPE_VIDEO_AVC, 384000, 352, 288, 15},*/
{MediaFormat.MIMETYPE_VIDEO_AVC, 768000, 352, 288, 30},
{MediaFormat.MIMETYPE_VIDEO_AVC, 2000000, 352, 288, 30},
// TODO (b/151423508)
/*{MediaFormat.MIMETYPE_VIDEO_AVC, 4000000, 352, 576, 25},
{MediaFormat.MIMETYPE_VIDEO_AVC, 4000000, 720, 576, 12},
{MediaFormat.MIMETYPE_VIDEO_AVC, 10000000, 720, 576, 25},*/
{MediaFormat.MIMETYPE_VIDEO_AVC, 14000000, 1280, 720, 30},
{MediaFormat.MIMETYPE_VIDEO_AVC, 20000000, 1280, 1024, 42},
{MediaFormat.MIMETYPE_VIDEO_AVC, 20000000, 2048, 1024, 30},
{MediaFormat.MIMETYPE_VIDEO_AVC, 50000000, 2048, 1024, 30},
{MediaFormat.MIMETYPE_VIDEO_AVC, 50000000, 2048, 1080, 60},
{MediaFormat.MIMETYPE_VIDEO_AVC, 135000000, 3672, 1536, 25},
{MediaFormat.MIMETYPE_VIDEO_AVC, 240000000, 4096, 2304, 25},
{MediaFormat.MIMETYPE_VIDEO_AVC, 240000000, 4096, 2304, 50},
{MediaFormat.MIMETYPE_VIDEO_AVC, 240000000, 8192, 4320, 30},
{MediaFormat.MIMETYPE_VIDEO_AVC, 480000000, 8192, 4320, 60},
{MediaFormat.MIMETYPE_VIDEO_AVC, 800000000, 8192, 4320, 120},
{MediaFormat.MIMETYPE_VIDEO_MPEG2, 4000000, 352, 288, 30},
{MediaFormat.MIMETYPE_VIDEO_MPEG2, 15000000, 720, 576, 30},
{MediaFormat.MIMETYPE_VIDEO_MPEG2, 60000000, 1440, 1088, 60},
{MediaFormat.MIMETYPE_VIDEO_MPEG2, 80000000, 1920, 1088, 60},
{MediaFormat.MIMETYPE_VIDEO_MPEG2, 80000000, 1920, 1088, 60},
{MediaFormat.MIMETYPE_VIDEO_MPEG4, 64000, 176, 144, 15},
{MediaFormat.MIMETYPE_VIDEO_MPEG4, 64000, 176, 144, 30},
{MediaFormat.MIMETYPE_VIDEO_MPEG4, 128000, 176, 144, 15},
{MediaFormat.MIMETYPE_VIDEO_MPEG4, 128000, 352, 288, 30},
{MediaFormat.MIMETYPE_VIDEO_MPEG4, 384000, 352, 288, 30},
{MediaFormat.MIMETYPE_VIDEO_MPEG4, 4000000, 640, 480, 30},
{MediaFormat.MIMETYPE_VIDEO_MPEG4, 8000000, 720, 576, 30},
{MediaFormat.MIMETYPE_VIDEO_MPEG4, 12000000, 1280, 720, 30},
{MediaFormat.MIMETYPE_VIDEO_MPEG4, 128000, 176, 144, 30},
{MediaFormat.MIMETYPE_VIDEO_MPEG4, 384000, 352, 288, 30},
{MediaFormat.MIMETYPE_VIDEO_MPEG4, 768000, 352, 288, 30},
{MediaFormat.MIMETYPE_VIDEO_MPEG4, 1500000, 352, 288, 30},
{MediaFormat.MIMETYPE_VIDEO_MPEG4, 3000000, 704, 576, 30},
{MediaFormat.MIMETYPE_VIDEO_MPEG4, 8000000, 720, 576, 30},
// TODO (b/151430764)
/*{MediaFormat.MIMETYPE_VIDEO_VP9, 200000, 256, 144, 15},
{MediaFormat.MIMETYPE_VIDEO_VP9, 8000000, 384, 192, 30},
{MediaFormat.MIMETYPE_VIDEO_VP9, 1800000, 480, 256, 30},
{MediaFormat.MIMETYPE_VIDEO_VP9, 3600000, 640, 384, 30},
{MediaFormat.MIMETYPE_VIDEO_VP9, 7200000, 1080, 512, 30},*/
{MediaFormat.MIMETYPE_VIDEO_VP9, 12000000, 1280, 768, 30},
{MediaFormat.MIMETYPE_VIDEO_VP9, 18000000, 2048, 1088, 30},
{MediaFormat.MIMETYPE_VIDEO_VP9, 30000000, 2048, 1088, 60},
{MediaFormat.MIMETYPE_VIDEO_VP9, 60000000, 4096, 2176, 30},
{MediaFormat.MIMETYPE_VIDEO_VP9, 120000000, 4096, 2176, 60},
{MediaFormat.MIMETYPE_VIDEO_VP9, 180000000, 4096, 2176, 120},
{MediaFormat.MIMETYPE_VIDEO_VP9, 180000000, 8192, 4352, 30},
{MediaFormat.MIMETYPE_VIDEO_VP9, 240000000, 8192, 4352, 60},
{MediaFormat.MIMETYPE_VIDEO_VP9, 480000000, 8192, 4352, 120},
{MediaFormat.MIMETYPE_VIDEO_H263, 64000, 176, 144, 15},
{MediaFormat.MIMETYPE_VIDEO_H263, 128000, 176, 144, 15},
{MediaFormat.MIMETYPE_VIDEO_H263, 128000, 176, 144, 30},
{MediaFormat.MIMETYPE_VIDEO_H263, 128000, 352, 288, 15},
{MediaFormat.MIMETYPE_VIDEO_H263, 384000, 352, 288, 30},
{MediaFormat.MIMETYPE_VIDEO_H263, 2048000, 352, 288, 30},
{MediaFormat.MIMETYPE_VIDEO_H263, 4096000, 352, 240, 60},
{MediaFormat.MIMETYPE_VIDEO_H263, 4096000, 352, 288, 50},
{MediaFormat.MIMETYPE_VIDEO_H263, 8192000, 720, 240, 60},
{MediaFormat.MIMETYPE_VIDEO_H263, 8192000, 720, 288, 50},
{MediaFormat.MIMETYPE_VIDEO_H263, 16384000, 720, 480, 60},
{MediaFormat.MIMETYPE_VIDEO_H263, 16384000, 720, 576, 50},
{MediaFormat.MIMETYPE_VIDEO_HEVC, 128000, 176, 144, 15},
{MediaFormat.MIMETYPE_VIDEO_HEVC, 1500000, 352, 288, 30},
// TODO (b/152576008) - Limit HEVC Encoder test to 512x512
{MediaFormat.MIMETYPE_VIDEO_HEVC, 3000000, 512, 512, 30},
//{MediaFormat.MIMETYPE_VIDEO_HEVC, 3000000, 640, 360, 30},
//{MediaFormat.MIMETYPE_VIDEO_HEVC, 6000000, 960, 540, 30},
{MediaFormat.MIMETYPE_VIDEO_HEVC, 10000000, 1280, 720, 33},
{MediaFormat.MIMETYPE_VIDEO_HEVC, 12000000, 2048, 1080, 30},
{MediaFormat.MIMETYPE_VIDEO_HEVC, 20000000, 2048, 1080, 60},
{MediaFormat.MIMETYPE_VIDEO_HEVC, 25000000, 4096, 2160, 30},
{MediaFormat.MIMETYPE_VIDEO_HEVC, 40000000, 4096, 2160, 60},
{MediaFormat.MIMETYPE_VIDEO_HEVC, 60000000, 4096, 2160, 120},
{MediaFormat.MIMETYPE_VIDEO_HEVC, 60000000, 8192, 4320, 30},
{MediaFormat.MIMETYPE_VIDEO_HEVC, 120000000, 8192, 4320, 60},
{MediaFormat.MIMETYPE_VIDEO_HEVC, 240000000, 8192, 4320, 120},
{MediaFormat.MIMETYPE_VIDEO_AV1, 1500000, 426, 240, 30},
{MediaFormat.MIMETYPE_VIDEO_AV1, 3000000, 640, 360, 30},
{MediaFormat.MIMETYPE_VIDEO_AV1, 6000000, 854, 480, 30},
{MediaFormat.MIMETYPE_VIDEO_AV1, 10000000, 1280, 720, 30},
{MediaFormat.MIMETYPE_VIDEO_AV1, 12000000, 1920, 1080, 30},
{MediaFormat.MIMETYPE_VIDEO_AV1, 20000000, 1920, 1080, 60},
{MediaFormat.MIMETYPE_VIDEO_AV1, 30000000, 3840, 2160, 30},
{MediaFormat.MIMETYPE_VIDEO_AV1, 40000000, 3840, 2160, 60},
{MediaFormat.MIMETYPE_VIDEO_AV1, 60000000, 3840, 2160, 120},
{MediaFormat.MIMETYPE_VIDEO_AV1, 60000000, 7680, 4320, 30},
{MediaFormat.MIMETYPE_VIDEO_AV1, 100000000, 7680, 4320, 60},
{MediaFormat.MIMETYPE_VIDEO_AV1, 160000000, 7680, 4320, 120},
{MediaFormat.MIMETYPE_VIDEO_VP8, 512000, 176, 144, 20},
{MediaFormat.MIMETYPE_VIDEO_VP8, 512000, 480, 360, 20},
});
return prepareParamList(exhaustiveArgsList, isEncoder, needAudio, needVideo, false);
}
static {
mProfileMap.put(MediaFormat.MIMETYPE_VIDEO_AVC,
new int[]{AVCProfileBaseline, AVCProfileMain, AVCProfileExtended, AVCProfileHigh,
AVCProfileHigh10, AVCProfileHigh422, AVCProfileHigh444,
AVCProfileConstrainedBaseline, AVCProfileConstrainedHigh});
mProfileMap.put(MediaFormat.MIMETYPE_VIDEO_HEVC,
new int[]{HEVCProfileMain, HEVCProfileMain10, HEVCProfileMainStill,
// TODO: test HDR profiles once they are supported by MediaMuxer
/* HEVCProfileMain10HDR10, HEVCProfileMain10HDR10Plus */});
mProfileMap.put(MediaFormat.MIMETYPE_VIDEO_H263,
new int[]{H263ProfileBaseline, H263ProfileH320Coding,
H263ProfileBackwardCompatible, H263ProfileISWV2, H263ProfileISWV3,
H263ProfileHighCompression, H263ProfileInternet, H263ProfileInterlace,
H263ProfileHighLatency});
mProfileMap.put(MediaFormat.MIMETYPE_VIDEO_MPEG2,
new int[]{MPEG2ProfileSimple, MPEG2ProfileMain, MPEG2Profile422, MPEG2ProfileSNR,
MPEG2ProfileSpatial, MPEG2ProfileHigh});
mProfileMap.put(MediaFormat.MIMETYPE_VIDEO_MPEG4,
new int[]{MPEG4ProfileSimple, MPEG4ProfileSimpleScalable, MPEG4ProfileCore,
MPEG4ProfileMain, MPEG4ProfileNbit, MPEG4ProfileScalableTexture,
MPEG4ProfileSimpleFace, MPEG4ProfileSimpleFBA, MPEG4ProfileBasicAnimated,
MPEG4ProfileHybrid, MPEG4ProfileAdvancedRealTime,
MPEG4ProfileCoreScalable, MPEG4ProfileAdvancedCoding,
MPEG4ProfileAdvancedCore, MPEG4ProfileAdvancedScalable,
MPEG4ProfileAdvancedSimple});
mProfileMap.put(MediaFormat.MIMETYPE_VIDEO_VP8, new int[]{VP8ProfileMain});
mProfileMap.put(MediaFormat.MIMETYPE_VIDEO_VP9, new int[]{VP9Profile0, VP9Profile1});
mProfileMap.put(MediaFormat.MIMETYPE_VIDEO_AV1,
new int[]{AV1ProfileMain8, AV1ProfileMain10,
// TODO: test HDR profiles once they are supported by MediaMuxer
/* AV1ProfileMain10HDR10, AV1ProfileMain10HDR10Plus */});
mProfileMap.put(MediaFormat.MIMETYPE_AUDIO_AAC,
new int[]{AACObjectMain, AACObjectLC, AACObjectSSR, AACObjectLTP, AACObjectHE,
AACObjectScalable, AACObjectERLC, AACObjectERScalable, AACObjectLD,
AACObjectELD, AACObjectXHE});
}
static {
mProfileLevelCdd.put(MediaFormat.MIMETYPE_AUDIO_AAC,
new Pair<>(new int[]{AACObjectLC, AACObjectHE, AACObjectELD}, -1));
mProfileLevelCdd.put(MediaFormat.MIMETYPE_VIDEO_H263,
new Pair<>(new int[]{H263ProfileBaseline}, H263Level45));
mProfileLevelCdd.put(MediaFormat.MIMETYPE_VIDEO_AVC,
new Pair<>(new int[]{AVCProfileBaseline}, AVCLevel3));
mProfileLevelCdd.put(MediaFormat.MIMETYPE_VIDEO_HEVC,
new Pair<>(new int[]{HEVCProfileMain}, HEVCMainTierLevel3));
mProfileLevelCdd.put(MediaFormat.MIMETYPE_VIDEO_VP8,
new Pair<>(new int[]{VP8ProfileMain}, VP8Level_Version0));
mProfileLevelCdd.put(MediaFormat.MIMETYPE_VIDEO_VP9,
new Pair<>(new int[]{VP9Profile0}, VP9Level3));
}
private int getMinLevel(String mime, int width, int height, int frameRate, int bitrate,
int profile) {
switch (mime) {
case MediaFormat.MIMETYPE_VIDEO_AVC:
return getMinLevelAVC(width, height, frameRate, bitrate);
case MediaFormat.MIMETYPE_VIDEO_HEVC:
return getMinLevelHEVC(width, height, frameRate, bitrate);
case MediaFormat.MIMETYPE_VIDEO_H263:
return getMinLevelH263(width, height, frameRate, bitrate);
case MediaFormat.MIMETYPE_VIDEO_MPEG2:
return getMinLevelMPEG2(width, height, frameRate, bitrate);
case MediaFormat.MIMETYPE_VIDEO_MPEG4:
return getMinLevelMPEG4(width, height, frameRate, bitrate, profile);
// complex features disabled in VP8 Level/Version 0
case MediaFormat.MIMETYPE_VIDEO_VP8:
return VP8Level_Version0;
case MediaFormat.MIMETYPE_VIDEO_VP9:
return getMinLevelVP9(width, height, frameRate, bitrate);
case MediaFormat.MIMETYPE_VIDEO_AV1:
return getMinLevelAV1(width, height, frameRate, bitrate);
default:
return -1;
}
}
private int getMinLevelAVC(int width, int height, int frameRate, int bitrate) {
class LevelLimitAVC {
private LevelLimitAVC(int level, int mbsPerSec, long mbs, int bitrate) {
this.level = level;
this.mbsPerSec = mbsPerSec;
this.mbs = mbs;
this.bitrate = bitrate;
}
private final int level;
private final int mbsPerSec;
private final long mbs;
private final int bitrate;
}
LevelLimitAVC[] limitsAVC = {
new LevelLimitAVC(AVCLevel1, 1485, 99, 64000),
new LevelLimitAVC(AVCLevel1b, 1485, 99, 128000),
new LevelLimitAVC(AVCLevel11, 3000, 396, 192000),
new LevelLimitAVC(AVCLevel12, 6000, 396, 384000),
new LevelLimitAVC(AVCLevel13, 11880, 396, 768000),
new LevelLimitAVC(AVCLevel2, 11880, 396, 2000000),
new LevelLimitAVC(AVCLevel21, 19800, 792, 4000000),
new LevelLimitAVC(AVCLevel22, 20250, 1620, 4000000),
new LevelLimitAVC(AVCLevel3, 40500, 1620, 10000000),
new LevelLimitAVC(AVCLevel31, 108000, 3600, 14000000),
new LevelLimitAVC(AVCLevel32, 216000, 5120, 20000000),
new LevelLimitAVC(AVCLevel4, 245760, 8192, 20000000),
new LevelLimitAVC(AVCLevel41, 245760, 8192, 50000000),
new LevelLimitAVC(AVCLevel42, 522240, 8704, 50000000),
new LevelLimitAVC(AVCLevel5, 589824, 22080, 135000000),
new LevelLimitAVC(AVCLevel51, 983040, 36864, 240000000),
new LevelLimitAVC(AVCLevel52, 2073600, 36864, 240000000),
new LevelLimitAVC(AVCLevel6, 4177920, 139264, 240000000),
new LevelLimitAVC(AVCLevel61, 8355840, 139264, 480000000),
new LevelLimitAVC(AVCLevel62, 16711680, 139264, 800000000),
};
long mbs = ((width + 15) / 16) * ((height + 15) / 16);
float mbsPerSec = mbs * frameRate;
for (LevelLimitAVC levelLimitsAVC : limitsAVC) {
if (mbs <= levelLimitsAVC.mbs && mbsPerSec <= levelLimitsAVC.mbsPerSec
&& bitrate <= levelLimitsAVC.bitrate) {
return levelLimitsAVC.level;
}
}
// if none of the levels suffice, select the highest level
return AVCLevel62;
}
private int getMinLevelHEVC(int width, int height, int frameRate, int bitrate) {
class LevelLimitHEVC {
private LevelLimitHEVC(int level, int frameRate, long samples, int bitrate) {
this.level = level;
this.frameRate = frameRate;
this.samples = samples;
this.bitrate = bitrate;
}
private final int level;
private final int frameRate;
private final long samples;
private final int bitrate;
}
LevelLimitHEVC[] limitsHEVC = {
new LevelLimitHEVC(HEVCMainTierLevel1, 15, 36864, 128000),
new LevelLimitHEVC(HEVCMainTierLevel2, 30, 122880, 1500000),
new LevelLimitHEVC(HEVCMainTierLevel21, 30, 245760, 3000000),
new LevelLimitHEVC(HEVCMainTierLevel3, 30, 552960, 6000000),
new LevelLimitHEVC(HEVCMainTierLevel31, 30, 983040, 10000000),
new LevelLimitHEVC(HEVCMainTierLevel4, 30, 2228224, 12000000),
new LevelLimitHEVC(HEVCHighTierLevel4, 30, 2228224, 30000000),
new LevelLimitHEVC(HEVCMainTierLevel41, 60, 2228224, 20000000),
new LevelLimitHEVC(HEVCHighTierLevel41, 60, 2228224, 50000000),
new LevelLimitHEVC(HEVCMainTierLevel5, 30, 8912896, 25000000),
new LevelLimitHEVC(HEVCHighTierLevel5, 30, 8912896, 100000000),
new LevelLimitHEVC(HEVCMainTierLevel51, 60, 8912896, 40000000),
new LevelLimitHEVC(HEVCHighTierLevel51, 60, 8912896, 160000000),
new LevelLimitHEVC(HEVCMainTierLevel52, 120, 8912896, 60000000),
new LevelLimitHEVC(HEVCHighTierLevel52, 120, 8912896, 240000000),
new LevelLimitHEVC(HEVCMainTierLevel6, 30, 35651584, 60000000),
new LevelLimitHEVC(HEVCHighTierLevel6, 30, 35651584, 240000000),
new LevelLimitHEVC(HEVCMainTierLevel61, 60, 35651584, 120000000),
new LevelLimitHEVC(HEVCHighTierLevel61, 60, 35651584, 480000000),
new LevelLimitHEVC(HEVCMainTierLevel62, 120, 35651584, 240000000),
new LevelLimitHEVC(HEVCHighTierLevel62, 120, 35651584, 800000000),
};
long samples = width * height;
for (LevelLimitHEVC levelLimitsHEVC : limitsHEVC) {
if (samples <= levelLimitsHEVC.samples && frameRate <= levelLimitsHEVC.frameRate
&& bitrate <= levelLimitsHEVC.bitrate) {
return levelLimitsHEVC.level;
}
}
// if none of the levels suffice, select the highest level
return HEVCHighTierLevel62;
}
private int getMinLevelH263(int width, int height, int frameRate, int bitrate) {
class LevelLimitH263 {
private LevelLimitH263(int level, int height, int width, int frameRate,
int bitrate) {
this.level = level;
this.height = height;
this.width = width;
this.frameRate = frameRate;
this.bitrate = bitrate;
}
private final int level;
private final int height;
private final int width;
private final int frameRate;
private final int bitrate;
}
LevelLimitH263[] limitsH263 = {
new LevelLimitH263(H263Level10, 176, 144, 15, 64000),
new LevelLimitH263(H263Level45, 176, 144, 15, 128000),
new LevelLimitH263(H263Level20, 176, 144, 30, 128000),
new LevelLimitH263(H263Level20, 352, 288, 15, 128000),
new LevelLimitH263(H263Level30, 352, 288, 30, 384000),
new LevelLimitH263(H263Level40, 352, 288, 30, 2048000),
new LevelLimitH263(H263Level50, 352, 240, 60, 4096000),
new LevelLimitH263(H263Level50, 352, 288, 50, 4096000),
new LevelLimitH263(H263Level60, 720, 240, 60, 8192000),
new LevelLimitH263(H263Level60, 720, 288, 50, 8192000),
new LevelLimitH263(H263Level70, 720, 480, 60, 16384000),
new LevelLimitH263(H263Level70, 720, 576, 50, 16384000),
};
for (LevelLimitH263 levelLimitsH263 : limitsH263) {
if (height <= levelLimitsH263.height && width <= levelLimitsH263.width &&
frameRate <= levelLimitsH263.frameRate && bitrate <= levelLimitsH263.bitrate) {
return levelLimitsH263.level;
}
}
// if none of the levels suffice, select the highest level
return H263Level70;
}
private int getMinLevelVP9(int width, int height, int frameRate, int bitrate) {
class LevelLimitVP9 {
private LevelLimitVP9(int level, long sampleRate, int size, int breadth,
int bitrate) {
this.level = level;
this.sampleRate = sampleRate;
this.size = size;
this.breadth = breadth;
this.bitrate = bitrate;
}
private final int level;
private final long sampleRate;
private final int size;
private final int breadth;
private final int bitrate;
}
LevelLimitVP9[] limitsVP9 = {
new LevelLimitVP9(VP9Level1, 829440, 36864, 512, 200000),
new LevelLimitVP9(VP9Level11, 2764800, 73728, 768, 800000),
new LevelLimitVP9(VP9Level2, 4608000, 122880, 960, 1800000),
new LevelLimitVP9(VP9Level21, 9216000, 245760, 1344, 3600000),
new LevelLimitVP9(VP9Level3, 20736000, 552960, 2048, 7200000),
new LevelLimitVP9(VP9Level31, 36864000, 983040, 2752, 12000000),
new LevelLimitVP9(VP9Level4, 83558400, 2228224, 4160, 18000000),
new LevelLimitVP9(VP9Level41, 160432128, 2228224, 4160, 30000000),
new LevelLimitVP9(VP9Level5, 311951360, 8912896, 8384, 60000000),
new LevelLimitVP9(VP9Level51, 588251136, 8912896, 8384, 120000000),
new LevelLimitVP9(VP9Level52, 1176502272, 8912896, 8384, 180000000),
new LevelLimitVP9(VP9Level6, 1176502272, 35651584, 16832, 180000000),
new LevelLimitVP9(VP9Level61, 2353004544L, 35651584, 16832, 240000000),
new LevelLimitVP9(VP9Level62, 4706009088L, 35651584, 16832, 480000000),
};
int size = width * height;
int sampleRate = size * frameRate;
int breadth = Math.max(width, height);
for (LevelLimitVP9 levelLimitsVP9 : limitsVP9) {
if (sampleRate <= levelLimitsVP9.sampleRate && size <= levelLimitsVP9.size &&
breadth <= levelLimitsVP9.breadth && bitrate <= levelLimitsVP9.bitrate) {
return levelLimitsVP9.level;
}
}
// if none of the levels suffice, select the highest level
return VP9Level62;
}
private int getMinLevelMPEG2(int width, int height, int frameRate, int bitrate) {
class LevelLimitMPEG2 {
private LevelLimitMPEG2(int level, long sampleRate, int width, int height,
int frameRate, int bitrate) {
this.level = level;
this.sampleRate = sampleRate;
this.width = width;
this.height = height;
this.frameRate = frameRate;
this.bitrate = bitrate;
}
private final int level;
private final long sampleRate;
private final int width;
private final int height;
private final int frameRate;
private final int bitrate;
}
// main profile limits, higher profiles will also support selected level
LevelLimitMPEG2[] limitsMPEG2 = {
new LevelLimitMPEG2(MPEG2LevelLL, 3041280, 352, 288, 30, 4000000),
new LevelLimitMPEG2(MPEG2LevelML, 10368000, 720, 576, 30, 15000000),
new LevelLimitMPEG2(MPEG2LevelH14, 47001600, 1440, 1088, 60, 60000000),
new LevelLimitMPEG2(MPEG2LevelHL, 62668800, 1920, 1088, 60, 80000000),
new LevelLimitMPEG2(MPEG2LevelHP, 125337600, 1920, 1088, 60, 80000000),
};
int size = width * height;
int sampleRate = size * frameRate;
for (LevelLimitMPEG2 levelLimitsMPEG2 : limitsMPEG2) {
if (sampleRate <= levelLimitsMPEG2.sampleRate && width <= levelLimitsMPEG2.width &&
height <= levelLimitsMPEG2.height && frameRate <= levelLimitsMPEG2.frameRate &&
bitrate <= levelLimitsMPEG2.bitrate) {
return levelLimitsMPEG2.level;
}
}
// if none of the levels suffice, select the highest level
return MPEG2LevelHP;
}
private int getMinLevelMPEG4(int width, int height, int frameRate, int bitrate, int profile) {
class LevelLimitMPEG4 {
private LevelLimitMPEG4(int profile, int level, long sampleRate, int width,
int height, int frameRate, int bitrate) {
this.profile = profile;
this.level = level;
this.sampleRate = sampleRate;
this.width = width;
this.height = height;
this.frameRate = frameRate;
this.bitrate = bitrate;
}
private final int profile;
private final int level;
private final long sampleRate;
private final int width;
private final int height;
private final int frameRate;
private final int bitrate;
}
// simple profile limits, higher profiles will also support selected level
LevelLimitMPEG4[] limitsMPEG4 = {
new LevelLimitMPEG4(MPEG4ProfileSimple, MPEG4Level0, 380160, 176, 144, 15, 64000),
new LevelLimitMPEG4(MPEG4ProfileSimple, MPEG4Level1, 380160, 176, 144, 30, 64000),
new LevelLimitMPEG4(MPEG4ProfileSimple, MPEG4Level0b, 380160, 176, 144, 15, 128000),
new LevelLimitMPEG4(MPEG4ProfileSimple, MPEG4Level2, 1520640, 352, 288, 30, 128000),
new LevelLimitMPEG4(MPEG4ProfileSimple, MPEG4Level3, 3041280, 352, 288, 30, 384000),
new LevelLimitMPEG4(
MPEG4ProfileSimple, MPEG4Level4a, 9216000, 640, 480, 30, 4000000),
new LevelLimitMPEG4(
MPEG4ProfileSimple, MPEG4Level5, 10368000, 720, 576, 30, 8000000),
new LevelLimitMPEG4(
MPEG4ProfileSimple, MPEG4Level6, 27648000, 1280, 720, 30, 12000000),
new LevelLimitMPEG4(
MPEG4ProfileAdvancedSimple, MPEG4Level1, 760320, 176, 144, 30, 128000),
new LevelLimitMPEG4(
MPEG4ProfileAdvancedSimple, MPEG4Level2, 1520640, 352, 288, 30, 384000),
new LevelLimitMPEG4(
MPEG4ProfileAdvancedSimple, MPEG4Level3, 3041280, 352, 288, 30, 768000),
new LevelLimitMPEG4(
MPEG4ProfileAdvancedSimple, MPEG4Level3b, 3041280, 352, 288, 30, 1500000),
new LevelLimitMPEG4(
MPEG4ProfileAdvancedSimple, MPEG4Level4, 3041280, 704, 576, 30, 3000000),
new LevelLimitMPEG4(
MPEG4ProfileAdvancedSimple, MPEG4Level5, 3041280, 720, 576, 30, 8000000),
};
int size = width * height;
int sampleRate = size * frameRate;
for (LevelLimitMPEG4 levelLimitsMPEG4 : limitsMPEG4) {
if (((profile & (MPEG4ProfileAdvancedSimple | MPEG4ProfileSimple)) != 0) &&
profile != levelLimitsMPEG4.profile) continue;
if (sampleRate <= levelLimitsMPEG4.sampleRate && width <= levelLimitsMPEG4.width &&
height <= levelLimitsMPEG4.height && frameRate <= levelLimitsMPEG4.frameRate &&
bitrate <= levelLimitsMPEG4.bitrate) {
return levelLimitsMPEG4.level;
}
}
// if none of the levels suffice, select the highest level
return MPEG4Level6;
}
private int getMinLevelAV1(int width, int height, int frameRate, int bitrate) {
class LevelLimitAV1 {
private LevelLimitAV1(int level, int size, int width, int height, long sampleRate,
int bitrate) {
this.level = level;
this.size = size;
this.width = width;
this.height = height;
this.sampleRate = sampleRate;
this.bitrate = bitrate;
}
private final int level;
private final int size;
private final int width;
private final int height;
private final long sampleRate;
private final int bitrate;
}
// taking bitrate from main profile, will also be supported by high profile
LevelLimitAV1[] limitsAV1 = {
new LevelLimitAV1(AV1Level2, 147456, 2048, 1152, 4423680, 1500000),
new LevelLimitAV1(AV1Level21, 278784, 2816, 1584, 8363520, 3000000),
new LevelLimitAV1(AV1Level3, 665856, 4352, 2448, 19975680, 6000000),
new LevelLimitAV1(AV1Level31, 1065024, 5504, 3096, 31950720, 10000000),
new LevelLimitAV1(AV1Level4, 2359296, 6144, 3456, 70778880, 12000000),
new LevelLimitAV1(AV1Level41, 2359296, 6144, 3456, 141557760, 20000000),
new LevelLimitAV1(AV1Level5, 8912896, 8192, 4352, 267386880, 30000000),
new LevelLimitAV1(AV1Level51, 8912896, 8192, 4352, 534773760, 40000000),
new LevelLimitAV1(AV1Level52, 8912896, 8192, 4352, 1069547520, 60000000),
new LevelLimitAV1(AV1Level53, 8912896, 8192, 4352, 1069547520, 60000000),
new LevelLimitAV1(AV1Level6, 35651584, 16384, 8704, 1069547520, 60000000),
new LevelLimitAV1(AV1Level61, 35651584, 16384, 8704, 2139095040, 100000000),
new LevelLimitAV1(AV1Level62, 35651584, 16384, 8704, 4278190080L, 160000000),
new LevelLimitAV1(AV1Level63, 35651584, 16384, 8704, 4278190080L, 160000000),
};
int size = width * height;
int sampleRate = size * frameRate;
for (LevelLimitAV1 levelLimitsAV1 : limitsAV1) {
if (size <= levelLimitsAV1.size && width <= levelLimitsAV1.width &&
height <= levelLimitsAV1.height && sampleRate <= levelLimitsAV1.sampleRate &&
bitrate <= levelLimitsAV1.bitrate) {
return levelLimitsAV1.level;
}
}
// if none of the levels suffice or high profile, select the highest level
return AV1Level73;
}
private int getAacProfile(MediaFormat format) {
int aacProfile = format.getInteger(MediaFormat.KEY_AAC_PROFILE, -1);
int profile = format.getInteger(MediaFormat.KEY_PROFILE, -1);
if (aacProfile != -1 && profile != -1) {
// If both aac-profile and profile are present in format, then they must be the same
assertTrue("aac-profile " + aacProfile + " and profile " + profile + " are different.",
aacProfile == profile);
return aacProfile;
} else if (aacProfile != -1) {
return aacProfile;
} else if (profile != -1) {
return profile;
} else {
Log.e(LOG_TAG,
"format doesn't contain either KEY_AAC_PROFILE or KEY_PROFILE");
return -1;
}
}
@Override
boolean isFormatSimilar(MediaFormat inpFormat, MediaFormat outFormat) {
if (!super.isFormatSimilar(inpFormat, outFormat)) {
Log.e(LOG_TAG, "Basic channel-rate/resolution comparisons failed");
return false;
}
String inpMime = inpFormat.getString(MediaFormat.KEY_MIME);
String outMime = outFormat.getString(MediaFormat.KEY_MIME);
assertTrue("Input and Output mimes are different.", inpMime.equals(outMime));
if (outMime.startsWith("audio/")) {
if (outFormat.getString(MediaFormat.KEY_MIME).equals(MediaFormat.MIMETYPE_AUDIO_AAC)) {
int inputProfileKey, outputProfileKey;
outputProfileKey = getAacProfile(outFormat);
inputProfileKey = getAacProfile(inpFormat);
if (outputProfileKey != inputProfileKey) {
Log.e(LOG_TAG, "aac-profile in output " + outputProfileKey +
" doesn't match configured input " + inputProfileKey);
return false;
}
}
} else if (outMime.startsWith("video/")) {
if (!outFormat.containsKey(MediaFormat.KEY_PROFILE)) {
Log.e(LOG_TAG, "Output format doesn't contain profile key");
//TODO (b/151398466)
if (true) return true;
return false;
}
if (!outFormat.containsKey(MediaFormat.KEY_LEVEL)) {
Log.e(LOG_TAG, "Output format doesn't contain level key");
//TODO (b/151398466)
if (true) return true;
return false;
}
if (!inpFormat.containsKey(MediaFormat.KEY_PROFILE)) {
Log.e(LOG_TAG, "Input format doesn't contain profile key");
return false;
}
if (!inpFormat.containsKey(MediaFormat.KEY_LEVEL)) {
Log.e(LOG_TAG, "Input format doesn't contain level key");
return false;
}
if (outFormat.getInteger(MediaFormat.KEY_PROFILE)
!= inpFormat.getInteger(MediaFormat.KEY_PROFILE)) {
Log.e(LOG_TAG, "profile in output doesn't match configured input");
return false;
}
if (outFormat.getInteger(MediaFormat.KEY_LEVEL)
!= inpFormat.getInteger(MediaFormat.KEY_LEVEL)) {
Log.e(LOG_TAG, "level key in output doesn't match configured input");
return false;
}
} else {
Log.w(LOG_TAG, "non media mime:" + outMime);
}
return true;
}
/**
* Sets profile and level keys in config format for encoder and validates the keys in output
* format if component supports the configuration and also verifies whether cdd mandated
* (profile, level) combination is supported
* Write the encoder output in all container formats that can hold the mime and validates the
* keys in extracted format.
*/
@Test(timeout = PER_TEST_TIMEOUT_LARGE_TEST_MS)
public void testValidateProfileLevel() throws IOException, InterruptedException {
int[] profiles = mProfileMap.get(mMime);
assertTrue("no profile entry found for mime" + mMime, profiles != null);
// cdd check initialization
boolean cddSupportedMime = mProfileLevelCdd.get(mMime) != null;
int[] profileCdd = new int[0];
int levelCdd = 0;
if (cddSupportedMime) {
Pair<int[], Integer> cddProfileLevel = mProfileLevelCdd.get(mMime);
profileCdd = cddProfileLevel.first;
levelCdd = cddProfileLevel.second;
}
MediaFormat format = mConfigFormat;
mOutputBuff = new OutputManager();
setUpSource(mInputFile);
mSaveToMem = true;
String tempMuxedFile = File.createTempFile("tmp", ".out").getAbsolutePath();
{
mCodec = MediaCodec.createByCodecName(mCodecName);
MediaCodecInfo.CodecCapabilities codecCapabilities =
mCodec.getCodecInfo().getCapabilitiesForType(mMime);
for (int profile : profiles) {
format.setInteger(MediaFormat.KEY_PROFILE, profile);
// for aac encoder, alongwith setting profile, also set aac-profile as some
// encoders may only support one of the two keys
if (mMime.equals(MediaFormat.MIMETYPE_AUDIO_AAC)) {
format.setInteger(MediaFormat.KEY_AAC_PROFILE, profile);
}
int level = mIsAudio ? 0 : getMinLevel(mMime, mWidth, mHeight,
format.getInteger(MediaFormat.KEY_FRAME_RATE),
format.getInteger(MediaFormat.KEY_BIT_RATE), profile);
assertTrue("no min level found for mime" + mMime, level != -1);
if (!mIsAudio) format.setInteger(MediaFormat.KEY_LEVEL, level);
if (!codecCapabilities.isFormatSupported(format)) {
if (cddSupportedMime) {
boolean shallSupportProfileLevel = false;
if (mIsAudio) {
for (int cddProfile : profileCdd) {
if (profile == cddProfile) {
shallSupportProfileLevel = true;
}
}
} else if (profile == profileCdd[0] && level == levelCdd) {
shallSupportProfileLevel = true;
}
// TODO (b/193173880) Check if there is at least one component that
// supports this profile and level combination
if (shallSupportProfileLevel) {
ArrayList<MediaFormat> formats = new ArrayList<>();
formats.add(format);
assertFalse(
"No components support cdd requirement profile level with \n "
+ "format :" + format + " for mime: " + mMime,
selectCodecs(mMime, formats, null, false).isEmpty());
}
Log.w(LOG_TAG,
"Component: " + mCodecName + " doesn't support cdd format: " +
format);
}
continue;
}
mOutputBuff.reset();
mInfoList.clear();
configureCodec(format, false, true, true);
mCodec.start();
doWork(1);
queueEOS();
waitForAllOutputs();
MediaFormat outFormat = mCodec.getOutputFormat();
/* TODO(b/147348711) */
if (false) mCodec.stop();
else mCodec.reset();
String log =
String.format("format: %s \n codec: %s, mode: %s:: ", format, mCodecName,
"sync");
assertFalse(log + " unexpected error", mAsyncHandle.hasSeenError());
assertTrue(log + "configured format and output format are not similar." +
(ENABLE_LOGS ? "\n output format:" + outFormat : ""),
isFormatSimilar(format, outFormat));
// TODO (b/151398466)
if (mMime.equals(MediaFormat.MIMETYPE_AUDIO_AAC)) {
Assume.assumeTrue("neither KEY_AAC_PROFILE nor KEY_PROFILE are present",
outFormat.containsKey(MediaFormat.KEY_AAC_PROFILE) ||
outFormat.containsKey(MediaFormat.KEY_PROFILE));
} else {
Assume.assumeTrue("KEY_PROFILE not present",
outFormat.containsKey(MediaFormat.KEY_PROFILE));
}
Assume.assumeTrue(outFormat.containsKey(MediaFormat.KEY_LEVEL));
// TODO (b/166300446) avc mime fails validation
if (mMime.equals(MediaFormat.MIMETYPE_VIDEO_AVC)) {
Log.w(LOG_TAG, "Skip validation after muxing for mime = " + mMime);
continue;
}
// TODO (b/166305723) hevc mime fails validation
if (mMime.equals(MediaFormat.MIMETYPE_VIDEO_HEVC)) {
Log.w(LOG_TAG, "Skip validation after muxing for mime = " + mMime);
continue;
}
// TODO (b/166300448) h263 and mpeg4 mimes fails validation
if (mMime.equals(MediaFormat.MIMETYPE_VIDEO_H263) ||
mMime.equals(MediaFormat.MIMETYPE_VIDEO_MPEG4)) {
Log.w(LOG_TAG, "Skip validation after muxing for mime = " + mMime);
continue;
}
// TODO (b/184889671) aac for profile AACObjectHE fails validation
// TODO (b/184890155) aac for profile AACObjectLD, AACObjectELD fails validation
if (mMime.equals(MediaFormat.MIMETYPE_AUDIO_AAC) &&
profile != AACObjectLC) {
Log.w(LOG_TAG, "Skip validation after muxing for mime = " + mMime +
" profile " + profile);
continue;
}
for (int muxerFormat = MediaMuxer.OutputFormat.MUXER_OUTPUT_FIRST;
muxerFormat <= MediaMuxer.OutputFormat.MUXER_OUTPUT_LAST; muxerFormat++) {
if (!MuxerTest.isCodecContainerPairValid(mMime, muxerFormat)) continue;
ByteBuffer mBuff = mOutputBuff.getBuffer();
mMuxer = new MediaMuxer(tempMuxedFile, muxerFormat);
try {
mMuxer.addTrack(outFormat);
mMuxer.start();
for (int i = 0; i < mInfoList.size(); i++) {
mMuxer.writeSampleData(0, mBuff, mInfoList.get(i));
}
mMuxer.stop();
} catch (Exception e) {
fail(log + "error! failed write to muxer format " + muxerFormat);
} finally {
mMuxer.release();
mMuxer = null;
}
MediaExtractor extractor = new MediaExtractor();
extractor.setDataSource(tempMuxedFile);
assertEquals("Should be only 1 track ", 1, extractor.getTrackCount());
MediaFormat extractedFormat = extractor.getTrackFormat(0);
assertTrue(log + "\nmuxer input config = " + outFormat +
"\ninput format and extracted format are not similar." +
"\nextracted format:" + extractedFormat +
"\ncontainer format = " + muxerFormat,
isFormatSimilar(format, extractedFormat));
extractor.release();
}
}
mCodec.release();
}
new File(tempMuxedFile).delete();
}
}