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.ICameraDeviceCallbacks; 27 import android.hardware.camera2.params.StreamConfigurationMap; 28 import android.hardware.camera2.utils.ArrayUtils; 29 import android.hardware.camera2.utils.SubmitInfo; 30 import android.hardware.camera2.impl.CameraMetadataNative; 31 import android.os.ConditionVariable; 32 import android.os.Handler; 33 import android.os.HandlerThread; 34 import android.os.RemoteException; 35 import android.os.ServiceSpecificException; 36 import android.util.Log; 37 import android.util.Pair; 38 import android.util.Size; 39 import android.util.SparseArray; 40 import android.view.Surface; 41 42 import java.util.ArrayList; 43 import java.util.Arrays; 44 import java.util.Collection; 45 import java.util.List; 46 47 import static android.hardware.camera2.legacy.LegacyExceptionUtils.*; 48 import 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 */ 60 public 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 getExtrasFromRequest(RequestHolder holder)92 private CaptureResultExtras getExtrasFromRequest(RequestHolder holder) { 93 return getExtrasFromRequest(holder, 94 /*errorCode*/CameraDeviceState.NO_CAPTURE_ERROR, /*errorArg*/null); 95 } 96 getExtrasFromRequest(RequestHolder holder, int errorCode, Object errorArg)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 onCaptureResult(final CameraMetadataNative result, final RequestHolder holder) { 227 final CaptureResultExtras extras = getExtrasFromRequest(holder); 228 229 mResultHandler.post(new Runnable() { 230 @Override 231 public void run() { 232 if (DEBUG) { 233 Log.d(TAG, "doing onCaptureResult callback for request " + 234 holder.getRequestId()); 235 } 236 try { 237 mDeviceCallbacks.onResultReceived(result, extras); 238 } catch (RemoteException e) { 239 throw new IllegalStateException( 240 "Received remote exception during onCameraError callback: ", e); 241 } 242 } 243 }); 244 } 245 246 @Override 247 public void onRepeatingRequestError(final long lastFrameNumber) { 248 mResultHandler.post(new Runnable() { 249 @Override 250 public void run() { 251 if (DEBUG) { 252 Log.d(TAG, "doing onRepeatingRequestError callback."); 253 } 254 try { 255 mDeviceCallbacks.onRepeatingRequestError(lastFrameNumber); 256 } catch (RemoteException e) { 257 throw new IllegalStateException( 258 "Received remote exception during onRepeatingRequestError " + 259 "callback: ", e); 260 } 261 } 262 }); 263 } 264 }; 265 266 private final RequestThreadManager mRequestThreadManager; 267 268 /** 269 * Check if a given surface uses {@link ImageFormat#YUV_420_888} or format that can be readily 270 * converted to this; YV12 and NV21 are the two currently supported formats. 271 * 272 * @param s the surface to check. 273 * @return {@code true} if the surfaces uses {@link ImageFormat#YUV_420_888} or a compatible 274 * format. 275 */ needsConversion(Surface s)276 static boolean needsConversion(Surface s) throws BufferQueueAbandonedException { 277 int nativeType = detectSurfaceType(s); 278 return nativeType == ImageFormat.YUV_420_888 || nativeType == ImageFormat.YV12 || 279 nativeType == ImageFormat.NV21; 280 } 281 282 /** 283 * Create a new emulated camera device from a given Camera 1 API camera. 284 * 285 * <p> 286 * The {@link Camera} provided to this constructor must already have been successfully opened, 287 * and ownership of the provided camera is passed to this object. No further calls to the 288 * camera methods should be made following this constructor. 289 * </p> 290 * 291 * @param cameraId the id of the camera. 292 * @param camera an open {@link Camera} device. 293 * @param characteristics the static camera characteristics for this camera device 294 * @param callbacks {@link ICameraDeviceCallbacks} callbacks to call for Camera2 API operations. 295 */ LegacyCameraDevice(int cameraId, Camera camera, CameraCharacteristics characteristics, ICameraDeviceCallbacks callbacks)296 public LegacyCameraDevice(int cameraId, Camera camera, CameraCharacteristics characteristics, 297 ICameraDeviceCallbacks callbacks) { 298 mCameraId = cameraId; 299 mDeviceCallbacks = callbacks; 300 TAG = String.format("CameraDevice-%d-LE", mCameraId); 301 302 mResultThread.start(); 303 mResultHandler = new Handler(mResultThread.getLooper()); 304 mCallbackHandlerThread.start(); 305 mCallbackHandler = new Handler(mCallbackHandlerThread.getLooper()); 306 mDeviceState.setCameraDeviceCallbacks(mCallbackHandler, mStateListener); 307 mStaticCharacteristics = characteristics; 308 mRequestThreadManager = 309 new RequestThreadManager(cameraId, camera, characteristics, mDeviceState); 310 mRequestThreadManager.start(); 311 } 312 313 /** 314 * Configure the device with a set of output surfaces. 315 * 316 * <p>Using empty or {@code null} {@code outputs} is the same as unconfiguring.</p> 317 * 318 * <p>Every surface in {@code outputs} must be non-{@code null}.</p> 319 * 320 * @param outputs a list of surfaces to set. LegacyCameraDevice will take ownership of this 321 * list; it must not be modified by the caller once it's passed in. 322 * @return an error code for this binder operation, or {@link NO_ERROR} 323 * on success. 324 */ configureOutputs(SparseArray<Surface> outputs)325 public int configureOutputs(SparseArray<Surface> outputs) { 326 List<Pair<Surface, Size>> sizedSurfaces = new ArrayList<>(); 327 if (outputs != null) { 328 int count = outputs.size(); 329 for (int i = 0; i < count; i++) { 330 Surface output = outputs.valueAt(i); 331 if (output == null) { 332 Log.e(TAG, "configureOutputs - null outputs are not allowed"); 333 return BAD_VALUE; 334 } 335 if (!output.isValid()) { 336 Log.e(TAG, "configureOutputs - invalid output surfaces are not allowed"); 337 return BAD_VALUE; 338 } 339 StreamConfigurationMap streamConfigurations = mStaticCharacteristics. 340 get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP); 341 342 // Validate surface size and format. 343 try { 344 Size s = getSurfaceSize(output); 345 int surfaceType = detectSurfaceType(output); 346 347 boolean flexibleConsumer = isFlexibleConsumer(output); 348 349 Size[] sizes = streamConfigurations.getOutputSizes(surfaceType); 350 if (sizes == null) { 351 if (surfaceType == ImageFormat.PRIVATE) { 352 353 // YUV_420_888 is always present in LEGACY for all 354 // IMPLEMENTATION_DEFINED output sizes, and is publicly visible in the 355 // API (i.e. {@code #getOutputSizes} works here). 356 sizes = streamConfigurations.getOutputSizes(ImageFormat.YUV_420_888); 357 } else if (surfaceType == LegacyMetadataMapper.HAL_PIXEL_FORMAT_BLOB) { 358 sizes = streamConfigurations.getOutputSizes(ImageFormat.JPEG); 359 } 360 } 361 362 if (!ArrayUtils.contains(sizes, s)) { 363 if (flexibleConsumer && (s = findClosestSize(s, sizes)) != null) { 364 sizedSurfaces.add(new Pair<>(output, s)); 365 } else { 366 String reason = (sizes == null) ? "format is invalid." : 367 ("size not in valid set: " + Arrays.toString(sizes)); 368 Log.e(TAG, String.format("Surface with size (w=%d, h=%d) and format " + 369 "0x%x is not valid, %s", s.getWidth(), s.getHeight(), 370 surfaceType, reason)); 371 return BAD_VALUE; 372 } 373 } else { 374 sizedSurfaces.add(new Pair<>(output, s)); 375 } 376 // Lock down the size before configuration 377 setSurfaceDimens(output, s.getWidth(), s.getHeight()); 378 } catch (BufferQueueAbandonedException e) { 379 Log.e(TAG, "Surface bufferqueue is abandoned, cannot configure as output: ", e); 380 return BAD_VALUE; 381 } 382 383 } 384 } 385 386 boolean success = false; 387 if (mDeviceState.setConfiguring()) { 388 mRequestThreadManager.configure(sizedSurfaces); 389 success = mDeviceState.setIdle(); 390 } 391 392 if (success) { 393 mConfiguredSurfaces = outputs; 394 } else { 395 return LegacyExceptionUtils.INVALID_OPERATION; 396 } 397 return LegacyExceptionUtils.NO_ERROR; 398 } 399 400 /** 401 * Submit a burst of capture requests. 402 * 403 * @param requestList a list of capture requests to execute. 404 * @param repeating {@code true} if this burst is repeating. 405 * @return the submission info, including the new request id, and the last frame number, which 406 * contains either the frame number of the last frame that will be returned for this request, 407 * or the frame number of the last frame that will be returned for the current repeating 408 * request if this burst is set to be repeating. 409 */ submitRequestList(CaptureRequest[] requestList, boolean repeating)410 public SubmitInfo submitRequestList(CaptureRequest[] requestList, boolean repeating) { 411 if (requestList == null || requestList.length == 0) { 412 Log.e(TAG, "submitRequestList - Empty/null requests are not allowed"); 413 throw new ServiceSpecificException(BAD_VALUE, 414 "submitRequestList - Empty/null requests are not allowed"); 415 } 416 417 List<Long> surfaceIds; 418 419 try { 420 surfaceIds = (mConfiguredSurfaces == null) ? new ArrayList<Long>() : 421 getSurfaceIds(mConfiguredSurfaces); 422 } catch (BufferQueueAbandonedException e) { 423 throw new ServiceSpecificException(BAD_VALUE, 424 "submitRequestList - configured surface is abandoned."); 425 } 426 427 // Make sure that there all requests have at least 1 surface; all surfaces are non-null 428 for (CaptureRequest request : requestList) { 429 if (request.getTargets().isEmpty()) { 430 Log.e(TAG, "submitRequestList - " 431 + "Each request must have at least one Surface target"); 432 throw new ServiceSpecificException(BAD_VALUE, 433 "submitRequestList - " 434 + "Each request must have at least one Surface target"); 435 } 436 437 for (Surface surface : request.getTargets()) { 438 if (surface == null) { 439 Log.e(TAG, "submitRequestList - Null Surface targets are not allowed"); 440 throw new ServiceSpecificException(BAD_VALUE, 441 "submitRequestList - Null Surface targets are not allowed"); 442 } else if (mConfiguredSurfaces == null) { 443 Log.e(TAG, "submitRequestList - must configure " + 444 " device with valid surfaces before submitting requests"); 445 throw new ServiceSpecificException(INVALID_OPERATION, 446 "submitRequestList - must configure " + 447 " device with valid surfaces before submitting requests"); 448 } else if (!containsSurfaceId(surface, surfaceIds)) { 449 Log.e(TAG, "submitRequestList - cannot use a surface that wasn't configured"); 450 throw new ServiceSpecificException(BAD_VALUE, 451 "submitRequestList - cannot use a surface that wasn't configured"); 452 } 453 } 454 } 455 456 // TODO: further validation of request here 457 mIdle.close(); 458 return mRequestThreadManager.submitCaptureRequests(requestList, repeating); 459 } 460 461 /** 462 * Submit a single capture request. 463 * 464 * @param request the capture request to execute. 465 * @param repeating {@code true} if this request is repeating. 466 * @return the submission info, including the new request id, and the last frame number, which 467 * contains either the frame number of the last frame that will be returned for this request, 468 * or the frame number of the last frame that will be returned for the current repeating 469 * request if this burst is set to be repeating. 470 */ submitRequest(CaptureRequest request, boolean repeating)471 public SubmitInfo submitRequest(CaptureRequest request, boolean repeating) { 472 CaptureRequest[] requestList = { request }; 473 return submitRequestList(requestList, repeating); 474 } 475 476 /** 477 * Cancel the repeating request with the given request id. 478 * 479 * @param requestId the request id of the request to cancel. 480 * @return the last frame number to be returned from the HAL for the given repeating request, or 481 * {@code INVALID_FRAME} if none exists. 482 */ cancelRequest(int requestId)483 public long cancelRequest(int requestId) { 484 return mRequestThreadManager.cancelRepeating(requestId); 485 } 486 487 /** 488 * Block until the {@link ICameraDeviceCallbacks#onCameraIdle()} callback is received. 489 */ waitUntilIdle()490 public void waitUntilIdle() { 491 mIdle.block(); 492 } 493 494 /** 495 * Flush any pending requests. 496 * 497 * @return the last frame number. 498 */ flush()499 public long flush() { 500 long lastFrame = mRequestThreadManager.flush(); 501 waitUntilIdle(); 502 return lastFrame; 503 } 504 505 /** 506 * Return {@code true} if the device has been closed. 507 */ isClosed()508 public boolean isClosed() { 509 return mClosed; 510 } 511 512 @Override close()513 public void close() { 514 mRequestThreadManager.quit(); 515 mCallbackHandlerThread.quitSafely(); 516 mResultThread.quitSafely(); 517 518 try { 519 mCallbackHandlerThread.join(); 520 } catch (InterruptedException e) { 521 Log.e(TAG, String.format("Thread %s (%d) interrupted while quitting.", 522 mCallbackHandlerThread.getName(), mCallbackHandlerThread.getId())); 523 } 524 525 try { 526 mResultThread.join(); 527 } catch (InterruptedException e) { 528 Log.e(TAG, String.format("Thread %s (%d) interrupted while quitting.", 529 mResultThread.getName(), mResultThread.getId())); 530 } 531 532 mClosed = true; 533 } 534 535 @Override finalize()536 protected void finalize() throws Throwable { 537 try { 538 close(); 539 } catch (ServiceSpecificException e) { 540 Log.e(TAG, "Got error while trying to finalize, ignoring: " + e.getMessage()); 541 } finally { 542 super.finalize(); 543 } 544 } 545 findEuclidDistSquare(Size a, Size b)546 static long findEuclidDistSquare(Size a, Size b) { 547 long d0 = a.getWidth() - b.getWidth(); 548 long d1 = a.getHeight() - b.getHeight(); 549 return d0 * d0 + d1 * d1; 550 } 551 552 // Keep up to date with rounding behavior in 553 // frameworks/av/services/camera/libcameraservice/api2/CameraDeviceClient.cpp findClosestSize(Size size, Size[] supportedSizes)554 static Size findClosestSize(Size size, Size[] supportedSizes) { 555 if (size == null || supportedSizes == null) { 556 return null; 557 } 558 Size bestSize = null; 559 for (Size s : supportedSizes) { 560 if (s.equals(size)) { 561 return size; 562 } else if (s.getWidth() <= MAX_DIMEN_FOR_ROUNDING && (bestSize == null || 563 LegacyCameraDevice.findEuclidDistSquare(size, s) < 564 LegacyCameraDevice.findEuclidDistSquare(bestSize, s))) { 565 bestSize = s; 566 } 567 } 568 return bestSize; 569 } 570 571 /** 572 * Query the surface for its currently configured default buffer size. 573 * @param surface a non-{@code null} {@code Surface} 574 * @return the width and height of the surface 575 * 576 * @throws NullPointerException if the {@code surface} was {@code null} 577 * @throws BufferQueueAbandonedException if the {@code surface} was invalid 578 */ getSurfaceSize(Surface surface)579 public static Size getSurfaceSize(Surface surface) throws BufferQueueAbandonedException { 580 checkNotNull(surface); 581 582 int[] dimens = new int[2]; 583 LegacyExceptionUtils.throwOnError(nativeDetectSurfaceDimens(surface, /*out*/dimens)); 584 585 return new Size(dimens[0], dimens[1]); 586 } 587 isFlexibleConsumer(Surface output)588 public static boolean isFlexibleConsumer(Surface output) { 589 int usageFlags = detectSurfaceUsageFlags(output); 590 591 // Keep up to date with allowed consumer types in 592 // frameworks/av/services/camera/libcameraservice/api2/CameraDeviceClient.cpp 593 int disallowedFlags = GRALLOC_USAGE_HW_VIDEO_ENCODER | GRALLOC_USAGE_RENDERSCRIPT; 594 int allowedFlags = GRALLOC_USAGE_HW_TEXTURE | GRALLOC_USAGE_SW_READ_OFTEN | 595 GRALLOC_USAGE_HW_COMPOSER; 596 boolean flexibleConsumer = ((usageFlags & disallowedFlags) == 0 && 597 (usageFlags & allowedFlags) != 0); 598 return flexibleConsumer; 599 } 600 isPreviewConsumer(Surface output)601 public static boolean isPreviewConsumer(Surface output) { 602 int usageFlags = detectSurfaceUsageFlags(output); 603 int disallowedFlags = GRALLOC_USAGE_HW_VIDEO_ENCODER | GRALLOC_USAGE_RENDERSCRIPT | 604 GRALLOC_USAGE_SW_READ_OFTEN; 605 int allowedFlags = GRALLOC_USAGE_HW_TEXTURE | GRALLOC_USAGE_HW_COMPOSER | 606 GRALLOC_USAGE_HW_RENDER; 607 boolean previewConsumer = ((usageFlags & disallowedFlags) == 0 && 608 (usageFlags & allowedFlags) != 0); 609 int surfaceFormat = ImageFormat.UNKNOWN; 610 try { 611 surfaceFormat = detectSurfaceType(output); 612 } catch(BufferQueueAbandonedException e) { 613 throw new IllegalArgumentException("Surface was abandoned", e); 614 } 615 616 return previewConsumer; 617 } 618 isVideoEncoderConsumer(Surface output)619 public static boolean isVideoEncoderConsumer(Surface output) { 620 int usageFlags = detectSurfaceUsageFlags(output); 621 int disallowedFlags = GRALLOC_USAGE_HW_TEXTURE | GRALLOC_USAGE_HW_COMPOSER | 622 GRALLOC_USAGE_RENDERSCRIPT | GRALLOC_USAGE_SW_READ_OFTEN; 623 int allowedFlags = GRALLOC_USAGE_HW_VIDEO_ENCODER; 624 boolean videoEncoderConsumer = ((usageFlags & disallowedFlags) == 0 && 625 (usageFlags & allowedFlags) != 0); 626 627 int surfaceFormat = ImageFormat.UNKNOWN; 628 try { 629 surfaceFormat = detectSurfaceType(output); 630 } catch(BufferQueueAbandonedException e) { 631 throw new IllegalArgumentException("Surface was abandoned", e); 632 } 633 634 return videoEncoderConsumer; 635 } 636 637 /** 638 * Query the surface for its currently configured usage flags 639 */ detectSurfaceUsageFlags(Surface surface)640 static int detectSurfaceUsageFlags(Surface surface) { 641 checkNotNull(surface); 642 return nativeDetectSurfaceUsageFlags(surface); 643 } 644 645 /** 646 * Query the surface for its currently configured format 647 */ detectSurfaceType(Surface surface)648 public static int detectSurfaceType(Surface surface) throws BufferQueueAbandonedException { 649 checkNotNull(surface); 650 int surfaceType = nativeDetectSurfaceType(surface); 651 652 // TODO: remove this override since the default format should be 653 // ImageFormat.PRIVATE. b/9487482 654 if ((surfaceType >= LegacyMetadataMapper.HAL_PIXEL_FORMAT_RGBA_8888 && 655 surfaceType <= LegacyMetadataMapper.HAL_PIXEL_FORMAT_BGRA_8888)) { 656 surfaceType = ImageFormat.PRIVATE; 657 } 658 659 return LegacyExceptionUtils.throwOnError(surfaceType); 660 } 661 662 /** 663 * Query the surface for its currently configured dataspace 664 */ detectSurfaceDataspace(Surface surface)665 public static int detectSurfaceDataspace(Surface surface) throws BufferQueueAbandonedException { 666 checkNotNull(surface); 667 return LegacyExceptionUtils.throwOnError(nativeDetectSurfaceDataspace(surface)); 668 } 669 connectSurface(Surface surface)670 static void connectSurface(Surface surface) throws BufferQueueAbandonedException { 671 checkNotNull(surface); 672 673 LegacyExceptionUtils.throwOnError(nativeConnectSurface(surface)); 674 } 675 disconnectSurface(Surface surface)676 static void disconnectSurface(Surface surface) throws BufferQueueAbandonedException { 677 if (surface == null) return; 678 679 LegacyExceptionUtils.throwOnError(nativeDisconnectSurface(surface)); 680 } 681 produceFrame(Surface surface, byte[] pixelBuffer, int width, int height, int pixelFormat)682 static void produceFrame(Surface surface, byte[] pixelBuffer, int width, 683 int height, int pixelFormat) 684 throws BufferQueueAbandonedException { 685 checkNotNull(surface); 686 checkNotNull(pixelBuffer); 687 checkArgumentPositive(width, "width must be positive."); 688 checkArgumentPositive(height, "height must be positive."); 689 690 LegacyExceptionUtils.throwOnError(nativeProduceFrame(surface, pixelBuffer, width, height, 691 pixelFormat)); 692 } 693 setSurfaceFormat(Surface surface, int pixelFormat)694 static void setSurfaceFormat(Surface surface, int pixelFormat) 695 throws BufferQueueAbandonedException { 696 checkNotNull(surface); 697 698 LegacyExceptionUtils.throwOnError(nativeSetSurfaceFormat(surface, pixelFormat)); 699 } 700 setSurfaceDimens(Surface surface, int width, int height)701 static void setSurfaceDimens(Surface surface, int width, int height) 702 throws BufferQueueAbandonedException { 703 checkNotNull(surface); 704 checkArgumentPositive(width, "width must be positive."); 705 checkArgumentPositive(height, "height must be positive."); 706 707 LegacyExceptionUtils.throwOnError(nativeSetSurfaceDimens(surface, width, height)); 708 } 709 getSurfaceId(Surface surface)710 static long getSurfaceId(Surface surface) throws BufferQueueAbandonedException { 711 checkNotNull(surface); 712 try { 713 return nativeGetSurfaceId(surface); 714 } catch (IllegalArgumentException e) { 715 throw new BufferQueueAbandonedException(); 716 } 717 } 718 getSurfaceIds(SparseArray<Surface> surfaces)719 static List<Long> getSurfaceIds(SparseArray<Surface> surfaces) 720 throws BufferQueueAbandonedException { 721 if (surfaces == null) { 722 throw new NullPointerException("Null argument surfaces"); 723 } 724 List<Long> surfaceIds = new ArrayList<>(); 725 int count = surfaces.size(); 726 for (int i = 0; i < count; i++) { 727 long id = getSurfaceId(surfaces.valueAt(i)); 728 if (id == 0) { 729 throw new IllegalStateException( 730 "Configured surface had null native GraphicBufferProducer pointer!"); 731 } 732 surfaceIds.add(id); 733 } 734 return surfaceIds; 735 } 736 getSurfaceIds(Collection<Surface> surfaces)737 static List<Long> getSurfaceIds(Collection<Surface> surfaces) 738 throws BufferQueueAbandonedException { 739 if (surfaces == null) { 740 throw new NullPointerException("Null argument surfaces"); 741 } 742 List<Long> surfaceIds = new ArrayList<>(); 743 for (Surface s : surfaces) { 744 long id = getSurfaceId(s); 745 if (id == 0) { 746 throw new IllegalStateException( 747 "Configured surface had null native GraphicBufferProducer pointer!"); 748 } 749 surfaceIds.add(id); 750 } 751 return surfaceIds; 752 } 753 containsSurfaceId(Surface s, Collection<Long> ids)754 static boolean containsSurfaceId(Surface s, Collection<Long> ids) { 755 long id = 0; 756 try { 757 id = getSurfaceId(s); 758 } catch (BufferQueueAbandonedException e) { 759 // If surface is abandoned, return false. 760 return false; 761 } 762 return ids.contains(id); 763 } 764 setSurfaceOrientation(Surface surface, int facing, int sensorOrientation)765 static void setSurfaceOrientation(Surface surface, int facing, int sensorOrientation) 766 throws BufferQueueAbandonedException { 767 checkNotNull(surface); 768 LegacyExceptionUtils.throwOnError(nativeSetSurfaceOrientation(surface, facing, 769 sensorOrientation)); 770 } 771 getTextureSize(SurfaceTexture surfaceTexture)772 static Size getTextureSize(SurfaceTexture surfaceTexture) 773 throws BufferQueueAbandonedException { 774 checkNotNull(surfaceTexture); 775 776 int[] dimens = new int[2]; 777 LegacyExceptionUtils.throwOnError(nativeDetectTextureDimens(surfaceTexture, 778 /*out*/dimens)); 779 780 return new Size(dimens[0], dimens[1]); 781 } 782 setNextTimestamp(Surface surface, long timestamp)783 static void setNextTimestamp(Surface surface, long timestamp) 784 throws BufferQueueAbandonedException { 785 checkNotNull(surface); 786 LegacyExceptionUtils.throwOnError(nativeSetNextTimestamp(surface, timestamp)); 787 } 788 setScalingMode(Surface surface, int mode)789 static void setScalingMode(Surface surface, int mode) 790 throws BufferQueueAbandonedException { 791 checkNotNull(surface); 792 LegacyExceptionUtils.throwOnError(nativeSetScalingMode(surface, mode)); 793 } 794 795 nativeDetectSurfaceType(Surface surface)796 private static native int nativeDetectSurfaceType(Surface surface); 797 nativeDetectSurfaceDataspace(Surface surface)798 private static native int nativeDetectSurfaceDataspace(Surface surface); 799 nativeDetectSurfaceDimens(Surface surface, int[ ] dimens)800 private static native int nativeDetectSurfaceDimens(Surface surface, 801 /*out*/int[/*2*/] dimens); 802 nativeConnectSurface(Surface surface)803 private static native int nativeConnectSurface(Surface surface); 804 nativeProduceFrame(Surface surface, byte[] pixelBuffer, int width, int height, int pixelFormat)805 private static native int nativeProduceFrame(Surface surface, byte[] pixelBuffer, int width, 806 int height, int pixelFormat); 807 nativeSetSurfaceFormat(Surface surface, int pixelFormat)808 private static native int nativeSetSurfaceFormat(Surface surface, int pixelFormat); 809 nativeSetSurfaceDimens(Surface surface, int width, int height)810 private static native int nativeSetSurfaceDimens(Surface surface, int width, int height); 811 nativeGetSurfaceId(Surface surface)812 private static native long nativeGetSurfaceId(Surface surface); 813 nativeSetSurfaceOrientation(Surface surface, int facing, int sensorOrientation)814 private static native int nativeSetSurfaceOrientation(Surface surface, int facing, 815 int sensorOrientation); 816 nativeDetectTextureDimens(SurfaceTexture surfaceTexture, int[ ] dimens)817 private static native int nativeDetectTextureDimens(SurfaceTexture surfaceTexture, 818 /*out*/int[/*2*/] dimens); 819 nativeSetNextTimestamp(Surface surface, long timestamp)820 private static native int nativeSetNextTimestamp(Surface surface, long timestamp); 821 nativeDetectSurfaceUsageFlags(Surface surface)822 private static native int nativeDetectSurfaceUsageFlags(Surface surface); 823 nativeSetScalingMode(Surface surface, int scalingMode)824 private static native int nativeSetScalingMode(Surface surface, int scalingMode); 825 nativeDisconnectSurface(Surface surface)826 private static native int nativeDisconnectSurface(Surface surface); 827 nativeGetJpegFooterSize()828 static native int nativeGetJpegFooterSize(); 829 } 830