blob: cab9d7046fcdcfbcecbc0c2e51f4a43f2bd85901 [file] [log] [blame]
Igor Murashkin70725502013-06-25 20:27:06 +00001/*
2 * Copyright (C) 2013 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
Eino-Ville Talvala2f1a2e42013-07-25 17:12:05 -070017package android.hardware.camera2.impl;
Igor Murashkin70725502013-06-25 20:27:06 +000018
Svet Ganov82f09bc2018-01-12 22:08:40 -080019import static com.android.internal.util.function.pooled.PooledLambda.obtainRunnable;
Igor Murashkin57ea59b2013-08-23 16:55:57 -070020
Shuzhen Wang23d29382017-11-26 17:24:56 -080021import android.hardware.ICameraService;
Eino-Ville Talvala2f1a2e42013-07-25 17:12:05 -070022import android.hardware.camera2.CameraAccessException;
Eino-Ville Talvalacca00c62014-05-14 10:53:20 -070023import android.hardware.camera2.CameraCaptureSession;
Ruben Brunk57493682014-05-27 18:58:08 -070024import android.hardware.camera2.CameraCharacteristics;
Eino-Ville Talvalaa6b5ba52014-07-02 16:30:53 -070025import android.hardware.camera2.CameraDevice;
Shuzhen Wang23d29382017-11-26 17:24:56 -080026import android.hardware.camera2.CaptureFailure;
Eino-Ville Talvala2f1a2e42013-07-25 17:12:05 -070027import android.hardware.camera2.CaptureRequest;
Igor Murashkin57ea59b2013-08-23 16:55:57 -070028import android.hardware.camera2.CaptureResult;
29import android.hardware.camera2.ICameraDeviceCallbacks;
30import android.hardware.camera2.ICameraDeviceUser;
Igor Murashkindb075af2014-05-21 10:07:08 -070031import android.hardware.camera2.TotalCaptureResult;
Chien-Yu Chen5398a672015-03-19 14:48:43 -070032import android.hardware.camera2.params.InputConfiguration;
Yin-Chia Yeh49ea6ae2015-03-09 16:53:14 -070033import android.hardware.camera2.params.OutputConfiguration;
Emilian Peev75a55702017-11-07 16:09:59 +000034import android.hardware.camera2.params.SessionConfiguration;
Chien-Yu Chen5398a672015-03-19 14:48:43 -070035import android.hardware.camera2.params.StreamConfigurationMap;
Eino-Ville Talvala5d2d7782015-12-17 16:50:50 -080036import android.hardware.camera2.utils.SubmitInfo;
Zhijun Hea7677722015-06-01 16:36:06 -070037import android.hardware.camera2.utils.SurfaceUtils;
Chien-Yu Chen3d2b5fe2017-04-05 16:42:31 -070038import android.os.Build;
Eino-Ville Talvala4af73c22013-08-14 10:35:46 -070039import android.os.Handler;
Ruben Brunkdecfe952013-10-29 11:00:32 -070040import android.os.IBinder;
Eino-Ville Talvala4af73c22013-08-14 10:35:46 -070041import android.os.Looper;
Ruben Brunkdecfe952013-10-29 11:00:32 -070042import android.os.RemoteException;
Eino-Ville Talvala5d2d7782015-12-17 16:50:50 -080043import android.os.ServiceSpecificException;
Igor Murashkin70725502013-06-25 20:27:06 +000044import android.util.Log;
Zhijun Hea7677722015-06-01 16:36:06 -070045import android.util.Range;
Chien-Yu Chen5398a672015-03-19 14:48:43 -070046import android.util.Size;
Igor Murashkin57ea59b2013-08-23 16:55:57 -070047import android.util.SparseArray;
Igor Murashkin70725502013-06-25 20:27:06 +000048import android.view.Surface;
49
Jianing Weid2c3a822014-03-27 18:27:43 -070050import java.util.AbstractMap.SimpleEntry;
Igor Murashkin57ea59b2013-08-23 16:55:57 -070051import java.util.ArrayList;
Zhijun Hea7677722015-06-01 16:36:06 -070052import java.util.Collection;
Igor Murashkin1e854c52014-08-28 15:21:49 -070053import java.util.HashMap;
Igor Murashkin57ea59b2013-08-23 16:55:57 -070054import java.util.HashSet;
Ruben Brunkdecfe952013-10-29 11:00:32 -070055import java.util.Iterator;
Chien-Yu Chen5398a672015-03-19 14:48:43 -070056import java.util.LinkedList;
Shuzhen Wang23d29382017-11-26 17:24:56 -080057import java.util.List;
Emilian Peev2100ae72018-01-12 16:56:25 +000058import java.util.Set;
Chien-Yu Chen5398a672015-03-19 14:48:43 -070059import java.util.TreeMap;
Shuzhen Wang23d29382017-11-26 17:24:56 -080060import java.util.concurrent.atomic.AtomicBoolean;
Igor Murashkin70725502013-06-25 20:27:06 +000061
62/**
Eino-Ville Talvala70c22072013-08-27 12:09:04 -070063 * HAL2.1+ implementation of CameraDevice. Use CameraManager#open to instantiate
Igor Murashkin70725502013-06-25 20:27:06 +000064 */
Shuzhen Wang78392932016-03-04 15:26:01 -080065public class CameraDeviceImpl extends CameraDevice
66 implements IBinder.DeathRecipient {
Igor Murashkin70725502013-06-25 20:27:06 +000067 private final String TAG;
Eino-Ville Talvalaa78791f2015-06-01 12:39:54 -070068 private final boolean DEBUG = false;
Igor Murashkin70725502013-06-25 20:27:06 +000069
Ruben Brunkdecfe952013-10-29 11:00:32 -070070 private static final int REQUEST_ID_NONE = -1;
71
Igor Murashkin70725502013-06-25 20:27:06 +000072 // TODO: guard every function with if (!mRemoteDevice) check (if it was closed)
Eino-Ville Talvala5d2d7782015-12-17 16:50:50 -080073 private ICameraDeviceUserWrapper mRemoteDevice;
Igor Murashkin70725502013-06-25 20:27:06 +000074
Eino-Ville Talvala2f75e7d2014-07-15 10:31:54 -070075 // Lock to synchronize cross-thread access to device public interface
Igor Murashkin51dcfd652014-09-25 16:55:01 -070076 final Object mInterfaceLock = new Object(); // access from this class and Session only!
Igor Murashkin70725502013-06-25 20:27:06 +000077 private final CameraDeviceCallbacks mCallbacks = new CameraDeviceCallbacks();
78
Eino-Ville Talvalafd887432014-09-04 13:07:40 -070079 private final StateCallback mDeviceCallback;
80 private volatile StateCallbackKK mSessionStateCallback;
Eino-Ville Talvala868d9042013-10-03 11:15:21 -070081 private final Handler mDeviceHandler;
82
Ruben Brunk0953b642015-06-11 16:12:35 -070083 private final AtomicBoolean mClosing = new AtomicBoolean();
Eino-Ville Talvala7fcb35782014-06-03 18:30:27 -070084 private boolean mInError = false;
Eino-Ville Talvala868d9042013-10-03 11:15:21 -070085 private boolean mIdle = true;
Eino-Ville Talvala4af73c22013-08-14 10:35:46 -070086
Eino-Ville Talvalafd887432014-09-04 13:07:40 -070087 /** map request IDs to callback/request data */
88 private final SparseArray<CaptureCallbackHolder> mCaptureCallbackMap =
89 new SparseArray<CaptureCallbackHolder>();
Igor Murashkin70725502013-06-25 20:27:06 +000090
Ruben Brunkdecfe952013-10-29 11:00:32 -070091 private int mRepeatingRequestId = REQUEST_ID_NONE;
Chien-Yu Chen5398a672015-03-19 14:48:43 -070092 // Map stream IDs to input/output configurations
93 private SimpleEntry<Integer, InputConfiguration> mConfiguredInput =
94 new SimpleEntry<>(REQUEST_ID_NONE, null);
Yin-Chia Yehbfbbee72015-03-12 13:39:26 -070095 private final SparseArray<OutputConfiguration> mConfiguredOutputs =
Chien-Yu Chen5398a672015-03-19 14:48:43 -070096 new SparseArray<>();
Igor Murashkin70725502013-06-25 20:27:06 +000097
98 private final String mCameraId;
Ruben Brunk57493682014-05-27 18:58:08 -070099 private final CameraCharacteristics mCharacteristics;
Zhijun He83159152014-07-16 11:32:59 -0700100 private final int mTotalPartialCount;
Igor Murashkin70725502013-06-25 20:27:06 +0000101
Shuzhen Wangae10eb92017-03-01 12:58:04 -0800102 private static final long NANO_PER_SECOND = 1000000000; //ns
103
Jianing Weid2c3a822014-03-27 18:27:43 -0700104 /**
Chien-Yu Chen4560df72015-04-27 17:21:31 -0700105 * A list tracking request and its expected last regular frame number and last reprocess frame
106 * number. Updated when calling ICameraDeviceUser methods.
Jianing Weid2c3a822014-03-27 18:27:43 -0700107 */
Chien-Yu Chen4560df72015-04-27 17:21:31 -0700108 private final List<RequestLastFrameNumbersHolder> mRequestLastFrameNumbersList =
109 new ArrayList<>();
Jianing Weid2c3a822014-03-27 18:27:43 -0700110
111 /**
112 * An object tracking received frame numbers.
113 * Updated when receiving callbacks from ICameraDeviceCallbacks.
114 */
115 private final FrameNumberTracker mFrameNumberTracker = new FrameNumberTracker();
116
Eino-Ville Talvala639fffe2015-06-30 10:34:48 -0700117 private CameraCaptureSessionCore mCurrentSession;
Eino-Ville Talvala95037852014-09-14 14:07:05 -0700118 private int mNextSessionId = 0;
Igor Murashkin0b27d342014-05-30 09:45:05 -0700119
Chien-Yu Chen3d2b5fe2017-04-05 16:42:31 -0700120 private final int mAppTargetSdkVersion;
121
Eino-Ville Talvala868d9042013-10-03 11:15:21 -0700122 // Runnables for all state transitions, except error, which needs the
123 // error code argument
124
125 private final Runnable mCallOnOpened = new Runnable() {
Jianing Weid2c3a822014-03-27 18:27:43 -0700126 @Override
Eino-Ville Talvala868d9042013-10-03 11:15:21 -0700127 public void run() {
Eino-Ville Talvalafd887432014-09-04 13:07:40 -0700128 StateCallbackKK sessionCallback = null;
Eino-Ville Talvala2f75e7d2014-07-15 10:31:54 -0700129 synchronized(mInterfaceLock) {
130 if (mRemoteDevice == null) return; // Camera already closed
Igor Murashkin49b2b132014-06-18 19:03:00 -0700131
Eino-Ville Talvalafd887432014-09-04 13:07:40 -0700132 sessionCallback = mSessionStateCallback;
Eino-Ville Talvala868d9042013-10-03 11:15:21 -0700133 }
Eino-Ville Talvalafd887432014-09-04 13:07:40 -0700134 if (sessionCallback != null) {
135 sessionCallback.onOpened(CameraDeviceImpl.this);
Eino-Ville Talvala2f75e7d2014-07-15 10:31:54 -0700136 }
Eino-Ville Talvalafd887432014-09-04 13:07:40 -0700137 mDeviceCallback.onOpened(CameraDeviceImpl.this);
Eino-Ville Talvala868d9042013-10-03 11:15:21 -0700138 }
139 };
140
141 private final Runnable mCallOnUnconfigured = new Runnable() {
Jianing Weid2c3a822014-03-27 18:27:43 -0700142 @Override
Eino-Ville Talvala868d9042013-10-03 11:15:21 -0700143 public void run() {
Eino-Ville Talvalafd887432014-09-04 13:07:40 -0700144 StateCallbackKK sessionCallback = null;
Eino-Ville Talvala2f75e7d2014-07-15 10:31:54 -0700145 synchronized(mInterfaceLock) {
146 if (mRemoteDevice == null) return; // Camera already closed
Igor Murashkin49b2b132014-06-18 19:03:00 -0700147
Eino-Ville Talvalafd887432014-09-04 13:07:40 -0700148 sessionCallback = mSessionStateCallback;
Eino-Ville Talvala868d9042013-10-03 11:15:21 -0700149 }
Eino-Ville Talvalafd887432014-09-04 13:07:40 -0700150 if (sessionCallback != null) {
151 sessionCallback.onUnconfigured(CameraDeviceImpl.this);
Eino-Ville Talvala2f75e7d2014-07-15 10:31:54 -0700152 }
Eino-Ville Talvala868d9042013-10-03 11:15:21 -0700153 }
154 };
155
156 private final Runnable mCallOnActive = new Runnable() {
Jianing Weid2c3a822014-03-27 18:27:43 -0700157 @Override
Eino-Ville Talvala868d9042013-10-03 11:15:21 -0700158 public void run() {
Eino-Ville Talvalafd887432014-09-04 13:07:40 -0700159 StateCallbackKK sessionCallback = null;
Eino-Ville Talvala2f75e7d2014-07-15 10:31:54 -0700160 synchronized(mInterfaceLock) {
161 if (mRemoteDevice == null) return; // Camera already closed
Igor Murashkin49b2b132014-06-18 19:03:00 -0700162
Eino-Ville Talvalafd887432014-09-04 13:07:40 -0700163 sessionCallback = mSessionStateCallback;
Eino-Ville Talvala868d9042013-10-03 11:15:21 -0700164 }
Eino-Ville Talvalafd887432014-09-04 13:07:40 -0700165 if (sessionCallback != null) {
166 sessionCallback.onActive(CameraDeviceImpl.this);
Eino-Ville Talvala2f75e7d2014-07-15 10:31:54 -0700167 }
Eino-Ville Talvala868d9042013-10-03 11:15:21 -0700168 }
169 };
170
171 private final Runnable mCallOnBusy = new Runnable() {
Jianing Weid2c3a822014-03-27 18:27:43 -0700172 @Override
Eino-Ville Talvala868d9042013-10-03 11:15:21 -0700173 public void run() {
Eino-Ville Talvalafd887432014-09-04 13:07:40 -0700174 StateCallbackKK sessionCallback = null;
Eino-Ville Talvala2f75e7d2014-07-15 10:31:54 -0700175 synchronized(mInterfaceLock) {
176 if (mRemoteDevice == null) return; // Camera already closed
Igor Murashkin49b2b132014-06-18 19:03:00 -0700177
Eino-Ville Talvalafd887432014-09-04 13:07:40 -0700178 sessionCallback = mSessionStateCallback;
Eino-Ville Talvala868d9042013-10-03 11:15:21 -0700179 }
Eino-Ville Talvalafd887432014-09-04 13:07:40 -0700180 if (sessionCallback != null) {
181 sessionCallback.onBusy(CameraDeviceImpl.this);
Eino-Ville Talvala2f75e7d2014-07-15 10:31:54 -0700182 }
Eino-Ville Talvala868d9042013-10-03 11:15:21 -0700183 }
184 };
185
186 private final Runnable mCallOnClosed = new Runnable() {
Igor Murashkin49b2b132014-06-18 19:03:00 -0700187 private boolean mClosedOnce = false;
188
Jianing Weid2c3a822014-03-27 18:27:43 -0700189 @Override
Eino-Ville Talvala868d9042013-10-03 11:15:21 -0700190 public void run() {
Igor Murashkin49b2b132014-06-18 19:03:00 -0700191 if (mClosedOnce) {
192 throw new AssertionError("Don't post #onClosed more than once");
193 }
Eino-Ville Talvalafd887432014-09-04 13:07:40 -0700194 StateCallbackKK sessionCallback = null;
Eino-Ville Talvala2f75e7d2014-07-15 10:31:54 -0700195 synchronized(mInterfaceLock) {
Eino-Ville Talvalafd887432014-09-04 13:07:40 -0700196 sessionCallback = mSessionStateCallback;
Eino-Ville Talvala2f75e7d2014-07-15 10:31:54 -0700197 }
Eino-Ville Talvalafd887432014-09-04 13:07:40 -0700198 if (sessionCallback != null) {
199 sessionCallback.onClosed(CameraDeviceImpl.this);
Igor Murashkin0b27d342014-05-30 09:45:05 -0700200 }
Eino-Ville Talvalafd887432014-09-04 13:07:40 -0700201 mDeviceCallback.onClosed(CameraDeviceImpl.this);
Igor Murashkin49b2b132014-06-18 19:03:00 -0700202 mClosedOnce = true;
Eino-Ville Talvala868d9042013-10-03 11:15:21 -0700203 }
204 };
205
206 private final Runnable mCallOnIdle = new Runnable() {
Jianing Weid2c3a822014-03-27 18:27:43 -0700207 @Override
Eino-Ville Talvala868d9042013-10-03 11:15:21 -0700208 public void run() {
Eino-Ville Talvalafd887432014-09-04 13:07:40 -0700209 StateCallbackKK sessionCallback = null;
Eino-Ville Talvala2f75e7d2014-07-15 10:31:54 -0700210 synchronized(mInterfaceLock) {
211 if (mRemoteDevice == null) return; // Camera already closed
Igor Murashkin49b2b132014-06-18 19:03:00 -0700212
Eino-Ville Talvalafd887432014-09-04 13:07:40 -0700213 sessionCallback = mSessionStateCallback;
Eino-Ville Talvala868d9042013-10-03 11:15:21 -0700214 }
Eino-Ville Talvalafd887432014-09-04 13:07:40 -0700215 if (sessionCallback != null) {
216 sessionCallback.onIdle(CameraDeviceImpl.this);
Eino-Ville Talvala2f75e7d2014-07-15 10:31:54 -0700217 }
Eino-Ville Talvala868d9042013-10-03 11:15:21 -0700218 }
219 };
220
221 private final Runnable mCallOnDisconnected = new Runnable() {
Jianing Weid2c3a822014-03-27 18:27:43 -0700222 @Override
Eino-Ville Talvala868d9042013-10-03 11:15:21 -0700223 public void run() {
Eino-Ville Talvalafd887432014-09-04 13:07:40 -0700224 StateCallbackKK sessionCallback = null;
Eino-Ville Talvala2f75e7d2014-07-15 10:31:54 -0700225 synchronized(mInterfaceLock) {
226 if (mRemoteDevice == null) return; // Camera already closed
Igor Murashkin49b2b132014-06-18 19:03:00 -0700227
Eino-Ville Talvalafd887432014-09-04 13:07:40 -0700228 sessionCallback = mSessionStateCallback;
Eino-Ville Talvala868d9042013-10-03 11:15:21 -0700229 }
Eino-Ville Talvalafd887432014-09-04 13:07:40 -0700230 if (sessionCallback != null) {
231 sessionCallback.onDisconnected(CameraDeviceImpl.this);
Eino-Ville Talvala2f75e7d2014-07-15 10:31:54 -0700232 }
Eino-Ville Talvalafd887432014-09-04 13:07:40 -0700233 mDeviceCallback.onDisconnected(CameraDeviceImpl.this);
Eino-Ville Talvala868d9042013-10-03 11:15:21 -0700234 }
235 };
236
Eino-Ville Talvalafd887432014-09-04 13:07:40 -0700237 public CameraDeviceImpl(String cameraId, StateCallback callback, Handler handler,
Chien-Yu Chen3d2b5fe2017-04-05 16:42:31 -0700238 CameraCharacteristics characteristics, int appTargetSdkVersion) {
Eino-Ville Talvalafd887432014-09-04 13:07:40 -0700239 if (cameraId == null || callback == null || handler == null || characteristics == null) {
Eino-Ville Talvala868d9042013-10-03 11:15:21 -0700240 throw new IllegalArgumentException("Null argument given");
241 }
Igor Murashkin70725502013-06-25 20:27:06 +0000242 mCameraId = cameraId;
Eino-Ville Talvalafd887432014-09-04 13:07:40 -0700243 mDeviceCallback = callback;
Eino-Ville Talvala868d9042013-10-03 11:15:21 -0700244 mDeviceHandler = handler;
Ruben Brunk57493682014-05-27 18:58:08 -0700245 mCharacteristics = characteristics;
Chien-Yu Chen3d2b5fe2017-04-05 16:42:31 -0700246 mAppTargetSdkVersion = appTargetSdkVersion;
Eino-Ville Talvalab72d1ef2014-04-28 13:21:18 -0700247
248 final int MAX_TAG_LEN = 23;
249 String tag = String.format("CameraDevice-JV-%s", mCameraId);
250 if (tag.length() > MAX_TAG_LEN) {
251 tag = tag.substring(0, MAX_TAG_LEN);
252 }
253 TAG = tag;
Zhijun He83159152014-07-16 11:32:59 -0700254
255 Integer partialCount =
256 mCharacteristics.get(CameraCharacteristics.REQUEST_PARTIAL_RESULT_COUNT);
257 if (partialCount == null) {
258 // 1 means partial result is not supported.
259 mTotalPartialCount = 1;
260 } else {
261 mTotalPartialCount = partialCount;
262 }
Igor Murashkin70725502013-06-25 20:27:06 +0000263 }
264
265 public CameraDeviceCallbacks getCallbacks() {
266 return mCallbacks;
267 }
268
Shuzhen Wang78392932016-03-04 15:26:01 -0800269 /**
270 * Set remote device, which triggers initial onOpened/onUnconfigured callbacks
271 *
272 * <p>This function may post onDisconnected and throw CAMERA_DISCONNECTED if remoteDevice dies
273 * during setup.</p>
274 *
275 */
276 public void setRemoteDevice(ICameraDeviceUser remoteDevice) throws CameraAccessException {
Eino-Ville Talvala2f75e7d2014-07-15 10:31:54 -0700277 synchronized(mInterfaceLock) {
278 // TODO: Move from decorator to direct binder-mediated exceptions
Eino-Ville Talvala7fcb35782014-06-03 18:30:27 -0700279 // If setRemoteFailure already called, do nothing
280 if (mInError) return;
281
Eino-Ville Talvala5d2d7782015-12-17 16:50:50 -0800282 mRemoteDevice = new ICameraDeviceUserWrapper(remoteDevice);
Eino-Ville Talvala868d9042013-10-03 11:15:21 -0700283
Shuzhen Wang78392932016-03-04 15:26:01 -0800284 IBinder remoteDeviceBinder = remoteDevice.asBinder();
285 // For legacy camera device, remoteDevice is in the same process, and
286 // asBinder returns NULL.
287 if (remoteDeviceBinder != null) {
288 try {
289 remoteDeviceBinder.linkToDeath(this, /*flag*/ 0);
290 } catch (RemoteException e) {
291 CameraDeviceImpl.this.mDeviceHandler.post(mCallOnDisconnected);
292
293 throw new CameraAccessException(CameraAccessException.CAMERA_DISCONNECTED,
294 "The camera device has encountered a serious error");
295 }
296 }
297
Eino-Ville Talvala868d9042013-10-03 11:15:21 -0700298 mDeviceHandler.post(mCallOnOpened);
299 mDeviceHandler.post(mCallOnUnconfigured);
300 }
Igor Murashkin70725502013-06-25 20:27:06 +0000301 }
302
Eino-Ville Talvala7fcb35782014-06-03 18:30:27 -0700303 /**
304 * Call to indicate failed connection to a remote camera device.
305 *
Eino-Ville Talvalafd887432014-09-04 13:07:40 -0700306 * <p>This places the camera device in the error state and informs the callback.
Eino-Ville Talvala7fcb35782014-06-03 18:30:27 -0700307 * Use in place of setRemoteDevice() when startup fails.</p>
308 */
Eino-Ville Talvala5d2d7782015-12-17 16:50:50 -0800309 public void setRemoteFailure(final ServiceSpecificException failure) {
Eino-Ville Talvalafd887432014-09-04 13:07:40 -0700310 int failureCode = StateCallback.ERROR_CAMERA_DEVICE;
Eino-Ville Talvala7fcb35782014-06-03 18:30:27 -0700311 boolean failureIsError = true;
312
Eino-Ville Talvala5d2d7782015-12-17 16:50:50 -0800313 switch (failure.errorCode) {
314 case ICameraService.ERROR_CAMERA_IN_USE:
Eino-Ville Talvalafd887432014-09-04 13:07:40 -0700315 failureCode = StateCallback.ERROR_CAMERA_IN_USE;
Eino-Ville Talvala7fcb35782014-06-03 18:30:27 -0700316 break;
Eino-Ville Talvala5d2d7782015-12-17 16:50:50 -0800317 case ICameraService.ERROR_MAX_CAMERAS_IN_USE:
Eino-Ville Talvalafd887432014-09-04 13:07:40 -0700318 failureCode = StateCallback.ERROR_MAX_CAMERAS_IN_USE;
Eino-Ville Talvala7fcb35782014-06-03 18:30:27 -0700319 break;
Eino-Ville Talvala5d2d7782015-12-17 16:50:50 -0800320 case ICameraService.ERROR_DISABLED:
Eino-Ville Talvalafd887432014-09-04 13:07:40 -0700321 failureCode = StateCallback.ERROR_CAMERA_DISABLED;
Eino-Ville Talvala7fcb35782014-06-03 18:30:27 -0700322 break;
Eino-Ville Talvala5d2d7782015-12-17 16:50:50 -0800323 case ICameraService.ERROR_DISCONNECTED:
Eino-Ville Talvala7fcb35782014-06-03 18:30:27 -0700324 failureIsError = false;
325 break;
Eino-Ville Talvala5d2d7782015-12-17 16:50:50 -0800326 case ICameraService.ERROR_INVALID_OPERATION:
Eino-Ville Talvalafd887432014-09-04 13:07:40 -0700327 failureCode = StateCallback.ERROR_CAMERA_DEVICE;
Eino-Ville Talvala7fcb35782014-06-03 18:30:27 -0700328 break;
329 default:
Eino-Ville Talvala5d2d7782015-12-17 16:50:50 -0800330 Log.e(TAG, "Unexpected failure in opening camera device: " + failure.errorCode +
331 failure.getMessage());
Eino-Ville Talvala7fcb35782014-06-03 18:30:27 -0700332 break;
333 }
334 final int code = failureCode;
335 final boolean isError = failureIsError;
Eino-Ville Talvala2f75e7d2014-07-15 10:31:54 -0700336 synchronized(mInterfaceLock) {
Eino-Ville Talvala7fcb35782014-06-03 18:30:27 -0700337 mInError = true;
338 mDeviceHandler.post(new Runnable() {
Igor Murashkine1442202014-06-09 17:51:24 -0700339 @Override
Eino-Ville Talvala7fcb35782014-06-03 18:30:27 -0700340 public void run() {
341 if (isError) {
Eino-Ville Talvalafd887432014-09-04 13:07:40 -0700342 mDeviceCallback.onError(CameraDeviceImpl.this, code);
Eino-Ville Talvala7fcb35782014-06-03 18:30:27 -0700343 } else {
Eino-Ville Talvalafd887432014-09-04 13:07:40 -0700344 mDeviceCallback.onDisconnected(CameraDeviceImpl.this);
Eino-Ville Talvala7fcb35782014-06-03 18:30:27 -0700345 }
346 }
347 });
348 }
349 }
350
Igor Murashkin70725502013-06-25 20:27:06 +0000351 @Override
Eino-Ville Talvala4af73c22013-08-14 10:35:46 -0700352 public String getId() {
353 return mCameraId;
354 }
355
Igor Murashkin70725502013-06-25 20:27:06 +0000356 public void configureOutputs(List<Surface> outputs) throws CameraAccessException {
Igor Murashkin10fbbbb2014-08-19 16:19:30 -0700357 // Leave this here for backwards compatibility with older code using this directly
Yin-Chia Yehbfbbee72015-03-12 13:39:26 -0700358 ArrayList<OutputConfiguration> outputConfigs = new ArrayList<>(outputs.size());
359 for (Surface s : outputs) {
360 outputConfigs.add(new OutputConfiguration(s));
361 }
Zhijun Hea7677722015-06-01 16:36:06 -0700362 configureStreamsChecked(/*inputConfig*/null, outputConfigs,
Emilian Peev75a55702017-11-07 16:09:59 +0000363 /*operatingMode*/ICameraDeviceUser.NORMAL_MODE, /*sessionParams*/ null);
Chien-Yu Chen5398a672015-03-19 14:48:43 -0700364
Igor Murashkin10fbbbb2014-08-19 16:19:30 -0700365 }
366
367 /**
Chien-Yu Chen5398a672015-03-19 14:48:43 -0700368 * Attempt to configure the input and outputs; the device goes to idle and then configures the
369 * new input and outputs if possible.
Igor Murashkin10fbbbb2014-08-19 16:19:30 -0700370 *
Chien-Yu Chen5398a672015-03-19 14:48:43 -0700371 * <p>The configuration may gracefully fail, if input configuration is not supported,
372 * if there are too many outputs, if the formats are not supported, or if the sizes for that
373 * format is not supported. In this case this function will return {@code false} and the
374 * unconfigured callback will be fired.</p>
Igor Murashkin10fbbbb2014-08-19 16:19:30 -0700375 *
Chien-Yu Chen5398a672015-03-19 14:48:43 -0700376 * <p>If the configuration succeeds (with 1 or more outputs with or without an input),
377 * then the idle callback is fired. Unconfiguring the device always fires the idle callback.</p>
Igor Murashkin10fbbbb2014-08-19 16:19:30 -0700378 *
Chien-Yu Chen5398a672015-03-19 14:48:43 -0700379 * @param inputConfig input configuration or {@code null} for no input
Igor Murashkin10fbbbb2014-08-19 16:19:30 -0700380 * @param outputs a list of one or more surfaces, or {@code null} to unconfigure
Eino-Ville Talvala0e04e912017-02-28 17:49:41 -0800381 * @param operatingMode If the stream configuration is for a normal session,
382 * a constrained high speed session, or something else.
Emilian Peev75a55702017-11-07 16:09:59 +0000383 * @param sessionParams Session parameters.
Igor Murashkin10fbbbb2014-08-19 16:19:30 -0700384 * @return whether or not the configuration was successful
385 *
386 * @throws CameraAccessException if there were any unexpected problems during configuration
387 */
Chien-Yu Chen5398a672015-03-19 14:48:43 -0700388 public boolean configureStreamsChecked(InputConfiguration inputConfig,
Emilian Peev75a55702017-11-07 16:09:59 +0000389 List<OutputConfiguration> outputs, int operatingMode, CaptureRequest sessionParams)
Zhijun Hea7677722015-06-01 16:36:06 -0700390 throws CameraAccessException {
Eino-Ville Talvala868d9042013-10-03 11:15:21 -0700391 // Treat a null input the same an empty list
392 if (outputs == null) {
Yin-Chia Yehbfbbee72015-03-12 13:39:26 -0700393 outputs = new ArrayList<OutputConfiguration>();
Eino-Ville Talvala868d9042013-10-03 11:15:21 -0700394 }
Chien-Yu Chen5398a672015-03-19 14:48:43 -0700395 if (outputs.size() == 0 && inputConfig != null) {
396 throw new IllegalArgumentException("cannot configure an input stream without " +
397 "any output streams");
398 }
399
400 checkInputConfiguration(inputConfig);
401
Igor Murashkin10fbbbb2014-08-19 16:19:30 -0700402 boolean success = false;
403
Eino-Ville Talvala2f75e7d2014-07-15 10:31:54 -0700404 synchronized(mInterfaceLock) {
Eino-Ville Talvala7fcb35782014-06-03 18:30:27 -0700405 checkIfCameraClosedOrInError();
Yin-Chia Yehbfbbee72015-03-12 13:39:26 -0700406 // Streams to create
407 HashSet<OutputConfiguration> addSet = new HashSet<OutputConfiguration>(outputs);
Chien-Yu Chen5398a672015-03-19 14:48:43 -0700408 // Streams to delete
Yin-Chia Yehbfbbee72015-03-12 13:39:26 -0700409 List<Integer> deleteList = new ArrayList<Integer>();
Igor Murashkin57ea59b2013-08-23 16:55:57 -0700410
411 // Determine which streams need to be created, which to be deleted
412 for (int i = 0; i < mConfiguredOutputs.size(); ++i) {
413 int streamId = mConfiguredOutputs.keyAt(i);
Yin-Chia Yehbfbbee72015-03-12 13:39:26 -0700414 OutputConfiguration outConfig = mConfiguredOutputs.valueAt(i);
Igor Murashkin57ea59b2013-08-23 16:55:57 -0700415
Zhijun Hec8b181e2016-05-30 14:54:39 -0700416 if (!outputs.contains(outConfig) || outConfig.isDeferredConfiguration()) {
417 // Always delete the deferred output configuration when the session
418 // is created, as the deferred output configuration doesn't have unique surface
419 // related identifies.
Igor Murashkin57ea59b2013-08-23 16:55:57 -0700420 deleteList.add(streamId);
421 } else {
Yin-Chia Yehbfbbee72015-03-12 13:39:26 -0700422 addSet.remove(outConfig); // Don't create a stream previously created
Igor Murashkin57ea59b2013-08-23 16:55:57 -0700423 }
424 }
425
Eino-Ville Talvala868d9042013-10-03 11:15:21 -0700426 mDeviceHandler.post(mCallOnBusy);
427 stopRepeating();
Igor Murashkin57ea59b2013-08-23 16:55:57 -0700428
Eino-Ville Talvala868d9042013-10-03 11:15:21 -0700429 try {
Ruben Brunkdecfe952013-10-29 11:00:32 -0700430 waitUntilIdle();
Eino-Ville Talvala868d9042013-10-03 11:15:21 -0700431
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700432 mRemoteDevice.beginConfigure();
Chien-Yu Chen5398a672015-03-19 14:48:43 -0700433
434 // reconfigure the input stream if the input configuration is different.
435 InputConfiguration currentInputConfig = mConfiguredInput.getValue();
436 if (inputConfig != currentInputConfig &&
437 (inputConfig == null || !inputConfig.equals(currentInputConfig))) {
438 if (currentInputConfig != null) {
439 mRemoteDevice.deleteStream(mConfiguredInput.getKey());
440 mConfiguredInput = new SimpleEntry<Integer, InputConfiguration>(
441 REQUEST_ID_NONE, null);
442 }
443 if (inputConfig != null) {
444 int streamId = mRemoteDevice.createInputStream(inputConfig.getWidth(),
445 inputConfig.getHeight(), inputConfig.getFormat());
446 mConfiguredInput = new SimpleEntry<Integer, InputConfiguration>(
447 streamId, inputConfig);
448 }
449 }
450
Igor Murashkin57ea59b2013-08-23 16:55:57 -0700451 // Delete all streams first (to free up HW resources)
452 for (Integer streamId : deleteList) {
453 mRemoteDevice.deleteStream(streamId);
454 mConfiguredOutputs.delete(streamId);
455 }
456
457 // Add all new streams
Yin-Chia Yehbfbbee72015-03-12 13:39:26 -0700458 for (OutputConfiguration outConfig : outputs) {
459 if (addSet.contains(outConfig)) {
460 int streamId = mRemoteDevice.createStream(outConfig);
461 mConfiguredOutputs.put(streamId, outConfig);
462 }
Igor Murashkin70725502013-06-25 20:27:06 +0000463 }
Igor Murashkin57ea59b2013-08-23 16:55:57 -0700464
Emilian Peev75a55702017-11-07 16:09:59 +0000465 if (sessionParams != null) {
466 mRemoteDevice.endConfigure(operatingMode, sessionParams.getNativeCopy());
467 } else {
468 mRemoteDevice.endConfigure(operatingMode, null);
469 }
Igor Murashkin10fbbbb2014-08-19 16:19:30 -0700470
471 success = true;
Eino-Ville Talvala5d2d7782015-12-17 16:50:50 -0800472 } catch (IllegalArgumentException e) {
473 // OK. camera service can reject stream config if it's not supported by HAL
474 // This is only the result of a programmer misusing the camera2 api.
475 Log.w(TAG, "Stream configuration failed due to: " + e.getMessage());
Igor Murashkin10fbbbb2014-08-19 16:19:30 -0700476 return false;
Eino-Ville Talvala5d2d7782015-12-17 16:50:50 -0800477 } catch (CameraAccessException e) {
478 if (e.getReason() == CameraAccessException.CAMERA_IN_USE) {
479 throw new IllegalStateException("The camera is currently busy." +
480 " You must wait until the previous operation completes.", e);
481 }
482 throw e;
Igor Murashkin10fbbbb2014-08-19 16:19:30 -0700483 } finally {
484 if (success && outputs.size() > 0) {
485 mDeviceHandler.post(mCallOnIdle);
486 } else {
487 // Always return to the 'unconfigured' state if we didn't hit a fatal error
488 mDeviceHandler.post(mCallOnUnconfigured);
489 }
Eino-Ville Talvala868d9042013-10-03 11:15:21 -0700490 }
Igor Murashkin70725502013-06-25 20:27:06 +0000491 }
Igor Murashkin10fbbbb2014-08-19 16:19:30 -0700492
493 return success;
Igor Murashkin70725502013-06-25 20:27:06 +0000494 }
495
496 @Override
Eino-Ville Talvalacca00c62014-05-14 10:53:20 -0700497 public void createCaptureSession(List<Surface> outputs,
Eino-Ville Talvalafd887432014-09-04 13:07:40 -0700498 CameraCaptureSession.StateCallback callback, Handler handler)
Eino-Ville Talvalacca00c62014-05-14 10:53:20 -0700499 throws CameraAccessException {
Yin-Chia Yeh49ea6ae2015-03-09 16:53:14 -0700500 List<OutputConfiguration> outConfigurations = new ArrayList<>(outputs.size());
501 for (Surface surface : outputs) {
502 outConfigurations.add(new OutputConfiguration(surface));
503 }
Zhijun Hea7677722015-06-01 16:36:06 -0700504 createCaptureSessionInternal(null, outConfigurations, callback, handler,
Emilian Peev75a55702017-11-07 16:09:59 +0000505 /*operatingMode*/ICameraDeviceUser.NORMAL_MODE, /*sessionParams*/ null);
Yin-Chia Yeh49ea6ae2015-03-09 16:53:14 -0700506 }
507
508 @Override
Zhijun He00347432016-04-07 17:34:10 -0700509 public void createCaptureSessionByOutputConfigurations(
Yin-Chia Yeh49ea6ae2015-03-09 16:53:14 -0700510 List<OutputConfiguration> outputConfigurations,
511 CameraCaptureSession.StateCallback callback, Handler handler)
512 throws CameraAccessException {
Chien-Yu Chen5398a672015-03-19 14:48:43 -0700513 if (DEBUG) {
Shuzhen Wang9c663d42016-10-28 23:25:02 -0700514 Log.d(TAG, "createCaptureSessionByOutputConfigurations");
Chien-Yu Chen5398a672015-03-19 14:48:43 -0700515 }
516
Eino-Ville Talvalafa5e2a12016-04-11 12:49:17 -0700517 // OutputConfiguration objects are immutable, but need to have our own array
518 List<OutputConfiguration> currentOutputs = new ArrayList<>(outputConfigurations);
Zhijun He445b3162016-01-18 15:34:28 -0800519
520 createCaptureSessionInternal(null, currentOutputs, callback, handler,
Emilian Peev75a55702017-11-07 16:09:59 +0000521 /*operatingMode*/ICameraDeviceUser.NORMAL_MODE, /*sessionParams*/null);
Chien-Yu Chen5398a672015-03-19 14:48:43 -0700522 }
523
524 @Override
Chien-Yu Chen8062d312015-05-12 14:24:10 -0700525 public void createReprocessableCaptureSession(InputConfiguration inputConfig,
Chien-Yu Chen5398a672015-03-19 14:48:43 -0700526 List<Surface> outputs, CameraCaptureSession.StateCallback callback, Handler handler)
527 throws CameraAccessException {
528 if (DEBUG) {
Chien-Yu Chen8062d312015-05-12 14:24:10 -0700529 Log.d(TAG, "createReprocessableCaptureSession");
Chien-Yu Chen5398a672015-03-19 14:48:43 -0700530 }
531
532 if (inputConfig == null) {
533 throw new IllegalArgumentException("inputConfig cannot be null when creating a " +
Chien-Yu Chen8062d312015-05-12 14:24:10 -0700534 "reprocessable capture session");
Chien-Yu Chen5398a672015-03-19 14:48:43 -0700535 }
536 List<OutputConfiguration> outConfigurations = new ArrayList<>(outputs.size());
537 for (Surface surface : outputs) {
538 outConfigurations.add(new OutputConfiguration(surface));
539 }
Zhijun Hea7677722015-06-01 16:36:06 -0700540 createCaptureSessionInternal(inputConfig, outConfigurations, callback, handler,
Emilian Peev75a55702017-11-07 16:09:59 +0000541 /*operatingMode*/ICameraDeviceUser.NORMAL_MODE, /*sessionParams*/ null);
Chien-Yu Chen5398a672015-03-19 14:48:43 -0700542 }
543
Eino-Ville Talvala639fffe2015-06-30 10:34:48 -0700544 @Override
Zhijun He00347432016-04-07 17:34:10 -0700545 public void createReprocessableCaptureSessionByConfigurations(InputConfiguration inputConfig,
Zhijun He445b3162016-01-18 15:34:28 -0800546 List<OutputConfiguration> outputs,
547 android.hardware.camera2.CameraCaptureSession.StateCallback callback, Handler handler)
548 throws CameraAccessException {
549 if (DEBUG) {
550 Log.d(TAG, "createReprocessableCaptureSessionWithConfigurations");
551 }
552
553 if (inputConfig == null) {
554 throw new IllegalArgumentException("inputConfig cannot be null when creating a " +
555 "reprocessable capture session");
556 }
557
558 if (outputs == null) {
559 throw new IllegalArgumentException("Output configurations cannot be null when " +
560 "creating a reprocessable capture session");
561 }
562
563 // OutputConfiguration objects aren't immutable, make a copy before using.
564 List<OutputConfiguration> currentOutputs = new ArrayList<OutputConfiguration>();
565 for (OutputConfiguration output : outputs) {
566 currentOutputs.add(new OutputConfiguration(output));
567 }
568 createCaptureSessionInternal(inputConfig, currentOutputs,
Emilian Peev75a55702017-11-07 16:09:59 +0000569 callback, handler, /*operatingMode*/ICameraDeviceUser.NORMAL_MODE,
570 /*sessionParams*/ null);
Zhijun He445b3162016-01-18 15:34:28 -0800571 }
572
573 @Override
Eino-Ville Talvala639fffe2015-06-30 10:34:48 -0700574 public void createConstrainedHighSpeedCaptureSession(List<Surface> outputs,
575 android.hardware.camera2.CameraCaptureSession.StateCallback callback, Handler handler)
576 throws CameraAccessException {
577 if (outputs == null || outputs.size() == 0 || outputs.size() > 2) {
578 throw new IllegalArgumentException(
579 "Output surface list must not be null and the size must be no more than 2");
580 }
Eino-Ville Talvala639fffe2015-06-30 10:34:48 -0700581 List<OutputConfiguration> outConfigurations = new ArrayList<>(outputs.size());
582 for (Surface surface : outputs) {
583 outConfigurations.add(new OutputConfiguration(surface));
584 }
585 createCaptureSessionInternal(null, outConfigurations, callback, handler,
Emilian Peev75a55702017-11-07 16:09:59 +0000586 /*operatingMode*/ICameraDeviceUser.CONSTRAINED_HIGH_SPEED_MODE,
587 /*sessionParams*/ null);
Eino-Ville Talvala0e04e912017-02-28 17:49:41 -0800588 }
589
590 @Override
591 public void createCustomCaptureSession(InputConfiguration inputConfig,
592 List<OutputConfiguration> outputs,
593 int operatingMode,
594 android.hardware.camera2.CameraCaptureSession.StateCallback callback,
595 Handler handler) throws CameraAccessException {
596 List<OutputConfiguration> currentOutputs = new ArrayList<OutputConfiguration>();
597 for (OutputConfiguration output : outputs) {
598 currentOutputs.add(new OutputConfiguration(output));
599 }
Emilian Peev75a55702017-11-07 16:09:59 +0000600 createCaptureSessionInternal(inputConfig, currentOutputs, callback, handler, operatingMode,
601 /*sessionParams*/ null);
602 }
603
604 @Override
605 public void createCaptureSession(SessionConfiguration config)
606 throws CameraAccessException {
607 if (config == null) {
608 throw new IllegalArgumentException("Invalid session configuration");
609 }
610
611 List<OutputConfiguration> outputConfigs = config.getOutputConfigurations();
612 if (outputConfigs == null) {
613 throw new IllegalArgumentException("Invalid output configurations");
614 }
615 createCaptureSessionInternal(config.getInputConfiguration(), outputConfigs,
616 config.getStateCallback(), config.getHandler(), config.getSessionType(),
617 config.getSessionParameters());
Eino-Ville Talvala639fffe2015-06-30 10:34:48 -0700618 }
619
Chien-Yu Chen5398a672015-03-19 14:48:43 -0700620 private void createCaptureSessionInternal(InputConfiguration inputConfig,
621 List<OutputConfiguration> outputConfigurations,
Zhijun Hea7677722015-06-01 16:36:06 -0700622 CameraCaptureSession.StateCallback callback, Handler handler,
Emilian Peev75a55702017-11-07 16:09:59 +0000623 int operatingMode, CaptureRequest sessionParams) throws CameraAccessException {
Eino-Ville Talvala2f75e7d2014-07-15 10:31:54 -0700624 synchronized(mInterfaceLock) {
Igor Murashkin0b27d342014-05-30 09:45:05 -0700625 if (DEBUG) {
Chien-Yu Chen5398a672015-03-19 14:48:43 -0700626 Log.d(TAG, "createCaptureSessionInternal");
Igor Murashkin0b27d342014-05-30 09:45:05 -0700627 }
628
Eino-Ville Talvala7fcb35782014-06-03 18:30:27 -0700629 checkIfCameraClosedOrInError();
Igor Murashkin0b27d342014-05-30 09:45:05 -0700630
Eino-Ville Talvala0e04e912017-02-28 17:49:41 -0800631 boolean isConstrainedHighSpeed =
632 (operatingMode == ICameraDeviceUser.CONSTRAINED_HIGH_SPEED_MODE);
Zhijun Hea7677722015-06-01 16:36:06 -0700633 if (isConstrainedHighSpeed && inputConfig != null) {
634 throw new IllegalArgumentException("Constrained high speed session doesn't support"
635 + " input configuration yet.");
636 }
637
Eino-Ville Talvalad3b85f62014-08-05 15:02:31 -0700638 // Notify current session that it's going away, before starting camera operations
639 // After this call completes, the session is not allowed to call into CameraDeviceImpl
640 if (mCurrentSession != null) {
641 mCurrentSession.replaceSessionClose();
642 }
Igor Murashkin0b27d342014-05-30 09:45:05 -0700643
644 // TODO: dont block for this
645 boolean configureSuccess = true;
646 CameraAccessException pendingException = null;
Chien-Yu Chen5398a672015-03-19 14:48:43 -0700647 Surface input = null;
Igor Murashkin0b27d342014-05-30 09:45:05 -0700648 try {
Chien-Yu Chenc58a6022015-06-16 11:13:48 -0700649 // configure streams and then block until IDLE
Zhijun Hea7677722015-06-01 16:36:06 -0700650 configureSuccess = configureStreamsChecked(inputConfig, outputConfigurations,
Emilian Peev75a55702017-11-07 16:09:59 +0000651 operatingMode, sessionParams);
Chien-Yu Chenc58a6022015-06-16 11:13:48 -0700652 if (configureSuccess == true && inputConfig != null) {
Eino-Ville Talvala5d2d7782015-12-17 16:50:50 -0800653 input = mRemoteDevice.getInputSurface();
Chien-Yu Chen5398a672015-03-19 14:48:43 -0700654 }
Igor Murashkin0b27d342014-05-30 09:45:05 -0700655 } catch (CameraAccessException e) {
656 configureSuccess = false;
657 pendingException = e;
Chien-Yu Chen5398a672015-03-19 14:48:43 -0700658 input = null;
Igor Murashkine1442202014-06-09 17:51:24 -0700659 if (DEBUG) {
660 Log.v(TAG, "createCaptureSession - failed with exception ", e);
661 }
Igor Murashkin0b27d342014-05-30 09:45:05 -0700662 }
663
664 // Fire onConfigured if configureOutputs succeeded, fire onConfigureFailed otherwise.
Eino-Ville Talvala639fffe2015-06-30 10:34:48 -0700665 CameraCaptureSessionCore newSession = null;
666 if (isConstrainedHighSpeed) {
Emilian Peev75a55702017-11-07 16:09:59 +0000667 ArrayList<Surface> surfaces = new ArrayList<>(outputConfigurations.size());
668 for (OutputConfiguration outConfig : outputConfigurations) {
669 surfaces.add(outConfig.getSurface());
670 }
671 StreamConfigurationMap config =
672 getCharacteristics().get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
673 SurfaceUtils.checkConstrainedHighSpeedSurfaces(surfaces, /*fpsRange*/null, config);
674
Eino-Ville Talvala639fffe2015-06-30 10:34:48 -0700675 newSession = new CameraConstrainedHighSpeedCaptureSessionImpl(mNextSessionId++,
Shuzhen Wang9c663d42016-10-28 23:25:02 -0700676 callback, handler, this, mDeviceHandler, configureSuccess,
Eino-Ville Talvala639fffe2015-06-30 10:34:48 -0700677 mCharacteristics);
678 } else {
679 newSession = new CameraCaptureSessionImpl(mNextSessionId++, input,
Shuzhen Wang9c663d42016-10-28 23:25:02 -0700680 callback, handler, this, mDeviceHandler,
Eino-Ville Talvala639fffe2015-06-30 10:34:48 -0700681 configureSuccess);
682 }
Igor Murashkin0b27d342014-05-30 09:45:05 -0700683
Igor Murashkin0b27d342014-05-30 09:45:05 -0700684 // TODO: wait until current session closes, then create the new session
685 mCurrentSession = newSession;
686
687 if (pendingException != null) {
688 throw pendingException;
689 }
690
Eino-Ville Talvalafd887432014-09-04 13:07:40 -0700691 mSessionStateCallback = mCurrentSession.getDeviceStateCallback();
Igor Murashkin0b27d342014-05-30 09:45:05 -0700692 }
Eino-Ville Talvalacca00c62014-05-14 10:53:20 -0700693 }
694
Eino-Ville Talvalaa6b5ba52014-07-02 16:30:53 -0700695 /**
696 * For use by backwards-compatibility code only.
697 */
Eino-Ville Talvalafd887432014-09-04 13:07:40 -0700698 public void setSessionListener(StateCallbackKK sessionCallback) {
Eino-Ville Talvalaa6b5ba52014-07-02 16:30:53 -0700699 synchronized(mInterfaceLock) {
Eino-Ville Talvalafd887432014-09-04 13:07:40 -0700700 mSessionStateCallback = sessionCallback;
Eino-Ville Talvalaa6b5ba52014-07-02 16:30:53 -0700701 }
702 }
703
Chien-Yu Chen3d2b5fe2017-04-05 16:42:31 -0700704 private void overrideEnableZsl(CameraMetadataNative request, boolean newValue) {
705 Boolean enableZsl = request.get(CaptureRequest.CONTROL_ENABLE_ZSL);
706 if (enableZsl == null) {
707 // If enableZsl is not available, don't override.
708 return;
709 }
710
711 request.set(CaptureRequest.CONTROL_ENABLE_ZSL, newValue);
712 }
713
Eino-Ville Talvalacca00c62014-05-14 10:53:20 -0700714 @Override
Emilian Peev2100ae72018-01-12 16:56:25 +0000715 public CaptureRequest.Builder createCaptureRequest(int templateType,
716 Set<String> physicalCameraIdSet)
717 throws CameraAccessException {
718 synchronized(mInterfaceLock) {
719 checkIfCameraClosedOrInError();
720
721 for (String physicalId : physicalCameraIdSet) {
722 if (physicalId == getId()) {
723 throw new IllegalStateException("Physical id matches the logical id!");
724 }
725 }
726
727 CameraMetadataNative templatedRequest = null;
728
729 templatedRequest = mRemoteDevice.createDefaultRequest(templateType);
730
731 // If app target SDK is older than O, or it's not a still capture template, enableZsl
732 // must be false in the default request.
733 if (mAppTargetSdkVersion < Build.VERSION_CODES.O ||
734 templateType != TEMPLATE_STILL_CAPTURE) {
735 overrideEnableZsl(templatedRequest, false);
736 }
737
738 CaptureRequest.Builder builder = new CaptureRequest.Builder(
739 templatedRequest, /*reprocess*/false, CameraCaptureSession.SESSION_ID_NONE,
740 getId(), physicalCameraIdSet);
741
742 return builder;
743 }
744 }
745
746 @Override
Eino-Ville Talvala70c22072013-08-27 12:09:04 -0700747 public CaptureRequest.Builder createCaptureRequest(int templateType)
748 throws CameraAccessException {
Eino-Ville Talvala2f75e7d2014-07-15 10:31:54 -0700749 synchronized(mInterfaceLock) {
Eino-Ville Talvala7fcb35782014-06-03 18:30:27 -0700750 checkIfCameraClosedOrInError();
Igor Murashkin70725502013-06-25 20:27:06 +0000751
Eino-Ville Talvala5d2d7782015-12-17 16:50:50 -0800752 CameraMetadataNative templatedRequest = null;
Igor Murashkin70725502013-06-25 20:27:06 +0000753
Eino-Ville Talvala5d2d7782015-12-17 16:50:50 -0800754 templatedRequest = mRemoteDevice.createDefaultRequest(templateType);
Igor Murashkin70725502013-06-25 20:27:06 +0000755
Chien-Yu Chen3d2b5fe2017-04-05 16:42:31 -0700756 // If app target SDK is older than O, or it's not a still capture template, enableZsl
757 // must be false in the default request.
758 if (mAppTargetSdkVersion < Build.VERSION_CODES.O ||
759 templateType != TEMPLATE_STILL_CAPTURE) {
760 overrideEnableZsl(templatedRequest, false);
761 }
762
Chien-Yu Chen7a316f6b2015-04-13 14:31:45 -0700763 CaptureRequest.Builder builder = new CaptureRequest.Builder(
Emilian Peev2100ae72018-01-12 16:56:25 +0000764 templatedRequest, /*reprocess*/false, CameraCaptureSession.SESSION_ID_NONE,
765 getId(), /*physicalCameraIdSet*/ null);
Igor Murashkin70725502013-06-25 20:27:06 +0000766
Eino-Ville Talvala70c22072013-08-27 12:09:04 -0700767 return builder;
Igor Murashkin70725502013-06-25 20:27:06 +0000768 }
769 }
770
Chien-Yu Chen5398a672015-03-19 14:48:43 -0700771 @Override
772 public CaptureRequest.Builder createReprocessCaptureRequest(TotalCaptureResult inputResult)
773 throws CameraAccessException {
774 synchronized(mInterfaceLock) {
775 checkIfCameraClosedOrInError();
776
777 CameraMetadataNative resultMetadata = new
778 CameraMetadataNative(inputResult.getNativeCopy());
779
Chien-Yu Chen7a316f6b2015-04-13 14:31:45 -0700780 return new CaptureRequest.Builder(resultMetadata, /*reprocess*/true,
Emilian Peev2100ae72018-01-12 16:56:25 +0000781 inputResult.getSessionId(), getId(), /*physicalCameraIdSet*/ null);
Chien-Yu Chen5398a672015-03-19 14:48:43 -0700782 }
783 }
784
Eino-Ville Talvalaad916f72015-04-11 12:09:11 -0700785 public void prepare(Surface surface) throws CameraAccessException {
Eino-Ville Talvala8b905572015-05-14 15:43:01 -0700786 if (surface == null) throw new IllegalArgumentException("Surface is null");
787
Eino-Ville Talvalaad916f72015-04-11 12:09:11 -0700788 synchronized(mInterfaceLock) {
789 int streamId = -1;
790 for (int i = 0; i < mConfiguredOutputs.size(); i++) {
Shuzhen Wange0a66672017-03-21 12:05:14 -0700791 final List<Surface> surfaces = mConfiguredOutputs.valueAt(i).getSurfaces();
792 if (surfaces.contains(surface)) {
Eino-Ville Talvalaad916f72015-04-11 12:09:11 -0700793 streamId = mConfiguredOutputs.keyAt(i);
794 break;
795 }
796 }
797 if (streamId == -1) {
798 throw new IllegalArgumentException("Surface is not part of this session");
799 }
Eino-Ville Talvala5d2d7782015-12-17 16:50:50 -0800800
801 mRemoteDevice.prepare(streamId);
Eino-Ville Talvalaad916f72015-04-11 12:09:11 -0700802 }
803 }
804
Ruben Brunk7ed1aaa2015-08-13 17:57:14 -0700805 public void prepare(int maxCount, Surface surface) throws CameraAccessException {
806 if (surface == null) throw new IllegalArgumentException("Surface is null");
807 if (maxCount <= 0) throw new IllegalArgumentException("Invalid maxCount given: " +
808 maxCount);
809
810 synchronized(mInterfaceLock) {
811 int streamId = -1;
812 for (int i = 0; i < mConfiguredOutputs.size(); i++) {
813 if (surface == mConfiguredOutputs.valueAt(i).getSurface()) {
814 streamId = mConfiguredOutputs.keyAt(i);
815 break;
816 }
817 }
818 if (streamId == -1) {
819 throw new IllegalArgumentException("Surface is not part of this session");
820 }
Eino-Ville Talvala5d2d7782015-12-17 16:50:50 -0800821
822 mRemoteDevice.prepare2(maxCount, streamId);
Ruben Brunk7ed1aaa2015-08-13 17:57:14 -0700823 }
824 }
825
Emilian Peev03233152017-10-27 16:01:20 +0100826 public void updateOutputConfiguration(OutputConfiguration config)
827 throws CameraAccessException {
828 synchronized(mInterfaceLock) {
829 int streamId = -1;
830 for (int i = 0; i < mConfiguredOutputs.size(); i++) {
831 if (config.getSurface() == mConfiguredOutputs.valueAt(i).getSurface()) {
832 streamId = mConfiguredOutputs.keyAt(i);
833 break;
834 }
835 }
836 if (streamId == -1) {
837 throw new IllegalArgumentException("Invalid output configuration");
838 }
839
840 mRemoteDevice.updateOutputConfiguration(streamId, config);
Yin-Chia Yehc60af902017-11-10 20:02:29 -0800841 mConfiguredOutputs.put(streamId, config);
Emilian Peev03233152017-10-27 16:01:20 +0100842 }
843 }
844
Eino-Ville Talvala14c09fa2015-07-15 16:04:04 -0700845 public void tearDown(Surface surface) throws CameraAccessException {
846 if (surface == null) throw new IllegalArgumentException("Surface is null");
847
848 synchronized(mInterfaceLock) {
849 int streamId = -1;
850 for (int i = 0; i < mConfiguredOutputs.size(); i++) {
851 if (surface == mConfiguredOutputs.valueAt(i).getSurface()) {
852 streamId = mConfiguredOutputs.keyAt(i);
853 break;
854 }
855 }
856 if (streamId == -1) {
857 throw new IllegalArgumentException("Surface is not part of this session");
858 }
Eino-Ville Talvala5d2d7782015-12-17 16:50:50 -0800859
860 mRemoteDevice.tearDown(streamId);
Eino-Ville Talvala14c09fa2015-07-15 16:04:04 -0700861 }
862 }
863
Shuzhen Wang4bd7abe2017-01-11 09:58:58 -0800864 public void finalizeOutputConfigs(List<OutputConfiguration> outputConfigs)
Zhijun Hec8b181e2016-05-30 14:54:39 -0700865 throws CameraAccessException {
Shuzhen Wang4bd7abe2017-01-11 09:58:58 -0800866 if (outputConfigs == null || outputConfigs.size() == 0) {
Zhijun Hec8b181e2016-05-30 14:54:39 -0700867 throw new IllegalArgumentException("deferred config is null or empty");
868 }
869
870 synchronized(mInterfaceLock) {
Shuzhen Wang4bd7abe2017-01-11 09:58:58 -0800871 for (OutputConfiguration config : outputConfigs) {
Zhijun Hec8b181e2016-05-30 14:54:39 -0700872 int streamId = -1;
873 for (int i = 0; i < mConfiguredOutputs.size(); i++) {
874 // Have to use equal here, as createCaptureSessionByOutputConfigurations() and
875 // createReprocessableCaptureSessionByConfigurations() do a copy of the configs.
876 if (config.equals(mConfiguredOutputs.valueAt(i))) {
877 streamId = mConfiguredOutputs.keyAt(i);
878 break;
879 }
880 }
881 if (streamId == -1) {
882 throw new IllegalArgumentException("Deferred config is not part of this "
883 + "session");
884 }
885
Shuzhen Wang4bd7abe2017-01-11 09:58:58 -0800886 if (config.getSurfaces().size() == 0) {
887 throw new IllegalArgumentException("The final config for stream " + streamId
888 + " must have at least 1 surface");
Zhijun Hec8b181e2016-05-30 14:54:39 -0700889 }
Shuzhen Wang4bd7abe2017-01-11 09:58:58 -0800890 mRemoteDevice.finalizeOutputConfigurations(streamId, config);
Yin-Chia Yehc60af902017-11-10 20:02:29 -0800891 mConfiguredOutputs.put(streamId, config);
Zhijun Hec8b181e2016-05-30 14:54:39 -0700892 }
893 }
894 }
895
Eino-Ville Talvalafd887432014-09-04 13:07:40 -0700896 public int capture(CaptureRequest request, CaptureCallback callback, Handler handler)
Igor Murashkin70725502013-06-25 20:27:06 +0000897 throws CameraAccessException {
Jianing Weid2c3a822014-03-27 18:27:43 -0700898 if (DEBUG) {
899 Log.d(TAG, "calling capture");
900 }
901 List<CaptureRequest> requestList = new ArrayList<CaptureRequest>();
902 requestList.add(request);
Eino-Ville Talvalafd887432014-09-04 13:07:40 -0700903 return submitCaptureRequest(requestList, callback, handler, /*streaming*/false);
Igor Murashkin70725502013-06-25 20:27:06 +0000904 }
905
Eino-Ville Talvalafd887432014-09-04 13:07:40 -0700906 public int captureBurst(List<CaptureRequest> requests, CaptureCallback callback,
Eino-Ville Talvala4af73c22013-08-14 10:35:46 -0700907 Handler handler) throws CameraAccessException {
Igor Murashkin0b27d342014-05-30 09:45:05 -0700908 if (requests == null || requests.isEmpty()) {
909 throw new IllegalArgumentException("At least one request must be given");
Zhijun Hefc19e2c2013-08-22 14:43:07 -0700910 }
Eino-Ville Talvalafd887432014-09-04 13:07:40 -0700911 return submitCaptureRequest(requests, callback, handler, /*streaming*/false);
Igor Murashkin70725502013-06-25 20:27:06 +0000912 }
913
Jianing Wei5a4b02b2014-04-11 09:59:24 -0700914 /**
915 * This method checks lastFrameNumber returned from ICameraDeviceUser methods for
916 * starting and stopping repeating request and flushing.
917 *
918 * <p>If lastFrameNumber is NO_FRAMES_CAPTURED, it means that the request was never
Eino-Ville Talvalaee37e302014-06-10 15:24:26 -0700919 * sent to HAL. Then onCaptureSequenceAborted is immediately triggered.
Chien-Yu Chen4560df72015-04-27 17:21:31 -0700920 * If lastFrameNumber is non-negative, then the requestId and lastFrameNumber as the last
921 * regular frame number will be added to the list mRequestLastFrameNumbersList.</p>
Jianing Wei5a4b02b2014-04-11 09:59:24 -0700922 *
923 * @param requestId the request ID of the current repeating request.
924 *
925 * @param lastFrameNumber last frame number returned from binder.
926 */
927 private void checkEarlyTriggerSequenceComplete(
928 final int requestId, final long lastFrameNumber) {
929 // lastFrameNumber being equal to NO_FRAMES_CAPTURED means that the request
Eino-Ville Talvalaee37e302014-06-10 15:24:26 -0700930 // was never sent to HAL. Should trigger onCaptureSequenceAborted immediately.
Eino-Ville Talvalafd887432014-09-04 13:07:40 -0700931 if (lastFrameNumber == CaptureCallback.NO_FRAMES_CAPTURED) {
932 final CaptureCallbackHolder holder;
933 int index = mCaptureCallbackMap.indexOfKey(requestId);
934 holder = (index >= 0) ? mCaptureCallbackMap.valueAt(index) : null;
Jianing Wei5a4b02b2014-04-11 09:59:24 -0700935 if (holder != null) {
Eino-Ville Talvalafd887432014-09-04 13:07:40 -0700936 mCaptureCallbackMap.removeAt(index);
Jianing Weibaf0c652014-04-18 17:35:00 -0700937 if (DEBUG) {
938 Log.v(TAG, String.format(
939 "remove holder for requestId %d, "
940 + "because lastFrame is %d.",
941 requestId, lastFrameNumber));
942 }
Jianing Wei5a4b02b2014-04-11 09:59:24 -0700943 }
944
945 if (holder != null) {
946 if (DEBUG) {
Eino-Ville Talvalaee37e302014-06-10 15:24:26 -0700947 Log.v(TAG, "immediately trigger onCaptureSequenceAborted because"
Jianing Wei5a4b02b2014-04-11 09:59:24 -0700948 + " request did not reach HAL");
949 }
950
951 Runnable resultDispatch = new Runnable() {
952 @Override
953 public void run() {
Igor Murashkin21547d62014-06-04 15:21:42 -0700954 if (!CameraDeviceImpl.this.isClosed()) {
Jianing Wei5a4b02b2014-04-11 09:59:24 -0700955 if (DEBUG) {
956 Log.d(TAG, String.format(
957 "early trigger sequence complete for request %d",
958 requestId));
959 }
Eino-Ville Talvalafd887432014-09-04 13:07:40 -0700960 holder.getCallback().onCaptureSequenceAborted(
Igor Murashkin21547d62014-06-04 15:21:42 -0700961 CameraDeviceImpl.this,
Eino-Ville Talvalaee37e302014-06-10 15:24:26 -0700962 requestId);
Jianing Wei5a4b02b2014-04-11 09:59:24 -0700963 }
964 }
965 };
966 holder.getHandler().post(resultDispatch);
967 } else {
968 Log.w(TAG, String.format(
Eino-Ville Talvalafd887432014-09-04 13:07:40 -0700969 "did not register callback to request %d",
Jianing Wei5a4b02b2014-04-11 09:59:24 -0700970 requestId));
971 }
972 } else {
Chien-Yu Chen4560df72015-04-27 17:21:31 -0700973 // This function is only called for regular request so lastFrameNumber is the last
974 // regular frame number.
975 mRequestLastFrameNumbersList.add(new RequestLastFrameNumbersHolder(requestId,
976 lastFrameNumber));
977
Eino-Ville Talvala848fe732014-09-15 16:24:08 -0700978 // It is possible that the last frame has already arrived, so we need to check
979 // for sequence completion right away
980 checkAndFireSequenceComplete();
Jianing Wei5a4b02b2014-04-11 09:59:24 -0700981 }
982 }
983
Eino-Ville Talvalafd887432014-09-04 13:07:40 -0700984 private int submitCaptureRequest(List<CaptureRequest> requestList, CaptureCallback callback,
Eino-Ville Talvala4af73c22013-08-14 10:35:46 -0700985 Handler handler, boolean repeating) throws CameraAccessException {
986
987 // Need a valid handler, or current thread needs to have a looper, if
Eino-Ville Talvalafd887432014-09-04 13:07:40 -0700988 // callback is valid
989 handler = checkHandler(handler, callback);
Igor Murashkin70725502013-06-25 20:27:06 +0000990
Shuzhen Wang23d29382017-11-26 17:24:56 -0800991 // Make sure that there all requests have at least 1 surface; all surfaces are non-null;
992 // the surface isn't a physical stream surface for reprocessing request
Ruben Brunk7f2372b2014-07-02 11:05:08 -0700993 for (CaptureRequest request : requestList) {
994 if (request.getTargets().isEmpty()) {
995 throw new IllegalArgumentException(
996 "Each request must have at least one Surface target");
997 }
998
999 for (Surface surface : request.getTargets()) {
1000 if (surface == null) {
1001 throw new IllegalArgumentException("Null Surface targets are not allowed");
1002 }
Shuzhen Wang23d29382017-11-26 17:24:56 -08001003
1004 if (!request.isReprocess()) {
1005 continue;
1006 }
1007 for (int i = 0; i < mConfiguredOutputs.size(); i++) {
1008 OutputConfiguration configuration = mConfiguredOutputs.valueAt(i);
1009 if (configuration.isForPhysicalCamera()
1010 && configuration.getSurfaces().contains(surface)) {
1011 throw new IllegalArgumentException(
1012 "Reprocess request on physical stream is not allowed");
1013 }
1014 }
Ruben Brunk7f2372b2014-07-02 11:05:08 -07001015 }
Shuzhen Wang23d29382017-11-26 17:24:56 -08001016
Ruben Brunk7f2372b2014-07-02 11:05:08 -07001017 }
1018
Eino-Ville Talvala2f75e7d2014-07-15 10:31:54 -07001019 synchronized(mInterfaceLock) {
Eino-Ville Talvala7fcb35782014-06-03 18:30:27 -07001020 checkIfCameraClosedOrInError();
Ruben Brunke73b41b2013-11-07 19:30:43 -08001021 if (repeating) {
1022 stopRepeating();
1023 }
1024
Eino-Ville Talvala5d2d7782015-12-17 16:50:50 -08001025 SubmitInfo requestInfo;
1026
1027 CaptureRequest[] requestArray = requestList.toArray(new CaptureRequest[requestList.size()]);
Yin-Chia Yehc60af902017-11-10 20:02:29 -08001028 // Convert Surface to streamIdx and surfaceIdx
1029 for (CaptureRequest request : requestArray) {
1030 request.convertSurfaceToStreamId(mConfiguredOutputs);
1031 }
1032
Eino-Ville Talvala5d2d7782015-12-17 16:50:50 -08001033 requestInfo = mRemoteDevice.submitRequestList(requestArray, repeating);
1034 if (DEBUG) {
1035 Log.v(TAG, "last frame number " + requestInfo.getLastFrameNumber());
Igor Murashkin70725502013-06-25 20:27:06 +00001036 }
Jianing Wei5a4b02b2014-04-11 09:59:24 -07001037
Yin-Chia Yehc60af902017-11-10 20:02:29 -08001038 for (CaptureRequest request : requestArray) {
1039 request.recoverStreamIdToSurface();
1040 }
1041
Eino-Ville Talvalafd887432014-09-04 13:07:40 -07001042 if (callback != null) {
Eino-Ville Talvala5d2d7782015-12-17 16:50:50 -08001043 mCaptureCallbackMap.put(requestInfo.getRequestId(),
1044 new CaptureCallbackHolder(
1045 callback, requestList, handler, repeating, mNextSessionId - 1));
Jianing Weibaf0c652014-04-18 17:35:00 -07001046 } else {
1047 if (DEBUG) {
Eino-Ville Talvala5d2d7782015-12-17 16:50:50 -08001048 Log.d(TAG, "Listen for request " + requestInfo.getRequestId() + " is null");
Jianing Weibaf0c652014-04-18 17:35:00 -07001049 }
Eino-Ville Talvala4af73c22013-08-14 10:35:46 -07001050 }
Igor Murashkin70725502013-06-25 20:27:06 +00001051
1052 if (repeating) {
Jianing Weid2c3a822014-03-27 18:27:43 -07001053 if (mRepeatingRequestId != REQUEST_ID_NONE) {
Eino-Ville Talvala5d2d7782015-12-17 16:50:50 -08001054 checkEarlyTriggerSequenceComplete(mRepeatingRequestId,
1055 requestInfo.getLastFrameNumber());
Jianing Weid2c3a822014-03-27 18:27:43 -07001056 }
Eino-Ville Talvala5d2d7782015-12-17 16:50:50 -08001057 mRepeatingRequestId = requestInfo.getRequestId();
Jianing Weid2c3a822014-03-27 18:27:43 -07001058 } else {
Eino-Ville Talvala5d2d7782015-12-17 16:50:50 -08001059 mRequestLastFrameNumbersList.add(
1060 new RequestLastFrameNumbersHolder(requestList, requestInfo));
Igor Murashkin70725502013-06-25 20:27:06 +00001061 }
1062
Eino-Ville Talvala868d9042013-10-03 11:15:21 -07001063 if (mIdle) {
1064 mDeviceHandler.post(mCallOnActive);
1065 }
1066 mIdle = false;
1067
Eino-Ville Talvala5d2d7782015-12-17 16:50:50 -08001068 return requestInfo.getRequestId();
Igor Murashkin70725502013-06-25 20:27:06 +00001069 }
1070 }
1071
Eino-Ville Talvalafd887432014-09-04 13:07:40 -07001072 public int setRepeatingRequest(CaptureRequest request, CaptureCallback callback,
Eino-Ville Talvala4af73c22013-08-14 10:35:46 -07001073 Handler handler) throws CameraAccessException {
Jianing Weid2c3a822014-03-27 18:27:43 -07001074 List<CaptureRequest> requestList = new ArrayList<CaptureRequest>();
1075 requestList.add(request);
Eino-Ville Talvalafd887432014-09-04 13:07:40 -07001076 return submitCaptureRequest(requestList, callback, handler, /*streaming*/true);
Igor Murashkin70725502013-06-25 20:27:06 +00001077 }
1078
Eino-Ville Talvalafd887432014-09-04 13:07:40 -07001079 public int setRepeatingBurst(List<CaptureRequest> requests, CaptureCallback callback,
Eino-Ville Talvala4af73c22013-08-14 10:35:46 -07001080 Handler handler) throws CameraAccessException {
Igor Murashkin0b27d342014-05-30 09:45:05 -07001081 if (requests == null || requests.isEmpty()) {
1082 throw new IllegalArgumentException("At least one request must be given");
Zhijun Hefc19e2c2013-08-22 14:43:07 -07001083 }
Eino-Ville Talvalafd887432014-09-04 13:07:40 -07001084 return submitCaptureRequest(requests, callback, handler, /*streaming*/true);
Igor Murashkin70725502013-06-25 20:27:06 +00001085 }
1086
Igor Murashkin70725502013-06-25 20:27:06 +00001087 public void stopRepeating() throws CameraAccessException {
1088
Eino-Ville Talvala2f75e7d2014-07-15 10:31:54 -07001089 synchronized(mInterfaceLock) {
Eino-Ville Talvala7fcb35782014-06-03 18:30:27 -07001090 checkIfCameraClosedOrInError();
Ruben Brunkdecfe952013-10-29 11:00:32 -07001091 if (mRepeatingRequestId != REQUEST_ID_NONE) {
1092
1093 int requestId = mRepeatingRequestId;
1094 mRepeatingRequestId = REQUEST_ID_NONE;
1095
Chien-Yu Chen2da496f2016-04-14 13:33:00 -07001096 long lastFrameNumber;
1097 try {
1098 lastFrameNumber = mRemoteDevice.cancelRequest(requestId);
1099 } catch (IllegalArgumentException e) {
1100 if (DEBUG) {
1101 Log.v(TAG, "Repeating request was already stopped for request " + requestId);
1102 }
1103 // Repeating request was already stopped. Nothing more to do.
1104 return;
1105 }
Jianing Wei5a4b02b2014-04-11 09:59:24 -07001106
Eino-Ville Talvala5d2d7782015-12-17 16:50:50 -08001107 checkEarlyTriggerSequenceComplete(requestId, lastFrameNumber);
Igor Murashkin70725502013-06-25 20:27:06 +00001108 }
1109 }
1110 }
1111
Zhijun Hed842fcd2013-12-26 14:14:04 -08001112 private void waitUntilIdle() throws CameraAccessException {
Zhijun He7f4d3142013-07-23 07:54:38 -07001113
Eino-Ville Talvala2f75e7d2014-07-15 10:31:54 -07001114 synchronized(mInterfaceLock) {
Eino-Ville Talvala7fcb35782014-06-03 18:30:27 -07001115 checkIfCameraClosedOrInError();
Eino-Ville Talvala2f75e7d2014-07-15 10:31:54 -07001116
Ruben Brunkdecfe952013-10-29 11:00:32 -07001117 if (mRepeatingRequestId != REQUEST_ID_NONE) {
Zhijun He7f4d3142013-07-23 07:54:38 -07001118 throw new IllegalStateException("Active repeating request ongoing");
1119 }
Eino-Ville Talvala5d2d7782015-12-17 16:50:50 -08001120
1121 mRemoteDevice.waitUntilIdle();
Eino-Ville Talvalae841d4e2013-09-05 09:04:08 -07001122 }
Igor Murashkin70725502013-06-25 20:27:06 +00001123 }
1124
Eino-Ville Talvala8ebd52b2013-08-13 12:09:44 -07001125 public void flush() throws CameraAccessException {
Eino-Ville Talvala2f75e7d2014-07-15 10:31:54 -07001126 synchronized(mInterfaceLock) {
Eino-Ville Talvala7fcb35782014-06-03 18:30:27 -07001127 checkIfCameraClosedOrInError();
Eino-Ville Talvala868d9042013-10-03 11:15:21 -07001128
1129 mDeviceHandler.post(mCallOnBusy);
Eino-Ville Talvalac346bbf2014-08-27 17:05:29 -07001130
1131 // If already idle, just do a busy->idle transition immediately, don't actually
1132 // flush.
1133 if (mIdle) {
1134 mDeviceHandler.post(mCallOnIdle);
1135 return;
1136 }
Eino-Ville Talvala5d2d7782015-12-17 16:50:50 -08001137
1138 long lastFrameNumber = mRemoteDevice.flush();
1139 if (mRepeatingRequestId != REQUEST_ID_NONE) {
1140 checkEarlyTriggerSequenceComplete(mRepeatingRequestId, lastFrameNumber);
1141 mRepeatingRequestId = REQUEST_ID_NONE;
Eino-Ville Talvala8ebd52b2013-08-13 12:09:44 -07001142 }
1143 }
1144 }
1145
1146 @Override
Igor Murashkin5c9eaf62013-09-10 19:35:24 -07001147 public void close() {
Eino-Ville Talvala2f75e7d2014-07-15 10:31:54 -07001148 synchronized (mInterfaceLock) {
Ruben Brunk0953b642015-06-11 16:12:35 -07001149 if (mClosing.getAndSet(true)) {
1150 return;
1151 }
1152
Eino-Ville Talvala5d2d7782015-12-17 16:50:50 -08001153 if (mRemoteDevice != null) {
1154 mRemoteDevice.disconnect();
Eino-Ville Talvalaee46b582016-04-04 12:26:40 -07001155 mRemoteDevice.unlinkToDeath(this, /*flags*/0);
Igor Murashkin70725502013-06-25 20:27:06 +00001156 }
1157
Eino-Ville Talvala7fcb35782014-06-03 18:30:27 -07001158 // Only want to fire the onClosed callback once;
1159 // either a normal close where the remote device is valid
1160 // or a close after a startup error (no remote device but in error state)
1161 if (mRemoteDevice != null || mInError) {
Eino-Ville Talvala868d9042013-10-03 11:15:21 -07001162 mDeviceHandler.post(mCallOnClosed);
1163 }
Igor Murashkin70725502013-06-25 20:27:06 +00001164
Eino-Ville Talvala868d9042013-10-03 11:15:21 -07001165 mRemoteDevice = null;
Igor Murashkin70725502013-06-25 20:27:06 +00001166 }
1167 }
1168
1169 @Override
1170 protected void finalize() throws Throwable {
1171 try {
1172 close();
Igor Murashkin70725502013-06-25 20:27:06 +00001173 }
1174 finally {
1175 super.finalize();
1176 }
1177 }
1178
Chien-Yu Chen5398a672015-03-19 14:48:43 -07001179 private void checkInputConfiguration(InputConfiguration inputConfig) {
1180 if (inputConfig != null) {
1181 StreamConfigurationMap configMap = mCharacteristics.get(
1182 CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
1183
1184 int[] inputFormats = configMap.getInputFormats();
1185 boolean validFormat = false;
1186 for (int format : inputFormats) {
1187 if (format == inputConfig.getFormat()) {
1188 validFormat = true;
1189 }
1190 }
1191
1192 if (validFormat == false) {
1193 throw new IllegalArgumentException("input format " + inputConfig.getFormat() +
1194 " is not valid");
1195 }
1196
1197 boolean validSize = false;
1198 Size[] inputSizes = configMap.getInputSizes(inputConfig.getFormat());
1199 for (Size s : inputSizes) {
1200 if (inputConfig.getWidth() == s.getWidth() &&
1201 inputConfig.getHeight() == s.getHeight()) {
1202 validSize = true;
1203 }
1204 }
1205
1206 if (validSize == false) {
1207 throw new IllegalArgumentException("input size " + inputConfig.getWidth() + "x" +
1208 inputConfig.getHeight() + " is not valid");
1209 }
1210 }
1211 }
1212
Eino-Ville Talvalaa6b5ba52014-07-02 16:30:53 -07001213 /**
Eino-Ville Talvalafd887432014-09-04 13:07:40 -07001214 * <p>A callback for tracking the progress of a {@link CaptureRequest}
Eino-Ville Talvalaa6b5ba52014-07-02 16:30:53 -07001215 * submitted to the camera device.</p>
1216 *
Eino-Ville Talvalacad1bfe2017-05-23 14:10:49 -07001217 * An interface instead of an abstract class because this is internal and
1218 * we want to make sure we always implement all its callbacks until we reach
1219 * the public layer.
Eino-Ville Talvalaa6b5ba52014-07-02 16:30:53 -07001220 */
Eino-Ville Talvalacad1bfe2017-05-23 14:10:49 -07001221 public interface CaptureCallback {
Eino-Ville Talvalaa6b5ba52014-07-02 16:30:53 -07001222
1223 /**
1224 * This constant is used to indicate that no images were captured for
1225 * the request.
1226 *
1227 * @hide
1228 */
1229 public static final int NO_FRAMES_CAPTURED = -1;
1230
1231 /**
1232 * This method is called when the camera device has started capturing
1233 * the output image for the request, at the beginning of image exposure.
1234 *
1235 * @see android.media.MediaActionSound
1236 */
1237 public void onCaptureStarted(CameraDevice camera,
Eino-Ville Talvalacad1bfe2017-05-23 14:10:49 -07001238 CaptureRequest request, long timestamp, long frameNumber);
Eino-Ville Talvalaa6b5ba52014-07-02 16:30:53 -07001239
1240 /**
1241 * This method is called when some results from an image capture are
1242 * available.
1243 *
1244 * @hide
1245 */
1246 public void onCapturePartial(CameraDevice camera,
Eino-Ville Talvalacad1bfe2017-05-23 14:10:49 -07001247 CaptureRequest request, CaptureResult result);
Eino-Ville Talvalaa6b5ba52014-07-02 16:30:53 -07001248
1249 /**
1250 * This method is called when an image capture makes partial forward progress; some
1251 * (but not all) results from an image capture are available.
1252 *
1253 */
1254 public void onCaptureProgressed(CameraDevice camera,
Eino-Ville Talvalacad1bfe2017-05-23 14:10:49 -07001255 CaptureRequest request, CaptureResult partialResult);
Eino-Ville Talvalaa6b5ba52014-07-02 16:30:53 -07001256
1257 /**
1258 * This method is called when an image capture has fully completed and all the
1259 * result metadata is available.
1260 */
1261 public void onCaptureCompleted(CameraDevice camera,
Eino-Ville Talvalacad1bfe2017-05-23 14:10:49 -07001262 CaptureRequest request, TotalCaptureResult result);
Eino-Ville Talvalaa6b5ba52014-07-02 16:30:53 -07001263
1264 /**
1265 * This method is called instead of {@link #onCaptureCompleted} when the
1266 * camera device failed to produce a {@link CaptureResult} for the
1267 * request.
1268 */
1269 public void onCaptureFailed(CameraDevice camera,
Eino-Ville Talvalacad1bfe2017-05-23 14:10:49 -07001270 CaptureRequest request, CaptureFailure failure);
Eino-Ville Talvalaa6b5ba52014-07-02 16:30:53 -07001271
1272 /**
Eino-Ville Talvalafd887432014-09-04 13:07:40 -07001273 * This method is called independently of the others in CaptureCallback,
Eino-Ville Talvalaa6b5ba52014-07-02 16:30:53 -07001274 * when a capture sequence finishes and all {@link CaptureResult}
Eino-Ville Talvalafd887432014-09-04 13:07:40 -07001275 * or {@link CaptureFailure} for it have been returned via this callback.
Eino-Ville Talvalaa6b5ba52014-07-02 16:30:53 -07001276 */
1277 public void onCaptureSequenceCompleted(CameraDevice camera,
Eino-Ville Talvalacad1bfe2017-05-23 14:10:49 -07001278 int sequenceId, long frameNumber);
Eino-Ville Talvalaa6b5ba52014-07-02 16:30:53 -07001279
1280 /**
Eino-Ville Talvalafd887432014-09-04 13:07:40 -07001281 * This method is called independently of the others in CaptureCallback,
Eino-Ville Talvalaa6b5ba52014-07-02 16:30:53 -07001282 * when a capture sequence aborts before any {@link CaptureResult}
Eino-Ville Talvalafd887432014-09-04 13:07:40 -07001283 * or {@link CaptureFailure} for it have been returned via this callback.
Eino-Ville Talvalaa6b5ba52014-07-02 16:30:53 -07001284 */
1285 public void onCaptureSequenceAborted(CameraDevice camera,
Eino-Ville Talvalacad1bfe2017-05-23 14:10:49 -07001286 int sequenceId);
Eino-Ville Talvala3e0023a2016-03-06 18:40:01 -08001287
Eino-Ville Talvalacad1bfe2017-05-23 14:10:49 -07001288 /**
1289 * This method is called independently of the others in CaptureCallback, if an output buffer
1290 * is dropped for a particular capture request.
1291 *
1292 * Loss of metadata is communicated via onCaptureFailed, independently of any buffer loss.
1293 */
Eino-Ville Talvala3e0023a2016-03-06 18:40:01 -08001294 public void onCaptureBufferLost(CameraDevice camera,
Eino-Ville Talvalacad1bfe2017-05-23 14:10:49 -07001295 CaptureRequest request, Surface target, long frameNumber);
Eino-Ville Talvalaa6b5ba52014-07-02 16:30:53 -07001296 }
1297
1298 /**
Eino-Ville Talvalafd887432014-09-04 13:07:40 -07001299 * A callback for notifications about the state of a camera device, adding in the callbacks that
Eino-Ville Talvalaa6b5ba52014-07-02 16:30:53 -07001300 * were part of the earlier KK API design, but now only used internally.
1301 */
Eino-Ville Talvalafd887432014-09-04 13:07:40 -07001302 public static abstract class StateCallbackKK extends StateCallback {
Eino-Ville Talvalaa6b5ba52014-07-02 16:30:53 -07001303 /**
1304 * The method called when a camera device has no outputs configured.
1305 *
1306 */
1307 public void onUnconfigured(CameraDevice camera) {
1308 // Default empty implementation
1309 }
1310
1311 /**
1312 * The method called when a camera device begins processing
1313 * {@link CaptureRequest capture requests}.
1314 *
1315 */
1316 public void onActive(CameraDevice camera) {
1317 // Default empty implementation
1318 }
1319
1320 /**
1321 * The method called when a camera device is busy.
1322 *
1323 */
1324 public void onBusy(CameraDevice camera) {
1325 // Default empty implementation
1326 }
1327
1328 /**
1329 * The method called when a camera device has finished processing all
1330 * submitted capture requests and has reached an idle state.
1331 *
1332 */
1333 public void onIdle(CameraDevice camera) {
1334 // Default empty implementation
1335 }
Eino-Ville Talvalaad916f72015-04-11 12:09:11 -07001336
1337 /**
Shuzhen Wang88f1af22016-09-30 10:29:28 -07001338 * This method is called when camera device's non-repeating request queue is empty,
1339 * and is ready to start capturing next image.
1340 */
1341 public void onRequestQueueEmpty() {
1342 // Default empty implementation
1343 }
1344
1345 /**
Eino-Ville Talvalaad916f72015-04-11 12:09:11 -07001346 * The method called when the camera device has finished preparing
1347 * an output Surface
1348 */
1349 public void onSurfacePrepared(Surface surface) {
1350 // Default empty implementation
1351 }
Eino-Ville Talvalaa6b5ba52014-07-02 16:30:53 -07001352 }
1353
Eino-Ville Talvalafd887432014-09-04 13:07:40 -07001354 static class CaptureCallbackHolder {
Igor Murashkin70725502013-06-25 20:27:06 +00001355
1356 private final boolean mRepeating;
Eino-Ville Talvalafd887432014-09-04 13:07:40 -07001357 private final CaptureCallback mCallback;
Jianing Weid2c3a822014-03-27 18:27:43 -07001358 private final List<CaptureRequest> mRequestList;
Eino-Ville Talvala4af73c22013-08-14 10:35:46 -07001359 private final Handler mHandler;
Chien-Yu Chen7a316f6b2015-04-13 14:31:45 -07001360 private final int mSessionId;
Shuzhen Wangae10eb92017-03-01 12:58:04 -08001361 /**
1362 * <p>Determine if the callback holder is for a constrained high speed request list that
1363 * expects batched capture results. Capture results will be batched if the request list
1364 * is interleaved with preview and video requests. Capture results won't be batched if the
1365 * request list only contains preview requests, or if the request doesn't belong to a
1366 * constrained high speed list.
1367 */
1368 private final boolean mHasBatchedOutputs;
Igor Murashkin70725502013-06-25 20:27:06 +00001369
Eino-Ville Talvalafd887432014-09-04 13:07:40 -07001370 CaptureCallbackHolder(CaptureCallback callback, List<CaptureRequest> requestList,
Chien-Yu Chen7a316f6b2015-04-13 14:31:45 -07001371 Handler handler, boolean repeating, int sessionId) {
Eino-Ville Talvalafd887432014-09-04 13:07:40 -07001372 if (callback == null || handler == null) {
Eino-Ville Talvala4af73c22013-08-14 10:35:46 -07001373 throw new UnsupportedOperationException(
Eino-Ville Talvalafd887432014-09-04 13:07:40 -07001374 "Must have a valid handler and a valid callback");
Eino-Ville Talvala4af73c22013-08-14 10:35:46 -07001375 }
Igor Murashkin70725502013-06-25 20:27:06 +00001376 mRepeating = repeating;
Eino-Ville Talvala4af73c22013-08-14 10:35:46 -07001377 mHandler = handler;
Jianing Weid2c3a822014-03-27 18:27:43 -07001378 mRequestList = new ArrayList<CaptureRequest>(requestList);
Eino-Ville Talvalafd887432014-09-04 13:07:40 -07001379 mCallback = callback;
Chien-Yu Chen7a316f6b2015-04-13 14:31:45 -07001380 mSessionId = sessionId;
Shuzhen Wangae10eb92017-03-01 12:58:04 -08001381
1382 // Check whether this callback holder is for batched outputs.
1383 // The logic here should match createHighSpeedRequestList.
1384 boolean hasBatchedOutputs = true;
1385 for (int i = 0; i < requestList.size(); i++) {
1386 CaptureRequest request = requestList.get(i);
1387 if (!request.isPartOfCRequestList()) {
1388 hasBatchedOutputs = false;
1389 break;
1390 }
1391 if (i == 0) {
1392 Collection<Surface> targets = request.getTargets();
1393 if (targets.size() != 2) {
1394 hasBatchedOutputs = false;
1395 break;
1396 }
1397 }
1398 }
1399 mHasBatchedOutputs = hasBatchedOutputs;
Igor Murashkin70725502013-06-25 20:27:06 +00001400 }
1401
1402 public boolean isRepeating() {
1403 return mRepeating;
1404 }
1405
Eino-Ville Talvalafd887432014-09-04 13:07:40 -07001406 public CaptureCallback getCallback() {
1407 return mCallback;
Igor Murashkin70725502013-06-25 20:27:06 +00001408 }
1409
Jianing Weid2c3a822014-03-27 18:27:43 -07001410 public CaptureRequest getRequest(int subsequenceId) {
1411 if (subsequenceId >= mRequestList.size()) {
1412 throw new IllegalArgumentException(
1413 String.format(
1414 "Requested subsequenceId %d is larger than request list size %d.",
1415 subsequenceId, mRequestList.size()));
1416 } else {
1417 if (subsequenceId < 0) {
1418 throw new IllegalArgumentException(String.format(
1419 "Requested subsequenceId %d is negative", subsequenceId));
1420 } else {
1421 return mRequestList.get(subsequenceId);
1422 }
1423 }
1424 }
1425
Igor Murashkin70725502013-06-25 20:27:06 +00001426 public CaptureRequest getRequest() {
Jianing Weid2c3a822014-03-27 18:27:43 -07001427 return getRequest(0);
Igor Murashkin70725502013-06-25 20:27:06 +00001428 }
Eino-Ville Talvala4af73c22013-08-14 10:35:46 -07001429
1430 public Handler getHandler() {
1431 return mHandler;
1432 }
1433
Chien-Yu Chen7a316f6b2015-04-13 14:31:45 -07001434 public int getSessionId() {
1435 return mSessionId;
1436 }
Shuzhen Wangae10eb92017-03-01 12:58:04 -08001437
1438 public int getRequestCount() {
1439 return mRequestList.size();
1440 }
1441
1442 public boolean hasBatchedOutputs() {
1443 return mHasBatchedOutputs;
1444 }
Chien-Yu Chen4560df72015-04-27 17:21:31 -07001445 }
Chien-Yu Chen7a316f6b2015-04-13 14:31:45 -07001446
Chien-Yu Chen4560df72015-04-27 17:21:31 -07001447 /**
1448 * This class holds a capture ID and its expected last regular frame number and last reprocess
1449 * frame number.
1450 */
1451 static class RequestLastFrameNumbersHolder {
1452 // request ID
1453 private final int mRequestId;
1454 // The last regular frame number for this request ID. It's
1455 // CaptureCallback.NO_FRAMES_CAPTURED if the request ID has no regular request.
1456 private final long mLastRegularFrameNumber;
1457 // The last reprocess frame number for this request ID. It's
1458 // CaptureCallback.NO_FRAMES_CAPTURED if the request ID has no reprocess request.
1459 private final long mLastReprocessFrameNumber;
1460
1461 /**
1462 * Create a request-last-frame-numbers holder with a list of requests, request ID, and
1463 * the last frame number returned by camera service.
1464 */
Eino-Ville Talvala5d2d7782015-12-17 16:50:50 -08001465 public RequestLastFrameNumbersHolder(List<CaptureRequest> requestList, SubmitInfo requestInfo) {
Chien-Yu Chen4560df72015-04-27 17:21:31 -07001466 long lastRegularFrameNumber = CaptureCallback.NO_FRAMES_CAPTURED;
1467 long lastReprocessFrameNumber = CaptureCallback.NO_FRAMES_CAPTURED;
Eino-Ville Talvala5d2d7782015-12-17 16:50:50 -08001468 long frameNumber = requestInfo.getLastFrameNumber();
Chien-Yu Chen4560df72015-04-27 17:21:31 -07001469
Eino-Ville Talvala5d2d7782015-12-17 16:50:50 -08001470 if (requestInfo.getLastFrameNumber() < requestList.size() - 1) {
1471 throw new IllegalArgumentException(
1472 "lastFrameNumber: " + requestInfo.getLastFrameNumber() +
Chien-Yu Chen4560df72015-04-27 17:21:31 -07001473 " should be at least " + (requestList.size() - 1) + " for the number of " +
1474 " requests in the list: " + requestList.size());
1475 }
1476
1477 // find the last regular frame number and the last reprocess frame number
1478 for (int i = requestList.size() - 1; i >= 0; i--) {
1479 CaptureRequest request = requestList.get(i);
1480 if (request.isReprocess() && lastReprocessFrameNumber ==
1481 CaptureCallback.NO_FRAMES_CAPTURED) {
1482 lastReprocessFrameNumber = frameNumber;
1483 } else if (!request.isReprocess() && lastRegularFrameNumber ==
1484 CaptureCallback.NO_FRAMES_CAPTURED) {
1485 lastRegularFrameNumber = frameNumber;
1486 }
1487
1488 if (lastReprocessFrameNumber != CaptureCallback.NO_FRAMES_CAPTURED &&
1489 lastRegularFrameNumber != CaptureCallback.NO_FRAMES_CAPTURED) {
1490 break;
1491 }
1492
1493 frameNumber--;
1494 }
1495
1496 mLastRegularFrameNumber = lastRegularFrameNumber;
1497 mLastReprocessFrameNumber = lastReprocessFrameNumber;
Eino-Ville Talvala5d2d7782015-12-17 16:50:50 -08001498 mRequestId = requestInfo.getRequestId();
Chien-Yu Chen4560df72015-04-27 17:21:31 -07001499 }
1500
1501 /**
1502 * Create a request-last-frame-numbers holder with a request ID and last regular frame
1503 * number.
1504 */
1505 public RequestLastFrameNumbersHolder(int requestId, long lastRegularFrameNumber) {
1506 mLastRegularFrameNumber = lastRegularFrameNumber;
1507 mLastReprocessFrameNumber = CaptureCallback.NO_FRAMES_CAPTURED;
1508 mRequestId = requestId;
1509 }
1510
1511 /**
1512 * Return the last regular frame number. Return CaptureCallback.NO_FRAMES_CAPTURED if
1513 * it contains no regular request.
1514 */
1515 public long getLastRegularFrameNumber() {
1516 return mLastRegularFrameNumber;
1517 }
1518
1519 /**
1520 * Return the last reprocess frame number. Return CaptureCallback.NO_FRAMES_CAPTURED if
1521 * it contains no reprocess request.
1522 */
1523 public long getLastReprocessFrameNumber() {
1524 return mLastReprocessFrameNumber;
1525 }
1526
1527 /**
1528 * Return the last frame number overall.
1529 */
1530 public long getLastFrameNumber() {
1531 return Math.max(mLastRegularFrameNumber, mLastReprocessFrameNumber);
1532 }
1533
1534 /**
1535 * Return the request ID.
1536 */
1537 public int getRequestId() {
1538 return mRequestId;
1539 }
Igor Murashkin70725502013-06-25 20:27:06 +00001540 }
1541
Jianing Weid2c3a822014-03-27 18:27:43 -07001542 /**
1543 * This class tracks the last frame number for submitted requests.
1544 */
1545 public class FrameNumberTracker {
1546
Chien-Yu Chen4560df72015-04-27 17:21:31 -07001547 private long mCompletedFrameNumber = CaptureCallback.NO_FRAMES_CAPTURED;
1548 private long mCompletedReprocessFrameNumber = CaptureCallback.NO_FRAMES_CAPTURED;
Chien-Yu Chen5398a672015-03-19 14:48:43 -07001549 /** the skipped frame numbers that belong to regular results */
1550 private final LinkedList<Long> mSkippedRegularFrameNumbers = new LinkedList<Long>();
1551 /** the skipped frame numbers that belong to reprocess results */
1552 private final LinkedList<Long> mSkippedReprocessFrameNumbers = new LinkedList<Long>();
1553 /** frame number -> is reprocess */
1554 private final TreeMap<Long, Boolean> mFutureErrorMap = new TreeMap<Long, Boolean>();
Igor Murashkin1e854c52014-08-28 15:21:49 -07001555 /** Map frame numbers to list of partial results */
1556 private final HashMap<Long, List<CaptureResult>> mPartialResults = new HashMap<>();
Jianing Weid2c3a822014-03-27 18:27:43 -07001557
1558 private void update() {
Chien-Yu Chen5398a672015-03-19 14:48:43 -07001559 Iterator iter = mFutureErrorMap.entrySet().iterator();
Jianing Weid2c3a822014-03-27 18:27:43 -07001560 while (iter.hasNext()) {
Chien-Yu Chen5398a672015-03-19 14:48:43 -07001561 TreeMap.Entry pair = (TreeMap.Entry)iter.next();
1562 Long errorFrameNumber = (Long)pair.getKey();
1563 Boolean reprocess = (Boolean)pair.getValue();
1564 Boolean removeError = true;
1565 if (reprocess) {
1566 if (errorFrameNumber == mCompletedReprocessFrameNumber + 1) {
1567 mCompletedReprocessFrameNumber = errorFrameNumber;
1568 } else if (mSkippedReprocessFrameNumbers.isEmpty() != true &&
1569 errorFrameNumber == mSkippedReprocessFrameNumbers.element()) {
1570 mCompletedReprocessFrameNumber = errorFrameNumber;
1571 mSkippedReprocessFrameNumbers.remove();
1572 } else {
1573 removeError = false;
1574 }
Jianing Weid2c3a822014-03-27 18:27:43 -07001575 } else {
Chien-Yu Chen5398a672015-03-19 14:48:43 -07001576 if (errorFrameNumber == mCompletedFrameNumber + 1) {
1577 mCompletedFrameNumber = errorFrameNumber;
1578 } else if (mSkippedRegularFrameNumbers.isEmpty() != true &&
1579 errorFrameNumber == mSkippedRegularFrameNumbers.element()) {
1580 mCompletedFrameNumber = errorFrameNumber;
1581 mSkippedRegularFrameNumbers.remove();
1582 } else {
1583 removeError = false;
1584 }
1585 }
1586 if (removeError) {
1587 iter.remove();
Jianing Weid2c3a822014-03-27 18:27:43 -07001588 }
1589 }
1590 }
1591
1592 /**
1593 * This function is called every time when a result or an error is received.
Igor Murashkin1e854c52014-08-28 15:21:49 -07001594 * @param frameNumber the frame number corresponding to the result or error
1595 * @param isError true if it is an error, false if it is not an error
Chien-Yu Chen5398a672015-03-19 14:48:43 -07001596 * @param isReprocess true if it is a reprocess result, false if it is a regular result.
Jianing Weid2c3a822014-03-27 18:27:43 -07001597 */
Chien-Yu Chen5398a672015-03-19 14:48:43 -07001598 public void updateTracker(long frameNumber, boolean isError, boolean isReprocess) {
Jianing Weid2c3a822014-03-27 18:27:43 -07001599 if (isError) {
Chien-Yu Chen5398a672015-03-19 14:48:43 -07001600 mFutureErrorMap.put(frameNumber, isReprocess);
Jianing Weid2c3a822014-03-27 18:27:43 -07001601 } else {
Chien-Yu Chen5398a672015-03-19 14:48:43 -07001602 try {
1603 if (isReprocess) {
1604 updateCompletedReprocessFrameNumber(frameNumber);
1605 } else {
1606 updateCompletedFrameNumber(frameNumber);
1607 }
1608 } catch (IllegalArgumentException e) {
1609 Log.e(TAG, e.getMessage());
Jianing Weid2c3a822014-03-27 18:27:43 -07001610 }
Jianing Weid2c3a822014-03-27 18:27:43 -07001611 }
1612 update();
1613 }
1614
Igor Murashkin1e854c52014-08-28 15:21:49 -07001615 /**
1616 * This function is called every time a result has been completed.
1617 *
1618 * <p>It keeps a track of all the partial results already created for a particular
1619 * frame number.</p>
1620 *
1621 * @param frameNumber the frame number corresponding to the result
1622 * @param result the total or partial result
1623 * @param partial {@true} if the result is partial, {@code false} if total
Chien-Yu Chen5398a672015-03-19 14:48:43 -07001624 * @param isReprocess true if it is a reprocess result, false if it is a regular result.
Igor Murashkin1e854c52014-08-28 15:21:49 -07001625 */
Chien-Yu Chen5398a672015-03-19 14:48:43 -07001626 public void updateTracker(long frameNumber, CaptureResult result, boolean partial,
1627 boolean isReprocess) {
Igor Murashkin1e854c52014-08-28 15:21:49 -07001628 if (!partial) {
1629 // Update the total result's frame status as being successful
Chien-Yu Chen5398a672015-03-19 14:48:43 -07001630 updateTracker(frameNumber, /*isError*/false, isReprocess);
Igor Murashkin1e854c52014-08-28 15:21:49 -07001631 // Don't keep a list of total results, we don't need to track them
1632 return;
1633 }
1634
1635 if (result == null) {
1636 // Do not record blank results; this also means there will be no total result
1637 // so it doesn't matter that the partials were not recorded
1638 return;
1639 }
1640
1641 // Partial results must be aggregated in-order for that frame number
1642 List<CaptureResult> partials = mPartialResults.get(frameNumber);
1643 if (partials == null) {
1644 partials = new ArrayList<>();
1645 mPartialResults.put(frameNumber, partials);
1646 }
1647
1648 partials.add(result);
1649 }
1650
1651 /**
1652 * Attempt to pop off all of the partial results seen so far for the {@code frameNumber}.
1653 *
1654 * <p>Once popped-off, the partial results are forgotten (unless {@code updateTracker}
1655 * is called again with new partials for that frame number).</p>
1656 *
1657 * @param frameNumber the frame number corresponding to the result
1658 * @return a list of partial results for that frame with at least 1 element,
1659 * or {@code null} if there were no partials recorded for that frame
1660 */
1661 public List<CaptureResult> popPartialResults(long frameNumber) {
1662 return mPartialResults.remove(frameNumber);
1663 }
1664
Jianing Weid2c3a822014-03-27 18:27:43 -07001665 public long getCompletedFrameNumber() {
1666 return mCompletedFrameNumber;
1667 }
1668
Chien-Yu Chen5398a672015-03-19 14:48:43 -07001669 public long getCompletedReprocessFrameNumber() {
1670 return mCompletedReprocessFrameNumber;
1671 }
1672
1673 /**
1674 * Update the completed frame number for regular results.
1675 *
1676 * It validates that all previous frames have arrived except for reprocess frames.
1677 *
1678 * If there is a gap since previous regular frame number, assume the frames in the gap are
1679 * reprocess frames and store them in the skipped reprocess frame number queue to check
1680 * against when reprocess frames arrive.
1681 */
1682 private void updateCompletedFrameNumber(long frameNumber) throws IllegalArgumentException {
1683 if (frameNumber <= mCompletedFrameNumber) {
1684 throw new IllegalArgumentException("frame number " + frameNumber + " is a repeat");
1685 } else if (frameNumber <= mCompletedReprocessFrameNumber) {
1686 // if frame number is smaller than completed reprocess frame number,
1687 // it must be the head of mSkippedRegularFrameNumbers
1688 if (mSkippedRegularFrameNumbers.isEmpty() == true ||
1689 frameNumber < mSkippedRegularFrameNumbers.element()) {
1690 throw new IllegalArgumentException("frame number " + frameNumber +
1691 " is a repeat");
1692 } else if (frameNumber > mSkippedRegularFrameNumbers.element()) {
1693 throw new IllegalArgumentException("frame number " + frameNumber +
1694 " comes out of order. Expecting " +
1695 mSkippedRegularFrameNumbers.element());
1696 }
1697 // frame number matches the head of the skipped frame number queue.
1698 mSkippedRegularFrameNumbers.remove();
1699 } else {
1700 // there is a gap of unseen frame numbers which should belong to reprocess result
1701 // put all the skipped frame numbers in the queue
1702 for (long i = Math.max(mCompletedFrameNumber, mCompletedReprocessFrameNumber) + 1;
1703 i < frameNumber; i++) {
1704 mSkippedReprocessFrameNumbers.add(i);
1705 }
1706 }
1707
1708 mCompletedFrameNumber = frameNumber;
1709 }
1710
1711 /**
1712 * Update the completed frame number for reprocess results.
1713 *
1714 * It validates that all previous frames have arrived except for regular frames.
1715 *
1716 * If there is a gap since previous reprocess frame number, assume the frames in the gap are
1717 * regular frames and store them in the skipped regular frame number queue to check
1718 * against when regular frames arrive.
1719 */
1720 private void updateCompletedReprocessFrameNumber(long frameNumber)
1721 throws IllegalArgumentException {
1722 if (frameNumber < mCompletedReprocessFrameNumber) {
1723 throw new IllegalArgumentException("frame number " + frameNumber + " is a repeat");
1724 } else if (frameNumber < mCompletedFrameNumber) {
1725 // if reprocess frame number is smaller than completed regular frame number,
1726 // it must be the head of the skipped reprocess frame number queue.
1727 if (mSkippedReprocessFrameNumbers.isEmpty() == true ||
1728 frameNumber < mSkippedReprocessFrameNumbers.element()) {
1729 throw new IllegalArgumentException("frame number " + frameNumber +
1730 " is a repeat");
1731 } else if (frameNumber > mSkippedReprocessFrameNumbers.element()) {
1732 throw new IllegalArgumentException("frame number " + frameNumber +
1733 " comes out of order. Expecting " +
1734 mSkippedReprocessFrameNumbers.element());
1735 }
1736 // frame number matches the head of the skipped frame number queue.
1737 mSkippedReprocessFrameNumbers.remove();
1738 } else {
1739 // put all the skipped frame numbers in the queue
1740 for (long i = Math.max(mCompletedFrameNumber, mCompletedReprocessFrameNumber) + 1;
1741 i < frameNumber; i++) {
1742 mSkippedRegularFrameNumbers.add(i);
1743 }
1744 }
1745 mCompletedReprocessFrameNumber = frameNumber;
1746 }
Jianing Weid2c3a822014-03-27 18:27:43 -07001747 }
1748
1749 private void checkAndFireSequenceComplete() {
1750 long completedFrameNumber = mFrameNumberTracker.getCompletedFrameNumber();
Chien-Yu Chen5398a672015-03-19 14:48:43 -07001751 long completedReprocessFrameNumber = mFrameNumberTracker.getCompletedReprocessFrameNumber();
1752 boolean isReprocess = false;
Chien-Yu Chen4560df72015-04-27 17:21:31 -07001753 Iterator<RequestLastFrameNumbersHolder> iter = mRequestLastFrameNumbersList.iterator();
Jianing Weid2c3a822014-03-27 18:27:43 -07001754 while (iter.hasNext()) {
Chien-Yu Chen4560df72015-04-27 17:21:31 -07001755 final RequestLastFrameNumbersHolder requestLastFrameNumbers = iter.next();
Chien-Yu Chen5398a672015-03-19 14:48:43 -07001756 boolean sequenceCompleted = false;
Chien-Yu Chen4560df72015-04-27 17:21:31 -07001757 final int requestId = requestLastFrameNumbers.getRequestId();
Chien-Yu Chen5398a672015-03-19 14:48:43 -07001758 final CaptureCallbackHolder holder;
1759 synchronized(mInterfaceLock) {
1760 if (mRemoteDevice == null) {
1761 Log.w(TAG, "Camera closed while checking sequences");
1762 return;
1763 }
Jianing Weid2c3a822014-03-27 18:27:43 -07001764
Chien-Yu Chen5398a672015-03-19 14:48:43 -07001765 int index = mCaptureCallbackMap.indexOfKey(requestId);
1766 holder = (index >= 0) ?
1767 mCaptureCallbackMap.valueAt(index) : null;
1768 if (holder != null) {
Chien-Yu Chen4560df72015-04-27 17:21:31 -07001769 long lastRegularFrameNumber =
1770 requestLastFrameNumbers.getLastRegularFrameNumber();
1771 long lastReprocessFrameNumber =
1772 requestLastFrameNumbers.getLastReprocessFrameNumber();
1773
Chien-Yu Chen5398a672015-03-19 14:48:43 -07001774 // check if it's okay to remove request from mCaptureCallbackMap
Chien-Yu Chen4560df72015-04-27 17:21:31 -07001775 if (lastRegularFrameNumber <= completedFrameNumber &&
1776 lastReprocessFrameNumber <= completedReprocessFrameNumber) {
Chien-Yu Chen5398a672015-03-19 14:48:43 -07001777 sequenceCompleted = true;
Eino-Ville Talvalafd887432014-09-04 13:07:40 -07001778 mCaptureCallbackMap.removeAt(index);
Jianing Wei5a4b02b2014-04-11 09:59:24 -07001779 if (DEBUG) {
1780 Log.v(TAG, String.format(
Chien-Yu Chen4560df72015-04-27 17:21:31 -07001781 "Remove holder for requestId %d, because lastRegularFrame %d " +
1782 "is <= %d and lastReprocessFrame %d is <= %d", requestId,
1783 lastRegularFrameNumber, completedFrameNumber,
1784 lastReprocessFrameNumber, completedReprocessFrameNumber));
Jianing Wei5a4b02b2014-04-11 09:59:24 -07001785 }
Jianing Weid2c3a822014-03-27 18:27:43 -07001786 }
1787 }
Chien-Yu Chen5398a672015-03-19 14:48:43 -07001788 }
1789
1790 // If no callback is registered for this requestId or sequence completed, remove it
1791 // from the frame number->request pair because it's not needed anymore.
1792 if (holder == null || sequenceCompleted) {
Jianing Weid2c3a822014-03-27 18:27:43 -07001793 iter.remove();
Chien-Yu Chen5398a672015-03-19 14:48:43 -07001794 }
Jianing Weid2c3a822014-03-27 18:27:43 -07001795
Chien-Yu Chen5398a672015-03-19 14:48:43 -07001796 // Call onCaptureSequenceCompleted
1797 if (sequenceCompleted) {
1798 Runnable resultDispatch = new Runnable() {
1799 @Override
1800 public void run() {
1801 if (!CameraDeviceImpl.this.isClosed()){
1802 if (DEBUG) {
1803 Log.d(TAG, String.format(
1804 "fire sequence complete for request %d",
1805 requestId));
Jianing Weid2c3a822014-03-27 18:27:43 -07001806 }
Jianing Weid2c3a822014-03-27 18:27:43 -07001807
Chien-Yu Chen5398a672015-03-19 14:48:43 -07001808 holder.getCallback().onCaptureSequenceCompleted(
1809 CameraDeviceImpl.this,
1810 requestId,
Chien-Yu Chen4560df72015-04-27 17:21:31 -07001811 requestLastFrameNumbers.getLastFrameNumber());
Chien-Yu Chen5398a672015-03-19 14:48:43 -07001812 }
1813 }
1814 };
1815 holder.getHandler().post(resultDispatch);
Jianing Weid2c3a822014-03-27 18:27:43 -07001816 }
1817 }
1818 }
1819
Zhijun Heecb323e2013-07-31 09:40:27 -07001820 public class CameraDeviceCallbacks extends ICameraDeviceCallbacks.Stub {
Eino-Ville Talvalaacc00952014-08-06 14:31:08 -07001821
Igor Murashkin70725502013-06-25 20:27:06 +00001822 @Override
1823 public IBinder asBinder() {
1824 return this;
1825 }
1826
Igor Murashkin70725502013-06-25 20:27:06 +00001827 @Override
Eino-Ville Talvalaacc00952014-08-06 14:31:08 -07001828 public void onDeviceError(final int errorCode, CaptureResultExtras resultExtras) {
1829 if (DEBUG) {
1830 Log.d(TAG, String.format(
Ruben Brunke663cb772014-09-16 13:18:31 -07001831 "Device error received, code %d, frame number %d, request ID %d, subseq ID %d",
1832 errorCode, resultExtras.getFrameNumber(), resultExtras.getRequestId(),
1833 resultExtras.getSubsequenceId()));
Eino-Ville Talvalaacc00952014-08-06 14:31:08 -07001834 }
Eino-Ville Talvala868d9042013-10-03 11:15:21 -07001835
Eino-Ville Talvala2f75e7d2014-07-15 10:31:54 -07001836 synchronized(mInterfaceLock) {
1837 if (mRemoteDevice == null) {
Igor Murashkin49b2b132014-06-18 19:03:00 -07001838 return; // Camera already closed
1839 }
1840
Eino-Ville Talvalae841d4e2013-09-05 09:04:08 -07001841 switch (errorCode) {
1842 case ERROR_CAMERA_DISCONNECTED:
Eino-Ville Talvalaacc00952014-08-06 14:31:08 -07001843 CameraDeviceImpl.this.mDeviceHandler.post(mCallOnDisconnected);
Eino-Ville Talvalae841d4e2013-09-05 09:04:08 -07001844 break;
Eino-Ville Talvalaacc00952014-08-06 14:31:08 -07001845 case ERROR_CAMERA_REQUEST:
1846 case ERROR_CAMERA_RESULT:
1847 case ERROR_CAMERA_BUFFER:
1848 onCaptureErrorLocked(errorCode, resultExtras);
Eino-Ville Talvalae841d4e2013-09-05 09:04:08 -07001849 break;
Svet Ganov82f09bc2018-01-12 22:08:40 -08001850 case ERROR_CAMERA_DEVICE:
1851 scheduleNotifyError(StateCallback.ERROR_CAMERA_DEVICE);
1852 break;
1853 case ERROR_CAMERA_DISABLED:
1854 scheduleNotifyError(StateCallback.ERROR_CAMERA_DISABLED);
1855 break;
1856 default:
1857 Log.e(TAG, "Unknown error from camera device: " + errorCode);
1858 scheduleNotifyError(StateCallback.ERROR_CAMERA_SERVICE);
Eino-Ville Talvalae841d4e2013-09-05 09:04:08 -07001859 }
Jianing Wei5a4b02b2014-04-11 09:59:24 -07001860 }
Eino-Ville Talvalae841d4e2013-09-05 09:04:08 -07001861 }
1862
Svet Ganov82f09bc2018-01-12 22:08:40 -08001863 private void scheduleNotifyError(int code) {
1864 mInError = true;
1865 CameraDeviceImpl.this.mDeviceHandler.post(obtainRunnable(
1866 CameraDeviceCallbacks::notifyError, this, code));
1867 }
1868
1869 private void notifyError(int code) {
1870 if (!CameraDeviceImpl.this.isClosed()) {
1871 mDeviceCallback.onError(CameraDeviceImpl.this, code);
1872 }
1873 }
1874
Eino-Ville Talvalae841d4e2013-09-05 09:04:08 -07001875 @Override
Yin-Chia Yeh8cd12e92017-09-05 18:14:21 -07001876 public void onRepeatingRequestError(long lastFrameNumber, int repeatingRequestId) {
Chien-Yu Chen2da496f2016-04-14 13:33:00 -07001877 if (DEBUG) {
1878 Log.d(TAG, "Repeating request error received. Last frame number is " +
1879 lastFrameNumber);
1880 }
1881
1882 synchronized(mInterfaceLock) {
1883 // Camera is already closed or no repeating request is present.
1884 if (mRemoteDevice == null || mRepeatingRequestId == REQUEST_ID_NONE) {
1885 return; // Camera already closed
1886 }
1887
1888 checkEarlyTriggerSequenceComplete(mRepeatingRequestId, lastFrameNumber);
Yin-Chia Yeh8cd12e92017-09-05 18:14:21 -07001889 // Check if there is already a new repeating request
1890 if (mRepeatingRequestId == repeatingRequestId) {
1891 mRepeatingRequestId = REQUEST_ID_NONE;
1892 }
Chien-Yu Chen2da496f2016-04-14 13:33:00 -07001893 }
1894 }
1895
1896 @Override
Eino-Ville Talvalaacc00952014-08-06 14:31:08 -07001897 public void onDeviceIdle() {
Eino-Ville Talvalae841d4e2013-09-05 09:04:08 -07001898 if (DEBUG) {
1899 Log.d(TAG, "Camera now idle");
1900 }
Eino-Ville Talvala2f75e7d2014-07-15 10:31:54 -07001901 synchronized(mInterfaceLock) {
1902 if (mRemoteDevice == null) return; // Camera already closed
Igor Murashkin49b2b132014-06-18 19:03:00 -07001903
Igor Murashkin21547d62014-06-04 15:21:42 -07001904 if (!CameraDeviceImpl.this.mIdle) {
1905 CameraDeviceImpl.this.mDeviceHandler.post(mCallOnIdle);
Eino-Ville Talvala868d9042013-10-03 11:15:21 -07001906 }
Igor Murashkin21547d62014-06-04 15:21:42 -07001907 CameraDeviceImpl.this.mIdle = true;
Eino-Ville Talvalae841d4e2013-09-05 09:04:08 -07001908 }
1909 }
1910
1911 @Override
Jianing Weid2c3a822014-03-27 18:27:43 -07001912 public void onCaptureStarted(final CaptureResultExtras resultExtras, final long timestamp) {
1913 int requestId = resultExtras.getRequestId();
Eino-Ville Talvalae8df3092014-09-08 14:30:50 -07001914 final long frameNumber = resultExtras.getFrameNumber();
1915
Eino-Ville Talvalae841d4e2013-09-05 09:04:08 -07001916 if (DEBUG) {
Eino-Ville Talvalae8df3092014-09-08 14:30:50 -07001917 Log.d(TAG, "Capture started for id " + requestId + " frame number " + frameNumber);
Eino-Ville Talvalae841d4e2013-09-05 09:04:08 -07001918 }
Eino-Ville Talvalafd887432014-09-04 13:07:40 -07001919 final CaptureCallbackHolder holder;
Eino-Ville Talvalae841d4e2013-09-05 09:04:08 -07001920
Eino-Ville Talvala2f75e7d2014-07-15 10:31:54 -07001921 synchronized(mInterfaceLock) {
1922 if (mRemoteDevice == null) return; // Camera already closed
Igor Murashkin49b2b132014-06-18 19:03:00 -07001923
Eino-Ville Talvalafd887432014-09-04 13:07:40 -07001924 // Get the callback for this frame ID, if there is one
1925 holder = CameraDeviceImpl.this.mCaptureCallbackMap.get(requestId);
Eino-Ville Talvalae841d4e2013-09-05 09:04:08 -07001926
Igor Murashkin49b2b132014-06-18 19:03:00 -07001927 if (holder == null) {
1928 return;
1929 }
Eino-Ville Talvalae841d4e2013-09-05 09:04:08 -07001930
Igor Murashkin49b2b132014-06-18 19:03:00 -07001931 if (isClosed()) return;
Eino-Ville Talvala868d9042013-10-03 11:15:21 -07001932
Igor Murashkin49b2b132014-06-18 19:03:00 -07001933 // Dispatch capture start notice
1934 holder.getHandler().post(
1935 new Runnable() {
1936 @Override
1937 public void run() {
1938 if (!CameraDeviceImpl.this.isClosed()) {
Shuzhen Wangae10eb92017-03-01 12:58:04 -08001939 final int subsequenceId = resultExtras.getSubsequenceId();
1940 final CaptureRequest request = holder.getRequest(subsequenceId);
1941
1942 if (holder.hasBatchedOutputs()) {
1943 // Send derived onCaptureStarted for requests within the batch
1944 final Range<Integer> fpsRange =
1945 request.get(CaptureRequest.CONTROL_AE_TARGET_FPS_RANGE);
1946 for (int i = 0; i < holder.getRequestCount(); i++) {
1947 holder.getCallback().onCaptureStarted(
1948 CameraDeviceImpl.this,
1949 holder.getRequest(i),
1950 timestamp - (subsequenceId - i) *
1951 NANO_PER_SECOND/fpsRange.getUpper(),
1952 frameNumber - (subsequenceId - i));
1953 }
1954 } else {
1955 holder.getCallback().onCaptureStarted(
1956 CameraDeviceImpl.this,
1957 holder.getRequest(resultExtras.getSubsequenceId()),
1958 timestamp, frameNumber);
1959 }
Igor Murashkin49b2b132014-06-18 19:03:00 -07001960 }
Eino-Ville Talvala868d9042013-10-03 11:15:21 -07001961 }
Igor Murashkin49b2b132014-06-18 19:03:00 -07001962 });
1963
1964 }
Igor Murashkin70725502013-06-25 20:27:06 +00001965 }
1966
1967 @Override
Jianing Weid2c3a822014-03-27 18:27:43 -07001968 public void onResultReceived(CameraMetadataNative result,
1969 CaptureResultExtras resultExtras) throws RemoteException {
Ruben Brunk57493682014-05-27 18:58:08 -07001970
Jianing Weid2c3a822014-03-27 18:27:43 -07001971 int requestId = resultExtras.getRequestId();
Igor Murashkinbdf366c2014-07-25 16:54:20 -07001972 long frameNumber = resultExtras.getFrameNumber();
1973
Zhijun Heecb323e2013-07-31 09:40:27 -07001974 if (DEBUG) {
Igor Murashkinbdf366c2014-07-25 16:54:20 -07001975 Log.v(TAG, "Received result frame " + frameNumber + " for id "
Jianing Weibaf0c652014-04-18 17:35:00 -07001976 + requestId);
Zhijun Heecb323e2013-07-31 09:40:27 -07001977 }
Ruben Brunk57493682014-05-27 18:58:08 -07001978
Eino-Ville Talvala2f75e7d2014-07-15 10:31:54 -07001979 synchronized(mInterfaceLock) {
1980 if (mRemoteDevice == null) return; // Camera already closed
Ruben Brunk57493682014-05-27 18:58:08 -07001981
Igor Murashkin49b2b132014-06-18 19:03:00 -07001982 // TODO: Handle CameraCharacteristics access from CaptureResult correctly.
1983 result.set(CameraCharacteristics.LENS_INFO_SHADING_MAP_SIZE,
1984 getCharacteristics().get(CameraCharacteristics.LENS_INFO_SHADING_MAP_SIZE));
Ruben Brunk57493682014-05-27 18:58:08 -07001985
Eino-Ville Talvalafd887432014-09-04 13:07:40 -07001986 final CaptureCallbackHolder holder =
1987 CameraDeviceImpl.this.mCaptureCallbackMap.get(requestId);
Chien-Yu Chen5398a672015-03-19 14:48:43 -07001988 final CaptureRequest request = holder.getRequest(resultExtras.getSubsequenceId());
Igor Murashkin70725502013-06-25 20:27:06 +00001989
Zhijun He83159152014-07-16 11:32:59 -07001990 boolean isPartialResult =
1991 (resultExtras.getPartialResultCount() < mTotalPartialCount);
Chien-Yu Chen5398a672015-03-19 14:48:43 -07001992 boolean isReprocess = request.isReprocess();
Eino-Ville Talvala7a313102013-11-07 14:45:06 -08001993
Eino-Ville Talvalafd887432014-09-04 13:07:40 -07001994 // Check if we have a callback for this
Igor Murashkin49b2b132014-06-18 19:03:00 -07001995 if (holder == null) {
1996 if (DEBUG) {
1997 Log.d(TAG,
1998 "holder is null, early return at frame "
Igor Murashkinbdf366c2014-07-25 16:54:20 -07001999 + frameNumber);
Igor Murashkin49b2b132014-06-18 19:03:00 -07002000 }
Igor Murashkin1e854c52014-08-28 15:21:49 -07002001
Chien-Yu Chen5398a672015-03-19 14:48:43 -07002002 mFrameNumberTracker.updateTracker(frameNumber, /*result*/null, isPartialResult,
2003 isReprocess);
Igor Murashkin1e854c52014-08-28 15:21:49 -07002004
Igor Murashkin49b2b132014-06-18 19:03:00 -07002005 return;
Jianing Wei5a4b02b2014-04-11 09:59:24 -07002006 }
Eino-Ville Talvala868d9042013-10-03 11:15:21 -07002007
Igor Murashkin49b2b132014-06-18 19:03:00 -07002008 if (isClosed()) {
2009 if (DEBUG) {
2010 Log.d(TAG,
2011 "camera is closed, early return at frame "
Igor Murashkinbdf366c2014-07-25 16:54:20 -07002012 + frameNumber);
Eino-Ville Talvala7a313102013-11-07 14:45:06 -08002013 }
Igor Murashkin1e854c52014-08-28 15:21:49 -07002014
Chien-Yu Chen5398a672015-03-19 14:48:43 -07002015 mFrameNumberTracker.updateTracker(frameNumber, /*result*/null, isPartialResult,
2016 isReprocess);
Igor Murashkin49b2b132014-06-18 19:03:00 -07002017 return;
2018 }
Igor Murashkindb075af2014-05-21 10:07:08 -07002019
Igor Murashkin49b2b132014-06-18 19:03:00 -07002020
Igor Murashkin49b2b132014-06-18 19:03:00 -07002021 Runnable resultDispatch = null;
2022
Igor Murashkin1e854c52014-08-28 15:21:49 -07002023 CaptureResult finalResult;
Shuzhen Wangae10eb92017-03-01 12:58:04 -08002024 // Make a copy of the native metadata before it gets moved to a CaptureResult
2025 // object.
2026 final CameraMetadataNative resultCopy;
2027 if (holder.hasBatchedOutputs()) {
2028 resultCopy = new CameraMetadataNative(result);
2029 } else {
2030 resultCopy = null;
2031 }
Igor Murashkin1e854c52014-08-28 15:21:49 -07002032
Igor Murashkin49b2b132014-06-18 19:03:00 -07002033 // Either send a partial result or the final capture completed result
Zhijun He83159152014-07-16 11:32:59 -07002034 if (isPartialResult) {
Igor Murashkin49b2b132014-06-18 19:03:00 -07002035 final CaptureResult resultAsCapture =
Igor Murashkinbdf366c2014-07-25 16:54:20 -07002036 new CaptureResult(result, request, resultExtras);
Igor Murashkin49b2b132014-06-18 19:03:00 -07002037 // Partial result
2038 resultDispatch = new Runnable() {
2039 @Override
2040 public void run() {
Shuzhen Wangae10eb92017-03-01 12:58:04 -08002041 if (!CameraDeviceImpl.this.isClosed()) {
2042 if (holder.hasBatchedOutputs()) {
2043 // Send derived onCaptureProgressed for requests within
2044 // the batch.
2045 for (int i = 0; i < holder.getRequestCount(); i++) {
2046 CameraMetadataNative resultLocal =
2047 new CameraMetadataNative(resultCopy);
2048 CaptureResult resultInBatch = new CaptureResult(
2049 resultLocal, holder.getRequest(i), resultExtras);
2050
2051 holder.getCallback().onCaptureProgressed(
2052 CameraDeviceImpl.this,
2053 holder.getRequest(i),
2054 resultInBatch);
2055 }
2056 } else {
2057 holder.getCallback().onCaptureProgressed(
2058 CameraDeviceImpl.this,
2059 request,
2060 resultAsCapture);
2061 }
Igor Murashkin49b2b132014-06-18 19:03:00 -07002062 }
Eino-Ville Talvala868d9042013-10-03 11:15:21 -07002063 }
Igor Murashkin49b2b132014-06-18 19:03:00 -07002064 };
Igor Murashkin1e854c52014-08-28 15:21:49 -07002065 finalResult = resultAsCapture;
Igor Murashkin49b2b132014-06-18 19:03:00 -07002066 } else {
Igor Murashkin1e854c52014-08-28 15:21:49 -07002067 List<CaptureResult> partialResults =
2068 mFrameNumberTracker.popPartialResults(frameNumber);
2069
Shuzhen Wangae10eb92017-03-01 12:58:04 -08002070 final long sensorTimestamp =
2071 result.get(CaptureResult.SENSOR_TIMESTAMP);
2072 final Range<Integer> fpsRange =
2073 request.get(CaptureRequest.CONTROL_AE_TARGET_FPS_RANGE);
2074 final int subsequenceId = resultExtras.getSubsequenceId();
Chien-Yu Chen7a316f6b2015-04-13 14:31:45 -07002075 final TotalCaptureResult resultAsCapture = new TotalCaptureResult(result,
2076 request, resultExtras, partialResults, holder.getSessionId());
Igor Murashkin49b2b132014-06-18 19:03:00 -07002077 // Final capture result
2078 resultDispatch = new Runnable() {
2079 @Override
2080 public void run() {
2081 if (!CameraDeviceImpl.this.isClosed()){
Shuzhen Wangae10eb92017-03-01 12:58:04 -08002082 if (holder.hasBatchedOutputs()) {
2083 // Send derived onCaptureCompleted for requests within
2084 // the batch.
2085 for (int i = 0; i < holder.getRequestCount(); i++) {
2086 resultCopy.set(CaptureResult.SENSOR_TIMESTAMP,
2087 sensorTimestamp - (subsequenceId - i) *
2088 NANO_PER_SECOND/fpsRange.getUpper());
2089 CameraMetadataNative resultLocal =
2090 new CameraMetadataNative(resultCopy);
2091 TotalCaptureResult resultInBatch = new TotalCaptureResult(
2092 resultLocal, holder.getRequest(i), resultExtras,
2093 partialResults, holder.getSessionId());
2094
2095 holder.getCallback().onCaptureCompleted(
2096 CameraDeviceImpl.this,
2097 holder.getRequest(i),
2098 resultInBatch);
2099 }
2100 } else {
2101 holder.getCallback().onCaptureCompleted(
2102 CameraDeviceImpl.this,
2103 request,
2104 resultAsCapture);
2105 }
Igor Murashkin49b2b132014-06-18 19:03:00 -07002106 }
2107 }
2108 };
Igor Murashkin1e854c52014-08-28 15:21:49 -07002109 finalResult = resultAsCapture;
Igor Murashkin49b2b132014-06-18 19:03:00 -07002110 }
Jianing Weid2c3a822014-03-27 18:27:43 -07002111
Igor Murashkin49b2b132014-06-18 19:03:00 -07002112 holder.getHandler().post(resultDispatch);
2113
Igor Murashkin1e854c52014-08-28 15:21:49 -07002114 // Collect the partials for a total result; or mark the frame as totally completed
Chien-Yu Chen7a316f6b2015-04-13 14:31:45 -07002115 mFrameNumberTracker.updateTracker(frameNumber, finalResult, isPartialResult,
2116 isReprocess);
Igor Murashkin1e854c52014-08-28 15:21:49 -07002117
Igor Murashkin49b2b132014-06-18 19:03:00 -07002118 // Fire onCaptureSequenceCompleted
Zhijun He83159152014-07-16 11:32:59 -07002119 if (!isPartialResult) {
Igor Murashkin49b2b132014-06-18 19:03:00 -07002120 checkAndFireSequenceComplete();
2121 }
Jianing Weid2c3a822014-03-27 18:27:43 -07002122 }
Igor Murashkin70725502013-06-25 20:27:06 +00002123 }
2124
Eino-Ville Talvalaad916f72015-04-11 12:09:11 -07002125 @Override
2126 public void onPrepared(int streamId) {
2127 final OutputConfiguration output;
2128 final StateCallbackKK sessionCallback;
2129
2130 if (DEBUG) {
2131 Log.v(TAG, "Stream " + streamId + " is prepared");
2132 }
2133
2134 synchronized(mInterfaceLock) {
2135 output = mConfiguredOutputs.get(streamId);
2136 sessionCallback = mSessionStateCallback;
2137 }
2138
2139 if (sessionCallback == null) return;
2140
2141 if (output == null) {
2142 Log.w(TAG, "onPrepared invoked for unknown output Surface");
2143 return;
2144 }
Shuzhen Wange0a66672017-03-21 12:05:14 -07002145 final List<Surface> surfaces = output.getSurfaces();
2146 for (Surface surface : surfaces) {
2147 sessionCallback.onSurfacePrepared(surface);
2148 }
Eino-Ville Talvalaad916f72015-04-11 12:09:11 -07002149 }
2150
Shuzhen Wang88f1af22016-09-30 10:29:28 -07002151 @Override
2152 public void onRequestQueueEmpty() {
2153 final StateCallbackKK sessionCallback;
2154
2155 if (DEBUG) {
2156 Log.v(TAG, "Request queue becomes empty");
2157 }
2158
2159 synchronized(mInterfaceLock) {
2160 sessionCallback = mSessionStateCallback;
2161 }
2162
2163 if (sessionCallback == null) return;
2164
2165 sessionCallback.onRequestQueueEmpty();
2166 }
2167
Eino-Ville Talvalaacc00952014-08-06 14:31:08 -07002168 /**
2169 * Called by onDeviceError for handling single-capture failures.
2170 */
2171 private void onCaptureErrorLocked(int errorCode, CaptureResultExtras resultExtras) {
2172
2173 final int requestId = resultExtras.getRequestId();
2174 final int subsequenceId = resultExtras.getSubsequenceId();
2175 final long frameNumber = resultExtras.getFrameNumber();
Eino-Ville Talvalafd887432014-09-04 13:07:40 -07002176 final CaptureCallbackHolder holder =
2177 CameraDeviceImpl.this.mCaptureCallbackMap.get(requestId);
Eino-Ville Talvalaacc00952014-08-06 14:31:08 -07002178
2179 final CaptureRequest request = holder.getRequest(subsequenceId);
2180
Eino-Ville Talvala3e0023a2016-03-06 18:40:01 -08002181 Runnable failureDispatch = null;
Eino-Ville Talvalaacc00952014-08-06 14:31:08 -07002182 if (errorCode == ERROR_CAMERA_BUFFER) {
Shuzhen Wang9c663d42016-10-28 23:25:02 -07002183 // Because 1 stream id could map to multiple surfaces, we need to specify both
2184 // streamId and surfaceId.
2185 List<Surface> surfaces =
2186 mConfiguredOutputs.get(resultExtras.getErrorStreamId()).getSurfaces();
2187 for (Surface surface : surfaces) {
2188 if (!request.containsTarget(surface)) {
2189 continue;
Eino-Ville Talvala3e0023a2016-03-06 18:40:01 -08002190 }
Shuzhen Wang9c663d42016-10-28 23:25:02 -07002191 if (DEBUG) {
2192 Log.v(TAG, String.format("Lost output buffer reported for frame %d, target %s",
2193 frameNumber, surface));
2194 }
2195 failureDispatch = new Runnable() {
2196 @Override
2197 public void run() {
2198 if (!CameraDeviceImpl.this.isClosed()){
2199 holder.getCallback().onCaptureBufferLost(
2200 CameraDeviceImpl.this,
2201 request,
2202 surface,
2203 frameNumber);
2204 }
2205 }
2206 };
2207 // Dispatch the failure callback
2208 holder.getHandler().post(failureDispatch);
2209 }
Eino-Ville Talvala3e0023a2016-03-06 18:40:01 -08002210 } else {
2211 boolean mayHaveBuffers = (errorCode == ERROR_CAMERA_RESULT);
Eino-Ville Talvalaacc00952014-08-06 14:31:08 -07002212
Eino-Ville Talvala3e0023a2016-03-06 18:40:01 -08002213 // This is only approximate - exact handling needs the camera service and HAL to
2214 // disambiguate between request failures to due abort and due to real errors. For
2215 // now, assume that if the session believes we're mid-abort, then the error is due
2216 // to abort.
2217 int reason = (mCurrentSession != null && mCurrentSession.isAborting()) ?
2218 CaptureFailure.REASON_FLUSHED :
2219 CaptureFailure.REASON_ERROR;
2220
2221 final CaptureFailure failure = new CaptureFailure(
2222 request,
2223 reason,
2224 /*dropped*/ mayHaveBuffers,
2225 requestId,
2226 frameNumber);
2227
2228 failureDispatch = new Runnable() {
2229 @Override
2230 public void run() {
2231 if (!CameraDeviceImpl.this.isClosed()){
2232 holder.getCallback().onCaptureFailed(
2233 CameraDeviceImpl.this,
2234 request,
2235 failure);
2236 }
2237 }
2238 };
2239
2240 // Fire onCaptureSequenceCompleted if appropriate
2241 if (DEBUG) {
2242 Log.v(TAG, String.format("got error frame %d", frameNumber));
2243 }
2244 mFrameNumberTracker.updateTracker(frameNumber, /*error*/true, request.isReprocess());
2245 checkAndFireSequenceComplete();
Shuzhen Wang9c663d42016-10-28 23:25:02 -07002246
2247 // Dispatch the failure callback
2248 holder.getHandler().post(failureDispatch);
Eino-Ville Talvalaacc00952014-08-06 14:31:08 -07002249 }
Eino-Ville Talvala3e0023a2016-03-06 18:40:01 -08002250
Eino-Ville Talvalaacc00952014-08-06 14:31:08 -07002251 }
2252
2253 } // public class CameraDeviceCallbacks
Igor Murashkin70725502013-06-25 20:27:06 +00002254
Eino-Ville Talvalae841d4e2013-09-05 09:04:08 -07002255 /**
Igor Murashkin0b27d342014-05-30 09:45:05 -07002256 * Default handler management.
2257 *
2258 * <p>
2259 * If handler is null, get the current thread's
2260 * Looper to create a Handler with. If no looper exists, throw {@code IllegalArgumentException}.
2261 * </p>
Eino-Ville Talvalae841d4e2013-09-05 09:04:08 -07002262 */
Igor Murashkin0b27d342014-05-30 09:45:05 -07002263 static Handler checkHandler(Handler handler) {
Eino-Ville Talvalae841d4e2013-09-05 09:04:08 -07002264 if (handler == null) {
2265 Looper looper = Looper.myLooper();
2266 if (looper == null) {
2267 throw new IllegalArgumentException(
2268 "No handler given, and current thread has no looper!");
2269 }
2270 handler = new Handler(looper);
2271 }
2272 return handler;
2273 }
2274
Eino-Ville Talvala7875a882014-07-31 12:47:07 -07002275 /**
Eino-Ville Talvalafd887432014-09-04 13:07:40 -07002276 * Default handler management, conditional on there being a callback.
Eino-Ville Talvala7875a882014-07-31 12:47:07 -07002277 *
Eino-Ville Talvalafd887432014-09-04 13:07:40 -07002278 * <p>If the callback isn't null, check the handler, otherwise pass it through.</p>
Eino-Ville Talvala7875a882014-07-31 12:47:07 -07002279 */
Eino-Ville Talvalafd887432014-09-04 13:07:40 -07002280 static <T> Handler checkHandler(Handler handler, T callback) {
2281 if (callback != null) {
Eino-Ville Talvala7875a882014-07-31 12:47:07 -07002282 return checkHandler(handler);
2283 }
2284 return handler;
2285 }
2286
Eino-Ville Talvala7fcb35782014-06-03 18:30:27 -07002287 private void checkIfCameraClosedOrInError() throws CameraAccessException {
Ruben Brunka45aa0d2015-06-03 19:40:22 -07002288 if (mRemoteDevice == null) {
2289 throw new IllegalStateException("CameraDevice was already closed");
2290 }
Eino-Ville Talvala7fcb35782014-06-03 18:30:27 -07002291 if (mInError) {
2292 throw new CameraAccessException(CameraAccessException.CAMERA_ERROR,
2293 "The camera device has encountered a serious error");
2294 }
Zhijun He7f4d3142013-07-23 07:54:38 -07002295 }
Eino-Ville Talvala868d9042013-10-03 11:15:21 -07002296
Igor Murashkin49b2b132014-06-18 19:03:00 -07002297 /** Whether the camera device has started to close (may not yet have finished) */
Eino-Ville Talvala868d9042013-10-03 11:15:21 -07002298 private boolean isClosed() {
Ruben Brunk0953b642015-06-11 16:12:35 -07002299 return mClosing.get();
Eino-Ville Talvala868d9042013-10-03 11:15:21 -07002300 }
Ruben Brunk57493682014-05-27 18:58:08 -07002301
2302 private CameraCharacteristics getCharacteristics() {
2303 return mCharacteristics;
2304 }
Zhijun Heb1300e32015-05-28 12:51:52 -07002305
Shuzhen Wang78392932016-03-04 15:26:01 -08002306 /**
2307 * Listener for binder death.
2308 *
2309 * <p> Handle binder death for ICameraDeviceUser. Trigger onError.</p>
2310 */
Zhijun Hec8b181e2016-05-30 14:54:39 -07002311 @Override
Shuzhen Wang78392932016-03-04 15:26:01 -08002312 public void binderDied() {
2313 Log.w(TAG, "CameraDevice " + mCameraId + " died unexpectedly");
2314
2315 if (mRemoteDevice == null) {
2316 return; // Camera already closed
2317 }
2318
2319 mInError = true;
2320 Runnable r = new Runnable() {
2321 @Override
2322 public void run() {
2323 if (!isClosed()) {
2324 mDeviceCallback.onError(CameraDeviceImpl.this,
Eino-Ville Talvala50eebe02016-06-09 17:07:13 -07002325 StateCallback.ERROR_CAMERA_SERVICE);
Shuzhen Wang78392932016-03-04 15:26:01 -08002326 }
2327 }
2328 };
2329 CameraDeviceImpl.this.mDeviceHandler.post(r);
2330 }
Igor Murashkin70725502013-06-25 20:27:06 +00002331}