• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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