1 /* 2 * Copyright (C) 2016 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 package com.android.devcamera; 17 18 import android.content.Context; 19 import android.graphics.ImageFormat; 20 import android.graphics.SurfaceTexture; 21 import android.hardware.camera2.CameraAccessException; 22 import android.hardware.camera2.CameraCaptureSession; 23 import android.hardware.camera2.CameraCharacteristics; 24 import android.hardware.camera2.CameraDevice; 25 import android.hardware.camera2.CameraManager; 26 import android.hardware.camera2.CameraMetadata; 27 import android.hardware.camera2.CaptureRequest; 28 import android.hardware.camera2.CaptureResult; 29 import android.hardware.camera2.TotalCaptureResult; 30 import android.hardware.camera2.params.Face; 31 import android.hardware.camera2.params.InputConfiguration; 32 import android.media.Image; 33 import android.media.ImageReader; 34 import android.media.ImageWriter; 35 import android.media.MediaActionSound; 36 import android.opengl.GLES11Ext; 37 import android.opengl.GLES20; 38 import android.os.Handler; 39 import android.os.HandlerThread; 40 import android.os.SystemClock; 41 import android.util.Log; 42 import android.util.Size; 43 import android.view.Surface; 44 45 import java.nio.ByteBuffer; 46 import java.util.ArrayList; 47 import java.util.LinkedList; 48 import java.util.List; 49 50 import javax.microedition.khronos.opengles.GL10; 51 52 53 /** 54 * Api2Camera : a camera2 implementation 55 * 56 * The goal here is to make the simplest possible API2 camera, 57 * where individual streams and capture options (e.g. edge enhancement, 58 * noise reduction, face detection) can be toggled on and off. 59 * 60 */ 61 62 public class Api2Camera implements CameraInterface, SurfaceTexture.OnFrameAvailableListener { 63 private static final String TAG = "DevCamera_API2"; 64 65 // Nth frame to log; put 10^6 if you don't want logging. 66 private static int LOG_NTH_FRAME = 30; 67 // Log dropped frames. There are a log on Angler MDA32. 68 private static boolean LOG_DROPPED_FRAMES = true; 69 70 // IMPORTANT: Only one of these can be true: 71 private static boolean SECOND_YUV_IMAGEREADER_STREAM = true; 72 private static boolean SECOND_SURFACE_TEXTURE_STREAM = false; 73 74 // Enable raw stream if available. 75 private static boolean RAW_STREAM_ENABLE = true; 76 // Use JPEG ImageReader and YUV ImageWriter if reprocessing is available 77 private static final boolean USE_REPROCESSING_IF_AVAIL = true; 78 79 // Whether we are continuously taking pictures, or not. 80 boolean mIsBursting = false; 81 // Last total capture result 82 TotalCaptureResult mLastTotalCaptureResult; 83 84 // ImageReader/Writer buffer sizes. 85 private static final int YUV1_IMAGEREADER_SIZE = 8; 86 private static final int YUV2_IMAGEREADER_SIZE = 8; 87 private static final int RAW_IMAGEREADER_SIZE = 8; 88 private static final int IMAGEWRITER_SIZE = 2; 89 90 private CameraInfoCache mCameraInfoCache; 91 private CameraManager mCameraManager; 92 private CameraCaptureSession mCurrentCaptureSession; 93 private MediaActionSound mMediaActionSound = new MediaActionSound(); 94 95 MyCameraCallback mMyCameraCallback; 96 97 // Generally everything running on this thread & this module is *not thread safe*. 98 private HandlerThread mOpsThread; 99 private Handler mOpsHandler; 100 private HandlerThread mInitThread; 101 private Handler mInitHandler; 102 private HandlerThread mJpegListenerThread; 103 private Handler mJpegListenerHandler; 104 105 Context mContext; 106 boolean mCameraIsFront; 107 SurfaceTexture mSurfaceTexture; 108 Surface mSurfaceTextureSurface; 109 110 private boolean mFirstFrameArrived; 111 private ImageReader mYuv1ImageReader; 112 private int mYuv1ImageCounter; 113 // Handle to last received Image: allows ZSL to be implemented. 114 private Image mYuv1LastReceivedImage = null; 115 // Time at which reprocessing request went in (right now we are doing one at a time). 116 private long mReprocessingRequestNanoTime; 117 118 private ImageReader mJpegImageReader; 119 private ImageReader mYuv2ImageReader; 120 private int mYuv2ImageCounter; 121 private ImageReader mRawImageReader; 122 private int mRawImageCounter; 123 124 // Starting the preview requires each of these 3 to be true/non-null: 125 volatile private Surface mPreviewSurface; 126 volatile private CameraDevice mCameraDevice; 127 volatile boolean mAllThingsInitialized = false; 128 129 /** 130 * Constructor. 131 */ 132 public Api2Camera(Context context, boolean useFrontCamera) { 133 mContext = context; 134 mCameraIsFront = useFrontCamera; 135 mCameraManager = (CameraManager) context.getSystemService(Context.CAMERA_SERVICE); 136 mCameraInfoCache = new CameraInfoCache(mCameraManager, useFrontCamera); 137 138 // Create thread and handler for camera operations. 139 mOpsThread = new HandlerThread("CameraOpsThread"); 140 mOpsThread.start(); 141 mOpsHandler = new Handler(mOpsThread.getLooper()); 142 143 // Create thread and handler for slow initialization operations. 144 // Don't want to use camera operations thread because we want to time camera open carefully. 145 mInitThread = new HandlerThread("CameraInitThread"); 146 mInitThread.start(); 147 mInitHandler = new Handler(mInitThread.getLooper()); 148 mInitHandler.post(new Runnable() { 149 @Override 150 public void run() { 151 InitializeAllTheThings(); 152 mAllThingsInitialized = true; 153 Log.v(TAG, "STARTUP_REQUIREMENT ImageReader initialization done."); 154 tryToStartCaptureSession(); 155 } 156 }); 157 158 // Set initial Noise and Edge modes. 159 if (mCameraInfoCache.isHardwareLevelAtLeast(CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_3)) { 160 // YUV streams. 161 if (mCameraInfoCache.supportedModesContains(mCameraInfoCache.noiseModes, 162 CameraCharacteristics.NOISE_REDUCTION_MODE_ZERO_SHUTTER_LAG)) { 163 mCaptureNoiseMode = CameraCharacteristics.NOISE_REDUCTION_MODE_ZERO_SHUTTER_LAG; 164 } else { 165 mCaptureNoiseMode = CameraCharacteristics.NOISE_REDUCTION_MODE_FAST; 166 } 167 if (mCameraInfoCache.supportedModesContains(mCameraInfoCache.edgeModes, 168 CameraCharacteristics.EDGE_MODE_ZERO_SHUTTER_LAG)) { 169 mCaptureEdgeMode = CameraCharacteristics.EDGE_MODE_ZERO_SHUTTER_LAG; 170 } else { 171 mCaptureEdgeMode = CameraCharacteristics.EDGE_MODE_FAST; 172 } 173 174 // Reprocessing. 175 mReprocessingNoiseMode = CameraCharacteristics.NOISE_REDUCTION_MODE_HIGH_QUALITY; 176 mReprocessingEdgeMode = CameraCharacteristics.EDGE_MODE_HIGH_QUALITY; 177 } 178 } 179 180 // Ugh, why is this stuff so slow? 181 private void InitializeAllTheThings() { 182 183 // Thread to handle returned JPEGs. 184 mJpegListenerThread = new HandlerThread("CameraJpegThread"); 185 mJpegListenerThread.start(); 186 mJpegListenerHandler = new Handler(mJpegListenerThread.getLooper()); 187 188 // Create ImageReader to receive JPEG image buffers via reprocessing. 189 mJpegImageReader = ImageReader.newInstance( 190 mCameraInfoCache.getYuvStream1Size().getWidth(), 191 mCameraInfoCache.getYuvStream1Size().getHeight(), 192 ImageFormat.JPEG, 193 2); 194 mJpegImageReader.setOnImageAvailableListener(mJpegImageListener, mJpegListenerHandler); 195 196 // Create ImageReader to receive YUV image buffers. 197 mYuv1ImageReader = ImageReader.newInstance( 198 mCameraInfoCache.getYuvStream1Size().getWidth(), 199 mCameraInfoCache.getYuvStream1Size().getHeight(), 200 ImageFormat.YUV_420_888, 201 YUV1_IMAGEREADER_SIZE); 202 mYuv1ImageReader.setOnImageAvailableListener(mYuv1ImageListener, mOpsHandler); 203 204 if (SECOND_YUV_IMAGEREADER_STREAM) { 205 // Create ImageReader to receive YUV image buffers. 206 mYuv2ImageReader = ImageReader.newInstance( 207 mCameraInfoCache.getYuvStream2Size().getWidth(), 208 mCameraInfoCache.getYuvStream2Size().getHeight(), 209 ImageFormat.YUV_420_888, 210 YUV2_IMAGEREADER_SIZE); 211 mYuv2ImageReader.setOnImageAvailableListener(mYuv2ImageListener, mOpsHandler); 212 } 213 214 if (SECOND_SURFACE_TEXTURE_STREAM) { 215 int[] textures = new int[1]; 216 // generate one texture pointer and bind it as an external texture. 217 GLES20.glGenTextures(1, textures, 0); 218 GLES20.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, textures[0]); 219 // No mip-mapping with camera source. 220 GLES20.glTexParameterf(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, 221 GL10.GL_TEXTURE_MIN_FILTER, 222 GL10.GL_LINEAR); 223 GLES20.glTexParameterf(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, 224 GL10.GL_TEXTURE_MAG_FILTER, GL10.GL_LINEAR); 225 // Clamp to edge is only option. 226 GLES20.glTexParameteri(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, 227 GL10.GL_TEXTURE_WRAP_S, GL10.GL_CLAMP_TO_EDGE); 228 GLES20.glTexParameteri(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, 229 GL10.GL_TEXTURE_WRAP_T, GL10.GL_CLAMP_TO_EDGE); 230 231 int texture_id = textures[0]; 232 mSurfaceTexture = new SurfaceTexture(texture_id); 233 mSurfaceTexture.setDefaultBufferSize(320, 240); 234 mSurfaceTexture.setOnFrameAvailableListener(this); 235 mSurfaceTextureSurface = new Surface(mSurfaceTexture); 236 } 237 238 if (RAW_STREAM_ENABLE && mCameraInfoCache.rawAvailable()) { 239 // Create ImageReader to receive thumbnail sized YUV image buffers. 240 mRawImageReader = ImageReader.newInstance( 241 mCameraInfoCache.getRawStreamSize().getWidth(), 242 mCameraInfoCache.getRawStreamSize().getHeight(), 243 mCameraInfoCache.getRawFormat(), 244 RAW_IMAGEREADER_SIZE); 245 mRawImageReader.setOnImageAvailableListener(mRawImageListener, mOpsHandler); 246 } 247 248 // Load click sound. 249 mMediaActionSound.load(MediaActionSound.SHUTTER_CLICK); 250 251 } 252 253 public void setCallback(MyCameraCallback callback) { 254 mMyCameraCallback = callback; 255 } 256 257 public void triggerAFScan() { 258 Log.v(TAG, "AF trigger"); 259 issuePreviewCaptureRequest(true); 260 } 261 262 public void setCAF() { 263 Log.v(TAG, "run CAF"); 264 issuePreviewCaptureRequest(false); 265 } 266 267 public void takePicture() { 268 mMediaActionSound.play(MediaActionSound.SHUTTER_CLICK); 269 mOpsHandler.post(new Runnable() { 270 @Override 271 public void run() { 272 runReprocessing(); 273 } 274 }); 275 } 276 277 public void onFrameAvailable (SurfaceTexture surfaceTexture) { 278 Log.v(TAG, " onFrameAvailable(SurfaceTexture)"); 279 } 280 281 public void setBurst(boolean go) { 282 // if false to true transition. 283 if (go && !mIsBursting) { 284 takePicture(); 285 } 286 mIsBursting = go; 287 } 288 289 public boolean isRawAvailable() { 290 return mCameraInfoCache.rawAvailable(); 291 } 292 293 public boolean isReprocessingAvailable() { 294 return mCameraInfoCache.isYuvReprocessingAvailable(); 295 } 296 297 @Override 298 public Size getPreviewSize() { 299 return mCameraInfoCache.getPreviewSize(); 300 } 301 302 @Override 303 public float[] getFieldOfView() { 304 return mCameraInfoCache.getFieldOfView(); 305 } 306 307 @Override 308 public int getOrientation() { 309 return mCameraInfoCache.sensorOrientation(); 310 } 311 312 @Override 313 public void openCamera() { 314 // If API2 FULL mode is not available, display toast 315 if (!mCameraInfoCache.isCamera2FullModeAvailable()) { 316 mMyCameraCallback.noCamera2Full(); 317 } 318 319 Log.v(TAG, "Opening camera " + mCameraInfoCache.getCameraId()); 320 mOpsHandler.post(new Runnable() { 321 @Override 322 public void run() { 323 CameraTimer.t_open_start = SystemClock.elapsedRealtime(); 324 try { 325 mCameraManager.openCamera(mCameraInfoCache.getCameraId(), mCameraStateCallback, null); 326 } catch (CameraAccessException e) { 327 Log.e(TAG, "Unable to openCamera()."); 328 } 329 } 330 }); 331 } 332 333 @Override 334 public void closeCamera() { 335 // TODO: We are stalling main thread now which is bad. 336 Log.v(TAG, "Closing camera " + mCameraInfoCache.getCameraId()); 337 if (mCameraDevice != null) { 338 try { 339 mCurrentCaptureSession.abortCaptures(); 340 } catch (CameraAccessException e) { 341 Log.e(TAG, "Could not abortCaptures()."); 342 } 343 mCameraDevice.close(); 344 } 345 mCurrentCaptureSession = null; 346 Log.v(TAG, "Done closing camera " + mCameraInfoCache.getCameraId()); 347 } 348 349 public void startPreview(final Surface surface) { 350 Log.v(TAG, "STARTUP_REQUIREMENT preview Surface ready."); 351 mPreviewSurface = surface; 352 tryToStartCaptureSession(); 353 } 354 355 private CameraDevice.StateCallback mCameraStateCallback = new LoggingCallbacks.DeviceStateCallback() { 356 @Override 357 public void onOpened(CameraDevice camera) { 358 CameraTimer.t_open_end = SystemClock.elapsedRealtime(); 359 mCameraDevice = camera; 360 Log.v(TAG, "STARTUP_REQUIREMENT Done opening camera " + mCameraInfoCache.getCameraId() + 361 ". HAL open took: (" + (CameraTimer.t_open_end - CameraTimer.t_open_start) + " ms)"); 362 363 super.onOpened(camera); 364 tryToStartCaptureSession(); 365 } 366 }; 367 368 private void tryToStartCaptureSession() { 369 if (mCameraDevice != null && mAllThingsInitialized && mPreviewSurface != null) { 370 mOpsHandler.post(new Runnable() { 371 @Override 372 public void run() { 373 // It used to be: this needed to be posted on a Handler. 374 startCaptureSession(); 375 } 376 }); 377 } 378 } 379 380 // Create CameraCaptureSession. Callback will start repeating request with current parameters. 381 private void startCaptureSession() { 382 CameraTimer.t_session_go = SystemClock.elapsedRealtime(); 383 384 Log.v(TAG, "Configuring session.."); 385 List<Surface> outputSurfaces = new ArrayList<Surface>(3); 386 387 outputSurfaces.add(mPreviewSurface); 388 Log.v(TAG, " .. added SurfaceView " + mCameraInfoCache.getPreviewSize().getWidth() + 389 " x " + mCameraInfoCache.getPreviewSize().getHeight()); 390 391 outputSurfaces.add(mYuv1ImageReader.getSurface()); 392 Log.v(TAG, " .. added YUV ImageReader " + mCameraInfoCache.getYuvStream1Size().getWidth() + 393 " x " + mCameraInfoCache.getYuvStream1Size().getHeight()); 394 395 if (SECOND_YUV_IMAGEREADER_STREAM) { 396 outputSurfaces.add(mYuv2ImageReader.getSurface()); 397 Log.v(TAG, " .. added YUV ImageReader " + mCameraInfoCache.getYuvStream2Size().getWidth() + 398 " x " + mCameraInfoCache.getYuvStream2Size().getHeight()); 399 } 400 401 if (SECOND_SURFACE_TEXTURE_STREAM) { 402 outputSurfaces.add(mSurfaceTextureSurface); 403 Log.v(TAG, " .. added SurfaceTexture"); 404 } 405 406 if (RAW_STREAM_ENABLE && mCameraInfoCache.rawAvailable()) { 407 outputSurfaces.add(mRawImageReader.getSurface()); 408 Log.v(TAG, " .. added Raw ImageReader " + mCameraInfoCache.getRawStreamSize().getWidth() + 409 " x " + mCameraInfoCache.getRawStreamSize().getHeight()); 410 } 411 412 if (USE_REPROCESSING_IF_AVAIL && mCameraInfoCache.isYuvReprocessingAvailable()) { 413 outputSurfaces.add(mJpegImageReader.getSurface()); 414 Log.v(TAG, " .. added JPEG ImageReader " + mCameraInfoCache.getJpegStreamSize().getWidth() + 415 " x " + mCameraInfoCache.getJpegStreamSize().getHeight()); 416 } 417 418 try { 419 if (USE_REPROCESSING_IF_AVAIL && mCameraInfoCache.isYuvReprocessingAvailable()) { 420 InputConfiguration inputConfig = new InputConfiguration(mCameraInfoCache.getYuvStream1Size().getWidth(), 421 mCameraInfoCache.getYuvStream1Size().getHeight(), ImageFormat.YUV_420_888); 422 mCameraDevice.createReprocessableCaptureSession(inputConfig, outputSurfaces, 423 mSessionStateCallback, null); 424 Log.v(TAG, " Call to createReprocessableCaptureSession complete."); 425 } else { 426 mCameraDevice.createCaptureSession(outputSurfaces, mSessionStateCallback, null); 427 Log.v(TAG, " Call to createCaptureSession complete."); 428 } 429 430 } catch (CameraAccessException e) { 431 Log.e(TAG, "Error configuring ISP."); 432 } 433 } 434 435 ImageWriter mImageWriter; 436 437 private CameraCaptureSession.StateCallback mSessionStateCallback = new LoggingCallbacks.SessionStateCallback() { 438 @Override 439 public void onReady(CameraCaptureSession session) { 440 Log.v(TAG, "capture session onReady(). HAL capture session took: (" + (SystemClock.elapsedRealtime() - CameraTimer.t_session_go) + " ms)"); 441 mCurrentCaptureSession = session; 442 issuePreviewCaptureRequest(false); 443 444 if (session.isReprocessable()) { 445 mImageWriter = ImageWriter.newInstance(session.getInputSurface(), IMAGEWRITER_SIZE); 446 mImageWriter.setOnImageReleasedListener( 447 new ImageWriter.OnImageReleasedListener() { 448 @Override 449 public void onImageReleased(ImageWriter writer) { 450 Log.v(TAG, "ImageWriter.OnImageReleasedListener onImageReleased()"); 451 } 452 }, null); 453 Log.v(TAG, "Created ImageWriter."); 454 } 455 super.onReady(session); 456 } 457 }; 458 459 // Variables to hold capture flow state. 460 private boolean mCaptureYuv1 = false; 461 private boolean mCaptureYuv2 = false; 462 private boolean mCaptureRaw = false; 463 private int mCaptureNoiseMode = CaptureRequest.NOISE_REDUCTION_MODE_FAST; 464 private int mCaptureEdgeMode = CaptureRequest.EDGE_MODE_FAST; 465 private boolean mCaptureFace = false; 466 // Variables to hold reprocessing state. 467 private int mReprocessingNoiseMode = CaptureRequest.NOISE_REDUCTION_MODE_HIGH_QUALITY; 468 private int mReprocessingEdgeMode = CaptureRequest.EDGE_MODE_HIGH_QUALITY; 469 470 public void setCaptureFlow(Boolean yuv1, Boolean yuv2, Boolean raw10, Boolean nr, Boolean edge, Boolean face) { 471 if (yuv1 != null) mCaptureYuv1 = yuv1; 472 if (yuv2 != null) mCaptureYuv2 = yuv2; 473 if (raw10 != null) mCaptureRaw = raw10 && RAW_STREAM_ENABLE; 474 if (nr) { 475 mCaptureNoiseMode = getNextMode(mCaptureNoiseMode, mCameraInfoCache.noiseModes); 476 } 477 if (edge) { 478 mCaptureEdgeMode = getNextMode(mCaptureEdgeMode, mCameraInfoCache.edgeModes); 479 } 480 if (face != null) mCaptureFace = face; 481 mMyCameraCallback.setNoiseEdgeText( 482 "NR " + noiseModeToString(mCaptureNoiseMode), 483 "Edge " + edgeModeToString(mCaptureEdgeMode) 484 ); 485 486 if (mCurrentCaptureSession != null) { 487 issuePreviewCaptureRequest(false); 488 } 489 } 490 491 public void setReprocessingFlow(Boolean nr, Boolean edge) { 492 if (nr) { 493 mReprocessingNoiseMode = getNextMode(mReprocessingNoiseMode, mCameraInfoCache.noiseModes); 494 } 495 if (edge) { 496 mReprocessingEdgeMode = getNextMode(mReprocessingEdgeMode, mCameraInfoCache.edgeModes); 497 } 498 mMyCameraCallback.setNoiseEdgeTextForReprocessing( 499 "NR " + noiseModeToString(mReprocessingNoiseMode), 500 "Edge " + edgeModeToString(mReprocessingEdgeMode) 501 ); 502 } 503 504 public void issuePreviewCaptureRequest(boolean AFtrigger) { 505 CameraTimer.t_burst = SystemClock.elapsedRealtime(); 506 Log.v(TAG, "issuePreviewCaptureRequest..."); 507 try { 508 CaptureRequest.Builder b1 = mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW); 509 b1.set(CaptureRequest.CONTROL_MODE, CameraMetadata.CONTROL_MODE_USE_SCENE_MODE); 510 b1.set(CaptureRequest.CONTROL_SCENE_MODE, CameraMetadata.CONTROL_SCENE_MODE_FACE_PRIORITY); 511 if (AFtrigger) { 512 b1.set(CaptureRequest.CONTROL_AF_MODE, CameraMetadata.CONTROL_AF_MODE_AUTO); 513 } else { 514 b1.set(CaptureRequest.CONTROL_AF_MODE, CameraMetadata.CONTROL_AF_MODE_CONTINUOUS_PICTURE); 515 } 516 517 b1.set(CaptureRequest.NOISE_REDUCTION_MODE, mCaptureNoiseMode); 518 b1.set(CaptureRequest.EDGE_MODE, mCaptureEdgeMode); 519 b1.set(CaptureRequest.STATISTICS_FACE_DETECT_MODE, mCaptureFace ? mCameraInfoCache.bestFaceDetectionMode() : CaptureRequest.STATISTICS_FACE_DETECT_MODE_OFF); 520 521 Log.v(TAG, " .. NR=" + mCaptureNoiseMode + " Edge=" + mCaptureEdgeMode + " Face=" + mCaptureFace); 522 523 if (mCaptureYuv1) { 524 b1.addTarget(mYuv1ImageReader.getSurface()); 525 Log.v(TAG, " .. YUV1 on"); 526 } 527 528 if (mCaptureRaw) { 529 b1.addTarget(mRawImageReader.getSurface()); 530 } 531 532 b1.addTarget(mPreviewSurface); 533 534 if (mCaptureYuv2) { 535 if (SECOND_SURFACE_TEXTURE_STREAM) { 536 b1.addTarget(mSurfaceTextureSurface); 537 } 538 if (SECOND_YUV_IMAGEREADER_STREAM) { 539 b1.addTarget(mYuv2ImageReader.getSurface()); 540 } 541 Log.v(TAG, " .. YUV2 on"); 542 } 543 544 if (AFtrigger) { 545 b1.set(CaptureRequest.CONTROL_AF_TRIGGER, CameraMetadata.CONTROL_AF_TRIGGER_START); 546 mCurrentCaptureSession.capture(b1.build(), mCaptureCallback, mOpsHandler); 547 b1.set(CaptureRequest.CONTROL_AF_TRIGGER, CameraMetadata.CONTROL_AF_TRIGGER_IDLE); 548 } 549 mCurrentCaptureSession.setRepeatingRequest(b1.build(), mCaptureCallback, mOpsHandler); 550 } catch (CameraAccessException e) { 551 Log.e(TAG, "Could not access camera for issuePreviewCaptureRequest."); 552 } 553 } 554 555 void runReprocessing() { 556 if (mYuv1LastReceivedImage == null) { 557 Log.e(TAG, "No YUV Image available."); 558 return; 559 } 560 mImageWriter.queueInputImage(mYuv1LastReceivedImage); 561 Log.v(TAG, " Sent YUV1 image to ImageWriter.queueInputImage()"); 562 try { 563 CaptureRequest.Builder b1 = mCameraDevice.createReprocessCaptureRequest(mLastTotalCaptureResult); 564 // Todo: Read current orientation instead of just assuming device is in native orientation 565 b1.set(CaptureRequest.JPEG_ORIENTATION, mCameraInfoCache.sensorOrientation()); 566 b1.set(CaptureRequest.JPEG_QUALITY, (byte) 95); 567 b1.set(CaptureRequest.NOISE_REDUCTION_MODE, mReprocessingNoiseMode); 568 b1.set(CaptureRequest.EDGE_MODE, mReprocessingEdgeMode); 569 b1.addTarget(mJpegImageReader.getSurface()); 570 mCurrentCaptureSession.capture(b1.build(), mReprocessingCaptureCallback, mOpsHandler); 571 mReprocessingRequestNanoTime = System.nanoTime(); 572 } catch (CameraAccessException e) { 573 Log.e(TAG, "Could not access camera for issuePreviewCaptureRequest."); 574 } 575 mYuv1LastReceivedImage = null; 576 Log.v(TAG, " Reprocessing request submitted."); 577 } 578 579 580 /********************************* 581 * onImageAvailable() processing * 582 *********************************/ 583 584 ImageReader.OnImageAvailableListener mYuv1ImageListener = 585 new ImageReader.OnImageAvailableListener() { 586 @Override 587 public void onImageAvailable(ImageReader reader) { 588 Image img = reader.acquireLatestImage(); 589 if (img == null) { 590 Log.e(TAG, "Null image returned YUV1"); 591 return; 592 } 593 if (mYuv1LastReceivedImage != null) { 594 mYuv1LastReceivedImage.close(); 595 } 596 mYuv1LastReceivedImage = img; 597 if (++mYuv1ImageCounter % LOG_NTH_FRAME == 0) { 598 Log.v(TAG, "YUV1 buffer available, Frame #=" + mYuv1ImageCounter + " w=" + img.getWidth() + " h=" + img.getHeight() + " time=" + img.getTimestamp()); 599 } 600 601 } 602 }; 603 604 605 ImageReader.OnImageAvailableListener mJpegImageListener = 606 new ImageReader.OnImageAvailableListener() { 607 @Override 608 public void onImageAvailable(ImageReader reader) { 609 Image img = reader.acquireLatestImage(); 610 if (img == null) { 611 Log.e(TAG, "Null image returned JPEG"); 612 return; 613 } 614 Image.Plane plane0 = img.getPlanes()[0]; 615 final ByteBuffer buffer = plane0.getBuffer(); 616 long dt = System.nanoTime() - mReprocessingRequestNanoTime; 617 Log.v(TAG, String.format("JPEG buffer available, w=%d h=%d time=%d size=%d dt=%.1f ms ISO=%d", 618 img.getWidth(), img.getHeight(), img.getTimestamp(), buffer.capacity(), 0.000001 * dt, mLastIso)); 619 // Save JPEG on the utility thread, 620 final byte[] jpegBuf; 621 if (buffer.hasArray()) { 622 jpegBuf = buffer.array(); 623 } else { 624 jpegBuf = new byte[buffer.capacity()]; 625 buffer.get(jpegBuf); 626 } 627 mMyCameraCallback.jpegAvailable(jpegBuf, img.getWidth(), img.getHeight()); 628 img.close(); 629 630 // take (reprocess) another picture right away if bursting. 631 if (mIsBursting) { 632 takePicture(); 633 } 634 } 635 }; 636 637 638 ImageReader.OnImageAvailableListener mYuv2ImageListener = 639 new ImageReader.OnImageAvailableListener() { 640 @Override 641 public void onImageAvailable(ImageReader reader) { 642 Image img = reader.acquireLatestImage(); 643 if (img == null) { 644 Log.e(TAG, "Null image returned YUV2"); 645 } else { 646 if (++mYuv2ImageCounter % LOG_NTH_FRAME == 0) { 647 Log.v(TAG, "YUV2 buffer available, Frame #=" + mYuv2ImageCounter + " w=" + img.getWidth() + " h=" + img.getHeight() + " time=" + img.getTimestamp()); 648 } 649 img.close(); 650 } 651 } 652 }; 653 654 655 ImageReader.OnImageAvailableListener mRawImageListener = 656 new ImageReader.OnImageAvailableListener() { 657 @Override 658 public void onImageAvailable(ImageReader reader) { 659 final Image img = reader.acquireLatestImage(); 660 if (img == null) { 661 Log.e(TAG, "Null image returned RAW"); 662 } else { 663 if (++mRawImageCounter % LOG_NTH_FRAME == 0) { 664 Image.Plane plane0 = img.getPlanes()[0]; 665 final ByteBuffer buffer = plane0.getBuffer(); 666 Log.v(TAG, "Raw buffer available, Frame #=" + mRawImageCounter + "w=" + img.getWidth() 667 + " h=" + img.getHeight() 668 + " format=" + CameraDeviceReport.getFormatName(img.getFormat()) 669 + " time=" + img.getTimestamp() 670 + " size=" + buffer.capacity() 671 + " getRowStride()=" + plane0.getRowStride()); 672 } 673 img.close(); 674 } 675 } 676 }; 677 678 /************************************* 679 * CaptureResult metadata processing * 680 *************************************/ 681 682 private CameraCaptureSession.CaptureCallback mCaptureCallback = new LoggingCallbacks.SessionCaptureCallback() { 683 @Override 684 public void onCaptureCompleted(CameraCaptureSession session, CaptureRequest request, TotalCaptureResult result) { 685 if (!mFirstFrameArrived) { 686 mFirstFrameArrived = true; 687 long now = SystemClock.elapsedRealtime(); 688 long dt = now - CameraTimer.t0; 689 long camera_dt = now - CameraTimer.t_session_go + CameraTimer.t_open_end - CameraTimer.t_open_start; 690 long repeating_req_dt = now - CameraTimer.t_burst; 691 Log.v(TAG, "App control to first frame: (" + dt + " ms)"); 692 Log.v(TAG, "HAL request to first frame: (" + repeating_req_dt + " ms) " + " Total HAL wait: (" + camera_dt + " ms)"); 693 mMyCameraCallback.receivedFirstFrame(); 694 mMyCameraCallback.performanceDataAvailable((int) dt, (int) camera_dt, null); 695 } 696 publishFrameData(result); 697 // Used for reprocessing. 698 mLastTotalCaptureResult = result; 699 super.onCaptureCompleted(session, request, result); 700 } 701 }; 702 703 // Reprocessing capture completed. 704 private CameraCaptureSession.CaptureCallback mReprocessingCaptureCallback = new LoggingCallbacks.SessionCaptureCallback() { 705 @Override 706 public void onCaptureCompleted(CameraCaptureSession session, CaptureRequest request, TotalCaptureResult result) { 707 Log.v(TAG, "Reprocessing onCaptureCompleted()"); 708 } 709 }; 710 711 private static double SHORT_LOG_EXPOSURE = Math.log10(1000000000 / 10000); // 1/10000 second 712 private static double LONG_LOG_EXPOSURE = Math.log10(1000000000 / 10); // 1/10 second 713 public int FPS_CALC_LOOKBACK = 15; 714 private LinkedList<Long> mFrameTimes = new LinkedList<Long>(); 715 716 private void publishFrameData(TotalCaptureResult result) { 717 // Faces. 718 final Face[] faces = result.get(CaptureResult.STATISTICS_FACES); 719 NormalizedFace[] newFaces = new NormalizedFace[faces.length]; 720 if (faces.length > 0) { 721 int offX = mCameraInfoCache.faceOffsetX(); 722 int offY = mCameraInfoCache.faceOffsetY(); 723 int dX = mCameraInfoCache.activeAreaWidth() - 2 * offX; 724 int dY = mCameraInfoCache.activeAreaHeight() - 2 * offY; 725 if (mCameraInfoCache.IS_NEXUS_6 && mCameraIsFront) { 726 // Front camera on Nexus 6 is currently 16 x 9 cropped to 4 x 3. 727 // TODO: Generalize this. 728 int cropOffset = dX / 8; 729 dX -= 2 * cropOffset; 730 offX += cropOffset; 731 } 732 int orientation = mCameraInfoCache.sensorOrientation(); 733 for (int i = 0; i < faces.length; ++i) { 734 newFaces[i] = new NormalizedFace(faces[i], dX, dY, offX, offY); 735 if (mCameraIsFront && orientation == 90) { 736 newFaces[i].mirrorInY(); 737 } 738 if (mCameraIsFront && orientation == 270) { 739 newFaces[i].mirrorInX(); 740 } 741 if (!mCameraIsFront && orientation == 270) { 742 newFaces[i].mirrorInX(); 743 newFaces[i].mirrorInY(); 744 } 745 } 746 } 747 748 // Normalized lens and exposure coordinates. 749 double rm = Math.log10(result.get(CaptureResult.SENSOR_EXPOSURE_TIME)); 750 float normExposure = (float) ((rm - SHORT_LOG_EXPOSURE) / (LONG_LOG_EXPOSURE - SHORT_LOG_EXPOSURE)); 751 float normLensPos = (mCameraInfoCache.getDiopterHi() - result.get(CaptureResult.LENS_FOCUS_DISTANCE)) / (mCameraInfoCache.getDiopterHi() - mCameraInfoCache.getDiopterLow()); 752 mLastIso = result.get(CaptureResult.SENSOR_SENSITIVITY); 753 754 // Update frame arrival history. 755 mFrameTimes.add(result.get(CaptureResult.SENSOR_TIMESTAMP)); 756 if (mFrameTimes.size() > FPS_CALC_LOOKBACK) { 757 mFrameTimes.removeFirst(); 758 } 759 760 // Frame drop detector 761 { 762 float frameDuration = result.get(CaptureResult.SENSOR_FRAME_DURATION); 763 if (mFrameTimes.size() > 1) { 764 long dt = result.get(CaptureResult.SENSOR_TIMESTAMP) - mFrameTimes.get(mFrameTimes.size()-2); 765 if (dt > 3 * frameDuration / 2 && LOG_DROPPED_FRAMES) { 766 float drops = (dt * 1f / frameDuration) - 1f; 767 Log.e(TAG, String.format("dropped %.2f frames", drops)); 768 mMyCameraCallback.performanceDataAvailable(null, null, drops); 769 } 770 } 771 } 772 773 // FPS calc. 774 float fps = 0; 775 if (mFrameTimes.size() > 1) { 776 long dt = mFrameTimes.getLast() - mFrameTimes.getFirst(); 777 fps = (mFrameTimes.size() - 1) * 1000000000f / dt; 778 fps = (float) Math.floor(fps + 0.1); // round to nearest whole number, ish. 779 } 780 781 // Do callback. 782 if (mMyCameraCallback != null) { 783 mMyCameraCallback.frameDataAvailable(newFaces, normExposure, normLensPos, fps, 784 (int) mLastIso, result.get(CaptureResult.CONTROL_AF_STATE), result.get(CaptureResult.CONTROL_AE_STATE), result.get(CaptureResult.CONTROL_AWB_STATE)); 785 } else { 786 Log.v(TAG, "mMyCameraCallbacks is null!!."); 787 } 788 } 789 790 long mLastIso = 0; 791 792 /********************* 793 * UTILITY FUNCTIONS * 794 *********************/ 795 796 /** 797 * Return the next mode after currentMode in supportedModes, wrapping to 798 * start of mode list if currentMode is last. Returns currentMode if it is not found in 799 * supportedModes. 800 * 801 * @param currentMode 802 * @param supportedModes 803 * @return next mode after currentMode in supportedModes 804 */ 805 private int getNextMode(int currentMode, int[] supportedModes) { 806 boolean getNext = false; 807 for (int m : supportedModes) { 808 if (getNext) { 809 return m; 810 } 811 if (m == currentMode) { 812 getNext = true; 813 } 814 } 815 if (getNext) { 816 return supportedModes[0]; 817 } 818 // Can't find mode in list 819 return currentMode; 820 } 821 822 private static String edgeModeToString(int mode) { 823 switch (mode) { 824 case CaptureRequest.EDGE_MODE_OFF: 825 return "OFF"; 826 case CaptureRequest.EDGE_MODE_FAST: 827 return "FAST"; 828 case CaptureRequest.EDGE_MODE_HIGH_QUALITY: 829 return "HiQ"; 830 case CaptureRequest.EDGE_MODE_ZERO_SHUTTER_LAG: 831 return "ZSL"; 832 } 833 return Integer.toString(mode); 834 } 835 836 private static String noiseModeToString(int mode) { 837 switch (mode) { 838 case CaptureRequest.NOISE_REDUCTION_MODE_OFF: 839 return "OFF"; 840 case CaptureRequest.NOISE_REDUCTION_MODE_FAST: 841 return "FAST"; 842 case CaptureRequest.NOISE_REDUCTION_MODE_HIGH_QUALITY: 843 return "HiQ"; 844 case CaptureRequest.NOISE_REDUCTION_MODE_MINIMAL: 845 return "MIN"; 846 case CaptureRequest.NOISE_REDUCTION_MODE_ZERO_SHUTTER_LAG: 847 return "ZSL"; 848 } 849 return Integer.toString(mode); 850 } 851 } 852