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