1 /* 2 * Copyright (C) 2020 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 android.hardware.camera2; 17 18 import android.annotation.IntDef; 19 import android.annotation.NonNull; 20 import android.annotation.Nullable; 21 import android.content.ComponentName; 22 import android.content.Context; 23 import android.content.Intent; 24 import android.content.ServiceConnection; 25 import android.graphics.ImageFormat; 26 import android.hardware.camera2.extension.IAdvancedExtenderImpl; 27 import android.hardware.camera2.extension.ICameraExtensionsProxyService; 28 import android.hardware.camera2.extension.IImageCaptureExtenderImpl; 29 import android.hardware.camera2.extension.IInitializeSessionCallback; 30 import android.hardware.camera2.extension.IPreviewExtenderImpl; 31 import android.hardware.camera2.extension.LatencyRange; 32 import android.hardware.camera2.extension.SizeList; 33 import android.hardware.camera2.impl.CameraMetadataNative; 34 import android.hardware.camera2.params.ExtensionSessionConfiguration; 35 import android.hardware.camera2.params.StreamConfigurationMap; 36 import android.os.ConditionVariable; 37 import android.os.IBinder; 38 import android.os.RemoteException; 39 import android.os.SystemProperties; 40 import android.util.Log; 41 import android.util.Pair; 42 import android.util.Range; 43 import android.util.Size; 44 45 import java.lang.annotation.Retention; 46 import java.lang.annotation.RetentionPolicy; 47 import java.util.ArrayList; 48 import java.util.Arrays; 49 import java.util.Collections; 50 import java.util.HashSet; 51 import java.util.List; 52 import java.util.Objects; 53 import java.util.Set; 54 import java.util.concurrent.Future; 55 import java.util.concurrent.TimeUnit; 56 import java.util.concurrent.TimeoutException; 57 58 /** 59 * <p>Allows clients to query availability and supported resolutions of camera extensions.</p> 60 * 61 * <p>Camera extensions give camera clients access to device-specific algorithms and sequences that 62 * can improve the overall image quality of snapshots in various cases such as low light, selfies, 63 * portraits, and scenes that can benefit from enhanced dynamic range. Often such sophisticated 64 * processing sequences will rely on multiple camera frames as input and will produce a single 65 * output.</p> 66 * 67 * <p>Camera extensions are not guaranteed to be present on all devices so camera clients must 68 * query for their availability via {@link CameraExtensionCharacteristics#getSupportedExtensions()}. 69 * </p> 70 * 71 * <p>In order to use any available camera extension, camera clients must create a corresponding 72 * {@link CameraExtensionSession} via 73 * {@link CameraDevice#createExtensionSession(ExtensionSessionConfiguration)}</p> 74 * 75 * <p>Camera clients must be aware that device-specific camera extensions may support only a 76 * subset of the available camera resolutions and must first query 77 * {@link CameraExtensionCharacteristics#getExtensionSupportedSizes(int, int)} for supported 78 * single high-quality request output sizes and 79 * {@link CameraExtensionCharacteristics#getExtensionSupportedSizes(int, Class)} for supported 80 * repeating request output sizes.</p> 81 * 82 * <p>The extension characteristics for a given device are expected to remain static under 83 * normal operating conditions.</p> 84 * 85 * @see CameraManager#getCameraExtensionCharacteristics(String) 86 */ 87 public final class CameraExtensionCharacteristics { 88 private static final String TAG = "CameraExtensionCharacteristics"; 89 90 /** 91 * Device-specific extension implementation for automatic selection of particular extension 92 * such as HDR or NIGHT depending on the current lighting and environment conditions. 93 */ 94 public static final int EXTENSION_AUTOMATIC = 0; 95 96 /** 97 * Device-specific extension implementation which tends to smooth the skin and apply other 98 * cosmetic effects to people's faces. 99 */ 100 public static final int EXTENSION_FACE_RETOUCH = 1; 101 102 /** 103 * Device-specific extension implementation which tends to smooth the skin and apply other 104 * cosmetic effects to people's faces. 105 * 106 * @deprecated Use {@link #EXTENSION_FACE_RETOUCH} instead. 107 */ 108 public @Deprecated static final int EXTENSION_BEAUTY = EXTENSION_FACE_RETOUCH; 109 110 /** 111 * Device-specific extension implementation which can blur certain regions of the final image 112 * thereby "enhancing" focus for all remaining non-blurred parts. 113 */ 114 public static final int EXTENSION_BOKEH = 2; 115 116 /** 117 * Device-specific extension implementation for enhancing the dynamic range of the 118 * final image. 119 */ 120 public static final int EXTENSION_HDR = 3; 121 122 /** 123 * Device-specific extension implementation that aims to suppress noise and improve the 124 * overall image quality under low light conditions. 125 */ 126 public static final int EXTENSION_NIGHT = 4; 127 128 /** 129 * @hide 130 */ 131 @Retention(RetentionPolicy.SOURCE) 132 @IntDef(flag = true, value = {EXTENSION_AUTOMATIC, 133 EXTENSION_FACE_RETOUCH, 134 EXTENSION_BOKEH, 135 EXTENSION_HDR, 136 EXTENSION_NIGHT}) 137 public @interface Extension { 138 } 139 140 /** 141 * Default camera output in case additional processing from CameraX extensions is not needed 142 * 143 * @hide 144 */ 145 public static final int NON_PROCESSING_INPUT_FORMAT = ImageFormat.PRIVATE; 146 147 /** 148 * CameraX extensions require YUV_420_888 as default input for processing at the moment 149 * 150 * @hide 151 */ 152 public static final int PROCESSING_INPUT_FORMAT = ImageFormat.YUV_420_888; 153 154 private static final @Extension 155 int[] EXTENSION_LIST = new int[]{ 156 EXTENSION_AUTOMATIC, 157 EXTENSION_FACE_RETOUCH, 158 EXTENSION_BOKEH, 159 EXTENSION_HDR, 160 EXTENSION_NIGHT}; 161 162 private final Context mContext; 163 private final String mCameraId; 164 private final CameraCharacteristics mChars; 165 166 /** 167 * @hide 168 */ CameraExtensionCharacteristics(Context context, String cameraId, CameraCharacteristics chars)169 public CameraExtensionCharacteristics(Context context, String cameraId, 170 CameraCharacteristics chars) { 171 mContext = context; 172 mCameraId = cameraId; 173 mChars = chars; 174 } 175 getSupportedSizes(List<SizeList> sizesList, Integer format)176 private static ArrayList<Size> getSupportedSizes(List<SizeList> sizesList, 177 Integer format) { 178 ArrayList<Size> ret = new ArrayList<>(); 179 if ((sizesList != null) && (!sizesList.isEmpty())) { 180 for (SizeList entry : sizesList) { 181 if ((entry.format == format) && !entry.sizes.isEmpty()) { 182 for (android.hardware.camera2.extension.Size sz : entry.sizes) { 183 ret.add(new Size(sz.width, sz.height)); 184 } 185 return ret; 186 } 187 } 188 } 189 190 return ret; 191 } 192 generateSupportedSizes(List<SizeList> sizesList, Integer format, StreamConfigurationMap streamMap)193 private static List<Size> generateSupportedSizes(List<SizeList> sizesList, 194 Integer format, 195 StreamConfigurationMap streamMap) { 196 // Per API contract it is assumed that the extension is able to support all 197 // camera advertised sizes for a given format in case it doesn't return 198 // a valid non-empty size list. 199 ArrayList<Size> ret = getSupportedSizes(sizesList, format); 200 Size[] supportedSizes = streamMap.getOutputSizes(format); 201 if ((ret.isEmpty()) && (supportedSizes != null)) { 202 ret.addAll(Arrays.asList(supportedSizes)); 203 } 204 return ret; 205 } 206 generateJpegSupportedSizes(List<SizeList> sizesList, StreamConfigurationMap streamMap)207 private static List<Size> generateJpegSupportedSizes(List<SizeList> sizesList, 208 StreamConfigurationMap streamMap) { 209 ArrayList<Size> extensionSizes = getSupportedSizes(sizesList, ImageFormat.YUV_420_888); 210 HashSet<Size> supportedSizes = extensionSizes.isEmpty() ? new HashSet<>(Arrays.asList( 211 streamMap.getOutputSizes(ImageFormat.YUV_420_888))) : new HashSet<>(extensionSizes); 212 HashSet<Size> supportedJpegSizes = new HashSet<>(Arrays.asList(streamMap.getOutputSizes( 213 ImageFormat.JPEG))); 214 supportedSizes.retainAll(supportedJpegSizes); 215 216 return new ArrayList<>(supportedSizes); 217 } 218 219 /** 220 * A per-process global camera extension manager instance, to track and 221 * initialize/release extensions depending on client activity. 222 */ 223 private static final class CameraExtensionManagerGlobal { 224 private static final String TAG = "CameraExtensionManagerGlobal"; 225 private static final String PROXY_PACKAGE_NAME = "com.android.cameraextensions"; 226 private static final String PROXY_SERVICE_NAME = 227 "com.android.cameraextensions.CameraExtensionsProxyService"; 228 229 // Singleton instance 230 private static final CameraExtensionManagerGlobal GLOBAL_CAMERA_MANAGER = 231 new CameraExtensionManagerGlobal(); 232 private final Object mLock = new Object(); 233 private final int PROXY_SERVICE_DELAY_MS = 1000; 234 private InitializerFuture mInitFuture = null; 235 private ServiceConnection mConnection = null; 236 private ICameraExtensionsProxyService mProxy = null; 237 private boolean mSupportsAdvancedExtensions = false; 238 239 // Singleton, don't allow construction CameraExtensionManagerGlobal()240 private CameraExtensionManagerGlobal() {} 241 get()242 public static CameraExtensionManagerGlobal get() { 243 return GLOBAL_CAMERA_MANAGER; 244 } 245 connectToProxyLocked(Context ctx)246 private void connectToProxyLocked(Context ctx) { 247 if (mConnection == null) { 248 Intent intent = new Intent(); 249 intent.setClassName(PROXY_PACKAGE_NAME, PROXY_SERVICE_NAME); 250 String vendorProxyPackage = SystemProperties.get( 251 "ro.vendor.camera.extensions.package"); 252 String vendorProxyService = SystemProperties.get( 253 "ro.vendor.camera.extensions.service"); 254 if (!vendorProxyPackage.isEmpty() && !vendorProxyService.isEmpty()) { 255 Log.v(TAG, 256 "Choosing the vendor camera extensions proxy package: " 257 + vendorProxyPackage); 258 Log.v(TAG, 259 "Choosing the vendor camera extensions proxy service: " 260 + vendorProxyService); 261 intent.setClassName(vendorProxyPackage, vendorProxyService); 262 } 263 mInitFuture = new InitializerFuture(); 264 mConnection = new ServiceConnection() { 265 @Override 266 public void onServiceDisconnected(ComponentName component) { 267 mInitFuture.setStatus(false); 268 mConnection = null; 269 mProxy = null; 270 } 271 272 @Override 273 public void onServiceConnected(ComponentName component, IBinder binder) { 274 mProxy = ICameraExtensionsProxyService.Stub.asInterface(binder); 275 if (mProxy == null) { 276 throw new IllegalStateException("Camera Proxy service is null"); 277 } 278 try { 279 mSupportsAdvancedExtensions = mProxy.advancedExtensionsSupported(); 280 } catch (RemoteException e) { 281 Log.e(TAG, "Remote IPC failed!"); 282 } 283 mInitFuture.setStatus(true); 284 } 285 }; 286 ctx.bindService(intent, Context.BIND_AUTO_CREATE | Context.BIND_IMPORTANT | 287 Context.BIND_ABOVE_CLIENT | Context.BIND_NOT_VISIBLE, 288 android.os.AsyncTask.THREAD_POOL_EXECUTOR, mConnection); 289 290 try { 291 mInitFuture.get(PROXY_SERVICE_DELAY_MS, TimeUnit.MILLISECONDS); 292 } catch (TimeoutException e) { 293 Log.e(TAG, "Timed out while initializing proxy service!"); 294 } 295 } 296 } 297 298 private static class InitializerFuture implements Future<Boolean> { 299 private volatile Boolean mStatus; 300 ConditionVariable mCondVar = new ConditionVariable(/*opened*/false); 301 setStatus(boolean status)302 public void setStatus(boolean status) { 303 mStatus = status; 304 mCondVar.open(); 305 } 306 307 @Override cancel(boolean mayInterruptIfRunning)308 public boolean cancel(boolean mayInterruptIfRunning) { 309 return false; // don't allow canceling this task 310 } 311 312 @Override isCancelled()313 public boolean isCancelled() { 314 return false; // can never cancel this task 315 } 316 317 @Override isDone()318 public boolean isDone() { 319 return mStatus != null; 320 } 321 322 @Override get()323 public Boolean get() { 324 mCondVar.block(); 325 return mStatus; 326 } 327 328 @Override get(long timeout, TimeUnit unit)329 public Boolean get(long timeout, TimeUnit unit) throws TimeoutException { 330 long timeoutMs = unit.convert(timeout, TimeUnit.MILLISECONDS); 331 if (!mCondVar.block(timeoutMs)) { 332 throw new TimeoutException( 333 "Failed to receive status after " + timeout + " " + unit); 334 } 335 336 if (mStatus == null) { 337 throw new AssertionError(); 338 } 339 return mStatus; 340 } 341 } 342 registerClient(Context ctx)343 public long registerClient(Context ctx) { 344 synchronized (mLock) { 345 connectToProxyLocked(ctx); 346 if (mProxy != null) { 347 try { 348 return mProxy.registerClient(); 349 } catch (RemoteException e) { 350 Log.e(TAG, "Failed to initialize extension! Extension service does " 351 + " not respond!"); 352 return -1; 353 } 354 } else { 355 return -1; 356 } 357 } 358 } 359 unregisterClient(long clientId)360 public void unregisterClient(long clientId) { 361 synchronized (mLock) { 362 if (mProxy != null) { 363 try { 364 mProxy.unregisterClient(clientId); 365 } catch (RemoteException e) { 366 Log.e(TAG, "Failed to de-initialize extension! Extension service does" 367 + " not respond!"); 368 } 369 } 370 } 371 } 372 initializeSession(IInitializeSessionCallback cb)373 public void initializeSession(IInitializeSessionCallback cb) throws RemoteException { 374 synchronized (mLock) { 375 if (mProxy != null) { 376 mProxy.initializeSession(cb); 377 } 378 } 379 } 380 releaseSession()381 public void releaseSession() { 382 synchronized (mLock) { 383 if (mProxy != null) { 384 try { 385 mProxy.releaseSession(); 386 } catch (RemoteException e) { 387 Log.e(TAG, "Failed to release session! Extension service does" 388 + " not respond!"); 389 } 390 } 391 } 392 } 393 areAdvancedExtensionsSupported()394 public boolean areAdvancedExtensionsSupported() { 395 return mSupportsAdvancedExtensions; 396 } 397 initializePreviewExtension(int extensionType)398 public IPreviewExtenderImpl initializePreviewExtension(int extensionType) 399 throws RemoteException { 400 synchronized (mLock) { 401 if (mProxy != null) { 402 return mProxy.initializePreviewExtension(extensionType); 403 } else { 404 return null; 405 } 406 } 407 } 408 initializeImageExtension(int extensionType)409 public IImageCaptureExtenderImpl initializeImageExtension(int extensionType) 410 throws RemoteException { 411 synchronized (mLock) { 412 if (mProxy != null) { 413 return mProxy.initializeImageExtension(extensionType); 414 } else { 415 return null; 416 } 417 } 418 } 419 initializeAdvancedExtension(int extensionType)420 public IAdvancedExtenderImpl initializeAdvancedExtension(int extensionType) 421 throws RemoteException { 422 synchronized (mLock) { 423 if (mProxy != null) { 424 return mProxy.initializeAdvancedExtension(extensionType); 425 } else { 426 return null; 427 } 428 } 429 } 430 } 431 432 /** 433 * @hide 434 */ registerClient(Context ctx)435 public static long registerClient(Context ctx) { 436 return CameraExtensionManagerGlobal.get().registerClient(ctx); 437 } 438 439 /** 440 * @hide 441 */ unregisterClient(long clientId)442 public static void unregisterClient(long clientId) { 443 CameraExtensionManagerGlobal.get().unregisterClient(clientId); 444 } 445 446 /** 447 * @hide 448 */ initializeSession(IInitializeSessionCallback cb)449 public static void initializeSession(IInitializeSessionCallback cb) throws RemoteException { 450 CameraExtensionManagerGlobal.get().initializeSession(cb); 451 } 452 453 /** 454 * @hide 455 */ releaseSession()456 public static void releaseSession() { 457 CameraExtensionManagerGlobal.get().releaseSession(); 458 } 459 460 /** 461 * @hide 462 */ areAdvancedExtensionsSupported()463 public static boolean areAdvancedExtensionsSupported() { 464 return CameraExtensionManagerGlobal.get().areAdvancedExtensionsSupported(); 465 } 466 467 /** 468 * @hide 469 */ isExtensionSupported(String cameraId, int extensionType, CameraCharacteristics chars)470 public static boolean isExtensionSupported(String cameraId, int extensionType, 471 CameraCharacteristics chars) { 472 if (areAdvancedExtensionsSupported()) { 473 try { 474 IAdvancedExtenderImpl extender = initializeAdvancedExtension(extensionType); 475 return extender.isExtensionAvailable(cameraId); 476 } catch (RemoteException e) { 477 Log.e(TAG, "Failed to query extension availability! Extension service does not" 478 + " respond!"); 479 return false; 480 } 481 } else { 482 Pair<IPreviewExtenderImpl, IImageCaptureExtenderImpl> extenders; 483 try { 484 extenders = initializeExtension(extensionType); 485 } catch (IllegalArgumentException e) { 486 return false; 487 } 488 489 try { 490 return extenders.first.isExtensionAvailable(cameraId, chars.getNativeMetadata()) && 491 extenders.second.isExtensionAvailable(cameraId, chars.getNativeMetadata()); 492 } catch (RemoteException e) { 493 Log.e(TAG, "Failed to query extension availability! Extension service does not" 494 + " respond!"); 495 return false; 496 } 497 } 498 } 499 500 /** 501 * @hide 502 */ initializeAdvancedExtension(@xtension int extensionType)503 public static IAdvancedExtenderImpl initializeAdvancedExtension(@Extension int extensionType) { 504 IAdvancedExtenderImpl extender; 505 try { 506 extender = CameraExtensionManagerGlobal.get().initializeAdvancedExtension( 507 extensionType); 508 } catch (RemoteException e) { 509 throw new IllegalStateException("Failed to initialize extension: " + extensionType); 510 } 511 512 if (extender == null) { 513 throw new IllegalArgumentException("Unknown extension: " + extensionType); 514 } 515 516 return extender; 517 } 518 519 /** 520 * @hide 521 */ initializeExtension( @xtension int extensionType)522 public static Pair<IPreviewExtenderImpl, IImageCaptureExtenderImpl> initializeExtension( 523 @Extension int extensionType) { 524 IPreviewExtenderImpl previewExtender; 525 IImageCaptureExtenderImpl imageExtender; 526 try { 527 previewExtender = 528 CameraExtensionManagerGlobal.get().initializePreviewExtension(extensionType); 529 imageExtender = 530 CameraExtensionManagerGlobal.get().initializeImageExtension(extensionType); 531 } catch (RemoteException e) { 532 throw new IllegalStateException("Failed to initialize extension: " + extensionType); 533 } 534 if ((imageExtender == null) || (previewExtender == null)) { 535 throw new IllegalArgumentException("Unknown extension: " + extensionType); 536 } 537 538 return new Pair<>(previewExtender, imageExtender); 539 } 540 isOutputSupportedFor(Class<T> klass)541 private static <T> boolean isOutputSupportedFor(Class<T> klass) { 542 Objects.requireNonNull(klass, "klass must not be null"); 543 544 if (klass == android.graphics.SurfaceTexture.class) { 545 return true; 546 } 547 548 return false; 549 } 550 551 /** 552 * Return a list of supported device-specific extensions for a given camera device. 553 * 554 * @return non-modifiable list of available extensions 555 */ getSupportedExtensions()556 public @NonNull List<Integer> getSupportedExtensions() { 557 ArrayList<Integer> ret = new ArrayList<>(); 558 long clientId = registerClient(mContext); 559 if (clientId < 0) { 560 return Collections.unmodifiableList(ret); 561 } 562 563 try { 564 for (int extensionType : EXTENSION_LIST) { 565 if (isExtensionSupported(mCameraId, extensionType, mChars)) { 566 ret.add(extensionType); 567 } 568 } 569 } finally { 570 unregisterClient(clientId); 571 } 572 573 return Collections.unmodifiableList(ret); 574 } 575 576 /** 577 * Get a list of sizes compatible with {@code klass} to use as an output for the 578 * repeating request 579 * {@link CameraExtensionSession#setRepeatingRequest}. 580 * 581 * <p>Note that device-specific extensions are allowed to support only a subset 582 * of the camera output surfaces and resolutions. 583 * The {@link android.graphics.SurfaceTexture} class is guaranteed at least one size for 584 * backward compatible cameras whereas other output classes are not guaranteed to be supported. 585 * </p> 586 * 587 * @param extension the extension type 588 * @param klass a non-{@code null} {@link Class} object reference 589 * @return non-modifiable list of available sizes or an empty list if the Surface output is not 590 * supported 591 * @throws NullPointerException if {@code klass} was {@code null} 592 * @throws IllegalArgumentException in case of unsupported extension. 593 */ 594 @NonNull getExtensionSupportedSizes(@xtension int extension, @NonNull Class<T> klass)595 public <T> List<Size> getExtensionSupportedSizes(@Extension int extension, 596 @NonNull Class<T> klass) { 597 if (!isOutputSupportedFor(klass)) { 598 return new ArrayList<>(); 599 } 600 // TODO: Revisit this code once the Extension preview processor output format 601 // ambiguity is resolved in b/169799538. 602 603 long clientId = registerClient(mContext); 604 if (clientId < 0) { 605 throw new IllegalArgumentException("Unsupported extensions"); 606 } 607 608 try { 609 if (!isExtensionSupported(mCameraId, extension, mChars)) { 610 throw new IllegalArgumentException("Unsupported extension"); 611 } 612 613 StreamConfigurationMap streamMap = mChars.get( 614 CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP); 615 if (areAdvancedExtensionsSupported()) { 616 IAdvancedExtenderImpl extender = initializeAdvancedExtension(extension); 617 extender.init(mCameraId); 618 return generateSupportedSizes( 619 extender.getSupportedPreviewOutputResolutions(mCameraId), 620 ImageFormat.PRIVATE, streamMap); 621 } else { 622 Pair<IPreviewExtenderImpl, IImageCaptureExtenderImpl> extenders = 623 initializeExtension(extension); 624 extenders.first.init(mCameraId, mChars.getNativeMetadata()); 625 return generateSupportedSizes(extenders.first.getSupportedResolutions(), 626 ImageFormat.PRIVATE, streamMap); 627 } 628 } catch (RemoteException e) { 629 Log.e(TAG, "Failed to query the extension supported sizes! Extension service does" 630 + " not respond!"); 631 return new ArrayList<>(); 632 } finally { 633 unregisterClient(clientId); 634 } 635 } 636 637 /** 638 * Check whether a given extension is available and return the 639 * supported output surface resolutions that can be used for high-quality capture 640 * requests via {@link CameraExtensionSession#capture}. 641 * 642 * <p>Note that device-specific extensions are allowed to support only a subset 643 * of the camera resolutions advertised by 644 * {@link StreamConfigurationMap#getOutputSizes}.</p> 645 * 646 * <p>Device-specific extensions currently support at most two 647 * multi-frame capture surface formats. ImageFormat.JPEG will be supported by all 648 * extensions and ImageFormat.YUV_420_888 may or may not be supported.</p> 649 * 650 * @param extension the extension type 651 * @param format device-specific extension output format 652 * @return non-modifiable list of available sizes or an empty list if the format is not 653 * supported. 654 * @throws IllegalArgumentException in case of format different from ImageFormat.JPEG / 655 * ImageFormat.YUV_420_888; or unsupported extension. 656 */ 657 public @NonNull getExtensionSupportedSizes(@xtension int extension, int format)658 List<Size> getExtensionSupportedSizes(@Extension int extension, int format) { 659 try { 660 long clientId = registerClient(mContext); 661 if (clientId < 0) { 662 throw new IllegalArgumentException("Unsupported extensions"); 663 } 664 665 try { 666 if (!isExtensionSupported(mCameraId, extension, mChars)) { 667 throw new IllegalArgumentException("Unsupported extension"); 668 } 669 670 StreamConfigurationMap streamMap = mChars.get( 671 CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP); 672 if (areAdvancedExtensionsSupported()) { 673 switch(format) { 674 case ImageFormat.YUV_420_888: 675 case ImageFormat.JPEG: 676 break; 677 default: 678 throw new IllegalArgumentException("Unsupported format: " + format); 679 } 680 IAdvancedExtenderImpl extender = initializeAdvancedExtension(extension); 681 extender.init(mCameraId); 682 return generateSupportedSizes(extender.getSupportedCaptureOutputResolutions( 683 mCameraId), format, streamMap); 684 } else { 685 if (format == ImageFormat.YUV_420_888) { 686 Pair<IPreviewExtenderImpl, IImageCaptureExtenderImpl> extenders = 687 initializeExtension(extension); 688 extenders.second.init(mCameraId, mChars.getNativeMetadata()); 689 if (extenders.second.getCaptureProcessor() == null) { 690 // Extensions that don't implement any capture processor are limited to 691 // JPEG only! 692 return new ArrayList<>(); 693 } 694 return generateSupportedSizes(extenders.second.getSupportedResolutions(), 695 format, streamMap); 696 } else if (format == ImageFormat.JPEG) { 697 Pair<IPreviewExtenderImpl, IImageCaptureExtenderImpl> extenders = 698 initializeExtension(extension); 699 extenders.second.init(mCameraId, mChars.getNativeMetadata()); 700 if (extenders.second.getCaptureProcessor() != null) { 701 // The framework will perform the additional encoding pass on the 702 // processed YUV_420 buffers. 703 return generateJpegSupportedSizes( 704 extenders.second.getSupportedResolutions(), streamMap); 705 } else { 706 return generateSupportedSizes(null, format, streamMap); 707 } 708 } else { 709 throw new IllegalArgumentException("Unsupported format: " + format); 710 } 711 } 712 } finally { 713 unregisterClient(clientId); 714 } 715 } catch (RemoteException e) { 716 Log.e(TAG, "Failed to query the extension supported sizes! Extension service does" 717 + " not respond!"); 718 return new ArrayList<>(); 719 } 720 } 721 722 /** 723 * Returns the estimated capture latency range in milliseconds for the 724 * target capture resolution during the calls to {@link CameraExtensionSession#capture}. This 725 * includes the time spent processing the multi-frame capture request along with any additional 726 * time for encoding of the processed buffer if necessary. 727 * 728 * @param extension the extension type 729 * @param captureOutputSize size of the capture output surface. If it is not in the supported 730 * output sizes, maximum capture output size is used for the estimation 731 * @param format device-specific extension output format 732 * @return the range of estimated minimal and maximal capture latency in milliseconds 733 * or null if no capture latency info can be provided 734 * 735 * @throws IllegalArgumentException in case of format different from {@link ImageFormat#JPEG} / 736 * {@link ImageFormat#YUV_420_888}; or unsupported extension. 737 */ getEstimatedCaptureLatencyRangeMillis(@xtension int extension, @NonNull Size captureOutputSize, @ImageFormat.Format int format)738 public @Nullable Range<Long> getEstimatedCaptureLatencyRangeMillis(@Extension int extension, 739 @NonNull Size captureOutputSize, @ImageFormat.Format int format) { 740 switch (format) { 741 case ImageFormat.YUV_420_888: 742 case ImageFormat.JPEG: 743 //No op 744 break; 745 default: 746 throw new IllegalArgumentException("Unsupported format: " + format); 747 } 748 749 long clientId = registerClient(mContext); 750 if (clientId < 0) { 751 throw new IllegalArgumentException("Unsupported extensions"); 752 } 753 754 try { 755 if (!isExtensionSupported(mCameraId, extension, mChars)) { 756 throw new IllegalArgumentException("Unsupported extension"); 757 } 758 759 android.hardware.camera2.extension.Size sz = 760 new android.hardware.camera2.extension.Size(); 761 sz.width = captureOutputSize.getWidth(); 762 sz.height = captureOutputSize.getHeight(); 763 if (areAdvancedExtensionsSupported()) { 764 IAdvancedExtenderImpl extender = initializeAdvancedExtension(extension); 765 extender.init(mCameraId); 766 LatencyRange latencyRange = extender.getEstimatedCaptureLatencyRange(mCameraId, 767 sz, format); 768 if (latencyRange != null) { 769 return new Range(latencyRange.min, latencyRange.max); 770 } 771 } else { 772 Pair<IPreviewExtenderImpl, IImageCaptureExtenderImpl> extenders = 773 initializeExtension(extension); 774 extenders.second.init(mCameraId, mChars.getNativeMetadata()); 775 if ((format == ImageFormat.YUV_420_888) && 776 (extenders.second.getCaptureProcessor() == null) ){ 777 // Extensions that don't implement any capture processor are limited to 778 // JPEG only! 779 return null; 780 } 781 if ((format == ImageFormat.JPEG) && 782 (extenders.second.getCaptureProcessor() != null)) { 783 // The framework will perform the additional encoding pass on the 784 // processed YUV_420 buffers. Latency in this case is very device 785 // specific and cannot be estimated accurately enough. 786 return null; 787 } 788 789 LatencyRange latencyRange = extenders.second.getEstimatedCaptureLatencyRange(sz); 790 if (latencyRange != null) { 791 return new Range(latencyRange.min, latencyRange.max); 792 } 793 } 794 } catch (RemoteException e) { 795 Log.e(TAG, "Failed to query the extension capture latency! Extension service does" 796 + " not respond!"); 797 } finally { 798 unregisterClient(clientId); 799 } 800 801 return null; 802 } 803 804 /** 805 * Returns the set of keys supported by a {@link CaptureRequest} submitted in a 806 * {@link CameraExtensionSession} with a given extension type. 807 * 808 * <p>The set returned is not modifiable, so any attempts to modify it will throw 809 * a {@code UnsupportedOperationException}.</p> 810 * 811 * @param extension the extension type 812 * 813 * @return non-modifiable set of capture keys supported by camera extension session initialized 814 * with the given extension type. 815 * @throws IllegalArgumentException in case of unsupported extension. 816 */ 817 @NonNull getAvailableCaptureRequestKeys(@xtension int extension)818 public Set<CaptureRequest.Key> getAvailableCaptureRequestKeys(@Extension int extension) { 819 long clientId = registerClient(mContext); 820 if (clientId < 0) { 821 throw new IllegalArgumentException("Unsupported extensions"); 822 } 823 824 HashSet<CaptureRequest.Key> ret = new HashSet<>(); 825 826 try { 827 if (!isExtensionSupported(mCameraId, extension, mChars)) { 828 throw new IllegalArgumentException("Unsupported extension"); 829 } 830 831 CameraMetadataNative captureRequestMeta = null; 832 if (areAdvancedExtensionsSupported()) { 833 IAdvancedExtenderImpl extender = initializeAdvancedExtension(extension); 834 extender.init(mCameraId); 835 captureRequestMeta = extender.getAvailableCaptureRequestKeys(mCameraId); 836 } else { 837 Pair<IPreviewExtenderImpl, IImageCaptureExtenderImpl> extenders = 838 initializeExtension(extension); 839 extenders.second.onInit(mCameraId, mChars.getNativeMetadata()); 840 extenders.second.init(mCameraId, mChars.getNativeMetadata()); 841 captureRequestMeta = extenders.second.getAvailableCaptureRequestKeys(); 842 extenders.second.onDeInit(); 843 } 844 845 if (captureRequestMeta != null) { 846 int[] requestKeys = captureRequestMeta.get( 847 CameraCharacteristics.REQUEST_AVAILABLE_REQUEST_KEYS); 848 if (requestKeys == null) { 849 throw new AssertionError( 850 "android.request.availableRequestKeys must be non-null" 851 + " in the characteristics"); 852 } 853 CameraCharacteristics requestChars = new CameraCharacteristics( 854 captureRequestMeta); 855 856 Object crKey = CaptureRequest.Key.class; 857 Class<CaptureRequest.Key<?>> crKeyTyped = (Class<CaptureRequest.Key<?>>) crKey; 858 859 ret.addAll(requestChars.getAvailableKeyList(CaptureRequest.class, crKeyTyped, 860 requestKeys, /*includeSynthetic*/ true)); 861 } 862 863 // Jpeg quality and orientation must always be supported 864 if (!ret.contains(CaptureRequest.JPEG_QUALITY)) { 865 ret.add(CaptureRequest.JPEG_QUALITY); 866 } 867 if (!ret.contains(CaptureRequest.JPEG_ORIENTATION)) { 868 ret.add(CaptureRequest.JPEG_ORIENTATION); 869 } 870 } catch (RemoteException e) { 871 throw new IllegalStateException("Failed to query the available capture request keys!"); 872 } finally { 873 unregisterClient(clientId); 874 } 875 876 return Collections.unmodifiableSet(ret); 877 } 878 879 /** 880 * Returns the set of keys supported by a {@link CaptureResult} passed as an argument to 881 * {@link CameraExtensionSession.ExtensionCaptureCallback#onCaptureResultAvailable}. 882 * 883 * <p>The set returned is not modifiable, so any attempts to modify it will throw 884 * a {@code UnsupportedOperationException}.</p> 885 * 886 * <p>In case the set is empty, then the extension is not able to support any capture results 887 * and the {@link CameraExtensionSession.ExtensionCaptureCallback#onCaptureResultAvailable} 888 * callback will not be fired.</p> 889 * 890 * @param extension the extension type 891 * 892 * @return non-modifiable set of capture result keys supported by camera extension session 893 * initialized with the given extension type. 894 * @throws IllegalArgumentException in case of unsupported extension. 895 */ 896 @NonNull getAvailableCaptureResultKeys(@xtension int extension)897 public Set<CaptureResult.Key> getAvailableCaptureResultKeys(@Extension int extension) { 898 long clientId = registerClient(mContext); 899 if (clientId < 0) { 900 throw new IllegalArgumentException("Unsupported extensions"); 901 } 902 903 HashSet<CaptureResult.Key> ret = new HashSet<>(); 904 try { 905 if (!isExtensionSupported(mCameraId, extension, mChars)) { 906 throw new IllegalArgumentException("Unsupported extension"); 907 } 908 909 CameraMetadataNative captureResultMeta = null; 910 if (areAdvancedExtensionsSupported()) { 911 IAdvancedExtenderImpl extender = initializeAdvancedExtension(extension); 912 extender.init(mCameraId); 913 captureResultMeta = extender.getAvailableCaptureResultKeys(mCameraId); 914 } else { 915 Pair<IPreviewExtenderImpl, IImageCaptureExtenderImpl> extenders = 916 initializeExtension(extension); 917 extenders.second.onInit(mCameraId, mChars.getNativeMetadata()); 918 extenders.second.init(mCameraId, mChars.getNativeMetadata()); 919 captureResultMeta = extenders.second.getAvailableCaptureResultKeys(); 920 extenders.second.onDeInit(); 921 } 922 923 if (captureResultMeta != null) { 924 int[] resultKeys = captureResultMeta.get( 925 CameraCharacteristics.REQUEST_AVAILABLE_RESULT_KEYS); 926 if (resultKeys == null) { 927 throw new AssertionError("android.request.availableResultKeys must be non-null " 928 + "in the characteristics"); 929 } 930 CameraCharacteristics resultChars = new CameraCharacteristics(captureResultMeta); 931 Object crKey = CaptureResult.Key.class; 932 Class<CaptureResult.Key<?>> crKeyTyped = (Class<CaptureResult.Key<?>>)crKey; 933 934 ret.addAll(resultChars.getAvailableKeyList(CaptureResult.class, crKeyTyped, 935 resultKeys, /*includeSynthetic*/ true)); 936 937 // Jpeg quality, orientation and sensor timestamp must always be supported 938 if (!ret.contains(CaptureResult.JPEG_QUALITY)) { 939 ret.add(CaptureResult.JPEG_QUALITY); 940 } 941 if (!ret.contains(CaptureResult.JPEG_ORIENTATION)) { 942 ret.add(CaptureResult.JPEG_ORIENTATION); 943 } 944 if (!ret.contains(CaptureResult.SENSOR_TIMESTAMP)) { 945 ret.add(CaptureResult.SENSOR_TIMESTAMP); 946 } 947 } 948 } catch (RemoteException e) { 949 throw new IllegalStateException("Failed to query the available capture result keys!"); 950 } finally { 951 unregisterClient(clientId); 952 } 953 954 return Collections.unmodifiableSet(ret); 955 } 956 } 957