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