TestingCamera2.java revision 419fb8918fcdb643dabc58437bcb0a8026a25604
1/* 2 * Copyright (C) 2013 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 17package com.android.testingcamera2; 18 19import android.app.Activity; 20import android.graphics.Bitmap; 21import android.graphics.BitmapFactory; 22import android.graphics.ImageFormat; 23import android.hardware.camera2.CameraDevice; 24import android.hardware.camera2.CameraProperties; 25import android.hardware.camera2.CameraPropertiesKeys; 26import android.hardware.camera2.CaptureRequest; 27import android.hardware.camera2.CaptureRequestKeys; 28import android.hardware.camera2.CaptureRequestKeys.Control.ModeKey.Enum; 29import android.hardware.camera2.CaptureResult; 30import android.media.Image; 31import android.os.AsyncTask; 32import android.os.Bundle; 33import android.os.Handler; 34import android.util.Log; 35import android.view.SurfaceHolder; 36import android.view.SurfaceView; 37import android.view.View; 38import android.view.WindowManager; 39import android.widget.Button; 40import android.widget.ImageView; 41import android.widget.SeekBar; 42import android.widget.SeekBar.OnSeekBarChangeListener; 43import android.widget.TextView; 44import android.widget.ToggleButton; 45 46import java.nio.ByteBuffer; 47import java.util.HashSet; 48import java.util.Set; 49 50public class TestingCamera2 extends Activity implements SurfaceHolder.Callback { 51 52 private static final String TAG = "TestingCamera2"; 53 private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE); 54 private CameraOps mCameraOps; 55 private static final int mSeekBarMax = 100; 56 private static final long MAX_EXPOSURE = 200000000L; // 200ms 57 private static final long MIN_EXPOSURE = 100000L; // 100us 58 private static final long MAX_FRAME_DURATION = 1000000000L; // 1s 59 // Manual control change step size 60 private static final int STEP_SIZE = 100; 61 // Min and max sensitivity ISO values 62 private static final int MIN_SENSITIVITY = 100; 63 private static final int MAX_SENSITIVITY = 1600; 64 65 private SurfaceView mPreviewView; 66 private ImageView mStillView; 67 68 private SurfaceHolder mCurrentPreviewHolder = null; 69 70 private Button mInfoButton; 71 72 private SeekBar mSensitivityBar; 73 private SeekBar mExposureBar; 74 private SeekBar mFrameDurationBar; 75 76 private TextView mSensitivityInfoView; 77 private TextView mExposureInfoView; 78 private TextView mFrameDurationInfoView; 79 private TextView mCaptureResultView; 80 private ToggleButton mRecordingToggle; 81 private ToggleButton mManualCtrlToggle; 82 83 private CameraControls mCameraControl = null; 84 private Set<View> mManualControls = new HashSet<View>(); 85 86 Handler mMainHandler; 87 88 @Override 89 public void onCreate(Bundle savedInstanceState) { 90 super.onCreate(savedInstanceState); 91 92 getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, 93 WindowManager.LayoutParams.FLAG_FULLSCREEN); 94 95 setContentView(R.layout.main); 96 97 mPreviewView = (SurfaceView) findViewById(R.id.preview_view); 98 mPreviewView.getHolder().addCallback(this); 99 100 mStillView = (ImageView) findViewById(R.id.still_view); 101 102 mInfoButton = (Button) findViewById(R.id.info_button); 103 mInfoButton.setOnClickListener(mInfoButtonListener); 104 mRecordingToggle = (ToggleButton) findViewById(R.id.start_recording); 105 mRecordingToggle.setOnClickListener(mRecordingToggleListener); 106 107 mManualCtrlToggle = (ToggleButton) findViewById(R.id.manual_control); 108 mManualCtrlToggle.setOnClickListener(mControlToggleListener); 109 110 mSensitivityBar = (SeekBar) findViewById(R.id.sensitivity_seekbar); 111 mSensitivityBar.setOnSeekBarChangeListener(mSensitivitySeekBarListener); 112 mSensitivityBar.setMax(mSeekBarMax); 113 mManualControls.add(mSensitivityBar); 114 115 mExposureBar = (SeekBar) findViewById(R.id.exposure_time_seekbar); 116 mExposureBar.setOnSeekBarChangeListener(mExposureSeekBarListener); 117 mExposureBar.setMax(mSeekBarMax); 118 mManualControls.add(mExposureBar); 119 120 mFrameDurationBar = (SeekBar) findViewById(R.id.frame_duration_seekbar); 121 mFrameDurationBar.setOnSeekBarChangeListener(mFrameDurationSeekBarListener); 122 mFrameDurationBar.setMax(mSeekBarMax); 123 mManualControls.add(mFrameDurationBar); 124 125 mSensitivityInfoView = (TextView) findViewById(R.id.sensitivity_bar_label); 126 mExposureInfoView = (TextView) findViewById(R.id.exposure_time_bar_label); 127 mFrameDurationInfoView = (TextView) findViewById(R.id.frame_duration_bar_label); 128 mCaptureResultView = (TextView) findViewById(R.id.capture_result_info_label); 129 130 enableManualControls(false); 131 mCameraControl = new CameraControls(); 132 133 // Get UI handler 134 mMainHandler = new Handler(); 135 136 try { 137 mCameraOps = CameraOps.create(this); 138 } catch(ApiFailureException e) { 139 logException("Cannot create camera ops!",e); 140 } 141 } 142 143 @Override 144 public void onResume() { 145 super.onResume(); 146 try { 147 mCameraOps.minimalPreviewConfig(mPreviewView.getHolder()); 148 mCurrentPreviewHolder = mPreviewView.getHolder(); 149 } catch (ApiFailureException e) { 150 logException("Can't configure preview surface: ",e); 151 } 152 } 153 154 @Override 155 public void onPause() { 156 super.onPause(); 157 try { 158 mCameraOps.closeDevice(); 159 } catch (ApiFailureException e) { 160 logException("Can't close device: ",e); 161 } 162 mCurrentPreviewHolder = null; 163 } 164 165 /** SurfaceHolder.Callback methods */ 166 @Override 167 public void surfaceChanged(SurfaceHolder holder, 168 int format, 169 int width, 170 int height) { 171 if (mCurrentPreviewHolder != null && holder == mCurrentPreviewHolder) { 172 try { 173 mCameraOps.minimalPreview(holder); 174 } catch (ApiFailureException e) { 175 logException("Can't start minimal preview: ", e); 176 } 177 } 178 } 179 180 @Override 181 public void surfaceCreated(SurfaceHolder holder) { 182 183 } 184 185 @Override 186 public void surfaceDestroyed(SurfaceHolder holder) { 187 } 188 189 private Button.OnClickListener mInfoButtonListener = new Button.OnClickListener() { 190 @Override 191 public void onClick(View v) { 192 final Handler uiHandler = new Handler(); 193 AsyncTask.execute(new Runnable() { 194 @Override 195 public void run() { 196 try { 197 mCameraOps.minimalJpegCapture(mCaptureListener, mCaptureResultListener, 198 uiHandler, mCameraControl); 199 if (mCurrentPreviewHolder != null) { 200 mCameraOps.minimalPreview(mCurrentPreviewHolder); 201 } 202 } catch (ApiFailureException e) { 203 logException("Can't take a JPEG! ", e); 204 } 205 } 206 }); 207 } 208 }; 209 210 /** 211 * UI controls enable/disable for all manual controls 212 */ 213 private void enableManualControls(boolean enabled) { 214 for (View v : mManualControls) { 215 v.setEnabled(enabled); 216 } 217 } 218 219 private CameraOps.CaptureListener mCaptureListener = new CameraOps.CaptureListener() { 220 @Override 221 public void onCaptureAvailable(Image capture) { 222 if (capture.getFormat() != ImageFormat.JPEG) { 223 Log.e(TAG, "Unexpected format: " + capture.getFormat()); 224 return; 225 } 226 ByteBuffer jpegBuffer = capture.getPlanes()[0].getBuffer(); 227 byte[] jpegData = new byte[jpegBuffer.capacity()]; 228 jpegBuffer.get(jpegData); 229 230 Bitmap b = BitmapFactory.decodeByteArray(jpegData, 0, jpegData.length); 231 mStillView.setImageBitmap(b); 232 } 233 }; 234 235 // TODO: this callback is not called for each capture, need figure out why. 236 private CameraOps.CaptureResultListener mCaptureResultListener = 237 new CameraOps.CaptureResultListener() { 238 239 @Override 240 public void onCaptureComplete( 241 CameraDevice camera, CaptureRequest request, CaptureResult result) { 242 Log.i(TAG, "Capture result is available"); 243 Enum reqCtrlMode; 244 Enum resCtrlMode; 245 if (request == null || result ==null) { 246 Log.e(TAG, "request/result is invalid"); 247 return; 248 } 249 Log.i(TAG, "Capture complete"); 250 final StringBuffer info = new StringBuffer("Capture Result:\n"); 251 reqCtrlMode = request.get(CaptureRequestKeys.Control.MODE); 252 resCtrlMode = result.get(CaptureRequestKeys.Control.MODE); 253 info.append("Control mode: request " + reqCtrlMode + ". result " + resCtrlMode); 254 info.append("\n"); 255 256 int reqSen = request.get(CaptureRequestKeys.Sensor.SENSITIVITY); 257 int resSen = result.get(CaptureRequestKeys.Sensor.SENSITIVITY); 258 info.append("Sensitivity: request " + reqSen + ". result " + resSen); 259 info.append("\n"); 260 261 long reqExp = request.get(CaptureRequestKeys.Sensor.EXPOSURE_TIME); 262 long resExp = result.get(CaptureRequestKeys.Sensor.EXPOSURE_TIME); 263 info.append("Exposure: request " + reqExp + ". result " + resExp); 264 info.append("\n"); 265 266 long reqFD = request.get(CaptureRequestKeys.Sensor.FRAME_DURATION); 267 long resFD = result.get(CaptureRequestKeys.Sensor.FRAME_DURATION); 268 info.append("Control mode: request " + reqFD + ". result " + resFD); 269 info.append("\n"); 270 271 if (mMainHandler != null) { 272 mMainHandler.post (new Runnable() { 273 @Override 274 public void run() { 275 // Update UI for capture result 276 mCaptureResultView.setText(info); 277 } 278 }); 279 } 280 } 281 282 @Override 283 public void onCaptureFailed(CameraDevice camera, CaptureRequest request) { 284 Log.e(TAG, "Capture failed"); 285 } 286 }; 287 288 private void logException(String msg, Throwable e) { 289 Log.e(TAG, msg + Log.getStackTraceString(e)); 290 } 291 292 private OnSeekBarChangeListener mSensitivitySeekBarListener = 293 new OnSeekBarChangeListener() { 294 295 @Override 296 public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { 297 int[] defaultRange = {MIN_SENSITIVITY, MAX_SENSITIVITY}; 298 CameraProperties properties = mCameraOps.getCameraProperties(); 299 int[] sensitivityRange = properties.get( 300 CameraPropertiesKeys.Sensor.Info.SENSITIVITY_RANGE); 301 if (sensitivityRange == null || sensitivityRange.length < 2 || 302 sensitivityRange[0] > MIN_SENSITIVITY || sensitivityRange[1] < MAX_SENSITIVITY) { 303 Log.e(TAG, "unable to get sensitivity range, use default range"); 304 sensitivityRange = defaultRange; 305 } 306 int min = sensitivityRange[0]; 307 int max = sensitivityRange[1]; 308 float progressFactor = progress / (float)mSeekBarMax; 309 int curSensitivity = (int) (min + (max - min) * progressFactor); 310 curSensitivity = (curSensitivity / STEP_SIZE ) * STEP_SIZE; 311 mCameraControl.setSensitivity(curSensitivity); 312 // Update the sensitivity info 313 StringBuffer info = new StringBuffer("Sensitivity(ISO):"); 314 info.append("" + curSensitivity); 315 mSensitivityInfoView.setText(info); 316 mCameraOps.updatePreview(mCameraControl); 317 } 318 319 @Override 320 public void onStartTrackingTouch(SeekBar seekBar) { 321 } 322 323 @Override 324 public void onStopTrackingTouch(SeekBar seekBar) { 325 } 326 }; 327 328 private OnSeekBarChangeListener mExposureSeekBarListener = 329 new OnSeekBarChangeListener() { 330 331 @Override 332 public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { 333 long[] defaultRange = {MIN_EXPOSURE, MAX_EXPOSURE}; 334 CameraProperties properties = mCameraOps.getCameraProperties(); 335 long[] exposureRange = properties.get( 336 CameraPropertiesKeys.Sensor.Info.EXPOSURE_TIME_RANGE); 337 // Not enforce the max value check here, most of the devices don't support 338 // larger than 30s exposure time 339 if (exposureRange == null || exposureRange.length < 2 || 340 exposureRange[0] > MIN_EXPOSURE || exposureRange[1] < 0) { 341 exposureRange = defaultRange; 342 Log.e(TAG, "exposure time range is invalid, use default range"); 343 } 344 long min = exposureRange[0]; 345 long max = exposureRange[1]; 346 float progressFactor = progress / (float)mSeekBarMax; 347 long curExposureTime = (long) (min + (max - min) * progressFactor); 348 mCameraControl.setExposure(curExposureTime); 349 // Update the sensitivity info 350 StringBuffer info = new StringBuffer("Exposure Time:"); 351 info.append("" + curExposureTime / 1000000.0 + "ms"); 352 mExposureInfoView.setText(info); 353 mCameraOps.updatePreview(mCameraControl); 354 } 355 356 @Override 357 public void onStartTrackingTouch(SeekBar seekBar) { 358 } 359 360 @Override 361 public void onStopTrackingTouch(SeekBar seekBar) { 362 } 363 }; 364 365 private OnSeekBarChangeListener mFrameDurationSeekBarListener = 366 new OnSeekBarChangeListener() { 367 368 @Override 369 public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { 370 CameraProperties properties = mCameraOps.getCameraProperties(); 371 Long frameDurationMax = properties.get( 372 CameraPropertiesKeys.Sensor.Info.MAX_FRAME_DURATION); 373 if (frameDurationMax == null || frameDurationMax <= 0) { 374 frameDurationMax = MAX_FRAME_DURATION; 375 Log.e(TAG, "max frame duration is invalid, set to " + frameDurationMax); 376 } 377 // Need calculate from different resolution, hard code to 10ms for now. 378 long min = 10000000L; 379 long max = frameDurationMax; 380 float progressFactor = progress / (float)mSeekBarMax; 381 long curFrameDuration = (long) (min + (max - min) * progressFactor); 382 mCameraControl.setFrameDuration(curFrameDuration); 383 // Update the sensitivity info 384 StringBuffer info = new StringBuffer("Frame Duration:"); 385 info.append("" + curFrameDuration / 1000000.0 + "ms"); 386 mFrameDurationInfoView.setText(info); 387 mCameraOps.updatePreview(mCameraControl); 388 } 389 390 @Override 391 public void onStartTrackingTouch(SeekBar seekBar) { 392 } 393 394 @Override 395 public void onStopTrackingTouch(SeekBar seekBar) { 396 } 397 }; 398 399 private View.OnClickListener mControlToggleListener = 400 new View.OnClickListener() { 401 @Override 402 public void onClick(View v) { 403 boolean enableManual; 404 if (mManualCtrlToggle.isChecked()) { 405 enableManual = true; 406 } else { 407 enableManual = false; 408 } 409 mCameraControl.enableManualControl(enableManual); 410 enableManualControls(enableManual); 411 mCameraOps.updatePreview(mCameraControl); 412 } 413 }; 414 415 private View.OnClickListener mRecordingToggleListener = 416 new View.OnClickListener() { 417 @Override 418 public void onClick(View v) { 419 if (mRecordingToggle.isChecked()) { 420 //TODO: start recording and set its states 421 } else { 422 // TODO: stop recording and set its states 423 } 424 } 425 }; 426} 427