blob: 7b1dc11ae89f0c97c65ddd73ece88c19971d0f13 [file] [log] [blame]
/*
* Copyright (C) 2013 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.video.cts;
import android.graphics.ImageFormat;
import android.graphics.Point;
import android.media.Image;
import android.media.Image.Plane;
import android.media.MediaCodec;
import android.media.MediaCodec.BufferInfo;
import android.media.MediaCodecInfo;
import android.media.MediaCodecInfo.CodecCapabilities;
import android.media.MediaFormat;
import android.media.cts.CodecImage;
import android.media.cts.CodecUtils;
import android.media.cts.YUVImage;
import android.util.Log;
import android.util.Pair;
import android.util.Range;
import com.android.compatibility.common.util.CtsAndroidTestCase;
import com.android.compatibility.common.util.DeviceReportLog;
import com.android.compatibility.common.util.MediaPerfUtils;
import com.android.compatibility.common.util.MediaUtils;
import com.android.compatibility.common.util.ResultType;
import com.android.compatibility.common.util.ResultUnit;
import com.android.compatibility.common.util.Stat;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.Random;
/**
* This tries to test video encoder / decoder performance by running encoding / decoding
* without displaying the raw data. To make things simpler, encoder is used to encode synthetic
* data and decoder is used to decode the encoded video. This approach does not work where
* there is only decoder. Performance index is total time taken for encoding and decoding
* the whole frames.
* To prevent sacrificing quality for faster encoding / decoding, randomly selected pixels are
* compared with the original image. As the pixel comparison can slow down the decoding process,
* only some randomly selected pixels are compared. As there can be only one performance index,
* error above certain threshold in pixel value will be treated as an error.
*/
public class VideoEncoderDecoderTest extends CtsAndroidTestCase {
private static final String TAG = "VideoEncoderDecoderTest";
private static final String REPORT_LOG_NAME = "CtsVideoTestCases";
// this wait time affects fps as too big value will work as a blocker if device fps
// is not very high.
private static final long VIDEO_CODEC_WAIT_TIME_US = 1000;
private static final boolean VERBOSE = false;
private static final int MAX_FPS = 30; // measure performance at 30fps, this is relevant for
// the meaning of bitrate
private static final String AVC = MediaFormat.MIMETYPE_VIDEO_AVC;
private static final String H263 = MediaFormat.MIMETYPE_VIDEO_H263;
private static final String HEVC = MediaFormat.MIMETYPE_VIDEO_HEVC;
private static final String MPEG2 = MediaFormat.MIMETYPE_VIDEO_MPEG2;
private static final String MPEG4 = MediaFormat.MIMETYPE_VIDEO_MPEG4;
private static final String VP8 = MediaFormat.MIMETYPE_VIDEO_VP8;
private static final String VP9 = MediaFormat.MIMETYPE_VIDEO_VP9;
private static final boolean GOOG = true;
private static final boolean OTHER = false;
// test results:
private int mCurrentTestRound = 0;
private double[][] mEncoderFrameTimeUsDiff;
private double[] mEncoderFpsResults;
private double[][] mDecoderFrameTimeUsDiff;
private double[] mDecoderFpsResults;
private double[] mTotalFpsResults;
private double[] mDecoderRmsErrorResults;
// i frame interval for encoder
private static final int KEY_I_FRAME_INTERVAL = 5;
private static final int MAX_TEST_TIMEOUT_MS = 300000; // 5 minutes
private static final int Y_CLAMP_MIN = 16;
private static final int Y_CLAMP_MAX = 235;
private static final int YUV_PLANE_ADDITIONAL_LENGTH = 200;
private ByteBuffer mYBuffer, mYDirectBuffer;
private ByteBuffer mUVBuffer, mUVDirectBuffer;
private int mSrcColorFormat;
private int mDstColorFormat;
private int mBufferWidth;
private int mBufferHeight;
private int mVideoWidth;
private int mVideoHeight;
private int mVideoStride;
private int mVideoVStride;
private int mFrameRate;
private MediaFormat mEncConfigFormat;
private MediaFormat mEncInputFormat;
private MediaFormat mEncOutputFormat;
private MediaFormat mDecOutputFormat;
private LinkedList<Pair<ByteBuffer, BufferInfo>> mEncodedOutputBuffer;
// check this many pixels per each decoded frame
// checking too many points decreases decoder frame rates a lot.
private static final int PIXEL_CHECK_PER_FRAME = 1000;
// RMS error in pixel values above this will be treated as error.
private static final double PIXEL_RMS_ERROR_MARGIN = 20.0;
private double mRmsErrorMargin;
private Random mRandom;
private class TestConfig {
public boolean mTestPixels = true;
public boolean mReportFrameTime = false;
public int mTotalFrames = 300;
public int mMinNumFrames = 300;
public int mMaxTimeMs = 120000; // 2 minutes
public int mMinTimeMs = 10000; // 10 seconds
public int mNumberOfRepeat = 10;
public void initPerfTest() {
mTestPixels = false;
mTotalFrames = 30000;
mMinNumFrames = 3000;
mNumberOfRepeat = 2;
}
}
private TestConfig mTestConfig;
@Override
protected void setUp() throws Exception {
mEncodedOutputBuffer = new LinkedList<Pair<ByteBuffer, BufferInfo>>();
mRmsErrorMargin = PIXEL_RMS_ERROR_MARGIN;
// Use time as a seed, hoping to prevent checking pixels in the same pattern
long now = System.currentTimeMillis();
mRandom = new Random(now);
mTestConfig = new TestConfig();
super.setUp();
}
@Override
protected void tearDown() throws Exception {
mEncodedOutputBuffer.clear();
mEncodedOutputBuffer = null;
mYBuffer = null;
mUVBuffer = null;
mYDirectBuffer = null;
mUVDirectBuffer = null;
mRandom = null;
mTestConfig = null;
super.tearDown();
}
private void count(String mime, int width, int height, int numGoog, int numOther)
throws Exception {
MediaFormat format = MediaFormat.createVideoFormat(mime, width, height);
MediaUtils.verifyNumCodecs(numGoog, true /* isEncoder */, true /* isGoog */, format);
MediaUtils.verifyNumCodecs(numOther, true /* isEncoder */, false /* isGoog */, format);
}
/** run performance test. */
private void perf(String mimeType, int w, int h, boolean isGoog, int ix) throws Exception {
doTest(mimeType, w, h, true /* isPerf */, isGoog, ix);
}
/** run quality test. */
private void qual(String mimeType, int w, int h, boolean isGoog, int ix) throws Exception {
doTest(mimeType, w, h, false /* isPerf */, isGoog, ix);
}
/** run quality test but do not report error. */
private void qual(String mimeType, int w, int h, boolean isGoog, int ix, double margin)
throws Exception {
mRmsErrorMargin = margin;
doTest(mimeType, w, h, false /* isPerf */, isGoog, ix);
}
// Poor man's Parametrized test as this test must still run on CTSv1 runner.
// The count tests are to ensure this Cts test covers all encoders. Add further
// tests and change the count if there can be more encoders.
// AVC tests
public void testAvcCount0320x0240() throws Exception { count(AVC, 320, 240, 1, 4); }
public void testAvcGoog0Qual0320x0240() throws Exception { qual(AVC, 320, 240, GOOG, 0); }
public void testAvcGoog0Perf0320x0240() throws Exception { perf(AVC, 320, 240, GOOG, 0); }
public void testAvcOther0Qual0320x0240() throws Exception { qual(AVC, 320, 240, OTHER, 0); }
public void testAvcOther0Perf0320x0240() throws Exception { perf(AVC, 320, 240, OTHER, 0); }
public void testAvcOther1Qual0320x0240() throws Exception { qual(AVC, 320, 240, OTHER, 1); }
public void testAvcOther1Perf0320x0240() throws Exception { perf(AVC, 320, 240, OTHER, 1); }
public void testAvcOther2Qual0320x0240() throws Exception { qual(AVC, 320, 240, OTHER, 2); }
public void testAvcOther2Perf0320x0240() throws Exception { perf(AVC, 320, 240, OTHER, 2); }
public void testAvcOther3Qual0320x0240() throws Exception { qual(AVC, 320, 240, OTHER, 3); }
public void testAvcOther3Perf0320x0240() throws Exception { perf(AVC, 320, 240, OTHER, 3); }
public void testAvcCount0720x0480() throws Exception { count(AVC, 720, 480, 1, 4); }
public void testAvcGoog0Qual0720x0480() throws Exception { qual(AVC, 720, 480, GOOG, 0); }
public void testAvcGoog0Perf0720x0480() throws Exception { perf(AVC, 720, 480, GOOG, 0); }
public void testAvcOther0Qual0720x0480() throws Exception { qual(AVC, 720, 480, OTHER, 0); }
public void testAvcOther0Perf0720x0480() throws Exception { perf(AVC, 720, 480, OTHER, 0); }
public void testAvcOther1Qual0720x0480() throws Exception { qual(AVC, 720, 480, OTHER, 1); }
public void testAvcOther1Perf0720x0480() throws Exception { perf(AVC, 720, 480, OTHER, 1); }
public void testAvcOther2Qual0720x0480() throws Exception { qual(AVC, 720, 480, OTHER, 2); }
public void testAvcOther2Perf0720x0480() throws Exception { perf(AVC, 720, 480, OTHER, 2); }
public void testAvcOther3Qual0720x0480() throws Exception { qual(AVC, 720, 480, OTHER, 3); }
public void testAvcOther3Perf0720x0480() throws Exception { perf(AVC, 720, 480, OTHER, 3); }
public void testAvcCount1280x0720() throws Exception { count(AVC, 1280, 720, 1, 4); }
public void testAvcGoog0Qual1280x0720() throws Exception { qual(AVC, 1280, 720, GOOG, 0); }
public void testAvcGoog0Perf1280x0720() throws Exception { perf(AVC, 1280, 720, GOOG, 0); }
public void testAvcOther0Qual1280x0720() throws Exception { qual(AVC, 1280, 720, OTHER, 0); }
public void testAvcOther0Perf1280x0720() throws Exception { perf(AVC, 1280, 720, OTHER, 0); }
public void testAvcOther1Qual1280x0720() throws Exception { qual(AVC, 1280, 720, OTHER, 1); }
public void testAvcOther1Perf1280x0720() throws Exception { perf(AVC, 1280, 720, OTHER, 1); }
public void testAvcOther2Qual1280x0720() throws Exception { qual(AVC, 1280, 720, OTHER, 2); }
public void testAvcOther2Perf1280x0720() throws Exception { perf(AVC, 1280, 720, OTHER, 2); }
public void testAvcOther3Qual1280x0720() throws Exception { qual(AVC, 1280, 720, OTHER, 3); }
public void testAvcOther3Perf1280x0720() throws Exception { perf(AVC, 1280, 720, OTHER, 3); }
public void testAvcCount1920x1080() throws Exception { count(AVC, 1920, 1080, 1, 4); }
public void testAvcGoog0Qual1920x1080() throws Exception { qual(AVC, 1920, 1080, GOOG, 0); }
public void testAvcGoog0Perf1920x1080() throws Exception { perf(AVC, 1920, 1080, GOOG, 0); }
public void testAvcOther0Qual1920x1080() throws Exception { qual(AVC, 1920, 1080, OTHER, 0); }
public void testAvcOther0Perf1920x1080() throws Exception { perf(AVC, 1920, 1080, OTHER, 0); }
public void testAvcOther1Qual1920x1080() throws Exception { qual(AVC, 1920, 1080, OTHER, 1); }
public void testAvcOther1Perf1920x1080() throws Exception { perf(AVC, 1920, 1080, OTHER, 1); }
public void testAvcOther2Qual1920x1080() throws Exception { qual(AVC, 1920, 1080, OTHER, 2); }
public void testAvcOther2Perf1920x1080() throws Exception { perf(AVC, 1920, 1080, OTHER, 2); }
public void testAvcOther3Qual1920x1080() throws Exception { qual(AVC, 1920, 1080, OTHER, 3); }
public void testAvcOther3Perf1920x1080() throws Exception { perf(AVC, 1920, 1080, OTHER, 3); }
// H263 tests
public void testH263Count0176x0144() throws Exception { count(H263, 176, 144, 1, 2); }
public void testH263Goog0Qual0176x0144() throws Exception { qual(H263, 176, 144, GOOG, 0); }
public void testH263Goog0Perf0176x0144() throws Exception { perf(H263, 176, 144, GOOG, 0); }
public void testH263Other0Qual0176x0144() throws Exception { qual(H263, 176, 144, OTHER, 0); }
public void testH263Other0Perf0176x0144() throws Exception { perf(H263, 176, 144, OTHER, 0); }
public void testH263Other1Qual0176x0144() throws Exception { qual(H263, 176, 144, OTHER, 1); }
public void testH263Other1Perf0176x0144() throws Exception { perf(H263, 176, 144, OTHER, 1); }
public void testH263Count0352x0288() throws Exception { count(H263, 352, 288, 1, 2); }
public void testH263Goog0Qual0352x0288() throws Exception { qual(H263, 352, 288, GOOG, 0); }
public void testH263Goog0Perf0352x0288() throws Exception { perf(H263, 352, 288, GOOG, 0); }
public void testH263Other0Qual0352x0288() throws Exception { qual(H263, 352, 288, OTHER, 0); }
public void testH263Other0Perf0352x0288() throws Exception { perf(H263, 352, 288, OTHER, 0); }
public void testH263Other1Qual0352x0288() throws Exception { qual(H263, 352, 288, OTHER, 1); }
public void testH263Other1Perf0352x0288() throws Exception { perf(H263, 352, 288, OTHER, 1); }
public void testH263Count0704x0576() throws Exception { count(H263, 704, 576, 1, 2); }
public void testH263Goog0Qual0704x0576() throws Exception { qual(H263, 704, 576, GOOG, 0, 25); }
public void testH263Goog0Perf0704x0576() throws Exception { perf(H263, 704, 576, GOOG, 0); }
public void testH263Other0Qual0704x0576() throws Exception { qual(H263, 704, 576, OTHER, 0, 25); }
public void testH263Other0Perf0704x0576() throws Exception { perf(H263, 704, 576, OTHER, 0); }
public void testH263Other1Qual0704x0576() throws Exception { qual(H263, 704, 576, OTHER, 1, 25); }
public void testH263Other1Perf0704x0576() throws Exception { perf(H263, 704, 576, OTHER, 1); }
public void testH263Count1408x1152() throws Exception { count(H263, 1408, 1152, 1, 2); }
public void testH263Goog0Qual1408x1152() throws Exception { qual(H263, 1408, 1152, GOOG, 0, 25); }
public void testH263Goog0Perf1408x1152() throws Exception { perf(H263, 1408, 1152, GOOG, 0); }
public void testH263Other0Qual1408x1152() throws Exception { qual(H263, 1408, 1152, OTHER, 0, 25); }
public void testH263Other0Perf1408x1152() throws Exception { perf(H263, 1408, 1152, OTHER, 0); }
public void testH263Other1Qual1408x1152() throws Exception { qual(H263, 1408, 1152, OTHER, 1, 25); }
public void testH263Other1Perf1408x1152() throws Exception { perf(H263, 1408, 1152, OTHER, 1); }
// HEVC tests
public void testHevcCount0320x0240() throws Exception { count(HEVC, 320, 240, 1, 4); }
public void testHevcGoog0Qual0320x0240() throws Exception { qual(HEVC, 320, 240, GOOG, 0); }
public void testHevcGoog0Perf0320x0240() throws Exception { perf(HEVC, 320, 240, GOOG, 0); }
public void testHevcOther0Qual0320x0240() throws Exception { qual(HEVC, 320, 240, OTHER, 0); }
public void testHevcOther0Perf0320x0240() throws Exception { perf(HEVC, 320, 240, OTHER, 0); }
public void testHevcOther1Qual0320x0240() throws Exception { qual(HEVC, 320, 240, OTHER, 1); }
public void testHevcOther1Perf0320x0240() throws Exception { perf(HEVC, 320, 240, OTHER, 1); }
public void testHevcOther2Qual0320x0240() throws Exception { qual(HEVC, 320, 240, OTHER, 2); }
public void testHevcOther2Perf0320x0240() throws Exception { perf(HEVC, 320, 240, OTHER, 2); }
public void testHevcOther3Qual0320x0240() throws Exception { qual(HEVC, 320, 240, OTHER, 3); }
public void testHevcOther3Perf0320x0240() throws Exception { perf(HEVC, 320, 240, OTHER, 3); }
public void testHevcCount0720x0480() throws Exception { count(HEVC, 720, 480, 1, 4); }
public void testHevcGoog0Qual0720x0480() throws Exception { qual(HEVC, 720, 480, GOOG, 0); }
public void testHevcGoog0Perf0720x0480() throws Exception { perf(HEVC, 720, 480, GOOG, 0); }
public void testHevcOther0Qual0720x0480() throws Exception { qual(HEVC, 720, 480, OTHER, 0); }
public void testHevcOther0Perf0720x0480() throws Exception { perf(HEVC, 720, 480, OTHER, 0); }
public void testHevcOther1Qual0720x0480() throws Exception { qual(HEVC, 720, 480, OTHER, 1); }
public void testHevcOther1Perf0720x0480() throws Exception { perf(HEVC, 720, 480, OTHER, 1); }
public void testHevcOther2Qual0720x0480() throws Exception { qual(HEVC, 720, 480, OTHER, 2); }
public void testHevcOther2Perf0720x0480() throws Exception { perf(HEVC, 720, 480, OTHER, 2); }
public void testHevcOther3Qual0720x0480() throws Exception { qual(HEVC, 720, 480, OTHER, 3); }
public void testHevcOther3Perf0720x0480() throws Exception { perf(HEVC, 720, 480, OTHER, 3); }
public void testHevcCount1280x0720() throws Exception { count(HEVC, 1280, 720, 1, 4); }
public void testHevcGoog0Qual1280x0720() throws Exception { qual(HEVC, 1280, 720, GOOG, 0); }
public void testHevcGoog0Perf1280x0720() throws Exception { perf(HEVC, 1280, 720, GOOG, 0); }
public void testHevcOther0Qual1280x0720() throws Exception { qual(HEVC, 1280, 720, OTHER, 0); }
public void testHevcOther0Perf1280x0720() throws Exception { perf(HEVC, 1280, 720, OTHER, 0); }
public void testHevcOther1Qual1280x0720() throws Exception { qual(HEVC, 1280, 720, OTHER, 1); }
public void testHevcOther1Perf1280x0720() throws Exception { perf(HEVC, 1280, 720, OTHER, 1); }
public void testHevcOther2Qual1280x0720() throws Exception { qual(HEVC, 1280, 720, OTHER, 2); }
public void testHevcOther2Perf1280x0720() throws Exception { perf(HEVC, 1280, 720, OTHER, 2); }
public void testHevcOther3Qual1280x0720() throws Exception { qual(HEVC, 1280, 720, OTHER, 3); }
public void testHevcOther3Perf1280x0720() throws Exception { perf(HEVC, 1280, 720, OTHER, 3); }
public void testHevcCount1920x1080() throws Exception { count(HEVC, 1920, 1080, 1, 4); }
public void testHevcGoog0Qual1920x1080() throws Exception { qual(HEVC, 1920, 1080, GOOG, 0); }
public void testHevcGoog0Perf1920x1080() throws Exception { perf(HEVC, 1920, 1080, GOOG, 0); }
public void testHevcOther0Qual1920x1080() throws Exception { qual(HEVC, 1920, 1080, OTHER, 0); }
public void testHevcOther0Perf1920x1080() throws Exception { perf(HEVC, 1920, 1080, OTHER, 0); }
public void testHevcOther1Qual1920x1080() throws Exception { qual(HEVC, 1920, 1080, OTHER, 1); }
public void testHevcOther1Perf1920x1080() throws Exception { perf(HEVC, 1920, 1080, OTHER, 1); }
public void testHevcOther2Qual1920x1080() throws Exception { qual(HEVC, 1920, 1080, OTHER, 2); }
public void testHevcOther2Perf1920x1080() throws Exception { perf(HEVC, 1920, 1080, OTHER, 2); }
public void testHevcOther3Qual1920x1080() throws Exception { qual(HEVC, 1920, 1080, OTHER, 3); }
public void testHevcOther3Perf1920x1080() throws Exception { perf(HEVC, 1920, 1080, OTHER, 3); }
public void testHevcCount3840x2160() throws Exception { count(HEVC, 3840, 2160, 1, 4); }
public void testHevcGoog0Qual3840x2160() throws Exception { qual(HEVC, 3840, 2160, GOOG, 0); }
public void testHevcGoog0Perf3840x2160() throws Exception { perf(HEVC, 3840, 2160, GOOG, 0); }
public void testHevcOther0Qual3840x2160() throws Exception { qual(HEVC, 3840, 2160, OTHER, 0); }
public void testHevcOther0Perf3840x2160() throws Exception { perf(HEVC, 3840, 2160, OTHER, 0); }
public void testHevcOther1Qual3840x2160() throws Exception { qual(HEVC, 3840, 2160, OTHER, 1); }
public void testHevcOther1Perf3840x2160() throws Exception { perf(HEVC, 3840, 2160, OTHER, 1); }
public void testHevcOther2Qual3840x2160() throws Exception { qual(HEVC, 3840, 2160, OTHER, 2); }
public void testHevcOther2Perf3840x2160() throws Exception { perf(HEVC, 3840, 2160, OTHER, 2); }
public void testHevcOther3Qual3840x2160() throws Exception { qual(HEVC, 3840, 2160, OTHER, 3); }
public void testHevcOther3Perf3840x2160() throws Exception { perf(HEVC, 3840, 2160, OTHER, 3); }
// MPEG2 tests
public void testMpeg2Count0176x0144() throws Exception { count(MPEG2, 176, 144, 1, 4); }
public void testMpeg2Goog0Qual0176x0144() throws Exception { qual(MPEG2, 176, 144, GOOG, 0); }
public void testMpeg2Goog0Perf0176x0144() throws Exception { perf(MPEG2, 176, 144, GOOG, 0); }
public void testMpeg2Other0Qual0176x0144() throws Exception { qual(MPEG2, 176, 144, OTHER, 0); }
public void testMpeg2Other0Perf0176x0144() throws Exception { perf(MPEG2, 176, 144, OTHER, 0); }
public void testMpeg2Other1Qual0176x0144() throws Exception { qual(MPEG2, 176, 144, OTHER, 1); }
public void testMpeg2Other1Perf0176x0144() throws Exception { perf(MPEG2, 176, 144, OTHER, 1); }
public void testMpeg2Other2Qual0176x0144() throws Exception { qual(MPEG2, 176, 144, OTHER, 2); }
public void testMpeg2Other2Perf0176x0144() throws Exception { perf(MPEG2, 176, 144, OTHER, 2); }
public void testMpeg2Other3Qual0176x0144() throws Exception { qual(MPEG2, 176, 144, OTHER, 3); }
public void testMpeg2Other3Perf0176x0144() throws Exception { perf(MPEG2, 176, 144, OTHER, 3); }
public void testMpeg2Count0352x0288() throws Exception { count(MPEG2, 352, 288, 1, 4); }
public void testMpeg2Goog0Qual0352x0288() throws Exception { qual(MPEG2, 352, 288, GOOG, 0); }
public void testMpeg2Goog0Perf0352x0288() throws Exception { perf(MPEG2, 352, 288, GOOG, 0); }
public void testMpeg2Other0Qual0352x0288() throws Exception { qual(MPEG2, 352, 288, OTHER, 0); }
public void testMpeg2Other0Perf0352x0288() throws Exception { perf(MPEG2, 352, 288, OTHER, 0); }
public void testMpeg2Other1Qual0352x0288() throws Exception { qual(MPEG2, 352, 288, OTHER, 1); }
public void testMpeg2Other1Perf0352x0288() throws Exception { perf(MPEG2, 352, 288, OTHER, 1); }
public void testMpeg2Other2Qual0352x0288() throws Exception { qual(MPEG2, 352, 288, OTHER, 2); }
public void testMpeg2Other2Perf0352x0288() throws Exception { perf(MPEG2, 352, 288, OTHER, 2); }
public void testMpeg2Other3Qual0352x0288() throws Exception { qual(MPEG2, 352, 288, OTHER, 3); }
public void testMpeg2Other3Perf0352x0288() throws Exception { perf(MPEG2, 352, 288, OTHER, 3); }
public void testMpeg2Count0640x0480() throws Exception { count(MPEG2, 640, 480, 1, 4); }
public void testMpeg2Goog0Qual0640x0480() throws Exception { qual(MPEG2, 640, 480, GOOG, 0); }
public void testMpeg2Goog0Perf0640x0480() throws Exception { perf(MPEG2, 640, 480, GOOG, 0); }
public void testMpeg2Other0Qual0640x0480() throws Exception { qual(MPEG2, 640, 480, OTHER, 0); }
public void testMpeg2Other0Perf0640x0480() throws Exception { perf(MPEG2, 640, 480, OTHER, 0); }
public void testMpeg2Other1Qual0640x0480() throws Exception { qual(MPEG2, 640, 480, OTHER, 1); }
public void testMpeg2Other1Perf0640x0480() throws Exception { perf(MPEG2, 640, 480, OTHER, 1); }
public void testMpeg2Other2Qual0640x0480() throws Exception { qual(MPEG2, 640, 480, OTHER, 2); }
public void testMpeg2Other2Perf0640x0480() throws Exception { perf(MPEG2, 640, 480, OTHER, 2); }
public void testMpeg2Other3Qual0640x0480() throws Exception { qual(MPEG2, 640, 480, OTHER, 3); }
public void testMpeg2Other3Perf0640x0480() throws Exception { perf(MPEG2, 640, 480, OTHER, 3); }
public void testMpeg2Count1280x0720() throws Exception { count(MPEG2, 1280, 720, 1, 4); }
public void testMpeg2Goog0Qual1280x0720() throws Exception { qual(MPEG2, 1280, 720, GOOG, 0); }
public void testMpeg2Goog0Perf1280x0720() throws Exception { perf(MPEG2, 1280, 720, GOOG, 0); }
public void testMpeg2Other0Qual1280x0720() throws Exception { qual(MPEG2, 1280, 720, OTHER, 0); }
public void testMpeg2Other0Perf1280x0720() throws Exception { perf(MPEG2, 1280, 720, OTHER, 0); }
public void testMpeg2Other1Qual1280x0720() throws Exception { qual(MPEG2, 1280, 720, OTHER, 1); }
public void testMpeg2Other1Perf1280x0720() throws Exception { perf(MPEG2, 1280, 720, OTHER, 1); }
public void testMpeg2Other2Qual1280x0720() throws Exception { qual(MPEG2, 1280, 720, OTHER, 2); }
public void testMpeg2Other2Perf1280x0720() throws Exception { perf(MPEG2, 1280, 720, OTHER, 2); }
public void testMpeg2Other3Qual1280x0720() throws Exception { qual(MPEG2, 1280, 720, OTHER, 3); }
public void testMpeg2Other3Perf1280x0720() throws Exception { perf(MPEG2, 1280, 720, OTHER, 3); }
public void testMpeg2Count1920x1080() throws Exception { count(MPEG2, 1920, 1080, 1, 4); }
public void testMpeg2Goog0Qual1920x1080() throws Exception { qual(MPEG2, 1920, 1080, GOOG, 0); }
public void testMpeg2Goog0Perf1920x1080() throws Exception { perf(MPEG2, 1920, 1080, GOOG, 0); }
public void testMpeg2Other0Qual1920x1080() throws Exception { qual(MPEG2, 1920, 1080, OTHER, 0); }
public void testMpeg2Other0Perf1920x1080() throws Exception { perf(MPEG2, 1920, 1080, OTHER, 0); }
public void testMpeg2Other1Qual1920x1080() throws Exception { qual(MPEG2, 1920, 1080, OTHER, 1); }
public void testMpeg2Other1Perf1920x1080() throws Exception { perf(MPEG2, 1920, 1080, OTHER, 1); }
public void testMpeg2Other2Qual1920x1080() throws Exception { qual(MPEG2, 1920, 1080, OTHER, 2); }
public void testMpeg2Other2Perf1920x1080() throws Exception { perf(MPEG2, 1920, 1080, OTHER, 2); }
public void testMpeg2Other3Qual1920x1080() throws Exception { qual(MPEG2, 1920, 1080, OTHER, 3); }
public void testMpeg2Other3Perf1920x1080() throws Exception { perf(MPEG2, 1920, 1080, OTHER, 3); }
// MPEG4 tests
public void testMpeg4Count0176x0144() throws Exception { count(MPEG4, 176, 144, 1, 4); }
public void testMpeg4Goog0Qual0176x0144() throws Exception { qual(MPEG4, 176, 144, GOOG, 0); }
public void testMpeg4Goog0Perf0176x0144() throws Exception { perf(MPEG4, 176, 144, GOOG, 0); }
public void testMpeg4Other0Qual0176x0144() throws Exception { qual(MPEG4, 176, 144, OTHER, 0); }
public void testMpeg4Other0Perf0176x0144() throws Exception { perf(MPEG4, 176, 144, OTHER, 0); }
public void testMpeg4Other1Qual0176x0144() throws Exception { qual(MPEG4, 176, 144, OTHER, 1); }
public void testMpeg4Other1Perf0176x0144() throws Exception { perf(MPEG4, 176, 144, OTHER, 1); }
public void testMpeg4Other2Qual0176x0144() throws Exception { qual(MPEG4, 176, 144, OTHER, 2); }
public void testMpeg4Other2Perf0176x0144() throws Exception { perf(MPEG4, 176, 144, OTHER, 2); }
public void testMpeg4Other3Qual0176x0144() throws Exception { qual(MPEG4, 176, 144, OTHER, 3); }
public void testMpeg4Other3Perf0176x0144() throws Exception { perf(MPEG4, 176, 144, OTHER, 3); }
public void testMpeg4Count0352x0288() throws Exception { count(MPEG4, 352, 288, 1, 4); }
public void testMpeg4Goog0Qual0352x0288() throws Exception { qual(MPEG4, 352, 288, GOOG, 0); }
public void testMpeg4Goog0Perf0352x0288() throws Exception { perf(MPEG4, 352, 288, GOOG, 0); }
public void testMpeg4Other0Qual0352x0288() throws Exception { qual(MPEG4, 352, 288, OTHER, 0); }
public void testMpeg4Other0Perf0352x0288() throws Exception { perf(MPEG4, 352, 288, OTHER, 0); }
public void testMpeg4Other1Qual0352x0288() throws Exception { qual(MPEG4, 352, 288, OTHER, 1); }
public void testMpeg4Other1Perf0352x0288() throws Exception { perf(MPEG4, 352, 288, OTHER, 1); }
public void testMpeg4Other2Qual0352x0288() throws Exception { qual(MPEG4, 352, 288, OTHER, 2); }
public void testMpeg4Other2Perf0352x0288() throws Exception { perf(MPEG4, 352, 288, OTHER, 2); }
public void testMpeg4Other3Qual0352x0288() throws Exception { qual(MPEG4, 352, 288, OTHER, 3); }
public void testMpeg4Other3Perf0352x0288() throws Exception { perf(MPEG4, 352, 288, OTHER, 3); }
public void testMpeg4Count0640x0480() throws Exception { count(MPEG4, 640, 480, 1, 4); }
public void testMpeg4Goog0Qual0640x0480() throws Exception { qual(MPEG4, 640, 480, GOOG, 0); }
public void testMpeg4Goog0Perf0640x0480() throws Exception { perf(MPEG4, 640, 480, GOOG, 0); }
public void testMpeg4Other0Qual0640x0480() throws Exception { qual(MPEG4, 640, 480, OTHER, 0); }
public void testMpeg4Other0Perf0640x0480() throws Exception { perf(MPEG4, 640, 480, OTHER, 0); }
public void testMpeg4Other1Qual0640x0480() throws Exception { qual(MPEG4, 640, 480, OTHER, 1); }
public void testMpeg4Other1Perf0640x0480() throws Exception { perf(MPEG4, 640, 480, OTHER, 1); }
public void testMpeg4Other2Qual0640x0480() throws Exception { qual(MPEG4, 640, 480, OTHER, 2); }
public void testMpeg4Other2Perf0640x0480() throws Exception { perf(MPEG4, 640, 480, OTHER, 2); }
public void testMpeg4Other3Qual0640x0480() throws Exception { qual(MPEG4, 640, 480, OTHER, 3); }
public void testMpeg4Other3Perf0640x0480() throws Exception { perf(MPEG4, 640, 480, OTHER, 3); }
public void testMpeg4Count1280x0720() throws Exception { count(MPEG4, 1280, 720, 1, 4); }
public void testMpeg4Goog0Qual1280x0720() throws Exception { qual(MPEG4, 1280, 720, GOOG, 0); }
public void testMpeg4Goog0Perf1280x0720() throws Exception { perf(MPEG4, 1280, 720, GOOG, 0); }
public void testMpeg4Other0Qual1280x0720() throws Exception { qual(MPEG4, 1280, 720, OTHER, 0); }
public void testMpeg4Other0Perf1280x0720() throws Exception { perf(MPEG4, 1280, 720, OTHER, 0); }
public void testMpeg4Other1Qual1280x0720() throws Exception { qual(MPEG4, 1280, 720, OTHER, 1); }
public void testMpeg4Other1Perf1280x0720() throws Exception { perf(MPEG4, 1280, 720, OTHER, 1); }
public void testMpeg4Other2Qual1280x0720() throws Exception { qual(MPEG4, 1280, 720, OTHER, 2); }
public void testMpeg4Other2Perf1280x0720() throws Exception { perf(MPEG4, 1280, 720, OTHER, 2); }
public void testMpeg4Other3Qual1280x0720() throws Exception { qual(MPEG4, 1280, 720, OTHER, 3); }
public void testMpeg4Other3Perf1280x0720() throws Exception { perf(MPEG4, 1280, 720, OTHER, 3); }
// VP8 tests
public void testVp8Count0320x0180() throws Exception { count(VP8, 320, 180, 1, 2); }
public void testVp8Goog0Qual0320x0180() throws Exception { qual(VP8, 320, 180, GOOG, 0); }
public void testVp8Goog0Perf0320x0180() throws Exception { perf(VP8, 320, 180, GOOG, 0); }
public void testVp8Other0Qual0320x0180() throws Exception { qual(VP8, 320, 180, OTHER, 0); }
public void testVp8Other0Perf0320x0180() throws Exception { perf(VP8, 320, 180, OTHER, 0); }
public void testVp8Other1Qual0320x0180() throws Exception { qual(VP8, 320, 180, OTHER, 1); }
public void testVp8Other1Perf0320x0180() throws Exception { perf(VP8, 320, 180, OTHER, 1); }
public void testVp8Count0640x0360() throws Exception { count(VP8, 640, 360, 1, 2); }
public void testVp8Goog0Qual0640x0360() throws Exception { qual(VP8, 640, 360, GOOG, 0); }
public void testVp8Goog0Perf0640x0360() throws Exception { perf(VP8, 640, 360, GOOG, 0); }
public void testVp8Other0Qual0640x0360() throws Exception { qual(VP8, 640, 360, OTHER, 0); }
public void testVp8Other0Perf0640x0360() throws Exception { perf(VP8, 640, 360, OTHER, 0); }
public void testVp8Other1Qual0640x0360() throws Exception { qual(VP8, 640, 360, OTHER, 1); }
public void testVp8Other1Perf0640x0360() throws Exception { perf(VP8, 640, 360, OTHER, 1); }
public void testVp8Count1280x0720() throws Exception { count(VP8, 1280, 720, 1, 2); }
public void testVp8Goog0Qual1280x0720() throws Exception { qual(VP8, 1280, 720, GOOG, 0); }
public void testVp8Goog0Perf1280x0720() throws Exception { perf(VP8, 1280, 720, GOOG, 0); }
public void testVp8Other0Qual1280x0720() throws Exception { qual(VP8, 1280, 720, OTHER, 0); }
public void testVp8Other0Perf1280x0720() throws Exception { perf(VP8, 1280, 720, OTHER, 0); }
public void testVp8Other1Qual1280x0720() throws Exception { qual(VP8, 1280, 720, OTHER, 1); }
public void testVp8Other1Perf1280x0720() throws Exception { perf(VP8, 1280, 720, OTHER, 1); }
public void testVp8Count1920x1080() throws Exception { count(VP8, 1920, 1080, 1, 2); }
public void testVp8Goog0Qual1920x1080() throws Exception { qual(VP8, 1920, 1080, GOOG, 0); }
public void testVp8Goog0Perf1920x1080() throws Exception { perf(VP8, 1920, 1080, GOOG, 0); }
public void testVp8Other0Qual1920x1080() throws Exception { qual(VP8, 1920, 1080, OTHER, 0); }
public void testVp8Other0Perf1920x1080() throws Exception { perf(VP8, 1920, 1080, OTHER, 0); }
public void testVp8Other1Qual1920x1080() throws Exception { qual(VP8, 1920, 1080, OTHER, 1); }
public void testVp8Other1Perf1920x1080() throws Exception { perf(VP8, 1920, 1080, OTHER, 1); }
// VP9 tests
public void testVp9Count0320x0180() throws Exception { count(VP9, 320, 180, 1, 4); }
public void testVp9Goog0Qual0320x0180() throws Exception { qual(VP9, 320, 180, GOOG, 0); }
public void testVp9Goog0Perf0320x0180() throws Exception { perf(VP9, 320, 180, GOOG, 0); }
public void testVp9Other0Qual0320x0180() throws Exception { qual(VP9, 320, 180, OTHER, 0); }
public void testVp9Other0Perf0320x0180() throws Exception { perf(VP9, 320, 180, OTHER, 0); }
public void testVp9Other1Qual0320x0180() throws Exception { qual(VP9, 320, 180, OTHER, 1); }
public void testVp9Other1Perf0320x0180() throws Exception { perf(VP9, 320, 180, OTHER, 1); }
public void testVp9Other2Qual0320x0180() throws Exception { qual(VP9, 320, 180, OTHER, 2); }
public void testVp9Other2Perf0320x0180() throws Exception { perf(VP9, 320, 180, OTHER, 2); }
public void testVp9Other3Qual0320x0180() throws Exception { qual(VP9, 320, 180, OTHER, 3); }
public void testVp9Other3Perf0320x0180() throws Exception { perf(VP9, 320, 180, OTHER, 3); }
public void testVp9Count0640x0360() throws Exception { count(VP9, 640, 360, 1, 4); }
public void testVp9Goog0Qual0640x0360() throws Exception { qual(VP9, 640, 360, GOOG, 0); }
public void testVp9Goog0Perf0640x0360() throws Exception { perf(VP9, 640, 360, GOOG, 0); }
public void testVp9Other0Qual0640x0360() throws Exception { qual(VP9, 640, 360, OTHER, 0); }
public void testVp9Other0Perf0640x0360() throws Exception { perf(VP9, 640, 360, OTHER, 0); }
public void testVp9Other1Qual0640x0360() throws Exception { qual(VP9, 640, 360, OTHER, 1); }
public void testVp9Other1Perf0640x0360() throws Exception { perf(VP9, 640, 360, OTHER, 1); }
public void testVp9Other2Qual0640x0360() throws Exception { qual(VP9, 640, 360, OTHER, 2); }
public void testVp9Other2Perf0640x0360() throws Exception { perf(VP9, 640, 360, OTHER, 2); }
public void testVp9Other3Qual0640x0360() throws Exception { qual(VP9, 640, 360, OTHER, 3); }
public void testVp9Other3Perf0640x0360() throws Exception { perf(VP9, 640, 360, OTHER, 3); }
public void testVp9Count1280x0720() throws Exception { count(VP9, 1280, 720, 1, 4); }
public void testVp9Goog0Qual1280x0720() throws Exception { qual(VP9, 1280, 720, GOOG, 0); }
public void testVp9Goog0Perf1280x0720() throws Exception { perf(VP9, 1280, 720, GOOG, 0); }
public void testVp9Other0Qual1280x0720() throws Exception { qual(VP9, 1280, 720, OTHER, 0); }
public void testVp9Other0Perf1280x0720() throws Exception { perf(VP9, 1280, 720, OTHER, 0); }
public void testVp9Other1Qual1280x0720() throws Exception { qual(VP9, 1280, 720, OTHER, 1); }
public void testVp9Other1Perf1280x0720() throws Exception { perf(VP9, 1280, 720, OTHER, 1); }
public void testVp9Other2Qual1280x0720() throws Exception { qual(VP9, 1280, 720, OTHER, 2); }
public void testVp9Other2Perf1280x0720() throws Exception { perf(VP9, 1280, 720, OTHER, 2); }
public void testVp9Other3Qual1280x0720() throws Exception { qual(VP9, 1280, 720, OTHER, 3); }
public void testVp9Other3Perf1280x0720() throws Exception { perf(VP9, 1280, 720, OTHER, 3); }
public void testVp9Count1920x1080() throws Exception { count(VP9, 1920, 1080, 1, 4); }
public void testVp9Goog0Qual1920x1080() throws Exception { qual(VP9, 1920, 1080, GOOG, 0); }
public void testVp9Goog0Perf1920x1080() throws Exception { perf(VP9, 1920, 1080, GOOG, 0); }
public void testVp9Other0Qual1920x1080() throws Exception { qual(VP9, 1920, 1080, OTHER, 0); }
public void testVp9Other0Perf1920x1080() throws Exception { perf(VP9, 1920, 1080, OTHER, 0); }
public void testVp9Other1Qual1920x1080() throws Exception { qual(VP9, 1920, 1080, OTHER, 1); }
public void testVp9Other1Perf1920x1080() throws Exception { perf(VP9, 1920, 1080, OTHER, 1); }
public void testVp9Other2Qual1920x1080() throws Exception { qual(VP9, 1920, 1080, OTHER, 2); }
public void testVp9Other2Perf1920x1080() throws Exception { perf(VP9, 1920, 1080, OTHER, 2); }
public void testVp9Other3Qual1920x1080() throws Exception { qual(VP9, 1920, 1080, OTHER, 3); }
public void testVp9Other3Perf1920x1080() throws Exception { perf(VP9, 1920, 1080, OTHER, 3); }
public void testVp9Count3840x2160() throws Exception { count(VP9, 3840, 2160, 1, 4); }
public void testVp9Goog0Qual3840x2160() throws Exception { qual(VP9, 3840, 2160, GOOG, 0); }
public void testVp9Goog0Perf3840x2160() throws Exception { perf(VP9, 3840, 2160, GOOG, 0); }
public void testVp9Other0Qual3840x2160() throws Exception { qual(VP9, 3840, 2160, OTHER, 0); }
public void testVp9Other0Perf3840x2160() throws Exception { perf(VP9, 3840, 2160, OTHER, 0); }
public void testVp9Other1Qual3840x2160() throws Exception { qual(VP9, 3840, 2160, OTHER, 1); }
public void testVp9Other1Perf3840x2160() throws Exception { perf(VP9, 3840, 2160, OTHER, 1); }
public void testVp9Other2Qual3840x2160() throws Exception { qual(VP9, 3840, 2160, OTHER, 2); }
public void testVp9Other2Perf3840x2160() throws Exception { perf(VP9, 3840, 2160, OTHER, 2); }
public void testVp9Other3Qual3840x2160() throws Exception { qual(VP9, 3840, 2160, OTHER, 3); }
public void testVp9Other3Perf3840x2160() throws Exception { perf(VP9, 3840, 2160, OTHER, 3); }
private boolean isSrcSemiPlanar() {
return mSrcColorFormat == CodecCapabilities.COLOR_FormatYUV420SemiPlanar;
}
private boolean isSrcFlexYUV() {
return mSrcColorFormat == CodecCapabilities.COLOR_FormatYUV420Flexible;
}
private boolean isDstSemiPlanar() {
return mDstColorFormat == CodecCapabilities.COLOR_FormatYUV420SemiPlanar;
}
private boolean isDstFlexYUV() {
return mDstColorFormat == CodecCapabilities.COLOR_FormatYUV420Flexible;
}
private static int getColorFormat(CodecInfo info) {
if (info.mSupportSemiPlanar) {
return CodecCapabilities.COLOR_FormatYUV420SemiPlanar;
} else if (info.mSupportPlanar) {
return CodecCapabilities.COLOR_FormatYUV420Planar;
} else {
// FlexYUV must be supported
return CodecCapabilities.COLOR_FormatYUV420Flexible;
}
}
private static class RunResult {
public final int mNumFrames;
public final double mDurationMs;
public final double mRmsError;
RunResult() {
mNumFrames = 0;
mDurationMs = Double.NaN;
mRmsError = Double.NaN;
}
RunResult(int numFrames, double durationMs) {
mNumFrames = numFrames;
mDurationMs = durationMs;
mRmsError = Double.NaN;
}
RunResult(int numFrames, double durationMs, double rmsError) {
mNumFrames = numFrames;
mDurationMs = durationMs;
mRmsError = rmsError;
}
}
private void doTest(String mimeType, int w, int h, boolean isPerf, boolean isGoog, int ix)
throws Exception {
MediaFormat format = MediaFormat.createVideoFormat(mimeType, w, h);
String[] encoderNames = MediaUtils.getEncoderNames(isGoog, format);
String kind = isGoog ? "Google" : "non-Google";
if (encoderNames.length == 0) {
MediaUtils.skipTest("No " + kind + " encoders for " + format);
return;
} else if (encoderNames.length <= ix) {
Log.i(TAG, "No more " + kind + " encoders for " + format);
return;
}
if (isPerf) {
mTestConfig.initPerfTest();
}
String encoderName = encoderNames[ix];
CodecInfo infoEnc = CodecInfo.getSupportedFormatInfo(encoderName, mimeType, w, h, MAX_FPS);
assertNotNull(infoEnc);
// Skip decoding pass for performance tests as bitstream complexity is not representative
String[] decoderNames = null; // no decoding pass required by default
int codingPasses = 1; // used for time limit. 1 for encoding pass
int numRuns = mTestConfig.mNumberOfRepeat; // used for result array sizing
if (!isPerf) {
// consider all decoders for quality tests
decoderNames = MediaUtils.getDecoderNames(format);
if (decoderNames.length == 0) {
MediaUtils.skipTest("No decoders for " + format);
return;
}
numRuns *= decoderNames.length; // combine each decoder with the encoder
codingPasses += decoderNames.length;
}
// be a bit conservative
mTestConfig.mMaxTimeMs = Math.min(
mTestConfig.mMaxTimeMs, MAX_TEST_TIMEOUT_MS / 5 * 4 / codingPasses
/ mTestConfig.mNumberOfRepeat);
mVideoWidth = w;
mVideoHeight = h;
mSrcColorFormat = getColorFormat(infoEnc);
Log.i(TAG, "Testing video resolution " + w + "x" + h + ": enc format " + mSrcColorFormat);
initYUVPlane(w + YUV_PLANE_ADDITIONAL_LENGTH, h + YUV_PLANE_ADDITIONAL_LENGTH);
// Adjust total number of frames to prevent OOM.
Runtime rt = Runtime.getRuntime();
long usedMemory = rt.totalMemory() - rt.freeMemory();
mTestConfig.mTotalFrames = Math.min(mTestConfig.mTotalFrames,
(int) (rt.maxMemory() - usedMemory) / 4 * 3 /
(infoEnc.mBitRate / 8 / infoEnc.mFps + 1));
Log.i(TAG, "Total testing frames " + mTestConfig.mTotalFrames);
mEncoderFrameTimeUsDiff = new double[numRuns][mTestConfig.mTotalFrames - 1];
mEncoderFpsResults = new double[numRuns];
if (decoderNames != null) {
mDecoderFrameTimeUsDiff = new double[numRuns][mTestConfig.mTotalFrames - 1];
mDecoderFpsResults = new double[numRuns];
mTotalFpsResults = new double[numRuns];
mDecoderRmsErrorResults = new double[numRuns];
}
boolean success = true;
int runIx = 0;
for (int i = 0; i < mTestConfig.mNumberOfRepeat && success; i++) {
mCurrentTestRound = runIx;
format = new MediaFormat();
format.setString(MediaFormat.KEY_MIME, mimeType);
format.setInteger(MediaFormat.KEY_BIT_RATE, infoEnc.mBitRate);
format.setInteger(MediaFormat.KEY_WIDTH, w);
format.setInteger(MediaFormat.KEY_HEIGHT, h);
format.setInteger(MediaFormat.KEY_COLOR_FORMAT, mSrcColorFormat);
format.setInteger(MediaFormat.KEY_FRAME_RATE, infoEnc.mFps);
mFrameRate = infoEnc.mFps;
format.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, KEY_I_FRAME_INTERVAL);
RunResult encodingResult =
runEncoder(encoderName, format, mTestConfig.mTotalFrames, i);
double encodingTime = encodingResult.mDurationMs;
int framesEncoded = encodingResult.mNumFrames;
if (decoderNames != null && decoderNames.length > 0) {
for (String decoderName : decoderNames) {
CodecInfo infoDec =
CodecInfo.getSupportedFormatInfo(decoderName, mimeType, w, h, MAX_FPS);
assertNotNull(infoDec);
mDstColorFormat = getColorFormat(infoDec);
// re-initialize format for decoder
format = new MediaFormat();
format.setString(MediaFormat.KEY_MIME, mimeType);
format.setInteger(MediaFormat.KEY_WIDTH, w);
format.setInteger(MediaFormat.KEY_HEIGHT, h);
format.setInteger(MediaFormat.KEY_COLOR_FORMAT, mDstColorFormat);
RunResult decoderResult = runDecoder(decoderName, format, i);
if (decoderResult == null) {
success = false;
} else {
double decodingTime = decoderResult.mDurationMs;
mDecoderRmsErrorResults[runIx] = decoderResult.mRmsError;
mEncoderFpsResults[runIx] = framesEncoded / encodingTime;
int framesDecoded = decoderResult.mNumFrames;
mDecoderFpsResults[runIx] = framesDecoded / decodingTime;
if (framesDecoded == framesEncoded) {
mTotalFpsResults[runIx] =
framesEncoded / (encodingTime + decodingTime);
}
}
++runIx;
}
} else {
mEncoderFpsResults[runIx] = mTestConfig.mTotalFrames / encodingTime;
++runIx;
}
// clear things for re-start
mEncodedOutputBuffer.clear();
// it will be good to clean everything to make every run the same.
System.gc();
}
// log results before verification
double[] measuredFps = new double[numRuns];
if (isPerf) {
for (int i = 0; i < numRuns; i++) {
measuredFps[i] = logPerformanceResults(encoderName, i);
}
}
if (mTestConfig.mTestPixels && decoderNames != null) {
logQualityResults(mimeType, encoderName, decoderNames);
for (int i = 0; i < numRuns; i++) {
// make sure that rms error is not too big for all runs
if (mDecoderRmsErrorResults[i] >= mRmsErrorMargin) {
fail("rms error is bigger than the limit "
+ Arrays.toString(mDecoderRmsErrorResults) + " vs " + mRmsErrorMargin);
}
}
}
if (isPerf) {
String error = MediaPerfUtils.verifyAchievableFrameRates(
encoderName, mimeType, w, h, measuredFps);
assertNull(error, error);
}
assertTrue(success);
}
private void logQualityResults(String mimeType, String encoderName, String[] decoderNames) {
String streamName = "video_encoder_decoder_quality";
DeviceReportLog log = new DeviceReportLog(REPORT_LOG_NAME, streamName);
log.addValue("encoder_name", encoderName, ResultType.NEUTRAL, ResultUnit.NONE);
log.addValues("decoder_names", Arrays.asList(decoderNames), ResultType.NEUTRAL, ResultUnit.NONE);
log.addValue("mime_type", mimeType, ResultType.NEUTRAL, ResultUnit.NONE);
log.addValue("width", mVideoWidth, ResultType.NEUTRAL, ResultUnit.NONE);
log.addValue("height", mVideoHeight, ResultType.NEUTRAL, ResultUnit.NONE);
log.addValues("encoder_fps", mEncoderFpsResults, ResultType.HIGHER_BETTER,
ResultUnit.FPS);
log.addValues("rms_error", mDecoderRmsErrorResults, ResultType.LOWER_BETTER,
ResultUnit.NONE);
log.addValues("decoder_fps", mDecoderFpsResults, ResultType.HIGHER_BETTER,
ResultUnit.FPS);
log.addValues("encoder_decoder_fps", mTotalFpsResults, ResultType.HIGHER_BETTER,
ResultUnit.FPS);
log.addValue("encoder_average_fps", Stat.getAverage(mEncoderFpsResults),
ResultType.HIGHER_BETTER, ResultUnit.FPS);
log.addValue("decoder_average_fps", Stat.getAverage(mDecoderFpsResults),
ResultType.HIGHER_BETTER, ResultUnit.FPS);
log.setSummary("encoder_decoder_average_fps", Stat.getAverage(mTotalFpsResults),
ResultType.HIGHER_BETTER, ResultUnit.FPS);
log.submit(getInstrumentation());
}
private double logPerformanceResults(String encoderName, int round) {
String streamName = "video_encoder_performance";
DeviceReportLog log = new DeviceReportLog(REPORT_LOG_NAME, streamName);
String message = MediaPerfUtils.addPerformanceHeadersToLog(
log, "encoder stats:", round, encoderName,
mEncConfigFormat, mEncInputFormat, mEncOutputFormat);
double[] frameTimeUsDiff = mEncoderFrameTimeUsDiff[round];
double fps = MediaPerfUtils.addPerformanceStatsToLog(
log, new MediaUtils.Stats(frameTimeUsDiff), message);
if (mTestConfig.mReportFrameTime) {
double[] msDiff = new double[frameTimeUsDiff.length];
double nowUs = 0, lastMs = 0;
for (int i = 0; i < frameTimeUsDiff.length; ++i) {
nowUs += frameTimeUsDiff[i];
double nowMs = Math.round(nowUs) / 1000.;
msDiff[i] = Math.round((nowMs - lastMs) * 1000) / 1000.;
lastMs = nowMs;
}
log.addValues("encoder_raw_diff", msDiff, ResultType.NEUTRAL, ResultUnit.MS);
}
log.submit(getInstrumentation());
return fps;
}
/**
* run encoder benchmarking
* @param encoderName encoder name
* @param format format of media to encode
* @param totalFrames total number of frames to encode
* @return time taken in ms to encode the frames. This does not include initialization time.
*/
private RunResult runEncoder(
String encoderName, MediaFormat format, int totalFrames, int runId) {
MediaCodec codec = null;
try {
codec = MediaCodec.createByCodecName(encoderName);
mEncConfigFormat = format;
codec.configure(
format,
null /* surface */,
null /* crypto */,
MediaCodec.CONFIGURE_FLAG_ENCODE);
} catch (IllegalStateException e) {
Log.e(TAG, "codec '" + encoderName + "' failed configuration.");
codec.release();
assertTrue("codec '" + encoderName + "' failed configuration.", false);
} catch (IOException | NullPointerException e) {
Log.i(TAG, "could not find codec for " + format);
return new RunResult();
}
codec.start();
mEncInputFormat = codec.getInputFormat();
ByteBuffer[] codecOutputBuffers = codec.getOutputBuffers();
MediaFormat inputFormat = codec.getInputFormat();
mVideoStride = inputFormat.containsKey(MediaFormat.KEY_STRIDE)
? inputFormat.getInteger(MediaFormat.KEY_STRIDE)
: inputFormat.getInteger(MediaFormat.KEY_WIDTH);
mVideoVStride = inputFormat.containsKey(MediaFormat.KEY_SLICE_HEIGHT)
? inputFormat.getInteger(MediaFormat.KEY_SLICE_HEIGHT)
: inputFormat.getInteger(MediaFormat.KEY_HEIGHT);
int numBytesSubmitted = 0;
int numBytesDequeued = 0;
int inFramesCount = 0;
int outFramesCount = 0;
long lastOutputTimeUs = 0;
long start = System.currentTimeMillis();
while (true) {
int index;
if (inFramesCount < totalFrames) {
index = codec.dequeueInputBuffer(VIDEO_CODEC_WAIT_TIME_US /* timeoutUs */);
if (index != MediaCodec.INFO_TRY_AGAIN_LATER) {
int size;
long elapsedMs = System.currentTimeMillis() - start;
boolean eos = (inFramesCount == totalFrames - 1
|| elapsedMs > mTestConfig.mMaxTimeMs
|| (elapsedMs > mTestConfig.mMinTimeMs
&& inFramesCount > mTestConfig.mMinNumFrames));
// when encoder only supports flexYUV, use Image only; otherwise,
// use ByteBuffer & Image each on half of the frames to test both
if (isSrcFlexYUV() || inFramesCount % 2 == 0) {
Image image = codec.getInputImage(index);
// image should always be available
assertTrue(image != null);
size = queueInputImageEncoder(
codec, image, index, inFramesCount,
eos ? MediaCodec.BUFFER_FLAG_END_OF_STREAM : 0, runId);
} else {
ByteBuffer buffer = codec.getInputBuffer(index);
size = queueInputBufferEncoder(
codec, buffer, index, inFramesCount,
eos ? MediaCodec.BUFFER_FLAG_END_OF_STREAM : 0, runId);
}
inFramesCount++;
numBytesSubmitted += size;
if (VERBOSE) {
Log.d(TAG, "queued " + size + " bytes of input data, frame " +
(inFramesCount - 1));
}
}
}
MediaCodec.BufferInfo info = new MediaCodec.BufferInfo();
index = codec.dequeueOutputBuffer(info, VIDEO_CODEC_WAIT_TIME_US /* timeoutUs */);
if (index == MediaCodec.INFO_TRY_AGAIN_LATER) {
} else if (index == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) {
mEncOutputFormat = codec.getOutputFormat();
} else if (index == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED) {
codecOutputBuffers = codec.getOutputBuffers();
} else if (index >= 0) {
long nowUs = (System.nanoTime() + 500) / 1000;
dequeueOutputBufferEncoder(codec, codecOutputBuffers, index, info);
if ((info.flags & MediaCodec.BUFFER_FLAG_CODEC_CONFIG) == 0) {
int pos = outFramesCount - 1;
if (pos >= 0 && pos < mEncoderFrameTimeUsDiff[mCurrentTestRound].length) {
mEncoderFrameTimeUsDiff[mCurrentTestRound][pos] = nowUs - lastOutputTimeUs;
}
lastOutputTimeUs = nowUs;
numBytesDequeued += info.size;
++outFramesCount;
}
if ((info.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) {
if (VERBOSE) {
Log.d(TAG, "dequeued output EOS.");
}
break;
}
if (VERBOSE) {
Log.d(TAG, "dequeued " + info.size + " bytes of output data.");
}
}
}
long finish = System.currentTimeMillis();
int validDataNum = Math.min(mEncodedOutputBuffer.size() - 1,
mEncoderFrameTimeUsDiff[mCurrentTestRound].length);
mEncoderFrameTimeUsDiff[mCurrentTestRound] =
Arrays.copyOf(mEncoderFrameTimeUsDiff[mCurrentTestRound], validDataNum);
if (VERBOSE) {
Log.d(TAG, "queued a total of " + numBytesSubmitted + "bytes, "
+ "dequeued " + numBytesDequeued + " bytes.");
}
codec.stop();
codec.release();
codec = null;
mEncOutputFormat.setInteger(MediaFormat.KEY_BIT_RATE,
format.getInteger(MediaFormat.KEY_BIT_RATE));
mEncOutputFormat.setInteger(MediaFormat.KEY_FRAME_RATE,
format.getInteger(MediaFormat.KEY_FRAME_RATE));
if (outFramesCount > 0) {
mEncOutputFormat.setInteger(
"actual-bitrate",
(int)(numBytesDequeued * 8. * format.getInteger(MediaFormat.KEY_FRAME_RATE)
/ outFramesCount));
}
return new RunResult(outFramesCount, (finish - start) / 1000.);
}
/**
* Fills input buffer for encoder from YUV buffers.
* @return size of enqueued data.
*/
private int queueInputBufferEncoder(
MediaCodec codec, ByteBuffer buffer, int index, int frameCount, int flags, int runId) {
buffer.clear();
Point origin = getOrigin(frameCount, runId);
// Y color first
int srcOffsetY = origin.x + origin.y * mBufferWidth;
final byte[] yBuffer = mYBuffer.array();
for (int i = 0; i < mVideoHeight; i++) {
buffer.position(i * mVideoStride);
buffer.put(yBuffer, srcOffsetY, mVideoWidth);
srcOffsetY += mBufferWidth;
}
if (isSrcSemiPlanar()) {
int srcOffsetU = origin.y / 2 * mBufferWidth + origin.x / 2 * 2;
final byte[] uvBuffer = mUVBuffer.array();
for (int i = 0; i < mVideoHeight / 2; i++) {
buffer.position(mVideoVStride * mVideoStride + i * mVideoStride);
buffer.put(uvBuffer, srcOffsetU, mVideoWidth);
srcOffsetU += mBufferWidth;
}
} else {
int srcOffsetU = origin.y / 2 * mBufferWidth / 2 + origin.x / 2;
int srcOffsetV = srcOffsetU + mBufferWidth / 2 * mBufferHeight / 2;
final byte[] uvBuffer = mUVBuffer.array();
for (int i = 0; i < mVideoHeight / 2; i++) { //U only
buffer.position(mVideoVStride * mVideoStride + i * mVideoStride / 2);
buffer.put(uvBuffer, srcOffsetU, mVideoWidth / 2);
srcOffsetU += mBufferWidth / 2;
}
for (int i = 0; i < mVideoHeight / 2; i++) { //V only
buffer.position(mVideoVStride * mVideoStride * 5 / 4 + i * mVideoStride / 2);
buffer.put(uvBuffer, srcOffsetV, mVideoWidth / 2);
srcOffsetV += mBufferWidth / 2;
}
}
// submit till end of stride
int size = /* buffer.position(); */ mVideoStride * (mVideoVStride + mVideoHeight / 2);
long ptsUsec = computePresentationTime(frameCount);
codec.queueInputBuffer(index, 0 /* offset */, size, ptsUsec /* timeUs */, flags);
if (VERBOSE && (frameCount == 0)) {
printByteArray("Y ", mYBuffer.array(), 0, 20);
printByteArray("UV ", mUVBuffer.array(), 0, 20);
printByteArray("UV ", mUVBuffer.array(), mBufferWidth * 60, 20);
}
return size;
}
/**
* Fills input image for encoder from YUV buffers.
* @return size of enqueued data.
*/
private int queueInputImageEncoder(
MediaCodec codec, Image image, int index, int frameCount, int flags, int runId) {
assertTrue(image.getFormat() == ImageFormat.YUV_420_888);
Point origin = getOrigin(frameCount, runId);
// Y color first
CodecImage srcImage = new YUVImage(
origin,
mVideoWidth, mVideoHeight,
mBufferWidth, mBufferHeight,
isSrcSemiPlanar(),
mYDirectBuffer, mUVDirectBuffer);
CodecUtils.copyFlexYUVImage(image, srcImage);
int size = mVideoHeight * mVideoWidth * 3 / 2;
long ptsUsec = computePresentationTime(frameCount);
codec.queueInputBuffer(index, 0 /* offset */, size, ptsUsec /* timeUs */, flags);
if (VERBOSE && (frameCount == 0)) {
printByteArray("Y ", mYBuffer.array(), 0, 20);
printByteArray("UV ", mUVBuffer.array(), 0, 20);
printByteArray("UV ", mUVBuffer.array(), mBufferWidth * 60, 20);
}
return size;
}
/**
* Dequeue encoded data from output buffer and store for later usage.
*/
private void dequeueOutputBufferEncoder(
MediaCodec codec, ByteBuffer[] outputBuffers,
int index, MediaCodec.BufferInfo info) {
ByteBuffer output = outputBuffers[index];
int l = info.size;
ByteBuffer copied = ByteBuffer.allocate(l);
output.get(copied.array(), 0, l);
BufferInfo savedInfo = new BufferInfo();
savedInfo.set(0, l, info.presentationTimeUs, info.flags);
mEncodedOutputBuffer.addLast(Pair.create(copied, savedInfo));
codec.releaseOutputBuffer(index, false /* render */);
}
/**
* run decoder benchmarking with encoded stream stored from encoding phase
* @param decoderName decoder name
* @param format format of media to decode
* @return returns length-2 array with 0: time for decoding, 1 : rms error of pixels
*/
private RunResult runDecoder(String decoderName, MediaFormat format, int runId) {
MediaCodec codec = null;
try {
codec = MediaCodec.createByCodecName(decoderName);
} catch (IOException | NullPointerException e) {
Log.i(TAG, "could not find decoder for " + format);
return null;
}
codec.configure(format, null /* surface */, null /* crypto */, 0 /* flags */);
codec.start();
ByteBuffer[] codecInputBuffers = codec.getInputBuffers();
double totalErrorSquared = 0;
MediaCodec.BufferInfo info = new MediaCodec.BufferInfo();
boolean sawOutputEOS = false;
int inputLeft = mEncodedOutputBuffer.size();
int inputBufferCount = 0;
int outFrameCount = 0;
YUVValue expected = new YUVValue();
YUVValue decoded = new YUVValue();
long lastOutputTimeUs = 0;
long start = System.currentTimeMillis();
while (!sawOutputEOS) {
if (inputLeft > 0) {
int inputBufIndex = codec.dequeueInputBuffer(VIDEO_CODEC_WAIT_TIME_US);
if (inputBufIndex >= 0) {
ByteBuffer dstBuf = codecInputBuffers[inputBufIndex];
dstBuf.clear();
ByteBuffer src = mEncodedOutputBuffer.get(inputBufferCount).first;
BufferInfo srcInfo = mEncodedOutputBuffer.get(inputBufferCount).second;
int writeSize = src.capacity();
dstBuf.put(src.array(), 0, writeSize);
int flags = srcInfo.flags;
if ((System.currentTimeMillis() - start) > mTestConfig.mMaxTimeMs) {
flags |= MediaCodec.BUFFER_FLAG_END_OF_STREAM;
}
codec.queueInputBuffer(
inputBufIndex,
0 /* offset */,
writeSize,
srcInfo.presentationTimeUs,
flags);
inputLeft --;
inputBufferCount ++;
}
}
int res = codec.dequeueOutputBuffer(info, VIDEO_CODEC_WAIT_TIME_US);
if (res >= 0) {
int outputBufIndex = res;
// only do YUV compare on EOS frame if the buffer size is none-zero
if (info.size > 0) {
long nowUs = (System.nanoTime() + 500) / 1000;
int pos = outFrameCount - 1;
if (pos >= 0 && pos < mDecoderFrameTimeUsDiff[mCurrentTestRound].length) {
mDecoderFrameTimeUsDiff[mCurrentTestRound][pos] = nowUs - lastOutputTimeUs;
}
lastOutputTimeUs = nowUs;
if (mTestConfig.mTestPixels) {
Point origin = getOrigin(outFrameCount, runId);
int i;
// if decoder supports planar or semiplanar, check output with
// ByteBuffer & Image each on half of the points
int pixelCheckPerFrame = PIXEL_CHECK_PER_FRAME;
if (!isDstFlexYUV()) {
pixelCheckPerFrame /= 2;
ByteBuffer buf = codec.getOutputBuffer(outputBufIndex);
if (VERBOSE && (outFrameCount == 0)) {
printByteBuffer("Y ", buf, 0, 20);
printByteBuffer("UV ", buf, mVideoWidth * mVideoHeight, 20);
printByteBuffer("UV ", buf,
mVideoWidth * mVideoHeight + mVideoWidth * 60, 20);
}
for (i = 0; i < pixelCheckPerFrame; i++) {
int w = mRandom.nextInt(mVideoWidth);
int h = mRandom.nextInt(mVideoHeight);
getPixelValuesFromYUVBuffers(origin.x, origin.y, w, h, expected);
getPixelValuesFromOutputBuffer(buf, w, h, decoded);
if (VERBOSE) {
Log.i(TAG, outFrameCount + "-" + i + "- th round: ByteBuffer:"
+ " expected "
+ expected.mY + "," + expected.mU + "," + expected.mV
+ " decoded "
+ decoded.mY + "," + decoded.mU + "," + decoded.mV);
}
totalErrorSquared += expected.calcErrorSquared(decoded);
}
}
Image image = codec.getOutputImage(outputBufIndex);
assertTrue(image != null);
for (i = 0; i < pixelCheckPerFrame; i++) {
int w = mRandom.nextInt(mVideoWidth);
int h = mRandom.nextInt(mVideoHeight);
getPixelValuesFromYUVBuffers(origin.x, origin.y, w, h, expected);
getPixelValuesFromImage(image, w, h, decoded);
if (VERBOSE) {
Log.i(TAG, outFrameCount + "-" + i + "- th round: FlexYUV:"
+ " expcted "
+ expected.mY + "," + expected.mU + "," + expected.mV
+ " decoded "
+ decoded.mY + "," + decoded.mU + "," + decoded.mV);
}
totalErrorSquared += expected.calcErrorSquared(decoded);
}
}
outFrameCount++;
}
codec.releaseOutputBuffer(outputBufIndex, false /* render */);
if ((info.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) {
Log.d(TAG, "saw output EOS.");
sawOutputEOS = true;
}
} else if (res == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) {
mDecOutputFormat = codec.getOutputFormat();
Log.d(TAG, "output format has changed to " + mDecOutputFormat);
int colorFormat = mDecOutputFormat.getInteger(MediaFormat.KEY_COLOR_FORMAT);
if (colorFormat == CodecCapabilities.COLOR_FormatYUV420SemiPlanar
|| colorFormat == CodecCapabilities.COLOR_FormatYUV420Planar) {
mDstColorFormat = colorFormat;
} else {
mDstColorFormat = CodecCapabilities.COLOR_FormatYUV420Flexible;
Log.w(TAG, "output format changed to unsupported one " +
Integer.toHexString(colorFormat) + ", using FlexYUV");
}
mVideoStride = mDecOutputFormat.containsKey(MediaFormat.KEY_STRIDE)
? mDecOutputFormat.getInteger(MediaFormat.KEY_STRIDE)
: mDecOutputFormat.getInteger(MediaFormat.KEY_WIDTH);
mVideoVStride = mDecOutputFormat.containsKey(MediaFormat.KEY_SLICE_HEIGHT)
? mDecOutputFormat.getInteger(MediaFormat.KEY_SLICE_HEIGHT)
: mDecOutputFormat.getInteger(MediaFormat.KEY_HEIGHT);
}
}
long finish = System.currentTimeMillis();
int validDataNum = Math.min(outFrameCount - 1,
mDecoderFrameTimeUsDiff[mCurrentTestRound].length);
mDecoderFrameTimeUsDiff[mCurrentTestRound] =
Arrays.copyOf(mDecoderFrameTimeUsDiff[mCurrentTestRound], validDataNum);
codec.stop();
codec.release();
codec = null;
// divide by 3 as sum is done for Y, U, V.
double errorRms = Math.sqrt(totalErrorSquared / PIXEL_CHECK_PER_FRAME / outFrameCount / 3);
return new RunResult(outFrameCount, (finish - start) / 1000., errorRms);
}
/**
* returns origin in the absolute frame for given frame count.
* The video scene is moving by moving origin per each frame.
*/
private Point getOrigin(int frameCount, int runId) {
// Translation is basically:
// x = A * sin(B * t) + C * t
// y = D * cos(E * t) + F * t
// 'bouncing' in a [0, length] regions (constrained to [0, length] by mirroring at 0
// and length.)
double x = (1 - Math.sin(frameCount / (7. + (runId % 2)))) * 0.1 + frameCount * 0.005;
double y = (1 - Math.cos(frameCount / (10. + (runId & ~1))))
+ frameCount * (0.01 + runId / 1000.);
// At every 32nd or 13th frame out of 32, an additional varying offset is added to
// produce a jerk.
if (frameCount % 32 == 0) {
x += ((frameCount % 64) / 32) + 0.3 + y;
}
if (frameCount % 32 == 13) {
y += ((frameCount % 64) / 32) + 0.6 + x;
}
// constrain to region
int xi = (int)((x % 2) * YUV_PLANE_ADDITIONAL_LENGTH);
int yi = (int)((y % 2) * YUV_PLANE_ADDITIONAL_LENGTH);
if (xi > YUV_PLANE_ADDITIONAL_LENGTH) {
xi = 2 * YUV_PLANE_ADDITIONAL_LENGTH - xi;
}
if (yi > YUV_PLANE_ADDITIONAL_LENGTH) {
yi = 2 * YUV_PLANE_ADDITIONAL_LENGTH - yi;
}
return new Point(xi, yi);
}
/**
* initialize reference YUV plane
* @param w This should be YUV_PLANE_ADDITIONAL_LENGTH pixels bigger than video resolution
* to allow movements
* @param h This should be YUV_PLANE_ADDITIONAL_LENGTH pixels bigger than video resolution
* to allow movements
* @param semiPlanarEnc
* @param semiPlanarDec
*/
private void initYUVPlane(int w, int h) {
int bufferSizeY = w * h;
mYBuffer = ByteBuffer.allocate(bufferSizeY);
mUVBuffer = ByteBuffer.allocate(bufferSizeY / 2);
mYDirectBuffer = ByteBuffer.allocateDirect(bufferSizeY);
mUVDirectBuffer = ByteBuffer.allocateDirect(bufferSizeY / 2);
mBufferWidth = w;
mBufferHeight = h;
final byte[] yArray = mYBuffer.array();
final byte[] uvArray = mUVBuffer.array();
for (int i = 0; i < h; i++) {
for (int j = 0; j < w; j++) {
yArray[i * w + j] = clampY((i + j) & 0xff);
}
}
if (isSrcSemiPlanar()) {
for (int i = 0; i < h/2; i++) {
for (int j = 0; j < w/2; j++) {
uvArray[i * w + 2 * j] = (byte) (i & 0xff);
uvArray[i * w + 2 * j + 1] = (byte) (j & 0xff);
}
}
} else { // planar, U first, then V
int vOffset = bufferSizeY / 4;
for (int i = 0; i < h/2; i++) {
for (int j = 0; j < w/2; j++) {
uvArray[i * w/2 + j] = (byte) (i & 0xff);
uvArray[i * w/2 + vOffset + j] = (byte) (j & 0xff);
}
}
}
mYDirectBuffer.put(yArray);
mUVDirectBuffer.put(uvArray);
mYDirectBuffer.rewind();
mUVDirectBuffer.rewind();
}
/**
* class to store pixel values in YUV
*
*/
public class YUVValue {
public byte mY;
public byte mU;
public byte mV;
public YUVValue() {
}
public boolean equalTo(YUVValue other) {
return (mY == other.mY) && (mU == other.mU) && (mV == other.mV);
}
public double calcErrorSquared(YUVValue other) {
// Java's byte is signed but here we want to calculate difference in unsigned bytes.
double yDelta = (mY & 0xFF) - (other.mY & 0xFF);
double uDelta = (mU & 0xFF) - (other.mU & 0xFF);
double vDelta = (mV & 0xFF) - (other.mV & 0xFF);
return yDelta * yDelta + uDelta * uDelta + vDelta * vDelta;
}
}
/**
* Read YUV values from given position (x,y) for given origin (originX, originY)
* The whole data is already available from YBuffer and UVBuffer.
* @param result pass the result via this. This is for avoiding creating / destroying too many
* instances
*/
private void getPixelValuesFromYUVBuffers(int originX, int originY, int x, int y,
YUVValue result) {
result.mY = mYBuffer.get((originY + y) * mBufferWidth + (originX + x));
if (isSrcSemiPlanar()) {
int index = (originY + y) / 2 * mBufferWidth + (originX + x) / 2 * 2;
//Log.d(TAG, "YUV " + originX + "," + originY + "," + x + "," + y + "," + index);
result.mU = mUVBuffer.get(index);
result.mV = mUVBuffer.get(index + 1);
} else {
int vOffset = mBufferWidth * mBufferHeight / 4;
int index = (originY + y) / 2 * mBufferWidth / 2 + (originX + x) / 2;
result.mU = mUVBuffer.get(index);
result.mV = mUVBuffer.get(vOffset + index);
}
}
/**
* Read YUV pixels from decoded output buffer for give (x, y) position
* Output buffer is composed of Y parts followed by U/V
* @param result pass the result via this. This is for avoiding creating / destroying too many
* instances
*/
private void getPixelValuesFromOutputBuffer(ByteBuffer buffer, int x, int y, YUVValue result) {
result.mY = buffer.get(y * mVideoStride + x);
if (isDstSemiPlanar()) {
int index = mVideoStride * mVideoVStride + y / 2 * mVideoStride + x / 2 * 2;
//Log.d(TAG, "Decoded " + x + "," + y + "," + index);
result.mU = buffer.get(index);
result.mV = buffer.get(index + 1);
} else {
int vOffset = mVideoStride * mVideoVStride / 4;
int index = mVideoStride * mVideoVStride + y / 2 * mVideoStride / 2 + x / 2;
result.mU = buffer.get(index);
result.mV = buffer.get(index + vOffset);
}
}
private void getPixelValuesFromImage(Image image, int x, int y, YUVValue result) {
assertTrue(image.getFormat() == ImageFormat.YUV_420_888);
Plane[] planes = image.getPlanes();
assertTrue(planes.length == 3);
result.mY = getPixelFromPlane(planes[0], x, y);
result.mU = getPixelFromPlane(planes[1], x / 2, y / 2);
result.mV = getPixelFromPlane(planes[2], x / 2, y / 2);
}
private byte getPixelFromPlane(Plane plane, int x, int y) {
ByteBuffer buf = plane.getBuffer();
return buf.get(y * plane.getRowStride() + x * plane.getPixelStride());
}
/**
* Y cannot have full range. clamp it to prevent invalid value.
*/
private byte clampY(int y) {
if (y < Y_CLAMP_MIN) {
y = Y_CLAMP_MIN;
} else if (y > Y_CLAMP_MAX) {
y = Y_CLAMP_MAX;
}
return (byte) (y & 0xff);
}
// for debugging
private void printByteArray(String msg, byte[] data, int offset, int len) {
StringBuilder builder = new StringBuilder();
builder.append(msg);
builder.append(":");
for (int i = offset; i < offset + len; i++) {
builder.append(Integer.toHexString(data[i]));
builder.append(",");
}
builder.deleteCharAt(builder.length() - 1);
Log.i(TAG, builder.toString());
}
// for debugging
private void printByteBuffer(String msg, ByteBuffer data, int offset, int len) {
StringBuilder builder = new StringBuilder();
builder.append(msg);
builder.append(":");
for (int i = offset; i < offset + len; i++) {
builder.append(Integer.toHexString(data.get(i)));
builder.append(",");
}
builder.deleteCharAt(builder.length() - 1);
Log.i(TAG, builder.toString());
}
/**
* Generates the presentation time for frame N, in microseconds.
*/
private long computePresentationTime(int frameIndex) {
return 132 + frameIndex * 1000000L / mFrameRate;
}
}