LegacyCameraDevice.java revision 68e4fc8ca9b923bc6d053589329eab8509d625ab
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 17package android.hardware.camera2.legacy; 18 19import android.graphics.ImageFormat; 20import android.graphics.SurfaceTexture; 21import android.hardware.Camera; 22import android.hardware.camera2.CameraCharacteristics; 23import android.hardware.camera2.CaptureRequest; 24import android.hardware.camera2.impl.CameraDeviceImpl; 25import android.hardware.camera2.impl.CaptureResultExtras; 26import android.hardware.camera2.ICameraDeviceCallbacks; 27import android.hardware.camera2.params.StreamConfigurationMap; 28import android.hardware.camera2.utils.ArrayUtils; 29import android.hardware.camera2.utils.CameraBinderDecorator; 30import android.hardware.camera2.utils.LongParcelable; 31import android.hardware.camera2.impl.CameraMetadataNative; 32import android.hardware.camera2.utils.CameraRuntimeException; 33import android.os.ConditionVariable; 34import android.os.Handler; 35import android.os.HandlerThread; 36import android.os.RemoteException; 37import android.util.Log; 38import android.util.Pair; 39import android.util.Size; 40import android.view.Surface; 41 42import java.util.ArrayList; 43import java.util.Arrays; 44import java.util.Collection; 45import java.util.List; 46 47import static android.hardware.camera2.legacy.LegacyExceptionUtils.*; 48import static android.hardware.camera2.utils.CameraBinderDecorator.*; 49import 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 */ 61public 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 List<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 private CaptureResultExtras getExtrasFromRequest(RequestHolder holder) { 91 if (holder == null) { 92 return new CaptureResultExtras(ILLEGAL_VALUE, ILLEGAL_VALUE, ILLEGAL_VALUE, 93 ILLEGAL_VALUE, ILLEGAL_VALUE, ILLEGAL_VALUE); 94 } 95 return new CaptureResultExtras(holder.getRequestId(), holder.getSubsequeceId(), 96 /*afTriggerId*/0, /*precaptureTriggerId*/0, holder.getFrameNumber(), 97 /*partialResultCount*/1); 98 } 99 100 /** 101 * Listener for the camera device state machine. Calls the appropriate 102 * {@link ICameraDeviceCallbacks} for each state transition. 103 */ 104 private final CameraDeviceState.CameraDeviceStateListener mStateListener = 105 new CameraDeviceState.CameraDeviceStateListener() { 106 @Override 107 public void onError(final int errorCode, final RequestHolder holder) { 108 if (DEBUG) { 109 Log.d(TAG, "onError called, errorCode = " + errorCode); 110 } 111 switch (errorCode) { 112 /* 113 * Only be considered idle if we hit a fatal error 114 * and no further requests can be processed. 115 */ 116 case CameraDeviceImpl.CameraDeviceCallbacks.ERROR_CAMERA_DISCONNECTED: 117 case CameraDeviceImpl.CameraDeviceCallbacks.ERROR_CAMERA_SERVICE: 118 case CameraDeviceImpl.CameraDeviceCallbacks.ERROR_CAMERA_DEVICE: { 119 mIdle.open(); 120 121 if (DEBUG) { 122 Log.d(TAG, "onError - opening idle"); 123 } 124 } 125 } 126 127 final CaptureResultExtras extras = getExtrasFromRequest(holder); 128 mResultHandler.post(new Runnable() { 129 @Override 130 public void run() { 131 if (DEBUG) { 132 Log.d(TAG, "doing onError callback for request " + holder.getRequestId() + 133 ", with error code " + errorCode); 134 } 135 try { 136 mDeviceCallbacks.onDeviceError(errorCode, extras); 137 } catch (RemoteException e) { 138 throw new IllegalStateException( 139 "Received remote exception during onCameraError callback: ", e); 140 } 141 } 142 }); 143 } 144 145 @Override 146 public void onConfiguring() { 147 // Do nothing 148 if (DEBUG) { 149 Log.d(TAG, "doing onConfiguring callback."); 150 } 151 } 152 153 @Override 154 public void onIdle() { 155 if (DEBUG) { 156 Log.d(TAG, "onIdle called"); 157 } 158 159 mIdle.open(); 160 161 mResultHandler.post(new Runnable() { 162 @Override 163 public void run() { 164 if (DEBUG) { 165 Log.d(TAG, "doing onIdle callback."); 166 } 167 try { 168 mDeviceCallbacks.onDeviceIdle(); 169 } catch (RemoteException e) { 170 throw new IllegalStateException( 171 "Received remote exception during onCameraIdle callback: ", e); 172 } 173 } 174 }); 175 } 176 177 @Override 178 public void onBusy() { 179 mIdle.close(); 180 181 if (DEBUG) { 182 Log.d(TAG, "onBusy called"); 183 } 184 } 185 186 @Override 187 public void onCaptureStarted(final RequestHolder holder, final long timestamp) { 188 final CaptureResultExtras extras = getExtrasFromRequest(holder); 189 190 mResultHandler.post(new Runnable() { 191 @Override 192 public void run() { 193 if (DEBUG) { 194 Log.d(TAG, "doing onCaptureStarted callback for request " + 195 holder.getRequestId()); 196 } 197 try { 198 mDeviceCallbacks.onCaptureStarted(extras, timestamp); 199 } catch (RemoteException e) { 200 throw new IllegalStateException( 201 "Received remote exception during onCameraError callback: ", e); 202 } 203 } 204 }); 205 } 206 207 @Override 208 public void onCaptureResult(final CameraMetadataNative result, final RequestHolder holder) { 209 final CaptureResultExtras extras = getExtrasFromRequest(holder); 210 211 mResultHandler.post(new Runnable() { 212 @Override 213 public void run() { 214 if (DEBUG) { 215 Log.d(TAG, "doing onCaptureResult callback for request " + 216 holder.getRequestId()); 217 } 218 try { 219 mDeviceCallbacks.onResultReceived(result, extras); 220 } catch (RemoteException e) { 221 throw new IllegalStateException( 222 "Received remote exception during onCameraError callback: ", e); 223 } 224 } 225 }); 226 } 227 }; 228 229 private final RequestThreadManager mRequestThreadManager; 230 231 /** 232 * Check if a given surface uses {@link ImageFormat#YUV_420_888} or format that can be readily 233 * converted to this; YV12 and NV21 are the two currently supported formats. 234 * 235 * @param s the surface to check. 236 * @return {@code true} if the surfaces uses {@link ImageFormat#YUV_420_888} or a compatible 237 * format. 238 */ 239 static boolean needsConversion(Surface s) throws BufferQueueAbandonedException { 240 int nativeType = detectSurfaceType(s); 241 return nativeType == ImageFormat.YUV_420_888 || nativeType == ImageFormat.YV12 || 242 nativeType == ImageFormat.NV21; 243 } 244 245 /** 246 * Create a new emulated camera device from a given Camera 1 API camera. 247 * 248 * <p> 249 * The {@link Camera} provided to this constructor must already have been successfully opened, 250 * and ownership of the provided camera is passed to this object. No further calls to the 251 * camera methods should be made following this constructor. 252 * </p> 253 * 254 * @param cameraId the id of the camera. 255 * @param camera an open {@link Camera} device. 256 * @param characteristics the static camera characteristics for this camera device 257 * @param callbacks {@link ICameraDeviceCallbacks} callbacks to call for Camera2 API operations. 258 */ 259 public LegacyCameraDevice(int cameraId, Camera camera, CameraCharacteristics characteristics, 260 ICameraDeviceCallbacks callbacks) { 261 mCameraId = cameraId; 262 mDeviceCallbacks = callbacks; 263 TAG = String.format("CameraDevice-%d-LE", mCameraId); 264 265 mResultThread.start(); 266 mResultHandler = new Handler(mResultThread.getLooper()); 267 mCallbackHandlerThread.start(); 268 mCallbackHandler = new Handler(mCallbackHandlerThread.getLooper()); 269 mDeviceState.setCameraDeviceCallbacks(mCallbackHandler, mStateListener); 270 mStaticCharacteristics = characteristics; 271 mRequestThreadManager = 272 new RequestThreadManager(cameraId, camera, characteristics, mDeviceState); 273 mRequestThreadManager.start(); 274 } 275 276 /** 277 * Configure the device with a set of output surfaces. 278 * 279 * <p>Using empty or {@code null} {@code outputs} is the same as unconfiguring.</p> 280 * 281 * <p>Every surface in {@code outputs} must be non-{@code null}.</p> 282 * 283 * @param outputs a list of surfaces to set. 284 * @return an error code for this binder operation, or {@link NO_ERROR} 285 * on success. 286 */ 287 public int configureOutputs(List<Surface> outputs) { 288 List<Pair<Surface, Size>> sizedSurfaces = new ArrayList<>(); 289 if (outputs != null) { 290 for (Surface output : outputs) { 291 if (output == null) { 292 Log.e(TAG, "configureOutputs - null outputs are not allowed"); 293 return BAD_VALUE; 294 } 295 if (!output.isValid()) { 296 Log.e(TAG, "configureOutputs - invalid output surfaces are not allowed"); 297 return BAD_VALUE; 298 } 299 StreamConfigurationMap streamConfigurations = mStaticCharacteristics. 300 get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP); 301 302 // Validate surface size and format. 303 try { 304 Size s = getSurfaceSize(output); 305 int surfaceType = detectSurfaceType(output); 306 307 boolean flexibleConsumer = isFlexibleConsumer(output); 308 309 Size[] sizes = streamConfigurations.getOutputSizes(surfaceType); 310 if (sizes == null) { 311 // WAR: Override default format to IMPLEMENTATION_DEFINED for b/9487482 312 if ((surfaceType >= LegacyMetadataMapper.HAL_PIXEL_FORMAT_RGBA_8888 && 313 surfaceType <= LegacyMetadataMapper.HAL_PIXEL_FORMAT_BGRA_8888)) { 314 315 // YUV_420_888 is always present in LEGACY for all 316 // IMPLEMENTATION_DEFINED output sizes, and is publicly visible in the 317 // API (i.e. {@code #getOutputSizes} works here). 318 sizes = streamConfigurations.getOutputSizes(ImageFormat.YUV_420_888); 319 } else if (surfaceType == LegacyMetadataMapper.HAL_PIXEL_FORMAT_BLOB) { 320 sizes = streamConfigurations.getOutputSizes(ImageFormat.JPEG); 321 } 322 } 323 324 if (!ArrayUtils.contains(sizes, s)) { 325 if (flexibleConsumer && (s = findClosestSize(s, sizes)) != null) { 326 sizedSurfaces.add(new Pair<>(output, s)); 327 } else { 328 String reason = (sizes == null) ? "format is invalid." : 329 ("size not in valid set: " + Arrays.toString(sizes)); 330 Log.e(TAG, String.format("Surface with size (w=%d, h=%d) and format " + 331 "0x%x is not valid, %s", s.getWidth(), s.getHeight(), 332 surfaceType, reason)); 333 return BAD_VALUE; 334 } 335 } else { 336 sizedSurfaces.add(new Pair<>(output, s)); 337 } 338 } catch (BufferQueueAbandonedException e) { 339 Log.e(TAG, "Surface bufferqueue is abandoned, cannot configure as output: ", e); 340 return BAD_VALUE; 341 } 342 343 } 344 } 345 346 boolean success = false; 347 if (mDeviceState.setConfiguring()) { 348 mRequestThreadManager.configure(sizedSurfaces); 349 success = mDeviceState.setIdle(); 350 } 351 352 if (success) { 353 mConfiguredSurfaces = outputs != null ? new ArrayList<>(outputs) : null; 354 } else { 355 return CameraBinderDecorator.INVALID_OPERATION; 356 } 357 return CameraBinderDecorator.NO_ERROR; 358 } 359 360 /** 361 * Submit a burst of capture requests. 362 * 363 * @param requestList a list of capture requests to execute. 364 * @param repeating {@code true} if this burst is repeating. 365 * @param frameNumber an output argument that contains either the frame number of the last frame 366 * that will be returned for this request, or the frame number of the last 367 * frame that will be returned for the current repeating request if this 368 * burst is set to be repeating. 369 * @return the request id. 370 */ 371 public int submitRequestList(List<CaptureRequest> requestList, boolean repeating, 372 /*out*/LongParcelable frameNumber) { 373 if (requestList == null || requestList.isEmpty()) { 374 Log.e(TAG, "submitRequestList - Empty/null requests are not allowed"); 375 return BAD_VALUE; 376 } 377 378 List<Long> surfaceIds = (mConfiguredSurfaces == null) ? new ArrayList<Long>() : 379 getSurfaceIds(mConfiguredSurfaces); 380 381 // Make sure that there all requests have at least 1 surface; all surfaces are non-null 382 for (CaptureRequest request : requestList) { 383 if (request.getTargets().isEmpty()) { 384 Log.e(TAG, "submitRequestList - " 385 + "Each request must have at least one Surface target"); 386 return BAD_VALUE; 387 } 388 389 for (Surface surface : request.getTargets()) { 390 if (surface == null) { 391 Log.e(TAG, "submitRequestList - Null Surface targets are not allowed"); 392 return BAD_VALUE; 393 } else if (mConfiguredSurfaces == null) { 394 Log.e(TAG, "submitRequestList - must configure " + 395 " device with valid surfaces before submitting requests"); 396 return INVALID_OPERATION; 397 } else if (!containsSurfaceId(surface, surfaceIds)) { 398 Log.e(TAG, "submitRequestList - cannot use a surface that wasn't configured"); 399 return BAD_VALUE; 400 } 401 } 402 } 403 404 // TODO: further validation of request here 405 mIdle.close(); 406 return mRequestThreadManager.submitCaptureRequests(requestList, repeating, 407 frameNumber); 408 } 409 410 /** 411 * Submit a single capture request. 412 * 413 * @param request the capture request to execute. 414 * @param repeating {@code true} if this request is repeating. 415 * @param frameNumber an output argument that contains either the frame number of the last frame 416 * that will be returned for this request, or the frame number of the last 417 * frame that will be returned for the current repeating request if this 418 * request is set to be repeating. 419 * @return the request id. 420 */ 421 public int submitRequest(CaptureRequest request, boolean repeating, 422 /*out*/LongParcelable frameNumber) { 423 ArrayList<CaptureRequest> requestList = new ArrayList<CaptureRequest>(); 424 requestList.add(request); 425 return submitRequestList(requestList, repeating, frameNumber); 426 } 427 428 /** 429 * Cancel the repeating request with the given request id. 430 * 431 * @param requestId the request id of the request to cancel. 432 * @return the last frame number to be returned from the HAL for the given repeating request, or 433 * {@code INVALID_FRAME} if none exists. 434 */ 435 public long cancelRequest(int requestId) { 436 return mRequestThreadManager.cancelRepeating(requestId); 437 } 438 439 /** 440 * Block until the {@link ICameraDeviceCallbacks#onCameraIdle()} callback is received. 441 */ 442 public void waitUntilIdle() { 443 mIdle.block(); 444 } 445 446 /** 447 * Flush any pending requests. 448 * 449 * @return the last frame number. 450 */ 451 public long flush() { 452 long lastFrame = mRequestThreadManager.flush(); 453 waitUntilIdle(); 454 return lastFrame; 455 } 456 457 /** 458 * Return {@code true} if the device has been closed. 459 */ 460 public boolean isClosed() { 461 return mClosed; 462 } 463 464 @Override 465 public void close() { 466 mRequestThreadManager.quit(); 467 mCallbackHandlerThread.quitSafely(); 468 mResultThread.quitSafely(); 469 470 try { 471 mCallbackHandlerThread.join(); 472 } catch (InterruptedException e) { 473 Log.e(TAG, String.format("Thread %s (%d) interrupted while quitting.", 474 mCallbackHandlerThread.getName(), mCallbackHandlerThread.getId())); 475 } 476 477 try { 478 mResultThread.join(); 479 } catch (InterruptedException e) { 480 Log.e(TAG, String.format("Thread %s (%d) interrupted while quitting.", 481 mResultThread.getName(), mResultThread.getId())); 482 } 483 484 mClosed = true; 485 } 486 487 @Override 488 protected void finalize() throws Throwable { 489 try { 490 close(); 491 } catch (CameraRuntimeException e) { 492 Log.e(TAG, "Got error while trying to finalize, ignoring: " + e.getMessage()); 493 } finally { 494 super.finalize(); 495 } 496 } 497 498 static long findEuclidDistSquare(Size a, Size b) { 499 long d0 = a.getWidth() - b.getWidth(); 500 long d1 = a.getHeight() - b.getHeight(); 501 return d0 * d0 + d1 * d1; 502 } 503 504 // Keep up to date with rounding behavior in 505 // frameworks/av/services/camera/libcameraservice/api2/CameraDeviceClient.cpp 506 static Size findClosestSize(Size size, Size[] supportedSizes) { 507 if (size == null || supportedSizes == null) { 508 return null; 509 } 510 Size bestSize = null; 511 for (Size s : supportedSizes) { 512 if (s.equals(size)) { 513 return size; 514 } else if (s.getWidth() <= MAX_DIMEN_FOR_ROUNDING && (bestSize == null || 515 LegacyCameraDevice.findEuclidDistSquare(size, s) < 516 LegacyCameraDevice.findEuclidDistSquare(bestSize, s))) { 517 bestSize = s; 518 } 519 } 520 return bestSize; 521 } 522 523 /** 524 * Query the surface for its currently configured default buffer size. 525 * @param surface a non-{@code null} {@code Surface} 526 * @return the width and height of the surface 527 * 528 * @throws NullPointerException if the {@code surface} was {@code null} 529 * @throws BufferQueueAbandonedException if the {@code surface} was invalid 530 */ 531 public static Size getSurfaceSize(Surface surface) throws BufferQueueAbandonedException { 532 checkNotNull(surface); 533 534 int[] dimens = new int[2]; 535 LegacyExceptionUtils.throwOnError(nativeDetectSurfaceDimens(surface, /*out*/dimens)); 536 537 return new Size(dimens[0], dimens[1]); 538 } 539 540 public static boolean isFlexibleConsumer(Surface output) { 541 int usageFlags = detectSurfaceUsageFlags(output); 542 543 // Keep up to date with allowed consumer types in 544 // frameworks/av/services/camera/libcameraservice/api2/CameraDeviceClient.cpp 545 int disallowedFlags = GRALLOC_USAGE_HW_VIDEO_ENCODER | GRALLOC_USAGE_RENDERSCRIPT; 546 int allowedFlags = GRALLOC_USAGE_HW_TEXTURE | GRALLOC_USAGE_SW_READ_OFTEN | 547 GRALLOC_USAGE_HW_COMPOSER; 548 boolean flexibleConsumer = ((usageFlags & disallowedFlags) == 0 && 549 (usageFlags & allowedFlags) != 0); 550 return flexibleConsumer; 551 } 552 553 public static boolean isPreviewConsumer(Surface output) { 554 int usageFlags = detectSurfaceUsageFlags(output); 555 int disallowedFlags = GRALLOC_USAGE_HW_VIDEO_ENCODER | GRALLOC_USAGE_RENDERSCRIPT | 556 GRALLOC_USAGE_SW_READ_OFTEN; 557 int allowedFlags = GRALLOC_USAGE_HW_TEXTURE | GRALLOC_USAGE_HW_COMPOSER | 558 GRALLOC_USAGE_HW_RENDER; 559 boolean previewConsumer = ((usageFlags & disallowedFlags) == 0 && 560 (usageFlags & allowedFlags) != 0); 561 int surfaceFormat = ImageFormat.UNKNOWN; 562 try { 563 surfaceFormat = detectSurfaceType(output); 564 } catch(BufferQueueAbandonedException e) { 565 throw new IllegalArgumentException("Surface was abandoned", e); 566 } 567 568 return previewConsumer && (surfaceFormat == ImageFormat.PRIVATE); 569 } 570 571 public static boolean isVideoEncoderConsumer(Surface output) { 572 int usageFlags = detectSurfaceUsageFlags(output); 573 int disallowedFlags = GRALLOC_USAGE_HW_TEXTURE | GRALLOC_USAGE_HW_COMPOSER | 574 GRALLOC_USAGE_RENDERSCRIPT | GRALLOC_USAGE_SW_READ_OFTEN; 575 int allowedFlags = GRALLOC_USAGE_HW_VIDEO_ENCODER; 576 boolean videoEncoderConsumer = ((usageFlags & disallowedFlags) == 0 && 577 (usageFlags & allowedFlags) != 0); 578 579 int surfaceFormat = ImageFormat.UNKNOWN; 580 try { 581 surfaceFormat = detectSurfaceType(output); 582 } catch(BufferQueueAbandonedException e) { 583 throw new IllegalArgumentException("Surface was abandoned", e); 584 } 585 586 return videoEncoderConsumer && (surfaceFormat == ImageFormat.PRIVATE); 587 } 588 589 /** 590 * Query the surface for its currently configured usage flags 591 */ 592 static int detectSurfaceUsageFlags(Surface surface) { 593 checkNotNull(surface); 594 return nativeDetectSurfaceUsageFlags(surface); 595 } 596 597 /** 598 * Query the surface for its currently configured format 599 */ 600 public static int detectSurfaceType(Surface surface) throws BufferQueueAbandonedException { 601 checkNotNull(surface); 602 return LegacyExceptionUtils.throwOnError(nativeDetectSurfaceType(surface)); 603 } 604 605 static void configureSurface(Surface surface, int width, int height, 606 int pixelFormat) throws BufferQueueAbandonedException { 607 checkNotNull(surface); 608 checkArgumentPositive(width, "width must be positive."); 609 checkArgumentPositive(height, "height must be positive."); 610 611 LegacyExceptionUtils.throwOnError(nativeConfigureSurface(surface, width, height, 612 pixelFormat)); 613 } 614 615 static void produceFrame(Surface surface, byte[] pixelBuffer, int width, 616 int height, int pixelFormat) 617 throws BufferQueueAbandonedException { 618 checkNotNull(surface); 619 checkNotNull(pixelBuffer); 620 checkArgumentPositive(width, "width must be positive."); 621 checkArgumentPositive(height, "height must be positive."); 622 623 LegacyExceptionUtils.throwOnError(nativeProduceFrame(surface, pixelBuffer, width, height, 624 pixelFormat)); 625 } 626 627 static void setSurfaceFormat(Surface surface, int pixelFormat) 628 throws BufferQueueAbandonedException { 629 checkNotNull(surface); 630 631 LegacyExceptionUtils.throwOnError(nativeSetSurfaceFormat(surface, pixelFormat)); 632 } 633 634 static void setSurfaceDimens(Surface surface, int width, int height) 635 throws BufferQueueAbandonedException { 636 checkNotNull(surface); 637 checkArgumentPositive(width, "width must be positive."); 638 checkArgumentPositive(height, "height must be positive."); 639 640 LegacyExceptionUtils.throwOnError(nativeSetSurfaceDimens(surface, width, height)); 641 } 642 643 static long getSurfaceId(Surface surface) { 644 checkNotNull(surface); 645 return nativeGetSurfaceId(surface); 646 } 647 648 static List<Long> getSurfaceIds(Collection<Surface> surfaces) { 649 if (surfaces == null) { 650 throw new NullPointerException("Null argument surfaces"); 651 } 652 List<Long> surfaceIds = new ArrayList<>(); 653 for (Surface s : surfaces) { 654 long id = getSurfaceId(s); 655 if (id == 0) { 656 throw new IllegalStateException( 657 "Configured surface had null native GraphicBufferProducer pointer!"); 658 } 659 surfaceIds.add(id); 660 } 661 return surfaceIds; 662 } 663 664 static boolean containsSurfaceId(Surface s, Collection<Long> ids) { 665 long id = getSurfaceId(s); 666 return ids.contains(id); 667 } 668 669 static void setSurfaceOrientation(Surface surface, int facing, int sensorOrientation) 670 throws BufferQueueAbandonedException { 671 checkNotNull(surface); 672 LegacyExceptionUtils.throwOnError(nativeSetSurfaceOrientation(surface, facing, 673 sensorOrientation)); 674 } 675 676 static Size getTextureSize(SurfaceTexture surfaceTexture) 677 throws BufferQueueAbandonedException { 678 checkNotNull(surfaceTexture); 679 680 int[] dimens = new int[2]; 681 LegacyExceptionUtils.throwOnError(nativeDetectTextureDimens(surfaceTexture, 682 /*out*/dimens)); 683 684 return new Size(dimens[0], dimens[1]); 685 } 686 687 static void setNextTimestamp(Surface surface, long timestamp) 688 throws BufferQueueAbandonedException { 689 checkNotNull(surface); 690 LegacyExceptionUtils.throwOnError(nativeSetNextTimestamp(surface, timestamp)); 691 } 692 693 private static native int nativeDetectSurfaceType(Surface surface); 694 695 private static native int nativeDetectSurfaceDimens(Surface surface, 696 /*out*/int[/*2*/] dimens); 697 698 private static native int nativeConfigureSurface(Surface surface, int width, int height, 699 int pixelFormat); 700 701 private static native int nativeProduceFrame(Surface surface, byte[] pixelBuffer, int width, 702 int height, int pixelFormat); 703 704 private static native int nativeSetSurfaceFormat(Surface surface, int pixelFormat); 705 706 private static native int nativeSetSurfaceDimens(Surface surface, int width, int height); 707 708 private static native long nativeGetSurfaceId(Surface surface); 709 710 private static native int nativeSetSurfaceOrientation(Surface surface, int facing, 711 int sensorOrientation); 712 713 private static native int nativeDetectTextureDimens(SurfaceTexture surfaceTexture, 714 /*out*/int[/*2*/] dimens); 715 716 private static native int nativeSetNextTimestamp(Surface surface, long timestamp); 717 718 private static native int nativeDetectSurfaceUsageFlags(Surface surface); 719 720 static native int nativeGetJpegFooterSize(); 721} 722