blob: f2501c0d84eead5938424eba01e777b7c369a0a4 [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.MediaCodecList;
import android.media.MediaExtractor;
import android.media.MediaFormat;
import android.media.MediaMuxer;
import android.os.Build;
import android.util.Log;
import android.util.Pair;
import android.view.Surface;
import androidx.test.filters.LargeTest;
import androidx.test.platform.app.InstrumentationRegistry;
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.Collection;
import java.util.List;
import java.util.Map;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
@RunWith(Parameterized.class)
public class CodecEncoderSurfaceTest {
private static final String LOG_TAG = CodecEncoderSurfaceTest.class.getSimpleName();
private static final String mInpPrefix = WorkDir.getMediaDirString();
private static final boolean ENABLE_LOGS = false;
private final String mMime;
private final String mTestFile;
private final int mBitrate;
private final int mFrameRate;
private final int mMaxBFrames;
private MediaExtractor mExtractor;
private MediaCodec mEncoder;
private CodecAsyncHandler mAsyncHandleEncoder;
private MediaCodec mDecoder;
private CodecAsyncHandler mAsyncHandleDecoder;
private boolean mIsCodecInAsyncMode;
private boolean mSignalEOSWithLastFrame;
private boolean mSawDecInputEOS;
private boolean mSawDecOutputEOS;
private boolean mSawEncOutputEOS;
private int mDecInputCount;
private int mDecOutputCount;
private int mEncOutputCount;
private boolean mSaveToMem;
private OutputManager mOutputBuff;
private Surface mSurface;
private MediaMuxer mMuxer;
private int mTrackID = -1;
static {
android.os.Bundle args = InstrumentationRegistry.getArguments();
CodecTestBase.codecSelKeys = args.getString(CodecTestBase.CODEC_SEL_KEY);
if (CodecTestBase.codecSelKeys == null)
CodecTestBase.codecSelKeys = CodecTestBase.CODEC_SEL_VALUE;
}
public CodecEncoderSurfaceTest(String mime, String testFile, int bitrate, int frameRate) {
mMime = mime;
mTestFile = testFile;
mBitrate = bitrate;
mFrameRate = frameRate;
mMaxBFrames = 0;
mAsyncHandleDecoder = new CodecAsyncHandler();
mAsyncHandleEncoder = new CodecAsyncHandler();
}
@Parameterized.Parameters(name = "{index}({0})")
public static Collection<Object[]> input() {
ArrayList<String> cddRequiredMimeList = new ArrayList<>();
if (CodecTestBase.isHandheld() || CodecTestBase.isTv() || CodecTestBase.isAutomotive()) {
// sec 2.2.2, 2.3.2, 2.5.2
cddRequiredMimeList.add(MediaFormat.MIMETYPE_VIDEO_AVC);
cddRequiredMimeList.add(MediaFormat.MIMETYPE_VIDEO_VP8);
}
final Object[][] exhaustiveArgsList = new Object[][]{
// Video - CodecMime, test file, bit rate, frame rate
{MediaFormat.MIMETYPE_VIDEO_H263, "bbb_176x144_128kbps_15fps_h263.3gp", 128000, 15},
{MediaFormat.MIMETYPE_VIDEO_MPEG4, "bbb_128x96_64kbps_12fps_mpeg4.mp4", 64000, 12},
{MediaFormat.MIMETYPE_VIDEO_AVC, "bbb_cif_768kbps_30fps_avc.mp4", 512000, 30},
{MediaFormat.MIMETYPE_VIDEO_HEVC, "bbb_cif_768kbps_30fps_avc.mp4", 512000, 30},
{MediaFormat.MIMETYPE_VIDEO_VP8, "bbb_cif_768kbps_30fps_avc.mp4", 512000, 30},
{MediaFormat.MIMETYPE_VIDEO_VP9, "bbb_cif_768kbps_30fps_avc.mp4", 512000, 30},
{MediaFormat.MIMETYPE_VIDEO_AV1, "bbb_cif_768kbps_30fps_avc.mp4", 512000, 30},
};
ArrayList<String> mimes = new ArrayList<>();
if (CodecTestBase.codecSelKeys.contains(CodecTestBase.CODEC_SEL_VALUE)) {
MediaCodecList codecList = new MediaCodecList(MediaCodecList.REGULAR_CODECS);
MediaCodecInfo[] codecInfos = codecList.getCodecInfos();
for (MediaCodecInfo codecInfo : codecInfos) {
if (!codecInfo.isEncoder()) continue;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q && codecInfo.isAlias()) continue;
String[] types = codecInfo.getSupportedTypes();
for (String type : types) {
if (!mimes.contains(type) && type.startsWith("video/")) {
mimes.add(type);
}
}
}
// TODO(b/154423708): add checks for video o/p port and display length >= 2.5"
/* sec 5.2: device implementations include an embedded screen display with the diagonal
length of at least 2.5inches or include a video output port or declare the support of a
camera */
if (CodecTestBase.hasCamera() && !mimes.contains(MediaFormat.MIMETYPE_VIDEO_AVC) &&
!mimes.contains(MediaFormat.MIMETYPE_VIDEO_VP8)) {
fail("device must support at least one of VP8 or AVC video encoders");
}
for (String mime : cddRequiredMimeList) {
if (!mimes.contains(mime)) {
fail("no codec found for mime " + mime + " as required by cdd");
}
}
} else {
for (Map.Entry<String, String> entry : CodecTestBase.codecSelKeyMimeMap.entrySet()) {
String key = entry.getKey();
String value = entry.getValue();
if (CodecTestBase.codecSelKeys.contains(key) && !mimes.contains(value)) {
mimes.add(value);
}
}
}
final List<Object[]> argsList = new ArrayList<>();
for (String mime : mimes) {
boolean miss = true;
for (Object[] arg : exhaustiveArgsList) {
if (mime.equals(arg[0])) {
argsList.add(arg);
miss = false;
}
}
if (miss) {
if (cddRequiredMimeList.contains(mime)) {
fail("no test vectors for required mimetype " + mime);
}
Log.w(LOG_TAG, "no test vectors available for optional mime type " + mime);
}
}
return argsList;
}
private boolean hasSeenError() {
return mAsyncHandleDecoder.hasSeenError() || mAsyncHandleEncoder.hasSeenError();
}
private MediaFormat setUpSource(String srcFile) throws IOException {
mExtractor = new MediaExtractor();
mExtractor.setDataSource(mInpPrefix + srcFile);
for (int trackID = 0; trackID < mExtractor.getTrackCount(); trackID++) {
MediaFormat format = mExtractor.getTrackFormat(trackID);
String mime = format.getString(MediaFormat.KEY_MIME);
if (mime.startsWith("video/")) {
mExtractor.selectTrack(trackID);
// COLOR_FormatYUV420Flexible by default should be supported by all components
// This call shouldn't effect configure() call for any codec
format.setInteger(MediaFormat.KEY_COLOR_FORMAT,
MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420Flexible);
return format;
}
}
mExtractor.release();
fail("No video track found in file: " + srcFile);
return null;
}
private void resetContext(boolean isAsync, boolean signalEOSWithLastFrame) {
mAsyncHandleDecoder.resetContext();
mAsyncHandleEncoder.resetContext();
mIsCodecInAsyncMode = isAsync;
mSignalEOSWithLastFrame = signalEOSWithLastFrame;
mSawDecInputEOS = false;
mSawDecOutputEOS = false;
mSawEncOutputEOS = false;
mDecInputCount = 0;
mDecOutputCount = 0;
mEncOutputCount = 0;
}
private void configureCodec(MediaFormat decFormat, MediaFormat encFormat, boolean isAsync,
boolean signalEOSWithLastFrame) {
resetContext(isAsync, signalEOSWithLastFrame);
mAsyncHandleEncoder.setCallBack(mEncoder, isAsync);
mEncoder.configure(encFormat, null, MediaCodec.CONFIGURE_FLAG_ENCODE, null);
mSurface = mEncoder.createInputSurface();
assertTrue("Surface is not valid", mSurface.isValid());
mAsyncHandleDecoder.setCallBack(mDecoder, isAsync);
mDecoder.configure(decFormat, mSurface, null, 0);
if (ENABLE_LOGS) {
Log.v(LOG_TAG, "codec configured");
}
}
private void enqueueDecoderEOS(int bufferIndex) {
if (!mSawDecInputEOS) {
mDecoder.queueInputBuffer(bufferIndex, 0, 0, 0, MediaCodec.BUFFER_FLAG_END_OF_STREAM);
mSawDecInputEOS = true;
if (ENABLE_LOGS) {
Log.v(LOG_TAG, "Queued End of Stream");
}
}
}
private void enqueueDecoderInput(int bufferIndex) {
if (mExtractor.getSampleSize() < 0) {
enqueueDecoderEOS(bufferIndex);
} else {
ByteBuffer inputBuffer = mDecoder.getInputBuffer(bufferIndex);
mExtractor.readSampleData(inputBuffer, 0);
int size = (int) mExtractor.getSampleSize();
long pts = mExtractor.getSampleTime();
int extractorFlags = mExtractor.getSampleFlags();
int codecFlags = 0;
if ((extractorFlags & MediaExtractor.SAMPLE_FLAG_SYNC) != 0) {
codecFlags |= MediaCodec.BUFFER_FLAG_KEY_FRAME;
}
if ((extractorFlags & MediaExtractor.SAMPLE_FLAG_PARTIAL_FRAME) != 0) {
codecFlags |= MediaCodec.BUFFER_FLAG_PARTIAL_FRAME;
}
if (!mExtractor.advance() && mSignalEOSWithLastFrame) {
codecFlags |= MediaCodec.BUFFER_FLAG_END_OF_STREAM;
mSawDecInputEOS = true;
}
if (ENABLE_LOGS) {
Log.v(LOG_TAG, "input: id: " + bufferIndex + " size: " + size + " pts: " + pts +
" flags: " + codecFlags);
}
mDecoder.queueInputBuffer(bufferIndex, 0, size, pts, codecFlags);
if (size > 0 && (codecFlags & (MediaCodec.BUFFER_FLAG_CODEC_CONFIG |
MediaCodec.BUFFER_FLAG_PARTIAL_FRAME)) == 0) {
mOutputBuff.saveInPTS(pts);
mDecInputCount++;
}
}
}
private void dequeueDecoderOutput(int bufferIndex, MediaCodec.BufferInfo info) {
if ((info.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) {
mSawDecOutputEOS = true;
}
if (ENABLE_LOGS) {
Log.v(LOG_TAG, "output: id: " + bufferIndex + " flags: " + info.flags + " size: " +
info.size + " timestamp: " + info.presentationTimeUs);
}
if (info.size > 0 && (info.flags & MediaCodec.BUFFER_FLAG_CODEC_CONFIG) == 0) {
mDecOutputCount++;
}
mDecoder.releaseOutputBuffer(bufferIndex, mSurface != null);
}
private void dequeueEncoderOutput(int bufferIndex, MediaCodec.BufferInfo info) {
if (ENABLE_LOGS) {
Log.v(LOG_TAG, "encoder output: id: " + bufferIndex + " flags: " + info.flags +
" size: " + info.size + " timestamp: " + info.presentationTimeUs);
}
if ((info.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) {
mSawEncOutputEOS = true;
}
if (info.size > 0) {
ByteBuffer buf = mEncoder.getOutputBuffer(bufferIndex);
if (mSaveToMem) {
mOutputBuff.saveToMemory(buf, info);
}
if (mMuxer != null) {
if (mTrackID == -1) {
mTrackID = mMuxer.addTrack(mEncoder.getOutputFormat());
mMuxer.start();
}
mMuxer.writeSampleData(mTrackID, buf, info);
}
if ((info.flags & MediaCodec.BUFFER_FLAG_CODEC_CONFIG) == 0) {
mOutputBuff.saveOutPTS(info.presentationTimeUs);
mEncOutputCount++;
}
}
mEncoder.releaseOutputBuffer(bufferIndex, false);
}
private void tryEncoderOutput(long timeOutUs) throws InterruptedException {
if (mIsCodecInAsyncMode) {
if (!hasSeenError() && !mSawEncOutputEOS) {
Pair<Integer, MediaCodec.BufferInfo> element = mAsyncHandleEncoder.getOutput();
if (element != null) {
dequeueEncoderOutput(element.first, element.second);
}
}
} else {
MediaCodec.BufferInfo outInfo = new MediaCodec.BufferInfo();
if (!mSawEncOutputEOS) {
int outputBufferId = mEncoder.dequeueOutputBuffer(outInfo, timeOutUs);
if (outputBufferId >= 0) {
dequeueEncoderOutput(outputBufferId, outInfo);
}
}
}
}
private void waitForAllEncoderOutputs() throws InterruptedException {
if (mIsCodecInAsyncMode) {
while (!hasSeenError() && !mSawEncOutputEOS) {
tryEncoderOutput(CodecTestBase.Q_DEQ_TIMEOUT_US);
}
} else {
while (!mSawEncOutputEOS) {
tryEncoderOutput(CodecTestBase.Q_DEQ_TIMEOUT_US);
}
}
}
private void queueEOS() throws InterruptedException {
if (mIsCodecInAsyncMode) {
while (!mAsyncHandleDecoder.hasSeenError() && !mSawDecInputEOS) {
Pair<Integer, MediaCodec.BufferInfo> element = mAsyncHandleDecoder.getWork();
if (element != null) {
int bufferID = element.first;
MediaCodec.BufferInfo info = element.second;
if (info != null) {
dequeueDecoderOutput(bufferID, info);
} else {
enqueueDecoderEOS(element.first);
}
}
}
} else if (!mSawDecInputEOS) {
enqueueDecoderEOS(mDecoder.dequeueInputBuffer(-1));
}
if (mIsCodecInAsyncMode) {
while (!hasSeenError() && !mSawDecOutputEOS) {
Pair<Integer, MediaCodec.BufferInfo> decOp = mAsyncHandleDecoder.getOutput();
if (decOp != null) dequeueDecoderOutput(decOp.first, decOp.second);
if (mSawDecOutputEOS) mEncoder.signalEndOfInputStream();
// TODO: remove fixed constant and change it according to encoder latency
if (mDecOutputCount - mEncOutputCount > mMaxBFrames) {
tryEncoderOutput(-1);
}
}
} else {
MediaCodec.BufferInfo outInfo = new MediaCodec.BufferInfo();
while (!mSawDecOutputEOS) {
int outputBufferId =
mDecoder.dequeueOutputBuffer(outInfo, CodecTestBase.Q_DEQ_TIMEOUT_US);
if (outputBufferId >= 0) {
dequeueDecoderOutput(outputBufferId, outInfo);
}
if (mSawDecOutputEOS) mEncoder.signalEndOfInputStream();
// TODO: remove fixed constant and change it according to encoder latency
if (mDecOutputCount - mEncOutputCount > mMaxBFrames) {
tryEncoderOutput(-1);
}
}
}
}
private void doWork(int frameLimit) throws InterruptedException {
int frameCnt = 0;
if (mIsCodecInAsyncMode) {
// dequeue output after inputEOS is expected to be done in waitForAllOutputs()
while (!hasSeenError() && !mSawDecInputEOS && frameCnt < frameLimit) {
Pair<Integer, MediaCodec.BufferInfo> element = mAsyncHandleDecoder.getWork();
if (element != null) {
int bufferID = element.first;
MediaCodec.BufferInfo info = element.second;
if (info != null) {
// <id, info> corresponds to output callback. Handle it accordingly
dequeueDecoderOutput(bufferID, info);
} else {
// <id, null> corresponds to input callback. Handle it accordingly
enqueueDecoderInput(bufferID);
frameCnt++;
}
}
// check decoder EOS
if (mSawDecOutputEOS) mEncoder.signalEndOfInputStream();
// encoder output
// TODO: remove fixed constant and change it according to encoder latency
if (mDecOutputCount - mEncOutputCount > mMaxBFrames) {
tryEncoderOutput(-1);
}
}
} else {
MediaCodec.BufferInfo outInfo = new MediaCodec.BufferInfo();
while (!mSawDecInputEOS && frameCnt < frameLimit) {
// decoder input
int inputBufferId = mDecoder.dequeueInputBuffer(CodecTestBase.Q_DEQ_TIMEOUT_US);
if (inputBufferId != -1) {
enqueueDecoderInput(inputBufferId);
frameCnt++;
}
// decoder output
int outputBufferId =
mDecoder.dequeueOutputBuffer(outInfo, CodecTestBase.Q_DEQ_TIMEOUT_US);
if (outputBufferId >= 0) {
dequeueDecoderOutput(outputBufferId, outInfo);
}
// check decoder EOS
if (mSawDecOutputEOS) mEncoder.signalEndOfInputStream();
// encoder output
// TODO: remove fixed constant and change it according to encoder latency
if (mDecOutputCount - mEncOutputCount > mMaxBFrames) {
tryEncoderOutput(-1);
}
}
}
}
private MediaFormat setUpEncoderFormat(MediaFormat decoderFormat) {
MediaFormat encoderFormat = new MediaFormat();
encoderFormat.setString(MediaFormat.KEY_MIME, mMime);
encoderFormat.setInteger(MediaFormat.KEY_WIDTH,
decoderFormat.getInteger(MediaFormat.KEY_WIDTH));
encoderFormat.setInteger(MediaFormat.KEY_HEIGHT,
decoderFormat.getInteger(MediaFormat.KEY_HEIGHT));
encoderFormat.setInteger(MediaFormat.KEY_FRAME_RATE, mFrameRate);
encoderFormat.setInteger(MediaFormat.KEY_BIT_RATE, mBitrate);
encoderFormat.setFloat(MediaFormat.KEY_I_FRAME_INTERVAL, 1.0f);
encoderFormat.setInteger(MediaFormat.KEY_COLOR_FORMAT,
MediaCodecInfo.CodecCapabilities.COLOR_FormatSurface);
encoderFormat.setInteger(MediaFormat.KEY_MAX_B_FRAMES, mMaxBFrames);
return encoderFormat;
}
/**
* Tests listed encoder components for sync and async mode in surface mode.The output has to
* be consistent (not flaky) in all runs.
*/
@LargeTest
@Test(timeout = CodecTestBase.PER_TEST_TIMEOUT_LARGE_TEST_MS)
public void testSimpleEncodeFromSurface() throws IOException, InterruptedException {
MediaFormat decoderFormat = setUpSource(mTestFile);
MediaCodecList codecList = new MediaCodecList(MediaCodecList.REGULAR_CODECS);
String decoder = codecList.findDecoderForFormat(decoderFormat);
if (decoder == null) {
mExtractor.release();
fail("no suitable decoder found for format: " + decoderFormat.toString());
}
mDecoder = MediaCodec.createByCodecName(decoder);
MediaFormat encoderFormat = setUpEncoderFormat(decoderFormat);
ArrayList<String> listOfEncoders = CodecTestBase.selectCodecs(mMime, null, null, true);
assertFalse("no suitable codecs found for mime: " + mMime, listOfEncoders.isEmpty());
boolean muxOutput = true;
for (String encoder : listOfEncoders) {
mEncoder = MediaCodec.createByCodecName(encoder);
/* TODO(b/149027258) */
mSaveToMem = false;
OutputManager ref = new OutputManager();
OutputManager test = new OutputManager();
int loopCounter = 0;
boolean[] boolStates = {true, false};
for (boolean isAsync : boolStates) {
mExtractor.seekTo(0, MediaExtractor.SEEK_TO_CLOSEST_SYNC);
mOutputBuff = loopCounter == 0 ? ref : test;
mOutputBuff.reset();
if (muxOutput && loopCounter == 0) {
String tmpPath;
int muxerFormat;
if (mMime.equals(MediaFormat.MIMETYPE_VIDEO_VP8) ||
mMime.equals(MediaFormat.MIMETYPE_VIDEO_VP9)) {
muxerFormat = MediaMuxer.OutputFormat.MUXER_OUTPUT_WEBM;
tmpPath = File.createTempFile("tmp", ".webm").getAbsolutePath();
} else {
muxerFormat = MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4;
tmpPath = File.createTempFile("tmp", ".mp4").getAbsolutePath();
}
mMuxer = new MediaMuxer(tmpPath, muxerFormat);
}
configureCodec(decoderFormat, encoderFormat, isAsync, false);
mEncoder.start();
mDecoder.start();
doWork(Integer.MAX_VALUE);
queueEOS();
waitForAllEncoderOutputs();
if (muxOutput) {
if (mTrackID != -1) {
mMuxer.stop();
mTrackID = -1;
}
if (mMuxer != null) {
mMuxer.release();
mMuxer = null;
}
}
/* TODO(b/147348711) */
if (false) mDecoder.stop();
else mDecoder.reset();
/* TODO(b/147348711) */
if (false) mEncoder.stop();
else mEncoder.reset();
String log = String.format(
"format: %s \n codec: %s, file: %s, mode: %s:: ",
encoderFormat, encoder, mTestFile, (isAsync ? "async" : "sync"));
assertTrue(log + " unexpected error", !hasSeenError());
assertTrue(log + "no input sent", 0 != mDecInputCount);
assertTrue(log + "no decoder output received", 0 != mDecOutputCount);
assertTrue(log + "no encoder output received", 0 != mEncOutputCount);
assertTrue(log + "decoder input count != output count, act/exp: " +
mDecOutputCount +
" / " + mDecInputCount, mDecInputCount == mDecOutputCount);
/* TODO(b/153127506)
* Currently disabling all encoder output checks. Added checks only for encoder
* timeStamp is in increasing order or not.
* Once issue is fixed remove increasing timestamp check and enable encoder checks.
*/
/*assertTrue(log + "encoder output count != decoder output count, act/exp: " +
mEncOutputCount + " / " + mDecOutputCount,
mEncOutputCount == mDecOutputCount);
if (loopCounter != 0) {
assertTrue(log + "encoder output is flaky", ref.equals(test));
} else {
assertTrue(log + " input pts list and output pts list are not identical",
ref.isOutPtsListIdenticalToInpPtsList((false)));
}*/
if (loopCounter != 0) {
assertTrue("test output pts is not strictly increasing",
test.isPtsStrictlyIncreasing(Long.MIN_VALUE));
} else {
assertTrue("ref output pts is not strictly increasing",
ref.isPtsStrictlyIncreasing(Long.MIN_VALUE));
}
loopCounter++;
mSurface.release();
mSurface = null;
}
mEncoder.release();
}
mDecoder.release();
mExtractor.release();
}
private native boolean nativeTestSimpleEncode(String encoder, String decoder, String mime,
String testFile, String muxFile, int bitrate, int framerate);
@LargeTest
@Test(timeout = CodecTestBase.PER_TEST_TIMEOUT_LARGE_TEST_MS)
public void testSimpleEncodeFromSurfaceNative() throws IOException {
MediaFormat decoderFormat = setUpSource(mTestFile);
MediaCodecList codecList = new MediaCodecList(MediaCodecList.REGULAR_CODECS);
String decoder = codecList.findDecoderForFormat(decoderFormat);
if (decoder == null) {
mExtractor.release();
fail("no suitable decoder found for format: " + decoderFormat.toString());
}
ArrayList<String> listOfEncoders = CodecTestBase.selectCodecs(mMime, null, null, true);
assertFalse("no suitable codecs found for mime: " + mMime, listOfEncoders.isEmpty());
for (String encoder : listOfEncoders) {
String tmpPath = null;
if (mMime.equals(MediaFormat.MIMETYPE_VIDEO_VP8) ||
mMime.equals(MediaFormat.MIMETYPE_VIDEO_VP9)) {
tmpPath = File.createTempFile("tmp", ".webm").getAbsolutePath();
} else {
tmpPath = File.createTempFile("tmp", ".mp4").getAbsolutePath();
}
assertTrue(
nativeTestSimpleEncode(encoder, decoder, mMime, mInpPrefix + mTestFile, tmpPath,
mBitrate, mFrameRate));
}
}
}