| /* |
| * Copyright (C) 2019 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.graphics.ImageFormat; |
| import android.media.Image; |
| import android.media.MediaCodec; |
| import android.media.MediaCodecInfo; |
| import android.media.MediaFormat; |
| import android.os.Bundle; |
| import android.os.PersistableBundle; |
| import android.util.Log; |
| |
| import androidx.test.filters.LargeTest; |
| import androidx.test.filters.SmallTest; |
| |
| import org.junit.Assert; |
| import org.junit.Assume; |
| import org.junit.Ignore; |
| import org.junit.Test; |
| import org.junit.runner.RunWith; |
| import org.junit.runners.Parameterized; |
| |
| import java.io.File; |
| import java.io.FileInputStream; |
| import java.io.IOException; |
| import java.nio.ByteBuffer; |
| import java.util.ArrayList; |
| import java.util.Arrays; |
| import java.util.Collection; |
| import java.util.List; |
| |
| import static org.junit.Assert.assertFalse; |
| import static org.junit.Assert.assertTrue; |
| import static org.junit.Assert.fail; |
| |
| /** |
| * Validate encode functionality of listed encoder components |
| * |
| * The test aims to test all encoders advertised in MediaCodecList. Hence we are not using |
| * MediaCodecList#findEncoderForFormat to create codec. Further, it can so happen that the |
| * test clip chosen is not supported by component (codecCapabilities.isFormatSupported() |
| * fails), then it is better to remove the format but not skip testing the component. The idea |
| * of these tests are not to cover CDD requirements but to test components and their plugins |
| */ |
| @RunWith(Parameterized.class) |
| public class CodecEncoderTest extends CodecTestBase { |
| private static final String LOG_TAG = CodecEncoderTest.class.getSimpleName(); |
| // files are in WorkDir.getMediaDirString(); |
| private static final String mInputAudioFile = "bbb_2ch_44kHz_s16le.raw"; |
| private static final String mInputVideoFile = "bbb_cif_yuv420p_30fps.yuv"; |
| private final int INP_FRM_WIDTH = 352; |
| private final int INP_FRM_HEIGHT = 288; |
| |
| private final String mMime; |
| private final int[] mBitrates; |
| private final int[] mEncParamList1; |
| private final int[] mEncParamList2; |
| private final String mInputFile; |
| private ArrayList<MediaFormat> mFormats; |
| private byte[] mInputData; |
| private int mNumBytesSubmitted; |
| private long mInputOffsetPts; |
| private int mNumSyncFramesReceived; |
| private ArrayList<Integer> mSyncFramesPos; |
| |
| private int mWidth, mHeight; |
| private int mChannels; |
| private int mSampleRate; |
| private int mFrameRate; |
| private int mMaxBFrames; |
| |
| public CodecEncoderTest(String mime, int[] bitrates, int[] encoderInfo1, int[] encoderInfo2) { |
| mMime = mime; |
| mFrameRate = 30; |
| if (mime.equals(MediaFormat.MIMETYPE_VIDEO_MPEG4)) mFrameRate = 12; |
| else if (mime.equals(MediaFormat.MIMETYPE_VIDEO_H263)) mFrameRate = 12; |
| mMaxBFrames = 0; |
| mBitrates = bitrates; |
| mEncParamList1 = encoderInfo1; |
| mEncParamList2 = encoderInfo2; |
| mFormats = new ArrayList<>(); |
| mSyncFramesPos = new ArrayList<>(); |
| mAsyncHandle = new CodecAsyncHandler(); |
| mIsAudio = mMime.startsWith("audio/"); |
| mInputFile = mIsAudio ? mInputAudioFile : mInputVideoFile; |
| } |
| |
| @Override |
| void resetContext(boolean isAsync, boolean signalEOSWithLastFrame) { |
| super.resetContext(isAsync, signalEOSWithLastFrame); |
| mNumBytesSubmitted = 0; |
| mInputOffsetPts = 0; |
| mNumSyncFramesReceived = 0; |
| mSyncFramesPos.clear(); |
| } |
| |
| @Override |
| void flushCodec() { |
| super.flushCodec(); |
| if (mIsAudio) { |
| mInputOffsetPts = |
| (mNumBytesSubmitted + 1024) * 1000000L / (2 * mChannels * mSampleRate); |
| } else { |
| mInputOffsetPts = (mInputCount + 5) * 1000000L / mFrameRate; |
| } |
| mPrevOutputPts = mInputOffsetPts - 1; |
| mNumBytesSubmitted = 0; |
| mNumSyncFramesReceived = 0; |
| mSyncFramesPos.clear(); |
| } |
| |
| private void setUpSource(String srcFile) throws IOException { |
| String inpPath = mInpPrefix + srcFile; |
| try (FileInputStream fInp = new FileInputStream(inpPath)) { |
| int size = (int) new File(inpPath).length(); |
| mInputData = new byte[size]; |
| fInp.read(mInputData, 0, size); |
| } |
| } |
| |
| void fillImage(Image image) { |
| Assert.assertTrue(image.getFormat() == ImageFormat.YUV_420_888); |
| int imageWidth = image.getWidth(); |
| int imageHeight = image.getHeight(); |
| Image.Plane[] planes = image.getPlanes(); |
| int offset = mNumBytesSubmitted; |
| for (int i = 0; i < planes.length; ++i) { |
| ByteBuffer buf = planes[i].getBuffer(); |
| int width = imageWidth; |
| int height = imageHeight; |
| int tileWidth = INP_FRM_WIDTH; |
| int tileHeight = INP_FRM_HEIGHT; |
| int rowStride = planes[i].getRowStride(); |
| int pixelStride = planes[i].getPixelStride(); |
| if (i != 0) { |
| width = imageWidth / 2; |
| height = imageHeight / 2; |
| tileWidth = INP_FRM_WIDTH / 2; |
| tileHeight = INP_FRM_HEIGHT / 2; |
| } |
| if (pixelStride == 1) { |
| if (width == rowStride && width == tileWidth && height == tileHeight) { |
| buf.put(mInputData, offset, width * height); |
| } else { |
| for (int z = 0; z < height; z += tileHeight) { |
| int rowsToCopy = Math.min(height - z, tileHeight); |
| for (int y = 0; y < rowsToCopy; y++) { |
| for (int x = 0; x < width; x += tileWidth) { |
| int colsToCopy = Math.min(width - x, tileWidth); |
| buf.position((z + y) * rowStride + x); |
| buf.put(mInputData, offset + y * tileWidth, colsToCopy); |
| } |
| } |
| } |
| } |
| } else { |
| // do it pixel-by-pixel |
| for (int z = 0; z < height; z += tileHeight) { |
| int rowsToCopy = Math.min(height - z, tileHeight); |
| for (int y = 0; y < rowsToCopy; y++) { |
| int lineOffset = (z + y) * rowStride; |
| for (int x = 0; x < width; x += tileWidth) { |
| int colsToCopy = Math.min(width - x, tileWidth); |
| for (int w = 0; w < colsToCopy; w++) { |
| buf.position(lineOffset + (x + w) * pixelStride); |
| buf.put(mInputData[offset + y * tileWidth + w]); |
| } |
| } |
| } |
| } |
| } |
| offset += tileWidth * tileHeight; |
| } |
| } |
| |
| void fillByteBuffer(ByteBuffer inputBuffer) { |
| int offset = 0, frmOffset = mNumBytesSubmitted; |
| for (int plane = 0; plane < 3; plane++) { |
| int width = mWidth; |
| int height = mHeight; |
| int tileWidth = INP_FRM_WIDTH; |
| int tileHeight = INP_FRM_HEIGHT; |
| if (plane != 0) { |
| width = mWidth / 2; |
| height = mHeight / 2; |
| tileWidth = INP_FRM_WIDTH / 2; |
| tileHeight = INP_FRM_HEIGHT / 2; |
| } |
| for (int k = 0; k < height; k += tileHeight) { |
| int rowsToCopy = Math.min(height - k, tileHeight); |
| for (int j = 0; j < rowsToCopy; j++) { |
| for (int i = 0; i < width; i += tileWidth) { |
| int colsToCopy = Math.min(width - i, tileWidth); |
| inputBuffer.position(offset + (k + j) * width + i); |
| inputBuffer.put(mInputData, frmOffset + j * tileWidth, colsToCopy); |
| } |
| } |
| } |
| offset += width * height; |
| frmOffset += tileWidth * tileHeight; |
| } |
| } |
| |
| void enqueueInput(int bufferIndex) { |
| ByteBuffer inputBuffer = mCodec.getInputBuffer(bufferIndex); |
| if (mNumBytesSubmitted >= mInputData.length) { |
| enqueueEOS(bufferIndex); |
| } else { |
| int size; |
| int flags = 0; |
| long pts = mInputOffsetPts; |
| if (mIsAudio) { |
| pts += mNumBytesSubmitted * 1000000L / (2 * mChannels * mSampleRate); |
| size = Math.min(inputBuffer.capacity(), mInputData.length - mNumBytesSubmitted); |
| inputBuffer.put(mInputData, mNumBytesSubmitted, size); |
| if (mNumBytesSubmitted + size >= mInputData.length && mSignalEOSWithLastFrame) { |
| flags |= MediaCodec.BUFFER_FLAG_END_OF_STREAM; |
| mSawInputEOS = true; |
| } |
| mNumBytesSubmitted += size; |
| } else { |
| pts += mInputCount * 1000000L / mFrameRate; |
| size = mWidth * mHeight * 3 / 2; |
| int frmSize = INP_FRM_WIDTH * INP_FRM_HEIGHT * 3 / 2; |
| if (mNumBytesSubmitted + frmSize > mInputData.length) { |
| fail("received partial frame to encode"); |
| } else { |
| Image img = mCodec.getInputImage(bufferIndex); |
| if (img != null) { |
| fillImage(img); |
| } else { |
| if (mWidth == INP_FRM_WIDTH && mHeight == INP_FRM_HEIGHT) { |
| inputBuffer.put(mInputData, mNumBytesSubmitted, size); |
| } else { |
| fillByteBuffer(inputBuffer); |
| } |
| } |
| } |
| if (mNumBytesSubmitted + frmSize >= mInputData.length && mSignalEOSWithLastFrame) { |
| flags |= MediaCodec.BUFFER_FLAG_END_OF_STREAM; |
| mSawInputEOS = true; |
| } |
| mNumBytesSubmitted += frmSize; |
| } |
| if (ENABLE_LOGS) { |
| Log.v(LOG_TAG, "input: id: " + bufferIndex + " size: " + size + " pts: " + pts + |
| " flags: " + flags); |
| } |
| mCodec.queueInputBuffer(bufferIndex, 0, size, pts, flags); |
| mOutputBuff.saveInPTS(pts); |
| mInputCount++; |
| } |
| } |
| |
| void dequeueOutput(int bufferIndex, MediaCodec.BufferInfo info) { |
| if (ENABLE_LOGS) { |
| Log.v(LOG_TAG, "output: id: " + bufferIndex + " flags: " + info.flags + " size: " + |
| info.size + " timestamp: " + info.presentationTimeUs); |
| } |
| if ((info.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) { |
| mSawOutputEOS = true; |
| } |
| if (info.size > 0) { |
| if (mSaveToMem) { |
| ByteBuffer buf = mCodec.getOutputBuffer(bufferIndex); |
| mOutputBuff.saveToMemory(buf, info); |
| } |
| if ((info.flags & MediaCodec.BUFFER_FLAG_CODEC_CONFIG) == 0) { |
| mOutputBuff.saveOutPTS(info.presentationTimeUs); |
| mOutputCount++; |
| } |
| if ((info.flags & MediaCodec.BUFFER_FLAG_KEY_FRAME) != 0) { |
| mNumSyncFramesReceived += 1; |
| mSyncFramesPos.add(mOutputCount); |
| } |
| } |
| mCodec.releaseOutputBuffer(bufferIndex, false); |
| } |
| |
| private void encodeToMemory(String file, String encoder, int frameLimit, MediaFormat format) |
| throws IOException, InterruptedException { |
| /* TODO(b/149027258) */ |
| if (true) mSaveToMem = false; |
| else mSaveToMem = true; |
| mOutputBuff = new OutputManager(); |
| mCodec = MediaCodec.createByCodecName(encoder); |
| setUpSource(file); |
| configureCodec(format, false, true, true); |
| if (mIsAudio) { |
| mSampleRate = format.getInteger(MediaFormat.KEY_SAMPLE_RATE); |
| mChannels = format.getInteger(MediaFormat.KEY_CHANNEL_COUNT); |
| } else { |
| mWidth = format.getInteger(MediaFormat.KEY_WIDTH); |
| mHeight = format.getInteger(MediaFormat.KEY_HEIGHT); |
| } |
| mCodec.start(); |
| doWork(frameLimit); |
| queueEOS(); |
| waitForAllOutputs(); |
| mCodec.stop(); |
| mCodec.release(); |
| mSaveToMem = false; |
| } |
| |
| /** |
| * Selects encoder input color format in byte buffer mode. As of now ndk tests support only |
| * 420p, 420sp. COLOR_FormatYUV420Flexible although can represent any form of yuv, it doesn't |
| * work in ndk due to lack of AMediaCodec_GetInputImage() |
| */ |
| private static int findByteBufferColorFormat(String encoder, String mime) throws IOException { |
| MediaCodec codec = MediaCodec.createByCodecName(encoder); |
| MediaCodecInfo.CodecCapabilities cap = codec.getCodecInfo().getCapabilitiesForType(mime); |
| int colorFormat = -1; |
| for (int c : cap.colorFormats) { |
| if (c == MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420SemiPlanar || |
| c == MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420Planar) { |
| Log.v(LOG_TAG, "selecting color format: " + c); |
| colorFormat = c; |
| break; |
| } |
| } |
| codec.release(); |
| return colorFormat; |
| } |
| |
| @Override |
| PersistableBundle validateMetrics(String codec, MediaFormat format) { |
| PersistableBundle metrics = super.validateMetrics(codec, format); |
| assertTrue(metrics.getString(MediaCodec.MetricsConstants.MIME_TYPE).equals(mMime)); |
| assertTrue(metrics.getInt(MediaCodec.MetricsConstants.ENCODER) == 1); |
| return metrics; |
| } |
| |
| private void forceSyncFrame() { |
| final Bundle syncFrame = new Bundle(); |
| syncFrame.putInt(MediaCodec.PARAMETER_KEY_REQUEST_SYNC_FRAME, 0); |
| if (ENABLE_LOGS) { |
| Log.v(LOG_TAG, "requesting key frame"); |
| } |
| mCodec.setParameters(syncFrame); |
| } |
| |
| private void updateBitrate(int bitrate) { |
| final Bundle bitrateUpdate = new Bundle(); |
| bitrateUpdate.putInt(MediaCodec.PARAMETER_KEY_VIDEO_BITRATE, bitrate); |
| if (ENABLE_LOGS) { |
| Log.v(LOG_TAG, "requesting bitrate to be changed to " + bitrate); |
| } |
| mCodec.setParameters(bitrateUpdate); |
| } |
| |
| @Parameterized.Parameters(name = "{index}({0})") |
| public static Collection<Object[]> input() { |
| final ArrayList<String> cddRequiredMimeList = |
| new ArrayList<>(Arrays.asList( |
| MediaFormat.MIMETYPE_AUDIO_FLAC, |
| MediaFormat.MIMETYPE_AUDIO_OPUS, |
| MediaFormat.MIMETYPE_AUDIO_AAC, |
| MediaFormat.MIMETYPE_AUDIO_AMR_NB, |
| MediaFormat.MIMETYPE_AUDIO_AMR_WB, |
| MediaFormat.MIMETYPE_VIDEO_MPEG4, |
| MediaFormat.MIMETYPE_VIDEO_H263, |
| MediaFormat.MIMETYPE_VIDEO_AVC, |
| MediaFormat.MIMETYPE_VIDEO_HEVC, |
| MediaFormat.MIMETYPE_VIDEO_VP8, |
| MediaFormat.MIMETYPE_VIDEO_VP9)); |
| final List<Object[]> exhaustiveArgsList = Arrays.asList(new Object[][]{ |
| // Audio - CodecMime, arrays of bit-rates, sample rates, channel counts |
| {MediaFormat.MIMETYPE_AUDIO_AAC, new int[]{64000, 128000}, new int[]{8000, 11025, |
| 22050, 44100, 48000}, new int[]{1, 2}}, |
| {MediaFormat.MIMETYPE_AUDIO_OPUS, new int[]{6600, 8850, 12650, 14250, 15850, |
| 18250, 19850, 23050, 23850}, new int[]{16000}, new int[]{1}}, |
| {MediaFormat.MIMETYPE_AUDIO_AMR_NB, new int[]{4750, 5150, 5900, 6700, 7400, 7950, |
| 10200, 12200}, new int[]{8000}, new int[]{1}}, |
| {MediaFormat.MIMETYPE_AUDIO_AMR_WB, new int[]{6600, 8850, 12650, 14250, 15850, |
| 18250, 19850, 23050, 23850}, new int[]{16000}, new int[]{1}}, |
| {MediaFormat.MIMETYPE_AUDIO_FLAC, new int[]{64000, 192000}, new int[]{8000, 48000 |
| , 96000, 192000}, new int[]{1, 2}}, |
| |
| // Video - CodecMime, arrays of bit-rates, height, width |
| {MediaFormat.MIMETYPE_VIDEO_H263, new int[]{32000, 64000}, new int[]{176}, |
| new int[]{144}}, |
| {MediaFormat.MIMETYPE_VIDEO_MPEG4, new int[]{32000, 64000}, new int[]{176}, |
| new int[]{144}}, |
| {MediaFormat.MIMETYPE_VIDEO_AVC, new int[]{256000, 512000}, new int[]{176, 352, |
| 352, 480}, new int[]{144, 240, 288, 360}}, |
| {MediaFormat.MIMETYPE_VIDEO_HEVC, new int[]{256000, 512000}, new int[]{176, 352, |
| 352, 480}, new int[]{144, 240, 288, 360}}, |
| {MediaFormat.MIMETYPE_VIDEO_VP8, new int[]{256000, 512000}, new int[]{176, 352, |
| 352, 480}, new int[]{144, 240, 288, 360}}, |
| {MediaFormat.MIMETYPE_VIDEO_VP9, new int[]{256000, 512000}, new int[]{176, 352, |
| 352, 480}, new int[]{144, 240, 288, 360}}, |
| {MediaFormat.MIMETYPE_VIDEO_AV1, new int[]{256000, 512000}, new int[]{176, 352, |
| 352, 480}, new int[]{144, 240, 288, 360}}, |
| }); |
| return prepareParamList(cddRequiredMimeList, exhaustiveArgsList, true); |
| } |
| |
| private void setUpParams(int limit) { |
| int count = 0; |
| for (int bitrate : mBitrates) { |
| if (mIsAudio) { |
| for (int rate : mEncParamList1) { |
| for (int channels : mEncParamList2) { |
| MediaFormat format = new MediaFormat(); |
| format.setString(MediaFormat.KEY_MIME, mMime); |
| format.setInteger(MediaFormat.KEY_BIT_RATE, bitrate); |
| format.setInteger(MediaFormat.KEY_SAMPLE_RATE, rate); |
| format.setInteger(MediaFormat.KEY_CHANNEL_COUNT, channels); |
| mFormats.add(format); |
| count++; |
| if (count >= limit) return; |
| } |
| } |
| } else { |
| assertTrue("Wrong number of height, width parameters", |
| mEncParamList1.length == mEncParamList2.length); |
| for (int i = 0; i < mEncParamList1.length; i++) { |
| MediaFormat format = new MediaFormat(); |
| format.setString(MediaFormat.KEY_MIME, mMime); |
| format.setInteger(MediaFormat.KEY_BIT_RATE, bitrate); |
| format.setInteger(MediaFormat.KEY_WIDTH, mEncParamList1[i]); |
| format.setInteger(MediaFormat.KEY_HEIGHT, mEncParamList2[i]); |
| format.setInteger(MediaFormat.KEY_FRAME_RATE, mFrameRate); |
| format.setInteger(MediaFormat.KEY_MAX_B_FRAMES, mMaxBFrames); |
| format.setFloat(MediaFormat.KEY_I_FRAME_INTERVAL, 1.0f); |
| format.setInteger(MediaFormat.KEY_COLOR_FORMAT, |
| MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420Flexible); |
| mFormats.add(format); |
| count++; |
| if (count >= limit) return; |
| } |
| } |
| } |
| } |
| |
| /** |
| * Tests encoder for combinations: |
| * 1. Codec Sync Mode, Signal Eos with Last frame |
| * 2. Codec Sync Mode, Signal Eos Separately |
| * 3. Codec Async Mode, Signal Eos with Last frame |
| * 4. Codec Async Mode, Signal Eos Separately |
| * In all these scenarios, Timestamp ordering is verified. The output has to be |
| * consistent (not flaky) in all runs. |
| */ |
| @LargeTest |
| @Test(timeout = PER_TEST_TIMEOUT_LARGE_TEST_MS) |
| public void testSimpleEncode() throws IOException, InterruptedException { |
| setUpParams(Integer.MAX_VALUE); |
| ArrayList<String> listOfEncoders = selectCodecs(mMime, null, null, true); |
| assertFalse("no suitable codecs found for mime: " + mMime, listOfEncoders.isEmpty()); |
| boolean[] boolStates = {true, false}; |
| setUpSource(mInputFile); |
| OutputManager ref = new OutputManager(); |
| OutputManager test = new OutputManager(); |
| for (String encoder : listOfEncoders) { |
| mCodec = MediaCodec.createByCodecName(encoder); |
| assertTrue("codec name act/got: " + mCodec.getName() + '/' + encoder, |
| mCodec.getName().equals(encoder)); |
| assertTrue("error! codec canonical name is null", |
| mCodec.getCanonicalName() != null && !mCodec.getCanonicalName().isEmpty()); |
| /* TODO(b/149027258) */ |
| if (true) mSaveToMem = false; |
| else mSaveToMem = true; |
| for (MediaFormat format : mFormats) { |
| int loopCounter = 0; |
| if (mIsAudio) { |
| mSampleRate = format.getInteger(MediaFormat.KEY_SAMPLE_RATE); |
| mChannels = format.getInteger(MediaFormat.KEY_CHANNEL_COUNT); |
| } else { |
| mWidth = format.getInteger(MediaFormat.KEY_WIDTH); |
| mHeight = format.getInteger(MediaFormat.KEY_HEIGHT); |
| } |
| for (boolean eosType : boolStates) { |
| for (boolean isAsync : boolStates) { |
| String log = String.format( |
| "format: %s \n codec: %s, file: %s, mode: %s, eos type: %s:: ", |
| format, encoder, mInputFile, (isAsync ? "async" : "sync"), |
| (eosType ? "eos with last frame" : "eos separate")); |
| mOutputBuff = loopCounter == 0 ? ref : test; |
| mOutputBuff.reset(); |
| validateMetrics(encoder); |
| configureCodec(format, isAsync, eosType, true); |
| mCodec.start(); |
| doWork(Integer.MAX_VALUE); |
| queueEOS(); |
| waitForAllOutputs(); |
| validateMetrics(encoder, format); |
| /* TODO(b/147348711) */ |
| if (false) mCodec.stop(); |
| else mCodec.reset(); |
| assertTrue(log + " unexpected error", !mAsyncHandle.hasSeenError()); |
| assertTrue(log + "no input sent", 0 != mInputCount); |
| assertTrue(log + "output received", 0 != mOutputCount); |
| if (!mIsAudio) { |
| assertTrue( |
| log + "input count != output count, act/exp: " + mOutputCount + |
| " / " + mInputCount, mInputCount == mOutputCount); |
| } |
| if (loopCounter != 0) { |
| assertTrue(log + "encoder output is flaky", ref.equals(test)); |
| } else { |
| if (mIsAudio) { |
| assertTrue(log + " pts is not strictly increasing", |
| ref.isPtsStrictlyIncreasing(mPrevOutputPts)); |
| } else { |
| assertTrue( |
| log + " input pts list and output pts list are not identical", |
| ref.isOutPtsListIdenticalToInpPtsList((mMaxBFrames != 0))); |
| } |
| } |
| loopCounter++; |
| } |
| } |
| } |
| mCodec.release(); |
| } |
| } |
| |
| private native boolean nativeTestSimpleEncode(String encoder, String file, String mime, |
| int[] list0, int[] list1, int[] list2, int colorFormat); |
| |
| @LargeTest |
| @Test(timeout = PER_TEST_TIMEOUT_LARGE_TEST_MS) |
| public void testSimpleEncodeNative() throws IOException { |
| ArrayList<String> listOfEncoders = selectCodecs(mMime, null, null, true); |
| assertFalse("no suitable codecs found for mime: " + mMime, listOfEncoders.isEmpty()); |
| int colorFormat = -1; |
| for (String encoder : listOfEncoders) { |
| if (!mIsAudio) { |
| colorFormat = findByteBufferColorFormat(encoder, mMime); |
| assertTrue("no valid color formats received", colorFormat != -1); |
| } |
| assertTrue(nativeTestSimpleEncode(encoder, mInpPrefix + mInputFile, mMime, mBitrates, |
| mEncParamList1, mEncParamList2, colorFormat)); |
| } |
| } |
| |
| /** |
| * Tests flush when codec is in sync and async mode. In these scenarios, Timestamp |
| * ordering is verified. The output has to be consistent (not flaky) in all runs |
| */ |
| @Ignore("TODO(b/147576107, b/148652492, b/148651699)") |
| @LargeTest |
| @Test(timeout = PER_TEST_TIMEOUT_LARGE_TEST_MS) |
| public void testFlush() throws IOException, InterruptedException { |
| setUpParams(1); |
| ArrayList<String> listOfEncoders = selectCodecs(mMime, null, null, true); |
| assertFalse("no suitable codecs found for mime: " + mMime, listOfEncoders.isEmpty()); |
| setUpSource(mInputFile); |
| boolean[] boolStates = {true, false}; |
| mOutputBuff = new OutputManager(); |
| for (String encoder : listOfEncoders) { |
| MediaFormat inpFormat = mFormats.get(0); |
| 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); |
| } |
| mCodec = MediaCodec.createByCodecName(encoder); |
| for (boolean isAsync : boolStates) { |
| String log = String.format("encoder: %s, input file: %s, mode: %s:: ", encoder, |
| mInputFile, (isAsync ? "async" : "sync")); |
| configureCodec(inpFormat, isAsync, true, true); |
| mCodec.start(); |
| |
| /* test flush in running state before queuing input */ |
| flushCodec(); |
| mOutputBuff.reset(); |
| if (mIsCodecInAsyncMode) mCodec.start(); |
| doWork(23); |
| assertTrue(log + " pts is not strictly increasing", |
| mOutputBuff.isPtsStrictlyIncreasing(mPrevOutputPts)); |
| boolean checkMetrics = (mOutputCount != 0); |
| |
| /* test flush in running state */ |
| flushCodec(); |
| mOutputBuff.reset(); |
| if (mIsCodecInAsyncMode) mCodec.start(); |
| if (checkMetrics) validateMetrics(encoder, inpFormat); |
| doWork(Integer.MAX_VALUE); |
| queueEOS(); |
| waitForAllOutputs(); |
| assertTrue(log + " unexpected error", !mAsyncHandle.hasSeenError()); |
| assertTrue(log + "no input sent", 0 != mInputCount); |
| assertTrue(log + "output received", 0 != mOutputCount); |
| if (!mIsAudio) { |
| assertTrue(log + "input count != output count, act/exp: " + mOutputCount + |
| " / " + mInputCount, mInputCount == mOutputCount); |
| assertTrue( |
| log + " input pts list and output pts list are not identical", |
| mOutputBuff.isOutPtsListIdenticalToInpPtsList((mMaxBFrames != 0))); |
| } else { |
| assertTrue(log + " pts is not strictly increasing", |
| mOutputBuff.isPtsStrictlyIncreasing(mPrevOutputPts)); |
| } |
| |
| /* test flush in eos state */ |
| flushCodec(); |
| mOutputBuff.reset(); |
| if (mIsCodecInAsyncMode) mCodec.start(); |
| doWork(Integer.MAX_VALUE); |
| queueEOS(); |
| waitForAllOutputs(); |
| /* TODO(b/147348711) */ |
| if (false) mCodec.stop(); |
| else mCodec.reset(); |
| assertTrue(log + " unexpected error", !mAsyncHandle.hasSeenError()); |
| assertTrue(log + "no input sent", 0 != mInputCount); |
| assertTrue(log + "output received", 0 != mOutputCount); |
| if (!mIsAudio) { |
| assertTrue(log + "input count != output count, act/exp: " + mOutputCount + |
| " / " + mInputCount, mInputCount == mOutputCount); |
| assertTrue( |
| log + " input pts list and output pts list are not identical", |
| mOutputBuff.isOutPtsListIdenticalToInpPtsList((mMaxBFrames != 0))); |
| } else { |
| assertTrue(log + " pts is not strictly increasing", |
| mOutputBuff.isPtsStrictlyIncreasing(mPrevOutputPts)); |
| } |
| } |
| mCodec.release(); |
| } |
| } |
| |
| private native boolean nativeTestFlush(String encoder, String file, String mime, |
| int[] list0, int[] list1, int[] list2, int colorFormat); |
| |
| @Ignore("TODO(b/147576107, b/148652492, b/148651699)") |
| @LargeTest |
| @Test(timeout = PER_TEST_TIMEOUT_LARGE_TEST_MS) |
| public void testFlushNative() throws IOException { |
| ArrayList<String> listOfEncoders = selectCodecs(mMime, null, null, true); |
| assertFalse("no suitable codecs found for mime: " + mMime, listOfEncoders.isEmpty()); |
| int colorFormat = -1; |
| for (String encoder : listOfEncoders) { |
| if (!mIsAudio) { |
| colorFormat = findByteBufferColorFormat(encoder, mMime); |
| assertTrue("no valid color formats received", colorFormat != -1); |
| } |
| assertTrue(nativeTestFlush(encoder, mInpPrefix + mInputFile, mMime, mBitrates, |
| mEncParamList1, mEncParamList2, colorFormat)); |
| } |
| } |
| |
| /** |
| * Tests reconfigure when codec is in sync and async mode. In these |
| * scenarios, Timestamp ordering is verified. The output has to be consistent (not flaky) |
| * in all runs |
| */ |
| @Ignore("TODO(b/148523403)") |
| @LargeTest |
| @Test(timeout = PER_TEST_TIMEOUT_LARGE_TEST_MS) |
| public void testReconfigure() throws IOException, InterruptedException { |
| setUpParams(2); |
| ArrayList<String> listOfEncoders = selectCodecs(mMime, null, null, true); |
| assertFalse("no suitable codecs found for mime: " + mMime, listOfEncoders.isEmpty()); |
| setUpSource(mInputFile); |
| boolean[] boolStates = {true, false}; |
| OutputManager test = new OutputManager(); |
| for (String encoder : listOfEncoders) { |
| OutputManager configRef = null; |
| if (mFormats.size() > 1) { |
| MediaFormat format = mFormats.get(1); |
| encodeToMemory(mInputFile, encoder, Integer.MAX_VALUE, format); |
| configRef = mOutputBuff; |
| if (mIsAudio) { |
| assertTrue("config reference output pts is not strictly increasing", |
| configRef.isPtsStrictlyIncreasing(mPrevOutputPts)); |
| } else { |
| assertTrue("input pts list and reconfig ref output pts list are not identical", |
| configRef.isOutPtsListIdenticalToInpPtsList((mMaxBFrames != 0))); |
| } |
| } |
| MediaFormat format = mFormats.get(0); |
| encodeToMemory(mInputFile, encoder, Integer.MAX_VALUE, format); |
| OutputManager ref = mOutputBuff; |
| if (mIsAudio) { |
| assertTrue("reference output pts is not strictly increasing", |
| ref.isPtsStrictlyIncreasing(mPrevOutputPts)); |
| } else { |
| assertTrue("input pts list and ref output pts list are not identical", |
| ref.isOutPtsListIdenticalToInpPtsList((mMaxBFrames != 0))); |
| } |
| mOutputBuff = test; |
| mCodec = MediaCodec.createByCodecName(encoder); |
| for (boolean isAsync : boolStates) { |
| String log = String.format("encoder: %s, input file: %s, mode: %s:: ", encoder, |
| mInputFile, (isAsync ? "async" : "sync")); |
| configureCodec(format, isAsync, true, true); |
| |
| /* test reconfigure in stopped state */ |
| reConfigureCodec(format, !isAsync, false, true); |
| mCodec.start(); |
| |
| /* test reconfigure in running state before queuing input */ |
| reConfigureCodec(format, !isAsync, false, true); |
| mCodec.start(); |
| doWork(23); |
| |
| if (mOutputCount != 0) validateMetrics(encoder, format); |
| |
| /* test reconfigure codec in running state */ |
| reConfigureCodec(format, isAsync, true, true); |
| mCodec.start(); |
| /* TODO(b/149027258) */ |
| if (true) mSaveToMem = false; |
| else mSaveToMem = true; |
| test.reset(); |
| doWork(Integer.MAX_VALUE); |
| queueEOS(); |
| waitForAllOutputs(); |
| /* TODO(b/147348711) */ |
| if (false) mCodec.stop(); |
| else mCodec.reset(); |
| assertTrue(log + " unexpected error", !mAsyncHandle.hasSeenError()); |
| assertTrue(log + "no input sent", 0 != mInputCount); |
| assertTrue(log + "output received", 0 != mOutputCount); |
| if (!mIsAudio) { |
| assertTrue(log + "input count != output count, act/exp: " + mOutputCount + |
| " / " + mInputCount, mInputCount == mOutputCount); |
| } |
| assertTrue(log + "encoder output is flaky", ref.equals(test)); |
| |
| /* test reconfigure codec at eos state */ |
| reConfigureCodec(format, !isAsync, false, true); |
| mCodec.start(); |
| test.reset(); |
| doWork(Integer.MAX_VALUE); |
| queueEOS(); |
| waitForAllOutputs(); |
| /* TODO(b/147348711) */ |
| if (false) mCodec.stop(); |
| else mCodec.reset(); |
| assertTrue(log + " unexpected error", !mAsyncHandle.hasSeenError()); |
| assertTrue(log + "no input sent", 0 != mInputCount); |
| assertTrue(log + "output received", 0 != mOutputCount); |
| if (!mIsAudio) { |
| assertTrue(log + "input count != output count, act/exp: " + mOutputCount + |
| " / " + mInputCount, mInputCount == mOutputCount); |
| } |
| assertTrue(log + "encoder output is flaky", ref.equals(test)); |
| |
| /* test reconfigure codec for new format */ |
| if (mFormats.size() > 1) { |
| reConfigureCodec(mFormats.get(1), isAsync, false, true); |
| mCodec.start(); |
| test.reset(); |
| doWork(Integer.MAX_VALUE); |
| queueEOS(); |
| waitForAllOutputs(); |
| /* TODO(b/147348711) */ |
| if (false) mCodec.stop(); |
| else mCodec.reset(); |
| assertTrue(log + " unexpected error", !mAsyncHandle.hasSeenError()); |
| assertTrue(log + "no input sent", 0 != mInputCount); |
| assertTrue(log + "output received", 0 != mOutputCount); |
| if (!mIsAudio) { |
| assertTrue(log + "input count != output count, act/exp: " + mOutputCount + |
| " / " + mInputCount, mInputCount == mOutputCount); |
| } |
| assertTrue(log + "encoder output is flaky", configRef.equals(test)); |
| } |
| mSaveToMem = false; |
| } |
| mCodec.release(); |
| } |
| } |
| |
| private native boolean nativeTestReconfigure(String encoder, String file, String mime, |
| int[] list0, int[] list1, int[] list2, int colorFormat); |
| |
| @Ignore("TODO(b/147348711, b/149981033)") |
| @LargeTest |
| @Test(timeout = PER_TEST_TIMEOUT_LARGE_TEST_MS) |
| public void testReconfigureNative() throws IOException { |
| ArrayList<String> listOfEncoders = selectCodecs(mMime, null, null, true); |
| assertFalse("no suitable codecs found for mime: " + mMime, listOfEncoders.isEmpty()); |
| int colorFormat = -1; |
| for (String encoder : listOfEncoders) { |
| if (!mIsAudio) { |
| colorFormat = findByteBufferColorFormat(encoder, mMime); |
| assertTrue("no valid color formats received", colorFormat != -1); |
| } |
| assertTrue(nativeTestReconfigure(encoder, mInpPrefix + mInputFile, mMime, mBitrates, |
| mEncParamList1, mEncParamList2, colorFormat)); |
| } |
| } |
| |
| /** |
| * Tests encoder for only EOS frame |
| */ |
| @SmallTest |
| @Test(timeout = PER_TEST_TIMEOUT_SMALL_TEST_MS) |
| public void testOnlyEos() throws IOException, InterruptedException { |
| setUpParams(1); |
| ArrayList<String> listOfEncoders = selectCodecs(mMime, null, null, true); |
| assertFalse("no suitable codecs found for mime: " + mMime, listOfEncoders.isEmpty()); |
| boolean[] boolStates = {true, false}; |
| OutputManager ref = new OutputManager(); |
| OutputManager test = new OutputManager(); |
| for (String encoder : listOfEncoders) { |
| mCodec = MediaCodec.createByCodecName(encoder); |
| /* TODO(b/149027258) */ |
| if (true) mSaveToMem = false; |
| else mSaveToMem = true; |
| int loopCounter = 0; |
| for (boolean isAsync : boolStates) { |
| String log = String.format("encoder: %s, input file: %s, mode: %s:: ", encoder, |
| mInputFile, (isAsync ? "async" : "sync")); |
| configureCodec(mFormats.get(0), isAsync, false, true); |
| mOutputBuff = loopCounter == 0 ? ref : test; |
| mOutputBuff.reset(); |
| mCodec.start(); |
| queueEOS(); |
| waitForAllOutputs(); |
| /* TODO(b/147348711) */ |
| if (false) mCodec.stop(); |
| else mCodec.reset(); |
| assertTrue(log + " unexpected error", !mAsyncHandle.hasSeenError()); |
| if (loopCounter != 0) { |
| assertTrue(log + "encoder output is flaky", ref.equals(test)); |
| } else { |
| if (mIsAudio) { |
| assertTrue(log + " pts is not strictly increasing", |
| ref.isPtsStrictlyIncreasing(mPrevOutputPts)); |
| } else { |
| assertTrue( |
| log + " input pts list and output pts list are not identical", |
| ref.isOutPtsListIdenticalToInpPtsList((mMaxBFrames != 0))); |
| } |
| } |
| loopCounter++; |
| } |
| mCodec.release(); |
| } |
| } |
| |
| private native boolean nativeTestOnlyEos(String encoder, String mime, int[] list0, int[] list1, |
| int[] list2, int colorFormat); |
| |
| @SmallTest |
| @Test(timeout = PER_TEST_TIMEOUT_SMALL_TEST_MS) |
| public void testOnlyEosNative() throws IOException { |
| ArrayList<String> listOfEncoders = selectCodecs(mMime, null, null, true); |
| assertFalse("no suitable codecs found for mime: " + mMime, listOfEncoders.isEmpty()); |
| int colorFormat = -1; |
| for (String encoder : listOfEncoders) { |
| if (!mIsAudio) { |
| colorFormat = findByteBufferColorFormat(encoder, mMime); |
| assertTrue("no valid color formats received", colorFormat != -1); |
| } |
| assertTrue(nativeTestOnlyEos(encoder, mMime, mBitrates, mEncParamList1, mEncParamList2, |
| colorFormat)); |
| } |
| } |
| |
| /** |
| * Test set parameters : force key frame |
| */ |
| @Ignore("TODO(b/151302863)") |
| @LargeTest |
| @Test(timeout = PER_TEST_TIMEOUT_LARGE_TEST_MS) |
| public void testSetForceSyncFrame() throws IOException, InterruptedException { |
| Assume.assumeTrue(!mIsAudio); |
| // Maximum allowed key frame interval variation from the target value. |
| final int MAX_KEYFRAME_INTERVAL_VARIATION = 3; |
| setUpParams(1); |
| ArrayList<String> listOfEncoders = selectCodecs(mMime, null, null, true); |
| assertFalse("no suitable codecs found for mime: " + mMime, listOfEncoders.isEmpty()); |
| boolean[] boolStates = {true, false}; |
| setUpSource(mInputFile); |
| MediaFormat format = mFormats.get(0); |
| format.removeKey(MediaFormat.KEY_I_FRAME_INTERVAL); |
| format.setFloat(MediaFormat.KEY_I_FRAME_INTERVAL, 500.f); |
| mWidth = format.getInteger(MediaFormat.KEY_WIDTH); |
| mHeight = format.getInteger(MediaFormat.KEY_HEIGHT); |
| final int KEY_FRAME_INTERVAL = 2; // force key frame every 2 seconds. |
| final int KEY_FRAME_POS = mFrameRate * KEY_FRAME_INTERVAL; |
| final int NUM_KEY_FRAME_REQUESTS = 7; |
| mOutputBuff = new OutputManager(); |
| for (String encoder : listOfEncoders) { |
| mCodec = MediaCodec.createByCodecName(encoder); |
| for (boolean isAsync : boolStates) { |
| String log = String.format( |
| "format: %s \n codec: %s, file: %s, mode: %s:: ", format, encoder, |
| mInputFile, (isAsync ? "async" : "sync")); |
| mOutputBuff.reset(); |
| configureCodec(format, isAsync, false, true); |
| mCodec.start(); |
| for (int i = 0; i < NUM_KEY_FRAME_REQUESTS; i++) { |
| doWork(KEY_FRAME_POS); |
| assertTrue(!mSawInputEOS); |
| forceSyncFrame(); |
| mNumBytesSubmitted = 0; |
| } |
| queueEOS(); |
| waitForAllOutputs(); |
| /* TODO(b/147348711) */ |
| if (false) mCodec.stop(); |
| else mCodec.reset(); |
| assertTrue(log + " unexpected error", !mAsyncHandle.hasSeenError()); |
| assertTrue(log + "no input sent", 0 != mInputCount); |
| assertTrue(log + "output received", 0 != mOutputCount); |
| assertTrue(log + "input count != output count, act/exp: " + mOutputCount + " / " + |
| mInputCount, mInputCount == mOutputCount); |
| assertTrue(log + " input pts list and output pts list are not identical", |
| mOutputBuff.isOutPtsListIdenticalToInpPtsList((mMaxBFrames != 0))); |
| assertTrue(log + "sync frames exp/act: " + NUM_KEY_FRAME_REQUESTS + " / " + |
| mNumSyncFramesReceived, mNumSyncFramesReceived >= NUM_KEY_FRAME_REQUESTS); |
| for (int i = 0, expPos = 0, index = 0; i < NUM_KEY_FRAME_REQUESTS; i++) { |
| int j = index; |
| for (; j < mSyncFramesPos.size(); j++) { |
| // Check key frame intervals: |
| // key frame position should not be greater than target value + 3 |
| // key frame position should not be less than target value - 3 |
| if (Math.abs(expPos - mSyncFramesPos.get(j)) <= |
| MAX_KEYFRAME_INTERVAL_VARIATION) { |
| index = j; |
| break; |
| } |
| } |
| if (j == mSyncFramesPos.size()) { |
| Log.w(LOG_TAG, "requested key frame at frame index " + expPos + |
| " none found near by"); |
| } |
| expPos += KEY_FRAME_POS; |
| } |
| } |
| mCodec.release(); |
| } |
| } |
| |
| private native boolean nativeTestSetForceSyncFrame(String encoder, String file, String mime, |
| int[] list0, int[] list1, int[] list2, int colorFormat); |
| |
| @Ignore("TODO(b/) = test sometimes timesout") |
| @LargeTest |
| @Test(timeout = PER_TEST_TIMEOUT_LARGE_TEST_MS) |
| public void testSetForceSyncFrameNative() throws IOException { |
| Assume.assumeTrue(!mIsAudio); |
| ArrayList<String> listOfEncoders = selectCodecs(mMime, null, null, true); |
| assertFalse("no suitable codecs found for mime: " + mMime, listOfEncoders.isEmpty()); |
| int colorFormat = -1; |
| for (String encoder : listOfEncoders) { |
| if (!mIsAudio) { |
| colorFormat = findByteBufferColorFormat(encoder, mMime); |
| assertTrue("no valid color formats received", colorFormat != -1); |
| } |
| assertTrue(nativeTestSetForceSyncFrame(encoder, mInpPrefix + mInputFile, mMime, |
| mBitrates, mEncParamList1, mEncParamList2, colorFormat)); |
| } |
| } |
| |
| /** |
| * Test set parameters : change bitrate dynamically |
| */ |
| @Ignore("TODO(b/151302863)") |
| @LargeTest |
| @Test(timeout = PER_TEST_TIMEOUT_LARGE_TEST_MS) |
| public void testAdaptiveBitRate() throws IOException, InterruptedException { |
| Assume.assumeTrue(!mIsAudio); |
| setUpParams(1); |
| ArrayList<String> listOfEncoders = selectCodecs(mMime, null, null, true); |
| assertFalse("no suitable codecs found for mime: " + mMime, listOfEncoders.isEmpty()); |
| boolean[] boolStates = {true, false}; |
| setUpSource(mInputFile); |
| MediaFormat format = mFormats.get(0); |
| mWidth = format.getInteger(MediaFormat.KEY_WIDTH); |
| mHeight = format.getInteger(MediaFormat.KEY_HEIGHT); |
| final int ADAPTIVE_BR_INTERVAL = 3; // change br every 3 seconds. |
| final int ADAPTIVE_BR_DUR_FRM = mFrameRate * ADAPTIVE_BR_INTERVAL; |
| final int BR_CHANGE_REQUESTS = 7; |
| mOutputBuff = new OutputManager(); |
| mSaveToMem = true; |
| for (String encoder : listOfEncoders) { |
| /* TODO(b/147574800) */ |
| if (encoder.equals("c2.android.hevc.encoder")) continue; |
| mCodec = MediaCodec.createByCodecName(encoder); |
| format.removeKey(MediaFormat.KEY_BITRATE_MODE); |
| MediaCodecInfo.EncoderCapabilities cap = |
| mCodec.getCodecInfo().getCapabilitiesForType(mMime).getEncoderCapabilities(); |
| if (cap.isBitrateModeSupported(MediaCodecInfo.EncoderCapabilities.BITRATE_MODE_CBR)) { |
| format.setInteger(MediaFormat.KEY_BITRATE_MODE, |
| MediaCodecInfo.EncoderCapabilities.BITRATE_MODE_CBR); |
| } else { |
| format.setInteger(MediaFormat.KEY_BITRATE_MODE, |
| MediaCodecInfo.EncoderCapabilities.BITRATE_MODE_VBR); |
| } |
| for (boolean isAsync : boolStates) { |
| String log = String.format( |
| "format: %s \n codec: %s, file: %s, mode: %s:: ", format, encoder, |
| mInputFile, (isAsync ? "async" : "sync")); |
| mOutputBuff.reset(); |
| configureCodec(format, isAsync, false, true); |
| mCodec.start(); |
| int expOutSize = 0; |
| int bitrate = format.getInteger(MediaFormat.KEY_BIT_RATE); |
| for (int i = 0; i < BR_CHANGE_REQUESTS; i++) { |
| doWork(ADAPTIVE_BR_DUR_FRM); |
| assertTrue(!mSawInputEOS); |
| expOutSize += ADAPTIVE_BR_INTERVAL * bitrate; |
| if ((i & 1) == 1) bitrate *= 2; |
| else bitrate /= 2; |
| updateBitrate(bitrate); |
| mNumBytesSubmitted = 0; |
| } |
| queueEOS(); |
| waitForAllOutputs(); |
| /* TODO(b/147348711) */ |
| if (false) mCodec.stop(); |
| else mCodec.reset(); |
| assertTrue(log + " unexpected error", !mAsyncHandle.hasSeenError()); |
| assertTrue(log + "no input sent", 0 != mInputCount); |
| assertTrue(log + "output received", 0 != mOutputCount); |
| assertTrue(log + "input count != output count, act/exp: " + mOutputCount + " / " + |
| mInputCount, mInputCount == mOutputCount); |
| assertTrue(log + " input pts list and output pts list are not identical", |
| mOutputBuff.isOutPtsListIdenticalToInpPtsList((mMaxBFrames != 0))); |
| /* TODO: validate output br with sliding window constraints Sec 5.2 cdd */ |
| int outSize = mOutputBuff.getOutStreamSize() * 8; |
| float brDev = Math.abs(expOutSize - outSize) * 100.0f / expOutSize; |
| if (ENABLE_LOGS) { |
| Log.d(LOG_TAG, log + "relative br error is " + brDev + '%'); |
| } |
| if (brDev > 50) { |
| fail(log + "relative br error is too large " + brDev + '%'); |
| } |
| } |
| mCodec.release(); |
| } |
| } |
| |
| private native boolean nativeTestAdaptiveBitRate(String encoder, String file, String mime, |
| int[] list0, int[] list1, int[] list2, int colorFormat); |
| |
| @Ignore("TODO(b/) = test sometimes timesout") |
| @LargeTest |
| @Test(timeout = PER_TEST_TIMEOUT_LARGE_TEST_MS) |
| public void testAdaptiveBitRateNative() throws IOException { |
| Assume.assumeTrue(!mIsAudio); |
| ArrayList<String> listOfEncoders = selectCodecs(mMime, null, null, true); |
| assertFalse("no suitable codecs found for mime: " + mMime, listOfEncoders.isEmpty()); |
| int colorFormat = -1; |
| for (String encoder : listOfEncoders) { |
| /* TODO(b/147574800) */ |
| if (encoder.equals("c2.android.hevc.encoder")) continue; |
| if (!mIsAudio) { |
| colorFormat = findByteBufferColorFormat(encoder, mMime); |
| assertTrue("no valid color formats received", colorFormat != -1); |
| } |
| assertTrue(nativeTestAdaptiveBitRate(encoder, mInpPrefix + mInputFile, mMime, mBitrates, |
| mEncParamList1, mEncParamList2, colorFormat)); |
| } |
| } |
| } |