LegacyCameraDevice.java revision 0fd198ad89ec9c600bb1761b10d938146c28bb98
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.CaptureResultExtras;
25import android.hardware.camera2.ICameraDeviceCallbacks;
26import android.hardware.camera2.params.StreamConfiguration;
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.Size;
39import android.view.Surface;
40
41import java.util.ArrayList;
42import java.util.Arrays;
43import java.util.Collection;
44import java.util.List;
45
46import static android.hardware.camera2.legacy.LegacyExceptionUtils.*;
47import static android.hardware.camera2.utils.CameraBinderDecorator.*;
48import static com.android.internal.util.Preconditions.*;
49
50/**
51 * This class emulates the functionality of a Camera2 device using a the old Camera class.
52 *
53 * <p>
54 * There are two main components that are used to implement this:
55 * - A state machine containing valid Camera2 device states ({@link CameraDeviceState}).
56 * - A message-queue based pipeline that manages an old Camera class, and executes capture and
57 *   configuration requests.
58 * </p>
59 */
60public class LegacyCameraDevice implements AutoCloseable {
61    public static final String DEBUG_PROP = "HAL1ShimLogging";
62    private final String TAG;
63
64    private static final boolean DEBUG = Log.isLoggable(LegacyCameraDevice.DEBUG_PROP, Log.DEBUG);
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    private CaptureResultExtras getExtrasFromRequest(RequestHolder holder) {
81        if (holder == null) {
82            return new CaptureResultExtras(ILLEGAL_VALUE, ILLEGAL_VALUE, ILLEGAL_VALUE,
83                    ILLEGAL_VALUE, ILLEGAL_VALUE, ILLEGAL_VALUE);
84        }
85        return new CaptureResultExtras(holder.getRequestId(), holder.getSubsequeceId(),
86                /*afTriggerId*/0, /*precaptureTriggerId*/0, holder.getFrameNumber(),
87                /*partialResultCount*/1);
88    }
89
90    /**
91     * Listener for the camera device state machine.  Calls the appropriate
92     * {@link ICameraDeviceCallbacks} for each state transition.
93     */
94    private final CameraDeviceState.CameraDeviceStateListener mStateListener =
95            new CameraDeviceState.CameraDeviceStateListener() {
96        @Override
97        public void onError(final int errorCode, final RequestHolder holder) {
98            mIdle.open();
99            final CaptureResultExtras extras = getExtrasFromRequest(holder);
100            mResultHandler.post(new Runnable() {
101                @Override
102                public void run() {
103                    if (DEBUG) {
104                        Log.d(TAG, "doing onError callback for request " + holder.getRequestId() +
105                                ", with error code " + errorCode);
106                    }
107                    try {
108                        mDeviceCallbacks.onDeviceError(errorCode, extras);
109                    } catch (RemoteException e) {
110                        throw new IllegalStateException(
111                                "Received remote exception during onCameraError callback: ", e);
112                    }
113                }
114            });
115        }
116
117        @Override
118        public void onConfiguring() {
119            // Do nothing
120            if (DEBUG) {
121                Log.d(TAG, "doing onConfiguring callback.");
122            }
123        }
124
125        @Override
126        public void onIdle() {
127            mIdle.open();
128
129            mResultHandler.post(new Runnable() {
130                @Override
131                public void run() {
132                    if (DEBUG) {
133                        Log.d(TAG, "doing onIdle callback.");
134                    }
135                    try {
136                        mDeviceCallbacks.onDeviceIdle();
137                    } catch (RemoteException e) {
138                        throw new IllegalStateException(
139                                "Received remote exception during onCameraIdle callback: ", e);
140                    }
141                }
142            });
143        }
144
145        @Override
146        public void onCaptureStarted(final RequestHolder holder, final long timestamp) {
147            final CaptureResultExtras extras = getExtrasFromRequest(holder);
148
149            mResultHandler.post(new Runnable() {
150                @Override
151                public void run() {
152                    if (DEBUG) {
153                        Log.d(TAG, "doing onCaptureStarted callback for request " +
154                                holder.getRequestId());
155                    }
156                    try {
157                        mDeviceCallbacks.onCaptureStarted(extras, timestamp);
158                    } catch (RemoteException e) {
159                        throw new IllegalStateException(
160                                "Received remote exception during onCameraError callback: ", e);
161                    }
162                }
163            });
164        }
165
166        @Override
167        public void onCaptureResult(final CameraMetadataNative result, final RequestHolder holder) {
168            final CaptureResultExtras extras = getExtrasFromRequest(holder);
169
170            mResultHandler.post(new Runnable() {
171                @Override
172                public void run() {
173                    if (DEBUG) {
174                        Log.d(TAG, "doing onCaptureResult callback for request " +
175                                holder.getRequestId());
176                    }
177                    try {
178                        mDeviceCallbacks.onResultReceived(result, extras);
179                    } catch (RemoteException e) {
180                        throw new IllegalStateException(
181                                "Received remote exception during onCameraError callback: ", e);
182                    }
183                }
184            });
185        }
186    };
187
188    private final RequestThreadManager mRequestThreadManager;
189
190    /**
191     * Check if a given surface uses {@link ImageFormat#YUV_420_888} or format that can be readily
192     * converted to this; YV12 and NV21 are the two currently supported formats.
193     *
194     * @param s the surface to check.
195     * @return {@code true} if the surfaces uses {@link ImageFormat#YUV_420_888} or a compatible
196     *          format.
197     */
198    static boolean needsConversion(Surface s) throws BufferQueueAbandonedException {
199        int nativeType = detectSurfaceType(s);
200        return nativeType == ImageFormat.YUV_420_888 || nativeType == ImageFormat.YV12 ||
201                nativeType == ImageFormat.NV21;
202    }
203
204    /**
205     * Create a new emulated camera device from a given Camera 1 API camera.
206     *
207     * <p>
208     * The {@link Camera} provided to this constructor must already have been successfully opened,
209     * and ownership of the provided camera is passed to this object.  No further calls to the
210     * camera methods should be made following this constructor.
211     * </p>
212     *
213     * @param cameraId the id of the camera.
214     * @param camera an open {@link Camera} device.
215     * @param characteristics the static camera characteristics for this camera device
216     * @param callbacks {@link ICameraDeviceCallbacks} callbacks to call for Camera2 API operations.
217     */
218    public LegacyCameraDevice(int cameraId, Camera camera, CameraCharacteristics characteristics,
219            ICameraDeviceCallbacks callbacks) {
220        mCameraId = cameraId;
221        mDeviceCallbacks = callbacks;
222        TAG = String.format("CameraDevice-%d-LE", mCameraId);
223
224        mResultThread.start();
225        mResultHandler = new Handler(mResultThread.getLooper());
226        mCallbackHandlerThread.start();
227        mCallbackHandler = new Handler(mCallbackHandlerThread.getLooper());
228        mDeviceState.setCameraDeviceCallbacks(mCallbackHandler, mStateListener);
229        mStaticCharacteristics = characteristics;
230        mRequestThreadManager =
231                new RequestThreadManager(cameraId, camera, characteristics, mDeviceState);
232        mRequestThreadManager.start();
233    }
234
235    /**
236     * Configure the device with a set of output surfaces.
237     *
238     * <p>Using empty or {@code null} {@code outputs} is the same as unconfiguring.</p>
239     *
240     * <p>Every surface in {@code outputs} must be non-{@code null}.</p>
241     *
242     * @param outputs a list of surfaces to set.
243     * @return an error code for this binder operation, or {@link NO_ERROR}
244     *          on success.
245     */
246    public int configureOutputs(List<Surface> outputs) {
247        if (outputs != null) {
248            for (Surface output : outputs) {
249                if (output == null) {
250                    Log.e(TAG, "configureOutputs - null outputs are not allowed");
251                    return BAD_VALUE;
252                }
253                StreamConfigurationMap streamConfigurations = mStaticCharacteristics.
254                        get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
255
256                // Validate surface size and format.
257                try {
258                    Size s = getSurfaceSize(output);
259                    int surfaceType = detectSurfaceType(output);
260                    Size[] sizes = streamConfigurations.getOutputSizes(surfaceType);
261
262                    if (sizes == null) {
263                        // WAR: Override default format to IMPLEMENTATION_DEFINED for b/9487482
264                        if ((surfaceType >= LegacyMetadataMapper.HAL_PIXEL_FORMAT_RGBA_8888 &&
265                            surfaceType <= LegacyMetadataMapper.HAL_PIXEL_FORMAT_BGRA_8888)) {
266
267                            // YUV_420_888 is always present in LEGACY for all IMPLEMENTATION_DEFINED
268                            // output sizes, and is publicly visible in the API (i.e.
269                            // {@code #getOutputSizes} works here).
270                            sizes = streamConfigurations.getOutputSizes(ImageFormat.YUV_420_888);
271                        } else if (surfaceType == LegacyMetadataMapper.HAL_PIXEL_FORMAT_BLOB) {
272                            sizes = streamConfigurations.getOutputSizes(ImageFormat.JPEG);
273                        }
274                    }
275
276                    if (!ArrayUtils.contains(sizes, s)) {
277                        String reason = (sizes == null) ? "format is invalid." :
278                                ("size not in valid set: " + Arrays.toString(sizes));
279                        Log.e(TAG, String.format("Surface with size (w=%d, h=%d) and format 0x%x is"
280                                + " not valid, %s", s.getWidth(), s.getHeight(), surfaceType,
281                                reason));
282                        return BAD_VALUE;
283                    }
284                } catch (BufferQueueAbandonedException e) {
285                    Log.e(TAG, "Surface bufferqueue is abandoned, cannot configure as output: ", e);
286                    return BAD_VALUE;
287                }
288
289            }
290        }
291
292        boolean success = false;
293        if (mDeviceState.setConfiguring()) {
294            mRequestThreadManager.configure(outputs);
295            success = mDeviceState.setIdle();
296        }
297
298        if (success) {
299            mConfiguredSurfaces = outputs != null ? new ArrayList<>(outputs) : null;
300        } else {
301            return CameraBinderDecorator.INVALID_OPERATION;
302        }
303        return CameraBinderDecorator.NO_ERROR;
304    }
305
306    /**
307     * Submit a burst of capture requests.
308     *
309     * @param requestList a list of capture requests to execute.
310     * @param repeating {@code true} if this burst is repeating.
311     * @param frameNumber an output argument that contains either the frame number of the last frame
312     *                    that will be returned for this request, or the frame number of the last
313     *                    frame that will be returned for the current repeating request if this
314     *                    burst is set to be repeating.
315     * @return the request id.
316     */
317    public int submitRequestList(List<CaptureRequest> requestList, boolean repeating,
318            /*out*/LongParcelable frameNumber) {
319        if (requestList == null || requestList.isEmpty()) {
320            Log.e(TAG, "submitRequestList - Empty/null requests are not allowed");
321            return BAD_VALUE;
322        }
323
324        List<Long> surfaceIds = (mConfiguredSurfaces == null) ? new ArrayList<Long>() :
325                getSurfaceIds(mConfiguredSurfaces);
326
327        // Make sure that there all requests have at least 1 surface; all surfaces are non-null
328        for (CaptureRequest request : requestList) {
329            if (request.getTargets().isEmpty()) {
330                Log.e(TAG, "submitRequestList - "
331                        + "Each request must have at least one Surface target");
332                return BAD_VALUE;
333            }
334
335            for (Surface surface : request.getTargets()) {
336                if (surface == null) {
337                    Log.e(TAG, "submitRequestList - Null Surface targets are not allowed");
338                    return BAD_VALUE;
339                } else if (mConfiguredSurfaces == null) {
340                    Log.e(TAG, "submitRequestList - must configure " +
341                            " device with valid surfaces before submitting requests");
342                    return INVALID_OPERATION;
343                } else if (!containsSurfaceId(surface, surfaceIds)) {
344                    Log.e(TAG, "submitRequestList - cannot use a surface that wasn't configured");
345                    return BAD_VALUE;
346                }
347            }
348        }
349
350        // TODO: further validation of request here
351        mIdle.close();
352        return mRequestThreadManager.submitCaptureRequests(requestList, repeating,
353                frameNumber);
354    }
355
356    /**
357     * Submit a single capture request.
358     *
359     * @param request the capture request to execute.
360     * @param repeating {@code true} if this request is repeating.
361     * @param frameNumber an output argument that contains either the frame number of the last frame
362     *                    that will be returned for this request, or the frame number of the last
363     *                    frame that will be returned for the current repeating request if this
364     *                    request is set to be repeating.
365     * @return the request id.
366     */
367    public int submitRequest(CaptureRequest request, boolean repeating,
368            /*out*/LongParcelable frameNumber) {
369        ArrayList<CaptureRequest> requestList = new ArrayList<CaptureRequest>();
370        requestList.add(request);
371        return submitRequestList(requestList, repeating, frameNumber);
372    }
373
374    /**
375     * Cancel the repeating request with the given request id.
376     *
377     * @param requestId the request id of the request to cancel.
378     * @return the last frame number to be returned from the HAL for the given repeating request, or
379     *          {@code INVALID_FRAME} if none exists.
380     */
381    public long cancelRequest(int requestId) {
382        return mRequestThreadManager.cancelRepeating(requestId);
383    }
384
385    /**
386     * Block until the {@link ICameraDeviceCallbacks#onCameraIdle()} callback is received.
387     */
388    public void waitUntilIdle()  {
389        mIdle.block();
390    }
391
392    /**
393     * Flush any pending requests.
394     *
395     * @return the last frame number.
396     */
397    public long flush() {
398        long lastFrame = mRequestThreadManager.flush();
399        waitUntilIdle();
400        return lastFrame;
401    }
402
403    /**
404     * Return {@code true} if the device has been closed.
405     */
406    public boolean isClosed() {
407        return mClosed;
408    }
409
410    @Override
411    public void close() {
412        mRequestThreadManager.quit();
413        mCallbackHandlerThread.quitSafely();
414        mResultThread.quitSafely();
415
416        try {
417            mCallbackHandlerThread.join();
418        } catch (InterruptedException e) {
419            Log.e(TAG, String.format("Thread %s (%d) interrupted while quitting.",
420                    mCallbackHandlerThread.getName(), mCallbackHandlerThread.getId()));
421        }
422
423        try {
424            mResultThread.join();
425        } catch (InterruptedException e) {
426            Log.e(TAG, String.format("Thread %s (%d) interrupted while quitting.",
427                    mResultThread.getName(), mResultThread.getId()));
428        }
429
430        mClosed = true;
431    }
432
433    @Override
434    protected void finalize() throws Throwable {
435        try {
436            close();
437        } catch (CameraRuntimeException e) {
438            Log.e(TAG, "Got error while trying to finalize, ignoring: " + e.getMessage());
439        } finally {
440            super.finalize();
441        }
442    }
443
444    /**
445     * Query the surface for its currently configured default buffer size.
446     * @param surface a non-{@code null} {@code Surface}
447     * @return the width and height of the surface
448     *
449     * @throws NullPointerException if the {@code surface} was {@code null}
450     * @throws IllegalStateException if the {@code surface} was invalid
451     */
452    static Size getSurfaceSize(Surface surface) throws BufferQueueAbandonedException {
453        checkNotNull(surface);
454
455        int[] dimens = new int[2];
456        LegacyExceptionUtils.throwOnError(nativeDetectSurfaceDimens(surface, /*out*/dimens));
457
458        return new Size(dimens[0], dimens[1]);
459    }
460
461    static int detectSurfaceType(Surface surface) throws BufferQueueAbandonedException {
462        checkNotNull(surface);
463        return LegacyExceptionUtils.throwOnError(nativeDetectSurfaceType(surface));
464    }
465
466    static void configureSurface(Surface surface, int width, int height,
467                                 int pixelFormat) throws BufferQueueAbandonedException {
468        checkNotNull(surface);
469        checkArgumentPositive(width, "width must be positive.");
470        checkArgumentPositive(height, "height must be positive.");
471
472        LegacyExceptionUtils.throwOnError(nativeConfigureSurface(surface, width, height,
473                pixelFormat));
474    }
475
476    static void produceFrame(Surface surface, byte[] pixelBuffer, int width,
477                             int height, int pixelFormat)
478            throws BufferQueueAbandonedException {
479        checkNotNull(surface);
480        checkNotNull(pixelBuffer);
481        checkArgumentPositive(width, "width must be positive.");
482        checkArgumentPositive(height, "height must be positive.");
483
484        LegacyExceptionUtils.throwOnError(nativeProduceFrame(surface, pixelBuffer, width, height,
485                pixelFormat));
486    }
487
488    static void setSurfaceFormat(Surface surface, int pixelFormat)
489            throws BufferQueueAbandonedException {
490        checkNotNull(surface);
491
492        LegacyExceptionUtils.throwOnError(nativeSetSurfaceFormat(surface, pixelFormat));
493    }
494
495    static void setSurfaceDimens(Surface surface, int width, int height)
496            throws BufferQueueAbandonedException {
497        checkNotNull(surface);
498        checkArgumentPositive(width, "width must be positive.");
499        checkArgumentPositive(height, "height must be positive.");
500
501        LegacyExceptionUtils.throwOnError(nativeSetSurfaceDimens(surface, width, height));
502    }
503
504    static long getSurfaceId(Surface surface) {
505        checkNotNull(surface);
506        return nativeGetSurfaceId(surface);
507    }
508
509    static List<Long> getSurfaceIds(Collection<Surface> surfaces) {
510        if (surfaces == null) {
511            throw new NullPointerException("Null argument surfaces");
512        }
513        List<Long> surfaceIds = new ArrayList<>();
514        for (Surface s : surfaces) {
515            long id = getSurfaceId(s);
516            if (id == 0) {
517                throw new IllegalStateException(
518                        "Configured surface had null native GraphicBufferProducer pointer!");
519            }
520            surfaceIds.add(id);
521        }
522        return surfaceIds;
523    }
524
525    static boolean containsSurfaceId(Surface s, Collection<Long> ids) {
526        long id = getSurfaceId(s);
527        return ids.contains(id);
528    }
529
530    static void setSurfaceOrientation(Surface surface, int facing, int sensorOrientation)
531            throws BufferQueueAbandonedException {
532        checkNotNull(surface);
533        LegacyExceptionUtils.throwOnError(nativeSetSurfaceOrientation(surface, facing,
534                sensorOrientation));
535    }
536
537    static Size getTextureSize(SurfaceTexture surfaceTexture)
538            throws BufferQueueAbandonedException {
539        checkNotNull(surfaceTexture);
540
541        int[] dimens = new int[2];
542        LegacyExceptionUtils.throwOnError(nativeDetectTextureDimens(surfaceTexture,
543                /*out*/dimens));
544
545        return new Size(dimens[0], dimens[1]);
546    }
547
548    static void setNextTimestamp(Surface surface, long timestamp)
549            throws BufferQueueAbandonedException {
550        checkNotNull(surface);
551        LegacyExceptionUtils.throwOnError(nativeSetNextTimestamp(surface, timestamp));
552    }
553
554    private static native int nativeDetectSurfaceType(Surface surface);
555
556    private static native int nativeDetectSurfaceDimens(Surface surface,
557            /*out*/int[/*2*/] dimens);
558
559    private static native int nativeConfigureSurface(Surface surface, int width, int height,
560                                                        int pixelFormat);
561
562    private static native int nativeProduceFrame(Surface surface, byte[] pixelBuffer, int width,
563                                                    int height, int pixelFormat);
564
565    private static native int nativeSetSurfaceFormat(Surface surface, int pixelFormat);
566
567    private static native int nativeSetSurfaceDimens(Surface surface, int width, int height);
568
569    private static native long nativeGetSurfaceId(Surface surface);
570
571    private static native int nativeSetSurfaceOrientation(Surface surface, int facing,
572                                                             int sensorOrientation);
573
574    private static native int nativeDetectTextureDimens(SurfaceTexture surfaceTexture,
575            /*out*/int[/*2*/] dimens);
576
577    private static native int nativeSetNextTimestamp(Surface surface, long timestamp);
578
579    static native int nativeGetJpegFooterSize();
580}
581