1// Copyright 2010 Google Inc. 2// All Rights Reserved. 3 4package com.example.android.videochatcameratest; 5 6import android.app.Activity; 7import android.graphics.ImageFormat; 8import android.hardware.Camera; 9import android.hardware.Camera.Size; 10import android.os.AsyncTask; 11import android.os.Bundle; 12import android.util.Log; 13import android.view.Surface; 14import android.view.TextureView; 15import android.view.View; 16import android.view.View.OnClickListener; 17import android.view.Window; 18import android.view.WindowManager; 19import android.widget.Button; 20import android.widget.CheckBox; 21import android.widget.FrameLayout; 22import android.widget.TextView; 23 24import java.io.IOException; 25import java.lang.UnsupportedOperationException; 26import java.util.ArrayList; 27import java.util.List; 28 29public 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 40 public VideoChatTestActivity() { 41 } 42 43 /** Called with the activity is first created. */ 44 @Override 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 74 private void logMessage(String message) { 75 Log.v(TAG, message); 76 mTextStatusHistory.append(message + "\r\n"); 77 } 78 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 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 112 protected void onResume() { 113 super.onResume(); 114 } 115 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 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 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; 258 public FrameCatcher(int width, int height) { 259 mExpectedSize = width * height * 3 / 2; 260 } 261 262 @Override 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 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 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 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 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