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