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