blob: 4587c6fa5231b43ca7121e440d6b89959aab9aae [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;
24import android.hardware.camera2.impl.CaptureResultExtras;
25import android.hardware.camera2.ICameraDeviceCallbacks;
Ruben Brunke663cb772014-09-16 13:18:31 -070026import android.hardware.camera2.params.StreamConfiguration;
27import android.hardware.camera2.params.StreamConfigurationMap;
28import android.hardware.camera2.utils.ArrayUtils;
Ruben Brunk4aed87a2014-09-21 18:35:31 -070029import android.hardware.camera2.utils.CameraBinderDecorator;
Ruben Brunkfeb50af2014-05-09 19:58:49 -070030import android.hardware.camera2.utils.LongParcelable;
31import android.hardware.camera2.impl.CameraMetadataNative;
Ruben Brunkfeb50af2014-05-09 19:58:49 -070032import android.hardware.camera2.utils.CameraRuntimeException;
33import android.os.ConditionVariable;
34import android.os.Handler;
35import android.os.HandlerThread;
36import android.os.RemoteException;
37import android.util.Log;
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 Murashkin49b2b132014-06-18 19:03:00 -070047import static android.hardware.camera2.utils.CameraBinderDecorator.*;
Igor Murashkina296fec2014-06-23 14:44:09 -070048import static com.android.internal.util.Preconditions.*;
Ruben Brunkfeb50af2014-05-09 19:58:49 -070049
50/**
51 * This class emulates the functionality of a Camera2 device using a the old Camera class.
52 *
53 * <p>
54 * There are two main components that are used to implement this:
55 * - A state machine containing valid Camera2 device states ({@link CameraDeviceState}).
56 * - A message-queue based pipeline that manages an old Camera class, and executes capture and
57 * configuration requests.
58 * </p>
59 */
60public class LegacyCameraDevice implements AutoCloseable {
61 public static final String DEBUG_PROP = "HAL1ShimLogging";
Ruben Brunkfeb50af2014-05-09 19:58:49 -070062 private final String TAG;
63
Ruben Brunke663cb772014-09-16 13:18:31 -070064 private static final boolean DEBUG = Log.isLoggable(LegacyCameraDevice.DEBUG_PROP, Log.DEBUG);
Ruben Brunkfeb50af2014-05-09 19:58:49 -070065 private final int mCameraId;
Ruben Brunke663cb772014-09-16 13:18:31 -070066 private final CameraCharacteristics mStaticCharacteristics;
Ruben Brunkfeb50af2014-05-09 19:58:49 -070067 private final ICameraDeviceCallbacks mDeviceCallbacks;
68 private final CameraDeviceState mDeviceState = new CameraDeviceState();
Igor Murashkin49b2b132014-06-18 19:03:00 -070069 private List<Surface> mConfiguredSurfaces;
Ruben Brunke663cb772014-09-16 13:18:31 -070070 private boolean mClosed = false;
Ruben Brunkfeb50af2014-05-09 19:58:49 -070071
72 private final ConditionVariable mIdle = new ConditionVariable(/*open*/true);
Ruben Brunkfeb50af2014-05-09 19:58:49 -070073
Ruben Brunkd85e1a62014-06-11 10:35:45 -070074 private final HandlerThread mResultThread = new HandlerThread("ResultThread");
75 private final HandlerThread mCallbackHandlerThread = new HandlerThread("CallbackThread");
Ruben Brunkfeb50af2014-05-09 19:58:49 -070076 private final Handler mCallbackHandler;
Ruben Brunkd85e1a62014-06-11 10:35:45 -070077 private final Handler mResultHandler;
Ruben Brunkfeb50af2014-05-09 19:58:49 -070078 private static final int ILLEGAL_VALUE = -1;
79
80 private CaptureResultExtras getExtrasFromRequest(RequestHolder holder) {
81 if (holder == null) {
82 return new CaptureResultExtras(ILLEGAL_VALUE, ILLEGAL_VALUE, ILLEGAL_VALUE,
Zhijun He83159152014-07-16 11:32:59 -070083 ILLEGAL_VALUE, ILLEGAL_VALUE, ILLEGAL_VALUE);
Ruben Brunkfeb50af2014-05-09 19:58:49 -070084 }
85 return new CaptureResultExtras(holder.getRequestId(), holder.getSubsequeceId(),
Zhijun He83159152014-07-16 11:32:59 -070086 /*afTriggerId*/0, /*precaptureTriggerId*/0, holder.getFrameNumber(),
87 /*partialResultCount*/1);
Ruben Brunkfeb50af2014-05-09 19:58:49 -070088 }
89
90 /**
91 * Listener for the camera device state machine. Calls the appropriate
92 * {@link ICameraDeviceCallbacks} for each state transition.
93 */
94 private final CameraDeviceState.CameraDeviceStateListener mStateListener =
95 new CameraDeviceState.CameraDeviceStateListener() {
96 @Override
Ruben Brunke663cb772014-09-16 13:18:31 -070097 public void onError(final int errorCode, final RequestHolder holder) {
Ruben Brunkfeb50af2014-05-09 19:58:49 -070098 mIdle.open();
99 final CaptureResultExtras extras = getExtrasFromRequest(holder);
Ruben Brunkd85e1a62014-06-11 10:35:45 -0700100 mResultHandler.post(new Runnable() {
101 @Override
102 public void run() {
Ruben Brunk3e4fed22014-06-18 17:08:42 -0700103 if (DEBUG) {
Ruben Brunke663cb772014-09-16 13:18:31 -0700104 Log.d(TAG, "doing onError callback for request " + holder.getRequestId() +
105 ", with error code " + errorCode);
Ruben Brunk3e4fed22014-06-18 17:08:42 -0700106 }
Ruben Brunkd85e1a62014-06-11 10:35:45 -0700107 try {
Eino-Ville Talvalaacc00952014-08-06 14:31:08 -0700108 mDeviceCallbacks.onDeviceError(errorCode, extras);
Ruben Brunkd85e1a62014-06-11 10:35:45 -0700109 } catch (RemoteException e) {
110 throw new IllegalStateException(
111 "Received remote exception during onCameraError callback: ", e);
112 }
113 }
114 });
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700115 }
116
117 @Override
118 public void onConfiguring() {
119 // Do nothing
Ruben Brunk3e4fed22014-06-18 17:08:42 -0700120 if (DEBUG) {
121 Log.d(TAG, "doing onConfiguring callback.");
122 }
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700123 }
124
125 @Override
126 public void onIdle() {
127 mIdle.open();
128
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) {
133 Log.d(TAG, "doing onIdle callback.");
134 }
Ruben Brunkd85e1a62014-06-11 10:35:45 -0700135 try {
Eino-Ville Talvalaacc00952014-08-06 14:31:08 -0700136 mDeviceCallbacks.onDeviceIdle();
Ruben Brunkd85e1a62014-06-11 10:35:45 -0700137 } catch (RemoteException e) {
138 throw new IllegalStateException(
139 "Received remote exception during onCameraIdle callback: ", e);
140 }
141 }
142 });
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700143 }
144
145 @Override
Ruben Brunke663cb772014-09-16 13:18:31 -0700146 public void onCaptureStarted(final RequestHolder holder, final long timestamp) {
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700147 final CaptureResultExtras extras = getExtrasFromRequest(holder);
148
Ruben Brunkd85e1a62014-06-11 10:35:45 -0700149 mResultHandler.post(new Runnable() {
150 @Override
151 public void run() {
Ruben Brunk3e4fed22014-06-18 17:08:42 -0700152 if (DEBUG) {
Ruben Brunke663cb772014-09-16 13:18:31 -0700153 Log.d(TAG, "doing onCaptureStarted callback for request " +
154 holder.getRequestId());
Ruben Brunk3e4fed22014-06-18 17:08:42 -0700155 }
Ruben Brunkd85e1a62014-06-11 10:35:45 -0700156 try {
Ruben Brunkd85e1a62014-06-11 10:35:45 -0700157 mDeviceCallbacks.onCaptureStarted(extras, timestamp);
158 } catch (RemoteException e) {
159 throw new IllegalStateException(
160 "Received remote exception during onCameraError callback: ", e);
161 }
162 }
163 });
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700164 }
165
166 @Override
Ruben Brunke663cb772014-09-16 13:18:31 -0700167 public void onCaptureResult(final CameraMetadataNative result, final RequestHolder holder) {
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700168 final CaptureResultExtras extras = getExtrasFromRequest(holder);
169
Ruben Brunkd85e1a62014-06-11 10:35:45 -0700170 mResultHandler.post(new Runnable() {
171 @Override
172 public void run() {
Ruben Brunk3e4fed22014-06-18 17:08:42 -0700173 if (DEBUG) {
Ruben Brunke663cb772014-09-16 13:18:31 -0700174 Log.d(TAG, "doing onCaptureResult callback for request " +
175 holder.getRequestId());
Ruben Brunk3e4fed22014-06-18 17:08:42 -0700176 }
Ruben Brunkd85e1a62014-06-11 10:35:45 -0700177 try {
Ruben Brunkd85e1a62014-06-11 10:35:45 -0700178 mDeviceCallbacks.onResultReceived(result, extras);
179 } catch (RemoteException e) {
180 throw new IllegalStateException(
181 "Received remote exception during onCameraError callback: ", e);
182 }
183 }
184 });
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700185 }
186 };
187
188 private final RequestThreadManager mRequestThreadManager;
189
190 /**
Ruben Brunk91b9aab2014-06-20 00:24:56 -0700191 * Check if a given surface uses {@link ImageFormat#YUV_420_888} or format that can be readily
192 * converted to this; YV12 and NV21 are the two currently supported formats.
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700193 *
194 * @param s the surface to check.
Ruben Brunk91b9aab2014-06-20 00:24:56 -0700195 * @return {@code true} if the surfaces uses {@link ImageFormat#YUV_420_888} or a compatible
196 * format.
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700197 */
Ruben Brunkef14da32014-06-24 16:06:54 -0700198 static boolean needsConversion(Surface s) throws BufferQueueAbandonedException {
199 int nativeType = detectSurfaceType(s);
Ruben Brunk91b9aab2014-06-20 00:24:56 -0700200 return nativeType == ImageFormat.YUV_420_888 || nativeType == ImageFormat.YV12 ||
201 nativeType == ImageFormat.NV21;
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700202 }
203
204 /**
205 * Create a new emulated camera device from a given Camera 1 API camera.
206 *
207 * <p>
208 * The {@link Camera} provided to this constructor must already have been successfully opened,
209 * and ownership of the provided camera is passed to this object. No further calls to the
210 * camera methods should be made following this constructor.
211 * </p>
212 *
213 * @param cameraId the id of the camera.
214 * @param camera an open {@link Camera} device.
Igor Murashkindf6242e2014-07-01 18:06:13 -0700215 * @param characteristics the static camera characteristics for this camera device
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700216 * @param callbacks {@link ICameraDeviceCallbacks} callbacks to call for Camera2 API operations.
217 */
Igor Murashkindf6242e2014-07-01 18:06:13 -0700218 public LegacyCameraDevice(int cameraId, Camera camera, CameraCharacteristics characteristics,
219 ICameraDeviceCallbacks callbacks) {
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700220 mCameraId = cameraId;
221 mDeviceCallbacks = callbacks;
222 TAG = String.format("CameraDevice-%d-LE", mCameraId);
223
Ruben Brunkd85e1a62014-06-11 10:35:45 -0700224 mResultThread.start();
225 mResultHandler = new Handler(mResultThread.getLooper());
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700226 mCallbackHandlerThread.start();
227 mCallbackHandler = new Handler(mCallbackHandlerThread.getLooper());
228 mDeviceState.setCameraDeviceCallbacks(mCallbackHandler, mStateListener);
Ruben Brunke663cb772014-09-16 13:18:31 -0700229 mStaticCharacteristics = characteristics;
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700230 mRequestThreadManager =
Igor Murashkindf6242e2014-07-01 18:06:13 -0700231 new RequestThreadManager(cameraId, camera, characteristics, mDeviceState);
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700232 mRequestThreadManager.start();
233 }
234
235 /**
236 * Configure the device with a set of output surfaces.
237 *
Igor Murashkin49b2b132014-06-18 19:03:00 -0700238 * <p>Using empty or {@code null} {@code outputs} is the same as unconfiguring.</p>
239 *
240 * <p>Every surface in {@code outputs} must be non-{@code null}.</p>
241 *
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700242 * @param outputs a list of surfaces to set.
Igor Murashkin49b2b132014-06-18 19:03:00 -0700243 * @return an error code for this binder operation, or {@link NO_ERROR}
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700244 * on success.
245 */
246 public int configureOutputs(List<Surface> outputs) {
Igor Murashkin49b2b132014-06-18 19:03:00 -0700247 if (outputs != null) {
248 for (Surface output : outputs) {
249 if (output == null) {
250 Log.e(TAG, "configureOutputs - null outputs are not allowed");
251 return BAD_VALUE;
252 }
Ruben Brunke663cb772014-09-16 13:18:31 -0700253 StreamConfigurationMap streamConfigurations = mStaticCharacteristics.
254 get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
255
256 // Validate surface size and format.
257 try {
258 Size s = getSurfaceSize(output);
259 int surfaceType = detectSurfaceType(output);
260 Size[] sizes = streamConfigurations.getOutputSizes(surfaceType);
261
262 if (sizes == null) {
263 // WAR: Override default format to IMPLEMENTATION_DEFINED for b/9487482
264 if ((surfaceType >= LegacyMetadataMapper.HAL_PIXEL_FORMAT_RGBA_8888 &&
265 surfaceType <= LegacyMetadataMapper.HAL_PIXEL_FORMAT_BGRA_8888)) {
266
267 // YUV_420_888 is always present in LEGACY for all IMPLEMENTATION_DEFINED
268 // output sizes, and is publicly visible in the API (i.e.
269 // {@code #getOutputSizes} works here).
270 sizes = streamConfigurations.getOutputSizes(ImageFormat.YUV_420_888);
271 } else if (surfaceType == LegacyMetadataMapper.HAL_PIXEL_FORMAT_BLOB) {
272 sizes = streamConfigurations.getOutputSizes(ImageFormat.JPEG);
273 }
274 }
275
276 if (!ArrayUtils.contains(sizes, s)) {
277 String reason = (sizes == null) ? "format is invalid." :
278 ("size not in valid set: " + Arrays.toString(sizes));
279 Log.e(TAG, String.format("Surface with size (w=%d, h=%d) and format 0x%x is"
280 + " not valid, %s", s.getWidth(), s.getHeight(), surfaceType,
281 reason));
282 return BAD_VALUE;
283 }
284 } catch (BufferQueueAbandonedException e) {
285 Log.e(TAG, "Surface bufferqueue is abandoned, cannot configure as output: ", e);
286 return BAD_VALUE;
287 }
288
Igor Murashkin49b2b132014-06-18 19:03:00 -0700289 }
290 }
291
Ruben Brunk4aed87a2014-09-21 18:35:31 -0700292 boolean success = false;
293 if (mDeviceState.setConfiguring()) {
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700294 mRequestThreadManager.configure(outputs);
Ruben Brunk4aed87a2014-09-21 18:35:31 -0700295 success = mDeviceState.setIdle();
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700296 }
Igor Murashkin49b2b132014-06-18 19:03:00 -0700297
Ruben Brunk4aed87a2014-09-21 18:35:31 -0700298 if (success) {
Igor Murashkin49b2b132014-06-18 19:03:00 -0700299 mConfiguredSurfaces = outputs != null ? new ArrayList<>(outputs) : null;
Ruben Brunk4aed87a2014-09-21 18:35:31 -0700300 } else {
301 return CameraBinderDecorator.INVALID_OPERATION;
Igor Murashkin49b2b132014-06-18 19:03:00 -0700302 }
Ruben Brunk4aed87a2014-09-21 18:35:31 -0700303 return CameraBinderDecorator.NO_ERROR;
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700304 }
305
306 /**
307 * Submit a burst of capture requests.
308 *
309 * @param requestList a list of capture requests to execute.
310 * @param repeating {@code true} if this burst is repeating.
311 * @param frameNumber an output argument that contains either the frame number of the last frame
312 * that will be returned for this request, or the frame number of the last
313 * frame that will be returned for the current repeating request if this
314 * burst is set to be repeating.
315 * @return the request id.
316 */
317 public int submitRequestList(List<CaptureRequest> requestList, boolean repeating,
318 /*out*/LongParcelable frameNumber) {
Igor Murashkin49b2b132014-06-18 19:03:00 -0700319 if (requestList == null || requestList.isEmpty()) {
320 Log.e(TAG, "submitRequestList - Empty/null requests are not allowed");
321 return BAD_VALUE;
322 }
323
Ruben Brunk3c8fa3b2014-06-30 16:45:05 -0700324 List<Long> surfaceIds = (mConfiguredSurfaces == null) ? new ArrayList<Long>() :
325 getSurfaceIds(mConfiguredSurfaces);
326
Igor Murashkin49b2b132014-06-18 19:03:00 -0700327 // Make sure that there all requests have at least 1 surface; all surfaces are non-null
328 for (CaptureRequest request : requestList) {
329 if (request.getTargets().isEmpty()) {
330 Log.e(TAG, "submitRequestList - "
331 + "Each request must have at least one Surface target");
332 return BAD_VALUE;
333 }
334
335 for (Surface surface : request.getTargets()) {
336 if (surface == null) {
337 Log.e(TAG, "submitRequestList - Null Surface targets are not allowed");
338 return BAD_VALUE;
339 } else if (mConfiguredSurfaces == null) {
340 Log.e(TAG, "submitRequestList - must configure " +
341 " device with valid surfaces before submitting requests");
342 return INVALID_OPERATION;
Ruben Brunk3c8fa3b2014-06-30 16:45:05 -0700343 } else if (!containsSurfaceId(surface, surfaceIds)) {
Igor Murashkin49b2b132014-06-18 19:03:00 -0700344 Log.e(TAG, "submitRequestList - cannot use a surface that wasn't configured");
345 return BAD_VALUE;
346 }
347 }
348 }
349
350 // TODO: further validation of request here
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700351 mIdle.close();
352 return mRequestThreadManager.submitCaptureRequests(requestList, repeating,
353 frameNumber);
354 }
355
356 /**
357 * Submit a single capture request.
358 *
359 * @param request the capture request to execute.
360 * @param repeating {@code true} if this request is repeating.
361 * @param frameNumber an output argument that contains either the frame number of the last frame
362 * that will be returned for this request, or the frame number of the last
363 * frame that will be returned for the current repeating request if this
364 * request is set to be repeating.
365 * @return the request id.
366 */
367 public int submitRequest(CaptureRequest request, boolean repeating,
368 /*out*/LongParcelable frameNumber) {
369 ArrayList<CaptureRequest> requestList = new ArrayList<CaptureRequest>();
370 requestList.add(request);
371 return submitRequestList(requestList, repeating, frameNumber);
372 }
373
374 /**
375 * Cancel the repeating request with the given request id.
376 *
377 * @param requestId the request id of the request to cancel.
378 * @return the last frame number to be returned from the HAL for the given repeating request, or
379 * {@code INVALID_FRAME} if none exists.
380 */
381 public long cancelRequest(int requestId) {
382 return mRequestThreadManager.cancelRepeating(requestId);
383 }
384
385 /**
386 * Block until the {@link ICameraDeviceCallbacks#onCameraIdle()} callback is received.
387 */
388 public void waitUntilIdle() {
389 mIdle.block();
390 }
391
Ruben Brunke663cb772014-09-16 13:18:31 -0700392 /**
393 * Flush any pending requests.
394 *
395 * @return the last frame number.
396 */
397 public long flush() {
398 long lastFrame = mRequestThreadManager.flush();
399 waitUntilIdle();
400 return lastFrame;
401 }
402
403 /**
404 * Return {@code true} if the device has been closed.
405 */
406 public boolean isClosed() {
407 return mClosed;
408 }
409
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700410 @Override
411 public void close() {
412 mRequestThreadManager.quit();
413 mCallbackHandlerThread.quitSafely();
Ruben Brunkd85e1a62014-06-11 10:35:45 -0700414 mResultThread.quitSafely();
415
416 try {
417 mCallbackHandlerThread.join();
418 } catch (InterruptedException e) {
419 Log.e(TAG, String.format("Thread %s (%d) interrupted while quitting.",
420 mCallbackHandlerThread.getName(), mCallbackHandlerThread.getId()));
421 }
422
423 try {
424 mResultThread.join();
425 } catch (InterruptedException e) {
426 Log.e(TAG, String.format("Thread %s (%d) interrupted while quitting.",
427 mResultThread.getName(), mResultThread.getId()));
428 }
429
Ruben Brunke663cb772014-09-16 13:18:31 -0700430 mClosed = true;
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700431 }
432
433 @Override
434 protected void finalize() throws Throwable {
435 try {
436 close();
437 } catch (CameraRuntimeException e) {
438 Log.e(TAG, "Got error while trying to finalize, ignoring: " + e.getMessage());
439 } finally {
440 super.finalize();
441 }
442 }
443
Igor Murashkina296fec2014-06-23 14:44:09 -0700444 /**
445 * Query the surface for its currently configured default buffer size.
446 * @param surface a non-{@code null} {@code Surface}
447 * @return the width and height of the surface
448 *
449 * @throws NullPointerException if the {@code surface} was {@code null}
450 * @throws IllegalStateException if the {@code surface} was invalid
451 */
Ruben Brunkef14da32014-06-24 16:06:54 -0700452 static Size getSurfaceSize(Surface surface) throws BufferQueueAbandonedException {
Igor Murashkina296fec2014-06-23 14:44:09 -0700453 checkNotNull(surface);
454
455 int[] dimens = new int[2];
Ruben Brunkef14da32014-06-24 16:06:54 -0700456 LegacyExceptionUtils.throwOnError(nativeDetectSurfaceDimens(surface, /*out*/dimens));
Igor Murashkina296fec2014-06-23 14:44:09 -0700457
458 return new Size(dimens[0], dimens[1]);
459 }
460
Ruben Brunkef14da32014-06-24 16:06:54 -0700461 static int detectSurfaceType(Surface surface) throws BufferQueueAbandonedException {
462 checkNotNull(surface);
463 return LegacyExceptionUtils.throwOnError(nativeDetectSurfaceType(surface));
464 }
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700465
Ruben Brunkef14da32014-06-24 16:06:54 -0700466 static void configureSurface(Surface surface, int width, int height,
467 int pixelFormat) throws BufferQueueAbandonedException {
468 checkNotNull(surface);
469 checkArgumentPositive(width, "width must be positive.");
470 checkArgumentPositive(height, "height must be positive.");
471
472 LegacyExceptionUtils.throwOnError(nativeConfigureSurface(surface, width, height,
473 pixelFormat));
474 }
475
476 static void produceFrame(Surface surface, byte[] pixelBuffer, int width,
477 int height, int pixelFormat)
478 throws BufferQueueAbandonedException {
479 checkNotNull(surface);
480 checkNotNull(pixelBuffer);
481 checkArgumentPositive(width, "width must be positive.");
482 checkArgumentPositive(height, "height must be positive.");
483
484 LegacyExceptionUtils.throwOnError(nativeProduceFrame(surface, pixelBuffer, width, height,
485 pixelFormat));
486 }
487
488 static void setSurfaceFormat(Surface surface, int pixelFormat)
489 throws BufferQueueAbandonedException {
490 checkNotNull(surface);
491
492 LegacyExceptionUtils.throwOnError(nativeSetSurfaceFormat(surface, pixelFormat));
493 }
494
495 static void setSurfaceDimens(Surface surface, int width, int height)
496 throws BufferQueueAbandonedException {
497 checkNotNull(surface);
498 checkArgumentPositive(width, "width must be positive.");
499 checkArgumentPositive(height, "height must be positive.");
500
501 LegacyExceptionUtils.throwOnError(nativeSetSurfaceDimens(surface, width, height));
502 }
503
Ruben Brunk3c8fa3b2014-06-30 16:45:05 -0700504 static long getSurfaceId(Surface surface) {
505 checkNotNull(surface);
506 return nativeGetSurfaceId(surface);
507 }
508
509 static List<Long> getSurfaceIds(Collection<Surface> surfaces) {
510 if (surfaces == null) {
511 throw new NullPointerException("Null argument surfaces");
512 }
513 List<Long> surfaceIds = new ArrayList<>();
514 for (Surface s : surfaces) {
515 long id = getSurfaceId(s);
516 if (id == 0) {
517 throw new IllegalStateException(
518 "Configured surface had null native GraphicBufferProducer pointer!");
519 }
520 surfaceIds.add(id);
521 }
522 return surfaceIds;
523 }
524
Ruben Brunk0fd198a2014-09-23 23:35:43 -0700525 static boolean containsSurfaceId(Surface s, Collection<Long> ids) {
Ruben Brunk3c8fa3b2014-06-30 16:45:05 -0700526 long id = getSurfaceId(s);
527 return ids.contains(id);
528 }
529
Ruben Brunk28c49c92014-06-16 18:43:59 -0700530 static void setSurfaceOrientation(Surface surface, int facing, int sensorOrientation)
531 throws BufferQueueAbandonedException {
532 checkNotNull(surface);
533 LegacyExceptionUtils.throwOnError(nativeSetSurfaceOrientation(surface, facing,
534 sensorOrientation));
535 }
536
537 static Size getTextureSize(SurfaceTexture surfaceTexture)
538 throws BufferQueueAbandonedException {
539 checkNotNull(surfaceTexture);
540
541 int[] dimens = new int[2];
542 LegacyExceptionUtils.throwOnError(nativeDetectTextureDimens(surfaceTexture,
543 /*out*/dimens));
544
545 return new Size(dimens[0], dimens[1]);
546 }
547
Ruben Brunk91838de2014-07-16 17:24:17 -0700548 static void setNextTimestamp(Surface surface, long timestamp)
549 throws BufferQueueAbandonedException {
550 checkNotNull(surface);
551 LegacyExceptionUtils.throwOnError(nativeSetNextTimestamp(surface, timestamp));
552 }
553
Ruben Brunkef14da32014-06-24 16:06:54 -0700554 private static native int nativeDetectSurfaceType(Surface surface);
555
556 private static native int nativeDetectSurfaceDimens(Surface surface,
Igor Murashkina296fec2014-06-23 14:44:09 -0700557 /*out*/int[/*2*/] dimens);
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700558
Ruben Brunkef14da32014-06-24 16:06:54 -0700559 private static native int nativeConfigureSurface(Surface surface, int width, int height,
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700560 int pixelFormat);
561
Ruben Brunkef14da32014-06-24 16:06:54 -0700562 private static native int nativeProduceFrame(Surface surface, byte[] pixelBuffer, int width,
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700563 int height, int pixelFormat);
564
Ruben Brunkef14da32014-06-24 16:06:54 -0700565 private static native int nativeSetSurfaceFormat(Surface surface, int pixelFormat);
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700566
Ruben Brunkef14da32014-06-24 16:06:54 -0700567 private static native int nativeSetSurfaceDimens(Surface surface, int width, int height);
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700568
Ruben Brunk3c8fa3b2014-06-30 16:45:05 -0700569 private static native long nativeGetSurfaceId(Surface surface);
Ruben Brunk28c49c92014-06-16 18:43:59 -0700570
571 private static native int nativeSetSurfaceOrientation(Surface surface, int facing,
572 int sensorOrientation);
573
574 private static native int nativeDetectTextureDimens(SurfaceTexture surfaceTexture,
575 /*out*/int[/*2*/] dimens);
576
Ruben Brunk91838de2014-07-16 17:24:17 -0700577 private static native int nativeSetNextTimestamp(Surface surface, long timestamp);
Ruben Brunk1dc13262014-07-31 11:43:27 -0700578
579 static native int nativeGetJpegFooterSize();
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700580}