1 // Copyright 2010 Google Inc.
2 // All Rights Reserved.
3 
4 package com.example.android.videochatcameratest;
5 
6 import android.app.Activity;
7 import android.graphics.ImageFormat;
8 import android.hardware.Camera;
9 import android.hardware.Camera.Size;
10 import android.os.AsyncTask;
11 import android.os.Bundle;
12 import android.util.Log;
13 import android.view.Surface;
14 import android.view.TextureView;
15 import android.view.View;
16 import android.view.View.OnClickListener;
17 import android.view.Window;
18 import android.view.WindowManager;
19 import android.widget.Button;
20 import android.widget.CheckBox;
21 import android.widget.FrameLayout;
22 import android.widget.TextView;
23 
24 import java.io.IOException;
25 import java.lang.UnsupportedOperationException;
26 import java.util.ArrayList;
27 import java.util.List;
28 
29 public class VideoChatTestActivity extends Activity {
30 
31     static final private int NUM_CAMERA_PREVIEW_BUFFERS = 2;
32     static final boolean sRunningOnHoneycomb;
33     static final private String TAG = "VideoChatTest";
34     TextView mTextStatusHistory;
35     static {
36         sRunningOnHoneycomb =
37                 android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.HONEYCOMB;
38     }
39 
VideoChatTestActivity()40     public VideoChatTestActivity() {
41     }
42 
43     /** Called with the activity is first created. */
44     @Override
onCreate(Bundle savedInstanceState)45     public void onCreate(Bundle savedInstanceState) {
46         super.onCreate(savedInstanceState);
47 
48         Window window = getWindow();
49         window.setFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON,
50                 WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
51 
52         // Inflate our UI from its XML layout description.
53         setContentView(R.layout.videochatcameratest_activity);
54 
55         FrameLayout fl = (FrameLayout)findViewById(R.id.previewFrame);
56 
57         if (sRunningOnHoneycomb) {
58             fl.addView(new SurfaceTextureView(this));
59         } else {
60             fl.addView(new CameraPreviewView(this));
61         }
62 
63         ((Button) findViewById(R.id.gobutton)).setOnClickListener(mGoListener);
64 
65         ((TextView)findViewById(R.id.statushistory)).setVerticalScrollBarEnabled(true);
66         mTextStatusHistory = (TextView) findViewById(R.id.statushistory);
67 
68         logMessage("Display Orientation " + getDisplayOrientation());
69         for (int i = 0; i < Camera.getNumberOfCameras(); i++) {
70             dumpCameraCaps(i);
71         }
72     }
73 
logMessage(String message)74     private void logMessage(String message) {
75         Log.v(TAG, message);
76         mTextStatusHistory.append(message + "\r\n");
77     }
78 
getCameraOrientation(int id)79     public int getCameraOrientation(int id) {
80         Camera.CameraInfo info =
81             new Camera.CameraInfo();
82         Camera.getCameraInfo(id, info);
83         return info.orientation;
84     }
85 
dumpCameraCaps(int id)86     private void dumpCameraCaps(int id) {
87         Camera cam = Camera.open(id);
88         Camera.Parameters params = cam.getParameters();
89         List<Integer> formats = params.getSupportedPreviewFormats();
90         List<int[]> frameRates = params.getSupportedPreviewFpsRange();
91         List<Camera.Size> sizes = params.getSupportedPreviewSizes();
92         logMessage("Camera " + id);
93         logMessage("Orientation " + getCameraOrientation(id));
94         logMessage("Sizes");
95         for (Size size : sizes) {
96             logMessage(size.width + "x" + size.height);
97         }
98         logMessage("frameRates");
99         for (int[] rates : frameRates) {
100             logMessage(rates[0] + "-" + rates[1]);
101         }
102         logMessage("formats");
103         for (Integer format : formats) {
104             logMessage(format.toString());
105         }
106         cam.release();
107     }
108     /**
109      * Called when the activity is about to start interacting with the user.
110      */
111     @Override
onResume()112     protected void onResume() {
113         super.onResume();
114     }
115 
getDisplayOrientation()116     private int getDisplayOrientation() {
117         int rotation = getWindowManager().getDefaultDisplay().getRotation();
118         int degrees = 0;
119         switch (rotation) {
120             case Surface.ROTATION_0:
121                 degrees = 0;
122                 break;
123             case Surface.ROTATION_90:
124                 degrees = 90;
125                 break;
126             case Surface.ROTATION_180:
127                 degrees = 180;
128                 break;
129             case Surface.ROTATION_270:
130                 degrees = 270;
131                 break;
132         }
133         return degrees;
134     }
135 
136     /**
137      * A call-back for when the user presses the back button.
138      */
139     OnClickListener mGoListener = new OnClickListener() {
140         @Override
141         public void onClick(View v) {
142             int degrees = getDisplayOrientation();
143             new CameraTestRunner().execute(new Integer[] { degrees });
144         }
145     };
146 
147     private class CameraTestRunner extends AsyncTask<Integer, String, Void> {
148 
149         TextView mTextStatus;
150         private int mDisplayOrientation;
151         private volatile boolean mClearStatusOnNextUpdate;
152 
153         @Override
doInBackground(Integer... params)154         protected Void doInBackground(Integer... params) {
155             mDisplayOrientation = params[0];
156             mTextStatus = (TextView) findViewById(R.id.status);
157             boolean testFrontCamera =
158                     ((CheckBox) findViewById(R.id.frontcameracheckbox)).isChecked();
159             boolean testBackCamera = ((CheckBox) findViewById(R.id.backcameracheckbox)).isChecked();
160             boolean testQVGA = ((CheckBox) findViewById(R.id.qvgacheckbox)).isChecked();
161             boolean testVGA = ((CheckBox) findViewById(R.id.vgacheckbox)).isChecked();
162             boolean test15fps = ((CheckBox) findViewById(R.id.fps15checkbox)).isChecked();
163             boolean test30fps = ((CheckBox) findViewById(R.id.fps30checkbox)).isChecked();
164             boolean testRotate0 = ((CheckBox) findViewById(R.id.rotate0checkbox)).isChecked();
165             boolean testRotate90 = ((CheckBox) findViewById(R.id.rotate90checkbox)).isChecked();
166             boolean testRotate180 = ((CheckBox) findViewById(R.id.rotate180checkbox)).isChecked();
167             boolean testRotate270 = ((CheckBox) findViewById(R.id.rotate270checkbox)).isChecked();
168             boolean testRotateAuto = ((CheckBox) findViewById(R.id.rotateautocheckbox)).isChecked();
169 
170             ArrayList<Integer> setDisplayOrentationAngles = new ArrayList<Integer>();
171 
172             if (testRotate0) {
173                 setDisplayOrentationAngles.add(0);
174             }
175             if (testRotate90) {
176                 setDisplayOrentationAngles.add(90);
177             }
178             if (testRotate180) {
179                 setDisplayOrentationAngles.add(180);
180             }
181             if (testRotate270) {
182                 setDisplayOrentationAngles.add(270);
183             }
184             if (testRotateAuto) {
185                 setDisplayOrentationAngles.add(-1);
186             }
187 
188             final int widths[] = new int[] {320, 640};
189             final int heights[] = new int[] {240, 480};
190 
191             final int framerates[] = new int[] {15, 30};
192 
193             ArrayList<Integer> whichCameras = new ArrayList<Integer>();
194             int numCameras = Camera.getNumberOfCameras();
195             if (testFrontCamera) {
196                 for (int i = 0; i < numCameras; i++) {
197                     Camera.CameraInfo info = new Camera.CameraInfo();
198                     Camera.getCameraInfo(i, info);
199                     if (info.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) {
200                         whichCameras.add(i);
201                         break;
202                     }
203                 }
204             }
205             if (testBackCamera) {
206                 for (int i = 0; i < numCameras; i++) {
207                     Camera.CameraInfo info = new Camera.CameraInfo();
208                     Camera.getCameraInfo(i, info);
209                     if (info.facing == Camera.CameraInfo.CAMERA_FACING_BACK) {
210                         whichCameras.add(i);
211                         break;
212                     }
213                 }
214             }
215             do {
216                 mClearStatusOnNextUpdate = true;
217                 for (Integer whichCamera : whichCameras) {
218                     for (int whichResolution = 0; whichResolution < 2; whichResolution++) {
219                         if (whichResolution == 0 && !testQVGA) {
220                             continue;
221                         }
222                         if (whichResolution == 1 && !testVGA) {
223                             continue;
224                         }
225 
226                         for (int whichFramerate = 0; whichFramerate < 2; whichFramerate++) {
227                             if (whichFramerate == 0 && !test15fps) {
228                                 continue;
229                             }
230                             if (whichFramerate == 1 && !test30fps) {
231                                 continue;
232                             }
233 
234                             TestCamera(whichCamera, widths[whichResolution],
235                                     heights[whichResolution], framerates[whichFramerate],
236                                     setDisplayOrentationAngles);
237                         }
238                     }
239                 }
240             } while (((CheckBox) findViewById(R.id.repeatcheckbox)).isChecked());
241             // start tests
242 
243             return null;
244         }
245 
246         @Override
onPostExecute(Void result)247         protected void onPostExecute(Void result) {
248             final String allDoneString = "Test complete";
249             Log.v(TAG, allDoneString);
250             mTextStatus.setText(allDoneString);
251             mTextStatusHistory.append(allDoneString + "\r\n");
252         }
253 
254 
255         private class FrameCatcher implements Camera.PreviewCallback {
256             public int mFrames = 0;
257             private final int mExpectedSize;
FrameCatcher(int width, int height)258             public FrameCatcher(int width, int height) {
259                 mExpectedSize = width * height * 3 / 2;
260             }
261 
262             @Override
onPreviewFrame(byte[] data, Camera camera)263             public void onPreviewFrame(byte[] data, Camera camera) {
264                 if (mExpectedSize != data.length) {
265                     throw new UnsupportedOperationException("bad size, got " + data.length + " expected " + mExpectedSize);
266                 }
267                 mFrames++;
268                 camera.addCallbackBuffer(data);
269             }
270 
271         }
272 
setupCallback(Camera camera, FrameCatcher catcher, int bufferSize)273         private void setupCallback(Camera camera, FrameCatcher catcher, int bufferSize) {
274             camera.setPreviewCallbackWithBuffer(null);
275             camera.setPreviewCallbackWithBuffer(catcher);
276             for (int i = 0; i < NUM_CAMERA_PREVIEW_BUFFERS; i++) {
277                 byte [] cameraBuffer = new byte[bufferSize];
278                 camera.addCallbackBuffer(cameraBuffer);
279             }
280         }
281 
getAutoDisplayOrientation(int displayOrientationDegrees, int cameraId, android.hardware.Camera camera)282         private int getAutoDisplayOrientation(int displayOrientationDegrees,
283                 int cameraId, android.hardware.Camera camera) {
284             android.hardware.Camera.CameraInfo info =
285                     new android.hardware.Camera.CameraInfo();
286             android.hardware.Camera.getCameraInfo(cameraId, info);
287 
288             int result;
289             if (info.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) {
290                 result = (info.orientation + displayOrientationDegrees) % 360;
291                 result = (360 - result) % 360; // compensate the mirror
292             } else { // back-facing
293                 result = (info.orientation - displayOrientationDegrees + 360) % 360;
294             }
295             return result;
296         }
297 
TestCamera(int whichCamera, int width, int height, int frameRate, List<Integer> setDisplayOrentationAngles)298         protected void TestCamera(int whichCamera,
299                 int width, int height,
300                 int frameRate,
301                 List<Integer> setDisplayOrentationAngles) {
302             String baseStatus = "Camera id " + whichCamera + " " +
303                 width + "x" + height + " " +
304                 frameRate + "fps";
305             publishProgress("Initializing " + baseStatus);
306             String status = "";
307             boolean succeeded = true;
308             Log.v(TAG, "Start test -- id " + whichCamera + " " + width + "x" + height +
309                     " " + frameRate + "fps");
310             Camera camera;
311             FrameLayout previewBlock = (FrameLayout)findViewById(R.id.previewFrame);
312             SurfaceTextureView surfaceTextureView = null;
313             CameraPreviewView previewView = null;
314             if (sRunningOnHoneycomb) {
315                 surfaceTextureView = (SurfaceTextureView)previewBlock.getChildAt(0);
316             } else {
317                 previewView = (CameraPreviewView)previewBlock.getChildAt(0);
318             }
319 
320             camera = Camera.open(whichCamera);
321             publishProgress("Opened " + baseStatus);
322             try {
323                 try {
324                     if (sRunningOnHoneycomb) {
325                         camera.setPreviewTexture(surfaceTextureView.getSurfaceTexture());
326                     } else {
327                         camera.setPreviewDisplay(previewView.mHolder);
328                     }
329                 } catch (IOException exception) {
330                     succeeded = false;
331                     status = exception.toString();
332                     return;
333                 }
334 
335                 camera.setPreviewCallbackWithBuffer(null);
336                 Camera.Parameters parameters = camera.getParameters();
337 
338                 publishProgress("Changing preview parameters " + width + "x" + height + baseStatus);
339 
340                 parameters.setPreviewSize(width, height);
341                 parameters.setPreviewFormat(ImageFormat.NV21);
342 
343                 parameters.setPreviewFrameRate(frameRate);
344                 camera.setParameters(parameters);
345 
346                 publishProgress("Validating preview parameters " + baseStatus);
347 
348                 parameters = camera.getParameters();
349                 Size setSize = parameters.getPreviewSize();
350                 if (setSize.width != width || setSize.height != height) {
351                     status += "Bad reported size, wanted " + width + "x" + height + ", got " +
352                     setSize.width + "x" + setSize.height;
353                     succeeded = false;
354                 }
355 
356                 if (parameters.getPreviewFrameRate() != frameRate) {
357                     status += "Bad reported frame rate, wanted " + frameRate
358                     + ", got " + parameters.getPreviewFrameRate();
359                     succeeded = false;
360                 }
361 
362                 publishProgress("Initializing callback buffers " + baseStatus);
363                 int imageFormat = parameters.getPreviewFormat();
364                 if (imageFormat != ImageFormat.NV21) {
365                     status = "Bad reported image format, wanted NV21 (" + ImageFormat.NV21 +
366                             ") got " + imageFormat;
367                     succeeded = false;
368                     throw new UnsupportedOperationException(status);
369                 }
370                 int bufferSize;
371                 bufferSize = setSize.width * setSize.height
372                                 * ImageFormat.getBitsPerPixel(imageFormat) / 8;
373                 int sizeWeShouldHave = (width * height * 3 / 2);
374                 if (bufferSize != sizeWeShouldHave) {
375                     status = "Bad calculate size. Should have been " + (width * height * 3 / 2) +
376                             " but got " + imageFormat;
377                     succeeded = false;
378                     throw new UnsupportedOperationException(status);
379                 }
380 
381                 FrameCatcher catcher = new FrameCatcher(setSize.width, setSize.height);
382 
383                 if (succeeded) {
384                     publishProgress("Starting " + baseStatus);
385                 } else {
386                     publishProgress("Starting " + baseStatus + " -- but " + status);
387                 }
388 
389                 int numPasses;
390                 boolean doSetDisplayOrientation;
391                 if (setDisplayOrentationAngles == null || setDisplayOrentationAngles.size() == 0) {
392                     numPasses = 1;
393                     doSetDisplayOrientation = false;
394                 } else {
395                     numPasses = setDisplayOrentationAngles.size();
396                     doSetDisplayOrientation = true;
397                 }
398 
399                 for (int i = 0; i < numPasses; i++) {
400                     if (doSetDisplayOrientation) {
401                         int rotation = setDisplayOrentationAngles.get(i);
402                         if (rotation == -1) {
403                             rotation = getAutoDisplayOrientation(mDisplayOrientation,
404                                     whichCamera, camera);
405                         }
406                         publishProgress("setDisplayOrientation to " + rotation);
407                         try {
408                             camera.setDisplayOrientation(rotation);
409                         } catch (RuntimeException exception) {
410                             succeeded = false;
411                             status = exception.toString();
412                             return;
413                         }
414                     }
415                     if (sRunningOnHoneycomb) {
416                         surfaceTextureView.resetFrameCounter();
417                         surfaceTextureView.setCameraEnabled(true);
418                     } else {
419                         setupCallback(camera, catcher, bufferSize);
420                     }
421                     camera.startPreview();
422                     try {
423                         Thread.sleep(5000);
424                     } catch (InterruptedException exception) {
425                         succeeded = false;
426                         status = exception.toString();
427                         return;
428                     }
429                     if (sRunningOnHoneycomb) {
430                         surfaceTextureView.setCameraEnabled(false);
431                     } else {
432                         camera.setPreviewCallbackWithBuffer(null);
433                     }
434                     camera.stopPreview();
435                 }
436 
437                 int frames;
438                 if (sRunningOnHoneycomb) {
439                     frames = surfaceTextureView.getFrameCounter();
440                 } else {
441                     frames = catcher.mFrames;
442                 }
443                 if (frames == 0) {
444                     succeeded = false;
445                     publishProgress("Preview callback received no frames from " + baseStatus);
446                 } else {
447                     publishProgress("Preview callback got " + frames + " frames (~" +
448                             Math.round(((double)frames)/(5.0 * numPasses)) + "fps) " +
449                             baseStatus);
450                 }
451                 try {
452                     camera.setPreviewDisplay(null);
453                 } catch (IOException exception) {
454                     succeeded = false;
455                     status = exception.toString();
456                     return;
457                 }
458             } finally {
459                 Log.v(TAG, "Releasing camera");
460 
461                 if (succeeded) {
462                     publishProgress("Success " + baseStatus);
463                 } else {
464                     publishProgress("Finished " + baseStatus + " -- but " + status);
465                 }
466 
467                 camera.release();
468             }
469         }
470 
471         @Override
onProgressUpdate(String... message)472         protected void onProgressUpdate(String... message) {
473             if (mClearStatusOnNextUpdate) {
474                 mClearStatusOnNextUpdate = false;
475                 mTextStatusHistory.setText("");
476             }
477             Log.v(TAG, message[0]);
478             mTextStatus.setText(message[0]);
479             mTextStatusHistory.append(message[0] + "\r\n");
480         }
481     }
482 }
483