1 /* 2 * Copyright 2015 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.misc.cts; 18 19 import android.app.Activity; 20 import android.media.MediaCodec; 21 import android.media.MediaCodecInfo; 22 import android.media.MediaCodecInfo.CodecCapabilities; 23 import android.media.MediaCodecInfo.VideoCapabilities; 24 import android.media.MediaCodecList; 25 import android.media.MediaFormat; 26 import android.os.Bundle; 27 import android.util.Log; 28 29 import java.io.IOException; 30 import java.util.Vector; 31 32 public class ResourceManagerTestActivityBase extends Activity { 33 public static final int TYPE_NONSECURE = 0; 34 public static final int TYPE_SECURE = 1; 35 public static final int TYPE_MIX = 2; 36 private static final int FRAME_RATE = 10; 37 // 10 seconds between I-frames 38 private static final int IFRAME_INTERVAL = 10; 39 protected static final int MAX_INSTANCES = 32; 40 41 private int mWidth = 0; 42 private int mHeight = 0; 43 protected String TAG; 44 private String mMime = MediaFormat.MIMETYPE_VIDEO_AVC; 45 46 private Vector<MediaCodec> mCodecs = new Vector<MediaCodec>(); 47 48 private class TestCodecCallback extends MediaCodec.Callback { 49 @Override onInputBufferAvailable(MediaCodec codec, int index)50 public void onInputBufferAvailable(MediaCodec codec, int index) { 51 Log.d(TAG, "onInputBufferAvailable " + codec.toString()); 52 } 53 54 @Override onOutputBufferAvailable( MediaCodec codec, int index, MediaCodec.BufferInfo info)55 public void onOutputBufferAvailable( 56 MediaCodec codec, int index, MediaCodec.BufferInfo info) { 57 Log.d(TAG, "onOutputBufferAvailable " + codec.toString()); 58 } 59 60 @Override onError(MediaCodec codec, MediaCodec.CodecException e)61 public void onError(MediaCodec codec, MediaCodec.CodecException e) { 62 Log.d(TAG, "onError " + codec.toString() + " errorCode " + e.getErrorCode()); 63 } 64 65 @Override onOutputFormatChanged(MediaCodec codec, MediaFormat format)66 public void onOutputFormatChanged(MediaCodec codec, MediaFormat format) { 67 Log.d(TAG, "onOutputFormatChanged " + codec.toString()); 68 } 69 } 70 71 private MediaCodec.Callback mCallback = new TestCodecCallback(); 72 getTestFormat(CodecCapabilities caps, boolean securePlayback, boolean highResolution)73 private MediaFormat getTestFormat(CodecCapabilities caps, boolean securePlayback, 74 boolean highResolution) { 75 VideoCapabilities vcaps = caps.getVideoCapabilities(); 76 int bitrate = 0; 77 78 if (highResolution) { 79 if (mWidth == 0 || mHeight == 0) { 80 mWidth = vcaps.getSupportedWidths().getUpper(); 81 mHeight = vcaps.getSupportedHeightsFor(mWidth).getUpper(); 82 } 83 bitrate = vcaps.getBitrateRange().getUpper(); 84 } else { 85 if (mWidth == 0 || mHeight == 0) { 86 mWidth = vcaps.getSupportedWidths().getLower(); 87 mHeight = vcaps.getSupportedHeightsFor(mWidth).getLower(); 88 } 89 bitrate = vcaps.getBitrateRange().getLower(); 90 } 91 92 MediaFormat format = MediaFormat.createVideoFormat(mMime, mWidth, mHeight); 93 format.setInteger(MediaFormat.KEY_COLOR_FORMAT, caps.colorFormats[0]); 94 format.setInteger(MediaFormat.KEY_BIT_RATE, bitrate); 95 format.setInteger(MediaFormat.KEY_FRAME_RATE, FRAME_RATE); 96 format.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, IFRAME_INTERVAL); 97 format.setFeatureEnabled(CodecCapabilities.FEATURE_SecurePlayback, securePlayback); 98 return format; 99 } 100 getTestCodecInfo(boolean securePlayback)101 private MediaCodecInfo getTestCodecInfo(boolean securePlayback) { 102 // Use avc decoder for testing. 103 boolean isEncoder = false; 104 105 MediaCodecList mcl = new MediaCodecList(MediaCodecList.ALL_CODECS); 106 for (MediaCodecInfo info : mcl.getCodecInfos()) { 107 if (info.isEncoder() != isEncoder) { 108 continue; 109 } 110 CodecCapabilities caps; 111 try { 112 caps = info.getCapabilitiesForType(mMime); 113 boolean securePlaybackSupported = 114 caps.isFeatureSupported(CodecCapabilities.FEATURE_SecurePlayback); 115 boolean securePlaybackRequired = 116 caps.isFeatureRequired(CodecCapabilities.FEATURE_SecurePlayback); 117 if ((securePlayback && securePlaybackSupported) || 118 (!securePlayback && !securePlaybackRequired) ) { 119 Log.d(TAG, "securePlayback " + securePlayback + " will use " + info.getName()); 120 } else { 121 Log.d(TAG, "securePlayback " + securePlayback + " skip " + info.getName()); 122 continue; 123 } 124 } catch (IllegalArgumentException e) { 125 // mime is not supported 126 continue; 127 } 128 return info; 129 } 130 131 return null; 132 } 133 allocateCodecs(int max)134 protected int allocateCodecs(int max) { 135 Bundle extras = getIntent().getExtras(); 136 int type = TYPE_NONSECURE; 137 boolean highResolution = false; 138 if (extras != null) { 139 type = extras.getInt("test-type", type); 140 // Check if mime has been passed. 141 mMime = extras.getString("mime", mMime); 142 // Check if resolution has been passed. 143 mWidth = extras.getInt("width"); 144 mHeight = extras.getInt("height"); 145 if (mWidth == 0 || mHeight == 0) { 146 // Either no resolution has been passed or its invalid. 147 // So, look for high-resolution flag. 148 highResolution = extras.getBoolean("high-resolution", highResolution); 149 } else if (mHeight >= 1080) { 150 highResolution = true; 151 } 152 153 Log.d(TAG, "type is: " + type + " high-resolution: " + highResolution); 154 } 155 156 boolean shouldSkip = false; 157 boolean securePlayback; 158 if (type == TYPE_NONSECURE || type == TYPE_MIX) { 159 securePlayback = false; 160 MediaCodecInfo info = getTestCodecInfo(securePlayback); 161 if (info != null) { 162 allocateCodecs(max, info, securePlayback, highResolution); 163 } else { 164 shouldSkip = true; 165 } 166 } 167 168 if (!shouldSkip) { 169 if (type == TYPE_SECURE || type == TYPE_MIX) { 170 securePlayback = true; 171 MediaCodecInfo info = getTestCodecInfo(securePlayback); 172 if (info != null) { 173 allocateCodecs(max, info, securePlayback, highResolution); 174 } else { 175 shouldSkip = true; 176 } 177 } 178 } 179 180 if (shouldSkip) { 181 Log.d(TAG, "test skipped as there's no supported codec."); 182 finishWithResult(ResourceManagerStubActivity.RESULT_CODE_NO_DECODER); 183 } 184 185 Log.d(TAG, "allocateCodecs returned " + mCodecs.size()); 186 return mCodecs.size(); 187 } 188 allocateCodecs(int max, MediaCodecInfo info, boolean securePlayback, boolean highResolution)189 protected void allocateCodecs(int max, MediaCodecInfo info, boolean securePlayback, 190 boolean highResolution) { 191 String name = info.getName(); 192 CodecCapabilities caps = info.getCapabilitiesForType(mMime); 193 MediaFormat format = getTestFormat(caps, securePlayback, highResolution); 194 MediaCodec codec = null; 195 for (int i = mCodecs.size(); i < max; ++i) { 196 try { 197 Log.d(TAG, "Create codec " + name + " #" + i); 198 codec = MediaCodec.createByCodecName(name); 199 codec.setCallback(mCallback); 200 Log.d(TAG, "Configure codec " + format); 201 codec.configure(format, null, null, 0); 202 Log.d(TAG, "Start codec " + format); 203 codec.start(); 204 mCodecs.add(codec); 205 codec = null; 206 } catch (IllegalArgumentException e) { 207 Log.d(TAG, "IllegalArgumentException " + e.getMessage()); 208 break; 209 } catch (IOException e) { 210 Log.d(TAG, "IOException " + e.getMessage()); 211 break; 212 } catch (MediaCodec.CodecException e) { 213 Log.d(TAG, "CodecException 0x" + Integer.toHexString(e.getErrorCode())); 214 break; 215 } finally { 216 if (codec != null) { 217 Log.d(TAG, "release codec"); 218 codec.release(); 219 codec = null; 220 } 221 } 222 } 223 } 224 finishWithResult(int result)225 protected void finishWithResult(int result) { 226 for (int i = 0; i < mCodecs.size(); ++i) { 227 Log.d(TAG, "release codec #" + i); 228 mCodecs.get(i).release(); 229 } 230 mCodecs.clear(); 231 setResult(result); 232 finish(); 233 Log.d(TAG, "activity finished"); 234 } 235 doUseCodecs()236 private void doUseCodecs() { 237 int current = 0; 238 try { 239 for (current = 0; current < mCodecs.size(); ++current) { 240 mCodecs.get(current).getName(); 241 } 242 } catch (MediaCodec.CodecException e) { 243 Log.d(TAG, "useCodecs got CodecException 0x" + Integer.toHexString(e.getErrorCode())); 244 if (e.getErrorCode() == MediaCodec.CodecException.ERROR_RECLAIMED) { 245 Log.d(TAG, "Remove codec " + current + " from the list"); 246 mCodecs.get(current).release(); 247 mCodecs.remove(current); 248 mGotReclaimedException = true; 249 mUseCodecs = false; 250 } 251 return; 252 } 253 } 254 255 protected boolean mWaitForReclaim = true; 256 private Thread mWorkerThread; 257 private volatile boolean mUseCodecs = true; 258 private volatile boolean mGotReclaimedException = false; useCodecs()259 protected void useCodecs() { 260 mWorkerThread = new Thread(new Runnable() { 261 @Override 262 public void run() { 263 long start = System.currentTimeMillis(); 264 long timeSinceStartedMs = 0; 265 while (mUseCodecs && (timeSinceStartedMs < 15000)) { // timeout in 15s 266 doUseCodecs(); 267 try { 268 Thread.sleep(50 /* millis */); 269 } catch (InterruptedException e) {} 270 timeSinceStartedMs = System.currentTimeMillis() - start; 271 } 272 if (mGotReclaimedException) { 273 Log.d(TAG, "Got expected reclaim exception."); 274 finishWithResult(RESULT_OK); 275 } else { 276 Log.d(TAG, "Stopped without getting reclaim exception."); 277 // if the test is supposed to wait for reclaim event then this is a failure, 278 // otherwise this is a pass. 279 finishWithResult(mWaitForReclaim ? RESULT_CANCELED : RESULT_OK); 280 } 281 } 282 }); 283 mWorkerThread.start(); 284 } 285 286 @Override onDestroy()287 protected void onDestroy() { 288 Log.d(TAG, "onDestroy called."); 289 super.onDestroy(); 290 } 291 } 292