blob: 3a976ba8b862ae85c337ed8f20c4f92e18aa5b50 [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.StreamConfiguration;
28import android.hardware.camera2.params.StreamConfigurationMap;
29import android.hardware.camera2.utils.ArrayUtils;
Ruben Brunk4aed87a2014-09-21 18:35:31 -070030import android.hardware.camera2.utils.CameraBinderDecorator;
Ruben Brunkfeb50af2014-05-09 19:58:49 -070031import android.hardware.camera2.utils.LongParcelable;
32import android.hardware.camera2.impl.CameraMetadataNative;
Ruben Brunkfeb50af2014-05-09 19:58:49 -070033import android.hardware.camera2.utils.CameraRuntimeException;
34import android.os.ConditionVariable;
35import android.os.Handler;
36import android.os.HandlerThread;
37import android.os.RemoteException;
38import android.util.Log;
Igor Murashkina296fec2014-06-23 14:44:09 -070039import android.util.Size;
Ruben Brunkfeb50af2014-05-09 19:58:49 -070040import android.view.Surface;
41
42import java.util.ArrayList;
Ruben Brunke663cb772014-09-16 13:18:31 -070043import java.util.Arrays;
Ruben Brunk3c8fa3b2014-06-30 16:45:05 -070044import java.util.Collection;
Ruben Brunkfeb50af2014-05-09 19:58:49 -070045import java.util.List;
Igor Murashkin49b2b132014-06-18 19:03:00 -070046
Ruben Brunkef14da32014-06-24 16:06:54 -070047import static android.hardware.camera2.legacy.LegacyExceptionUtils.*;
Igor Murashkin49b2b132014-06-18 19:03:00 -070048import static android.hardware.camera2.utils.CameraBinderDecorator.*;
Igor Murashkina296fec2014-06-23 14:44:09 -070049import static com.android.internal.util.Preconditions.*;
Ruben Brunkfeb50af2014-05-09 19:58:49 -070050
51/**
52 * This class emulates the functionality of a Camera2 device using a the old Camera class.
53 *
54 * <p>
55 * There are two main components that are used to implement this:
56 * - A state machine containing valid Camera2 device states ({@link CameraDeviceState}).
57 * - A message-queue based pipeline that manages an old Camera class, and executes capture and
58 * configuration requests.
59 * </p>
60 */
61public class LegacyCameraDevice implements AutoCloseable {
62 public static final String DEBUG_PROP = "HAL1ShimLogging";
Ruben Brunkfeb50af2014-05-09 19:58:49 -070063 private final String TAG;
64
Ruben Brunke663cb772014-09-16 13:18:31 -070065 private static final boolean DEBUG = Log.isLoggable(LegacyCameraDevice.DEBUG_PROP, Log.DEBUG);
Ruben Brunkfeb50af2014-05-09 19:58:49 -070066 private final int mCameraId;
Ruben Brunke663cb772014-09-16 13:18:31 -070067 private final CameraCharacteristics mStaticCharacteristics;
Ruben Brunkfeb50af2014-05-09 19:58:49 -070068 private final ICameraDeviceCallbacks mDeviceCallbacks;
69 private final CameraDeviceState mDeviceState = new CameraDeviceState();
Igor Murashkin49b2b132014-06-18 19:03:00 -070070 private List<Surface> mConfiguredSurfaces;
Ruben Brunke663cb772014-09-16 13:18:31 -070071 private boolean mClosed = false;
Ruben Brunkfeb50af2014-05-09 19:58:49 -070072
73 private final ConditionVariable mIdle = new ConditionVariable(/*open*/true);
Ruben Brunkfeb50af2014-05-09 19:58:49 -070074
Ruben Brunkd85e1a62014-06-11 10:35:45 -070075 private final HandlerThread mResultThread = new HandlerThread("ResultThread");
76 private final HandlerThread mCallbackHandlerThread = new HandlerThread("CallbackThread");
Ruben Brunkfeb50af2014-05-09 19:58:49 -070077 private final Handler mCallbackHandler;
Ruben Brunkd85e1a62014-06-11 10:35:45 -070078 private final Handler mResultHandler;
Ruben Brunkfeb50af2014-05-09 19:58:49 -070079 private static final int ILLEGAL_VALUE = -1;
80
81 private CaptureResultExtras getExtrasFromRequest(RequestHolder holder) {
82 if (holder == null) {
83 return new CaptureResultExtras(ILLEGAL_VALUE, ILLEGAL_VALUE, ILLEGAL_VALUE,
Zhijun He83159152014-07-16 11:32:59 -070084 ILLEGAL_VALUE, ILLEGAL_VALUE, ILLEGAL_VALUE);
Ruben Brunkfeb50af2014-05-09 19:58:49 -070085 }
86 return new CaptureResultExtras(holder.getRequestId(), holder.getSubsequeceId(),
Zhijun He83159152014-07-16 11:32:59 -070087 /*afTriggerId*/0, /*precaptureTriggerId*/0, holder.getFrameNumber(),
88 /*partialResultCount*/1);
Ruben Brunkfeb50af2014-05-09 19:58:49 -070089 }
90
91 /**
92 * Listener for the camera device state machine. Calls the appropriate
93 * {@link ICameraDeviceCallbacks} for each state transition.
94 */
95 private final CameraDeviceState.CameraDeviceStateListener mStateListener =
96 new CameraDeviceState.CameraDeviceStateListener() {
97 @Override
Ruben Brunke663cb772014-09-16 13:18:31 -070098 public void onError(final int errorCode, final RequestHolder holder) {
Igor Murashkin51dcfd652014-09-25 16:55:01 -070099 if (DEBUG) {
100 Log.d(TAG, "onError called, errorCode = " + errorCode);
101 }
102 switch (errorCode) {
103 /*
104 * Only be considered idle if we hit a fatal error
105 * and no further requests can be processed.
106 */
107 case CameraDeviceImpl.CameraDeviceCallbacks.ERROR_CAMERA_DISCONNECTED:
108 case CameraDeviceImpl.CameraDeviceCallbacks.ERROR_CAMERA_SERVICE:
109 case CameraDeviceImpl.CameraDeviceCallbacks.ERROR_CAMERA_DEVICE: {
110 mIdle.open();
111
112 if (DEBUG) {
113 Log.d(TAG, "onError - opening idle");
114 }
115 }
116 }
117
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700118 final CaptureResultExtras extras = getExtrasFromRequest(holder);
Ruben Brunkd85e1a62014-06-11 10:35:45 -0700119 mResultHandler.post(new Runnable() {
120 @Override
121 public void run() {
Ruben Brunk3e4fed22014-06-18 17:08:42 -0700122 if (DEBUG) {
Ruben Brunke663cb772014-09-16 13:18:31 -0700123 Log.d(TAG, "doing onError callback for request " + holder.getRequestId() +
124 ", with error code " + errorCode);
Ruben Brunk3e4fed22014-06-18 17:08:42 -0700125 }
Ruben Brunkd85e1a62014-06-11 10:35:45 -0700126 try {
Eino-Ville Talvalaacc00952014-08-06 14:31:08 -0700127 mDeviceCallbacks.onDeviceError(errorCode, extras);
Ruben Brunkd85e1a62014-06-11 10:35:45 -0700128 } catch (RemoteException e) {
129 throw new IllegalStateException(
130 "Received remote exception during onCameraError callback: ", e);
131 }
132 }
133 });
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700134 }
135
136 @Override
137 public void onConfiguring() {
138 // Do nothing
Ruben Brunk3e4fed22014-06-18 17:08:42 -0700139 if (DEBUG) {
140 Log.d(TAG, "doing onConfiguring callback.");
141 }
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700142 }
143
144 @Override
145 public void onIdle() {
Igor Murashkin51dcfd652014-09-25 16:55:01 -0700146 if (DEBUG) {
147 Log.d(TAG, "onIdle called");
148 }
149
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700150 mIdle.open();
151
Ruben Brunkd85e1a62014-06-11 10:35:45 -0700152 mResultHandler.post(new Runnable() {
153 @Override
154 public void run() {
Ruben Brunk3e4fed22014-06-18 17:08:42 -0700155 if (DEBUG) {
156 Log.d(TAG, "doing onIdle callback.");
157 }
Ruben Brunkd85e1a62014-06-11 10:35:45 -0700158 try {
Eino-Ville Talvalaacc00952014-08-06 14:31:08 -0700159 mDeviceCallbacks.onDeviceIdle();
Ruben Brunkd85e1a62014-06-11 10:35:45 -0700160 } catch (RemoteException e) {
161 throw new IllegalStateException(
162 "Received remote exception during onCameraIdle callback: ", e);
163 }
164 }
165 });
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700166 }
167
168 @Override
Igor Murashkin51dcfd652014-09-25 16:55:01 -0700169 public void onBusy() {
170 mIdle.close();
171
172 if (DEBUG) {
173 Log.d(TAG, "onBusy called");
174 }
175 }
176
177 @Override
Ruben Brunke663cb772014-09-16 13:18:31 -0700178 public void onCaptureStarted(final RequestHolder holder, final long timestamp) {
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700179 final CaptureResultExtras extras = getExtrasFromRequest(holder);
180
Ruben Brunkd85e1a62014-06-11 10:35:45 -0700181 mResultHandler.post(new Runnable() {
182 @Override
183 public void run() {
Ruben Brunk3e4fed22014-06-18 17:08:42 -0700184 if (DEBUG) {
Ruben Brunke663cb772014-09-16 13:18:31 -0700185 Log.d(TAG, "doing onCaptureStarted callback for request " +
186 holder.getRequestId());
Ruben Brunk3e4fed22014-06-18 17:08:42 -0700187 }
Ruben Brunkd85e1a62014-06-11 10:35:45 -0700188 try {
Ruben Brunkd85e1a62014-06-11 10:35:45 -0700189 mDeviceCallbacks.onCaptureStarted(extras, timestamp);
190 } catch (RemoteException e) {
191 throw new IllegalStateException(
192 "Received remote exception during onCameraError callback: ", e);
193 }
194 }
195 });
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700196 }
197
198 @Override
Ruben Brunke663cb772014-09-16 13:18:31 -0700199 public void onCaptureResult(final CameraMetadataNative result, final RequestHolder holder) {
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700200 final CaptureResultExtras extras = getExtrasFromRequest(holder);
201
Ruben Brunkd85e1a62014-06-11 10:35:45 -0700202 mResultHandler.post(new Runnable() {
203 @Override
204 public void run() {
Ruben Brunk3e4fed22014-06-18 17:08:42 -0700205 if (DEBUG) {
Ruben Brunke663cb772014-09-16 13:18:31 -0700206 Log.d(TAG, "doing onCaptureResult callback for request " +
207 holder.getRequestId());
Ruben Brunk3e4fed22014-06-18 17:08:42 -0700208 }
Ruben Brunkd85e1a62014-06-11 10:35:45 -0700209 try {
Ruben Brunkd85e1a62014-06-11 10:35:45 -0700210 mDeviceCallbacks.onResultReceived(result, extras);
211 } catch (RemoteException e) {
212 throw new IllegalStateException(
213 "Received remote exception during onCameraError callback: ", e);
214 }
215 }
216 });
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700217 }
218 };
219
220 private final RequestThreadManager mRequestThreadManager;
221
222 /**
Ruben Brunk91b9aab2014-06-20 00:24:56 -0700223 * Check if a given surface uses {@link ImageFormat#YUV_420_888} or format that can be readily
224 * converted to this; YV12 and NV21 are the two currently supported formats.
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700225 *
226 * @param s the surface to check.
Ruben Brunk91b9aab2014-06-20 00:24:56 -0700227 * @return {@code true} if the surfaces uses {@link ImageFormat#YUV_420_888} or a compatible
228 * format.
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700229 */
Ruben Brunkef14da32014-06-24 16:06:54 -0700230 static boolean needsConversion(Surface s) throws BufferQueueAbandonedException {
231 int nativeType = detectSurfaceType(s);
Ruben Brunk91b9aab2014-06-20 00:24:56 -0700232 return nativeType == ImageFormat.YUV_420_888 || nativeType == ImageFormat.YV12 ||
233 nativeType == ImageFormat.NV21;
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700234 }
235
236 /**
237 * Create a new emulated camera device from a given Camera 1 API camera.
238 *
239 * <p>
240 * The {@link Camera} provided to this constructor must already have been successfully opened,
241 * and ownership of the provided camera is passed to this object. No further calls to the
242 * camera methods should be made following this constructor.
243 * </p>
244 *
245 * @param cameraId the id of the camera.
246 * @param camera an open {@link Camera} device.
Igor Murashkindf6242e2014-07-01 18:06:13 -0700247 * @param characteristics the static camera characteristics for this camera device
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700248 * @param callbacks {@link ICameraDeviceCallbacks} callbacks to call for Camera2 API operations.
249 */
Igor Murashkindf6242e2014-07-01 18:06:13 -0700250 public LegacyCameraDevice(int cameraId, Camera camera, CameraCharacteristics characteristics,
251 ICameraDeviceCallbacks callbacks) {
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700252 mCameraId = cameraId;
253 mDeviceCallbacks = callbacks;
254 TAG = String.format("CameraDevice-%d-LE", mCameraId);
255
Ruben Brunkd85e1a62014-06-11 10:35:45 -0700256 mResultThread.start();
257 mResultHandler = new Handler(mResultThread.getLooper());
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700258 mCallbackHandlerThread.start();
259 mCallbackHandler = new Handler(mCallbackHandlerThread.getLooper());
260 mDeviceState.setCameraDeviceCallbacks(mCallbackHandler, mStateListener);
Ruben Brunke663cb772014-09-16 13:18:31 -0700261 mStaticCharacteristics = characteristics;
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700262 mRequestThreadManager =
Igor Murashkindf6242e2014-07-01 18:06:13 -0700263 new RequestThreadManager(cameraId, camera, characteristics, mDeviceState);
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700264 mRequestThreadManager.start();
265 }
266
267 /**
268 * Configure the device with a set of output surfaces.
269 *
Igor Murashkin49b2b132014-06-18 19:03:00 -0700270 * <p>Using empty or {@code null} {@code outputs} is the same as unconfiguring.</p>
271 *
272 * <p>Every surface in {@code outputs} must be non-{@code null}.</p>
273 *
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700274 * @param outputs a list of surfaces to set.
Igor Murashkin49b2b132014-06-18 19:03:00 -0700275 * @return an error code for this binder operation, or {@link NO_ERROR}
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700276 * on success.
277 */
278 public int configureOutputs(List<Surface> outputs) {
Igor Murashkin49b2b132014-06-18 19:03:00 -0700279 if (outputs != null) {
280 for (Surface output : outputs) {
281 if (output == null) {
282 Log.e(TAG, "configureOutputs - null outputs are not allowed");
283 return BAD_VALUE;
284 }
Ruben Brunke663cb772014-09-16 13:18:31 -0700285 StreamConfigurationMap streamConfigurations = mStaticCharacteristics.
286 get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
287
288 // Validate surface size and format.
289 try {
290 Size s = getSurfaceSize(output);
291 int surfaceType = detectSurfaceType(output);
292 Size[] sizes = streamConfigurations.getOutputSizes(surfaceType);
293
294 if (sizes == null) {
295 // WAR: Override default format to IMPLEMENTATION_DEFINED for b/9487482
296 if ((surfaceType >= LegacyMetadataMapper.HAL_PIXEL_FORMAT_RGBA_8888 &&
297 surfaceType <= LegacyMetadataMapper.HAL_PIXEL_FORMAT_BGRA_8888)) {
298
299 // YUV_420_888 is always present in LEGACY for all IMPLEMENTATION_DEFINED
300 // output sizes, and is publicly visible in the API (i.e.
301 // {@code #getOutputSizes} works here).
302 sizes = streamConfigurations.getOutputSizes(ImageFormat.YUV_420_888);
303 } else if (surfaceType == LegacyMetadataMapper.HAL_PIXEL_FORMAT_BLOB) {
304 sizes = streamConfigurations.getOutputSizes(ImageFormat.JPEG);
305 }
306 }
307
308 if (!ArrayUtils.contains(sizes, s)) {
309 String reason = (sizes == null) ? "format is invalid." :
310 ("size not in valid set: " + Arrays.toString(sizes));
311 Log.e(TAG, String.format("Surface with size (w=%d, h=%d) and format 0x%x is"
312 + " not valid, %s", s.getWidth(), s.getHeight(), surfaceType,
313 reason));
314 return BAD_VALUE;
315 }
316 } catch (BufferQueueAbandonedException e) {
317 Log.e(TAG, "Surface bufferqueue is abandoned, cannot configure as output: ", e);
318 return BAD_VALUE;
319 }
320
Igor Murashkin49b2b132014-06-18 19:03:00 -0700321 }
322 }
323
Ruben Brunk4aed87a2014-09-21 18:35:31 -0700324 boolean success = false;
325 if (mDeviceState.setConfiguring()) {
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700326 mRequestThreadManager.configure(outputs);
Ruben Brunk4aed87a2014-09-21 18:35:31 -0700327 success = mDeviceState.setIdle();
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700328 }
Igor Murashkin49b2b132014-06-18 19:03:00 -0700329
Ruben Brunk4aed87a2014-09-21 18:35:31 -0700330 if (success) {
Igor Murashkin49b2b132014-06-18 19:03:00 -0700331 mConfiguredSurfaces = outputs != null ? new ArrayList<>(outputs) : null;
Ruben Brunk4aed87a2014-09-21 18:35:31 -0700332 } else {
333 return CameraBinderDecorator.INVALID_OPERATION;
Igor Murashkin49b2b132014-06-18 19:03:00 -0700334 }
Ruben Brunk4aed87a2014-09-21 18:35:31 -0700335 return CameraBinderDecorator.NO_ERROR;
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700336 }
337
338 /**
339 * Submit a burst of capture requests.
340 *
341 * @param requestList a list of capture requests to execute.
342 * @param repeating {@code true} if this burst is repeating.
343 * @param frameNumber an output argument that contains either the frame number of the last frame
344 * that will be returned for this request, or the frame number of the last
345 * frame that will be returned for the current repeating request if this
346 * burst is set to be repeating.
347 * @return the request id.
348 */
349 public int submitRequestList(List<CaptureRequest> requestList, boolean repeating,
350 /*out*/LongParcelable frameNumber) {
Igor Murashkin49b2b132014-06-18 19:03:00 -0700351 if (requestList == null || requestList.isEmpty()) {
352 Log.e(TAG, "submitRequestList - Empty/null requests are not allowed");
353 return BAD_VALUE;
354 }
355
Ruben Brunk3c8fa3b2014-06-30 16:45:05 -0700356 List<Long> surfaceIds = (mConfiguredSurfaces == null) ? new ArrayList<Long>() :
357 getSurfaceIds(mConfiguredSurfaces);
358
Igor Murashkin49b2b132014-06-18 19:03:00 -0700359 // Make sure that there all requests have at least 1 surface; all surfaces are non-null
360 for (CaptureRequest request : requestList) {
361 if (request.getTargets().isEmpty()) {
362 Log.e(TAG, "submitRequestList - "
363 + "Each request must have at least one Surface target");
364 return BAD_VALUE;
365 }
366
367 for (Surface surface : request.getTargets()) {
368 if (surface == null) {
369 Log.e(TAG, "submitRequestList - Null Surface targets are not allowed");
370 return BAD_VALUE;
371 } else if (mConfiguredSurfaces == null) {
372 Log.e(TAG, "submitRequestList - must configure " +
373 " device with valid surfaces before submitting requests");
374 return INVALID_OPERATION;
Ruben Brunk3c8fa3b2014-06-30 16:45:05 -0700375 } else if (!containsSurfaceId(surface, surfaceIds)) {
Igor Murashkin49b2b132014-06-18 19:03:00 -0700376 Log.e(TAG, "submitRequestList - cannot use a surface that wasn't configured");
377 return BAD_VALUE;
378 }
379 }
380 }
381
382 // TODO: further validation of request here
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700383 mIdle.close();
384 return mRequestThreadManager.submitCaptureRequests(requestList, repeating,
385 frameNumber);
386 }
387
388 /**
389 * Submit a single capture request.
390 *
391 * @param request the capture request to execute.
392 * @param repeating {@code true} if this request is repeating.
393 * @param frameNumber an output argument that contains either the frame number of the last frame
394 * that will be returned for this request, or the frame number of the last
395 * frame that will be returned for the current repeating request if this
396 * request is set to be repeating.
397 * @return the request id.
398 */
399 public int submitRequest(CaptureRequest request, boolean repeating,
400 /*out*/LongParcelable frameNumber) {
401 ArrayList<CaptureRequest> requestList = new ArrayList<CaptureRequest>();
402 requestList.add(request);
403 return submitRequestList(requestList, repeating, frameNumber);
404 }
405
406 /**
407 * Cancel the repeating request with the given request id.
408 *
409 * @param requestId the request id of the request to cancel.
410 * @return the last frame number to be returned from the HAL for the given repeating request, or
411 * {@code INVALID_FRAME} if none exists.
412 */
413 public long cancelRequest(int requestId) {
414 return mRequestThreadManager.cancelRepeating(requestId);
415 }
416
417 /**
418 * Block until the {@link ICameraDeviceCallbacks#onCameraIdle()} callback is received.
419 */
420 public void waitUntilIdle() {
421 mIdle.block();
422 }
423
Ruben Brunke663cb772014-09-16 13:18:31 -0700424 /**
425 * Flush any pending requests.
426 *
427 * @return the last frame number.
428 */
429 public long flush() {
430 long lastFrame = mRequestThreadManager.flush();
431 waitUntilIdle();
432 return lastFrame;
433 }
434
435 /**
436 * Return {@code true} if the device has been closed.
437 */
438 public boolean isClosed() {
439 return mClosed;
440 }
441
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700442 @Override
443 public void close() {
444 mRequestThreadManager.quit();
445 mCallbackHandlerThread.quitSafely();
Ruben Brunkd85e1a62014-06-11 10:35:45 -0700446 mResultThread.quitSafely();
447
448 try {
449 mCallbackHandlerThread.join();
450 } catch (InterruptedException e) {
451 Log.e(TAG, String.format("Thread %s (%d) interrupted while quitting.",
452 mCallbackHandlerThread.getName(), mCallbackHandlerThread.getId()));
453 }
454
455 try {
456 mResultThread.join();
457 } catch (InterruptedException e) {
458 Log.e(TAG, String.format("Thread %s (%d) interrupted while quitting.",
459 mResultThread.getName(), mResultThread.getId()));
460 }
461
Ruben Brunke663cb772014-09-16 13:18:31 -0700462 mClosed = true;
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700463 }
464
465 @Override
466 protected void finalize() throws Throwable {
467 try {
468 close();
469 } catch (CameraRuntimeException e) {
470 Log.e(TAG, "Got error while trying to finalize, ignoring: " + e.getMessage());
471 } finally {
472 super.finalize();
473 }
474 }
475
Igor Murashkina296fec2014-06-23 14:44:09 -0700476 /**
477 * Query the surface for its currently configured default buffer size.
478 * @param surface a non-{@code null} {@code Surface}
479 * @return the width and height of the surface
480 *
481 * @throws NullPointerException if the {@code surface} was {@code null}
482 * @throws IllegalStateException if the {@code surface} was invalid
483 */
Ruben Brunkef14da32014-06-24 16:06:54 -0700484 static Size getSurfaceSize(Surface surface) throws BufferQueueAbandonedException {
Igor Murashkina296fec2014-06-23 14:44:09 -0700485 checkNotNull(surface);
486
487 int[] dimens = new int[2];
Ruben Brunkef14da32014-06-24 16:06:54 -0700488 LegacyExceptionUtils.throwOnError(nativeDetectSurfaceDimens(surface, /*out*/dimens));
Igor Murashkina296fec2014-06-23 14:44:09 -0700489
490 return new Size(dimens[0], dimens[1]);
491 }
492
Ruben Brunkef14da32014-06-24 16:06:54 -0700493 static int detectSurfaceType(Surface surface) throws BufferQueueAbandonedException {
494 checkNotNull(surface);
495 return LegacyExceptionUtils.throwOnError(nativeDetectSurfaceType(surface));
496 }
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700497
Ruben Brunkef14da32014-06-24 16:06:54 -0700498 static void configureSurface(Surface surface, int width, int height,
499 int pixelFormat) throws BufferQueueAbandonedException {
500 checkNotNull(surface);
501 checkArgumentPositive(width, "width must be positive.");
502 checkArgumentPositive(height, "height must be positive.");
503
504 LegacyExceptionUtils.throwOnError(nativeConfigureSurface(surface, width, height,
505 pixelFormat));
506 }
507
508 static void produceFrame(Surface surface, byte[] pixelBuffer, int width,
509 int height, int pixelFormat)
510 throws BufferQueueAbandonedException {
511 checkNotNull(surface);
512 checkNotNull(pixelBuffer);
513 checkArgumentPositive(width, "width must be positive.");
514 checkArgumentPositive(height, "height must be positive.");
515
516 LegacyExceptionUtils.throwOnError(nativeProduceFrame(surface, pixelBuffer, width, height,
517 pixelFormat));
518 }
519
520 static void setSurfaceFormat(Surface surface, int pixelFormat)
521 throws BufferQueueAbandonedException {
522 checkNotNull(surface);
523
524 LegacyExceptionUtils.throwOnError(nativeSetSurfaceFormat(surface, pixelFormat));
525 }
526
527 static void setSurfaceDimens(Surface surface, int width, int height)
528 throws BufferQueueAbandonedException {
529 checkNotNull(surface);
530 checkArgumentPositive(width, "width must be positive.");
531 checkArgumentPositive(height, "height must be positive.");
532
533 LegacyExceptionUtils.throwOnError(nativeSetSurfaceDimens(surface, width, height));
534 }
535
Ruben Brunk3c8fa3b2014-06-30 16:45:05 -0700536 static long getSurfaceId(Surface surface) {
537 checkNotNull(surface);
538 return nativeGetSurfaceId(surface);
539 }
540
541 static List<Long> getSurfaceIds(Collection<Surface> surfaces) {
542 if (surfaces == null) {
543 throw new NullPointerException("Null argument surfaces");
544 }
545 List<Long> surfaceIds = new ArrayList<>();
546 for (Surface s : surfaces) {
547 long id = getSurfaceId(s);
548 if (id == 0) {
549 throw new IllegalStateException(
550 "Configured surface had null native GraphicBufferProducer pointer!");
551 }
552 surfaceIds.add(id);
553 }
554 return surfaceIds;
555 }
556
Ruben Brunk0fd198a2014-09-23 23:35:43 -0700557 static boolean containsSurfaceId(Surface s, Collection<Long> ids) {
Ruben Brunk3c8fa3b2014-06-30 16:45:05 -0700558 long id = getSurfaceId(s);
559 return ids.contains(id);
560 }
561
Ruben Brunk28c49c92014-06-16 18:43:59 -0700562 static void setSurfaceOrientation(Surface surface, int facing, int sensorOrientation)
563 throws BufferQueueAbandonedException {
564 checkNotNull(surface);
565 LegacyExceptionUtils.throwOnError(nativeSetSurfaceOrientation(surface, facing,
566 sensorOrientation));
567 }
568
569 static Size getTextureSize(SurfaceTexture surfaceTexture)
570 throws BufferQueueAbandonedException {
571 checkNotNull(surfaceTexture);
572
573 int[] dimens = new int[2];
574 LegacyExceptionUtils.throwOnError(nativeDetectTextureDimens(surfaceTexture,
575 /*out*/dimens));
576
577 return new Size(dimens[0], dimens[1]);
578 }
579
Ruben Brunk91838de2014-07-16 17:24:17 -0700580 static void setNextTimestamp(Surface surface, long timestamp)
581 throws BufferQueueAbandonedException {
582 checkNotNull(surface);
583 LegacyExceptionUtils.throwOnError(nativeSetNextTimestamp(surface, timestamp));
584 }
585
Ruben Brunkef14da32014-06-24 16:06:54 -0700586 private static native int nativeDetectSurfaceType(Surface surface);
587
588 private static native int nativeDetectSurfaceDimens(Surface surface,
Igor Murashkina296fec2014-06-23 14:44:09 -0700589 /*out*/int[/*2*/] dimens);
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700590
Ruben Brunkef14da32014-06-24 16:06:54 -0700591 private static native int nativeConfigureSurface(Surface surface, int width, int height,
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700592 int pixelFormat);
593
Ruben Brunkef14da32014-06-24 16:06:54 -0700594 private static native int nativeProduceFrame(Surface surface, byte[] pixelBuffer, int width,
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700595 int height, int pixelFormat);
596
Ruben Brunkef14da32014-06-24 16:06:54 -0700597 private static native int nativeSetSurfaceFormat(Surface surface, int pixelFormat);
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700598
Ruben Brunkef14da32014-06-24 16:06:54 -0700599 private static native int nativeSetSurfaceDimens(Surface surface, int width, int height);
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700600
Ruben Brunk3c8fa3b2014-06-30 16:45:05 -0700601 private static native long nativeGetSurfaceId(Surface surface);
Ruben Brunk28c49c92014-06-16 18:43:59 -0700602
603 private static native int nativeSetSurfaceOrientation(Surface surface, int facing,
604 int sensorOrientation);
605
606 private static native int nativeDetectTextureDimens(SurfaceTexture surfaceTexture,
607 /*out*/int[/*2*/] dimens);
608
Ruben Brunk91838de2014-07-16 17:24:17 -0700609 private static native int nativeSetNextTimestamp(Surface surface, long timestamp);
Ruben Brunk1dc13262014-07-31 11:43:27 -0700610
611 static native int nativeGetJpegFooterSize();
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700612}