blob: 589c8502749ec2c6eab365b4a02186f66a29cec9 [file] [log] [blame]
/*
* Copyright (C) 2022 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.videocodec.cts;
import static android.media.MediaFormat.PICTURE_TYPE_I;
import static android.media.MediaFormat.PICTURE_TYPE_UNKNOWN;
import static android.videocodec.cts.VideoEncoderInput.BIRTHDAY_FULLHD_LANDSCAPE;
import static android.videocodec.cts.VideoEncoderInput.RES_YUV_MAP;
import static android.videocodec.cts.VideoEncoderInput.RIVER_HD_LANDSCAPE;
import static android.videocodec.cts.VideoEncoderInput.SELFIEGROUP_FULLHD_PORTRAIT;
import static android.videocodec.cts.VideoEncoderInput.getRawResource;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.fail;
import android.media.Image;
import android.media.MediaCodec;
import android.media.MediaFormat;
import android.mediav2.common.cts.BitStreamUtils;
import android.mediav2.common.cts.CodecEncoderTestBase;
import android.mediav2.common.cts.DecodeStreamToYuv;
import android.mediav2.common.cts.EncoderConfigParams;
import android.mediav2.common.cts.RawResource;
import android.util.Log;
import com.android.compatibility.common.util.Preconditions;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Objects;
import java.util.TreeMap;
/**
* Wrapper class for handling and testing video encoder components.
*/
public class VideoEncoderValidationTestBase extends CodecEncoderTestBase {
private static final String LOG_TAG = VideoEncoderValidationTestBase.class.getSimpleName();
protected static final boolean ENABLE_LOGS = false;
protected static final StringBuilder DIAGNOSTICS = new StringBuilder();
protected BitStreamUtils.ParserBase mParser;
final TreeMap<Long, Integer> mPtsPicTypeMap = new TreeMap<>();
RandomAccessFile mFileInp;
long mFileReadOffset;
long mFileLength;
static void logAllFilesInCacheDir(boolean isStartOfTest) {
if (isStartOfTest) DIAGNOSTICS.setLength(0);
String cacheDir = CONTEXT.getCacheDir().toString();
DIAGNOSTICS.append(String.format("\nThe state of cache dir : %s, %s is :", cacheDir,
isStartOfTest ? "at start" : "now"));
File dir = new File(cacheDir);
if (dir.exists()) {
for (File f : Objects.requireNonNull(dir.listFiles())) {
DIAGNOSTICS.append(f.getName()).append("\n");
}
} else {
DIAGNOSTICS.append(" directory not present");
}
}
static void decodeStreamsToYuv(List<VideoEncoderInput.CompressedResource> resources,
HashMap<String, RawResource> streamYuvMap, String prefix) {
decodeStreamsToYuv(resources, streamYuvMap, Integer.MAX_VALUE, prefix);
}
static void decodeStreamsToYuv(List<VideoEncoderInput.CompressedResource> resources,
HashMap<String, RawResource> streamYuvMap, int frameLimit, String prefix) {
logAllFilesInCacheDir(true);
for (VideoEncoderInput.CompressedResource res : resources) {
decodeStreamsToYuv(res, streamYuvMap, frameLimit, prefix);
}
if (streamYuvMap.values().stream().allMatch(Objects::isNull)) {
decodeStreamsToYuv(RIVER_HD_LANDSCAPE, streamYuvMap, frameLimit, prefix);
}
}
static void decodeStreamsToYuv(VideoEncoderInput.CompressedResource res,
HashMap<String, RawResource> streamYuvMap, int frameLimit, String prefix) {
if (!(streamYuvMap.containsKey(res.uniqueLabel()))) {
try {
DecodeStreamToYuv yuv = new DecodeStreamToYuv(res.mMediaType, res.mResFile,
frameLimit, prefix);
streamYuvMap.put(res.uniqueLabel(), yuv.getDecodedYuv());
} catch (Exception e) {
streamYuvMap.put(res.uniqueLabel(), null);
DIAGNOSTICS.append(String.format("\nWhile decoding the resource : %s,"
+ " encountered exception : %s was thrown", res, e));
logAllFilesInCacheDir(false);
}
}
}
@BeforeClass
public static void decodeResourcesToYuv() {
ArrayList<VideoEncoderInput.CompressedResource> resources = new ArrayList<>();
resources.add(BIRTHDAY_FULLHD_LANDSCAPE);
resources.add(SELFIEGROUP_FULLHD_PORTRAIT);
decodeStreamsToYuv(resources, RES_YUV_MAP, LOG_TAG);
}
@AfterClass
public static void cleanUpResources() {
for (RawResource res : RES_YUV_MAP.values()) {
if (res != null) {
new File(res.mFileName).delete();
}
}
RES_YUV_MAP.clear();
}
VideoEncoderValidationTestBase(String encoder, String mediaType,
EncoderConfigParams encCfgParams, String allTestParams) {
super(encoder, mediaType, new EncoderConfigParams[]{encCfgParams}, allTestParams);
}
protected void setUpSource(String inpPath) throws IOException {
Preconditions.assertTestFileExists(inpPath);
mFileInp = new RandomAccessFile(inpPath, "r");
mInputData = null;
mFileReadOffset = 0L;
mFileLength = mFileInp.length();
}
protected void resetContext(boolean isAsync, boolean signalEOSWithLastFrame) {
super.resetContext(isAsync, signalEOSWithLastFrame);
mPtsPicTypeMap.clear();
}
protected void encodeToMemory(String encoder, EncoderConfigParams cfg, int frameLimit,
boolean saveToMem, boolean muxOutput) throws IOException, InterruptedException {
RawResource res = getRawResource(cfg);
assertNotNull("no raw resource found for testing config : " + mEncCfgParams[0]
+ mTestConfig + mTestEnv + DIAGNOSTICS, res);
super.encodeToMemory(encoder, cfg, res, frameLimit, saveToMem, muxOutput);
}
protected void enqueueInput(int bufferIndex) {
int frmSize = 3 * mActiveRawRes.mBytesPerSample * mActiveRawRes.mWidth
* mActiveRawRes.mHeight / 2;
if (mInputData == null || mInputData.length != frmSize) {
mInputData = new byte[frmSize];
}
int bytesRead = 0;
try {
bytesRead = mFileInp.read(mInputData);
if (mIsLoopBack && mInputCount < mLoopBackFrameLimit && bytesRead == -1) {
mFileInp.seek(0);
bytesRead = mFileInp.read(mInputData);
}
} catch (IOException e) {
fail("encountered exception during file read." + e + "\n" + mTestConfig + mTestEnv);
}
if (bytesRead != -1 && bytesRead != frmSize) {
fail("received partial frame to encode \n" + mTestConfig + mTestEnv);
}
if (bytesRead == -1) {
assertEquals("mFileReadOffset, mFileLength and EOS state are not in sync \n"
+ mTestConfig + mTestEnv, mFileReadOffset, mFileLength);
enqueueEOS(bufferIndex);
} else {
int size = mActiveRawRes.mBytesPerSample * mActiveEncCfg.mWidth
* mActiveEncCfg.mHeight * 3 / 2;
int flags = 0;
long pts = mInputOffsetPts + mInputCount * 1000000L / mActiveEncCfg.mFrameRate;
Image img = mCodec.getInputImage(bufferIndex);
assertNotNull("CPU-read via ImageReader API is not available \n" + mTestConfig
+ mTestEnv, img);
fillImage(img);
if (mSignalEOSWithLastFrame) {
if (mIsLoopBack ? (mInputCount + 1 >= mLoopBackFrameLimit) :
(mFileReadOffset + frmSize >= mFileLength)) {
flags |= MediaCodec.BUFFER_FLAG_END_OF_STREAM;
mSawInputEOS = true;
}
}
mNumBytesSubmitted += size;
mFileReadOffset += frmSize;
mCodec.queueInputBuffer(bufferIndex, 0, size, pts, flags);
if (ENABLE_LOGS) {
Log.v(LOG_TAG, "input: id: " + bufferIndex + " size: " + size + " pts: " + pts
+ " flags: " + flags);
}
mOutputBuff.saveInPTS(pts);
mInputCount++;
}
}
protected void dequeueOutput(int bufferIndex, MediaCodec.BufferInfo info) {
if (info.size > 0 && ((info.flags & MediaCodec.BUFFER_FLAG_CODEC_CONFIG) == 0)) {
int picType = PICTURE_TYPE_UNKNOWN;
if ((info.flags & MediaCodec.BUFFER_FLAG_KEY_FRAME) != 0) {
picType = PICTURE_TYPE_I;
}
if (picType == PICTURE_TYPE_UNKNOWN) {
MediaFormat format = mCodec.getOutputFormat(bufferIndex);
picType = format.getInteger(MediaFormat.KEY_PICTURE_TYPE, PICTURE_TYPE_UNKNOWN);
}
if (picType == PICTURE_TYPE_UNKNOWN && mParser != null) {
ByteBuffer buf = mCodec.getOutputBuffer(bufferIndex);
picType = BitStreamUtils.getFrameTypeFromBitStream(buf, info, mParser);
}
mPtsPicTypeMap.put(info.presentationTimeUs, picType);
}
super.dequeueOutput(bufferIndex, info);
}
}