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