LegacyCameraDevice.java revision f4a637d0be2e028d1e78c8bf90ad17ec3f84b5f3
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 public static final String DEBUG_PROP = "HAL1ShimLogging"; 63 private final String TAG; 64 65 private static final boolean DEBUG = Log.isLoggable(LegacyCameraDevice.DEBUG_PROP, Log.DEBUG); 66 private final int mCameraId; 67 private final CameraCharacteristics mStaticCharacteristics; 68 private final ICameraDeviceCallbacks mDeviceCallbacks; 69 private final CameraDeviceState mDeviceState = new CameraDeviceState(); 70 private List<Surface> mConfiguredSurfaces; 71 private boolean mClosed = false; 72 73 private final ConditionVariable mIdle = new ConditionVariable(/*open*/true); 74 75 private final HandlerThread mResultThread = new HandlerThread("ResultThread"); 76 private final HandlerThread mCallbackHandlerThread = new HandlerThread("CallbackThread"); 77 private final Handler mCallbackHandler; 78 private final Handler mResultHandler; 79 private static final int ILLEGAL_VALUE = -1; 80 81 // Keep up to date with values in hardware/libhardware/include/hardware/gralloc.h 82 private static final int GRALLOC_USAGE_RENDERSCRIPT = 0x00100000; 83 private static final int GRALLOC_USAGE_SW_READ_OFTEN = 0x00000003; 84 private static final int GRALLOC_USAGE_HW_TEXTURE = 0x00000100; 85 private static final int GRALLOC_USAGE_HW_COMPOSER = 0x00000800; 86 private static final int GRALLOC_USAGE_HW_VIDEO_ENCODER = 0x00010000; 87 88 private static final int MAX_DIMEN_FOR_ROUNDING = 1080; // 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 StreamConfigurationMap streamConfigurations = mStaticCharacteristics. 296 get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP); 297 298 // Validate surface size and format. 299 try { 300 Size s = getSurfaceSize(output); 301 int surfaceType = detectSurfaceType(output); 302 int usageFlags = detectSurfaceUsageFlags(output); 303 304 // Keep up to date with allowed consumer types in 305 // frameworks/av/services/camera/libcameraservice/api2/CameraDeviceClient.cpp 306 int disallowedFlags = GRALLOC_USAGE_HW_VIDEO_ENCODER | GRALLOC_USAGE_RENDERSCRIPT; 307 int allowedFlags = GRALLOC_USAGE_HW_TEXTURE | GRALLOC_USAGE_SW_READ_OFTEN | 308 GRALLOC_USAGE_HW_COMPOSER; 309 boolean flexibleConsumer = ((usageFlags & disallowedFlags) == 0 && 310 (usageFlags & allowedFlags) != 0); 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 IllegalStateException if the {@code surface} was invalid 533 */ 534 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 static int detectSurfaceUsageFlags(Surface surface) { 544 checkNotNull(surface); 545 return nativeDetectSurfaceUsageFlags(surface); 546 } 547 548 static int detectSurfaceType(Surface surface) throws BufferQueueAbandonedException { 549 checkNotNull(surface); 550 return LegacyExceptionUtils.throwOnError(nativeDetectSurfaceType(surface)); 551 } 552 553 static void configureSurface(Surface surface, int width, int height, 554 int pixelFormat) throws BufferQueueAbandonedException { 555 checkNotNull(surface); 556 checkArgumentPositive(width, "width must be positive."); 557 checkArgumentPositive(height, "height must be positive."); 558 559 LegacyExceptionUtils.throwOnError(nativeConfigureSurface(surface, width, height, 560 pixelFormat)); 561 } 562 563 static void produceFrame(Surface surface, byte[] pixelBuffer, int width, 564 int height, int pixelFormat) 565 throws BufferQueueAbandonedException { 566 checkNotNull(surface); 567 checkNotNull(pixelBuffer); 568 checkArgumentPositive(width, "width must be positive."); 569 checkArgumentPositive(height, "height must be positive."); 570 571 LegacyExceptionUtils.throwOnError(nativeProduceFrame(surface, pixelBuffer, width, height, 572 pixelFormat)); 573 } 574 575 static void setSurfaceFormat(Surface surface, int pixelFormat) 576 throws BufferQueueAbandonedException { 577 checkNotNull(surface); 578 579 LegacyExceptionUtils.throwOnError(nativeSetSurfaceFormat(surface, pixelFormat)); 580 } 581 582 static void setSurfaceDimens(Surface surface, int width, int height) 583 throws BufferQueueAbandonedException { 584 checkNotNull(surface); 585 checkArgumentPositive(width, "width must be positive."); 586 checkArgumentPositive(height, "height must be positive."); 587 588 LegacyExceptionUtils.throwOnError(nativeSetSurfaceDimens(surface, width, height)); 589 } 590 591 static long getSurfaceId(Surface surface) { 592 checkNotNull(surface); 593 return nativeGetSurfaceId(surface); 594 } 595 596 static List<Long> getSurfaceIds(Collection<Surface> surfaces) { 597 if (surfaces == null) { 598 throw new NullPointerException("Null argument surfaces"); 599 } 600 List<Long> surfaceIds = new ArrayList<>(); 601 for (Surface s : surfaces) { 602 long id = getSurfaceId(s); 603 if (id == 0) { 604 throw new IllegalStateException( 605 "Configured surface had null native GraphicBufferProducer pointer!"); 606 } 607 surfaceIds.add(id); 608 } 609 return surfaceIds; 610 } 611 612 static boolean containsSurfaceId(Surface s, Collection<Long> ids) { 613 long id = getSurfaceId(s); 614 return ids.contains(id); 615 } 616 617 static void setSurfaceOrientation(Surface surface, int facing, int sensorOrientation) 618 throws BufferQueueAbandonedException { 619 checkNotNull(surface); 620 LegacyExceptionUtils.throwOnError(nativeSetSurfaceOrientation(surface, facing, 621 sensorOrientation)); 622 } 623 624 static Size getTextureSize(SurfaceTexture surfaceTexture) 625 throws BufferQueueAbandonedException { 626 checkNotNull(surfaceTexture); 627 628 int[] dimens = new int[2]; 629 LegacyExceptionUtils.throwOnError(nativeDetectTextureDimens(surfaceTexture, 630 /*out*/dimens)); 631 632 return new Size(dimens[0], dimens[1]); 633 } 634 635 static void setNextTimestamp(Surface surface, long timestamp) 636 throws BufferQueueAbandonedException { 637 checkNotNull(surface); 638 LegacyExceptionUtils.throwOnError(nativeSetNextTimestamp(surface, timestamp)); 639 } 640 641 private static native int nativeDetectSurfaceType(Surface surface); 642 643 private static native int nativeDetectSurfaceDimens(Surface surface, 644 /*out*/int[/*2*/] dimens); 645 646 private static native int nativeConfigureSurface(Surface surface, int width, int height, 647 int pixelFormat); 648 649 private static native int nativeProduceFrame(Surface surface, byte[] pixelBuffer, int width, 650 int height, int pixelFormat); 651 652 private static native int nativeSetSurfaceFormat(Surface surface, int pixelFormat); 653 654 private static native int nativeSetSurfaceDimens(Surface surface, int width, int height); 655 656 private static native long nativeGetSurfaceId(Surface surface); 657 658 private static native int nativeSetSurfaceOrientation(Surface surface, int facing, 659 int sensorOrientation); 660 661 private static native int nativeDetectTextureDimens(SurfaceTexture surfaceTexture, 662 /*out*/int[/*2*/] dimens); 663 664 private static native int nativeSetNextTimestamp(Surface surface, long timestamp); 665 666 private static native int nativeDetectSurfaceUsageFlags(Surface surface); 667 668 static native int nativeGetJpegFooterSize(); 669} 670