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