LegacyCameraDevice.java revision 68e4fc8ca9b923bc6d053589329eab8509d625ab
1/*
2 * Copyright (C) 2014 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package android.hardware.camera2.legacy;
18
19import android.graphics.ImageFormat;
20import android.graphics.SurfaceTexture;
21import android.hardware.Camera;
22import android.hardware.camera2.CameraCharacteristics;
23import android.hardware.camera2.CaptureRequest;
24import android.hardware.camera2.impl.CameraDeviceImpl;
25import android.hardware.camera2.impl.CaptureResultExtras;
26import android.hardware.camera2.ICameraDeviceCallbacks;
27import android.hardware.camera2.params.StreamConfigurationMap;
28import android.hardware.camera2.utils.ArrayUtils;
29import android.hardware.camera2.utils.CameraBinderDecorator;
30import android.hardware.camera2.utils.LongParcelable;
31import android.hardware.camera2.impl.CameraMetadataNative;
32import android.hardware.camera2.utils.CameraRuntimeException;
33import android.os.ConditionVariable;
34import android.os.Handler;
35import android.os.HandlerThread;
36import android.os.RemoteException;
37import android.util.Log;
38import android.util.Pair;
39import android.util.Size;
40import android.view.Surface;
41
42import java.util.ArrayList;
43import java.util.Arrays;
44import java.util.Collection;
45import java.util.List;
46
47import static android.hardware.camera2.legacy.LegacyExceptionUtils.*;
48import static android.hardware.camera2.utils.CameraBinderDecorator.*;
49import static com.android.internal.util.Preconditions.*;
50
51/**
52 * This class emulates the functionality of a Camera2 device using a the old Camera class.
53 *
54 * <p>
55 * There are two main components that are used to implement this:
56 * - A state machine containing valid Camera2 device states ({@link CameraDeviceState}).
57 * - A message-queue based pipeline that manages an old Camera class, and executes capture and
58 *   configuration requests.
59 * </p>
60 */
61public class LegacyCameraDevice implements AutoCloseable {
62    private final String TAG;
63
64    private static final boolean DEBUG = false;
65    private final int mCameraId;
66    private final CameraCharacteristics mStaticCharacteristics;
67    private final ICameraDeviceCallbacks mDeviceCallbacks;
68    private final CameraDeviceState mDeviceState = new CameraDeviceState();
69    private List<Surface> mConfiguredSurfaces;
70    private boolean mClosed = false;
71
72    private final ConditionVariable mIdle = new ConditionVariable(/*open*/true);
73
74    private final HandlerThread mResultThread = new HandlerThread("ResultThread");
75    private final HandlerThread mCallbackHandlerThread = new HandlerThread("CallbackThread");
76    private final Handler mCallbackHandler;
77    private final Handler mResultHandler;
78    private static final int ILLEGAL_VALUE = -1;
79
80    // Keep up to date with values in hardware/libhardware/include/hardware/gralloc.h
81    private static final int GRALLOC_USAGE_RENDERSCRIPT = 0x00100000;
82    private static final int GRALLOC_USAGE_SW_READ_OFTEN = 0x00000003;
83    private static final int GRALLOC_USAGE_HW_TEXTURE = 0x00000100;
84    private static final int GRALLOC_USAGE_HW_COMPOSER = 0x00000800;
85    private static final int GRALLOC_USAGE_HW_RENDER = 0x00000200;
86    private static final int GRALLOC_USAGE_HW_VIDEO_ENCODER = 0x00010000;
87
88    public static final int MAX_DIMEN_FOR_ROUNDING = 1920; // maximum allowed width for rounding
89
90    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                if (!output.isValid()) {
296                    Log.e(TAG, "configureOutputs - invalid output surfaces are not allowed");
297                    return BAD_VALUE;
298                }
299                StreamConfigurationMap streamConfigurations = mStaticCharacteristics.
300                        get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
301
302                // Validate surface size and format.
303                try {
304                    Size s = getSurfaceSize(output);
305                    int surfaceType = detectSurfaceType(output);
306
307                    boolean flexibleConsumer = isFlexibleConsumer(output);
308
309                    Size[] sizes = streamConfigurations.getOutputSizes(surfaceType);
310                    if (sizes == null) {
311                        // WAR: Override default format to IMPLEMENTATION_DEFINED for b/9487482
312                        if ((surfaceType >= LegacyMetadataMapper.HAL_PIXEL_FORMAT_RGBA_8888 &&
313                            surfaceType <= LegacyMetadataMapper.HAL_PIXEL_FORMAT_BGRA_8888)) {
314
315                            // YUV_420_888 is always present in LEGACY for all
316                            // IMPLEMENTATION_DEFINED output sizes, and is publicly visible in the
317                            // API (i.e. {@code #getOutputSizes} works here).
318                            sizes = streamConfigurations.getOutputSizes(ImageFormat.YUV_420_888);
319                        } else if (surfaceType == LegacyMetadataMapper.HAL_PIXEL_FORMAT_BLOB) {
320                            sizes = streamConfigurations.getOutputSizes(ImageFormat.JPEG);
321                        }
322                    }
323
324                    if (!ArrayUtils.contains(sizes, s)) {
325                        if (flexibleConsumer && (s = findClosestSize(s, sizes)) != null) {
326                            sizedSurfaces.add(new Pair<>(output, s));
327                        } else {
328                            String reason = (sizes == null) ? "format is invalid." :
329                                    ("size not in valid set: " + Arrays.toString(sizes));
330                            Log.e(TAG, String.format("Surface with size (w=%d, h=%d) and format " +
331                                    "0x%x is not valid, %s", s.getWidth(), s.getHeight(),
332                                    surfaceType, reason));
333                            return BAD_VALUE;
334                        }
335                    } else {
336                        sizedSurfaces.add(new Pair<>(output, s));
337                    }
338                } catch (BufferQueueAbandonedException e) {
339                    Log.e(TAG, "Surface bufferqueue is abandoned, cannot configure as output: ", e);
340                    return BAD_VALUE;
341                }
342
343            }
344        }
345
346        boolean success = false;
347        if (mDeviceState.setConfiguring()) {
348            mRequestThreadManager.configure(sizedSurfaces);
349            success = mDeviceState.setIdle();
350        }
351
352        if (success) {
353            mConfiguredSurfaces = outputs != null ? new ArrayList<>(outputs) : null;
354        } else {
355            return CameraBinderDecorator.INVALID_OPERATION;
356        }
357        return CameraBinderDecorator.NO_ERROR;
358    }
359
360    /**
361     * Submit a burst of capture requests.
362     *
363     * @param requestList a list of capture requests to execute.
364     * @param repeating {@code true} if this burst is repeating.
365     * @param frameNumber an output argument that contains either the frame number of the last frame
366     *                    that will be returned for this request, or the frame number of the last
367     *                    frame that will be returned for the current repeating request if this
368     *                    burst is set to be repeating.
369     * @return the request id.
370     */
371    public int submitRequestList(List<CaptureRequest> requestList, boolean repeating,
372            /*out*/LongParcelable frameNumber) {
373        if (requestList == null || requestList.isEmpty()) {
374            Log.e(TAG, "submitRequestList - Empty/null requests are not allowed");
375            return BAD_VALUE;
376        }
377
378        List<Long> surfaceIds = (mConfiguredSurfaces == null) ? new ArrayList<Long>() :
379                getSurfaceIds(mConfiguredSurfaces);
380
381        // Make sure that there all requests have at least 1 surface; all surfaces are non-null
382        for (CaptureRequest request : requestList) {
383            if (request.getTargets().isEmpty()) {
384                Log.e(TAG, "submitRequestList - "
385                        + "Each request must have at least one Surface target");
386                return BAD_VALUE;
387            }
388
389            for (Surface surface : request.getTargets()) {
390                if (surface == null) {
391                    Log.e(TAG, "submitRequestList - Null Surface targets are not allowed");
392                    return BAD_VALUE;
393                } else if (mConfiguredSurfaces == null) {
394                    Log.e(TAG, "submitRequestList - must configure " +
395                            " device with valid surfaces before submitting requests");
396                    return INVALID_OPERATION;
397                } else if (!containsSurfaceId(surface, surfaceIds)) {
398                    Log.e(TAG, "submitRequestList - cannot use a surface that wasn't configured");
399                    return BAD_VALUE;
400                }
401            }
402        }
403
404        // TODO: further validation of request here
405        mIdle.close();
406        return mRequestThreadManager.submitCaptureRequests(requestList, repeating,
407                frameNumber);
408    }
409
410    /**
411     * Submit a single capture request.
412     *
413     * @param request the capture request to execute.
414     * @param repeating {@code true} if this request is repeating.
415     * @param frameNumber an output argument that contains either the frame number of the last frame
416     *                    that will be returned for this request, or the frame number of the last
417     *                    frame that will be returned for the current repeating request if this
418     *                    request is set to be repeating.
419     * @return the request id.
420     */
421    public int submitRequest(CaptureRequest request, boolean repeating,
422            /*out*/LongParcelable frameNumber) {
423        ArrayList<CaptureRequest> requestList = new ArrayList<CaptureRequest>();
424        requestList.add(request);
425        return submitRequestList(requestList, repeating, frameNumber);
426    }
427
428    /**
429     * Cancel the repeating request with the given request id.
430     *
431     * @param requestId the request id of the request to cancel.
432     * @return the last frame number to be returned from the HAL for the given repeating request, or
433     *          {@code INVALID_FRAME} if none exists.
434     */
435    public long cancelRequest(int requestId) {
436        return mRequestThreadManager.cancelRepeating(requestId);
437    }
438
439    /**
440     * Block until the {@link ICameraDeviceCallbacks#onCameraIdle()} callback is received.
441     */
442    public void waitUntilIdle()  {
443        mIdle.block();
444    }
445
446    /**
447     * Flush any pending requests.
448     *
449     * @return the last frame number.
450     */
451    public long flush() {
452        long lastFrame = mRequestThreadManager.flush();
453        waitUntilIdle();
454        return lastFrame;
455    }
456
457    /**
458     * Return {@code true} if the device has been closed.
459     */
460    public boolean isClosed() {
461        return mClosed;
462    }
463
464    @Override
465    public void close() {
466        mRequestThreadManager.quit();
467        mCallbackHandlerThread.quitSafely();
468        mResultThread.quitSafely();
469
470        try {
471            mCallbackHandlerThread.join();
472        } catch (InterruptedException e) {
473            Log.e(TAG, String.format("Thread %s (%d) interrupted while quitting.",
474                    mCallbackHandlerThread.getName(), mCallbackHandlerThread.getId()));
475        }
476
477        try {
478            mResultThread.join();
479        } catch (InterruptedException e) {
480            Log.e(TAG, String.format("Thread %s (%d) interrupted while quitting.",
481                    mResultThread.getName(), mResultThread.getId()));
482        }
483
484        mClosed = true;
485    }
486
487    @Override
488    protected void finalize() throws Throwable {
489        try {
490            close();
491        } catch (CameraRuntimeException e) {
492            Log.e(TAG, "Got error while trying to finalize, ignoring: " + e.getMessage());
493        } finally {
494            super.finalize();
495        }
496    }
497
498    static long findEuclidDistSquare(Size a, Size b) {
499        long d0 = a.getWidth() - b.getWidth();
500        long d1 = a.getHeight() - b.getHeight();
501        return d0 * d0 + d1 * d1;
502    }
503
504    // Keep up to date with rounding behavior in
505    // frameworks/av/services/camera/libcameraservice/api2/CameraDeviceClient.cpp
506    static Size findClosestSize(Size size, Size[] supportedSizes) {
507        if (size == null || supportedSizes == null) {
508            return null;
509        }
510        Size bestSize = null;
511        for (Size s : supportedSizes) {
512            if (s.equals(size)) {
513                return size;
514            } else if (s.getWidth() <= MAX_DIMEN_FOR_ROUNDING && (bestSize == null ||
515                    LegacyCameraDevice.findEuclidDistSquare(size, s) <
516                    LegacyCameraDevice.findEuclidDistSquare(bestSize, s))) {
517                bestSize = s;
518            }
519        }
520        return bestSize;
521    }
522
523    /**
524     * Query the surface for its currently configured default buffer size.
525     * @param surface a non-{@code null} {@code Surface}
526     * @return the width and height of the surface
527     *
528     * @throws NullPointerException if the {@code surface} was {@code null}
529     * @throws BufferQueueAbandonedException if the {@code surface} was invalid
530     */
531    public static Size getSurfaceSize(Surface surface) throws BufferQueueAbandonedException {
532        checkNotNull(surface);
533
534        int[] dimens = new int[2];
535        LegacyExceptionUtils.throwOnError(nativeDetectSurfaceDimens(surface, /*out*/dimens));
536
537        return new Size(dimens[0], dimens[1]);
538    }
539
540    public static boolean isFlexibleConsumer(Surface output) {
541        int usageFlags = detectSurfaceUsageFlags(output);
542
543        // Keep up to date with allowed consumer types in
544        // frameworks/av/services/camera/libcameraservice/api2/CameraDeviceClient.cpp
545        int disallowedFlags = GRALLOC_USAGE_HW_VIDEO_ENCODER | GRALLOC_USAGE_RENDERSCRIPT;
546        int allowedFlags = GRALLOC_USAGE_HW_TEXTURE | GRALLOC_USAGE_SW_READ_OFTEN |
547            GRALLOC_USAGE_HW_COMPOSER;
548        boolean flexibleConsumer = ((usageFlags & disallowedFlags) == 0 &&
549                (usageFlags & allowedFlags) != 0);
550        return flexibleConsumer;
551    }
552
553    public static boolean isPreviewConsumer(Surface output) {
554        int usageFlags = detectSurfaceUsageFlags(output);
555        int disallowedFlags = GRALLOC_USAGE_HW_VIDEO_ENCODER | GRALLOC_USAGE_RENDERSCRIPT |
556                GRALLOC_USAGE_SW_READ_OFTEN;
557        int allowedFlags = GRALLOC_USAGE_HW_TEXTURE | GRALLOC_USAGE_HW_COMPOSER |
558                GRALLOC_USAGE_HW_RENDER;
559        boolean previewConsumer = ((usageFlags & disallowedFlags) == 0 &&
560                (usageFlags & allowedFlags) != 0);
561        int surfaceFormat = ImageFormat.UNKNOWN;
562        try {
563            surfaceFormat = detectSurfaceType(output);
564        } catch(BufferQueueAbandonedException e) {
565            throw new IllegalArgumentException("Surface was abandoned", e);
566        }
567
568        return previewConsumer && (surfaceFormat == ImageFormat.PRIVATE);
569    }
570
571    public static boolean isVideoEncoderConsumer(Surface output) {
572        int usageFlags = detectSurfaceUsageFlags(output);
573        int disallowedFlags = GRALLOC_USAGE_HW_TEXTURE | GRALLOC_USAGE_HW_COMPOSER |
574                GRALLOC_USAGE_RENDERSCRIPT | GRALLOC_USAGE_SW_READ_OFTEN;
575        int allowedFlags = GRALLOC_USAGE_HW_VIDEO_ENCODER;
576        boolean videoEncoderConsumer = ((usageFlags & disallowedFlags) == 0 &&
577                (usageFlags & allowedFlags) != 0);
578
579        int surfaceFormat = ImageFormat.UNKNOWN;
580        try {
581            surfaceFormat = detectSurfaceType(output);
582        } catch(BufferQueueAbandonedException e) {
583            throw new IllegalArgumentException("Surface was abandoned", e);
584        }
585
586        return videoEncoderConsumer && (surfaceFormat == ImageFormat.PRIVATE);
587    }
588
589    /**
590     * Query the surface for its currently configured usage flags
591     */
592    static int detectSurfaceUsageFlags(Surface surface) {
593        checkNotNull(surface);
594        return nativeDetectSurfaceUsageFlags(surface);
595    }
596
597    /**
598     * Query the surface for its currently configured format
599     */
600    public static int detectSurfaceType(Surface surface) throws BufferQueueAbandonedException {
601        checkNotNull(surface);
602        return LegacyExceptionUtils.throwOnError(nativeDetectSurfaceType(surface));
603    }
604
605    static void configureSurface(Surface surface, int width, int height,
606                                 int pixelFormat) throws BufferQueueAbandonedException {
607        checkNotNull(surface);
608        checkArgumentPositive(width, "width must be positive.");
609        checkArgumentPositive(height, "height must be positive.");
610
611        LegacyExceptionUtils.throwOnError(nativeConfigureSurface(surface, width, height,
612                pixelFormat));
613    }
614
615    static void produceFrame(Surface surface, byte[] pixelBuffer, int width,
616                             int height, int pixelFormat)
617            throws BufferQueueAbandonedException {
618        checkNotNull(surface);
619        checkNotNull(pixelBuffer);
620        checkArgumentPositive(width, "width must be positive.");
621        checkArgumentPositive(height, "height must be positive.");
622
623        LegacyExceptionUtils.throwOnError(nativeProduceFrame(surface, pixelBuffer, width, height,
624                pixelFormat));
625    }
626
627    static void setSurfaceFormat(Surface surface, int pixelFormat)
628            throws BufferQueueAbandonedException {
629        checkNotNull(surface);
630
631        LegacyExceptionUtils.throwOnError(nativeSetSurfaceFormat(surface, pixelFormat));
632    }
633
634    static void setSurfaceDimens(Surface surface, int width, int height)
635            throws BufferQueueAbandonedException {
636        checkNotNull(surface);
637        checkArgumentPositive(width, "width must be positive.");
638        checkArgumentPositive(height, "height must be positive.");
639
640        LegacyExceptionUtils.throwOnError(nativeSetSurfaceDimens(surface, width, height));
641    }
642
643    static long getSurfaceId(Surface surface) {
644        checkNotNull(surface);
645        return nativeGetSurfaceId(surface);
646    }
647
648    static List<Long> getSurfaceIds(Collection<Surface> surfaces) {
649        if (surfaces == null) {
650            throw new NullPointerException("Null argument surfaces");
651        }
652        List<Long> surfaceIds = new ArrayList<>();
653        for (Surface s : surfaces) {
654            long id = getSurfaceId(s);
655            if (id == 0) {
656                throw new IllegalStateException(
657                        "Configured surface had null native GraphicBufferProducer pointer!");
658            }
659            surfaceIds.add(id);
660        }
661        return surfaceIds;
662    }
663
664    static boolean containsSurfaceId(Surface s, Collection<Long> ids) {
665        long id = getSurfaceId(s);
666        return ids.contains(id);
667    }
668
669    static void setSurfaceOrientation(Surface surface, int facing, int sensorOrientation)
670            throws BufferQueueAbandonedException {
671        checkNotNull(surface);
672        LegacyExceptionUtils.throwOnError(nativeSetSurfaceOrientation(surface, facing,
673                sensorOrientation));
674    }
675
676    static Size getTextureSize(SurfaceTexture surfaceTexture)
677            throws BufferQueueAbandonedException {
678        checkNotNull(surfaceTexture);
679
680        int[] dimens = new int[2];
681        LegacyExceptionUtils.throwOnError(nativeDetectTextureDimens(surfaceTexture,
682                /*out*/dimens));
683
684        return new Size(dimens[0], dimens[1]);
685    }
686
687    static void setNextTimestamp(Surface surface, long timestamp)
688            throws BufferQueueAbandonedException {
689        checkNotNull(surface);
690        LegacyExceptionUtils.throwOnError(nativeSetNextTimestamp(surface, timestamp));
691    }
692
693    private static native int nativeDetectSurfaceType(Surface surface);
694
695    private static native int nativeDetectSurfaceDimens(Surface surface,
696            /*out*/int[/*2*/] dimens);
697
698    private static native int nativeConfigureSurface(Surface surface, int width, int height,
699                                                        int pixelFormat);
700
701    private static native int nativeProduceFrame(Surface surface, byte[] pixelBuffer, int width,
702                                                    int height, int pixelFormat);
703
704    private static native int nativeSetSurfaceFormat(Surface surface, int pixelFormat);
705
706    private static native int nativeSetSurfaceDimens(Surface surface, int width, int height);
707
708    private static native long nativeGetSurfaceId(Surface surface);
709
710    private static native int nativeSetSurfaceOrientation(Surface surface, int facing,
711                                                             int sensorOrientation);
712
713    private static native int nativeDetectTextureDimens(SurfaceTexture surfaceTexture,
714            /*out*/int[/*2*/] dimens);
715
716    private static native int nativeSetNextTimestamp(Surface surface, long timestamp);
717
718    private static native int nativeDetectSurfaceUsageFlags(Surface surface);
719
720    static native int nativeGetJpegFooterSize();
721}
722