blob: 5f176783d59e49501bea39195aea94372aa0965c [file] [log] [blame]
/*
* Copyright (C) 2021 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.AudioFormat;
import android.media.MediaCodec;
import android.media.MediaFormat;
import androidx.test.filters.LargeTest;
import org.junit.Assume;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
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 java.util.Map;
import static android.media.MediaCodecInfo.CodecCapabilities.COLOR_FormatYUVP010;
import static android.mediav2.cts.CodecTestBase.SupportClass.*;
import static org.junit.Assert.*;
@RunWith(Parameterized.class)
public class CodecEncoderValidationTest extends CodecEncoderTestBase {
private final boolean mUseHBD;
// Key: mediaType, Value: tolerance duration in ms
private static final Map<String, Integer> toleranceMap = new HashMap<>();
static {
toleranceMap.put(MediaFormat.MIMETYPE_AUDIO_AAC, 20);
toleranceMap.put(MediaFormat.MIMETYPE_AUDIO_OPUS, 10);
toleranceMap.put(MediaFormat.MIMETYPE_AUDIO_AMR_NB, 10);
toleranceMap.put(MediaFormat.MIMETYPE_AUDIO_AMR_WB, 20);
toleranceMap.put(MediaFormat.MIMETYPE_AUDIO_FLAC, 0);
}
public CodecEncoderValidationTest(String encoder, String mediaType, int bitrate,
int encoderInfo1, int encoderInfo2, boolean useHBD) {
super(encoder, mediaType, new int[]{bitrate}, new int[]{encoderInfo1},
new int[]{encoderInfo2});
mUseHBD = useHBD;
}
private static List<Object[]> flattenParams(List<Object[]> params) {
List<Object[]> argsList = new ArrayList<>();
for (Object[] param : params) {
String mediaType = (String) param[0];
int[] bitRates = (int[]) param[1];
int[] infoList1 = (int[]) param[2];
int[] infoList2 = (int[]) param[3];
boolean useHBD = (boolean) param[4];
for (int bitrate : bitRates) {
for (int info1 : infoList1) {
for (int info2 : infoList2) {
argsList.add(new Object[]{mediaType, bitrate, info1, info2, useHBD});
}
}
}
}
return argsList;
}
@Parameterized.Parameters(name = "{index}({0}_{1}_{2}_{3}_{4}_{5})")
public static Collection<Object[]> input() {
final boolean isEncoder = true;
final boolean needAudio = true;
final boolean needVideo = true;
List<Object[]> defArgsList = new ArrayList<>(Arrays.asList(new Object[][]{
// Audio tests covering cdd sec 5.1.3
// mediaType, arrays of bit-rates, sample rates, channel counts, useHBD
{MediaFormat.MIMETYPE_AUDIO_AAC, new int[]{64000, 128000}, new int[]{8000, 12000,
16000, 22050, 24000, 32000, 44100, 48000}, new int[]{1, 2}, false},
{MediaFormat.MIMETYPE_AUDIO_OPUS, new int[]{64000, 128000}, new int[]{8000, 12000
, 16000, 24000, 48000}, new int[]{1, 2}, false},
{MediaFormat.MIMETYPE_AUDIO_AMR_NB, new int[]{4750, 5150, 5900, 6700, 7400, 7950,
10200, 12200}, new int[]{8000}, new int[]{1}, false},
{MediaFormat.MIMETYPE_AUDIO_AMR_WB, new int[]{6600, 8850, 12650, 14250, 15850,
18250, 19850, 23050, 23850}, new int[]{16000}, new int[]{1}, false},
/* TODO(169310292) */
{MediaFormat.MIMETYPE_AUDIO_FLAC, new int[]{/* 0, 1, 2, */ 3, 4, 5, 6, 7, 8},
new int[]{8000, 16000, 32000, 48000, 96000, 192000}, new int[]{1, 2},
false},
{MediaFormat.MIMETYPE_AUDIO_FLAC, new int[]{/* 0, 1, 2, */ 3, 4, 5, 6, 7, 8},
new int[]{8000, 16000, 32000, 48000, 96000, 192000}, new int[]{1, 2},
true},
// mediaType, arrays of bit-rates, width, height, useHBD
{MediaFormat.MIMETYPE_VIDEO_H263, new int[]{32000, 64000}, new int[]{176},
new int[]{144}, false},
{MediaFormat.MIMETYPE_VIDEO_MPEG4, new int[]{32000, 64000}, new int[]{176},
new int[]{144}, false},
{MediaFormat.MIMETYPE_VIDEO_AVC, new int[]{256000}, new int[]{352, 480},
new int[]{240, 360}, false},
{MediaFormat.MIMETYPE_VIDEO_HEVC, new int[]{256000}, new int[]{352, 480},
new int[]{240, 360}, false},
{MediaFormat.MIMETYPE_VIDEO_VP8, new int[]{256000}, new int[]{352, 480},
new int[]{240, 360}, false},
{MediaFormat.MIMETYPE_VIDEO_VP9, new int[]{256000}, new int[]{352, 480},
new int[]{240, 360}, false},
{MediaFormat.MIMETYPE_VIDEO_AV1, new int[]{256000}, new int[]{352, 480},
new int[]{240, 360}, false},
}));
// P010 support was added in Android T, hence limit the following tests to Android T and
// above
if (IS_AT_LEAST_T) {
defArgsList.addAll(Arrays.asList(new Object[][]{
{MediaFormat.MIMETYPE_VIDEO_AVC, new int[]{256000}, new int[]{352, 480},
new int[]{240, 360}, true},
{MediaFormat.MIMETYPE_VIDEO_HEVC, new int[]{256000}, new int[]{352, 480},
new int[]{240, 360}, true},
{MediaFormat.MIMETYPE_VIDEO_VP9, new int[]{256000}, new int[]{352, 480},
new int[]{240, 360}, true},
{MediaFormat.MIMETYPE_VIDEO_AV1, new int[]{256000}, new int[]{352, 480},
new int[]{240, 360}, true},
}));
}
List<Object[]> argsList = flattenParams(defArgsList);
return prepareParamList(argsList, isEncoder, needAudio, needVideo, false);
}
void encodeAndValidate(String inputFile) throws IOException, InterruptedException {
if (!mIsAudio) {
int colorFormat = mFormats.get(0).getInteger(MediaFormat.KEY_COLOR_FORMAT);
Assume.assumeTrue(hasSupportForColorFormat(mCodecName, mMime, colorFormat));
}
checkFormatSupport(mCodecName, mMime, true, mFormats, null, CODEC_OPTIONAL);
setUpSource(inputFile);
mOutputBuff = new OutputManager();
{
mCodec = MediaCodec.createByCodecName(mCodecName);
mSaveToMem = true;
for (MediaFormat inpFormat : mFormats) {
if (mIsAudio) {
mSampleRate = inpFormat.getInteger(MediaFormat.KEY_SAMPLE_RATE);
mChannels = inpFormat.getInteger(MediaFormat.KEY_CHANNEL_COUNT);
} else {
mWidth = inpFormat.getInteger(MediaFormat.KEY_WIDTH);
mHeight = inpFormat.getInteger(MediaFormat.KEY_HEIGHT);
}
String log = String.format("format: %s \n codec: %s, file: %s :: ", inpFormat,
mCodecName, inputFile);
mOutputBuff.reset();
mInfoList.clear();
configureCodec(inpFormat, false, true, true);
mCodec.start();
doWork(Integer.MAX_VALUE);
queueEOS();
waitForAllOutputs();
if (mUseHBD && mIsAudio) {
assertEquals(AudioFormat.ENCODING_PCM_FLOAT,
mCodec.getOutputFormat().getInteger(MediaFormat.KEY_PCM_ENCODING));
}
mCodec.reset();
assertFalse(log + "unexpected error", mAsyncHandle.hasSeenError());
assertTrue(log + "no input sent", 0 != mInputCount);
assertTrue(log + "no output received", 0 != mOutputCount);
if (!mIsAudio) {
assertEquals(log + "input count != output count, act/exp: " + mOutputCount +
" / " + mInputCount, mInputCount, mOutputCount);
} else {
assertTrue(log + " pts is not strictly increasing",
mOutputBuff.isPtsStrictlyIncreasing(mPrevOutputPts));
}
ArrayList<MediaFormat> fmts = new ArrayList<>();
fmts.add(mOutFormat);
ArrayList<String> listOfDecoders = selectCodecs(mMime, fmts, null, false);
assertFalse("no suitable codecs found for mediaType: " + mMime,
listOfDecoders.isEmpty());
CodecDecoderTestBase cdtb =
new CodecDecoderTestBase(listOfDecoders.get(0), mMime, null);
cdtb.mOutputBuff = new OutputManager();
cdtb.mSaveToMem = true;
cdtb.mCodec = MediaCodec.createByCodecName(cdtb.mCodecName);
cdtb.configureCodec(mOutFormat, false, true, false);
cdtb.mCodec.start();
cdtb.doWork(mOutputBuff.getBuffer(), mInfoList);
cdtb.queueEOS();
cdtb.waitForAllOutputs();
if (mUseHBD && mIsAudio) {
assertEquals(AudioFormat.ENCODING_PCM_FLOAT,
cdtb.mOutFormat.getInteger(MediaFormat.KEY_PCM_ENCODING));
}
cdtb.mCodec.stop();
cdtb.mCodec.release();
ByteBuffer out = cdtb.mOutputBuff.getBuffer();
if (isCodecLossless(mMime)) {
if (mUseHBD && mMime.equals(MediaFormat.MIMETYPE_AUDIO_FLAC)) {
CodecDecoderTest.verify(cdtb.mOutputBuff, inputFile, 3.446394f,
AudioFormat.ENCODING_PCM_FLOAT, -1L);
} else {
assertEquals(log + "identity test failed", out,
ByteBuffer.wrap(mInputData));
}
}
if (!mIsAudio) {
assertEquals(log + "input frames queued != output frames of decoder, " +
"act/exp: " + mInputCount + " / " + cdtb.mOutputCount,
mInputCount, cdtb.mOutputCount);
assertTrue(cdtb.mOutputBuff.isOutPtsListIdenticalToInpPtsList(true));
} else {
int tolerance = toleranceMap.get(mMime) * mSampleRate * mChannels *
mBytesPerSample / 1000;
assertTrue(log + "out bytes + tolerance < input bytes, act/exp: " +
out.limit() + " + " + tolerance + " > " + mInputData.length,
mInputData.length <= out.limit() + tolerance);
assertTrue(cdtb.mOutputBuff.isPtsStrictlyIncreasing(mPrevOutputPts));
}
}
mCodec.release();
}
}
@LargeTest
@Test(timeout = PER_TEST_TIMEOUT_LARGE_TEST_MS)
public void testEncodeAndValidate() throws IOException, InterruptedException {
setUpParams(Integer.MAX_VALUE);
String inputFile = mInputFile;
if (mUseHBD) {
if (mIsAudio) {
for (MediaFormat format : mFormats) {
format.setInteger(MediaFormat.KEY_PCM_ENCODING, AudioFormat.ENCODING_PCM_FLOAT);
}
mBytesPerSample = 4;
inputFile = INPUT_AUDIO_FILE_HBD;
} else {
for (MediaFormat format : mFormats) {
format.setInteger(MediaFormat.KEY_COLOR_FORMAT, COLOR_FormatYUVP010);
}
mBytesPerSample = 2;
inputFile = INPUT_VIDEO_FILE_HBD;
}
}
encodeAndValidate(inputFile);
}
}