Home | History | Annotate | Download | only in legacy
      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.CameraBinderDecorator;
     30 import android.hardware.camera2.utils.LongParcelable;
     31 import android.hardware.camera2.impl.CameraMetadataNative;
     32 import android.hardware.camera2.utils.CameraRuntimeException;
     33 import android.os.ConditionVariable;
     34 import android.os.Handler;
     35 import android.os.HandlerThread;
     36 import android.os.RemoteException;
     37 import android.util.Log;
     38 import android.util.Pair;
     39 import android.util.Size;
     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 android.hardware.camera2.utils.CameraBinderDecorator.*;
     49 import 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  */
     61 public 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                     // Lock down the size before configuration
    342                     setSurfaceDimens(output, s.getWidth(), s.getHeight());
    343                 } catch (BufferQueueAbandonedException e) {
    344                     Log.e(TAG, "Surface bufferqueue is abandoned, cannot configure as output: ", e);
    345                     return BAD_VALUE;
    346                 }
    347 
    348             }
    349         }
    350 
    351         boolean success = false;
    352         if (mDeviceState.setConfiguring()) {
    353             mRequestThreadManager.configure(sizedSurfaces);
    354             success = mDeviceState.setIdle();
    355         }
    356 
    357         if (success) {
    358             mConfiguredSurfaces = outputs != null ? new ArrayList<>(outputs) : null;
    359         } else {
    360             return CameraBinderDecorator.INVALID_OPERATION;
    361         }
    362         return CameraBinderDecorator.NO_ERROR;
    363     }
    364 
    365     /**
    366      * Submit a burst of capture requests.
    367      *
    368      * @param requestList a list of capture requests to execute.
    369      * @param repeating {@code true} if this burst is repeating.
    370      * @param frameNumber an output argument that contains either the frame number of the last frame
    371      *                    that will be returned for this request, or the frame number of the last
    372      *                    frame that will be returned for the current repeating request if this
    373      *                    burst is set to be repeating.
    374      * @return the request id.
    375      */
    376     public int submitRequestList(List<CaptureRequest> requestList, boolean repeating,
    377             /*out*/LongParcelable frameNumber) {
    378         if (requestList == null || requestList.isEmpty()) {
    379             Log.e(TAG, "submitRequestList - Empty/null requests are not allowed");
    380             return BAD_VALUE;
    381         }
    382 
    383         List<Long> surfaceIds = (mConfiguredSurfaces == null) ? new ArrayList<Long>() :
    384                 getSurfaceIds(mConfiguredSurfaces);
    385 
    386         // Make sure that there all requests have at least 1 surface; all surfaces are non-null
    387         for (CaptureRequest request : requestList) {
    388             if (request.getTargets().isEmpty()) {
    389                 Log.e(TAG, "submitRequestList - "
    390                         + "Each request must have at least one Surface target");
    391                 return BAD_VALUE;
    392             }
    393 
    394             for (Surface surface : request.getTargets()) {
    395                 if (surface == null) {
    396                     Log.e(TAG, "submitRequestList - Null Surface targets are not allowed");
    397                     return BAD_VALUE;
    398                 } else if (mConfiguredSurfaces == null) {
    399                     Log.e(TAG, "submitRequestList - must configure " +
    400                             " device with valid surfaces before submitting requests");
    401                     return INVALID_OPERATION;
    402                 } else if (!containsSurfaceId(surface, surfaceIds)) {
    403                     Log.e(TAG, "submitRequestList - cannot use a surface that wasn't configured");
    404                     return BAD_VALUE;
    405                 }
    406             }
    407         }
    408 
    409         // TODO: further validation of request here
    410         mIdle.close();
    411         return mRequestThreadManager.submitCaptureRequests(requestList, repeating,
    412                 frameNumber);
    413     }
    414 
    415     /**
    416      * Submit a single capture request.
    417      *
    418      * @param request the capture request to execute.
    419      * @param repeating {@code true} if this request is repeating.
    420      * @param frameNumber an output argument that contains either the frame number of the last frame
    421      *                    that will be returned for this request, or the frame number of the last
    422      *                    frame that will be returned for the current repeating request if this
    423      *                    request is set to be repeating.
    424      * @return the request id.
    425      */
    426     public int submitRequest(CaptureRequest request, boolean repeating,
    427             /*out*/LongParcelable frameNumber) {
    428         ArrayList<CaptureRequest> requestList = new ArrayList<CaptureRequest>();
    429         requestList.add(request);
    430         return submitRequestList(requestList, repeating, frameNumber);
    431     }
    432 
    433     /**
    434      * Cancel the repeating request with the given request id.
    435      *
    436      * @param requestId the request id of the request to cancel.
    437      * @return the last frame number to be returned from the HAL for the given repeating request, or
    438      *          {@code INVALID_FRAME} if none exists.
    439      */
    440     public long cancelRequest(int requestId) {
    441         return mRequestThreadManager.cancelRepeating(requestId);
    442     }
    443 
    444     /**
    445      * Block until the {@link ICameraDeviceCallbacks#onCameraIdle()} callback is received.
    446      */
    447     public void waitUntilIdle()  {
    448         mIdle.block();
    449     }
    450 
    451     /**
    452      * Flush any pending requests.
    453      *
    454      * @return the last frame number.
    455      */
    456     public long flush() {
    457         long lastFrame = mRequestThreadManager.flush();
    458         waitUntilIdle();
    459         return lastFrame;
    460     }
    461 
    462     /**
    463      * Return {@code true} if the device has been closed.
    464      */
    465     public boolean isClosed() {
    466         return mClosed;
    467     }
    468 
    469     @Override
    470     public void close() {
    471         mRequestThreadManager.quit();
    472         mCallbackHandlerThread.quitSafely();
    473         mResultThread.quitSafely();
    474 
    475         try {
    476             mCallbackHandlerThread.join();
    477         } catch (InterruptedException e) {
    478             Log.e(TAG, String.format("Thread %s (%d) interrupted while quitting.",
    479                     mCallbackHandlerThread.getName(), mCallbackHandlerThread.getId()));
    480         }
    481 
    482         try {
    483             mResultThread.join();
    484         } catch (InterruptedException e) {
    485             Log.e(TAG, String.format("Thread %s (%d) interrupted while quitting.",
    486                     mResultThread.getName(), mResultThread.getId()));
    487         }
    488 
    489         mClosed = true;
    490     }
    491 
    492     @Override
    493     protected void finalize() throws Throwable {
    494         try {
    495             close();
    496         } catch (CameraRuntimeException e) {
    497             Log.e(TAG, "Got error while trying to finalize, ignoring: " + e.getMessage());
    498         } finally {
    499             super.finalize();
    500         }
    501     }
    502 
    503     static long findEuclidDistSquare(Size a, Size b) {
    504         long d0 = a.getWidth() - b.getWidth();
    505         long d1 = a.getHeight() - b.getHeight();
    506         return d0 * d0 + d1 * d1;
    507     }
    508 
    509     // Keep up to date with rounding behavior in
    510     // frameworks/av/services/camera/libcameraservice/api2/CameraDeviceClient.cpp
    511     static Size findClosestSize(Size size, Size[] supportedSizes) {
    512         if (size == null || supportedSizes == null) {
    513             return null;
    514         }
    515         Size bestSize = null;
    516         for (Size s : supportedSizes) {
    517             if (s.equals(size)) {
    518                 return size;
    519             } else if (s.getWidth() <= MAX_DIMEN_FOR_ROUNDING && (bestSize == null ||
    520                     LegacyCameraDevice.findEuclidDistSquare(size, s) <
    521                     LegacyCameraDevice.findEuclidDistSquare(bestSize, s))) {
    522                 bestSize = s;
    523             }
    524         }
    525         return bestSize;
    526     }
    527 
    528     /**
    529      * Query the surface for its currently configured default buffer size.
    530      * @param surface a non-{@code null} {@code Surface}
    531      * @return the width and height of the surface
    532      *
    533      * @throws NullPointerException if the {@code surface} was {@code null}
    534      * @throws BufferQueueAbandonedException if the {@code surface} was invalid
    535      */
    536     public static Size getSurfaceSize(Surface surface) throws BufferQueueAbandonedException {
    537         checkNotNull(surface);
    538 
    539         int[] dimens = new int[2];
    540         LegacyExceptionUtils.throwOnError(nativeDetectSurfaceDimens(surface, /*out*/dimens));
    541 
    542         return new Size(dimens[0], dimens[1]);
    543     }
    544 
    545     public static boolean isFlexibleConsumer(Surface output) {
    546         int usageFlags = detectSurfaceUsageFlags(output);
    547 
    548         // Keep up to date with allowed consumer types in
    549         // frameworks/av/services/camera/libcameraservice/api2/CameraDeviceClient.cpp
    550         int disallowedFlags = GRALLOC_USAGE_HW_VIDEO_ENCODER | GRALLOC_USAGE_RENDERSCRIPT;
    551         int allowedFlags = GRALLOC_USAGE_HW_TEXTURE | GRALLOC_USAGE_SW_READ_OFTEN |
    552             GRALLOC_USAGE_HW_COMPOSER;
    553         boolean flexibleConsumer = ((usageFlags & disallowedFlags) == 0 &&
    554                 (usageFlags & allowedFlags) != 0);
    555         return flexibleConsumer;
    556     }
    557 
    558     public static boolean isPreviewConsumer(Surface output) {
    559         int usageFlags = detectSurfaceUsageFlags(output);
    560         int disallowedFlags = GRALLOC_USAGE_HW_VIDEO_ENCODER | GRALLOC_USAGE_RENDERSCRIPT |
    561                 GRALLOC_USAGE_SW_READ_OFTEN;
    562         int allowedFlags = GRALLOC_USAGE_HW_TEXTURE | GRALLOC_USAGE_HW_COMPOSER |
    563                 GRALLOC_USAGE_HW_RENDER;
    564         boolean previewConsumer = ((usageFlags & disallowedFlags) == 0 &&
    565                 (usageFlags & allowedFlags) != 0);
    566         int surfaceFormat = ImageFormat.UNKNOWN;
    567         try {
    568             surfaceFormat = detectSurfaceType(output);
    569         } catch(BufferQueueAbandonedException e) {
    570             throw new IllegalArgumentException("Surface was abandoned", e);
    571         }
    572 
    573         return previewConsumer;
    574     }
    575 
    576     public static boolean isVideoEncoderConsumer(Surface output) {
    577         int usageFlags = detectSurfaceUsageFlags(output);
    578         int disallowedFlags = GRALLOC_USAGE_HW_TEXTURE | GRALLOC_USAGE_HW_COMPOSER |
    579                 GRALLOC_USAGE_RENDERSCRIPT | GRALLOC_USAGE_SW_READ_OFTEN;
    580         int allowedFlags = GRALLOC_USAGE_HW_VIDEO_ENCODER;
    581         boolean videoEncoderConsumer = ((usageFlags & disallowedFlags) == 0 &&
    582                 (usageFlags & allowedFlags) != 0);
    583 
    584         int surfaceFormat = ImageFormat.UNKNOWN;
    585         try {
    586             surfaceFormat = detectSurfaceType(output);
    587         } catch(BufferQueueAbandonedException e) {
    588             throw new IllegalArgumentException("Surface was abandoned", e);
    589         }
    590 
    591         return videoEncoderConsumer;
    592     }
    593 
    594     /**
    595      * Query the surface for its currently configured usage flags
    596      */
    597     static int detectSurfaceUsageFlags(Surface surface) {
    598         checkNotNull(surface);
    599         return nativeDetectSurfaceUsageFlags(surface);
    600     }
    601 
    602     /**
    603      * Query the surface for its currently configured format
    604      */
    605     public static int detectSurfaceType(Surface surface) throws BufferQueueAbandonedException {
    606         checkNotNull(surface);
    607         return LegacyExceptionUtils.throwOnError(nativeDetectSurfaceType(surface));
    608     }
    609 
    610     /**
    611      * Query the surface for its currently configured dataspace
    612      */
    613     public static int detectSurfaceDataspace(Surface surface) throws BufferQueueAbandonedException {
    614         checkNotNull(surface);
    615         return LegacyExceptionUtils.throwOnError(nativeDetectSurfaceDataspace(surface));
    616     }
    617 
    618     static void configureSurface(Surface surface, int width, int height,
    619                                  int pixelFormat) throws BufferQueueAbandonedException {
    620         checkNotNull(surface);
    621         checkArgumentPositive(width, "width must be positive.");
    622         checkArgumentPositive(height, "height must be positive.");
    623 
    624         LegacyExceptionUtils.throwOnError(nativeConfigureSurface(surface, width, height,
    625                 pixelFormat));
    626     }
    627 
    628     static void produceFrame(Surface surface, byte[] pixelBuffer, int width,
    629                              int height, int pixelFormat)
    630             throws BufferQueueAbandonedException {
    631         checkNotNull(surface);
    632         checkNotNull(pixelBuffer);
    633         checkArgumentPositive(width, "width must be positive.");
    634         checkArgumentPositive(height, "height must be positive.");
    635 
    636         LegacyExceptionUtils.throwOnError(nativeProduceFrame(surface, pixelBuffer, width, height,
    637                 pixelFormat));
    638     }
    639 
    640     static void setSurfaceFormat(Surface surface, int pixelFormat)
    641             throws BufferQueueAbandonedException {
    642         checkNotNull(surface);
    643 
    644         LegacyExceptionUtils.throwOnError(nativeSetSurfaceFormat(surface, pixelFormat));
    645     }
    646 
    647     static void setSurfaceDimens(Surface surface, int width, int height)
    648             throws BufferQueueAbandonedException {
    649         checkNotNull(surface);
    650         checkArgumentPositive(width, "width must be positive.");
    651         checkArgumentPositive(height, "height must be positive.");
    652 
    653         LegacyExceptionUtils.throwOnError(nativeSetSurfaceDimens(surface, width, height));
    654     }
    655 
    656     static long getSurfaceId(Surface surface) {
    657         checkNotNull(surface);
    658         return nativeGetSurfaceId(surface);
    659     }
    660 
    661     static List<Long> getSurfaceIds(Collection<Surface> surfaces) {
    662         if (surfaces == null) {
    663             throw new NullPointerException("Null argument surfaces");
    664         }
    665         List<Long> surfaceIds = new ArrayList<>();
    666         for (Surface s : surfaces) {
    667             long id = getSurfaceId(s);
    668             if (id == 0) {
    669                 throw new IllegalStateException(
    670                         "Configured surface had null native GraphicBufferProducer pointer!");
    671             }
    672             surfaceIds.add(id);
    673         }
    674         return surfaceIds;
    675     }
    676 
    677     static boolean containsSurfaceId(Surface s, Collection<Long> ids) {
    678         long id = getSurfaceId(s);
    679         return ids.contains(id);
    680     }
    681 
    682     static void setSurfaceOrientation(Surface surface, int facing, int sensorOrientation)
    683             throws BufferQueueAbandonedException {
    684         checkNotNull(surface);
    685         LegacyExceptionUtils.throwOnError(nativeSetSurfaceOrientation(surface, facing,
    686                 sensorOrientation));
    687     }
    688 
    689     static Size getTextureSize(SurfaceTexture surfaceTexture)
    690             throws BufferQueueAbandonedException {
    691         checkNotNull(surfaceTexture);
    692 
    693         int[] dimens = new int[2];
    694         LegacyExceptionUtils.throwOnError(nativeDetectTextureDimens(surfaceTexture,
    695                 /*out*/dimens));
    696 
    697         return new Size(dimens[0], dimens[1]);
    698     }
    699 
    700     static void setNextTimestamp(Surface surface, long timestamp)
    701             throws BufferQueueAbandonedException {
    702         checkNotNull(surface);
    703         LegacyExceptionUtils.throwOnError(nativeSetNextTimestamp(surface, timestamp));
    704     }
    705 
    706     static void setScalingMode(Surface surface, int mode)
    707             throws BufferQueueAbandonedException {
    708         checkNotNull(surface);
    709         LegacyExceptionUtils.throwOnError(nativeSetScalingMode(surface, mode));
    710     }
    711 
    712 
    713     private static native int nativeDetectSurfaceType(Surface surface);
    714 
    715     private static native int nativeDetectSurfaceDataspace(Surface surface);
    716 
    717     private static native int nativeDetectSurfaceDimens(Surface surface,
    718             /*out*/int[/*2*/] dimens);
    719 
    720     private static native int nativeConfigureSurface(Surface surface, int width, int height,
    721                                                         int pixelFormat);
    722 
    723     private static native int nativeProduceFrame(Surface surface, byte[] pixelBuffer, int width,
    724                                                     int height, int pixelFormat);
    725 
    726     private static native int nativeSetSurfaceFormat(Surface surface, int pixelFormat);
    727 
    728     private static native int nativeSetSurfaceDimens(Surface surface, int width, int height);
    729 
    730     private static native long nativeGetSurfaceId(Surface surface);
    731 
    732     private static native int nativeSetSurfaceOrientation(Surface surface, int facing,
    733                                                              int sensorOrientation);
    734 
    735     private static native int nativeDetectTextureDimens(SurfaceTexture surfaceTexture,
    736             /*out*/int[/*2*/] dimens);
    737 
    738     private static native int nativeSetNextTimestamp(Surface surface, long timestamp);
    739 
    740     private static native int nativeDetectSurfaceUsageFlags(Surface surface);
    741 
    742     private static native int nativeSetScalingMode(Surface surface, int scalingMode);
    743 
    744     static native int nativeGetJpegFooterSize();
    745 }
    746