blob: 661edd734a7137b1e09f97fd32b15ccfb2145cac [file] [log] [blame]
Ruben Brunkfeb50af2014-05-09 19:58:49 -07001/*
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;
Ruben Brunk28c49c92014-06-16 18:43:59 -070020import android.graphics.SurfaceTexture;
Ruben Brunkfeb50af2014-05-09 19:58:49 -070021import android.hardware.Camera;
Igor Murashkindf6242e2014-07-01 18:06:13 -070022import android.hardware.camera2.CameraCharacteristics;
Ruben Brunkfeb50af2014-05-09 19:58:49 -070023import android.hardware.camera2.CaptureRequest;
Igor Murashkin51dcfd652014-09-25 16:55:01 -070024import android.hardware.camera2.impl.CameraDeviceImpl;
Ruben Brunkfeb50af2014-05-09 19:58:49 -070025import android.hardware.camera2.impl.CaptureResultExtras;
26import android.hardware.camera2.ICameraDeviceCallbacks;
Ruben Brunke663cb772014-09-16 13:18:31 -070027import android.hardware.camera2.params.StreamConfigurationMap;
28import android.hardware.camera2.utils.ArrayUtils;
Eino-Ville Talvala5d2d7782015-12-17 16:50:50 -080029import android.hardware.camera2.utils.SubmitInfo;
Ruben Brunkfeb50af2014-05-09 19:58:49 -070030import android.hardware.camera2.impl.CameraMetadataNative;
Ruben Brunkfeb50af2014-05-09 19:58:49 -070031import android.os.ConditionVariable;
32import android.os.Handler;
33import android.os.HandlerThread;
34import android.os.RemoteException;
Eino-Ville Talvala5d2d7782015-12-17 16:50:50 -080035import android.os.ServiceSpecificException;
Ruben Brunkfeb50af2014-05-09 19:58:49 -070036import android.util.Log;
Ruben Brunkf4a637d2014-11-20 18:01:36 -080037import android.util.Pair;
Igor Murashkina296fec2014-06-23 14:44:09 -070038import android.util.Size;
Ruben Brunkfeb50af2014-05-09 19:58:49 -070039import android.view.Surface;
40
41import java.util.ArrayList;
Ruben Brunke663cb772014-09-16 13:18:31 -070042import java.util.Arrays;
Ruben Brunk3c8fa3b2014-06-30 16:45:05 -070043import java.util.Collection;
Ruben Brunkfeb50af2014-05-09 19:58:49 -070044import java.util.List;
Igor Murashkin49b2b132014-06-18 19:03:00 -070045
Ruben Brunkef14da32014-06-24 16:06:54 -070046import static android.hardware.camera2.legacy.LegacyExceptionUtils.*;
Igor Murashkina296fec2014-06-23 14:44:09 -070047import static com.android.internal.util.Preconditions.*;
Ruben Brunkfeb50af2014-05-09 19:58:49 -070048
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 {
Ruben Brunkfeb50af2014-05-09 19:58:49 -070060 private final String TAG;
61
Eino-Ville Talvalaa78791f2015-06-01 12:39:54 -070062 private static final boolean DEBUG = false;
Ruben Brunkfeb50af2014-05-09 19:58:49 -070063 private final int mCameraId;
Ruben Brunke663cb772014-09-16 13:18:31 -070064 private final CameraCharacteristics mStaticCharacteristics;
Ruben Brunkfeb50af2014-05-09 19:58:49 -070065 private final ICameraDeviceCallbacks mDeviceCallbacks;
66 private final CameraDeviceState mDeviceState = new CameraDeviceState();
Igor Murashkin49b2b132014-06-18 19:03:00 -070067 private List<Surface> mConfiguredSurfaces;
Ruben Brunke663cb772014-09-16 13:18:31 -070068 private boolean mClosed = false;
Ruben Brunkfeb50af2014-05-09 19:58:49 -070069
70 private final ConditionVariable mIdle = new ConditionVariable(/*open*/true);
Ruben Brunkfeb50af2014-05-09 19:58:49 -070071
Ruben Brunkd85e1a62014-06-11 10:35:45 -070072 private final HandlerThread mResultThread = new HandlerThread("ResultThread");
73 private final HandlerThread mCallbackHandlerThread = new HandlerThread("CallbackThread");
Ruben Brunkfeb50af2014-05-09 19:58:49 -070074 private final Handler mCallbackHandler;
Ruben Brunkd85e1a62014-06-11 10:35:45 -070075 private final Handler mResultHandler;
Ruben Brunkfeb50af2014-05-09 19:58:49 -070076 private static final int ILLEGAL_VALUE = -1;
77
Ruben Brunkf4a637d2014-11-20 18:01:36 -080078 // Keep up to date with values in hardware/libhardware/include/hardware/gralloc.h
79 private static final int GRALLOC_USAGE_RENDERSCRIPT = 0x00100000;
80 private static final int GRALLOC_USAGE_SW_READ_OFTEN = 0x00000003;
81 private static final int GRALLOC_USAGE_HW_TEXTURE = 0x00000100;
82 private static final int GRALLOC_USAGE_HW_COMPOSER = 0x00000800;
Zhijun Hea7677722015-06-01 16:36:06 -070083 private static final int GRALLOC_USAGE_HW_RENDER = 0x00000200;
Ruben Brunkf4a637d2014-11-20 18:01:36 -080084 private static final int GRALLOC_USAGE_HW_VIDEO_ENCODER = 0x00010000;
85
Ruben Brunk68e4fc82015-06-12 16:16:42 -070086 public static final int MAX_DIMEN_FOR_ROUNDING = 1920; // maximum allowed width for rounding
Ruben Brunkf4a637d2014-11-20 18:01:36 -080087
Ruben Brunka94c6032015-06-10 16:44:28 -070088 // Keep up to date with values in system/core/include/system/window.h
89 public static final int NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW = 1;
90
Ruben Brunkfeb50af2014-05-09 19:58:49 -070091 private CaptureResultExtras getExtrasFromRequest(RequestHolder holder) {
92 if (holder == null) {
93 return new CaptureResultExtras(ILLEGAL_VALUE, ILLEGAL_VALUE, ILLEGAL_VALUE,
Eino-Ville Talvala3e0023a2016-03-06 18:40:01 -080094 ILLEGAL_VALUE, ILLEGAL_VALUE, ILLEGAL_VALUE, ILLEGAL_VALUE);
Ruben Brunkfeb50af2014-05-09 19:58:49 -070095 }
96 return new CaptureResultExtras(holder.getRequestId(), holder.getSubsequeceId(),
Zhijun He83159152014-07-16 11:32:59 -070097 /*afTriggerId*/0, /*precaptureTriggerId*/0, holder.getFrameNumber(),
Eino-Ville Talvala3e0023a2016-03-06 18:40:01 -080098 /*partialResultCount*/1, /*errorStreamId*/-1);
Ruben Brunkfeb50af2014-05-09 19:58:49 -070099 }
100
101 /**
102 * Listener for the camera device state machine. Calls the appropriate
103 * {@link ICameraDeviceCallbacks} for each state transition.
104 */
105 private final CameraDeviceState.CameraDeviceStateListener mStateListener =
106 new CameraDeviceState.CameraDeviceStateListener() {
107 @Override
Ruben Brunke663cb772014-09-16 13:18:31 -0700108 public void onError(final int errorCode, final RequestHolder holder) {
Igor Murashkin51dcfd652014-09-25 16:55:01 -0700109 if (DEBUG) {
110 Log.d(TAG, "onError called, errorCode = " + errorCode);
111 }
112 switch (errorCode) {
113 /*
114 * Only be considered idle if we hit a fatal error
115 * and no further requests can be processed.
116 */
117 case CameraDeviceImpl.CameraDeviceCallbacks.ERROR_CAMERA_DISCONNECTED:
118 case CameraDeviceImpl.CameraDeviceCallbacks.ERROR_CAMERA_SERVICE:
119 case CameraDeviceImpl.CameraDeviceCallbacks.ERROR_CAMERA_DEVICE: {
120 mIdle.open();
121
122 if (DEBUG) {
123 Log.d(TAG, "onError - opening idle");
124 }
125 }
126 }
127
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700128 final CaptureResultExtras extras = getExtrasFromRequest(holder);
Ruben Brunkd85e1a62014-06-11 10:35:45 -0700129 mResultHandler.post(new Runnable() {
130 @Override
131 public void run() {
Ruben Brunk3e4fed22014-06-18 17:08:42 -0700132 if (DEBUG) {
Ruben Brunke663cb772014-09-16 13:18:31 -0700133 Log.d(TAG, "doing onError callback for request " + holder.getRequestId() +
134 ", with error code " + errorCode);
Ruben Brunk3e4fed22014-06-18 17:08:42 -0700135 }
Ruben Brunkd85e1a62014-06-11 10:35:45 -0700136 try {
Eino-Ville Talvalaacc00952014-08-06 14:31:08 -0700137 mDeviceCallbacks.onDeviceError(errorCode, extras);
Ruben Brunkd85e1a62014-06-11 10:35:45 -0700138 } catch (RemoteException e) {
139 throw new IllegalStateException(
140 "Received remote exception during onCameraError callback: ", e);
141 }
142 }
143 });
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700144 }
145
146 @Override
147 public void onConfiguring() {
148 // Do nothing
Ruben Brunk3e4fed22014-06-18 17:08:42 -0700149 if (DEBUG) {
150 Log.d(TAG, "doing onConfiguring callback.");
151 }
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700152 }
153
154 @Override
155 public void onIdle() {
Igor Murashkin51dcfd652014-09-25 16:55:01 -0700156 if (DEBUG) {
157 Log.d(TAG, "onIdle called");
158 }
159
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700160 mIdle.open();
161
Ruben Brunkd85e1a62014-06-11 10:35:45 -0700162 mResultHandler.post(new Runnable() {
163 @Override
164 public void run() {
Ruben Brunk3e4fed22014-06-18 17:08:42 -0700165 if (DEBUG) {
166 Log.d(TAG, "doing onIdle callback.");
167 }
Ruben Brunkd85e1a62014-06-11 10:35:45 -0700168 try {
Eino-Ville Talvalaacc00952014-08-06 14:31:08 -0700169 mDeviceCallbacks.onDeviceIdle();
Ruben Brunkd85e1a62014-06-11 10:35:45 -0700170 } catch (RemoteException e) {
171 throw new IllegalStateException(
172 "Received remote exception during onCameraIdle callback: ", e);
173 }
174 }
175 });
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700176 }
177
178 @Override
Igor Murashkin51dcfd652014-09-25 16:55:01 -0700179 public void onBusy() {
180 mIdle.close();
181
182 if (DEBUG) {
183 Log.d(TAG, "onBusy called");
184 }
185 }
186
187 @Override
Ruben Brunke663cb772014-09-16 13:18:31 -0700188 public void onCaptureStarted(final RequestHolder holder, final long timestamp) {
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700189 final CaptureResultExtras extras = getExtrasFromRequest(holder);
190
Ruben Brunkd85e1a62014-06-11 10:35:45 -0700191 mResultHandler.post(new Runnable() {
192 @Override
193 public void run() {
Ruben Brunk3e4fed22014-06-18 17:08:42 -0700194 if (DEBUG) {
Ruben Brunke663cb772014-09-16 13:18:31 -0700195 Log.d(TAG, "doing onCaptureStarted callback for request " +
196 holder.getRequestId());
Ruben Brunk3e4fed22014-06-18 17:08:42 -0700197 }
Ruben Brunkd85e1a62014-06-11 10:35:45 -0700198 try {
Ruben Brunkd85e1a62014-06-11 10:35:45 -0700199 mDeviceCallbacks.onCaptureStarted(extras, timestamp);
200 } catch (RemoteException e) {
201 throw new IllegalStateException(
202 "Received remote exception during onCameraError callback: ", e);
203 }
204 }
205 });
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700206 }
207
208 @Override
Ruben Brunke663cb772014-09-16 13:18:31 -0700209 public void onCaptureResult(final CameraMetadataNative result, final RequestHolder holder) {
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700210 final CaptureResultExtras extras = getExtrasFromRequest(holder);
211
Ruben Brunkd85e1a62014-06-11 10:35:45 -0700212 mResultHandler.post(new Runnable() {
213 @Override
214 public void run() {
Ruben Brunk3e4fed22014-06-18 17:08:42 -0700215 if (DEBUG) {
Ruben Brunke663cb772014-09-16 13:18:31 -0700216 Log.d(TAG, "doing onCaptureResult callback for request " +
217 holder.getRequestId());
Ruben Brunk3e4fed22014-06-18 17:08:42 -0700218 }
Ruben Brunkd85e1a62014-06-11 10:35:45 -0700219 try {
Ruben Brunkd85e1a62014-06-11 10:35:45 -0700220 mDeviceCallbacks.onResultReceived(result, extras);
221 } catch (RemoteException e) {
222 throw new IllegalStateException(
223 "Received remote exception during onCameraError callback: ", e);
224 }
225 }
226 });
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700227 }
228 };
229
230 private final RequestThreadManager mRequestThreadManager;
231
232 /**
Ruben Brunk91b9aab2014-06-20 00:24:56 -0700233 * Check if a given surface uses {@link ImageFormat#YUV_420_888} or format that can be readily
234 * converted to this; YV12 and NV21 are the two currently supported formats.
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700235 *
236 * @param s the surface to check.
Ruben Brunk91b9aab2014-06-20 00:24:56 -0700237 * @return {@code true} if the surfaces uses {@link ImageFormat#YUV_420_888} or a compatible
238 * format.
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700239 */
Ruben Brunkef14da32014-06-24 16:06:54 -0700240 static boolean needsConversion(Surface s) throws BufferQueueAbandonedException {
241 int nativeType = detectSurfaceType(s);
Ruben Brunk91b9aab2014-06-20 00:24:56 -0700242 return nativeType == ImageFormat.YUV_420_888 || nativeType == ImageFormat.YV12 ||
243 nativeType == ImageFormat.NV21;
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700244 }
245
246 /**
247 * Create a new emulated camera device from a given Camera 1 API camera.
248 *
249 * <p>
250 * The {@link Camera} provided to this constructor must already have been successfully opened,
251 * and ownership of the provided camera is passed to this object. No further calls to the
252 * camera methods should be made following this constructor.
253 * </p>
254 *
255 * @param cameraId the id of the camera.
256 * @param camera an open {@link Camera} device.
Igor Murashkindf6242e2014-07-01 18:06:13 -0700257 * @param characteristics the static camera characteristics for this camera device
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700258 * @param callbacks {@link ICameraDeviceCallbacks} callbacks to call for Camera2 API operations.
259 */
Igor Murashkindf6242e2014-07-01 18:06:13 -0700260 public LegacyCameraDevice(int cameraId, Camera camera, CameraCharacteristics characteristics,
261 ICameraDeviceCallbacks callbacks) {
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700262 mCameraId = cameraId;
263 mDeviceCallbacks = callbacks;
264 TAG = String.format("CameraDevice-%d-LE", mCameraId);
265
Ruben Brunkd85e1a62014-06-11 10:35:45 -0700266 mResultThread.start();
267 mResultHandler = new Handler(mResultThread.getLooper());
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700268 mCallbackHandlerThread.start();
269 mCallbackHandler = new Handler(mCallbackHandlerThread.getLooper());
270 mDeviceState.setCameraDeviceCallbacks(mCallbackHandler, mStateListener);
Ruben Brunke663cb772014-09-16 13:18:31 -0700271 mStaticCharacteristics = characteristics;
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700272 mRequestThreadManager =
Igor Murashkindf6242e2014-07-01 18:06:13 -0700273 new RequestThreadManager(cameraId, camera, characteristics, mDeviceState);
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700274 mRequestThreadManager.start();
275 }
276
277 /**
278 * Configure the device with a set of output surfaces.
279 *
Igor Murashkin49b2b132014-06-18 19:03:00 -0700280 * <p>Using empty or {@code null} {@code outputs} is the same as unconfiguring.</p>
281 *
282 * <p>Every surface in {@code outputs} must be non-{@code null}.</p>
283 *
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700284 * @param outputs a list of surfaces to set.
Igor Murashkin49b2b132014-06-18 19:03:00 -0700285 * @return an error code for this binder operation, or {@link NO_ERROR}
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700286 * on success.
287 */
288 public int configureOutputs(List<Surface> outputs) {
Ruben Brunkf4a637d2014-11-20 18:01:36 -0800289 List<Pair<Surface, Size>> sizedSurfaces = new ArrayList<>();
Igor Murashkin49b2b132014-06-18 19:03:00 -0700290 if (outputs != null) {
291 for (Surface output : outputs) {
292 if (output == null) {
293 Log.e(TAG, "configureOutputs - null outputs are not allowed");
294 return BAD_VALUE;
295 }
Ruben Brunk443ab2c2015-03-12 20:54:03 -0700296 if (!output.isValid()) {
297 Log.e(TAG, "configureOutputs - invalid output surfaces are not allowed");
298 return BAD_VALUE;
299 }
Ruben Brunke663cb772014-09-16 13:18:31 -0700300 StreamConfigurationMap streamConfigurations = mStaticCharacteristics.
301 get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
302
303 // Validate surface size and format.
304 try {
305 Size s = getSurfaceSize(output);
306 int surfaceType = detectSurfaceType(output);
Ruben Brunke663cb772014-09-16 13:18:31 -0700307
Eino-Ville Talvalafa0b9a02015-01-20 12:30:59 -0800308 boolean flexibleConsumer = isFlexibleConsumer(output);
Ruben Brunkf4a637d2014-11-20 18:01:36 -0800309
310 Size[] sizes = streamConfigurations.getOutputSizes(surfaceType);
Ruben Brunke663cb772014-09-16 13:18:31 -0700311 if (sizes == null) {
312 // WAR: Override default format to IMPLEMENTATION_DEFINED for b/9487482
313 if ((surfaceType >= LegacyMetadataMapper.HAL_PIXEL_FORMAT_RGBA_8888 &&
314 surfaceType <= LegacyMetadataMapper.HAL_PIXEL_FORMAT_BGRA_8888)) {
315
Ruben Brunkf4a637d2014-11-20 18:01:36 -0800316 // YUV_420_888 is always present in LEGACY for all
317 // IMPLEMENTATION_DEFINED output sizes, and is publicly visible in the
318 // API (i.e. {@code #getOutputSizes} works here).
Ruben Brunke663cb772014-09-16 13:18:31 -0700319 sizes = streamConfigurations.getOutputSizes(ImageFormat.YUV_420_888);
320 } else if (surfaceType == LegacyMetadataMapper.HAL_PIXEL_FORMAT_BLOB) {
321 sizes = streamConfigurations.getOutputSizes(ImageFormat.JPEG);
322 }
323 }
324
325 if (!ArrayUtils.contains(sizes, s)) {
Ruben Brunkf4a637d2014-11-20 18:01:36 -0800326 if (flexibleConsumer && (s = findClosestSize(s, sizes)) != null) {
327 sizedSurfaces.add(new Pair<>(output, s));
328 } else {
329 String reason = (sizes == null) ? "format is invalid." :
330 ("size not in valid set: " + Arrays.toString(sizes));
331 Log.e(TAG, String.format("Surface with size (w=%d, h=%d) and format " +
332 "0x%x is not valid, %s", s.getWidth(), s.getHeight(),
333 surfaceType, reason));
334 return BAD_VALUE;
335 }
336 } else {
337 sizedSurfaces.add(new Pair<>(output, s));
Ruben Brunke663cb772014-09-16 13:18:31 -0700338 }
Eino-Ville Talvala315bc092015-08-21 16:30:27 -0700339 // Lock down the size before configuration
340 setSurfaceDimens(output, s.getWidth(), s.getHeight());
Ruben Brunke663cb772014-09-16 13:18:31 -0700341 } catch (BufferQueueAbandonedException e) {
342 Log.e(TAG, "Surface bufferqueue is abandoned, cannot configure as output: ", e);
343 return BAD_VALUE;
344 }
345
Igor Murashkin49b2b132014-06-18 19:03:00 -0700346 }
347 }
348
Ruben Brunk4aed87a2014-09-21 18:35:31 -0700349 boolean success = false;
350 if (mDeviceState.setConfiguring()) {
Ruben Brunkf4a637d2014-11-20 18:01:36 -0800351 mRequestThreadManager.configure(sizedSurfaces);
Ruben Brunk4aed87a2014-09-21 18:35:31 -0700352 success = mDeviceState.setIdle();
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700353 }
Igor Murashkin49b2b132014-06-18 19:03:00 -0700354
Ruben Brunk4aed87a2014-09-21 18:35:31 -0700355 if (success) {
Igor Murashkin49b2b132014-06-18 19:03:00 -0700356 mConfiguredSurfaces = outputs != null ? new ArrayList<>(outputs) : null;
Ruben Brunk4aed87a2014-09-21 18:35:31 -0700357 } else {
Eino-Ville Talvala5d2d7782015-12-17 16:50:50 -0800358 return LegacyExceptionUtils.INVALID_OPERATION;
Igor Murashkin49b2b132014-06-18 19:03:00 -0700359 }
Eino-Ville Talvala5d2d7782015-12-17 16:50:50 -0800360 return LegacyExceptionUtils.NO_ERROR;
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700361 }
362
363 /**
364 * Submit a burst of capture requests.
365 *
366 * @param requestList a list of capture requests to execute.
367 * @param repeating {@code true} if this burst is repeating.
Eino-Ville Talvala5d2d7782015-12-17 16:50:50 -0800368 * @return the submission info, including the new request id, and the last frame number, which
369 * contains either the frame number of the last frame that will be returned for this request,
370 * or the frame number of the last frame that will be returned for the current repeating
371 * request if this burst is set to be repeating.
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700372 */
Eino-Ville Talvala5d2d7782015-12-17 16:50:50 -0800373 public SubmitInfo submitRequestList(CaptureRequest[] requestList, boolean repeating) {
374 if (requestList == null || requestList.length == 0) {
Igor Murashkin49b2b132014-06-18 19:03:00 -0700375 Log.e(TAG, "submitRequestList - Empty/null requests are not allowed");
Eino-Ville Talvala5d2d7782015-12-17 16:50:50 -0800376 throw new ServiceSpecificException(BAD_VALUE,
377 "submitRequestList - Empty/null requests are not allowed");
Igor Murashkin49b2b132014-06-18 19:03:00 -0700378 }
379
Ruben Brunk3c8fa3b2014-06-30 16:45:05 -0700380 List<Long> surfaceIds = (mConfiguredSurfaces == null) ? new ArrayList<Long>() :
381 getSurfaceIds(mConfiguredSurfaces);
382
Igor Murashkin49b2b132014-06-18 19:03:00 -0700383 // Make sure that there all requests have at least 1 surface; all surfaces are non-null
384 for (CaptureRequest request : requestList) {
385 if (request.getTargets().isEmpty()) {
386 Log.e(TAG, "submitRequestList - "
387 + "Each request must have at least one Surface target");
Eino-Ville Talvala5d2d7782015-12-17 16:50:50 -0800388 throw new ServiceSpecificException(BAD_VALUE,
389 "submitRequestList - "
390 + "Each request must have at least one Surface target");
Igor Murashkin49b2b132014-06-18 19:03:00 -0700391 }
392
393 for (Surface surface : request.getTargets()) {
394 if (surface == null) {
395 Log.e(TAG, "submitRequestList - Null Surface targets are not allowed");
Eino-Ville Talvala5d2d7782015-12-17 16:50:50 -0800396 throw new ServiceSpecificException(BAD_VALUE,
397 "submitRequestList - Null Surface targets are not allowed");
Igor Murashkin49b2b132014-06-18 19:03:00 -0700398 } else if (mConfiguredSurfaces == null) {
399 Log.e(TAG, "submitRequestList - must configure " +
400 " device with valid surfaces before submitting requests");
Eino-Ville Talvala5d2d7782015-12-17 16:50:50 -0800401 throw new ServiceSpecificException(INVALID_OPERATION,
402 "submitRequestList - must configure " +
403 " device with valid surfaces before submitting requests");
Ruben Brunk3c8fa3b2014-06-30 16:45:05 -0700404 } else if (!containsSurfaceId(surface, surfaceIds)) {
Igor Murashkin49b2b132014-06-18 19:03:00 -0700405 Log.e(TAG, "submitRequestList - cannot use a surface that wasn't configured");
Eino-Ville Talvala5d2d7782015-12-17 16:50:50 -0800406 throw new ServiceSpecificException(BAD_VALUE,
407 "submitRequestList - cannot use a surface that wasn't configured");
Igor Murashkin49b2b132014-06-18 19:03:00 -0700408 }
409 }
410 }
411
412 // TODO: further validation of request here
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700413 mIdle.close();
Eino-Ville Talvala5d2d7782015-12-17 16:50:50 -0800414 return mRequestThreadManager.submitCaptureRequests(requestList, repeating);
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700415 }
416
417 /**
418 * Submit a single capture request.
419 *
420 * @param request the capture request to execute.
421 * @param repeating {@code true} if this request is repeating.
Eino-Ville Talvala5d2d7782015-12-17 16:50:50 -0800422 * @return the submission info, including the new request id, and the last frame number, which
423 * contains either the frame number of the last frame that will be returned for this request,
424 * or the frame number of the last frame that will be returned for the current repeating
425 * request if this burst is set to be repeating.
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700426 */
Eino-Ville Talvala5d2d7782015-12-17 16:50:50 -0800427 public SubmitInfo submitRequest(CaptureRequest request, boolean repeating) {
428 CaptureRequest[] requestList = { request };
429 return submitRequestList(requestList, repeating);
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700430 }
431
432 /**
433 * Cancel the repeating request with the given request id.
434 *
435 * @param requestId the request id of the request to cancel.
436 * @return the last frame number to be returned from the HAL for the given repeating request, or
437 * {@code INVALID_FRAME} if none exists.
438 */
439 public long cancelRequest(int requestId) {
440 return mRequestThreadManager.cancelRepeating(requestId);
441 }
442
443 /**
444 * Block until the {@link ICameraDeviceCallbacks#onCameraIdle()} callback is received.
445 */
446 public void waitUntilIdle() {
447 mIdle.block();
448 }
449
Ruben Brunke663cb772014-09-16 13:18:31 -0700450 /**
451 * Flush any pending requests.
452 *
453 * @return the last frame number.
454 */
455 public long flush() {
456 long lastFrame = mRequestThreadManager.flush();
457 waitUntilIdle();
458 return lastFrame;
459 }
460
461 /**
462 * Return {@code true} if the device has been closed.
463 */
464 public boolean isClosed() {
465 return mClosed;
466 }
467
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700468 @Override
469 public void close() {
470 mRequestThreadManager.quit();
471 mCallbackHandlerThread.quitSafely();
Ruben Brunkd85e1a62014-06-11 10:35:45 -0700472 mResultThread.quitSafely();
473
474 try {
475 mCallbackHandlerThread.join();
476 } catch (InterruptedException e) {
477 Log.e(TAG, String.format("Thread %s (%d) interrupted while quitting.",
478 mCallbackHandlerThread.getName(), mCallbackHandlerThread.getId()));
479 }
480
481 try {
482 mResultThread.join();
483 } catch (InterruptedException e) {
484 Log.e(TAG, String.format("Thread %s (%d) interrupted while quitting.",
485 mResultThread.getName(), mResultThread.getId()));
486 }
487
Ruben Brunke663cb772014-09-16 13:18:31 -0700488 mClosed = true;
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700489 }
490
491 @Override
492 protected void finalize() throws Throwable {
493 try {
494 close();
Eino-Ville Talvala5d2d7782015-12-17 16:50:50 -0800495 } catch (ServiceSpecificException e) {
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700496 Log.e(TAG, "Got error while trying to finalize, ignoring: " + e.getMessage());
497 } finally {
498 super.finalize();
499 }
500 }
501
Ruben Brunkf4a637d2014-11-20 18:01:36 -0800502 static long findEuclidDistSquare(Size a, Size b) {
503 long d0 = a.getWidth() - b.getWidth();
504 long d1 = a.getHeight() - b.getHeight();
505 return d0 * d0 + d1 * d1;
506 }
507
508 // Keep up to date with rounding behavior in
509 // frameworks/av/services/camera/libcameraservice/api2/CameraDeviceClient.cpp
510 static Size findClosestSize(Size size, Size[] supportedSizes) {
511 if (size == null || supportedSizes == null) {
512 return null;
513 }
514 Size bestSize = null;
515 for (Size s : supportedSizes) {
516 if (s.equals(size)) {
517 return size;
518 } else if (s.getWidth() <= MAX_DIMEN_FOR_ROUNDING && (bestSize == null ||
519 LegacyCameraDevice.findEuclidDistSquare(size, s) <
520 LegacyCameraDevice.findEuclidDistSquare(bestSize, s))) {
521 bestSize = s;
522 }
523 }
524 return bestSize;
525 }
526
Igor Murashkina296fec2014-06-23 14:44:09 -0700527 /**
528 * Query the surface for its currently configured default buffer size.
529 * @param surface a non-{@code null} {@code Surface}
530 * @return the width and height of the surface
531 *
532 * @throws NullPointerException if the {@code surface} was {@code null}
Ruben Brunk443ab2c2015-03-12 20:54:03 -0700533 * @throws BufferQueueAbandonedException if the {@code surface} was invalid
Igor Murashkina296fec2014-06-23 14:44:09 -0700534 */
Eino-Ville Talvalafa0b9a02015-01-20 12:30:59 -0800535 public static Size getSurfaceSize(Surface surface) throws BufferQueueAbandonedException {
Igor Murashkina296fec2014-06-23 14:44:09 -0700536 checkNotNull(surface);
537
538 int[] dimens = new int[2];
Ruben Brunkef14da32014-06-24 16:06:54 -0700539 LegacyExceptionUtils.throwOnError(nativeDetectSurfaceDimens(surface, /*out*/dimens));
Igor Murashkina296fec2014-06-23 14:44:09 -0700540
541 return new Size(dimens[0], dimens[1]);
542 }
543
Eino-Ville Talvalafa0b9a02015-01-20 12:30:59 -0800544 public static boolean isFlexibleConsumer(Surface output) {
545 int usageFlags = detectSurfaceUsageFlags(output);
546
547 // Keep up to date with allowed consumer types in
548 // frameworks/av/services/camera/libcameraservice/api2/CameraDeviceClient.cpp
549 int disallowedFlags = GRALLOC_USAGE_HW_VIDEO_ENCODER | GRALLOC_USAGE_RENDERSCRIPT;
550 int allowedFlags = GRALLOC_USAGE_HW_TEXTURE | GRALLOC_USAGE_SW_READ_OFTEN |
551 GRALLOC_USAGE_HW_COMPOSER;
552 boolean flexibleConsumer = ((usageFlags & disallowedFlags) == 0 &&
553 (usageFlags & allowedFlags) != 0);
554 return flexibleConsumer;
555 }
556
Zhijun Hea7677722015-06-01 16:36:06 -0700557 public static boolean isPreviewConsumer(Surface output) {
558 int usageFlags = detectSurfaceUsageFlags(output);
559 int disallowedFlags = GRALLOC_USAGE_HW_VIDEO_ENCODER | GRALLOC_USAGE_RENDERSCRIPT |
560 GRALLOC_USAGE_SW_READ_OFTEN;
561 int allowedFlags = GRALLOC_USAGE_HW_TEXTURE | GRALLOC_USAGE_HW_COMPOSER |
562 GRALLOC_USAGE_HW_RENDER;
563 boolean previewConsumer = ((usageFlags & disallowedFlags) == 0 &&
564 (usageFlags & allowedFlags) != 0);
565 int surfaceFormat = ImageFormat.UNKNOWN;
566 try {
567 surfaceFormat = detectSurfaceType(output);
568 } catch(BufferQueueAbandonedException e) {
569 throw new IllegalArgumentException("Surface was abandoned", e);
570 }
571
Zhijun He47ac3492015-06-10 14:41:57 -0700572 return previewConsumer;
Zhijun Hea7677722015-06-01 16:36:06 -0700573 }
574
575 public static boolean isVideoEncoderConsumer(Surface output) {
576 int usageFlags = detectSurfaceUsageFlags(output);
577 int disallowedFlags = GRALLOC_USAGE_HW_TEXTURE | GRALLOC_USAGE_HW_COMPOSER |
578 GRALLOC_USAGE_RENDERSCRIPT | GRALLOC_USAGE_SW_READ_OFTEN;
579 int allowedFlags = GRALLOC_USAGE_HW_VIDEO_ENCODER;
580 boolean videoEncoderConsumer = ((usageFlags & disallowedFlags) == 0 &&
581 (usageFlags & allowedFlags) != 0);
582
583 int surfaceFormat = ImageFormat.UNKNOWN;
584 try {
585 surfaceFormat = detectSurfaceType(output);
586 } catch(BufferQueueAbandonedException e) {
587 throw new IllegalArgumentException("Surface was abandoned", e);
588 }
589
Zhijun He47ac3492015-06-10 14:41:57 -0700590 return videoEncoderConsumer;
Zhijun Hea7677722015-06-01 16:36:06 -0700591 }
592
Eino-Ville Talvalafa0b9a02015-01-20 12:30:59 -0800593 /**
594 * Query the surface for its currently configured usage flags
595 */
Ruben Brunkf4a637d2014-11-20 18:01:36 -0800596 static int detectSurfaceUsageFlags(Surface surface) {
597 checkNotNull(surface);
598 return nativeDetectSurfaceUsageFlags(surface);
599 }
600
Eino-Ville Talvalafa0b9a02015-01-20 12:30:59 -0800601 /**
602 * Query the surface for its currently configured format
603 */
604 public static int detectSurfaceType(Surface surface) throws BufferQueueAbandonedException {
Ruben Brunkef14da32014-06-24 16:06:54 -0700605 checkNotNull(surface);
606 return LegacyExceptionUtils.throwOnError(nativeDetectSurfaceType(surface));
607 }
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700608
Eino-Ville Talvalae3651202015-06-19 17:29:14 -0700609 /**
610 * Query the surface for its currently configured dataspace
611 */
612 public static int detectSurfaceDataspace(Surface surface) throws BufferQueueAbandonedException {
613 checkNotNull(surface);
614 return LegacyExceptionUtils.throwOnError(nativeDetectSurfaceDataspace(surface));
615 }
616
Chien-Yu Chen29c36302016-03-02 15:34:00 -0800617 static void connectSurface(Surface surface) throws BufferQueueAbandonedException {
Ruben Brunkef14da32014-06-24 16:06:54 -0700618 checkNotNull(surface);
Ruben Brunkef14da32014-06-24 16:06:54 -0700619
Chien-Yu Chen29c36302016-03-02 15:34:00 -0800620 LegacyExceptionUtils.throwOnError(nativeConnectSurface(surface));
621 }
622
623 static void disconnectSurface(Surface surface) throws BufferQueueAbandonedException {
624 if (surface == null) return;
625
626 LegacyExceptionUtils.throwOnError(nativeDisconnectSurface(surface));
Ruben Brunkef14da32014-06-24 16:06:54 -0700627 }
628
629 static void produceFrame(Surface surface, byte[] pixelBuffer, int width,
630 int height, int pixelFormat)
631 throws BufferQueueAbandonedException {
632 checkNotNull(surface);
633 checkNotNull(pixelBuffer);
634 checkArgumentPositive(width, "width must be positive.");
635 checkArgumentPositive(height, "height must be positive.");
636
637 LegacyExceptionUtils.throwOnError(nativeProduceFrame(surface, pixelBuffer, width, height,
638 pixelFormat));
639 }
640
641 static void setSurfaceFormat(Surface surface, int pixelFormat)
642 throws BufferQueueAbandonedException {
643 checkNotNull(surface);
644
645 LegacyExceptionUtils.throwOnError(nativeSetSurfaceFormat(surface, pixelFormat));
646 }
647
648 static void setSurfaceDimens(Surface surface, int width, int height)
649 throws BufferQueueAbandonedException {
650 checkNotNull(surface);
651 checkArgumentPositive(width, "width must be positive.");
652 checkArgumentPositive(height, "height must be positive.");
653
654 LegacyExceptionUtils.throwOnError(nativeSetSurfaceDimens(surface, width, height));
655 }
656
Ruben Brunk3c8fa3b2014-06-30 16:45:05 -0700657 static long getSurfaceId(Surface surface) {
658 checkNotNull(surface);
659 return nativeGetSurfaceId(surface);
660 }
661
662 static List<Long> getSurfaceIds(Collection<Surface> surfaces) {
663 if (surfaces == null) {
664 throw new NullPointerException("Null argument surfaces");
665 }
666 List<Long> surfaceIds = new ArrayList<>();
667 for (Surface s : surfaces) {
668 long id = getSurfaceId(s);
669 if (id == 0) {
670 throw new IllegalStateException(
671 "Configured surface had null native GraphicBufferProducer pointer!");
672 }
673 surfaceIds.add(id);
674 }
675 return surfaceIds;
676 }
677
Ruben Brunk0fd198a2014-09-23 23:35:43 -0700678 static boolean containsSurfaceId(Surface s, Collection<Long> ids) {
Ruben Brunk3c8fa3b2014-06-30 16:45:05 -0700679 long id = getSurfaceId(s);
680 return ids.contains(id);
681 }
682
Ruben Brunk28c49c92014-06-16 18:43:59 -0700683 static void setSurfaceOrientation(Surface surface, int facing, int sensorOrientation)
684 throws BufferQueueAbandonedException {
685 checkNotNull(surface);
686 LegacyExceptionUtils.throwOnError(nativeSetSurfaceOrientation(surface, facing,
687 sensorOrientation));
688 }
689
690 static Size getTextureSize(SurfaceTexture surfaceTexture)
691 throws BufferQueueAbandonedException {
692 checkNotNull(surfaceTexture);
693
694 int[] dimens = new int[2];
695 LegacyExceptionUtils.throwOnError(nativeDetectTextureDimens(surfaceTexture,
696 /*out*/dimens));
697
698 return new Size(dimens[0], dimens[1]);
699 }
700
Ruben Brunk91838de2014-07-16 17:24:17 -0700701 static void setNextTimestamp(Surface surface, long timestamp)
702 throws BufferQueueAbandonedException {
703 checkNotNull(surface);
704 LegacyExceptionUtils.throwOnError(nativeSetNextTimestamp(surface, timestamp));
705 }
706
Ruben Brunka94c6032015-06-10 16:44:28 -0700707 static void setScalingMode(Surface surface, int mode)
708 throws BufferQueueAbandonedException {
709 checkNotNull(surface);
710 LegacyExceptionUtils.throwOnError(nativeSetScalingMode(surface, mode));
711 }
712
713
Ruben Brunkef14da32014-06-24 16:06:54 -0700714 private static native int nativeDetectSurfaceType(Surface surface);
715
Eino-Ville Talvalae3651202015-06-19 17:29:14 -0700716 private static native int nativeDetectSurfaceDataspace(Surface surface);
717
Ruben Brunkef14da32014-06-24 16:06:54 -0700718 private static native int nativeDetectSurfaceDimens(Surface surface,
Igor Murashkina296fec2014-06-23 14:44:09 -0700719 /*out*/int[/*2*/] dimens);
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700720
Chien-Yu Chen29c36302016-03-02 15:34:00 -0800721 private static native int nativeConnectSurface(Surface surface);
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700722
Ruben Brunkef14da32014-06-24 16:06:54 -0700723 private static native int nativeProduceFrame(Surface surface, byte[] pixelBuffer, int width,
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700724 int height, int pixelFormat);
725
Ruben Brunkef14da32014-06-24 16:06:54 -0700726 private static native int nativeSetSurfaceFormat(Surface surface, int pixelFormat);
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700727
Ruben Brunkef14da32014-06-24 16:06:54 -0700728 private static native int nativeSetSurfaceDimens(Surface surface, int width, int height);
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700729
Ruben Brunk3c8fa3b2014-06-30 16:45:05 -0700730 private static native long nativeGetSurfaceId(Surface surface);
Ruben Brunk28c49c92014-06-16 18:43:59 -0700731
732 private static native int nativeSetSurfaceOrientation(Surface surface, int facing,
733 int sensorOrientation);
734
735 private static native int nativeDetectTextureDimens(SurfaceTexture surfaceTexture,
736 /*out*/int[/*2*/] dimens);
737
Ruben Brunk91838de2014-07-16 17:24:17 -0700738 private static native int nativeSetNextTimestamp(Surface surface, long timestamp);
Ruben Brunk1dc13262014-07-31 11:43:27 -0700739
Ruben Brunkf4a637d2014-11-20 18:01:36 -0800740 private static native int nativeDetectSurfaceUsageFlags(Surface surface);
741
Ruben Brunka94c6032015-06-10 16:44:28 -0700742 private static native int nativeSetScalingMode(Surface surface, int scalingMode);
743
Chien-Yu Chen29c36302016-03-02 15:34:00 -0800744 private static native int nativeDisconnectSurface(Surface surface);
745
Ruben Brunk1dc13262014-07-31 11:43:27 -0700746 static native int nativeGetJpegFooterSize();
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700747}