ItsService.java revision d59e4becb0168a32f2d829cb7c26433bf3616855
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.camera2.its;
18
19import android.app.Service;
20import android.content.Context;
21import android.content.Intent;
22import android.graphics.ImageFormat;
23import android.hardware.camera2.CameraAccessException;
24import android.hardware.camera2.CameraDevice;
25import android.hardware.camera2.CameraManager;
26import android.hardware.camera2.CameraProperties;
27import android.hardware.camera2.CaptureRequest;
28import android.hardware.camera2.CaptureResult;
29import android.hardware.camera2.Rational;
30import android.media.Image;
31import android.media.ImageReader;
32import android.net.Uri;
33import android.os.ConditionVariable;
34import android.os.Handler;
35import android.os.HandlerThread;
36import android.os.IBinder;
37import android.os.Message;
38import android.util.Log;
39import android.view.Surface;
40
41import com.android.ex.camera2.blocking.BlockingCameraManager;
42import com.android.ex.camera2.blocking.BlockingCameraManager.BlockingOpenException;
43
44import org.json.JSONObject;
45
46import java.io.File;
47import java.nio.ByteBuffer;
48import java.util.ArrayList;
49import java.util.Arrays;
50import java.util.List;
51import java.util.concurrent.CountDownLatch;
52import java.util.concurrent.TimeUnit;
53
54public class ItsService extends Service {
55    public static final String TAG = ItsService.class.getSimpleName();
56    public static final String PYTAG = "CAMERA-ITS-PY";
57
58    // Supported intents
59    public static final String ACTION_CAPTURE = "com.android.camera2.its.CAPTURE";
60    public static final String ACTION_3A = "com.android.camera2.its.3A";
61    public static final String ACTION_GETPROPS = "com.android.camera2.its.GETPROPS";
62    private static final int MESSAGE_CAPTURE = 1;
63    private static final int MESSAGE_3A = 2;
64    private static final int MESSAGE_GETPROPS = 3;
65
66    // Timeouts, in seconds.
67    public static final int TIMEOUT_CAPTURE = 10;
68    public static final int TIMEOUT_3A = 10;
69
70    private static final int MAX_CONCURRENT_READER_BUFFERS = 8;
71
72    public static final String REGION_KEY = "regions";
73    public static final String REGION_AE_KEY = "ae";
74    public static final String REGION_AWB_KEY = "awb";
75    public static final String REGION_AF_KEY = "af";
76    public static final String TRIGGER_KEY = "triggers";
77    public static final String TRIGGER_AE_KEY = "ae";
78    public static final String TRIGGER_AF_KEY = "af";
79
80    private CameraManager mCameraManager = null;
81    private BlockingCameraManager mBlockingCameraManager = null;
82    private CameraDevice mCamera = null;
83    private ImageReader mCaptureReader = null;
84    private CameraProperties mCameraProperties = null;
85
86    private HandlerThread mCommandThread;
87    private Handler mCommandHandler;
88    private HandlerThread mSaveThread;
89    private Handler mSaveHandler;
90    private HandlerThread mResultThread;
91    private Handler mResultHandler;
92
93    private ConditionVariable mInterlock3A = new ConditionVariable(true);
94    private volatile boolean mIssuedRequest3A = false;
95    private volatile boolean mConvergedAE = false;
96    private volatile boolean mConvergedAF = false;
97    private volatile boolean mConvergedAWB = false;
98
99    private CountDownLatch mCaptureCallbackLatch;
100
101    public interface CaptureListener {
102        void onCaptureAvailable(Image capture);
103    }
104
105    public abstract class CaptureResultListener extends CameraDevice.CaptureListener {}
106
107    @Override
108    public IBinder onBind(Intent intent) {
109        return null;
110    }
111
112    @Override
113    public void onCreate() {
114
115        try {
116            // Get handle to camera manager.
117            mCameraManager = (CameraManager) this.getSystemService(Context.CAMERA_SERVICE);
118            if (mCameraManager == null) {
119                throw new ItsException("Failed to connect to camera manager");
120            }
121            mBlockingCameraManager = new BlockingCameraManager(mCameraManager);
122
123            // Open the camera device, and get its properties.
124            String[] devices;
125            try {
126                devices = mCameraManager.getCameraIdList();
127                if (devices == null || devices.length == 0) {
128                    throw new ItsException("No camera devices");
129                }
130            } catch (CameraAccessException e) {
131                throw new ItsException("Failed to get device ID list", e);
132            }
133
134            HandlerThread openThread = new HandlerThread("OpenThread");
135            try {
136                openThread.start();
137                Handler openHandler = new Handler(openThread.getLooper());
138
139                // TODO: Add support for specifying which device to open.
140                mCamera = mBlockingCameraManager.openCamera(devices[0], /*listener*/null,
141                        openHandler);
142                mCameraProperties = mCamera.getProperties();
143            } catch (CameraAccessException e) {
144                throw new ItsException("Failed to open camera", e);
145            } catch (BlockingOpenException e) {
146                throw new ItsException("Failed to open camera (after blocking)", e);
147            } finally {
148                /**
149                 * OK to shut down thread immediately after #openCamera since there is no listener.
150                 * If listener ever becomes non-null then handler's thread must be valid for
151                 * the full lifetime of the listener.
152                 */
153                openThread.quitSafely();
154            }
155
156            // Create a thread to receive images and save them.
157            mSaveThread = new HandlerThread("SaveThread");
158            mSaveThread.start();
159            mSaveHandler = new Handler(mSaveThread.getLooper());
160
161            // Create a thread to receive capture results and process them
162            mResultThread = new HandlerThread("ResultThread");
163            mResultThread.start();
164            mResultHandler = new Handler(mResultThread.getLooper());
165
166            // Create a thread to process commands.
167            mCommandThread = new HandlerThread("CaptureThread");
168            mCommandThread.start();
169            mCommandHandler = new Handler(mCommandThread.getLooper(), new Handler.Callback() {
170                @Override
171                public boolean handleMessage(Message msg) {
172                    try {
173                        switch (msg.what) {
174                            case MESSAGE_CAPTURE:
175                                doCapture((Uri) msg.obj);
176                                break;
177                            case MESSAGE_3A:
178                                do3A((Uri) msg.obj);
179                                break;
180                            case MESSAGE_GETPROPS:
181                                doGetProps();
182                                break;
183                            default:
184                                throw new ItsException("Unknown message type");
185                        }
186                        Log.i(PYTAG, "### DONE");
187                        return true;
188                    }
189                    catch (ItsException e) {
190                        Log.e(TAG, "Script failed: ", e);
191                        Log.e(PYTAG, "### FAIL");
192                        return true;
193                    }
194                }
195            });
196        } catch (ItsException e) {
197            Log.e(TAG, "Script failed: ", e);
198            Log.e(PYTAG, "### FAIL");
199        }
200    }
201
202    @Override
203    public void onDestroy() {
204        try {
205            if (mCommandThread != null) {
206                mCommandThread.quit();
207                mCommandThread = null;
208            }
209            if (mSaveThread != null) {
210                mSaveThread.quit();
211                mSaveThread = null;
212            }
213
214            try {
215                mCamera.close();
216            } catch (Exception e) {
217                throw new ItsException("Failed to close device");
218            }
219        } catch (ItsException e) {
220            Log.e(TAG, "Script failed: ", e);
221            Log.e(PYTAG, "### FAIL");
222        }
223    }
224
225    @Override
226    public int onStartCommand(Intent intent, int flags, int startId) {
227        try {
228            Log.i(PYTAG, "### RECV");
229            String action = intent.getAction();
230            if (ACTION_CAPTURE.equals(action)) {
231                Uri uri = intent.getData();
232                Message m = mCommandHandler.obtainMessage(MESSAGE_CAPTURE, uri);
233                mCommandHandler.sendMessage(m);
234            } else if (ACTION_3A.equals(action)) {
235                Uri uri = intent.getData();
236                Message m = mCommandHandler.obtainMessage(MESSAGE_3A, uri);
237                mCommandHandler.sendMessage(m);
238            } else if (ACTION_GETPROPS.equals(action)) {
239                Uri uri = intent.getData();
240                Message m = mCommandHandler.obtainMessage(MESSAGE_GETPROPS, uri);
241                mCommandHandler.sendMessage(m);
242            } else {
243                throw new ItsException("Unhandled intent: " + intent.toString());
244            }
245        } catch (ItsException e) {
246            Log.e(TAG, "Script failed: ", e);
247            Log.e(PYTAG, "### FAIL");
248        }
249        return START_STICKY;
250    }
251
252    public void idleCamera() throws ItsException {
253        try {
254            mCamera.stopRepeating();
255            mCamera.waitUntilIdle();
256        } catch (CameraAccessException e) {
257            throw new ItsException("Error waiting for camera idle", e);
258        }
259    }
260
261    private ImageReader.OnImageAvailableListener
262            createAvailableListener(final CaptureListener listener) {
263        return new ImageReader.OnImageAvailableListener() {
264            @Override
265            public void onImageAvailable(ImageReader reader) {
266                Image i = null;
267                try {
268                    i = reader.acquireNextImage();
269                    listener.onCaptureAvailable(i);
270                } finally {
271                    if (i != null) {
272                        i.close();
273                    }
274                }
275            }
276        };
277    }
278
279    private ImageReader.OnImageAvailableListener
280            createAvailableListenerDropper(final CaptureListener listener) {
281        return new ImageReader.OnImageAvailableListener() {
282            @Override
283            public void onImageAvailable(ImageReader reader) {
284                Image i = reader.acquireNextImage();
285                i.close();
286            }
287        };
288    }
289
290    private void doGetProps() throws ItsException {
291        String fileName = ItsUtils.getMetadataFileName(0);
292        File mdFile = ItsUtils.getOutputFile(ItsService.this, fileName);
293        ItsUtils.storeCameraProperties(mCameraProperties, mdFile);
294        Log.i(PYTAG,
295              String.format("### FILE %s",
296                            ItsUtils.getExternallyVisiblePath(ItsService.this, mdFile.toString())));
297    }
298
299    private void prepareCaptureReader(int width, int height, int format) {
300        if (mCaptureReader == null
301                || mCaptureReader.getWidth() != width
302                || mCaptureReader.getHeight() != height
303                || mCaptureReader.getImageFormat() != format) {
304            if (mCaptureReader != null) {
305                mCaptureReader.close();
306            }
307            mCaptureReader = ImageReader.newInstance(width, height, format,
308                    MAX_CONCURRENT_READER_BUFFERS);
309        }
310    }
311
312    private void do3A(Uri uri) throws ItsException {
313        try {
314            if (uri == null || !uri.toString().endsWith(".json")) {
315                throw new ItsException("Invalid URI: " + uri);
316            }
317
318            idleCamera();
319
320            // Start a 3A action, and wait for it to converge.
321            // Get the converged values for each "A", and package into JSON result for caller.
322
323            // 3A happens on full-res frames.
324            android.hardware.camera2.Size sizes[] = mCameraProperties.get(
325                    CameraProperties.SCALER_AVAILABLE_JPEG_SIZES);
326            int width = sizes[0].getWidth();
327            int height = sizes[0].getHeight();
328            int format = ImageFormat.YUV_420_888;
329
330            prepareCaptureReader(width, height, format);
331            List<Surface> outputSurfaces = new ArrayList<Surface>(1);
332            outputSurfaces.add(mCaptureReader.getSurface());
333            mCamera.configureOutputs(outputSurfaces);
334
335            // Add a listener that just recycles buffers; they aren't saved anywhere.
336            ImageReader.OnImageAvailableListener readerListener =
337                    createAvailableListenerDropper(mCaptureListener);
338            mCaptureReader.setOnImageAvailableListener(readerListener, mSaveHandler);
339
340            // Get the user-specified regions for AE, AWB, AF.
341            // Note that the user specifies normalized [x,y,w,h], which is converted below
342            // to an [x0,y0,x1,y1] region in sensor coords. The capture request region
343            // also has a fifth "weight" element: [x0,y0,x1,y1,w].
344            int[] regionAE = new int[]{0,0,width-1,height-1,1};
345            int[] regionAF = new int[]{0,0,width-1,height-1,1};
346            int[] regionAWB = new int[]{0,0,width-1,height-1,1};
347            JSONObject params = ItsUtils.loadJsonFile(uri);
348            if (params.has(REGION_KEY)) {
349                JSONObject regions = params.getJSONObject(REGION_KEY);
350                if (regions.has(REGION_AE_KEY)) {
351                    int[] r = ItsUtils.getJsonRectFromArray(
352                            regions.getJSONArray(REGION_AE_KEY), true, width, height);
353                    regionAE = new int[]{r[0],r[1],r[0]+r[2]-1,r[1]+r[3]-1,1};
354                }
355                if (regions.has(REGION_AF_KEY)) {
356                    int[] r = ItsUtils.getJsonRectFromArray(
357                            regions.getJSONArray(REGION_AF_KEY), true, width, height);
358                    regionAF = new int[]{r[0],r[1],r[0]+r[2]-1,r[1]+r[3]-1,1};
359                }
360                if (regions.has(REGION_AWB_KEY)) {
361                    int[] r = ItsUtils.getJsonRectFromArray(
362                            regions.getJSONArray(REGION_AWB_KEY), true, width, height);
363                    regionAWB = new int[]{r[0],r[1],r[0]+r[2]-1,r[1]+r[3]-1,1};
364                }
365            }
366            Log.i(TAG, "AE region: " + Arrays.toString(regionAE));
367            Log.i(TAG, "AF region: " + Arrays.toString(regionAF));
368            Log.i(TAG, "AWB region: " + Arrays.toString(regionAWB));
369
370            // By default, AE and AF both get triggered, but the user can optionally override this.
371            boolean doAE = true;
372            boolean doAF = true;
373            if (params.has(TRIGGER_KEY)) {
374                JSONObject triggers = params.getJSONObject(TRIGGER_KEY);
375                if (triggers.has(TRIGGER_AE_KEY)) {
376                    doAE = triggers.getBoolean(TRIGGER_AE_KEY);
377                }
378                if (triggers.has(TRIGGER_AF_KEY)) {
379                    doAF = triggers.getBoolean(TRIGGER_AF_KEY);
380                }
381            }
382
383            mInterlock3A.open();
384            mIssuedRequest3A = false;
385            mConvergedAE = false;
386            mConvergedAWB = false;
387            mConvergedAF = false;
388            long tstart = System.currentTimeMillis();
389            boolean triggeredAE = false;
390            boolean triggeredAF = false;
391
392            // Keep issuing capture requests until 3A has converged.
393            // First do AE, then do AF and AWB together.
394            while (true) {
395
396                // Block until can take the next 3A frame. Only want one outstanding frame
397                // at a time, to simplify the logic here.
398                if (!mInterlock3A.block(TIMEOUT_3A * 1000) ||
399                        System.currentTimeMillis() - tstart > TIMEOUT_3A * 1000) {
400                    throw new ItsException("3A failed to converge (timeout)");
401                }
402                mInterlock3A.close();
403
404                // If not converged yet, issue another capture request.
405                if ((doAE && !mConvergedAE) || !mConvergedAWB || (doAF && !mConvergedAF)) {
406
407                    // Baseline capture request for 3A.
408                    CaptureRequest.Builder req = mCamera.createCaptureRequest(
409                            CameraDevice.TEMPLATE_PREVIEW);
410                    req.set(CaptureRequest.FLASH_MODE, CaptureRequest.FLASH_MODE_OFF);
411                    req.set(CaptureRequest.CONTROL_MODE, CaptureRequest.CONTROL_MODE_AUTO);
412                    req.set(CaptureRequest.CONTROL_CAPTURE_INTENT,
413                            CaptureRequest.CONTROL_CAPTURE_INTENT_PREVIEW);
414                    req.set(CaptureRequest.CONTROL_AE_MODE,
415                            CaptureRequest.CONTROL_AE_MODE_ON);
416                    req.set(CaptureRequest.CONTROL_AE_EXPOSURE_COMPENSATION, 0);
417                    req.set(CaptureRequest.CONTROL_AE_LOCK, false);
418                    req.set(CaptureRequest.CONTROL_AE_REGIONS, regionAE);
419                    req.set(CaptureRequest.CONTROL_AF_MODE,
420                            CaptureRequest.CONTROL_AF_MODE_AUTO);
421                    req.set(CaptureRequest.CONTROL_AF_REGIONS, regionAF);
422                    req.set(CaptureRequest.CONTROL_AWB_MODE,
423                            CaptureRequest.CONTROL_AWB_MODE_AUTO);
424                    req.set(CaptureRequest.CONTROL_AWB_LOCK, false);
425                    req.set(CaptureRequest.CONTROL_AWB_REGIONS, regionAWB);
426
427                    // Trigger AE first.
428                    if (doAE && !triggeredAE) {
429                        Log.i(TAG, "Triggering AE");
430                        req.set(CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER,
431                                CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER_START);
432                        triggeredAE = true;
433                    }
434
435                    // After AE has converged, trigger AF.
436                    if (doAF && !triggeredAF && (!doAE || (triggeredAE && mConvergedAE))) {
437                        Log.i(TAG, "Triggering AF");
438                        req.set(CaptureRequest.CONTROL_AF_TRIGGER,
439                                CaptureRequest.CONTROL_AF_TRIGGER_START);
440                        triggeredAF = true;
441                    }
442
443                    req.addTarget(mCaptureReader.getSurface());
444
445                    mIssuedRequest3A = true;
446                    mCamera.capture(req.build(), mCaptureResultListener, mResultHandler);
447                } else {
448                    Log.i(TAG, "3A converged");
449                    break;
450                }
451            }
452        } catch (android.hardware.camera2.CameraAccessException e) {
453            throw new ItsException("Access error: ", e);
454        } catch (org.json.JSONException e) {
455            throw new ItsException("JSON error: ", e);
456        }
457    }
458
459    private void doCapture(Uri uri) throws ItsException {
460        try {
461            if (uri == null || !uri.toString().endsWith(".json")) {
462                throw new ItsException("Invalid URI: " + uri);
463            }
464
465            idleCamera();
466
467            // Parse the JSON to get the list of capture requests.
468            List<CaptureRequest.Builder> requests = ItsUtils.loadRequestList(mCamera, uri);
469
470            // Set the output surface and listeners.
471            try {
472                // Default:
473                // Capture full-frame images. Use the reported JPEG size rather than the sensor
474                // size since this is more likely to be the unscaled size; the crop from sensor
475                // size is probably for the ISP (e.g. demosaicking) rather than the encoder.
476                android.hardware.camera2.Size sizes[] = mCameraProperties.get(
477                        CameraProperties.SCALER_AVAILABLE_JPEG_SIZES);
478                int width = sizes[0].getWidth();
479                int height = sizes[0].getHeight();
480                int format = ImageFormat.YUV_420_888;
481
482                JSONObject jsonOutputSpecs = ItsUtils.getOutputSpecs(uri);
483                if (jsonOutputSpecs != null) {
484                    // Use the user's JSON capture spec.
485                    int width2 = jsonOutputSpecs.optInt("width");
486                    int height2 = jsonOutputSpecs.optInt("height");
487                    if (width2 > 0) {
488                        width = width2;
489                    }
490                    if (height2 > 0) {
491                        height = height2;
492                    }
493                    String sformat = jsonOutputSpecs.optString("format");
494                    if ("yuv".equals(sformat)) {
495                        format = ImageFormat.YUV_420_888;
496                    } else if ("jpg".equals(sformat) || "jpeg".equals(sformat)) {
497                        format = ImageFormat.JPEG;
498                    } else if ("".equals(sformat)) {
499                        // No format specified.
500                    } else {
501                        throw new ItsException("Unsupported format: " + sformat);
502                    }
503                }
504
505                Log.i(PYTAG, String.format("### SIZE %d %d", width, height));
506
507                prepareCaptureReader(width, height, format);
508                List<Surface> outputSurfaces = new ArrayList<Surface>(1);
509                outputSurfaces.add(mCaptureReader.getSurface());
510                mCamera.configureOutputs(outputSurfaces);
511
512                ImageReader.OnImageAvailableListener readerListener =
513                        createAvailableListener(mCaptureListener);
514                mCaptureReader.setOnImageAvailableListener(readerListener, mSaveHandler);
515
516                // Plan for how many callbacks need to be received throughout the duration of this
517                // sequence of capture requests.
518                int numCaptures = requests.size();
519                mCaptureCallbackLatch = new CountDownLatch(
520                        numCaptures * ItsUtils.getCallbacksPerCapture(format));
521
522            } catch (CameraAccessException e) {
523                throw new ItsException("Error configuring outputs", e);
524            }
525
526            // Initiate the captures.
527            for (int i = 0; i < requests.size(); i++) {
528                CaptureRequest.Builder req = requests.get(i);
529                Log.i(PYTAG, String.format("### CAPT %d of %d", i+1, requests.size()));
530                req.addTarget(mCaptureReader.getSurface());
531                mCamera.capture(req.build(), mCaptureResultListener, mResultHandler);
532            }
533
534            // Make sure all callbacks have been hit (wait until captures are done).
535            try {
536                if (!mCaptureCallbackLatch.await(TIMEOUT_CAPTURE, TimeUnit.SECONDS)) {
537                    throw new ItsException(
538                            "Timeout hit, but all callbacks not received");
539                }
540            } catch (InterruptedException e) {
541                throw new ItsException("Interrupted: ", e);
542            }
543
544        } catch (android.hardware.camera2.CameraAccessException e) {
545            throw new ItsException("Access error: ", e);
546        }
547    }
548
549    private final CaptureListener mCaptureListener = new CaptureListener() {
550        @Override
551        public void onCaptureAvailable(Image capture) {
552            try {
553                int format = capture.getFormat();
554                String extFileName = null;
555                if (format == ImageFormat.JPEG) {
556                    String fileName = ItsUtils.getJpegFileName(capture.getTimestamp());
557                    ByteBuffer buf = capture.getPlanes()[0].getBuffer();
558                    extFileName = ItsUtils.writeImageToFile(ItsService.this, buf, fileName);
559                } else if (format == ImageFormat.YUV_420_888) {
560                    String fileName = ItsUtils.getYuvFileName(capture.getTimestamp());
561                    byte[] img = ItsUtils.getDataFromImage(capture);
562                    ByteBuffer buf = ByteBuffer.wrap(img);
563                    extFileName = ItsUtils.writeImageToFile(ItsService.this, buf, fileName);
564                } else {
565                    throw new ItsException("Unsupported image format: " + format);
566                }
567                Log.i(PYTAG, String.format("### FILE %s", extFileName));
568                mCaptureCallbackLatch.countDown();
569            } catch (ItsException e) {
570                Log.e(TAG, "Script error: " + e);
571                Log.e(PYTAG, "### FAIL");
572            }
573        }
574    };
575
576    private static float r2f(Rational r) {
577        return (float)r.getNumerator() / (float)r.getDenominator();
578    }
579
580    private final CaptureResultListener mCaptureResultListener = new CaptureResultListener() {
581        @Override
582        public void onCaptureStarted(CameraDevice camera, CaptureRequest request, long timestamp) {
583        }
584
585        @Override
586        public void onCaptureCompleted(CameraDevice camera, CaptureRequest request,
587                CaptureResult result) {
588            try {
589                // Currently result has all 0 values.
590                if (request == null || result == null) {
591                    throw new ItsException("Request/result is invalid");
592                }
593
594                StringBuilder logMsg = new StringBuilder();
595                logMsg.append(String.format(
596                        "Capt result: AE=%d, AF=%d, AWB=%d, sens=%d, exp=%.1fms, dur=%.1fms, ",
597                        result.get(CaptureResult.CONTROL_AE_STATE),
598                        result.get(CaptureResult.CONTROL_AF_STATE),
599                        result.get(CaptureResult.CONTROL_AWB_STATE),
600                        result.get(CaptureResult.SENSOR_SENSITIVITY),
601                        result.get(CaptureResult.SENSOR_EXPOSURE_TIME).intValue() / 1000000.0f,
602                        result.get(CaptureResult.SENSOR_FRAME_DURATION).intValue() / 1000000.0f));
603                if (result.get(CaptureResult.COLOR_CORRECTION_GAINS) != null) {
604                    logMsg.append(String.format(
605                            "gains=[%.1f, %.1f, %.1f, %.1f], ",
606                            result.get(CaptureResult.COLOR_CORRECTION_GAINS)[0],
607                            result.get(CaptureResult.COLOR_CORRECTION_GAINS)[1],
608                            result.get(CaptureResult.COLOR_CORRECTION_GAINS)[2],
609                            result.get(CaptureResult.COLOR_CORRECTION_GAINS)[3]));
610                } else {
611                    logMsg.append("gains=[], ");
612                }
613                if (result.get(CaptureResult.COLOR_CORRECTION_TRANSFORM) != null) {
614                    logMsg.append(String.format(
615                            "xform=[%.1f, %.1f, %.1f, %.1f, %.1f, %.1f, %.1f, %.1f, %.1f], ",
616                             r2f(result.get(CaptureResult.COLOR_CORRECTION_TRANSFORM)[0]),
617                             r2f(result.get(CaptureResult.COLOR_CORRECTION_TRANSFORM)[1]),
618                             r2f(result.get(CaptureResult.COLOR_CORRECTION_TRANSFORM)[2]),
619                             r2f(result.get(CaptureResult.COLOR_CORRECTION_TRANSFORM)[3]),
620                             r2f(result.get(CaptureResult.COLOR_CORRECTION_TRANSFORM)[4]),
621                             r2f(result.get(CaptureResult.COLOR_CORRECTION_TRANSFORM)[5]),
622                             r2f(result.get(CaptureResult.COLOR_CORRECTION_TRANSFORM)[6]),
623                             r2f(result.get(CaptureResult.COLOR_CORRECTION_TRANSFORM)[7]),
624                             r2f(result.get(CaptureResult.COLOR_CORRECTION_TRANSFORM)[8])));
625                } else {
626                    logMsg.append("xform=[], ");
627                }
628                logMsg.append(String.format(
629                        "foc=%.1f",
630                        result.get(CaptureResult.LENS_FOCUS_DISTANCE)));
631                Log.i(TAG, logMsg.toString());
632
633                mConvergedAE = result.get(CaptureResult.CONTROL_AE_STATE) ==
634                                          CaptureResult.CONTROL_AE_STATE_CONVERGED;
635                mConvergedAF = result.get(CaptureResult.CONTROL_AF_STATE) ==
636                                          CaptureResult.CONTROL_AF_STATE_FOCUSED_LOCKED;
637                mConvergedAWB = result.get(CaptureResult.CONTROL_AWB_STATE) ==
638                                           CaptureResult.CONTROL_AWB_STATE_CONVERGED;
639
640                if (mConvergedAE) {
641                    Log.i(PYTAG, String.format(
642                            "### 3A-E %d %d",
643                            result.get(CaptureResult.SENSOR_SENSITIVITY).intValue(),
644                            result.get(CaptureResult.SENSOR_EXPOSURE_TIME).intValue()
645                            ));
646                }
647
648                if (mConvergedAF) {
649                    Log.i(PYTAG, String.format(
650                            "### 3A-F %f",
651                            result.get(CaptureResult.LENS_FOCUS_DISTANCE)
652                            ));
653                }
654
655                if (mConvergedAWB && result.get(CaptureResult.COLOR_CORRECTION_GAINS) != null
656                        && result.get(CaptureResult.COLOR_CORRECTION_TRANSFORM) != null) {
657                    Log.i(PYTAG, String.format(
658                            "### 3A-W %f %f %f %f %f %f %f %f %f %f %f %f %f",
659                            result.get(CaptureResult.COLOR_CORRECTION_GAINS)[0],
660                            result.get(CaptureResult.COLOR_CORRECTION_GAINS)[1],
661                            result.get(CaptureResult.COLOR_CORRECTION_GAINS)[2],
662                            result.get(CaptureResult.COLOR_CORRECTION_GAINS)[3],
663                            r2f(result.get(CaptureResult.COLOR_CORRECTION_TRANSFORM)[0]),
664                            r2f(result.get(CaptureResult.COLOR_CORRECTION_TRANSFORM)[1]),
665                            r2f(result.get(CaptureResult.COLOR_CORRECTION_TRANSFORM)[2]),
666                            r2f(result.get(CaptureResult.COLOR_CORRECTION_TRANSFORM)[3]),
667                            r2f(result.get(CaptureResult.COLOR_CORRECTION_TRANSFORM)[4]),
668                            r2f(result.get(CaptureResult.COLOR_CORRECTION_TRANSFORM)[5]),
669                            r2f(result.get(CaptureResult.COLOR_CORRECTION_TRANSFORM)[6]),
670                            r2f(result.get(CaptureResult.COLOR_CORRECTION_TRANSFORM)[7]),
671                            r2f(result.get(CaptureResult.COLOR_CORRECTION_TRANSFORM)[8])
672                            ));
673                }
674
675                if (mIssuedRequest3A) {
676                    mIssuedRequest3A = false;
677                    mInterlock3A.open();
678                } else {
679                    String fileName = ItsUtils.getMetadataFileName(
680                            result.get(CaptureResult.SENSOR_TIMESTAMP));
681                    File mdFile = ItsUtils.getOutputFile(ItsService.this, fileName);
682                    ItsUtils.storeResults(mCameraProperties, request, result, mdFile);
683                    mCaptureCallbackLatch.countDown();
684                }
685            } catch (ItsException e) {
686                Log.e(TAG, "Script error: " + e);
687                Log.e(PYTAG, "### FAIL");
688            } catch (Exception e) {
689                Log.e(TAG, "Script error: " + e);
690                Log.e(PYTAG, "### FAIL");
691            }
692        }
693
694        @Override
695        public void onCaptureFailed(CameraDevice camera, CaptureRequest request) {
696            mCaptureCallbackLatch.countDown();
697            Log.e(TAG, "Script error: capture failed");
698            Log.e(PYTAG, "### FAIL");
699        }
700    };
701
702}
703