1 /* 2 * Copyright 2021 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 17 package androidx.camera.extensions.internal.sessionprocessor; 18 19 import static android.hardware.camera2.CameraExtensionCharacteristics.EXTENSION_AUTOMATIC; 20 import static android.hardware.camera2.CameraExtensionCharacteristics.EXTENSION_BOKEH; 21 import static android.hardware.camera2.CameraExtensionCharacteristics.EXTENSION_FACE_RETOUCH; 22 import static android.hardware.camera2.CameraExtensionCharacteristics.EXTENSION_HDR; 23 import static android.hardware.camera2.CameraExtensionCharacteristics.EXTENSION_NIGHT; 24 25 import android.annotation.SuppressLint; 26 import android.content.Context; 27 import android.hardware.camera2.CameraCharacteristics; 28 import android.hardware.camera2.CaptureFailure; 29 import android.hardware.camera2.CaptureRequest; 30 import android.hardware.camera2.CaptureResult; 31 import android.hardware.camera2.TotalCaptureResult; 32 import android.hardware.camera2.params.SessionConfiguration; 33 import android.media.Image; 34 import android.os.Build; 35 import android.util.Pair; 36 import android.util.Size; 37 import android.view.Surface; 38 39 import androidx.annotation.GuardedBy; 40 import androidx.annotation.IntRange; 41 import androidx.camera.core.Logger; 42 import androidx.camera.core.impl.CameraCaptureFailure; 43 import androidx.camera.core.impl.CameraCaptureResult; 44 import androidx.camera.core.impl.Config; 45 import androidx.camera.core.impl.OutputSurface; 46 import androidx.camera.core.impl.OutputSurfaceConfiguration; 47 import androidx.camera.core.impl.RequestProcessor; 48 import androidx.camera.core.impl.SessionProcessor; 49 import androidx.camera.core.impl.TagBundle; 50 import androidx.camera.extensions.ExtensionMode; 51 import androidx.camera.extensions.impl.advanced.Camera2OutputConfigImpl; 52 import androidx.camera.extensions.impl.advanced.Camera2SessionConfigImpl; 53 import androidx.camera.extensions.impl.advanced.ImageProcessorImpl; 54 import androidx.camera.extensions.impl.advanced.ImageReferenceImpl; 55 import androidx.camera.extensions.impl.advanced.OutputSurfaceConfigurationImpl; 56 import androidx.camera.extensions.impl.advanced.OutputSurfaceImpl; 57 import androidx.camera.extensions.impl.advanced.RequestProcessorImpl; 58 import androidx.camera.extensions.impl.advanced.SessionProcessorImpl; 59 import androidx.camera.extensions.internal.ClientVersion; 60 import androidx.camera.extensions.internal.ExtensionVersion; 61 import androidx.camera.extensions.internal.RequestOptionConfig; 62 import androidx.camera.extensions.internal.VendorExtender; 63 import androidx.camera.extensions.internal.Version; 64 import androidx.core.util.Preconditions; 65 import androidx.lifecycle.LiveData; 66 import androidx.lifecycle.MutableLiveData; 67 68 import org.jspecify.annotations.NonNull; 69 import org.jspecify.annotations.Nullable; 70 71 import java.util.ArrayList; 72 import java.util.Collections; 73 import java.util.HashMap; 74 import java.util.List; 75 import java.util.Map; 76 import java.util.Objects; 77 78 /** 79 * A {@link SessionProcessor} based on OEMs' {@link SessionProcessorImpl}. 80 */ 81 public class AdvancedSessionProcessor extends SessionProcessorBase { 82 private static final String TAG = "AdvancedSessionProcessor"; 83 private final @NonNull SessionProcessorImpl mImpl; 84 private final @NonNull VendorExtender mVendorExtender; 85 private final @NonNull Context mContext; 86 @ExtensionMode.Mode 87 private final int mMode; 88 private final @Nullable MutableLiveData<Integer> mCurrentExtensionTypeLiveData; 89 private boolean mIsPostviewConfigured = false; 90 // Caches the working capture config so that the new extension strength can be applied on top 91 // of the existing config. 92 @GuardedBy("mLock") 93 private HashMap<CaptureRequest.Key<?>, Object> mWorkingCaptureConfigMap = new HashMap<>(); 94 // Caches the capture callback adapter so that repeating can be started again to apply the 95 // new extension strength setting. 96 @GuardedBy("mLock") 97 private SessionProcessorImplCaptureCallbackAdapter mRepeatingCaptureCallbackAdapter = null; 98 private final @Nullable MutableLiveData<Integer> mExtensionStrengthLiveData; 99 private final @Nullable ExtensionMetadataMonitor mExtensionMetadataMonitor; 100 private final boolean mWillReceiveOnCaptureCompleted; 101 AdvancedSessionProcessor(@onNull SessionProcessorImpl impl, @NonNull List<CaptureRequest.Key<?>> supportedKeys, @NonNull VendorExtender vendorExtender, @NonNull Context context)102 public AdvancedSessionProcessor(@NonNull SessionProcessorImpl impl, 103 @NonNull List<CaptureRequest.Key<?>> supportedKeys, 104 @NonNull VendorExtender vendorExtender, 105 @NonNull Context context) { 106 this(impl, supportedKeys, vendorExtender, context, ExtensionMode.NONE); 107 } 108 AdvancedSessionProcessor(@onNull SessionProcessorImpl impl, @NonNull List<CaptureRequest.Key<?>> supportedKeys, @NonNull VendorExtender vendorExtender, @NonNull Context context, @ExtensionMode.Mode int mode)109 public AdvancedSessionProcessor(@NonNull SessionProcessorImpl impl, 110 @NonNull List<CaptureRequest.Key<?>> supportedKeys, 111 @NonNull VendorExtender vendorExtender, 112 @NonNull Context context, 113 @ExtensionMode.Mode int mode) { 114 super(supportedKeys, mode); 115 mImpl = impl; 116 mVendorExtender = vendorExtender; 117 mContext = context; 118 mWillReceiveOnCaptureCompleted = vendorExtender.willReceiveOnCaptureCompleted(); 119 mMode = mode; 120 mCurrentExtensionTypeLiveData = isCurrentExtensionModeAvailable() ? new MutableLiveData<>( 121 mMode) : null; 122 mExtensionStrengthLiveData = isExtensionStrengthAvailable() ? new MutableLiveData<>(100) 123 : null; 124 if (mCurrentExtensionTypeLiveData != null || mExtensionStrengthLiveData != null) { 125 mExtensionMetadataMonitor = new ExtensionMetadataMonitor(mCurrentExtensionTypeLiveData, 126 mExtensionStrengthLiveData); 127 } else { 128 mExtensionMetadataMonitor = null; 129 } 130 } 131 132 @Override initSessionInternal( @onNull String cameraId, @NonNull Map<String, CameraCharacteristics> cameraCharacteristicsMap, @NonNull OutputSurfaceConfiguration outputSurfaceConfig)133 protected @NonNull Camera2SessionConfig initSessionInternal( 134 @NonNull String cameraId, 135 @NonNull Map<String, CameraCharacteristics> cameraCharacteristicsMap, 136 @NonNull OutputSurfaceConfiguration outputSurfaceConfig) { 137 Camera2SessionConfigImpl sessionConfigImpl = null; 138 if (ClientVersion.isMinimumCompatibleVersion(Version.VERSION_1_4) 139 && ExtensionVersion.isMinimumCompatibleVersion(Version.VERSION_1_4)) { 140 sessionConfigImpl = 141 mImpl.initSession( 142 cameraId, 143 cameraCharacteristicsMap, 144 mContext, 145 new OutputSurfaceConfigurationImplAdapter(outputSurfaceConfig)); 146 147 } 148 149 // In case of OEM doesn't implement the v1.4 version of initSession, we fallback to invoke 150 // prior version. 151 if (sessionConfigImpl == null) { 152 sessionConfigImpl = 153 mImpl.initSession( 154 cameraId, 155 cameraCharacteristicsMap, 156 mContext, 157 new OutputSurfaceImplAdapter( 158 outputSurfaceConfig.getPreviewOutputSurface()), 159 new OutputSurfaceImplAdapter( 160 outputSurfaceConfig.getImageCaptureOutputSurface()), 161 outputSurfaceConfig.getImageAnalysisOutputSurface() == null 162 ? null : new OutputSurfaceImplAdapter( 163 outputSurfaceConfig.getImageAnalysisOutputSurface())); 164 } 165 166 mIsPostviewConfigured = outputSurfaceConfig.getPostviewOutputSurface() != null; 167 // Resets the extension type and strength result when initializing the session 168 if (mCurrentExtensionTypeLiveData != null) { 169 mCurrentExtensionTypeLiveData.postValue(mMode); 170 } 171 if (mExtensionStrengthLiveData != null) { 172 mExtensionStrengthLiveData.postValue(100); 173 } 174 // Convert Camera2SessionConfigImpl(implemented in OEM) into Camera2SessionConfig 175 return convertToCamera2SessionConfig(sessionConfigImpl); 176 } 177 convertToCamera2SessionConfig( @onNull Camera2SessionConfigImpl sessionConfigImpl)178 private Camera2SessionConfig convertToCamera2SessionConfig( 179 @NonNull Camera2SessionConfigImpl sessionConfigImpl) { 180 Camera2SessionConfigBuilder camera2SessionConfigBuilder = new Camera2SessionConfigBuilder(); 181 for (Camera2OutputConfigImpl outputConfigImpl : sessionConfigImpl.getOutputConfigs()) { 182 Camera2OutputConfig outputConfig = 183 Camera2OutputConfigConverter.fromImpl(outputConfigImpl); 184 camera2SessionConfigBuilder.addOutputConfig(outputConfig); 185 } 186 187 for (CaptureRequest.Key<?> key : sessionConfigImpl.getSessionParameters().keySet()) { 188 @SuppressWarnings("unchecked") 189 CaptureRequest.Key<Object> objKey = (CaptureRequest.Key<Object>) key; 190 camera2SessionConfigBuilder.addSessionParameter(objKey, 191 sessionConfigImpl.getSessionParameters().get(objKey)); 192 } 193 camera2SessionConfigBuilder 194 .setSessionTemplateId(sessionConfigImpl.getSessionTemplateId()); 195 if (ClientVersion.isMinimumCompatibleVersion(Version.VERSION_1_4) 196 && ExtensionVersion.isMinimumCompatibleVersion(Version.VERSION_1_4)) { 197 try { 198 int sessionType = sessionConfigImpl.getSessionType(); 199 if (sessionType == -1) { // -1 means using default value 200 sessionType = SessionConfiguration.SESSION_REGULAR; 201 } 202 camera2SessionConfigBuilder.setSessionType(sessionType); 203 } catch (NoSuchMethodError e) { 204 camera2SessionConfigBuilder.setSessionType(SessionConfiguration.SESSION_REGULAR); 205 } 206 } 207 return camera2SessionConfigBuilder.build(); 208 } 209 210 @Override deInitSessionInternal()211 protected void deInitSessionInternal() { 212 synchronized (mLock) { 213 // Clears the working config map 214 mWorkingCaptureConfigMap = new HashMap<>(); 215 mRepeatingCaptureCallbackAdapter = null; 216 } 217 mImpl.deInitSession(); 218 } 219 220 @Override isCurrentExtensionModeAvailable()221 public boolean isCurrentExtensionModeAvailable() { 222 return mVendorExtender.isCurrentExtensionModeAvailable(); 223 } 224 225 @Override getCurrentExtensionMode()226 public @NonNull LiveData<Integer> getCurrentExtensionMode() { 227 return mCurrentExtensionTypeLiveData; 228 } 229 230 @Override isExtensionStrengthAvailable()231 public boolean isExtensionStrengthAvailable() { 232 return mVendorExtender.isExtensionStrengthAvailable(); 233 } 234 235 @Override 236 @SuppressWarnings("unchecked") setExtensionStrength(@ntRangefrom = 0, to = 100) int strength)237 public void setExtensionStrength(@IntRange(from = 0, to = 100) int strength) { 238 if (!isExtensionStrengthAvailable() 239 || Build.VERSION.SDK_INT < Build.VERSION_CODES.UPSIDE_DOWN_CAKE) { 240 return; 241 } 242 243 SessionProcessorImplCaptureCallbackAdapter captureCallbackAdapter; 244 HashMap<CaptureRequest.Key<?>, Object> captureConfigMap; 245 246 synchronized (mLock) { 247 mExtensionStrength = strength; 248 mWorkingCaptureConfigMap.put(CaptureRequest.EXTENSION_STRENGTH, mExtensionStrength); 249 captureCallbackAdapter = mRepeatingCaptureCallbackAdapter; 250 captureConfigMap = 251 (HashMap<CaptureRequest.Key<?>, Object>) mWorkingCaptureConfigMap.clone(); 252 } 253 254 mImpl.setParameters(captureConfigMap); 255 256 // Starts the repeating again to apply the new strength setting if it has been started. 257 // Otherwise, the new strength setting will be applied when the capture session is 258 // configured and repeating is started. 259 if (captureCallbackAdapter != null) { 260 mImpl.startRepeating(captureCallbackAdapter); 261 } 262 } 263 264 @SuppressLint("KotlinPropertyAccess") 265 @Override getExtensionStrength()266 public @NonNull LiveData<Integer> getExtensionStrength() { 267 return mExtensionStrengthLiveData; 268 } 269 270 @Override setParameters( @onNull Config parameters)271 public void setParameters( 272 @NonNull Config parameters) { 273 HashMap<CaptureRequest.Key<?>, Object> captureConfigMap; 274 275 synchronized (mLock) { 276 captureConfigMap = convertConfigToMap(parameters); 277 // Applies extension strength setting if it is set via 278 // CameraExtensionsControl#setExtensionStrength() API. 279 if (mExtensionStrength != EXTENSION_STRENGTH_UNKNOWN 280 && Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) { 281 captureConfigMap.put(CaptureRequest.EXTENSION_STRENGTH, mExtensionStrength); 282 } 283 mWorkingCaptureConfigMap = captureConfigMap; 284 } 285 286 mImpl.setParameters(captureConfigMap); 287 } 288 convertConfigToMap( @onNull Config parameters)289 private static @NonNull HashMap<CaptureRequest.Key<?>, Object> convertConfigToMap( 290 @NonNull Config parameters) { 291 HashMap<CaptureRequest.Key<?>, Object> map = new HashMap<>(); 292 293 RequestOptionConfig options = 294 RequestOptionConfig.Builder.from(parameters).build(); 295 296 for (Config.Option<?> option : options.listOptions()) { 297 @SuppressWarnings("unchecked") 298 CaptureRequest.Key<Object> key = (CaptureRequest.Key<Object>) option.getToken(); 299 map.put(key, options.retrieveOption(option)); 300 } 301 return map; 302 } 303 304 @Override onCaptureSessionStart( @onNull RequestProcessor requestProcessor)305 public void onCaptureSessionStart( 306 @NonNull RequestProcessor requestProcessor) { 307 mImpl.onCaptureSessionStart(new RequestProcessorImplAdapter(requestProcessor)); 308 } 309 310 @Override onCaptureSessionEnd()311 public void onCaptureSessionEnd() { 312 mImpl.onCaptureSessionEnd(); 313 } 314 315 @Override startCapture( boolean postviewEnabled, @NonNull TagBundle tagBundle, SessionProcessor.@NonNull CaptureCallback callback)316 public int startCapture( 317 boolean postviewEnabled, 318 @NonNull TagBundle tagBundle, 319 SessionProcessor.@NonNull CaptureCallback callback) { 320 Logger.d(TAG, "startCapture postviewEnabled = " + postviewEnabled 321 + " mWillReceiveOnCaptureCompleted = " + mWillReceiveOnCaptureCompleted); 322 SessionProcessorImplCaptureCallbackAdapter stillCaptureCallback = 323 new SessionProcessorImplCaptureCallbackAdapter( 324 callback, tagBundle, mWillReceiveOnCaptureCompleted); 325 326 if (ClientVersion.isMinimumCompatibleVersion(Version.VERSION_1_4) 327 && ExtensionVersion.isMinimumCompatibleVersion(Version.VERSION_1_4) 328 && mIsPostviewConfigured && postviewEnabled 329 && mVendorExtender.isPostviewAvailable()) { 330 return mImpl.startCaptureWithPostview(stillCaptureCallback); 331 } else { 332 return mImpl.startCapture(stillCaptureCallback); 333 } 334 } 335 336 @Override startRepeating(@onNull TagBundle tagBundle, SessionProcessor.@NonNull CaptureCallback callback)337 public int startRepeating(@NonNull TagBundle tagBundle, 338 SessionProcessor.@NonNull CaptureCallback callback) { 339 SessionProcessorImplCaptureCallbackAdapter captureCallbackAdapter; 340 synchronized (mLock) { 341 captureCallbackAdapter = new SessionProcessorImplCaptureCallbackAdapter(callback, 342 tagBundle, mExtensionMetadataMonitor, mWillReceiveOnCaptureCompleted); 343 mRepeatingCaptureCallbackAdapter = captureCallbackAdapter; 344 } 345 return mImpl.startRepeating(captureCallbackAdapter); 346 } 347 348 @Override startTrigger(@onNull Config config, @NonNull TagBundle tagBundle, @NonNull CaptureCallback callback)349 public int startTrigger(@NonNull Config config, @NonNull TagBundle tagBundle, 350 @NonNull CaptureCallback callback) { 351 HashMap<CaptureRequest.Key<?>, Object> map = convertConfigToMap(config); 352 if (ClientVersion.isMinimumCompatibleVersion(Version.VERSION_1_3) 353 && ExtensionVersion.isMinimumCompatibleVersion(Version.VERSION_1_3)) { 354 return mImpl.startTrigger(map, 355 new SessionProcessorImplCaptureCallbackAdapter( 356 callback, tagBundle, mWillReceiveOnCaptureCompleted)); 357 } 358 return -1; 359 } 360 361 @Override stopRepeating()362 public void stopRepeating() { 363 mImpl.stopRepeating(); 364 synchronized (mLock) { 365 mRepeatingCaptureCallbackAdapter = null; 366 } 367 } 368 369 @Override abortCapture(int captureSequenceId)370 public void abortCapture(int captureSequenceId) { 371 mImpl.abortCapture(captureSequenceId); 372 } 373 374 @Override getRealtimeCaptureLatency()375 public @Nullable Pair<Long, Long> getRealtimeCaptureLatency() { 376 if (ClientVersion.isMinimumCompatibleVersion(Version.VERSION_1_4) 377 && ExtensionVersion.isMinimumCompatibleVersion(Version.VERSION_1_4)) { 378 return mImpl.getRealtimeCaptureLatency(); 379 } 380 return null; 381 } 382 383 @Override getSupportedPostviewSize(@onNull Size captureSize)384 public @NonNull Map<Integer, List<Size>> getSupportedPostviewSize(@NonNull Size captureSize) { 385 return mVendorExtender.getSupportedPostviewResolutions(captureSize); 386 } 387 388 @Override 389 public @NonNull List<Pair<CameraCharacteristics.Key, Object>> getAvailableCharacteristicsKeyValues()390 getAvailableCharacteristicsKeyValues() { 391 return mVendorExtender.getAvailableCharacteristicsKeyValues(); 392 } 393 394 /** 395 * Adapter to transform a {@link OutputSurface} to a {@link OutputSurfaceImpl}. 396 */ 397 private static class OutputSurfaceImplAdapter implements OutputSurfaceImpl { 398 private final OutputSurface mOutputSurface; 399 400 @SuppressWarnings("WeakerAccess") /* synthetic accessor */ OutputSurfaceImplAdapter(OutputSurface outputSurface)401 OutputSurfaceImplAdapter(OutputSurface outputSurface) { 402 mOutputSurface = outputSurface; 403 } 404 405 @Override getSurface()406 public @NonNull Surface getSurface() { 407 return mOutputSurface.getSurface(); 408 } 409 410 @Override getSize()411 public @NonNull Size getSize() { 412 return mOutputSurface.getSize(); 413 } 414 415 @Override getImageFormat()416 public int getImageFormat() { 417 return mOutputSurface.getImageFormat(); 418 } 419 } 420 421 private static class OutputSurfaceConfigurationImplAdapter implements 422 OutputSurfaceConfigurationImpl { 423 private final OutputSurfaceImpl mPreviewOutputSurface; 424 private final OutputSurfaceImpl mCaptureOutputSurface; 425 private final OutputSurfaceImpl mAnalysisOutputSurface; 426 private final OutputSurfaceImpl mPostviewOutputSurface; 427 OutputSurfaceConfigurationImplAdapter( @onNull OutputSurfaceConfiguration outputSurfaceConfig)428 OutputSurfaceConfigurationImplAdapter( 429 @NonNull OutputSurfaceConfiguration outputSurfaceConfig) { 430 mPreviewOutputSurface = new OutputSurfaceImplAdapter( 431 outputSurfaceConfig.getPreviewOutputSurface()); 432 mCaptureOutputSurface = new OutputSurfaceImplAdapter( 433 outputSurfaceConfig.getImageCaptureOutputSurface()); 434 mAnalysisOutputSurface = 435 outputSurfaceConfig.getImageAnalysisOutputSurface() != null 436 ? new OutputSurfaceImplAdapter( 437 outputSurfaceConfig.getImageAnalysisOutputSurface()) : null; 438 mPostviewOutputSurface = 439 outputSurfaceConfig.getPostviewOutputSurface() != null 440 ? new OutputSurfaceImplAdapter( 441 outputSurfaceConfig.getPostviewOutputSurface()) : null; 442 } 443 444 @Override getPreviewOutputSurface()445 public @NonNull OutputSurfaceImpl getPreviewOutputSurface() { 446 return mPreviewOutputSurface; 447 } 448 449 @Override getImageCaptureOutputSurface()450 public @NonNull OutputSurfaceImpl getImageCaptureOutputSurface() { 451 return mCaptureOutputSurface; 452 } 453 454 @Override getImageAnalysisOutputSurface()455 public @Nullable OutputSurfaceImpl getImageAnalysisOutputSurface() { 456 return mAnalysisOutputSurface; 457 } 458 459 @Override getPostviewOutputSurface()460 public @Nullable OutputSurfaceImpl getPostviewOutputSurface() { 461 return mPostviewOutputSurface; 462 } 463 } 464 465 /** 466 * Adapter to transform a {@link RequestProcessor} to {@link RequestProcessorImpl}. 467 */ 468 private class RequestProcessorImplAdapter implements RequestProcessorImpl { 469 private final RequestProcessor mRequestProcessor; 470 471 @SuppressWarnings("WeakerAccess") /* synthetic accessor */ RequestProcessorImplAdapter(@onNull RequestProcessor requestProcessor)472 RequestProcessorImplAdapter(@NonNull RequestProcessor requestProcessor) { 473 mRequestProcessor = requestProcessor; 474 } 475 476 @Override setImageProcessor(int outputConfigId, @NonNull ImageProcessorImpl imageProcessorImpl)477 public void setImageProcessor(int outputConfigId, 478 @NonNull ImageProcessorImpl imageProcessorImpl) { 479 AdvancedSessionProcessor.this.setImageProcessor(outputConfigId, 480 new ImageProcessorAdapter(imageProcessorImpl)); 481 } 482 483 @Override submit( RequestProcessorImpl.@onNull Request request, @NonNull Callback callback)484 public int submit( 485 RequestProcessorImpl.@NonNull Request request, @NonNull Callback callback) { 486 return mRequestProcessor.submit(new RequestAdapter(request), 487 new CallbackAdapter(callback)); 488 } 489 490 @Override submit( @onNull List<RequestProcessorImpl.Request> requests, @NonNull Callback callback)491 public int submit( 492 @NonNull List<RequestProcessorImpl.Request> requests, @NonNull Callback callback) { 493 ArrayList<RequestProcessor.Request> outRequests = new ArrayList<>(); 494 for (RequestProcessorImpl.Request request : requests) { 495 outRequests.add(new RequestAdapter(request)); 496 } 497 return mRequestProcessor.submit(outRequests, new CallbackAdapter(callback)); 498 } 499 500 @Override setRepeating( RequestProcessorImpl.@onNull Request request, @NonNull Callback callback)501 public int setRepeating( 502 RequestProcessorImpl.@NonNull Request request, @NonNull Callback callback) { 503 return mRequestProcessor.setRepeating(new RequestAdapter(request), 504 new CallbackAdapter(callback)); 505 } 506 507 @Override abortCaptures()508 public void abortCaptures() { 509 mRequestProcessor.abortCaptures(); 510 } 511 512 @Override stopRepeating()513 public void stopRepeating() { 514 mRequestProcessor.stopRepeating(); 515 } 516 } 517 518 /** 519 * Adapter to transform a {@link RequestProcessorImpl.Request} to a 520 * {@link RequestProcessor.Request}. 521 */ 522 private static class RequestAdapter implements RequestProcessor.Request { 523 private final RequestProcessorImpl.Request mImplRequest; 524 private final List<Integer> mTargetOutputConfigIds; 525 private final Config mParameters; 526 private final int mTemplateId; 527 528 @SuppressWarnings("WeakerAccess") /* synthetic accessor */ RequestAdapter(RequestProcessorImpl.@onNull Request implRequest)529 RequestAdapter(RequestProcessorImpl.@NonNull Request implRequest) { 530 mImplRequest = implRequest; 531 532 List<Integer> targetOutputConfigIds = new ArrayList<>(); 533 for (Integer outputConfigId : implRequest.getTargetOutputConfigIds()) { 534 targetOutputConfigIds.add(outputConfigId); 535 } 536 mTargetOutputConfigIds = targetOutputConfigIds; 537 538 RequestOptionConfig.Builder optionBuilder = new RequestOptionConfig.Builder(); 539 for (CaptureRequest.Key<?> key : implRequest.getParameters().keySet()) { 540 @SuppressWarnings("unchecked") 541 CaptureRequest.Key<Object> objKey = (CaptureRequest.Key<Object>) key; 542 optionBuilder.setCaptureRequestOption(objKey, 543 implRequest.getParameters().get(objKey)); 544 } 545 mParameters = optionBuilder.build(); 546 mTemplateId = implRequest.getTemplateId(); 547 } 548 549 @Override getTargetOutputConfigIds()550 public @NonNull List<Integer> getTargetOutputConfigIds() { 551 return mTargetOutputConfigIds; 552 } 553 554 @Override getParameters()555 public @NonNull Config getParameters() { 556 return mParameters; 557 } 558 559 @Override getTemplateId()560 public int getTemplateId() { 561 return mTemplateId; 562 } 563 getImplRequest()564 public RequestProcessorImpl.@Nullable Request getImplRequest() { 565 return mImplRequest; 566 } 567 } 568 569 /** 570 * Adapter to transform a {@link ImageProcessorImpl} to {@link ImageProcessor}. 571 */ 572 private static class ImageProcessorAdapter implements ImageProcessor { 573 private final ImageProcessorImpl mImpl; 574 575 @SuppressWarnings("WeakerAccess") /* synthetic accessor */ ImageProcessorAdapter(ImageProcessorImpl impl)576 ImageProcessorAdapter(ImageProcessorImpl impl) { 577 mImpl = impl; 578 } 579 580 @Override onNextImageAvailable(int outputStreamId, long timestampNs, @NonNull ImageReference imageReference, @Nullable String physicalCameraId)581 public void onNextImageAvailable(int outputStreamId, long timestampNs, 582 @NonNull ImageReference imageReference, @Nullable String physicalCameraId) { 583 mImpl.onNextImageAvailable(outputStreamId, timestampNs, 584 new ImageReferenceImplAdapter(imageReference), physicalCameraId); 585 } 586 } 587 588 /** 589 * Adapter to transform a {@link ImageReference} to a {@link ImageReferenceImpl}. 590 */ 591 private static class ImageReferenceImplAdapter implements ImageReferenceImpl { 592 private final ImageReference mImageReference; 593 594 @SuppressWarnings("WeakerAccess") /* synthetic accessor */ ImageReferenceImplAdapter(ImageReference imageReference)595 ImageReferenceImplAdapter(ImageReference imageReference) { 596 mImageReference = imageReference; 597 } 598 599 @Override increment()600 public boolean increment() { 601 return mImageReference.increment(); 602 } 603 604 @Override decrement()605 public boolean decrement() { 606 return mImageReference.decrement(); 607 } 608 609 @Override get()610 public @Nullable Image get() { 611 return mImageReference.get(); 612 } 613 } 614 615 /** 616 * Adapter to transform a {@link RequestProcessorImpl.Callback} to a 617 * {@link RequestProcessor.Callback}. 618 */ 619 private static class CallbackAdapter implements RequestProcessor.Callback { 620 private final RequestProcessorImpl.Callback mCallback; 621 622 @SuppressWarnings("WeakerAccess") /* synthetic accessor */ CallbackAdapter(RequestProcessorImpl.@onNull Callback callback)623 CallbackAdapter(RequestProcessorImpl.@NonNull Callback callback) { 624 mCallback = callback; 625 } 626 627 @Override onCaptureStarted( RequestProcessor.@onNull Request request, long frameNumber, long timestamp)628 public void onCaptureStarted( 629 RequestProcessor.@NonNull Request request, 630 long frameNumber, long timestamp) { 631 mCallback.onCaptureStarted(getImplRequest(request), frameNumber, 632 timestamp); 633 } 634 635 @Override onCaptureProgressed( RequestProcessor.@onNull Request request, @NonNull CameraCaptureResult cameraCaptureResult)636 public void onCaptureProgressed( 637 RequestProcessor.@NonNull Request request, 638 @NonNull CameraCaptureResult cameraCaptureResult) { 639 CaptureResult captureResult = cameraCaptureResult.getCaptureResult(); 640 Preconditions.checkArgument(captureResult != null, 641 "Cannot get CaptureResult from the cameraCaptureResult "); 642 mCallback.onCaptureProgressed(getImplRequest(request), captureResult); 643 } 644 645 @Override onCaptureCompleted( RequestProcessor.@onNull Request request, @Nullable CameraCaptureResult cameraCaptureResult)646 public void onCaptureCompleted( 647 RequestProcessor.@NonNull Request request, 648 @Nullable CameraCaptureResult cameraCaptureResult) { 649 CaptureResult captureResult = cameraCaptureResult.getCaptureResult(); 650 Preconditions.checkArgument(captureResult instanceof TotalCaptureResult, 651 "CaptureResult in cameraCaptureResult is not a TotalCaptureResult"); 652 mCallback.onCaptureCompleted(getImplRequest(request), 653 (TotalCaptureResult) captureResult); 654 } 655 656 @Override onCaptureFailed( RequestProcessor.@onNull Request request, @Nullable CameraCaptureFailure cameraCaptureFailure)657 public void onCaptureFailed( 658 RequestProcessor.@NonNull Request request, 659 @Nullable CameraCaptureFailure cameraCaptureFailure) { 660 Object captureFailure = cameraCaptureFailure.getCaptureFailure(); 661 Preconditions.checkArgument(captureFailure instanceof CaptureFailure, 662 "CameraCaptureFailure does not contain CaptureFailure."); 663 mCallback.onCaptureFailed(getImplRequest(request), (CaptureFailure) captureFailure); 664 } 665 666 @Override onCaptureBufferLost( RequestProcessor.@onNull Request request, long frameNumber, int outputStreamId)667 public void onCaptureBufferLost( 668 RequestProcessor.@NonNull Request request, 669 long frameNumber, int outputStreamId) { 670 mCallback.onCaptureBufferLost(getImplRequest(request), frameNumber, outputStreamId); 671 } 672 673 @Override onCaptureSequenceCompleted( int sequenceId, long frameNumber)674 public void onCaptureSequenceCompleted( 675 int sequenceId, long frameNumber) { 676 mCallback.onCaptureSequenceCompleted(sequenceId, frameNumber); 677 } 678 679 @Override onCaptureSequenceAborted(int sequenceId)680 public void onCaptureSequenceAborted(int sequenceId) { 681 mCallback.onCaptureSequenceAborted(sequenceId); 682 } 683 getImplRequest( RequestProcessor.Request request)684 private RequestProcessorImpl.Request getImplRequest( 685 RequestProcessor.Request request) { 686 Preconditions.checkArgument(request instanceof RequestAdapter); 687 688 RequestAdapter requestProcessorRequest = (RequestAdapter) request; 689 return requestProcessorRequest.getImplRequest(); 690 } 691 } 692 693 /** 694 * Adapter to transform a {@link SessionProcessor.CaptureCallback} to a 695 * {@link SessionProcessorImpl.CaptureCallback}. 696 */ 697 private static class SessionProcessorImplCaptureCallbackAdapter implements 698 SessionProcessorImpl.CaptureCallback { 699 private final SessionProcessor.CaptureCallback mCaptureCallback; 700 private final @Nullable ExtensionMetadataMonitor mExtensionMetadataMonitor; 701 private final @NonNull TagBundle mTagBundle; 702 private long mOnCaptureStartedTimestamp = -1; 703 private boolean mWillReceiveOnCaptureCompleted; 704 SessionProcessorImplCaptureCallbackAdapter( SessionProcessor.@onNull CaptureCallback callback, @NonNull TagBundle tagBundle, boolean willReceiveOnCaptureCompleted)705 SessionProcessorImplCaptureCallbackAdapter( 706 SessionProcessor.@NonNull CaptureCallback callback, 707 @NonNull TagBundle tagBundle, 708 boolean willReceiveOnCaptureCompleted) { 709 this(callback, tagBundle, null, willReceiveOnCaptureCompleted); 710 } 711 712 @SuppressWarnings("WeakerAccess") /* synthetic accessor */ SessionProcessorImplCaptureCallbackAdapter( SessionProcessor.@onNull CaptureCallback callback, @NonNull TagBundle tagBundle, @Nullable ExtensionMetadataMonitor extensionMetadataMonitor, boolean willReceiveOnCaptureCompleted)713 SessionProcessorImplCaptureCallbackAdapter( 714 SessionProcessor.@NonNull CaptureCallback callback, 715 @NonNull TagBundle tagBundle, 716 @Nullable ExtensionMetadataMonitor extensionMetadataMonitor, 717 boolean willReceiveOnCaptureCompleted) { 718 mCaptureCallback = callback; 719 mTagBundle = tagBundle; 720 mExtensionMetadataMonitor = extensionMetadataMonitor; 721 mWillReceiveOnCaptureCompleted = willReceiveOnCaptureCompleted; 722 } 723 724 @Override onCaptureStarted( int captureSequenceId, long timestamp)725 public void onCaptureStarted( 726 int captureSequenceId, 727 long timestamp) { 728 mOnCaptureStartedTimestamp = timestamp; 729 mCaptureCallback.onCaptureStarted(captureSequenceId, timestamp); 730 } 731 732 @Override onCaptureProcessStarted( int captureSequenceId)733 public void onCaptureProcessStarted( 734 int captureSequenceId) { 735 mCaptureCallback.onCaptureProcessStarted(captureSequenceId); 736 } 737 738 @Override onCaptureFailed(int captureSequenceId)739 public void onCaptureFailed(int captureSequenceId) { 740 mCaptureCallback.onCaptureFailed(captureSequenceId); 741 } 742 743 @Override onCaptureSequenceCompleted(int captureSequenceId)744 public void onCaptureSequenceCompleted(int captureSequenceId) { 745 if (!mWillReceiveOnCaptureCompleted) { 746 // If SessionProcessorImpl.CaptureCallback.onCaptureCompleted won't be invoked, 747 // We finish the capture sequence using the timestamp retrieved at onCaptureStarted 748 // when onCaptureSequenceCompleted is invoked. 749 mCaptureCallback.onCaptureCompleted(mOnCaptureStartedTimestamp, 750 captureSequenceId, 751 new KeyValueMapCameraCaptureResult( 752 mOnCaptureStartedTimestamp, mTagBundle, Collections.emptyMap())); 753 mCaptureCallback.onCaptureSequenceCompleted(captureSequenceId); 754 } 755 } 756 757 @Override onCaptureSequenceAborted(int captureSequenceId)758 public void onCaptureSequenceAborted(int captureSequenceId) { 759 mCaptureCallback.onCaptureSequenceAborted(captureSequenceId); 760 } 761 762 @Override onCaptureCompleted(long timestamp, int captureSequenceId, Map<CaptureResult.Key, Object> result)763 public void onCaptureCompleted(long timestamp, int captureSequenceId, 764 Map<CaptureResult.Key, Object> result) { 765 if (mExtensionMetadataMonitor != null) { 766 mExtensionMetadataMonitor.checkExtensionMetadata(result); 767 } 768 if (mWillReceiveOnCaptureCompleted) { 769 mCaptureCallback.onCaptureCompleted(timestamp, captureSequenceId, 770 new KeyValueMapCameraCaptureResult(timestamp, mTagBundle, result)); 771 mCaptureCallback.onCaptureSequenceCompleted(captureSequenceId); 772 } 773 } 774 775 @Override onCaptureProcessProgressed(int progress)776 public void onCaptureProcessProgressed(int progress) { 777 mCaptureCallback.onCaptureProcessProgressed(progress); 778 } 779 } 780 781 /** 782 * Monitors the extension metadata (extension strength, type) changes from the capture results. 783 */ 784 private static class ExtensionMetadataMonitor { 785 private final @Nullable MutableLiveData<Integer> mCurrentExtensionTypeLiveData; 786 private final @Nullable MutableLiveData<Integer> mExtensionStrengthLiveData; 787 ExtensionMetadataMonitor( @ullable MutableLiveData<Integer> currentExtensionTypeLiveData, @Nullable MutableLiveData<Integer> extensionStrengthLiveData)788 ExtensionMetadataMonitor( 789 @Nullable MutableLiveData<Integer> currentExtensionTypeLiveData, 790 @Nullable MutableLiveData<Integer> extensionStrengthLiveData) { 791 mCurrentExtensionTypeLiveData = currentExtensionTypeLiveData; 792 mExtensionStrengthLiveData = extensionStrengthLiveData; 793 } 794 checkExtensionMetadata(Map<CaptureResult.Key, Object> captureResult)795 void checkExtensionMetadata(Map<CaptureResult.Key, Object> captureResult) { 796 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) { 797 798 if (mCurrentExtensionTypeLiveData != null) { 799 // Monitors and update current extension type 800 Object extensionType = captureResult.get(CaptureResult.EXTENSION_CURRENT_TYPE); 801 // The returned type should be the value defined by the Camera2 API. 802 // Needs to 803 // convert it to the value defined by CameraX. 804 if (extensionType != null && !Objects.equals( 805 mCurrentExtensionTypeLiveData.getValue(), 806 convertExtensionMode((int) extensionType))) { 807 mCurrentExtensionTypeLiveData.postValue( 808 convertExtensionMode((int) extensionType)); 809 } 810 } 811 812 if (mExtensionStrengthLiveData != null) { 813 // Monitors and update current extension strength 814 Object extensionStrength = captureResult.get(CaptureResult.EXTENSION_STRENGTH); 815 if (extensionStrength != null && !Objects.equals( 816 mExtensionStrengthLiveData.getValue(), extensionStrength)) { 817 mExtensionStrengthLiveData.postValue((Integer) extensionStrength); 818 } 819 } 820 } 821 } 822 823 @ExtensionMode.Mode convertExtensionMode(int camera2ExtensionMode)824 private int convertExtensionMode(int camera2ExtensionMode) { 825 switch (camera2ExtensionMode) { 826 case EXTENSION_AUTOMATIC: 827 return ExtensionMode.AUTO; 828 case EXTENSION_FACE_RETOUCH: 829 return ExtensionMode.FACE_RETOUCH; 830 case EXTENSION_BOKEH: 831 return ExtensionMode.BOKEH; 832 case EXTENSION_HDR: 833 return ExtensionMode.HDR; 834 case EXTENSION_NIGHT: 835 return ExtensionMode.NIGHT; 836 default: 837 return ExtensionMode.NONE; 838 } 839 } 840 } 841 } 842