Home | History | Annotate | Download | only in cts
      1 /*
      2  * Copyright 2014 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package android.media.cts;
     18 
     19 import android.media.cts.R;
     20 
     21 import android.media.cts.CodecUtils;
     22 
     23 import android.graphics.ImageFormat;
     24 import android.graphics.SurfaceTexture;
     25 import android.media.Image;
     26 import android.media.MediaCodec;
     27 import android.media.MediaCodec.BufferInfo;
     28 import android.media.MediaCodecInfo;
     29 import android.media.MediaCodecInfo.CodecCapabilities;
     30 import android.media.MediaCodecInfo.VideoCapabilities;
     31 import android.media.MediaCodecList;
     32 import android.media.MediaExtractor;
     33 import android.media.MediaFormat;
     34 import android.media.MediaMuxer;
     35 import android.net.Uri;
     36 import android.platform.test.annotations.AppModeFull;
     37 import android.util.Log;
     38 import android.util.Pair;
     39 import android.util.Range;
     40 import android.util.Size;
     41 import android.view.Surface;
     42 
     43 import com.android.compatibility.common.util.MediaUtils;
     44 
     45 import java.io.File;
     46 import java.io.IOException;
     47 import java.nio.ByteBuffer;
     48 import java.util.ArrayList;
     49 import java.util.function.Consumer;
     50 import java.util.function.Function;
     51 import java.util.HashMap;
     52 import java.util.HashSet;
     53 import java.util.Iterator;
     54 import java.util.LinkedList;
     55 import java.util.Map;
     56 import java.util.Set;
     57 
     58 @AppModeFull(reason = "TODO: evaluate and port to instant")
     59 public class VideoEncoderTest extends MediaPlayerTestBase {
     60     private static final int MAX_SAMPLE_SIZE = 256 * 1024;
     61     private static final String TAG = "VideoEncoderTest";
     62     private static final long FRAME_TIMEOUT_MS = 1000;
     63     // use larger delay before we get first frame, some encoders may need more time
     64     private static final long INIT_TIMEOUT_MS = 2000;
     65 
     66     private static final String SOURCE_URL =
     67         "android.resource://android.media.cts/raw/video_480x360_mp4_h264_871kbps_30fps";
     68 
     69     private final boolean DEBUG = false;
     70 
     71     class VideoStorage {
     72         private LinkedList<Pair<ByteBuffer, BufferInfo>> mStream;
     73         private MediaFormat mFormat;
     74         private int mInputBufferSize;
     75 
     76         public VideoStorage() {
     77             mStream = new LinkedList<Pair<ByteBuffer, BufferInfo>>();
     78         }
     79 
     80         public void setFormat(MediaFormat format) {
     81             mFormat = format;
     82         }
     83 
     84         public void addBuffer(ByteBuffer buffer, BufferInfo info) {
     85             ByteBuffer savedBuffer = ByteBuffer.allocate(info.size);
     86             savedBuffer.put(buffer);
     87             if (info.size > mInputBufferSize) {
     88                 mInputBufferSize = info.size;
     89             }
     90             BufferInfo savedInfo = new BufferInfo();
     91             savedInfo.set(0, savedBuffer.position(), info.presentationTimeUs, info.flags);
     92             mStream.addLast(Pair.create(savedBuffer, savedInfo));
     93         }
     94 
     95         private void play(MediaCodec decoder, Surface surface) {
     96             decoder.reset();
     97             final Object condition = new Object();
     98             final Iterator<Pair<ByteBuffer, BufferInfo>> it = mStream.iterator();
     99             decoder.setCallback(new MediaCodec.Callback() {
    100                 public void onOutputBufferAvailable(MediaCodec codec, int ix, BufferInfo info) {
    101                     codec.releaseOutputBuffer(ix, info.size > 0);
    102                     if ((info.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) {
    103                         synchronized (condition) {
    104                             condition.notifyAll();
    105                         }
    106                     }
    107                 }
    108                 public void onInputBufferAvailable(MediaCodec codec, int ix) {
    109                     if (it.hasNext()) {
    110                         Pair<ByteBuffer, BufferInfo> el = it.next();
    111                         el.first.clear();
    112                         try {
    113                             codec.getInputBuffer(ix).put(el.first);
    114                         } catch (java.nio.BufferOverflowException e) {
    115                             Log.e(TAG, "cannot fit " + el.first.limit()
    116                                     + "-byte encoded buffer into "
    117                                     + codec.getInputBuffer(ix).remaining()
    118                                     + "-byte input buffer of " + codec.getName()
    119                                     + " configured for " + codec.getInputFormat());
    120                             throw e;
    121                         }
    122                         BufferInfo info = el.second;
    123                         codec.queueInputBuffer(
    124                                 ix, 0, info.size, info.presentationTimeUs, info.flags);
    125                     }
    126                 }
    127                 public void onError(MediaCodec codec, MediaCodec.CodecException e) {
    128                     Log.i(TAG, "got codec exception", e);
    129                     fail("received codec error during decode" + e);
    130                 }
    131                 public void onOutputFormatChanged(MediaCodec codec, MediaFormat format) {
    132                     Log.i(TAG, "got output format " + format);
    133                 }
    134             });
    135             mFormat.setInteger(MediaFormat.KEY_MAX_INPUT_SIZE, mInputBufferSize);
    136             decoder.configure(mFormat, surface, null /* crypto */, 0 /* flags */);
    137             decoder.start();
    138             synchronized (condition) {
    139                 try {
    140                     condition.wait();
    141                 } catch (InterruptedException e) {
    142                     fail("playback interrupted");
    143                 }
    144             }
    145             decoder.stop();
    146         }
    147 
    148         public void playAll(Surface surface) {
    149             if (mFormat == null) {
    150                 Log.i(TAG, "no stream to play");
    151                 return;
    152             }
    153             String mime = mFormat.getString(MediaFormat.KEY_MIME);
    154             MediaCodecList mcl = new MediaCodecList(MediaCodecList.REGULAR_CODECS);
    155             for (MediaCodecInfo info : mcl.getCodecInfos()) {
    156                 if (info.isEncoder()) {
    157                     continue;
    158                 }
    159                 MediaCodec codec = null;
    160                 try {
    161                     CodecCapabilities caps = info.getCapabilitiesForType(mime);
    162                     if (!caps.isFormatSupported(mFormat)) {
    163                         continue;
    164                     }
    165                     codec = MediaCodec.createByCodecName(info.getName());
    166                 } catch (IllegalArgumentException | IOException e) {
    167                     continue;
    168                 }
    169                 play(codec, surface);
    170                 codec.release();
    171             }
    172         }
    173     }
    174 
    175     abstract class VideoProcessorBase extends MediaCodec.Callback {
    176         private static final String TAG = "VideoProcessorBase";
    177 
    178         /*
    179          * Set this to true to save the encoding results to /data/local/tmp
    180          * You will need to make /data/local/tmp writeable, run "setenforce 0",
    181          * and remove files left from a previous run.
    182          */
    183         private boolean mSaveResults = false;
    184         private static final String FILE_DIR = "/data/local/tmp";
    185         protected int mMuxIndex = -1;
    186 
    187         protected String mProcessorName = "VideoProcessor";
    188         private MediaExtractor mExtractor;
    189         protected MediaMuxer mMuxer;
    190         private ByteBuffer mBuffer = ByteBuffer.allocate(MAX_SAMPLE_SIZE);
    191         protected int mTrackIndex = -1;
    192         private boolean mSignaledDecoderEOS;
    193 
    194         protected boolean mCompleted;
    195         protected boolean mEncoderIsActive;
    196         protected boolean mEncodeOutputFormatUpdated;
    197         protected final Object mCondition = new Object();
    198         protected final Object mCodecLock = new Object();
    199 
    200         protected MediaFormat mDecFormat;
    201         protected MediaCodec mDecoder, mEncoder;
    202 
    203         private VideoStorage mEncodedStream;
    204         protected int mFrameRate = 0;
    205         protected int mBitRate = 0;
    206 
    207         protected Function<MediaFormat, Boolean> mUpdateConfigFormatHook;
    208         protected Function<MediaFormat, Boolean> mCheckOutputFormatHook;
    209 
    210         public void setProcessorName(String name) {
    211             mProcessorName = name;
    212         }
    213 
    214         public void setUpdateConfigHook(Function<MediaFormat, Boolean> hook) {
    215             mUpdateConfigFormatHook = hook;
    216         }
    217 
    218         public void setCheckOutputFormatHook(Function<MediaFormat, Boolean> hook) {
    219             mCheckOutputFormatHook = hook;
    220         }
    221 
    222         protected void open(String path) throws IOException {
    223             mExtractor = new MediaExtractor();
    224             if (path.startsWith("android.resource://")) {
    225                 mExtractor.setDataSource(mContext, Uri.parse(path), null);
    226             } else {
    227                 mExtractor.setDataSource(path);
    228             }
    229 
    230             for (int i = 0; i < mExtractor.getTrackCount(); i++) {
    231                 MediaFormat fmt = mExtractor.getTrackFormat(i);
    232                 String mime = fmt.getString(MediaFormat.KEY_MIME).toLowerCase();
    233                 if (mime.startsWith("video/")) {
    234                     mTrackIndex = i;
    235                     mDecFormat = fmt;
    236                     mExtractor.selectTrack(i);
    237                     break;
    238                 }
    239             }
    240             mEncodedStream = new VideoStorage();
    241             assertTrue("file " + path + " has no video", mTrackIndex >= 0);
    242         }
    243 
    244         // returns true if encoder supports the size
    245         protected boolean initCodecsAndConfigureEncoder(
    246                 String videoEncName, String outMime, int width, int height,
    247                 int colorFormat) throws IOException {
    248             mDecFormat.setInteger(MediaFormat.KEY_COLOR_FORMAT, colorFormat);
    249 
    250             MediaCodecList mcl = new MediaCodecList(MediaCodecList.REGULAR_CODECS);
    251             String videoDecName = mcl.findDecoderForFormat(mDecFormat);
    252             Log.i(TAG, "decoder for " + mDecFormat + " is " + videoDecName);
    253             mDecoder = MediaCodec.createByCodecName(videoDecName);
    254             mEncoder = MediaCodec.createByCodecName(videoEncName);
    255 
    256             mDecoder.setCallback(this);
    257             mEncoder.setCallback(this);
    258 
    259             VideoCapabilities encCaps =
    260                 mEncoder.getCodecInfo().getCapabilitiesForType(outMime).getVideoCapabilities();
    261             if (!encCaps.isSizeSupported(width, height)) {
    262                 Log.i(TAG, videoEncName + " does not support size: " + width + "x" + height);
    263                 return false;
    264             }
    265 
    266             MediaFormat outFmt = MediaFormat.createVideoFormat(outMime, width, height);
    267             int bitRate = 0;
    268             MediaUtils.setMaxEncoderFrameAndBitrates(encCaps, outFmt, 30);
    269             if (mFrameRate > 0) {
    270                 outFmt.setInteger(MediaFormat.KEY_FRAME_RATE, mFrameRate);
    271             }
    272             if (mBitRate > 0) {
    273                 outFmt.setInteger(MediaFormat.KEY_BIT_RATE, mBitRate);
    274             }
    275             outFmt.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, 1);
    276             outFmt.setInteger(MediaFormat.KEY_COLOR_FORMAT, colorFormat);
    277             // Some extra configure before starting the encoder.
    278             if (mUpdateConfigFormatHook != null) {
    279                 if (!mUpdateConfigFormatHook.apply(outFmt)) {
    280                     return false;
    281                 }
    282             }
    283             mEncoder.configure(outFmt, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE);
    284             Log.i(TAG, "encoder input format " + mEncoder.getInputFormat() + " from " + outFmt);
    285             if (mSaveResults) {
    286                 try {
    287                     String outFileName =
    288                             FILE_DIR + mProcessorName + "_" + bitRate + "bps";
    289                     if (outMime.equals(MediaFormat.MIMETYPE_VIDEO_VP8) ||
    290                             outMime.equals(MediaFormat.MIMETYPE_VIDEO_VP9)) {
    291                         mMuxer = new MediaMuxer(
    292                                 outFileName + ".webm", MediaMuxer.OutputFormat.MUXER_OUTPUT_WEBM);
    293                     } else {
    294                         mMuxer = new MediaMuxer(
    295                                 outFileName + ".mp4", MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4);
    296                     }
    297                     // The track can't be added until we have the codec specific data
    298                 } catch (Exception e) {
    299                     Log.i(TAG, "couldn't create muxer: " + e);
    300                 }
    301             }
    302             return true;
    303         }
    304 
    305         protected void close() {
    306             synchronized (mCodecLock) {
    307                 if (mDecoder != null) {
    308                     mDecoder.release();
    309                     mDecoder = null;
    310                 }
    311                 if (mEncoder != null) {
    312                     mEncoder.release();
    313                     mEncoder = null;
    314                 }
    315             }
    316             if (mExtractor != null) {
    317                 mExtractor.release();
    318                 mExtractor = null;
    319             }
    320             if (mMuxer != null) {
    321                 mMuxer.stop();
    322                 mMuxer.release();
    323                 mMuxer = null;
    324             }
    325         }
    326 
    327         // returns true if filled buffer
    328         protected boolean fillDecoderInputBuffer(int ix) {
    329             if (DEBUG) Log.v(TAG, "decoder received input #" + ix);
    330             while (!mSignaledDecoderEOS) {
    331                 int track = mExtractor.getSampleTrackIndex();
    332                 if (track >= 0 && track != mTrackIndex) {
    333                     mExtractor.advance();
    334                     continue;
    335                 }
    336                 int size = mExtractor.readSampleData(mBuffer, 0);
    337                 if (size < 0) {
    338                     // queue decoder input EOS
    339                     if (DEBUG) Log.v(TAG, "queuing decoder EOS");
    340                     mDecoder.queueInputBuffer(
    341                             ix, 0, 0, 0, MediaCodec.BUFFER_FLAG_END_OF_STREAM);
    342                     mSignaledDecoderEOS = true;
    343                 } else {
    344                     mBuffer.limit(size);
    345                     mBuffer.position(0);
    346                     BufferInfo info = new BufferInfo();
    347                     info.set(
    348                             0, mBuffer.limit(), mExtractor.getSampleTime(),
    349                             mExtractor.getSampleFlags());
    350                     mDecoder.getInputBuffer(ix).put(mBuffer);
    351                     if (DEBUG) Log.v(TAG, "queing input #" + ix + " for decoder with timestamp "
    352                             + info.presentationTimeUs);
    353                     mDecoder.queueInputBuffer(
    354                             ix, 0, mBuffer.limit(), info.presentationTimeUs, 0);
    355                 }
    356                 mExtractor.advance();
    357                 return true;
    358             }
    359             return false;
    360         }
    361 
    362         protected void emptyEncoderOutputBuffer(int ix, BufferInfo info) {
    363             if (DEBUG) Log.v(TAG, "encoder received output #" + ix
    364                      + " (sz=" + info.size + ", f=" + info.flags
    365                      + ", ts=" + info.presentationTimeUs + ")");
    366             ByteBuffer outputBuffer = mEncoder.getOutputBuffer(ix);
    367             mEncodedStream.addBuffer(outputBuffer, info);
    368 
    369             if (mMuxer != null) {
    370                 // reset position as addBuffer() modifies it
    371                 outputBuffer.position(info.offset);
    372                 outputBuffer.limit(info.offset + info.size);
    373                 mMuxer.writeSampleData(mMuxIndex, outputBuffer, info);
    374             }
    375 
    376             if (!mCompleted) {
    377                 mEncoder.releaseOutputBuffer(ix, false);
    378                 if ((info.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) {
    379                     Log.d(TAG, "encoder received output EOS");
    380                     synchronized(mCondition) {
    381                         mCompleted = true;
    382                         mCondition.notifyAll(); // condition is always satisfied
    383                     }
    384                 } else {
    385                     synchronized(mCondition) {
    386                         mEncoderIsActive = true;
    387                     }
    388                 }
    389             }
    390         }
    391 
    392         protected void saveEncoderFormat(MediaFormat format) {
    393             mEncodedStream.setFormat(format);
    394             if (mCheckOutputFormatHook != null) {
    395                 mCheckOutputFormatHook.apply(format);
    396             }
    397             if (mMuxer != null) {
    398                 if (mMuxIndex < 0) {
    399                     mMuxIndex = mMuxer.addTrack(format);
    400                     mMuxer.start();
    401                 }
    402             }
    403         }
    404 
    405         public void playBack(Surface surface) {
    406             mEncodedStream.playAll(surface);
    407         }
    408 
    409         public void setFrameAndBitRates(int frameRate, int bitRate) {
    410             mFrameRate = frameRate;
    411             mBitRate = bitRate;
    412         }
    413 
    414         @Override
    415         public void onInputBufferAvailable(MediaCodec mediaCodec, int ix) {
    416             synchronized (mCodecLock) {
    417                 if (mEncoder != null && mDecoder != null) {
    418                     onInputBufferAvailableLocked(mediaCodec, ix);
    419                 }
    420             }
    421         }
    422 
    423         @Override
    424         public void onOutputBufferAvailable(
    425                 MediaCodec mediaCodec, int ix, BufferInfo info) {
    426             synchronized (mCodecLock) {
    427                 if (mEncoder != null && mDecoder != null) {
    428                     onOutputBufferAvailableLocked(mediaCodec, ix, info);
    429                 }
    430             }
    431         }
    432 
    433         public abstract boolean processLoop(
    434                 String path, String outMime, String videoEncName,
    435                 int width, int height, boolean optional);
    436         protected abstract void onInputBufferAvailableLocked(
    437                 MediaCodec mediaCodec, int ix);
    438         protected abstract void onOutputBufferAvailableLocked(
    439                 MediaCodec mediaCodec, int ix, BufferInfo info);
    440     }
    441 
    442     class VideoProcessor extends VideoProcessorBase {
    443         private static final String TAG = "VideoProcessor";
    444         private boolean mWorkInProgress;
    445         private boolean mGotDecoderEOS;
    446         private boolean mSignaledEncoderEOS;
    447 
    448         private LinkedList<Pair<Integer, BufferInfo>> mBuffersToRender =
    449             new LinkedList<Pair<Integer, BufferInfo>>();
    450         private LinkedList<Integer> mEncInputBuffers = new LinkedList<Integer>();
    451 
    452         private int mEncInputBufferSize = -1;
    453 
    454         @Override
    455         public boolean processLoop(
    456                  String path, String outMime, String videoEncName,
    457                  int width, int height, boolean optional) {
    458             boolean skipped = true;
    459             try {
    460                 open(path);
    461                 if (!initCodecsAndConfigureEncoder(
    462                         videoEncName, outMime, width, height,
    463                         CodecCapabilities.COLOR_FormatYUV420Flexible)) {
    464                     assertTrue("could not configure encoder for supported size", optional);
    465                     return !skipped;
    466                 }
    467                 skipped = false;
    468 
    469                 mDecoder.configure(mDecFormat, null /* surface */, null /* crypto */, 0);
    470 
    471                 mDecoder.start();
    472                 mEncoder.start();
    473 
    474                 // main loop - process GL ops as only main thread has GL context
    475                 while (!mCompleted) {
    476                     Pair<Integer, BufferInfo> decBuffer = null;
    477                     int encBuffer = -1;
    478                     synchronized (mCondition) {
    479                         try {
    480                             // wait for an encoder input buffer and a decoder output buffer
    481                             // Use a timeout to avoid stalling the test if it doesn't arrive.
    482                             if (!haveBuffers() && !mCompleted) {
    483                                 mCondition.wait(mEncodeOutputFormatUpdated ?
    484                                         FRAME_TIMEOUT_MS : INIT_TIMEOUT_MS);
    485                             }
    486                         } catch (InterruptedException ie) {
    487                             fail("wait interrupted");  // shouldn't happen
    488                         }
    489                         if (mCompleted) {
    490                             break;
    491                         }
    492                         if (!haveBuffers()) {
    493                             if (mEncoderIsActive) {
    494                                 mEncoderIsActive = false;
    495                                 Log.d(TAG, "No more input but still getting output from encoder.");
    496                                 continue;
    497                             }
    498                             fail("timed out after " + mBuffersToRender.size()
    499                                     + " decoder output and " + mEncInputBuffers.size()
    500                                     + " encoder input buffers");
    501                         }
    502 
    503                         if (DEBUG) Log.v(TAG, "got image");
    504                         decBuffer = mBuffersToRender.removeFirst();
    505                         encBuffer = mEncInputBuffers.removeFirst();
    506                         if (isEOSOnlyBuffer(decBuffer)) {
    507                             queueEncoderEOS(decBuffer, encBuffer);
    508                             continue;
    509                         }
    510                         mWorkInProgress = true;
    511                     }
    512 
    513                     if (mWorkInProgress) {
    514                         renderDecodedBuffer(decBuffer, encBuffer);
    515                         synchronized(mCondition) {
    516                             mWorkInProgress = false;
    517                         }
    518                     }
    519                 }
    520             } catch (IOException e) {
    521                 e.printStackTrace();
    522                 fail("received exception " + e);
    523             } finally {
    524                 close();
    525             }
    526             return !skipped;
    527         }
    528 
    529         @Override
    530         public void onInputBufferAvailableLocked(MediaCodec mediaCodec, int ix) {
    531             if (mediaCodec == mDecoder) {
    532                 // fill input buffer from extractor
    533                 fillDecoderInputBuffer(ix);
    534             } else if (mediaCodec == mEncoder) {
    535                 synchronized(mCondition) {
    536                     mEncInputBuffers.addLast(ix);
    537                     tryToPropagateEOS();
    538                     if (haveBuffers()) {
    539                         mCondition.notifyAll();
    540                     }
    541                 }
    542             } else {
    543                 fail("received input buffer on " + mediaCodec.getName());
    544             }
    545         }
    546 
    547         @Override
    548         public void onOutputBufferAvailableLocked(
    549                 MediaCodec mediaCodec, int ix, BufferInfo info) {
    550             if (mediaCodec == mDecoder) {
    551                 if (DEBUG) Log.v(TAG, "decoder received output #" + ix
    552                          + " (sz=" + info.size + ", f=" + info.flags
    553                          + ", ts=" + info.presentationTimeUs + ")");
    554                 // render output buffer from decoder
    555                 if (!mGotDecoderEOS) {
    556                     boolean eos = (info.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0;
    557                     // can release empty buffers now
    558                     if (info.size == 0) {
    559                         mDecoder.releaseOutputBuffer(ix, false /* render */);
    560                         ix = -1; // dummy index used by render to not render
    561                     }
    562                     synchronized(mCondition) {
    563                         if (ix < 0 && eos && mBuffersToRender.size() > 0) {
    564                             // move lone EOS flag to last buffer to be rendered
    565                             mBuffersToRender.peekLast().second.flags |=
    566                                 MediaCodec.BUFFER_FLAG_END_OF_STREAM;
    567                         } else if (ix >= 0 || eos) {
    568                             mBuffersToRender.addLast(Pair.create(ix, info));
    569                         }
    570                         if (eos) {
    571                             tryToPropagateEOS();
    572                             mGotDecoderEOS = true;
    573                         }
    574                         if (haveBuffers()) {
    575                             mCondition.notifyAll();
    576                         }
    577                     }
    578                 }
    579             } else if (mediaCodec == mEncoder) {
    580                 emptyEncoderOutputBuffer(ix, info);
    581             } else {
    582                 fail("received output buffer on " + mediaCodec.getName());
    583             }
    584         }
    585 
    586         private void renderDecodedBuffer(Pair<Integer, BufferInfo> decBuffer, int encBuffer) {
    587             // process heavyweight actions under instance lock
    588             Image encImage = mEncoder.getInputImage(encBuffer);
    589             Image decImage = mDecoder.getOutputImage(decBuffer.first);
    590             assertNotNull("could not get encoder image for " + mEncoder.getInputFormat(), encImage);
    591             assertNotNull("could not get decoder image for " + mDecoder.getInputFormat(), decImage);
    592             assertEquals("incorrect decoder format",decImage.getFormat(), ImageFormat.YUV_420_888);
    593             assertEquals("incorrect encoder format", encImage.getFormat(), ImageFormat.YUV_420_888);
    594 
    595             CodecUtils.copyFlexYUVImage(encImage, decImage);
    596 
    597             // TRICKY: need this for queueBuffer
    598             if (mEncInputBufferSize < 0) {
    599                 mEncInputBufferSize = mEncoder.getInputBuffer(encBuffer).capacity();
    600             }
    601             Log.d(TAG, "queuing input #" + encBuffer + " for encoder (sz="
    602                     + mEncInputBufferSize + ", f=" + decBuffer.second.flags
    603                     + ", ts=" + decBuffer.second.presentationTimeUs + ")");
    604             mEncoder.queueInputBuffer(
    605                     encBuffer, 0, mEncInputBufferSize, decBuffer.second.presentationTimeUs,
    606                     decBuffer.second.flags);
    607             if ((decBuffer.second.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) {
    608                 mSignaledEncoderEOS = true;
    609             }
    610             mDecoder.releaseOutputBuffer(decBuffer.first, false /* render */);
    611         }
    612 
    613         @Override
    614         public void onError(MediaCodec mediaCodec, MediaCodec.CodecException e) {
    615             fail("received error on " + mediaCodec.getName() + ": " + e);
    616         }
    617 
    618         @Override
    619         public void onOutputFormatChanged(MediaCodec mediaCodec, MediaFormat mediaFormat) {
    620             Log.i(TAG, mediaCodec.getName() + " got new output format " + mediaFormat);
    621             if (mediaCodec == mEncoder) {
    622                 mEncodeOutputFormatUpdated = true;
    623                 saveEncoderFormat(mediaFormat);
    624             }
    625         }
    626 
    627         // next methods are synchronized on mCondition
    628         private boolean haveBuffers() {
    629             return mEncInputBuffers.size() > 0 && mBuffersToRender.size() > 0
    630                     && !mSignaledEncoderEOS;
    631         }
    632 
    633         private boolean isEOSOnlyBuffer(Pair<Integer, BufferInfo> decBuffer) {
    634             return decBuffer.first < 0 || decBuffer.second.size == 0;
    635         }
    636 
    637         protected void tryToPropagateEOS() {
    638             if (!mWorkInProgress && haveBuffers() && isEOSOnlyBuffer(mBuffersToRender.getFirst())) {
    639                 Pair<Integer, BufferInfo> decBuffer = mBuffersToRender.removeFirst();
    640                 int encBuffer = mEncInputBuffers.removeFirst();
    641                 queueEncoderEOS(decBuffer, encBuffer);
    642             }
    643         }
    644 
    645         void queueEncoderEOS(Pair<Integer, BufferInfo> decBuffer, int encBuffer) {
    646             Log.d(TAG, "signaling encoder EOS");
    647             mEncoder.queueInputBuffer(encBuffer, 0, 0, 0, MediaCodec.BUFFER_FLAG_END_OF_STREAM);
    648             mSignaledEncoderEOS = true;
    649             if (decBuffer.first >= 0) {
    650                 mDecoder.releaseOutputBuffer(decBuffer.first, false /* render */);
    651             }
    652         }
    653     }
    654 
    655 
    656     class SurfaceVideoProcessor extends VideoProcessorBase
    657             implements SurfaceTexture.OnFrameAvailableListener {
    658         private static final String TAG = "SurfaceVideoProcessor";
    659         private boolean mFrameAvailable;
    660         private boolean mGotDecoderEOS;
    661         private boolean mSignaledEncoderEOS;
    662 
    663         private InputSurface mEncSurface;
    664         private OutputSurface mDecSurface;
    665         private BufferInfo mInfoOnSurface;
    666 
    667         private LinkedList<Pair<Integer, BufferInfo>> mBuffersToRender =
    668             new LinkedList<Pair<Integer, BufferInfo>>();
    669 
    670         @Override
    671         public boolean processLoop(
    672                 String path, String outMime, String videoEncName,
    673                 int width, int height, boolean optional) {
    674             boolean skipped = true;
    675             try {
    676                 open(path);
    677                 if (!initCodecsAndConfigureEncoder(
    678                         videoEncName, outMime, width, height,
    679                         CodecCapabilities.COLOR_FormatSurface)) {
    680                     assertTrue("could not configure encoder for supported size", optional);
    681                     return !skipped;
    682                 }
    683                 skipped = false;
    684 
    685                 mEncSurface = new InputSurface(mEncoder.createInputSurface());
    686                 mEncSurface.makeCurrent();
    687 
    688                 mDecSurface = new OutputSurface(this);
    689                 //mDecSurface.changeFragmentShader(FRAGMENT_SHADER);
    690                 mDecoder.configure(mDecFormat, mDecSurface.getSurface(), null /* crypto */, 0);
    691 
    692                 mDecoder.start();
    693                 mEncoder.start();
    694 
    695                 // main loop - process GL ops as only main thread has GL context
    696                 while (!mCompleted) {
    697                     BufferInfo info = null;
    698                     synchronized (mCondition) {
    699                         try {
    700                             // wait for mFrameAvailable, which is set by onFrameAvailable().
    701                             // Use a timeout to avoid stalling the test if it doesn't arrive.
    702                             if (!mFrameAvailable && !mCompleted && !mEncoderIsActive) {
    703                                 mCondition.wait(mEncodeOutputFormatUpdated ?
    704                                         FRAME_TIMEOUT_MS : INIT_TIMEOUT_MS);
    705                             }
    706                         } catch (InterruptedException ie) {
    707                             fail("wait interrupted");  // shouldn't happen
    708                         }
    709                         if (mCompleted) {
    710                             break;
    711                         }
    712                         if (mEncoderIsActive) {
    713                             mEncoderIsActive = false;
    714                             if (DEBUG) Log.d(TAG, "encoder is still active, continue");
    715                             continue;
    716                         }
    717                         assertTrue("still waiting for image", mFrameAvailable);
    718                         if (DEBUG) Log.v(TAG, "got image");
    719                         info = mInfoOnSurface;
    720                     }
    721                     if (info == null) {
    722                         continue;
    723                     }
    724                     if (info.size > 0) {
    725                         mDecSurface.latchImage();
    726                         if (DEBUG) Log.v(TAG, "latched image");
    727                         mFrameAvailable = false;
    728 
    729                         mDecSurface.drawImage();
    730                         Log.d(TAG, "encoding frame at " + info.presentationTimeUs * 1000);
    731 
    732                         mEncSurface.setPresentationTime(info.presentationTimeUs * 1000);
    733                         mEncSurface.swapBuffers();
    734                     }
    735                     if ((info.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) {
    736                         mSignaledEncoderEOS = true;
    737                         Log.d(TAG, "signaling encoder EOS");
    738                         mEncoder.signalEndOfInputStream();
    739                     }
    740 
    741                     synchronized (mCondition) {
    742                         mInfoOnSurface = null;
    743                         if (mBuffersToRender.size() > 0 && mInfoOnSurface == null) {
    744                             if (DEBUG) Log.v(TAG, "handling postponed frame");
    745                             Pair<Integer, BufferInfo> nextBuffer = mBuffersToRender.removeFirst();
    746                             renderDecodedBuffer(nextBuffer.first, nextBuffer.second);
    747                         }
    748                     }
    749                 }
    750             } catch (IOException e) {
    751                 e.printStackTrace();
    752                 fail("received exception " + e);
    753             } finally {
    754                 close();
    755                 if (mEncSurface != null) {
    756                     mEncSurface.release();
    757                     mEncSurface = null;
    758                 }
    759                 if (mDecSurface != null) {
    760                     mDecSurface.release();
    761                     mDecSurface = null;
    762                 }
    763             }
    764             return !skipped;
    765         }
    766 
    767         @Override
    768         public void onFrameAvailable(SurfaceTexture st) {
    769             if (DEBUG) Log.v(TAG, "new frame available");
    770             synchronized (mCondition) {
    771                 assertFalse("mFrameAvailable already set, frame could be dropped", mFrameAvailable);
    772                 mFrameAvailable = true;
    773                 mCondition.notifyAll();
    774             }
    775         }
    776 
    777         @Override
    778         public void onInputBufferAvailableLocked(MediaCodec mediaCodec, int ix) {
    779             if (mediaCodec == mDecoder) {
    780                 // fill input buffer from extractor
    781                 fillDecoderInputBuffer(ix);
    782             } else {
    783                 fail("received input buffer on " + mediaCodec.getName());
    784             }
    785         }
    786 
    787         @Override
    788         public void onOutputBufferAvailableLocked(
    789                 MediaCodec mediaCodec, int ix, BufferInfo info) {
    790             if (mediaCodec == mDecoder) {
    791                 if (DEBUG) Log.v(TAG, "decoder received output #" + ix
    792                          + " (sz=" + info.size + ", f=" + info.flags
    793                          + ", ts=" + info.presentationTimeUs + ")");
    794                 // render output buffer from decoder
    795                 if (!mGotDecoderEOS) {
    796                     boolean eos = (info.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0;
    797                     if (eos) {
    798                         mGotDecoderEOS = true;
    799                     }
    800                     // can release empty buffers now
    801                     if (info.size == 0) {
    802                         mDecoder.releaseOutputBuffer(ix, false /* render */);
    803                         ix = -1; // dummy index used by render to not render
    804                     }
    805                     if (eos || info.size > 0) {
    806                         synchronized(mCondition) {
    807                             if (mInfoOnSurface != null || mBuffersToRender.size() > 0) {
    808                                 if (DEBUG) Log.v(TAG, "postponing render, surface busy");
    809                                 mBuffersToRender.addLast(Pair.create(ix, info));
    810                             } else {
    811                                 renderDecodedBuffer(ix, info);
    812                             }
    813                         }
    814                     }
    815                 }
    816             } else if (mediaCodec == mEncoder) {
    817                 emptyEncoderOutputBuffer(ix, info);
    818                 synchronized(mCondition) {
    819                     if (!mCompleted) {
    820                         mEncoderIsActive = true;
    821                         mCondition.notifyAll();
    822                     }
    823                 }
    824             } else {
    825                 fail("received output buffer on " + mediaCodec.getName());
    826             }
    827         }
    828 
    829         private void renderDecodedBuffer(int ix, BufferInfo info) {
    830             boolean eos = (info.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0;
    831             mInfoOnSurface = info;
    832             if (info.size > 0) {
    833                 Log.d(TAG, "rendering frame #" + ix + " at " + info.presentationTimeUs * 1000
    834                         + (eos ? " with EOS" : ""));
    835                 mDecoder.releaseOutputBuffer(ix, info.presentationTimeUs * 1000);
    836             }
    837 
    838             if (eos && info.size == 0) {
    839                 if (DEBUG) Log.v(TAG, "decoder output EOS available");
    840                 mFrameAvailable = true;
    841                 mCondition.notifyAll();
    842             }
    843         }
    844 
    845         @Override
    846         public void onError(MediaCodec mediaCodec, MediaCodec.CodecException e) {
    847             fail("received error on " + mediaCodec.getName() + ": " + e);
    848         }
    849 
    850         @Override
    851         public void onOutputFormatChanged(MediaCodec mediaCodec, MediaFormat mediaFormat) {
    852             Log.i(TAG, mediaCodec.getName() + " got new output format " + mediaFormat);
    853             if (mediaCodec == mEncoder) {
    854                 mEncodeOutputFormatUpdated = true;
    855                 saveEncoderFormat(mediaFormat);
    856             }
    857         }
    858     }
    859 
    860     class Encoder {
    861         final private String mName;
    862         final private String mMime;
    863         final private CodecCapabilities mCaps;
    864         final private VideoCapabilities mVideoCaps;
    865 
    866         final private Map<Size, Set<Size>> mMinMax;     // extreme sizes
    867         final private Map<Size, Set<Size>> mNearMinMax; // sizes near extreme
    868         final private Set<Size> mArbitraryW;            // arbitrary widths in the middle
    869         final private Set<Size> mArbitraryH;            // arbitrary heights in the middle
    870         final private Set<Size> mSizes;                 // all non-specifically tested sizes
    871 
    872         final private int xAlign;
    873         final private int yAlign;
    874 
    875         Encoder(String name, String mime, CodecCapabilities caps) {
    876             mName = name;
    877             mMime = mime;
    878             mCaps = caps;
    879             mVideoCaps = caps.getVideoCapabilities();
    880 
    881             /* calculate min/max sizes */
    882             mMinMax = new HashMap<Size, Set<Size>>();
    883             mNearMinMax = new HashMap<Size, Set<Size>>();
    884             mArbitraryW = new HashSet<Size>();
    885             mArbitraryH = new HashSet<Size>();
    886             mSizes = new HashSet<Size>();
    887 
    888             xAlign = mVideoCaps.getWidthAlignment();
    889             yAlign = mVideoCaps.getHeightAlignment();
    890 
    891             initializeSizes();
    892         }
    893 
    894         private void initializeSizes() {
    895             for (int x = 0; x < 2; ++x) {
    896                 for (int y = 0; y < 2; ++y) {
    897                     addExtremeSizesFor(x, y);
    898                 }
    899             }
    900 
    901             // initialize arbitrary sizes
    902             for (int i = 1; i <= 7; ++i) {
    903                 int j = ((7 * i) % 11) + 1;
    904                 int width, height;
    905                 try {
    906                     width = alignedPointInRange(i * 0.125, xAlign, mVideoCaps.getSupportedWidths());
    907                     height = alignedPointInRange(
    908                             j * 0.077, yAlign, mVideoCaps.getSupportedHeightsFor(width));
    909                     mArbitraryW.add(new Size(width, height));
    910                 } catch (IllegalArgumentException e) {
    911                 }
    912 
    913                 try {
    914                     height = alignedPointInRange(i * 0.125, yAlign, mVideoCaps.getSupportedHeights());
    915                     width = alignedPointInRange(j * 0.077, xAlign, mVideoCaps.getSupportedWidthsFor(height));
    916                     mArbitraryH.add(new Size(width, height));
    917                 } catch (IllegalArgumentException e) {
    918                 }
    919             }
    920             mArbitraryW.removeAll(mArbitraryH);
    921             mArbitraryW.removeAll(mSizes);
    922             mSizes.addAll(mArbitraryW);
    923             mArbitraryH.removeAll(mSizes);
    924             mSizes.addAll(mArbitraryH);
    925             if (DEBUG) Log.i(TAG, "arbitrary=" + mArbitraryW + "/" + mArbitraryH);
    926         }
    927 
    928         private void addExtremeSizesFor(int x, int y) {
    929             Set<Size> minMax = new HashSet<Size>();
    930             Set<Size> nearMinMax = new HashSet<Size>();
    931 
    932             for (int dx = 0; dx <= xAlign; dx += xAlign) {
    933                 for (int dy = 0; dy <= yAlign; dy += yAlign) {
    934                     Set<Size> bucket = (dx + dy == 0) ? minMax : nearMinMax;
    935                     try {
    936                         int width = getExtreme(mVideoCaps.getSupportedWidths(), x, dx);
    937                         int height = getExtreme(mVideoCaps.getSupportedHeightsFor(width), y, dy);
    938                         bucket.add(new Size(width, height));
    939 
    940                         // try max max with more reasonable ratio if too skewed
    941                         if (x + y == 2 && width >= 4 * height) {
    942                             Size wideScreen = getLargestSizeForRatio(16, 9);
    943                             width = getExtreme(
    944                                     mVideoCaps.getSupportedWidths()
    945                                             .intersect(0, wideScreen.getWidth()), x, dx);
    946                             height = getExtreme(mVideoCaps.getSupportedHeightsFor(width), y, 0);
    947                             bucket.add(new Size(width, height));
    948                         }
    949                     } catch (IllegalArgumentException e) {
    950                     }
    951 
    952                     try {
    953                         int height = getExtreme(mVideoCaps.getSupportedHeights(), y, dy);
    954                         int width = getExtreme(mVideoCaps.getSupportedWidthsFor(height), x, dx);
    955                         bucket.add(new Size(width, height));
    956 
    957                         // try max max with more reasonable ratio if too skewed
    958                         if (x + y == 2 && height >= 4 * width) {
    959                             Size wideScreen = getLargestSizeForRatio(9, 16);
    960                             height = getExtreme(
    961                                     mVideoCaps.getSupportedHeights()
    962                                             .intersect(0, wideScreen.getHeight()), y, dy);
    963                             width = getExtreme(mVideoCaps.getSupportedWidthsFor(height), x, dx);
    964                             bucket.add(new Size(width, height));
    965                         }
    966                     } catch (IllegalArgumentException e) {
    967                     }
    968                 }
    969             }
    970 
    971             // keep unique sizes
    972             minMax.removeAll(mSizes);
    973             mSizes.addAll(minMax);
    974             nearMinMax.removeAll(mSizes);
    975             mSizes.addAll(nearMinMax);
    976 
    977             mMinMax.put(new Size(x, y), minMax);
    978             mNearMinMax.put(new Size(x, y), nearMinMax);
    979             if (DEBUG) Log.i(TAG, x + "x" + y + ": minMax=" + mMinMax + ", near=" + mNearMinMax);
    980         }
    981 
    982         private int alignInRange(double value, int align, Range<Integer> range) {
    983             return range.clamp(align * (int)Math.round(value / align));
    984         }
    985 
    986         /* point should be between 0. and 1. */
    987         private int alignedPointInRange(double point, int align, Range<Integer> range) {
    988             return alignInRange(
    989                     range.getLower() + point * (range.getUpper() - range.getLower()), align, range);
    990         }
    991 
    992         private int getExtreme(Range<Integer> range, int i, int delta) {
    993             int dim = i == 1 ? range.getUpper() - delta : range.getLower() + delta;
    994             if (delta == 0
    995                     || (dim > range.getLower() && dim < range.getUpper())) {
    996                 return dim;
    997             }
    998             throw new IllegalArgumentException();
    999         }
   1000 
   1001         private Size getLargestSizeForRatio(int x, int y) {
   1002             Range<Integer> widthRange = mVideoCaps.getSupportedWidths();
   1003             Range<Integer> heightRange = mVideoCaps.getSupportedHeightsFor(widthRange.getUpper());
   1004             final int xAlign = mVideoCaps.getWidthAlignment();
   1005             final int yAlign = mVideoCaps.getHeightAlignment();
   1006 
   1007             // scale by alignment
   1008             int width = alignInRange(
   1009                     Math.sqrt(widthRange.getUpper() * heightRange.getUpper() * (double)x / y),
   1010                     xAlign, widthRange);
   1011             int height = alignInRange(
   1012                     width * (double)y / x, yAlign, mVideoCaps.getSupportedHeightsFor(width));
   1013             return new Size(width, height);
   1014         }
   1015 
   1016 
   1017         public boolean testExtreme(int x, int y, boolean flexYUV, boolean near) {
   1018             boolean skipped = true;
   1019             for (Size s : (near ? mNearMinMax : mMinMax).get(new Size(x, y))) {
   1020                 if (test(s.getWidth(), s.getHeight(), false /* optional */, flexYUV)) {
   1021                     skipped = false;
   1022                 }
   1023             }
   1024             return !skipped;
   1025         }
   1026 
   1027         public boolean testArbitrary(boolean flexYUV, boolean widths) {
   1028             boolean skipped = true;
   1029             for (Size s : (widths ? mArbitraryW : mArbitraryH)) {
   1030                 if (test(s.getWidth(), s.getHeight(), false /* optional */, flexYUV)) {
   1031                     skipped = false;
   1032                 }
   1033             }
   1034             return !skipped;
   1035         }
   1036 
   1037         public boolean testSpecific(int width, int height, boolean flexYUV) {
   1038             // already tested by one of the min/max tests
   1039             if (mSizes.contains(new Size(width, height))) {
   1040                 return false;
   1041             }
   1042             return test(width, height, true /* optional */, flexYUV);
   1043         }
   1044 
   1045         public boolean testIntraRefresh(int width, int height) {
   1046             if (!mCaps.isFeatureSupported(CodecCapabilities.FEATURE_IntraRefresh)) {
   1047                 return false;
   1048             }
   1049 
   1050             final int refreshPeriod[] = new int[] {10, 13, 17, 22, 29, 38, 50, 60};
   1051 
   1052             // Test the support of refresh periods in the range of 10 - 60 frames
   1053             for (int period : refreshPeriod) {
   1054                 Function<MediaFormat, Boolean> updateConfigFormatHook =
   1055                 new Function<MediaFormat, Boolean>() {
   1056                     public Boolean apply(MediaFormat fmt) {
   1057                         // set i-frame-interval to 10000 so encoded video only has 1 i-frame.
   1058                         fmt.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, 10000);
   1059                         fmt.setInteger(MediaFormat.KEY_INTRA_REFRESH_PERIOD, period);
   1060                         return true;
   1061                     }
   1062                 };
   1063 
   1064                 Function<MediaFormat, Boolean> checkOutputFormatHook =
   1065                 new Function<MediaFormat, Boolean>() {
   1066                     public Boolean apply(MediaFormat fmt) {
   1067                         int intraPeriod = fmt.getInteger(MediaFormat.KEY_INTRA_REFRESH_PERIOD);
   1068                         // Make sure intra period is correct and carried in the output format.
   1069                         // intraPeriod must be larger than 0 and operate within 20% of refresh period.
   1070                         if (intraPeriod > 1.2 * period || intraPeriod < 0.8 * period) {
   1071                             throw new RuntimeException("Intra period mismatch");
   1072                         }
   1073                         return true;
   1074                     }
   1075                 };
   1076 
   1077                 String testName =
   1078                 mName + '_' + width + "x" + height + '_' + "flexYUV_intraRefresh";
   1079 
   1080                 Consumer<VideoProcessorBase> configureVideoProcessor =
   1081                 new Consumer<VideoProcessorBase>() {
   1082                     public void accept(VideoProcessorBase processor) {
   1083                         processor.setProcessorName(testName);
   1084                         processor.setUpdateConfigHook(updateConfigFormatHook);
   1085                         processor.setCheckOutputFormatHook(checkOutputFormatHook);
   1086                     }
   1087                 };
   1088 
   1089                 if (!test(width, height, 0 /* frameRate */, 0 /* bitRate */, true /* optional */,
   1090                     true /* flex */, configureVideoProcessor)) {
   1091                     return false;
   1092                 }
   1093             }
   1094 
   1095             return true;
   1096         }
   1097 
   1098         public boolean testDetailed(
   1099                 int width, int height, int frameRate, int bitRate, boolean flexYUV) {
   1100             String testName =
   1101                     mName + '_' + width + "x" + height + '_' + (flexYUV ? "flexYUV" : " surface");
   1102             Consumer<VideoProcessorBase> configureVideoProcessor =
   1103                     new Consumer<VideoProcessorBase>() {
   1104                 public void accept(VideoProcessorBase processor) {
   1105                     processor.setProcessorName(testName);
   1106                 }
   1107             };
   1108             return test(width, height, frameRate, bitRate, true /* optional */, flexYUV,
   1109                     configureVideoProcessor);
   1110         }
   1111 
   1112         public boolean testSupport(int width, int height, int frameRate, int bitRate) {
   1113             return mVideoCaps.areSizeAndRateSupported(width, height, frameRate) &&
   1114                     mVideoCaps.getBitrateRange().contains(bitRate);
   1115         }
   1116 
   1117         private boolean test(
   1118                 int width, int height, boolean optional, boolean flexYUV) {
   1119             String testName =
   1120                     mName + '_' + width + "x" + height + '_' + (flexYUV ? "flexYUV" : " surface");
   1121             Consumer<VideoProcessorBase> configureVideoProcessor =
   1122                     new Consumer<VideoProcessorBase>() {
   1123                 public void accept(VideoProcessorBase processor) {
   1124                     processor.setProcessorName(testName);
   1125                 }
   1126             };
   1127             return test(width, height, 0 /* frameRate */, 0 /* bitRate */,
   1128                     optional, flexYUV, configureVideoProcessor);
   1129         }
   1130 
   1131         private boolean test(
   1132                 int width, int height, int frameRate, int bitRate, boolean optional,
   1133                 boolean flexYUV, Consumer<VideoProcessorBase> configureVideoProcessor) {
   1134             Log.i(TAG, "testing " + mMime + " on " + mName + " for " + width + "x" + height
   1135                     + (flexYUV ? " flexYUV" : " surface"));
   1136 
   1137             VideoProcessorBase processor =
   1138                 flexYUV ? new VideoProcessor() : new SurfaceVideoProcessor();
   1139 
   1140             processor.setFrameAndBitRates(frameRate, bitRate);
   1141             configureVideoProcessor.accept(processor);
   1142 
   1143             // We are using a resource URL as an example
   1144             boolean success = processor.processLoop(
   1145                     SOURCE_URL, mMime, mName, width, height, optional);
   1146             if (success) {
   1147                 processor.playBack(getActivity().getSurfaceHolder().getSurface());
   1148             }
   1149             return success;
   1150         }
   1151     }
   1152 
   1153     private Encoder[] googH265()  { return goog(MediaFormat.MIMETYPE_VIDEO_HEVC); }
   1154     private Encoder[] googH264()  { return goog(MediaFormat.MIMETYPE_VIDEO_AVC); }
   1155     private Encoder[] googH263()  { return goog(MediaFormat.MIMETYPE_VIDEO_H263); }
   1156     private Encoder[] googMpeg4() { return goog(MediaFormat.MIMETYPE_VIDEO_MPEG4); }
   1157     private Encoder[] googVP8()   { return goog(MediaFormat.MIMETYPE_VIDEO_VP8); }
   1158     private Encoder[] googVP9()   { return goog(MediaFormat.MIMETYPE_VIDEO_VP9); }
   1159 
   1160     private Encoder[] otherH265()  { return other(MediaFormat.MIMETYPE_VIDEO_HEVC); }
   1161     private Encoder[] otherH264()  { return other(MediaFormat.MIMETYPE_VIDEO_AVC); }
   1162     private Encoder[] otherH263()  { return other(MediaFormat.MIMETYPE_VIDEO_H263); }
   1163     private Encoder[] otherMpeg4() { return other(MediaFormat.MIMETYPE_VIDEO_MPEG4); }
   1164     private Encoder[] otherVP8()   { return other(MediaFormat.MIMETYPE_VIDEO_VP8); }
   1165     private Encoder[] otherVP9()   { return other(MediaFormat.MIMETYPE_VIDEO_VP9); }
   1166 
   1167     private Encoder[] goog(String mime) {
   1168         return encoders(mime, true /* goog */);
   1169     }
   1170 
   1171     private Encoder[] other(String mime) {
   1172         return encoders(mime, false /* goog */);
   1173     }
   1174 
   1175     private Encoder[] combineArray(Encoder[] a, Encoder[] b) {
   1176         Encoder[] all = new Encoder[a.length + b.length];
   1177         System.arraycopy(a, 0, all, 0, a.length);
   1178         System.arraycopy(b, 0, all, a.length, b.length);
   1179         return all;
   1180     }
   1181 
   1182     private Encoder[] h264()  {
   1183         return combineArray(googH264(), otherH264());
   1184     }
   1185 
   1186     private Encoder[] vp8()  {
   1187         return combineArray(googVP8(), otherVP8());
   1188     }
   1189 
   1190     private Encoder[] encoders(String mime, boolean goog) {
   1191         MediaCodecList mcl = new MediaCodecList(MediaCodecList.REGULAR_CODECS);
   1192         ArrayList<Encoder> result = new ArrayList<Encoder>();
   1193 
   1194         for (MediaCodecInfo info : mcl.getCodecInfos()) {
   1195             if (!info.isEncoder() || MediaUtils.isGoogle(info.getName()) != goog) {
   1196                 continue;
   1197             }
   1198             CodecCapabilities caps = null;
   1199             try {
   1200                 caps = info.getCapabilitiesForType(mime);
   1201             } catch (IllegalArgumentException e) { // mime is not supported
   1202                 continue;
   1203             }
   1204             assertNotNull(info.getName() + " capabilties for " + mime + " returned null", caps);
   1205             result.add(new Encoder(info.getName(), mime, caps));
   1206         }
   1207         return result.toArray(new Encoder[result.size()]);
   1208     }
   1209 
   1210     public void testGoogH265FlexMinMin()   { minmin(googH265(),   true /* flex */); }
   1211     public void testGoogH265SurfMinMin()   { minmin(googH265(),   false /* flex */); }
   1212     public void testGoogH264FlexMinMin()   { minmin(googH264(),   true /* flex */); }
   1213     public void testGoogH264SurfMinMin()   { minmin(googH264(),   false /* flex */); }
   1214     public void testGoogH263FlexMinMin()   { minmin(googH263(),   true /* flex */); }
   1215     public void testGoogH263SurfMinMin()   { minmin(googH263(),   false /* flex */); }
   1216     public void testGoogMpeg4FlexMinMin()  { minmin(googMpeg4(),  true /* flex */); }
   1217     public void testGoogMpeg4SurfMinMin()  { minmin(googMpeg4(),  false /* flex */); }
   1218     public void testGoogVP8FlexMinMin()    { minmin(googVP8(),    true /* flex */); }
   1219     public void testGoogVP8SurfMinMin()    { minmin(googVP8(),    false /* flex */); }
   1220     public void testGoogVP9FlexMinMin()    { minmin(googVP9(),    true /* flex */); }
   1221     public void testGoogVP9SurfMinMin()    { minmin(googVP9(),    false /* flex */); }
   1222 
   1223     public void testOtherH265FlexMinMin()  { minmin(otherH265(),  true /* flex */); }
   1224     public void testOtherH265SurfMinMin()  { minmin(otherH265(),  false /* flex */); }
   1225     public void testOtherH264FlexMinMin()  { minmin(otherH264(),  true /* flex */); }
   1226     public void testOtherH264SurfMinMin()  { minmin(otherH264(),  false /* flex */); }
   1227     public void testOtherH263FlexMinMin()  { minmin(otherH263(),  true /* flex */); }
   1228     public void testOtherH263SurfMinMin()  { minmin(otherH263(),  false /* flex */); }
   1229     public void testOtherMpeg4FlexMinMin() { minmin(otherMpeg4(), true /* flex */); }
   1230     public void testOtherMpeg4SurfMinMin() { minmin(otherMpeg4(), false /* flex */); }
   1231     public void testOtherVP8FlexMinMin()   { minmin(otherVP8(),   true /* flex */); }
   1232     public void testOtherVP8SurfMinMin()   { minmin(otherVP8(),   false /* flex */); }
   1233     public void testOtherVP9FlexMinMin()   { minmin(otherVP9(),   true /* flex */); }
   1234     public void testOtherVP9SurfMinMin()   { minmin(otherVP9(),   false /* flex */); }
   1235 
   1236     public void testGoogH265FlexMinMax()   { minmax(googH265(),   true /* flex */); }
   1237     public void testGoogH265SurfMinMax()   { minmax(googH265(),   false /* flex */); }
   1238     public void testGoogH264FlexMinMax()   { minmax(googH264(),   true /* flex */); }
   1239     public void testGoogH264SurfMinMax()   { minmax(googH264(),   false /* flex */); }
   1240     public void testGoogH263FlexMinMax()   { minmax(googH263(),   true /* flex */); }
   1241     public void testGoogH263SurfMinMax()   { minmax(googH263(),   false /* flex */); }
   1242     public void testGoogMpeg4FlexMinMax()  { minmax(googMpeg4(),  true /* flex */); }
   1243     public void testGoogMpeg4SurfMinMax()  { minmax(googMpeg4(),  false /* flex */); }
   1244     public void testGoogVP8FlexMinMax()    { minmax(googVP8(),    true /* flex */); }
   1245     public void testGoogVP8SurfMinMax()    { minmax(googVP8(),    false /* flex */); }
   1246     public void testGoogVP9FlexMinMax()    { minmax(googVP9(),    true /* flex */); }
   1247     public void testGoogVP9SurfMinMax()    { minmax(googVP9(),    false /* flex */); }
   1248 
   1249     public void testOtherH265FlexMinMax()  { minmax(otherH265(),  true /* flex */); }
   1250     public void testOtherH265SurfMinMax()  { minmax(otherH265(),  false /* flex */); }
   1251     public void testOtherH264FlexMinMax()  { minmax(otherH264(),  true /* flex */); }
   1252     public void testOtherH264SurfMinMax()  { minmax(otherH264(),  false /* flex */); }
   1253     public void testOtherH263FlexMinMax()  { minmax(otherH263(),  true /* flex */); }
   1254     public void testOtherH263SurfMinMax()  { minmax(otherH263(),  false /* flex */); }
   1255     public void testOtherMpeg4FlexMinMax() { minmax(otherMpeg4(), true /* flex */); }
   1256     public void testOtherMpeg4SurfMinMax() { minmax(otherMpeg4(), false /* flex */); }
   1257     public void testOtherVP8FlexMinMax()   { minmax(otherVP8(),   true /* flex */); }
   1258     public void testOtherVP8SurfMinMax()   { minmax(otherVP8(),   false /* flex */); }
   1259     public void testOtherVP9FlexMinMax()   { minmax(otherVP9(),   true /* flex */); }
   1260     public void testOtherVP9SurfMinMax()   { minmax(otherVP9(),   false /* flex */); }
   1261 
   1262     public void testGoogH265FlexMaxMin()   { maxmin(googH265(),   true /* flex */); }
   1263     public void testGoogH265SurfMaxMin()   { maxmin(googH265(),   false /* flex */); }
   1264     public void testGoogH264FlexMaxMin()   { maxmin(googH264(),   true /* flex */); }
   1265     public void testGoogH264SurfMaxMin()   { maxmin(googH264(),   false /* flex */); }
   1266     public void testGoogH263FlexMaxMin()   { maxmin(googH263(),   true /* flex */); }
   1267     public void testGoogH263SurfMaxMin()   { maxmin(googH263(),   false /* flex */); }
   1268     public void testGoogMpeg4FlexMaxMin()  { maxmin(googMpeg4(),  true /* flex */); }
   1269     public void testGoogMpeg4SurfMaxMin()  { maxmin(googMpeg4(),  false /* flex */); }
   1270     public void testGoogVP8FlexMaxMin()    { maxmin(googVP8(),    true /* flex */); }
   1271     public void testGoogVP8SurfMaxMin()    { maxmin(googVP8(),    false /* flex */); }
   1272     public void testGoogVP9FlexMaxMin()    { maxmin(googVP9(),    true /* flex */); }
   1273     public void testGoogVP9SurfMaxMin()    { maxmin(googVP9(),    false /* flex */); }
   1274 
   1275     public void testOtherH265FlexMaxMin()  { maxmin(otherH265(),  true /* flex */); }
   1276     public void testOtherH265SurfMaxMin()  { maxmin(otherH265(),  false /* flex */); }
   1277     public void testOtherH264FlexMaxMin()  { maxmin(otherH264(),  true /* flex */); }
   1278     public void testOtherH264SurfMaxMin()  { maxmin(otherH264(),  false /* flex */); }
   1279     public void testOtherH263FlexMaxMin()  { maxmin(otherH263(),  true /* flex */); }
   1280     public void testOtherH263SurfMaxMin()  { maxmin(otherH263(),  false /* flex */); }
   1281     public void testOtherMpeg4FlexMaxMin() { maxmin(otherMpeg4(), true /* flex */); }
   1282     public void testOtherMpeg4SurfMaxMin() { maxmin(otherMpeg4(), false /* flex */); }
   1283     public void testOtherVP8FlexMaxMin()   { maxmin(otherVP8(),   true /* flex */); }
   1284     public void testOtherVP8SurfMaxMin()   { maxmin(otherVP8(),   false /* flex */); }
   1285     public void testOtherVP9FlexMaxMin()   { maxmin(otherVP9(),   true /* flex */); }
   1286     public void testOtherVP9SurfMaxMin()   { maxmin(otherVP9(),   false /* flex */); }
   1287 
   1288     public void testGoogH265FlexMaxMax()   { maxmax(googH265(),   true /* flex */); }
   1289     public void testGoogH265SurfMaxMax()   { maxmax(googH265(),   false /* flex */); }
   1290     public void testGoogH264FlexMaxMax()   { maxmax(googH264(),   true /* flex */); }
   1291     public void testGoogH264SurfMaxMax()   { maxmax(googH264(),   false /* flex */); }
   1292     public void testGoogH263FlexMaxMax()   { maxmax(googH263(),   true /* flex */); }
   1293     public void testGoogH263SurfMaxMax()   { maxmax(googH263(),   false /* flex */); }
   1294     public void testGoogMpeg4FlexMaxMax()  { maxmax(googMpeg4(),  true /* flex */); }
   1295     public void testGoogMpeg4SurfMaxMax()  { maxmax(googMpeg4(),  false /* flex */); }
   1296     public void testGoogVP8FlexMaxMax()    { maxmax(googVP8(),    true /* flex */); }
   1297     public void testGoogVP8SurfMaxMax()    { maxmax(googVP8(),    false /* flex */); }
   1298     public void testGoogVP9FlexMaxMax()    { maxmax(googVP9(),    true /* flex */); }
   1299     public void testGoogVP9SurfMaxMax()    { maxmax(googVP9(),    false /* flex */); }
   1300 
   1301     public void testOtherH265FlexMaxMax()  { maxmax(otherH265(),  true /* flex */); }
   1302     public void testOtherH265SurfMaxMax()  { maxmax(otherH265(),  false /* flex */); }
   1303     public void testOtherH264FlexMaxMax()  { maxmax(otherH264(),  true /* flex */); }
   1304     public void testOtherH264SurfMaxMax()  { maxmax(otherH264(),  false /* flex */); }
   1305     public void testOtherH263FlexMaxMax()  { maxmax(otherH263(),  true /* flex */); }
   1306     public void testOtherH263SurfMaxMax()  { maxmax(otherH263(),  false /* flex */); }
   1307     public void testOtherMpeg4FlexMaxMax() { maxmax(otherMpeg4(), true /* flex */); }
   1308     public void testOtherMpeg4SurfMaxMax() { maxmax(otherMpeg4(), false /* flex */); }
   1309     public void testOtherVP8FlexMaxMax()   { maxmax(otherVP8(),   true /* flex */); }
   1310     public void testOtherVP8SurfMaxMax()   { maxmax(otherVP8(),   false /* flex */); }
   1311     public void testOtherVP9FlexMaxMax()   { maxmax(otherVP9(),   true /* flex */); }
   1312     public void testOtherVP9SurfMaxMax()   { maxmax(otherVP9(),   false /* flex */); }
   1313 
   1314     public void testGoogH265FlexNearMinMin()   { nearminmin(googH265(),   true /* flex */); }
   1315     public void testGoogH265SurfNearMinMin()   { nearminmin(googH265(),   false /* flex */); }
   1316     public void testGoogH264FlexNearMinMin()   { nearminmin(googH264(),   true /* flex */); }
   1317     public void testGoogH264SurfNearMinMin()   { nearminmin(googH264(),   false /* flex */); }
   1318     public void testGoogH263FlexNearMinMin()   { nearminmin(googH263(),   true /* flex */); }
   1319     public void testGoogH263SurfNearMinMin()   { nearminmin(googH263(),   false /* flex */); }
   1320     public void testGoogMpeg4FlexNearMinMin()  { nearminmin(googMpeg4(),  true /* flex */); }
   1321     public void testGoogMpeg4SurfNearMinMin()  { nearminmin(googMpeg4(),  false /* flex */); }
   1322     public void testGoogVP8FlexNearMinMin()    { nearminmin(googVP8(),    true /* flex */); }
   1323     public void testGoogVP8SurfNearMinMin()    { nearminmin(googVP8(),    false /* flex */); }
   1324     public void testGoogVP9FlexNearMinMin()    { nearminmin(googVP9(),    true /* flex */); }
   1325     public void testGoogVP9SurfNearMinMin()    { nearminmin(googVP9(),    false /* flex */); }
   1326 
   1327     public void testOtherH265FlexNearMinMin()  { nearminmin(otherH265(),  true /* flex */); }
   1328     public void testOtherH265SurfNearMinMin()  { nearminmin(otherH265(),  false /* flex */); }
   1329     public void testOtherH264FlexNearMinMin()  { nearminmin(otherH264(),  true /* flex */); }
   1330     public void testOtherH264SurfNearMinMin()  { nearminmin(otherH264(),  false /* flex */); }
   1331     public void testOtherH263FlexNearMinMin()  { nearminmin(otherH263(),  true /* flex */); }
   1332     public void testOtherH263SurfNearMinMin()  { nearminmin(otherH263(),  false /* flex */); }
   1333     public void testOtherMpeg4FlexNearMinMin() { nearminmin(otherMpeg4(), true /* flex */); }
   1334     public void testOtherMpeg4SurfNearMinMin() { nearminmin(otherMpeg4(), false /* flex */); }
   1335     public void testOtherVP8FlexNearMinMin()   { nearminmin(otherVP8(),   true /* flex */); }
   1336     public void testOtherVP8SurfNearMinMin()   { nearminmin(otherVP8(),   false /* flex */); }
   1337     public void testOtherVP9FlexNearMinMin()   { nearminmin(otherVP9(),   true /* flex */); }
   1338     public void testOtherVP9SurfNearMinMin()   { nearminmin(otherVP9(),   false /* flex */); }
   1339 
   1340     public void testGoogH265FlexNearMinMax()   { nearminmax(googH265(),   true /* flex */); }
   1341     public void testGoogH265SurfNearMinMax()   { nearminmax(googH265(),   false /* flex */); }
   1342     public void testGoogH264FlexNearMinMax()   { nearminmax(googH264(),   true /* flex */); }
   1343     public void testGoogH264SurfNearMinMax()   { nearminmax(googH264(),   false /* flex */); }
   1344     public void testGoogH263FlexNearMinMax()   { nearminmax(googH263(),   true /* flex */); }
   1345     public void testGoogH263SurfNearMinMax()   { nearminmax(googH263(),   false /* flex */); }
   1346     public void testGoogMpeg4FlexNearMinMax()  { nearminmax(googMpeg4(),  true /* flex */); }
   1347     public void testGoogMpeg4SurfNearMinMax()  { nearminmax(googMpeg4(),  false /* flex */); }
   1348     public void testGoogVP8FlexNearMinMax()    { nearminmax(googVP8(),    true /* flex */); }
   1349     public void testGoogVP8SurfNearMinMax()    { nearminmax(googVP8(),    false /* flex */); }
   1350     public void testGoogVP9FlexNearMinMax()    { nearminmax(googVP9(),    true /* flex */); }
   1351     public void testGoogVP9SurfNearMinMax()    { nearminmax(googVP9(),    false /* flex */); }
   1352 
   1353     public void testOtherH265FlexNearMinMax()  { nearminmax(otherH265(),  true /* flex */); }
   1354     public void testOtherH265SurfNearMinMax()  { nearminmax(otherH265(),  false /* flex */); }
   1355     public void testOtherH264FlexNearMinMax()  { nearminmax(otherH264(),  true /* flex */); }
   1356     public void testOtherH264SurfNearMinMax()  { nearminmax(otherH264(),  false /* flex */); }
   1357     public void testOtherH263FlexNearMinMax()  { nearminmax(otherH263(),  true /* flex */); }
   1358     public void testOtherH263SurfNearMinMax()  { nearminmax(otherH263(),  false /* flex */); }
   1359     public void testOtherMpeg4FlexNearMinMax() { nearminmax(otherMpeg4(), true /* flex */); }
   1360     public void testOtherMpeg4SurfNearMinMax() { nearminmax(otherMpeg4(), false /* flex */); }
   1361     public void testOtherVP8FlexNearMinMax()   { nearminmax(otherVP8(),   true /* flex */); }
   1362     public void testOtherVP8SurfNearMinMax()   { nearminmax(otherVP8(),   false /* flex */); }
   1363     public void testOtherVP9FlexNearMinMax()   { nearminmax(otherVP9(),   true /* flex */); }
   1364     public void testOtherVP9SurfNearMinMax()   { nearminmax(otherVP9(),   false /* flex */); }
   1365 
   1366     public void testGoogH265FlexNearMaxMin()   { nearmaxmin(googH265(),   true /* flex */); }
   1367     public void testGoogH265SurfNearMaxMin()   { nearmaxmin(googH265(),   false /* flex */); }
   1368     public void testGoogH264FlexNearMaxMin()   { nearmaxmin(googH264(),   true /* flex */); }
   1369     public void testGoogH264SurfNearMaxMin()   { nearmaxmin(googH264(),   false /* flex */); }
   1370     public void testGoogH263FlexNearMaxMin()   { nearmaxmin(googH263(),   true /* flex */); }
   1371     public void testGoogH263SurfNearMaxMin()   { nearmaxmin(googH263(),   false /* flex */); }
   1372     public void testGoogMpeg4FlexNearMaxMin()  { nearmaxmin(googMpeg4(),  true /* flex */); }
   1373     public void testGoogMpeg4SurfNearMaxMin()  { nearmaxmin(googMpeg4(),  false /* flex */); }
   1374     public void testGoogVP8FlexNearMaxMin()    { nearmaxmin(googVP8(),    true /* flex */); }
   1375     public void testGoogVP8SurfNearMaxMin()    { nearmaxmin(googVP8(),    false /* flex */); }
   1376     public void testGoogVP9FlexNearMaxMin()    { nearmaxmin(googVP9(),    true /* flex */); }
   1377     public void testGoogVP9SurfNearMaxMin()    { nearmaxmin(googVP9(),    false /* flex */); }
   1378 
   1379     public void testOtherH265FlexNearMaxMin()  { nearmaxmin(otherH265(),  true /* flex */); }
   1380     public void testOtherH265SurfNearMaxMin()  { nearmaxmin(otherH265(),  false /* flex */); }
   1381     public void testOtherH264FlexNearMaxMin()  { nearmaxmin(otherH264(),  true /* flex */); }
   1382     public void testOtherH264SurfNearMaxMin()  { nearmaxmin(otherH264(),  false /* flex */); }
   1383     public void testOtherH263FlexNearMaxMin()  { nearmaxmin(otherH263(),  true /* flex */); }
   1384     public void testOtherH263SurfNearMaxMin()  { nearmaxmin(otherH263(),  false /* flex */); }
   1385     public void testOtherMpeg4FlexNearMaxMin() { nearmaxmin(otherMpeg4(), true /* flex */); }
   1386     public void testOtherMpeg4SurfNearMaxMin() { nearmaxmin(otherMpeg4(), false /* flex */); }
   1387     public void testOtherVP8FlexNearMaxMin()   { nearmaxmin(otherVP8(),   true /* flex */); }
   1388     public void testOtherVP8SurfNearMaxMin()   { nearmaxmin(otherVP8(),   false /* flex */); }
   1389     public void testOtherVP9FlexNearMaxMin()   { nearmaxmin(otherVP9(),   true /* flex */); }
   1390     public void testOtherVP9SurfNearMaxMin()   { nearmaxmin(otherVP9(),   false /* flex */); }
   1391 
   1392     public void testGoogH265FlexNearMaxMax()   { nearmaxmax(googH265(),   true /* flex */); }
   1393     public void testGoogH265SurfNearMaxMax()   { nearmaxmax(googH265(),   false /* flex */); }
   1394     public void testGoogH264FlexNearMaxMax()   { nearmaxmax(googH264(),   true /* flex */); }
   1395     public void testGoogH264SurfNearMaxMax()   { nearmaxmax(googH264(),   false /* flex */); }
   1396     public void testGoogH263FlexNearMaxMax()   { nearmaxmax(googH263(),   true /* flex */); }
   1397     public void testGoogH263SurfNearMaxMax()   { nearmaxmax(googH263(),   false /* flex */); }
   1398     public void testGoogMpeg4FlexNearMaxMax()  { nearmaxmax(googMpeg4(),  true /* flex */); }
   1399     public void testGoogMpeg4SurfNearMaxMax()  { nearmaxmax(googMpeg4(),  false /* flex */); }
   1400     public void testGoogVP8FlexNearMaxMax()    { nearmaxmax(googVP8(),    true /* flex */); }
   1401     public void testGoogVP8SurfNearMaxMax()    { nearmaxmax(googVP8(),    false /* flex */); }
   1402     public void testGoogVP9FlexNearMaxMax()    { nearmaxmax(googVP9(),    true /* flex */); }
   1403     public void testGoogVP9SurfNearMaxMax()    { nearmaxmax(googVP9(),    false /* flex */); }
   1404 
   1405     public void testOtherH265FlexNearMaxMax()  { nearmaxmax(otherH265(),  true /* flex */); }
   1406     public void testOtherH265SurfNearMaxMax()  { nearmaxmax(otherH265(),  false /* flex */); }
   1407     public void testOtherH264FlexNearMaxMax()  { nearmaxmax(otherH264(),  true /* flex */); }
   1408     public void testOtherH264SurfNearMaxMax()  { nearmaxmax(otherH264(),  false /* flex */); }
   1409     public void testOtherH263FlexNearMaxMax()  { nearmaxmax(otherH263(),  true /* flex */); }
   1410     public void testOtherH263SurfNearMaxMax()  { nearmaxmax(otherH263(),  false /* flex */); }
   1411     public void testOtherMpeg4FlexNearMaxMax() { nearmaxmax(otherMpeg4(), true /* flex */); }
   1412     public void testOtherMpeg4SurfNearMaxMax() { nearmaxmax(otherMpeg4(), false /* flex */); }
   1413     public void testOtherVP8FlexNearMaxMax()   { nearmaxmax(otherVP8(),   true /* flex */); }
   1414     public void testOtherVP8SurfNearMaxMax()   { nearmaxmax(otherVP8(),   false /* flex */); }
   1415     public void testOtherVP9FlexNearMaxMax()   { nearmaxmax(otherVP9(),   true /* flex */); }
   1416     public void testOtherVP9SurfNearMaxMax()   { nearmaxmax(otherVP9(),   false /* flex */); }
   1417 
   1418     public void testGoogH265FlexArbitraryW()   { arbitraryw(googH265(),   true /* flex */); }
   1419     public void testGoogH265SurfArbitraryW()   { arbitraryw(googH265(),   false /* flex */); }
   1420     public void testGoogH264FlexArbitraryW()   { arbitraryw(googH264(),   true /* flex */); }
   1421     public void testGoogH264SurfArbitraryW()   { arbitraryw(googH264(),   false /* flex */); }
   1422     public void testGoogH263FlexArbitraryW()   { arbitraryw(googH263(),   true /* flex */); }
   1423     public void testGoogH263SurfArbitraryW()   { arbitraryw(googH263(),   false /* flex */); }
   1424     public void testGoogMpeg4FlexArbitraryW()  { arbitraryw(googMpeg4(),  true /* flex */); }
   1425     public void testGoogMpeg4SurfArbitraryW()  { arbitraryw(googMpeg4(),  false /* flex */); }
   1426     public void testGoogVP8FlexArbitraryW()    { arbitraryw(googVP8(),    true /* flex */); }
   1427     public void testGoogVP8SurfArbitraryW()    { arbitraryw(googVP8(),    false /* flex */); }
   1428     public void testGoogVP9FlexArbitraryW()    { arbitraryw(googVP9(),    true /* flex */); }
   1429     public void testGoogVP9SurfArbitraryW()    { arbitraryw(googVP9(),    false /* flex */); }
   1430 
   1431     public void testOtherH265FlexArbitraryW()  { arbitraryw(otherH265(),  true /* flex */); }
   1432     public void testOtherH265SurfArbitraryW()  { arbitraryw(otherH265(),  false /* flex */); }
   1433     public void testOtherH264FlexArbitraryW()  { arbitraryw(otherH264(),  true /* flex */); }
   1434     public void testOtherH264SurfArbitraryW()  { arbitraryw(otherH264(),  false /* flex */); }
   1435     public void testOtherH263FlexArbitraryW()  { arbitraryw(otherH263(),  true /* flex */); }
   1436     public void testOtherH263SurfArbitraryW()  { arbitraryw(otherH263(),  false /* flex */); }
   1437     public void testOtherMpeg4FlexArbitraryW() { arbitraryw(otherMpeg4(), true /* flex */); }
   1438     public void testOtherMpeg4SurfArbitraryW() { arbitraryw(otherMpeg4(), false /* flex */); }
   1439     public void testOtherVP8FlexArbitraryW()   { arbitraryw(otherVP8(),   true /* flex */); }
   1440     public void testOtherVP8SurfArbitraryW()   { arbitraryw(otherVP8(),   false /* flex */); }
   1441     public void testOtherVP9FlexArbitraryW()   { arbitraryw(otherVP9(),   true /* flex */); }
   1442     public void testOtherVP9SurfArbitraryW()   { arbitraryw(otherVP9(),   false /* flex */); }
   1443 
   1444     public void testGoogH265FlexArbitraryH()   { arbitraryh(googH265(),   true /* flex */); }
   1445     public void testGoogH265SurfArbitraryH()   { arbitraryh(googH265(),   false /* flex */); }
   1446     public void testGoogH264FlexArbitraryH()   { arbitraryh(googH264(),   true /* flex */); }
   1447     public void testGoogH264SurfArbitraryH()   { arbitraryh(googH264(),   false /* flex */); }
   1448     public void testGoogH263FlexArbitraryH()   { arbitraryh(googH263(),   true /* flex */); }
   1449     public void testGoogH263SurfArbitraryH()   { arbitraryh(googH263(),   false /* flex */); }
   1450     public void testGoogMpeg4FlexArbitraryH()  { arbitraryh(googMpeg4(),  true /* flex */); }
   1451     public void testGoogMpeg4SurfArbitraryH()  { arbitraryh(googMpeg4(),  false /* flex */); }
   1452     public void testGoogVP8FlexArbitraryH()    { arbitraryh(googVP8(),    true /* flex */); }
   1453     public void testGoogVP8SurfArbitraryH()    { arbitraryh(googVP8(),    false /* flex */); }
   1454     public void testGoogVP9FlexArbitraryH()    { arbitraryh(googVP9(),    true /* flex */); }
   1455     public void testGoogVP9SurfArbitraryH()    { arbitraryh(googVP9(),    false /* flex */); }
   1456 
   1457     public void testOtherH265FlexArbitraryH()  { arbitraryh(otherH265(),  true /* flex */); }
   1458     public void testOtherH265SurfArbitraryH()  { arbitraryh(otherH265(),  false /* flex */); }
   1459     public void testOtherH264FlexArbitraryH()  { arbitraryh(otherH264(),  true /* flex */); }
   1460     public void testOtherH264SurfArbitraryH()  { arbitraryh(otherH264(),  false /* flex */); }
   1461     public void testOtherH263FlexArbitraryH()  { arbitraryh(otherH263(),  true /* flex */); }
   1462     public void testOtherH263SurfArbitraryH()  { arbitraryh(otherH263(),  false /* flex */); }
   1463     public void testOtherMpeg4FlexArbitraryH() { arbitraryh(otherMpeg4(), true /* flex */); }
   1464     public void testOtherMpeg4SurfArbitraryH() { arbitraryh(otherMpeg4(), false /* flex */); }
   1465     public void testOtherVP8FlexArbitraryH()   { arbitraryh(otherVP8(),   true /* flex */); }
   1466     public void testOtherVP8SurfArbitraryH()   { arbitraryh(otherVP8(),   false /* flex */); }
   1467     public void testOtherVP9FlexArbitraryH()   { arbitraryh(otherVP9(),   true /* flex */); }
   1468     public void testOtherVP9SurfArbitraryH()   { arbitraryh(otherVP9(),   false /* flex */); }
   1469 
   1470     public void testGoogH265FlexQCIF()   { specific(googH265(),   176, 144, true /* flex */); }
   1471     public void testGoogH265SurfQCIF()   { specific(googH265(),   176, 144, false /* flex */); }
   1472     public void testGoogH264FlexQCIF()   { specific(googH264(),   176, 144, true /* flex */); }
   1473     public void testGoogH264SurfQCIF()   { specific(googH264(),   176, 144, false /* flex */); }
   1474     public void testGoogH263FlexQCIF()   { specific(googH263(),   176, 144, true /* flex */); }
   1475     public void testGoogH263SurfQCIF()   { specific(googH263(),   176, 144, false /* flex */); }
   1476     public void testGoogMpeg4FlexQCIF()  { specific(googMpeg4(),  176, 144, true /* flex */); }
   1477     public void testGoogMpeg4SurfQCIF()  { specific(googMpeg4(),  176, 144, false /* flex */); }
   1478     public void testGoogVP8FlexQCIF()    { specific(googVP8(),    176, 144, true /* flex */); }
   1479     public void testGoogVP8SurfQCIF()    { specific(googVP8(),    176, 144, false /* flex */); }
   1480     public void testGoogVP9FlexQCIF()    { specific(googVP9(),    176, 144, true /* flex */); }
   1481     public void testGoogVP9SurfQCIF()    { specific(googVP9(),    176, 144, false /* flex */); }
   1482 
   1483     public void testOtherH265FlexQCIF()  { specific(otherH265(),  176, 144, true /* flex */); }
   1484     public void testOtherH265SurfQCIF()  { specific(otherH265(),  176, 144, false /* flex */); }
   1485     public void testOtherH264FlexQCIF()  { specific(otherH264(),  176, 144, true /* flex */); }
   1486     public void testOtherH264SurfQCIF()  { specific(otherH264(),  176, 144, false /* flex */); }
   1487     public void testOtherH263FlexQCIF()  { specific(otherH263(),  176, 144, true /* flex */); }
   1488     public void testOtherH263SurfQCIF()  { specific(otherH263(),  176, 144, false /* flex */); }
   1489     public void testOtherMpeg4FlexQCIF() { specific(otherMpeg4(), 176, 144, true /* flex */); }
   1490     public void testOtherMpeg4SurfQCIF() { specific(otherMpeg4(), 176, 144, false /* flex */); }
   1491     public void testOtherVP8FlexQCIF()   { specific(otherVP8(),   176, 144, true /* flex */); }
   1492     public void testOtherVP8SurfQCIF()   { specific(otherVP8(),   176, 144, false /* flex */); }
   1493     public void testOtherVP9FlexQCIF()   { specific(otherVP9(),   176, 144, true /* flex */); }
   1494     public void testOtherVP9SurfQCIF()   { specific(otherVP9(),   176, 144, false /* flex */); }
   1495 
   1496     public void testGoogH265Flex480p()   { specific(googH265(),   720, 480, true /* flex */); }
   1497     public void testGoogH265Surf480p()   { specific(googH265(),   720, 480, false /* flex */); }
   1498     public void testGoogH264Flex480p()   { specific(googH264(),   720, 480, true /* flex */); }
   1499     public void testGoogH264Surf480p()   { specific(googH264(),   720, 480, false /* flex */); }
   1500     public void testGoogH263Flex480p()   { specific(googH263(),   720, 480, true /* flex */); }
   1501     public void testGoogH263Surf480p()   { specific(googH263(),   720, 480, false /* flex */); }
   1502     public void testGoogMpeg4Flex480p()  { specific(googMpeg4(),  720, 480, true /* flex */); }
   1503     public void testGoogMpeg4Surf480p()  { specific(googMpeg4(),  720, 480, false /* flex */); }
   1504     public void testGoogVP8Flex480p()    { specific(googVP8(),    720, 480, true /* flex */); }
   1505     public void testGoogVP8Surf480p()    { specific(googVP8(),    720, 480, false /* flex */); }
   1506     public void testGoogVP9Flex480p()    { specific(googVP9(),    720, 480, true /* flex */); }
   1507     public void testGoogVP9Surf480p()    { specific(googVP9(),    720, 480, false /* flex */); }
   1508 
   1509     public void testOtherH265Flex480p()  { specific(otherH265(),  720, 480, true /* flex */); }
   1510     public void testOtherH265Surf480p()  { specific(otherH265(),  720, 480, false /* flex */); }
   1511     public void testOtherH264Flex480p()  { specific(otherH264(),  720, 480, true /* flex */); }
   1512     public void testOtherH264Surf480p()  { specific(otherH264(),  720, 480, false /* flex */); }
   1513     public void testOtherH263Flex480p()  { specific(otherH263(),  720, 480, true /* flex */); }
   1514     public void testOtherH263Surf480p()  { specific(otherH263(),  720, 480, false /* flex */); }
   1515     public void testOtherMpeg4Flex480p() { specific(otherMpeg4(), 720, 480, true /* flex */); }
   1516     public void testOtherMpeg4Surf480p() { specific(otherMpeg4(), 720, 480, false /* flex */); }
   1517     public void testOtherVP8Flex480p()   { specific(otherVP8(),   720, 480, true /* flex */); }
   1518     public void testOtherVP8Surf480p()   { specific(otherVP8(),   720, 480, false /* flex */); }
   1519     public void testOtherVP9Flex480p()   { specific(otherVP9(),   720, 480, true /* flex */); }
   1520     public void testOtherVP9Surf480p()   { specific(otherVP9(),   720, 480, false /* flex */); }
   1521 
   1522     // even though H.263 and MPEG-4 are not defined for 720p or 1080p
   1523     // test for it, in case device claims support for it.
   1524 
   1525     public void testGoogH265Flex720p()   { specific(googH265(),   1280, 720, true /* flex */); }
   1526     public void testGoogH265Surf720p()   { specific(googH265(),   1280, 720, false /* flex */); }
   1527     public void testGoogH264Flex720p()   { specific(googH264(),   1280, 720, true /* flex */); }
   1528     public void testGoogH264Surf720p()   { specific(googH264(),   1280, 720, false /* flex */); }
   1529     public void testGoogH263Flex720p()   { specific(googH263(),   1280, 720, true /* flex */); }
   1530     public void testGoogH263Surf720p()   { specific(googH263(),   1280, 720, false /* flex */); }
   1531     public void testGoogMpeg4Flex720p()  { specific(googMpeg4(),  1280, 720, true /* flex */); }
   1532     public void testGoogMpeg4Surf720p()  { specific(googMpeg4(),  1280, 720, false /* flex */); }
   1533     public void testGoogVP8Flex720p()    { specific(googVP8(),    1280, 720, true /* flex */); }
   1534     public void testGoogVP8Surf720p()    { specific(googVP8(),    1280, 720, false /* flex */); }
   1535     public void testGoogVP9Flex720p()    { specific(googVP9(),    1280, 720, true /* flex */); }
   1536     public void testGoogVP9Surf720p()    { specific(googVP9(),    1280, 720, false /* flex */); }
   1537 
   1538     public void testOtherH265Flex720p()  { specific(otherH265(),  1280, 720, true /* flex */); }
   1539     public void testOtherH265Surf720p()  { specific(otherH265(),  1280, 720, false /* flex */); }
   1540     public void testOtherH264Flex720p()  { specific(otherH264(),  1280, 720, true /* flex */); }
   1541     public void testOtherH264Surf720p()  { specific(otherH264(),  1280, 720, false /* flex */); }
   1542     public void testOtherH263Flex720p()  { specific(otherH263(),  1280, 720, true /* flex */); }
   1543     public void testOtherH263Surf720p()  { specific(otherH263(),  1280, 720, false /* flex */); }
   1544     public void testOtherMpeg4Flex720p() { specific(otherMpeg4(), 1280, 720, true /* flex */); }
   1545     public void testOtherMpeg4Surf720p() { specific(otherMpeg4(), 1280, 720, false /* flex */); }
   1546     public void testOtherVP8Flex720p()   { specific(otherVP8(),   1280, 720, true /* flex */); }
   1547     public void testOtherVP8Surf720p()   { specific(otherVP8(),   1280, 720, false /* flex */); }
   1548     public void testOtherVP9Flex720p()   { specific(otherVP9(),   1280, 720, true /* flex */); }
   1549     public void testOtherVP9Surf720p()   { specific(otherVP9(),   1280, 720, false /* flex */); }
   1550 
   1551     public void testGoogH265Flex1080p()   { specific(googH265(),   1920, 1080, true /* flex */); }
   1552     public void testGoogH265Surf1080p()   { specific(googH265(),   1920, 1080, false /* flex */); }
   1553     public void testGoogH264Flex1080p()   { specific(googH264(),   1920, 1080, true /* flex */); }
   1554     public void testGoogH264Surf1080p()   { specific(googH264(),   1920, 1080, false /* flex */); }
   1555     public void testGoogH263Flex1080p()   { specific(googH263(),   1920, 1080, true /* flex */); }
   1556     public void testGoogH263Surf1080p()   { specific(googH263(),   1920, 1080, false /* flex */); }
   1557     public void testGoogMpeg4Flex1080p()  { specific(googMpeg4(),  1920, 1080, true /* flex */); }
   1558     public void testGoogMpeg4Surf1080p()  { specific(googMpeg4(),  1920, 1080, false /* flex */); }
   1559     public void testGoogVP8Flex1080p()    { specific(googVP8(),    1920, 1080, true /* flex */); }
   1560     public void testGoogVP8Surf1080p()    { specific(googVP8(),    1920, 1080, false /* flex */); }
   1561     public void testGoogVP9Flex1080p()    { specific(googVP9(),    1920, 1080, true /* flex */); }
   1562     public void testGoogVP9Surf1080p()    { specific(googVP9(),    1920, 1080, false /* flex */); }
   1563 
   1564     public void testOtherH265Flex1080p()  { specific(otherH265(),  1920, 1080, true /* flex */); }
   1565     public void testOtherH265Surf1080p()  { specific(otherH265(),  1920, 1080, false /* flex */); }
   1566     public void testOtherH264Flex1080p()  { specific(otherH264(),  1920, 1080, true /* flex */); }
   1567     public void testOtherH264Surf1080p()  { specific(otherH264(),  1920, 1080, false /* flex */); }
   1568     public void testOtherH263Flex1080p()  { specific(otherH263(),  1920, 1080, true /* flex */); }
   1569     public void testOtherH263Surf1080p()  { specific(otherH263(),  1920, 1080, false /* flex */); }
   1570     public void testOtherMpeg4Flex1080p() { specific(otherMpeg4(), 1920, 1080, true /* flex */); }
   1571     public void testOtherMpeg4Surf1080p() { specific(otherMpeg4(), 1920, 1080, false /* flex */); }
   1572     public void testOtherVP8Flex1080p()   { specific(otherVP8(),   1920, 1080, true /* flex */); }
   1573     public void testOtherVP8Surf1080p()   { specific(otherVP8(),   1920, 1080, false /* flex */); }
   1574     public void testOtherVP9Flex1080p()   { specific(otherVP9(),   1920, 1080, true /* flex */); }
   1575     public void testOtherVP9Surf1080p()   { specific(otherVP9(),   1920, 1080, false /* flex */); }
   1576 
   1577     public void testGoogH265Flex360pWithIntraRefresh() {
   1578         intraRefresh(googH265(), 480, 360);
   1579     }
   1580 
   1581     public void testGoogH264Flex360pWithIntraRefresh() {
   1582         intraRefresh(googH264(), 480, 360);
   1583     }
   1584 
   1585     public void testGoogH263Flex360pWithIntraRefresh() {
   1586         intraRefresh(googH263(), 480, 360);
   1587     }
   1588 
   1589     public void testGoogMpeg4Flex360pWithIntraRefresh() {
   1590         intraRefresh(googMpeg4(), 480, 360);
   1591     }
   1592 
   1593     public void testGoogVP8Flex360pWithIntraRefresh() {
   1594         intraRefresh(googVP8(), 480, 360);
   1595     }
   1596 
   1597     public void testOtherH265Flex360pWithIntraRefresh() {
   1598         intraRefresh(otherH265(), 480, 360);
   1599     }
   1600 
   1601     public void testOtherH264Flex360pWithIntraRefresh() {
   1602         intraRefresh(otherH264(), 480, 360);
   1603     }
   1604 
   1605     public void testOtherH263FlexQCIFWithIntraRefresh() {
   1606         intraRefresh(otherH263(), 176, 120);
   1607     }
   1608 
   1609     public void testOtherMpeg4Flex360pWithIntraRefresh() {
   1610         intraRefresh(otherMpeg4(), 480, 360);
   1611     }
   1612 
   1613     public void testOtherVP8Flex360pWithIntraRefresh() {
   1614         intraRefresh(otherVP8(), 480, 360);
   1615     }
   1616 
   1617     // Tests encoder profiles required by CDD.
   1618     // H264
   1619     public void testH264LowQualitySDSupport()   {
   1620         support(h264(), 320, 240, 20, 384 * 1000);
   1621     }
   1622 
   1623     public void testH264HighQualitySDSupport()   {
   1624         support(h264(), 720, 480, 30, 2 * 1000000);
   1625     }
   1626 
   1627     public void testH264FlexQVGA20fps384kbps()   {
   1628         detailed(h264(), 320, 240, 20, 384 * 1000, true /* flex */);
   1629     }
   1630 
   1631     public void testH264SurfQVGA20fps384kbps()   {
   1632         detailed(h264(), 320, 240, 20, 384 * 1000, false /* flex */);
   1633     }
   1634 
   1635     public void testH264Flex480p30fps2Mbps()   {
   1636         detailed(h264(), 720, 480, 30, 2 * 1000000, true /* flex */);
   1637     }
   1638 
   1639     public void testH264Surf480p30fps2Mbps()   {
   1640         detailed(h264(), 720, 480, 30, 2 * 1000000, false /* flex */);
   1641     }
   1642 
   1643     public void testH264Flex720p30fps4Mbps()   {
   1644         detailed(h264(), 1280, 720, 30, 4 * 1000000, true /* flex */);
   1645     }
   1646 
   1647     public void testH264Surf720p30fps4Mbps()   {
   1648         detailed(h264(), 1280, 720, 30, 4 * 1000000, false /* flex */);
   1649     }
   1650 
   1651     public void testH264Flex1080p30fps10Mbps()   {
   1652         detailed(h264(), 1920, 1080, 30, 10 * 1000000, true /* flex */);
   1653     }
   1654 
   1655     public void testH264Surf1080p30fps10Mbps()   {
   1656         detailed(h264(), 1920, 1080, 30, 10 * 1000000, false /* flex */);
   1657     }
   1658 
   1659     // VP8
   1660     public void testVP8LowQualitySDSupport()   {
   1661         support(vp8(), 320, 180, 30, 800 * 1000);
   1662     }
   1663 
   1664     public void testVP8HighQualitySDSupport()   {
   1665         support(vp8(), 640, 360, 30, 2 * 1000000);
   1666     }
   1667 
   1668     public void testVP8Flex180p30fps800kbps()   {
   1669         detailed(vp8(), 320, 180, 30, 800 * 1000, true /* flex */);
   1670     }
   1671 
   1672     public void testVP8Surf180p30fps800kbps()   {
   1673         detailed(vp8(), 320, 180, 30, 800 * 1000, false /* flex */);
   1674     }
   1675 
   1676     public void testVP8Flex360p30fps2Mbps()   {
   1677         detailed(vp8(), 640, 360, 30, 2 * 1000000, true /* flex */);
   1678     }
   1679 
   1680     public void testVP8Surf360p30fps2Mbps()   {
   1681         detailed(vp8(), 640, 360, 30, 2 * 1000000, false /* flex */);
   1682     }
   1683 
   1684     public void testVP8Flex720p30fps4Mbps()   {
   1685         detailed(vp8(), 1280, 720, 30, 4 * 1000000, true /* flex */);
   1686     }
   1687 
   1688     public void testVP8Surf720p30fps4Mbps()   {
   1689         detailed(vp8(), 1280, 720, 30, 4 * 1000000, false /* flex */);
   1690     }
   1691 
   1692     public void testVP8Flex1080p30fps10Mbps()   {
   1693         detailed(vp8(), 1920, 1080, 30, 10 * 1000000, true /* flex */);
   1694     }
   1695 
   1696     public void testVP8Surf1080p30fps10Mbps()   {
   1697         detailed(vp8(), 1920, 1080, 30, 10 * 1000000, false /* flex */);
   1698     }
   1699 
   1700     private void minmin(Encoder[] encoders, boolean flexYUV) {
   1701         extreme(encoders, 0 /* x */, 0 /* y */, flexYUV, false /* near */);
   1702     }
   1703 
   1704     private void minmax(Encoder[] encoders, boolean flexYUV) {
   1705         extreme(encoders, 0 /* x */, 1 /* y */, flexYUV, false /* near */);
   1706     }
   1707 
   1708     private void maxmin(Encoder[] encoders, boolean flexYUV) {
   1709         extreme(encoders, 1 /* x */, 0 /* y */, flexYUV, false /* near */);
   1710     }
   1711 
   1712     private void maxmax(Encoder[] encoders, boolean flexYUV) {
   1713         extreme(encoders, 1 /* x */, 1 /* y */, flexYUV, false /* near */);
   1714     }
   1715 
   1716     private void nearminmin(Encoder[] encoders, boolean flexYUV) {
   1717         extreme(encoders, 0 /* x */, 0 /* y */, flexYUV, true /* near */);
   1718     }
   1719 
   1720     private void nearminmax(Encoder[] encoders, boolean flexYUV) {
   1721         extreme(encoders, 0 /* x */, 1 /* y */, flexYUV, true /* near */);
   1722     }
   1723 
   1724     private void nearmaxmin(Encoder[] encoders, boolean flexYUV) {
   1725         extreme(encoders, 1 /* x */, 0 /* y */, flexYUV, true /* near */);
   1726     }
   1727 
   1728     private void nearmaxmax(Encoder[] encoders, boolean flexYUV) {
   1729         extreme(encoders, 1 /* x */, 1 /* y */, flexYUV, true /* near */);
   1730     }
   1731 
   1732     private void extreme(Encoder[] encoders, int x, int y, boolean flexYUV, boolean near) {
   1733         boolean skipped = true;
   1734         if (encoders.length == 0) {
   1735             MediaUtils.skipTest("no such encoder present");
   1736             return;
   1737         }
   1738         for (Encoder encoder: encoders) {
   1739             if (encoder.testExtreme(x, y, flexYUV, near)) {
   1740                 skipped = false;
   1741             }
   1742         }
   1743         if (skipped) {
   1744             MediaUtils.skipTest("duplicate resolution extreme");
   1745         }
   1746     }
   1747 
   1748     private void arbitrary(Encoder[] encoders, boolean flexYUV, boolean widths) {
   1749         boolean skipped = true;
   1750         if (encoders.length == 0) {
   1751             MediaUtils.skipTest("no such encoder present");
   1752             return;
   1753         }
   1754         for (Encoder encoder: encoders) {
   1755             if (encoder.testArbitrary(flexYUV, widths)) {
   1756                 skipped = false;
   1757             }
   1758         }
   1759         if (skipped) {
   1760             MediaUtils.skipTest("duplicate resolution");
   1761         }
   1762     }
   1763 
   1764     private void arbitraryw(Encoder[] encoders, boolean flexYUV) {
   1765         arbitrary(encoders, flexYUV, true /* widths */);
   1766     }
   1767 
   1768     private void arbitraryh(Encoder[] encoders, boolean flexYUV) {
   1769         arbitrary(encoders, flexYUV, false /* widths */);
   1770     }
   1771 
   1772     /* test specific size */
   1773     private void specific(Encoder[] encoders, int width, int height, boolean flexYUV) {
   1774         boolean skipped = true;
   1775         if (encoders.length == 0) {
   1776             MediaUtils.skipTest("no such encoder present");
   1777             return;
   1778         }
   1779         for (Encoder encoder : encoders) {
   1780             if (encoder.testSpecific(width, height, flexYUV)) {
   1781                 skipped = false;
   1782             }
   1783         }
   1784         if (skipped) {
   1785             MediaUtils.skipTest("duplicate or unsupported resolution");
   1786         }
   1787     }
   1788 
   1789     /* test intra refresh with flexYUV */
   1790     private void intraRefresh(Encoder[] encoders, int width, int height) {
   1791         boolean skipped = true;
   1792         if (encoders.length == 0) {
   1793             MediaUtils.skipTest("no such encoder present");
   1794             return;
   1795         }
   1796         for (Encoder encoder : encoders) {
   1797             if (encoder.testIntraRefresh(width, height)) {
   1798                 skipped = false;
   1799             }
   1800         }
   1801         if (skipped) {
   1802             MediaUtils.skipTest("intra-refresh unsupported");
   1803         }
   1804     }
   1805 
   1806     /* test size, frame rate and bit rate */
   1807     private void detailed(
   1808             Encoder[] encoders, int width, int height, int frameRate, int bitRate,
   1809             boolean flexYUV) {
   1810         if (encoders.length == 0) {
   1811             MediaUtils.skipTest("no such encoder present");
   1812             return;
   1813         }
   1814         boolean skipped = true;
   1815         for (Encoder encoder : encoders) {
   1816             if (encoder.testSupport(width, height, frameRate, bitRate)) {
   1817                 skipped = false;
   1818                 encoder.testDetailed(width, height, frameRate, bitRate, flexYUV);
   1819             }
   1820         }
   1821         if (skipped) {
   1822             MediaUtils.skipTest("unsupported resolution and rate");
   1823         }
   1824     }
   1825 
   1826     /* test size and rate are supported */
   1827     private void support(Encoder[] encoders, int width, int height, int frameRate, int bitRate) {
   1828         boolean supported = false;
   1829         if (encoders.length == 0) {
   1830             MediaUtils.skipTest("no such encoder present");
   1831             return;
   1832         }
   1833         for (Encoder encoder : encoders) {
   1834             if (encoder.testSupport(width, height, frameRate, bitRate)) {
   1835                 supported = true;
   1836                 break;
   1837             }
   1838         }
   1839         if (!supported) {
   1840             fail("unsupported format " + width + "x" + height + " " +
   1841                     frameRate + "fps " + bitRate + "bps");
   1842         }
   1843     }
   1844 }
   1845