1 /* 2 * Copyright (C) 2014 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 android.hardware.camera2.legacy; 18 19 import android.graphics.ImageFormat; 20 import android.graphics.SurfaceTexture; 21 import android.hardware.Camera; 22 import android.hardware.camera2.CameraCharacteristics; 23 import android.hardware.camera2.CaptureRequest; 24 import android.hardware.camera2.impl.CameraDeviceImpl; 25 import android.hardware.camera2.impl.CaptureResultExtras; 26 import android.hardware.camera2.impl.PhysicalCaptureResultInfo; 27 import android.hardware.camera2.ICameraDeviceCallbacks; 28 import android.hardware.camera2.params.StreamConfigurationMap; 29 import android.hardware.camera2.utils.ArrayUtils; 30 import android.hardware.camera2.utils.SubmitInfo; 31 import android.hardware.camera2.impl.CameraMetadataNative; 32 import android.os.ConditionVariable; 33 import android.os.Handler; 34 import android.os.HandlerThread; 35 import android.os.RemoteException; 36 import android.os.ServiceSpecificException; 37 import android.util.Log; 38 import android.util.Pair; 39 import android.util.Size; 40 import android.util.SparseArray; 41 import android.view.Surface; 42 43 import java.util.ArrayList; 44 import java.util.Arrays; 45 import java.util.Collection; 46 import java.util.List; 47 48 import static android.hardware.camera2.legacy.LegacyExceptionUtils.*; 49 import static com.android.internal.util.Preconditions.*; 50 51 /** 52 * This class emulates the functionality of a Camera2 device using a the old Camera class. 53 * 54 * <p> 55 * There are two main components that are used to implement this: 56 * - A state machine containing valid Camera2 device states ({@link CameraDeviceState}). 57 * - A message-queue based pipeline that manages an old Camera class, and executes capture and 58 * configuration requests. 59 * </p> 60 */ 61 public class LegacyCameraDevice implements AutoCloseable { 62 private final String TAG; 63 64 private static final boolean DEBUG = false; 65 private final int mCameraId; 66 private final CameraCharacteristics mStaticCharacteristics; 67 private final ICameraDeviceCallbacks mDeviceCallbacks; 68 private final CameraDeviceState mDeviceState = new CameraDeviceState(); 69 private SparseArray<Surface> mConfiguredSurfaces; 70 private boolean mClosed = false; 71 72 private final ConditionVariable mIdle = new ConditionVariable(/*open*/true); 73 74 private final HandlerThread mResultThread = new HandlerThread("ResultThread"); 75 private final HandlerThread mCallbackHandlerThread = new HandlerThread("CallbackThread"); 76 private final Handler mCallbackHandler; 77 private final Handler mResultHandler; 78 private static final int ILLEGAL_VALUE = -1; 79 80 // Keep up to date with values in hardware/libhardware/include/hardware/gralloc.h 81 private static final int GRALLOC_USAGE_RENDERSCRIPT = 0x00100000; 82 private static final int GRALLOC_USAGE_SW_READ_OFTEN = 0x00000003; 83 private static final int GRALLOC_USAGE_HW_TEXTURE = 0x00000100; 84 private static final int GRALLOC_USAGE_HW_COMPOSER = 0x00000800; 85 private static final int GRALLOC_USAGE_HW_RENDER = 0x00000200; 86 private static final int GRALLOC_USAGE_HW_VIDEO_ENCODER = 0x00010000; 87 88 public static final int MAX_DIMEN_FOR_ROUNDING = 1920; // maximum allowed width for rounding 89 90 // Keep up to date with values in system/core/include/system/window.h 91 public static final int NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW = 1; 92 getExtrasFromRequest(RequestHolder holder)93 private CaptureResultExtras getExtrasFromRequest(RequestHolder holder) { 94 return getExtrasFromRequest(holder, 95 /*errorCode*/CameraDeviceState.NO_CAPTURE_ERROR, /*errorArg*/null); 96 } 97 getExtrasFromRequest(RequestHolder holder, int errorCode, Object errorArg)98 private CaptureResultExtras getExtrasFromRequest(RequestHolder holder, 99 int errorCode, Object errorArg) { 100 int errorStreamId = -1; 101 if (errorCode == CameraDeviceImpl.CameraDeviceCallbacks.ERROR_CAMERA_BUFFER) { 102 Surface errorTarget = (Surface) errorArg; 103 int indexOfTarget = mConfiguredSurfaces.indexOfValue(errorTarget); 104 if (indexOfTarget < 0) { 105 Log.e(TAG, "Buffer drop error reported for unknown Surface"); 106 } else { 107 errorStreamId = mConfiguredSurfaces.keyAt(indexOfTarget); 108 } 109 } 110 if (holder == null) { 111 return new CaptureResultExtras(ILLEGAL_VALUE, ILLEGAL_VALUE, ILLEGAL_VALUE, 112 ILLEGAL_VALUE, ILLEGAL_VALUE, ILLEGAL_VALUE, ILLEGAL_VALUE, null, 113 ILLEGAL_VALUE, ILLEGAL_VALUE, ILLEGAL_VALUE); 114 } 115 return new CaptureResultExtras(holder.getRequestId(), holder.getSubsequeceId(), 116 /*afTriggerId*/0, /*precaptureTriggerId*/0, holder.getFrameNumber(), 117 /*partialResultCount*/1, errorStreamId, null, holder.getFrameNumber(), -1, -1); 118 } 119 120 /** 121 * Listener for the camera device state machine. Calls the appropriate 122 * {@link ICameraDeviceCallbacks} for each state transition. 123 */ 124 private final CameraDeviceState.CameraDeviceStateListener mStateListener = 125 new CameraDeviceState.CameraDeviceStateListener() { 126 @Override 127 public void onError(final int errorCode, final Object errorArg, final RequestHolder holder) { 128 if (DEBUG) { 129 Log.d(TAG, "onError called, errorCode = " + errorCode + ", errorArg = " + errorArg); 130 } 131 switch (errorCode) { 132 /* 133 * Only be considered idle if we hit a fatal error 134 * and no further requests can be processed. 135 */ 136 case CameraDeviceImpl.CameraDeviceCallbacks.ERROR_CAMERA_DISCONNECTED: 137 case CameraDeviceImpl.CameraDeviceCallbacks.ERROR_CAMERA_SERVICE: 138 case CameraDeviceImpl.CameraDeviceCallbacks.ERROR_CAMERA_DEVICE: { 139 mIdle.open(); 140 141 if (DEBUG) { 142 Log.d(TAG, "onError - opening idle"); 143 } 144 } 145 } 146 147 final CaptureResultExtras extras = getExtrasFromRequest(holder, errorCode, errorArg); 148 mResultHandler.post(new Runnable() { 149 @Override 150 public void run() { 151 if (DEBUG) { 152 Log.d(TAG, "doing onError callback for request " + holder.getRequestId() + 153 ", with error code " + errorCode); 154 } 155 try { 156 mDeviceCallbacks.onDeviceError(errorCode, extras); 157 } catch (RemoteException e) { 158 throw new IllegalStateException( 159 "Received remote exception during onCameraError callback: ", e); 160 } 161 } 162 }); 163 } 164 165 @Override 166 public void onConfiguring() { 167 // Do nothing 168 if (DEBUG) { 169 Log.d(TAG, "doing onConfiguring callback."); 170 } 171 } 172 173 @Override 174 public void onIdle() { 175 if (DEBUG) { 176 Log.d(TAG, "onIdle called"); 177 } 178 179 mIdle.open(); 180 181 mResultHandler.post(new Runnable() { 182 @Override 183 public void run() { 184 if (DEBUG) { 185 Log.d(TAG, "doing onIdle callback."); 186 } 187 try { 188 mDeviceCallbacks.onDeviceIdle(); 189 } catch (RemoteException e) { 190 throw new IllegalStateException( 191 "Received remote exception during onCameraIdle callback: ", e); 192 } 193 } 194 }); 195 } 196 197 @Override 198 public void onBusy() { 199 mIdle.close(); 200 201 if (DEBUG) { 202 Log.d(TAG, "onBusy called"); 203 } 204 } 205 206 @Override 207 public void onCaptureStarted(final RequestHolder holder, final long timestamp) { 208 final CaptureResultExtras extras = getExtrasFromRequest(holder); 209 210 mResultHandler.post(new Runnable() { 211 @Override 212 public void run() { 213 if (DEBUG) { 214 Log.d(TAG, "doing onCaptureStarted callback for request " + 215 holder.getRequestId()); 216 } 217 try { 218 mDeviceCallbacks.onCaptureStarted(extras, timestamp); 219 } catch (RemoteException e) { 220 throw new IllegalStateException( 221 "Received remote exception during onCameraError callback: ", e); 222 } 223 } 224 }); 225 } 226 227 @Override 228 public void onRequestQueueEmpty() { 229 mResultHandler.post(new Runnable() { 230 @Override 231 public void run() { 232 if (DEBUG) { 233 Log.d(TAG, "doing onRequestQueueEmpty callback"); 234 } 235 try { 236 mDeviceCallbacks.onRequestQueueEmpty(); 237 } catch (RemoteException e) { 238 throw new IllegalStateException( 239 "Received remote exception during onRequestQueueEmpty callback: ", 240 e); 241 } 242 } 243 }); 244 } 245 246 @Override 247 public void onCaptureResult(final CameraMetadataNative result, final RequestHolder holder) { 248 final CaptureResultExtras extras = getExtrasFromRequest(holder); 249 250 mResultHandler.post(new Runnable() { 251 @Override 252 public void run() { 253 if (DEBUG) { 254 Log.d(TAG, "doing onCaptureResult callback for request " + 255 holder.getRequestId()); 256 } 257 try { 258 mDeviceCallbacks.onResultReceived(result, extras, 259 new PhysicalCaptureResultInfo[0]); 260 } catch (RemoteException e) { 261 throw new IllegalStateException( 262 "Received remote exception during onCameraError callback: ", e); 263 } 264 } 265 }); 266 } 267 268 @Override 269 public void onRepeatingRequestError(final long lastFrameNumber, 270 final int repeatingRequestId) { 271 mResultHandler.post(new Runnable() { 272 @Override 273 public void run() { 274 if (DEBUG) { 275 Log.d(TAG, "doing onRepeatingRequestError callback."); 276 } 277 try { 278 mDeviceCallbacks.onRepeatingRequestError(lastFrameNumber, 279 repeatingRequestId); 280 } catch (RemoteException e) { 281 throw new IllegalStateException( 282 "Received remote exception during onRepeatingRequestError " + 283 "callback: ", e); 284 } 285 } 286 }); 287 } 288 }; 289 290 private final RequestThreadManager mRequestThreadManager; 291 292 /** 293 * Check if a given surface uses {@link ImageFormat#YUV_420_888} or format that can be readily 294 * converted to this; YV12 and NV21 are the two currently supported formats. 295 * 296 * @param s the surface to check. 297 * @return {@code true} if the surfaces uses {@link ImageFormat#YUV_420_888} or a compatible 298 * format. 299 */ needsConversion(Surface s)300 static boolean needsConversion(Surface s) throws BufferQueueAbandonedException { 301 int nativeType = detectSurfaceType(s); 302 return nativeType == ImageFormat.YUV_420_888 || nativeType == ImageFormat.YV12 || 303 nativeType == ImageFormat.NV21; 304 } 305 306 /** 307 * Create a new emulated camera device from a given Camera 1 API camera. 308 * 309 * <p> 310 * The {@link Camera} provided to this constructor must already have been successfully opened, 311 * and ownership of the provided camera is passed to this object. No further calls to the 312 * camera methods should be made following this constructor. 313 * </p> 314 * 315 * @param cameraId the id of the camera. 316 * @param camera an open {@link Camera} device. 317 * @param characteristics the static camera characteristics for this camera device 318 * @param callbacks {@link ICameraDeviceCallbacks} callbacks to call for Camera2 API operations. 319 */ LegacyCameraDevice(int cameraId, Camera camera, CameraCharacteristics characteristics, ICameraDeviceCallbacks callbacks)320 public LegacyCameraDevice(int cameraId, Camera camera, CameraCharacteristics characteristics, 321 ICameraDeviceCallbacks callbacks) { 322 mCameraId = cameraId; 323 mDeviceCallbacks = callbacks; 324 TAG = String.format("CameraDevice-%d-LE", mCameraId); 325 326 mResultThread.start(); 327 mResultHandler = new Handler(mResultThread.getLooper()); 328 mCallbackHandlerThread.start(); 329 mCallbackHandler = new Handler(mCallbackHandlerThread.getLooper()); 330 mDeviceState.setCameraDeviceCallbacks(mCallbackHandler, mStateListener); 331 mStaticCharacteristics = characteristics; 332 mRequestThreadManager = 333 new RequestThreadManager(cameraId, camera, characteristics, mDeviceState); 334 mRequestThreadManager.start(); 335 } 336 337 /** 338 * Configure the device with a set of output surfaces. 339 * 340 * <p>Using empty or {@code null} {@code outputs} is the same as unconfiguring.</p> 341 * 342 * <p>Every surface in {@code outputs} must be non-{@code null}.</p> 343 * 344 * @param outputs a list of surfaces to set. LegacyCameraDevice will take ownership of this 345 * list; it must not be modified by the caller once it's passed in. 346 * @return an error code for this binder operation, or {@link NO_ERROR} 347 * on success. 348 */ configureOutputs(SparseArray<Surface> outputs)349 public int configureOutputs(SparseArray<Surface> outputs) { 350 return configureOutputs(outputs, /*validateSurfacesOnly*/false); 351 } 352 353 /** 354 * Configure the device with a set of output surfaces. 355 * 356 * <p>Using empty or {@code null} {@code outputs} is the same as unconfiguring.</p> 357 * 358 * <p>Every surface in {@code outputs} must be non-{@code null}.</p> 359 * 360 * @param outputs a list of surfaces to set. LegacyCameraDevice will take ownership of this 361 * list; it must not be modified by the caller once it's passed in. 362 * @param validateSurfacesOnly If set it will only check whether the outputs are supported 363 * and avoid any device configuration. 364 * @return an error code for this binder operation, or {@link NO_ERROR} 365 * on success. 366 * @hide 367 */ configureOutputs(SparseArray<Surface> outputs, boolean validateSurfacesOnly)368 public int configureOutputs(SparseArray<Surface> outputs, boolean validateSurfacesOnly) { 369 List<Pair<Surface, Size>> sizedSurfaces = new ArrayList<>(); 370 if (outputs != null) { 371 int count = outputs.size(); 372 for (int i = 0; i < count; i++) { 373 Surface output = outputs.valueAt(i); 374 if (output == null) { 375 Log.e(TAG, "configureOutputs - null outputs are not allowed"); 376 return BAD_VALUE; 377 } 378 if (!output.isValid()) { 379 Log.e(TAG, "configureOutputs - invalid output surfaces are not allowed"); 380 return BAD_VALUE; 381 } 382 StreamConfigurationMap streamConfigurations = mStaticCharacteristics. 383 get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP); 384 385 // Validate surface size and format. 386 try { 387 Size s = getSurfaceSize(output); 388 int surfaceType = detectSurfaceType(output); 389 390 boolean flexibleConsumer = isFlexibleConsumer(output); 391 392 Size[] sizes = streamConfigurations.getOutputSizes(surfaceType); 393 if (sizes == null) { 394 if (surfaceType == ImageFormat.PRIVATE) { 395 396 // YUV_420_888 is always present in LEGACY for all 397 // IMPLEMENTATION_DEFINED output sizes, and is publicly visible in the 398 // API (i.e. {@code #getOutputSizes} works here). 399 sizes = streamConfigurations.getOutputSizes(ImageFormat.YUV_420_888); 400 } else if (surfaceType == LegacyMetadataMapper.HAL_PIXEL_FORMAT_BLOB) { 401 sizes = streamConfigurations.getOutputSizes(ImageFormat.JPEG); 402 } 403 } 404 405 if (!ArrayUtils.contains(sizes, s)) { 406 if (flexibleConsumer && (s = findClosestSize(s, sizes)) != null) { 407 sizedSurfaces.add(new Pair<>(output, s)); 408 } else { 409 String reason = (sizes == null) ? "format is invalid." : 410 ("size not in valid set: " + Arrays.toString(sizes)); 411 Log.e(TAG, String.format("Surface with size (w=%d, h=%d) and format " + 412 "0x%x is not valid, %s", s.getWidth(), s.getHeight(), 413 surfaceType, reason)); 414 return BAD_VALUE; 415 } 416 } else { 417 sizedSurfaces.add(new Pair<>(output, s)); 418 } 419 // Lock down the size before configuration 420 if (!validateSurfacesOnly) { 421 setSurfaceDimens(output, s.getWidth(), s.getHeight()); 422 } 423 } catch (BufferQueueAbandonedException e) { 424 Log.e(TAG, "Surface bufferqueue is abandoned, cannot configure as output: ", e); 425 return BAD_VALUE; 426 } 427 428 } 429 } 430 431 if (validateSurfacesOnly) { 432 return LegacyExceptionUtils.NO_ERROR; 433 } 434 435 boolean success = false; 436 if (mDeviceState.setConfiguring()) { 437 mRequestThreadManager.configure(sizedSurfaces); 438 success = mDeviceState.setIdle(); 439 } 440 441 if (success) { 442 mConfiguredSurfaces = outputs; 443 } else { 444 return LegacyExceptionUtils.INVALID_OPERATION; 445 } 446 return LegacyExceptionUtils.NO_ERROR; 447 } 448 449 /** 450 * Submit a burst of capture requests. 451 * 452 * @param requestList a list of capture requests to execute. 453 * @param repeating {@code true} if this burst is repeating. 454 * @return the submission info, including the new request id, and the last frame number, which 455 * contains either the frame number of the last frame that will be returned for this request, 456 * or the frame number of the last frame that will be returned for the current repeating 457 * request if this burst is set to be repeating. 458 */ submitRequestList(CaptureRequest[] requestList, boolean repeating)459 public SubmitInfo submitRequestList(CaptureRequest[] requestList, boolean repeating) { 460 if (requestList == null || requestList.length == 0) { 461 Log.e(TAG, "submitRequestList - Empty/null requests are not allowed"); 462 throw new ServiceSpecificException(BAD_VALUE, 463 "submitRequestList - Empty/null requests are not allowed"); 464 } 465 466 List<Long> surfaceIds; 467 468 try { 469 surfaceIds = (mConfiguredSurfaces == null) ? new ArrayList<Long>() : 470 getSurfaceIds(mConfiguredSurfaces); 471 } catch (BufferQueueAbandonedException e) { 472 throw new ServiceSpecificException(BAD_VALUE, 473 "submitRequestList - configured surface is abandoned."); 474 } 475 476 // Make sure that there all requests have at least 1 surface; all surfaces are non-null 477 for (CaptureRequest request : requestList) { 478 if (request.getTargets().isEmpty()) { 479 Log.e(TAG, "submitRequestList - " 480 + "Each request must have at least one Surface target"); 481 throw new ServiceSpecificException(BAD_VALUE, 482 "submitRequestList - " 483 + "Each request must have at least one Surface target"); 484 } 485 486 for (Surface surface : request.getTargets()) { 487 if (surface == null) { 488 Log.e(TAG, "submitRequestList - Null Surface targets are not allowed"); 489 throw new ServiceSpecificException(BAD_VALUE, 490 "submitRequestList - Null Surface targets are not allowed"); 491 } else if (mConfiguredSurfaces == null) { 492 Log.e(TAG, "submitRequestList - must configure " + 493 " device with valid surfaces before submitting requests"); 494 throw new ServiceSpecificException(INVALID_OPERATION, 495 "submitRequestList - must configure " + 496 " device with valid surfaces before submitting requests"); 497 } else if (!containsSurfaceId(surface, surfaceIds)) { 498 Log.e(TAG, "submitRequestList - cannot use a surface that wasn't configured"); 499 throw new ServiceSpecificException(BAD_VALUE, 500 "submitRequestList - cannot use a surface that wasn't configured"); 501 } 502 } 503 } 504 505 // TODO: further validation of request here 506 mIdle.close(); 507 return mRequestThreadManager.submitCaptureRequests(requestList, repeating); 508 } 509 510 /** 511 * Submit a single capture request. 512 * 513 * @param request the capture request to execute. 514 * @param repeating {@code true} if this request is repeating. 515 * @return the submission info, including the new request id, and the last frame number, which 516 * contains either the frame number of the last frame that will be returned for this request, 517 * or the frame number of the last frame that will be returned for the current repeating 518 * request if this burst is set to be repeating. 519 */ submitRequest(CaptureRequest request, boolean repeating)520 public SubmitInfo submitRequest(CaptureRequest request, boolean repeating) { 521 CaptureRequest[] requestList = { request }; 522 return submitRequestList(requestList, repeating); 523 } 524 525 /** 526 * Cancel the repeating request with the given request id. 527 * 528 * @param requestId the request id of the request to cancel. 529 * @return the last frame number to be returned from the HAL for the given repeating request, or 530 * {@code INVALID_FRAME} if none exists. 531 */ cancelRequest(int requestId)532 public long cancelRequest(int requestId) { 533 return mRequestThreadManager.cancelRepeating(requestId); 534 } 535 536 /** 537 * Block until the {@link ICameraDeviceCallbacks#onCameraIdle()} callback is received. 538 */ waitUntilIdle()539 public void waitUntilIdle() { 540 mIdle.block(); 541 } 542 543 /** 544 * Flush any pending requests. 545 * 546 * @return the last frame number. 547 */ flush()548 public long flush() { 549 long lastFrame = mRequestThreadManager.flush(); 550 waitUntilIdle(); 551 return lastFrame; 552 } 553 setAudioRestriction(int mode)554 public void setAudioRestriction(int mode) { 555 mRequestThreadManager.setAudioRestriction(mode); 556 } 557 getAudioRestriction()558 public int getAudioRestriction() { 559 return mRequestThreadManager.getAudioRestriction(); 560 } 561 562 /** 563 * Return {@code true} if the device has been closed. 564 */ isClosed()565 public boolean isClosed() { 566 return mClosed; 567 } 568 569 @Override close()570 public void close() { 571 mRequestThreadManager.quit(); 572 mCallbackHandlerThread.quitSafely(); 573 mResultThread.quitSafely(); 574 575 try { 576 mCallbackHandlerThread.join(); 577 } catch (InterruptedException e) { 578 Log.e(TAG, String.format("Thread %s (%d) interrupted while quitting.", 579 mCallbackHandlerThread.getName(), mCallbackHandlerThread.getId())); 580 } 581 582 try { 583 mResultThread.join(); 584 } catch (InterruptedException e) { 585 Log.e(TAG, String.format("Thread %s (%d) interrupted while quitting.", 586 mResultThread.getName(), mResultThread.getId())); 587 } 588 589 mClosed = true; 590 } 591 592 @Override finalize()593 protected void finalize() throws Throwable { 594 try { 595 close(); 596 } catch (ServiceSpecificException e) { 597 Log.e(TAG, "Got error while trying to finalize, ignoring: " + e.getMessage()); 598 } finally { 599 super.finalize(); 600 } 601 } 602 findEuclidDistSquare(Size a, Size b)603 static long findEuclidDistSquare(Size a, Size b) { 604 long d0 = a.getWidth() - b.getWidth(); 605 long d1 = a.getHeight() - b.getHeight(); 606 return d0 * d0 + d1 * d1; 607 } 608 609 // Keep up to date with rounding behavior in 610 // frameworks/av/services/camera/libcameraservice/api2/CameraDeviceClient.cpp findClosestSize(Size size, Size[] supportedSizes)611 static Size findClosestSize(Size size, Size[] supportedSizes) { 612 if (size == null || supportedSizes == null) { 613 return null; 614 } 615 Size bestSize = null; 616 for (Size s : supportedSizes) { 617 if (s.equals(size)) { 618 return size; 619 } else if (s.getWidth() <= MAX_DIMEN_FOR_ROUNDING && (bestSize == null || 620 LegacyCameraDevice.findEuclidDistSquare(size, s) < 621 LegacyCameraDevice.findEuclidDistSquare(bestSize, s))) { 622 bestSize = s; 623 } 624 } 625 return bestSize; 626 } 627 628 /** 629 * Query the surface for its currently configured default buffer size. 630 * @param surface a non-{@code null} {@code Surface} 631 * @return the width and height of the surface 632 * 633 * @throws NullPointerException if the {@code surface} was {@code null} 634 * @throws BufferQueueAbandonedException if the {@code surface} was invalid 635 */ getSurfaceSize(Surface surface)636 public static Size getSurfaceSize(Surface surface) throws BufferQueueAbandonedException { 637 checkNotNull(surface); 638 639 int[] dimens = new int[2]; 640 LegacyExceptionUtils.throwOnError(nativeDetectSurfaceDimens(surface, /*out*/dimens)); 641 642 return new Size(dimens[0], dimens[1]); 643 } 644 isFlexibleConsumer(Surface output)645 public static boolean isFlexibleConsumer(Surface output) { 646 int usageFlags = detectSurfaceUsageFlags(output); 647 648 // Keep up to date with allowed consumer types in 649 // frameworks/av/services/camera/libcameraservice/api2/CameraDeviceClient.cpp 650 int disallowedFlags = GRALLOC_USAGE_HW_VIDEO_ENCODER | GRALLOC_USAGE_RENDERSCRIPT; 651 int allowedFlags = GRALLOC_USAGE_HW_TEXTURE | GRALLOC_USAGE_SW_READ_OFTEN | 652 GRALLOC_USAGE_HW_COMPOSER; 653 boolean flexibleConsumer = ((usageFlags & disallowedFlags) == 0 && 654 (usageFlags & allowedFlags) != 0); 655 return flexibleConsumer; 656 } 657 isPreviewConsumer(Surface output)658 public static boolean isPreviewConsumer(Surface output) { 659 int usageFlags = detectSurfaceUsageFlags(output); 660 int disallowedFlags = GRALLOC_USAGE_HW_VIDEO_ENCODER | GRALLOC_USAGE_RENDERSCRIPT | 661 GRALLOC_USAGE_SW_READ_OFTEN; 662 int allowedFlags = GRALLOC_USAGE_HW_TEXTURE | GRALLOC_USAGE_HW_COMPOSER | 663 GRALLOC_USAGE_HW_RENDER; 664 boolean previewConsumer = ((usageFlags & disallowedFlags) == 0 && 665 (usageFlags & allowedFlags) != 0); 666 int surfaceFormat = ImageFormat.UNKNOWN; 667 try { 668 surfaceFormat = detectSurfaceType(output); 669 } catch(BufferQueueAbandonedException e) { 670 throw new IllegalArgumentException("Surface was abandoned", e); 671 } 672 673 return previewConsumer; 674 } 675 isVideoEncoderConsumer(Surface output)676 public static boolean isVideoEncoderConsumer(Surface output) { 677 int usageFlags = detectSurfaceUsageFlags(output); 678 int disallowedFlags = GRALLOC_USAGE_HW_TEXTURE | GRALLOC_USAGE_HW_COMPOSER | 679 GRALLOC_USAGE_RENDERSCRIPT | GRALLOC_USAGE_SW_READ_OFTEN; 680 int allowedFlags = GRALLOC_USAGE_HW_VIDEO_ENCODER; 681 boolean videoEncoderConsumer = ((usageFlags & disallowedFlags) == 0 && 682 (usageFlags & allowedFlags) != 0); 683 684 int surfaceFormat = ImageFormat.UNKNOWN; 685 try { 686 surfaceFormat = detectSurfaceType(output); 687 } catch(BufferQueueAbandonedException e) { 688 throw new IllegalArgumentException("Surface was abandoned", e); 689 } 690 691 return videoEncoderConsumer; 692 } 693 694 /** 695 * Query the surface for its currently configured usage flags 696 */ detectSurfaceUsageFlags(Surface surface)697 static int detectSurfaceUsageFlags(Surface surface) { 698 checkNotNull(surface); 699 return nativeDetectSurfaceUsageFlags(surface); 700 } 701 702 /** 703 * Query the surface for its currently configured format 704 */ detectSurfaceType(Surface surface)705 public static int detectSurfaceType(Surface surface) throws BufferQueueAbandonedException { 706 checkNotNull(surface); 707 int surfaceType = nativeDetectSurfaceType(surface); 708 709 // TODO: remove this override since the default format should be 710 // ImageFormat.PRIVATE. b/9487482 711 if ((surfaceType >= LegacyMetadataMapper.HAL_PIXEL_FORMAT_RGBA_8888 && 712 surfaceType <= LegacyMetadataMapper.HAL_PIXEL_FORMAT_BGRA_8888)) { 713 surfaceType = ImageFormat.PRIVATE; 714 } 715 716 return LegacyExceptionUtils.throwOnError(surfaceType); 717 } 718 719 /** 720 * Query the surface for its currently configured dataspace 721 */ detectSurfaceDataspace(Surface surface)722 public static int detectSurfaceDataspace(Surface surface) throws BufferQueueAbandonedException { 723 checkNotNull(surface); 724 return LegacyExceptionUtils.throwOnError(nativeDetectSurfaceDataspace(surface)); 725 } 726 connectSurface(Surface surface)727 static void connectSurface(Surface surface) throws BufferQueueAbandonedException { 728 checkNotNull(surface); 729 730 LegacyExceptionUtils.throwOnError(nativeConnectSurface(surface)); 731 } 732 disconnectSurface(Surface surface)733 static void disconnectSurface(Surface surface) throws BufferQueueAbandonedException { 734 if (surface == null) return; 735 736 LegacyExceptionUtils.throwOnError(nativeDisconnectSurface(surface)); 737 } 738 produceFrame(Surface surface, byte[] pixelBuffer, int width, int height, int pixelFormat)739 static void produceFrame(Surface surface, byte[] pixelBuffer, int width, 740 int height, int pixelFormat) 741 throws BufferQueueAbandonedException { 742 checkNotNull(surface); 743 checkNotNull(pixelBuffer); 744 checkArgumentPositive(width, "width must be positive."); 745 checkArgumentPositive(height, "height must be positive."); 746 747 LegacyExceptionUtils.throwOnError(nativeProduceFrame(surface, pixelBuffer, width, height, 748 pixelFormat)); 749 } 750 setSurfaceFormat(Surface surface, int pixelFormat)751 static void setSurfaceFormat(Surface surface, int pixelFormat) 752 throws BufferQueueAbandonedException { 753 checkNotNull(surface); 754 755 LegacyExceptionUtils.throwOnError(nativeSetSurfaceFormat(surface, pixelFormat)); 756 } 757 setSurfaceDimens(Surface surface, int width, int height)758 static void setSurfaceDimens(Surface surface, int width, int height) 759 throws BufferQueueAbandonedException { 760 checkNotNull(surface); 761 checkArgumentPositive(width, "width must be positive."); 762 checkArgumentPositive(height, "height must be positive."); 763 764 LegacyExceptionUtils.throwOnError(nativeSetSurfaceDimens(surface, width, height)); 765 } 766 getSurfaceId(Surface surface)767 public static long getSurfaceId(Surface surface) throws BufferQueueAbandonedException { 768 checkNotNull(surface); 769 try { 770 return nativeGetSurfaceId(surface); 771 } catch (IllegalArgumentException e) { 772 throw new BufferQueueAbandonedException(); 773 } 774 } 775 getSurfaceIds(SparseArray<Surface> surfaces)776 static List<Long> getSurfaceIds(SparseArray<Surface> surfaces) 777 throws BufferQueueAbandonedException { 778 if (surfaces == null) { 779 throw new NullPointerException("Null argument surfaces"); 780 } 781 List<Long> surfaceIds = new ArrayList<>(); 782 int count = surfaces.size(); 783 for (int i = 0; i < count; i++) { 784 long id = getSurfaceId(surfaces.valueAt(i)); 785 if (id == 0) { 786 throw new IllegalStateException( 787 "Configured surface had null native GraphicBufferProducer pointer!"); 788 } 789 surfaceIds.add(id); 790 } 791 return surfaceIds; 792 } 793 getSurfaceIds(Collection<Surface> surfaces)794 static List<Long> getSurfaceIds(Collection<Surface> surfaces) 795 throws BufferQueueAbandonedException { 796 if (surfaces == null) { 797 throw new NullPointerException("Null argument surfaces"); 798 } 799 List<Long> surfaceIds = new ArrayList<>(); 800 for (Surface s : surfaces) { 801 long id = getSurfaceId(s); 802 if (id == 0) { 803 throw new IllegalStateException( 804 "Configured surface had null native GraphicBufferProducer pointer!"); 805 } 806 surfaceIds.add(id); 807 } 808 return surfaceIds; 809 } 810 containsSurfaceId(Surface s, Collection<Long> ids)811 static boolean containsSurfaceId(Surface s, Collection<Long> ids) { 812 long id = 0; 813 try { 814 id = getSurfaceId(s); 815 } catch (BufferQueueAbandonedException e) { 816 // If surface is abandoned, return false. 817 return false; 818 } 819 return ids.contains(id); 820 } 821 setSurfaceOrientation(Surface surface, int facing, int sensorOrientation)822 static void setSurfaceOrientation(Surface surface, int facing, int sensorOrientation) 823 throws BufferQueueAbandonedException { 824 checkNotNull(surface); 825 LegacyExceptionUtils.throwOnError(nativeSetSurfaceOrientation(surface, facing, 826 sensorOrientation)); 827 } 828 getTextureSize(SurfaceTexture surfaceTexture)829 static Size getTextureSize(SurfaceTexture surfaceTexture) 830 throws BufferQueueAbandonedException { 831 checkNotNull(surfaceTexture); 832 833 int[] dimens = new int[2]; 834 LegacyExceptionUtils.throwOnError(nativeDetectTextureDimens(surfaceTexture, 835 /*out*/dimens)); 836 837 return new Size(dimens[0], dimens[1]); 838 } 839 setNextTimestamp(Surface surface, long timestamp)840 static void setNextTimestamp(Surface surface, long timestamp) 841 throws BufferQueueAbandonedException { 842 checkNotNull(surface); 843 LegacyExceptionUtils.throwOnError(nativeSetNextTimestamp(surface, timestamp)); 844 } 845 setScalingMode(Surface surface, int mode)846 static void setScalingMode(Surface surface, int mode) 847 throws BufferQueueAbandonedException { 848 checkNotNull(surface); 849 LegacyExceptionUtils.throwOnError(nativeSetScalingMode(surface, mode)); 850 } 851 852 nativeDetectSurfaceType(Surface surface)853 private static native int nativeDetectSurfaceType(Surface surface); 854 nativeDetectSurfaceDataspace(Surface surface)855 private static native int nativeDetectSurfaceDataspace(Surface surface); 856 nativeDetectSurfaceDimens(Surface surface, int[ ] dimens)857 private static native int nativeDetectSurfaceDimens(Surface surface, 858 /*out*/int[/*2*/] dimens); 859 nativeConnectSurface(Surface surface)860 private static native int nativeConnectSurface(Surface surface); 861 nativeProduceFrame(Surface surface, byte[] pixelBuffer, int width, int height, int pixelFormat)862 private static native int nativeProduceFrame(Surface surface, byte[] pixelBuffer, int width, 863 int height, int pixelFormat); 864 nativeSetSurfaceFormat(Surface surface, int pixelFormat)865 private static native int nativeSetSurfaceFormat(Surface surface, int pixelFormat); 866 nativeSetSurfaceDimens(Surface surface, int width, int height)867 private static native int nativeSetSurfaceDimens(Surface surface, int width, int height); 868 nativeGetSurfaceId(Surface surface)869 private static native long nativeGetSurfaceId(Surface surface); 870 nativeSetSurfaceOrientation(Surface surface, int facing, int sensorOrientation)871 private static native int nativeSetSurfaceOrientation(Surface surface, int facing, 872 int sensorOrientation); 873 nativeDetectTextureDimens(SurfaceTexture surfaceTexture, int[ ] dimens)874 private static native int nativeDetectTextureDimens(SurfaceTexture surfaceTexture, 875 /*out*/int[/*2*/] dimens); 876 nativeSetNextTimestamp(Surface surface, long timestamp)877 private static native int nativeSetNextTimestamp(Surface surface, long timestamp); 878 nativeDetectSurfaceUsageFlags(Surface surface)879 private static native int nativeDetectSurfaceUsageFlags(Surface surface); 880 nativeSetScalingMode(Surface surface, int scalingMode)881 private static native int nativeSetScalingMode(Surface surface, int scalingMode); 882 nativeDisconnectSurface(Surface surface)883 private static native int nativeDisconnectSurface(Surface surface); 884 nativeGetJpegFooterSize()885 static native int nativeGetJpegFooterSize(); 886 } 887