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