Merge "Camera2 API: enable burst capture and repeating burst."
This commit is contained in:
20
core/java/android/hardware/camera2/CaptureResultExtras.aidl
Normal file
20
core/java/android/hardware/camera2/CaptureResultExtras.aidl
Normal file
@@ -0,0 +1,20 @@
|
||||
/*
|
||||
* Copyright (C) 2014 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package android.hardware.camera2;
|
||||
|
||||
/** @hide */
|
||||
parcelable CaptureResultExtras;
|
||||
90
core/java/android/hardware/camera2/CaptureResultExtras.java
Normal file
90
core/java/android/hardware/camera2/CaptureResultExtras.java
Normal file
@@ -0,0 +1,90 @@
|
||||
/*
|
||||
* Copyright (C) 2014 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package android.hardware.camera2;
|
||||
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
|
||||
/**
|
||||
* @hide
|
||||
*/
|
||||
public class CaptureResultExtras implements Parcelable {
|
||||
private int requestId;
|
||||
private int subsequenceId;
|
||||
private int afTriggerId;
|
||||
private int precaptureTriggerId;
|
||||
private long frameNumber;
|
||||
|
||||
public static final Parcelable.Creator<CaptureResultExtras> CREATOR =
|
||||
new Parcelable.Creator<CaptureResultExtras>() {
|
||||
@Override
|
||||
public CaptureResultExtras createFromParcel(Parcel in) {
|
||||
return new CaptureResultExtras(in);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CaptureResultExtras[] newArray(int size) {
|
||||
return new CaptureResultExtras[size];
|
||||
}
|
||||
};
|
||||
|
||||
private CaptureResultExtras(Parcel in) {
|
||||
readFromParcel(in);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int describeContents() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeToParcel(Parcel dest, int flags) {
|
||||
dest.writeInt(requestId);
|
||||
dest.writeInt(subsequenceId);
|
||||
dest.writeInt(afTriggerId);
|
||||
dest.writeInt(precaptureTriggerId);
|
||||
dest.writeLong(frameNumber);
|
||||
}
|
||||
|
||||
public void readFromParcel(Parcel in) {
|
||||
requestId = in.readInt();
|
||||
subsequenceId = in.readInt();
|
||||
afTriggerId = in.readInt();
|
||||
precaptureTriggerId = in.readInt();
|
||||
frameNumber = in.readLong();
|
||||
}
|
||||
|
||||
public int getRequestId() {
|
||||
return requestId;
|
||||
}
|
||||
|
||||
public int getSubsequenceId() {
|
||||
return subsequenceId;
|
||||
}
|
||||
|
||||
public int getAfTriggerId() {
|
||||
return afTriggerId;
|
||||
}
|
||||
|
||||
public int getPrecaptureTriggerId() {
|
||||
return precaptureTriggerId;
|
||||
}
|
||||
|
||||
public long getFrameNumber() {
|
||||
return frameNumber;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -17,6 +17,7 @@
|
||||
package android.hardware.camera2;
|
||||
|
||||
import android.hardware.camera2.impl.CameraMetadataNative;
|
||||
import android.hardware.camera2.CaptureResultExtras;
|
||||
|
||||
/** @hide */
|
||||
interface ICameraDeviceCallbacks
|
||||
@@ -25,8 +26,9 @@ interface ICameraDeviceCallbacks
|
||||
* Keep up-to-date with frameworks/av/include/camera/camera2/ICameraDeviceCallbacks.h
|
||||
*/
|
||||
|
||||
oneway void onCameraError(int errorCode);
|
||||
oneway void onCameraError(int errorCode, in CaptureResultExtras resultExtras);
|
||||
oneway void onCameraIdle();
|
||||
oneway void onCaptureStarted(int requestId, long timestamp);
|
||||
oneway void onResultReceived(int requestId, in CameraMetadataNative result);
|
||||
oneway void onCaptureStarted(in CaptureResultExtras resultExtras, long timestamp);
|
||||
oneway void onResultReceived(in CameraMetadataNative result,
|
||||
in CaptureResultExtras resultExtras);
|
||||
}
|
||||
|
||||
@@ -20,6 +20,8 @@ import android.view.Surface;
|
||||
import android.hardware.camera2.impl.CameraMetadataNative;
|
||||
import android.hardware.camera2.CaptureRequest;
|
||||
|
||||
import android.hardware.camera2.LongParcelable;
|
||||
|
||||
/** @hide */
|
||||
interface ICameraDeviceUser
|
||||
{
|
||||
@@ -31,9 +33,13 @@ interface ICameraDeviceUser
|
||||
// ints here are status_t
|
||||
|
||||
// non-negative value is the requestId. negative value is status_t
|
||||
int submitRequest(in CaptureRequest request, boolean streaming);
|
||||
int submitRequest(in CaptureRequest request, boolean streaming,
|
||||
out LongParcelable lastFrameNumber);
|
||||
|
||||
int cancelRequest(int requestId);
|
||||
int submitRequestList(in List<CaptureRequest> requestList, boolean streaming,
|
||||
out LongParcelable lastFrameNumber);
|
||||
|
||||
int cancelRequest(int requestId, out LongParcelable lastFrameNumber);
|
||||
|
||||
int deleteStream(int streamId);
|
||||
|
||||
@@ -46,5 +52,5 @@ interface ICameraDeviceUser
|
||||
|
||||
int waitUntilIdle();
|
||||
|
||||
int flush();
|
||||
int flush(out LongParcelable lastFrameNumber);
|
||||
}
|
||||
|
||||
20
core/java/android/hardware/camera2/LongParcelable.aidl
Normal file
20
core/java/android/hardware/camera2/LongParcelable.aidl
Normal file
@@ -0,0 +1,20 @@
|
||||
/*
|
||||
* Copyright (C) 2014 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package android.hardware.camera2;
|
||||
|
||||
/** @hide */
|
||||
parcelable LongParcelable;
|
||||
74
core/java/android/hardware/camera2/LongParcelable.java
Normal file
74
core/java/android/hardware/camera2/LongParcelable.java
Normal file
@@ -0,0 +1,74 @@
|
||||
/*
|
||||
* Copyright (C) 2014 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package android.hardware.camera2;
|
||||
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
|
||||
/**
|
||||
* @hide
|
||||
*/
|
||||
public class LongParcelable implements Parcelable {
|
||||
private long number;
|
||||
|
||||
public LongParcelable() {
|
||||
this.number = 0;
|
||||
}
|
||||
|
||||
public LongParcelable(long number) {
|
||||
this.number = number;
|
||||
}
|
||||
|
||||
public static final Parcelable.Creator<LongParcelable> CREATOR =
|
||||
new Parcelable.Creator<LongParcelable>() {
|
||||
@Override
|
||||
public LongParcelable createFromParcel(Parcel in) {
|
||||
return new LongParcelable(in);
|
||||
}
|
||||
|
||||
@Override
|
||||
public LongParcelable[] newArray(int size) {
|
||||
return new LongParcelable[size];
|
||||
}
|
||||
};
|
||||
|
||||
private LongParcelable(Parcel in) {
|
||||
readFromParcel(in);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int describeContents() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeToParcel(Parcel dest, int flags) {
|
||||
dest.writeLong(number);
|
||||
}
|
||||
|
||||
public void readFromParcel(Parcel in) {
|
||||
number = in.readLong();
|
||||
}
|
||||
|
||||
public long getNumber() {
|
||||
return number;
|
||||
}
|
||||
|
||||
public void setNumber(long number) {
|
||||
this.number = number;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -21,8 +21,10 @@ import static android.hardware.camera2.CameraAccessException.CAMERA_IN_USE;
|
||||
import android.hardware.camera2.CameraAccessException;
|
||||
import android.hardware.camera2.CaptureRequest;
|
||||
import android.hardware.camera2.CaptureResult;
|
||||
import android.hardware.camera2.CaptureResultExtras;
|
||||
import android.hardware.camera2.ICameraDeviceCallbacks;
|
||||
import android.hardware.camera2.ICameraDeviceUser;
|
||||
import android.hardware.camera2.LongParcelable;
|
||||
import android.hardware.camera2.utils.CameraBinderDecorator;
|
||||
import android.hardware.camera2.utils.CameraRuntimeException;
|
||||
import android.os.Handler;
|
||||
@@ -33,10 +35,12 @@ import android.util.Log;
|
||||
import android.util.SparseArray;
|
||||
import android.view.Surface;
|
||||
|
||||
import java.util.AbstractMap.SimpleEntry;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.TreeSet;
|
||||
|
||||
/**
|
||||
* HAL2.1+ implementation of CameraDevice. Use CameraManager#open to instantiate
|
||||
@@ -69,10 +73,24 @@ public class CameraDevice implements android.hardware.camera2.CameraDevice {
|
||||
|
||||
private final String mCameraId;
|
||||
|
||||
/**
|
||||
* A list tracking request and its expected last frame.
|
||||
* Updated when calling ICameraDeviceUser methods.
|
||||
*/
|
||||
private final List<SimpleEntry</*frameNumber*/Long, /*requestId*/Integer>>
|
||||
mFrameNumberRequestPairs = new ArrayList<SimpleEntry<Long, Integer>>();
|
||||
|
||||
/**
|
||||
* An object tracking received frame numbers.
|
||||
* Updated when receiving callbacks from ICameraDeviceCallbacks.
|
||||
*/
|
||||
private final FrameNumberTracker mFrameNumberTracker = new FrameNumberTracker();
|
||||
|
||||
// Runnables for all state transitions, except error, which needs the
|
||||
// error code argument
|
||||
|
||||
private final Runnable mCallOnOpened = new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (!CameraDevice.this.isClosed()) {
|
||||
mDeviceListener.onOpened(CameraDevice.this);
|
||||
@@ -81,6 +99,7 @@ public class CameraDevice implements android.hardware.camera2.CameraDevice {
|
||||
};
|
||||
|
||||
private final Runnable mCallOnUnconfigured = new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (!CameraDevice.this.isClosed()) {
|
||||
mDeviceListener.onUnconfigured(CameraDevice.this);
|
||||
@@ -89,6 +108,7 @@ public class CameraDevice implements android.hardware.camera2.CameraDevice {
|
||||
};
|
||||
|
||||
private final Runnable mCallOnActive = new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (!CameraDevice.this.isClosed()) {
|
||||
mDeviceListener.onActive(CameraDevice.this);
|
||||
@@ -97,6 +117,7 @@ public class CameraDevice implements android.hardware.camera2.CameraDevice {
|
||||
};
|
||||
|
||||
private final Runnable mCallOnBusy = new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (!CameraDevice.this.isClosed()) {
|
||||
mDeviceListener.onBusy(CameraDevice.this);
|
||||
@@ -105,12 +126,14 @@ public class CameraDevice implements android.hardware.camera2.CameraDevice {
|
||||
};
|
||||
|
||||
private final Runnable mCallOnClosed = new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
mDeviceListener.onClosed(CameraDevice.this);
|
||||
}
|
||||
};
|
||||
|
||||
private final Runnable mCallOnIdle = new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (!CameraDevice.this.isClosed()) {
|
||||
mDeviceListener.onIdle(CameraDevice.this);
|
||||
@@ -119,6 +142,7 @@ public class CameraDevice implements android.hardware.camera2.CameraDevice {
|
||||
};
|
||||
|
||||
private final Runnable mCallOnDisconnected = new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (!CameraDevice.this.isClosed()) {
|
||||
mDeviceListener.onDisconnected(CameraDevice.this);
|
||||
@@ -249,22 +273,26 @@ public class CameraDevice implements android.hardware.camera2.CameraDevice {
|
||||
@Override
|
||||
public int capture(CaptureRequest request, CaptureListener listener, Handler handler)
|
||||
throws CameraAccessException {
|
||||
return submitCaptureRequest(request, listener, handler, /*streaming*/false);
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "calling capture");
|
||||
}
|
||||
List<CaptureRequest> requestList = new ArrayList<CaptureRequest>();
|
||||
requestList.add(request);
|
||||
return submitCaptureRequest(requestList, listener, handler, /*streaming*/false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int captureBurst(List<CaptureRequest> requests, CaptureListener listener,
|
||||
Handler handler) throws CameraAccessException {
|
||||
// TODO: remove this. Throw IAE if the request is null or empty. Need to update API doc.
|
||||
if (requests.isEmpty()) {
|
||||
Log.w(TAG, "Capture burst request list is empty, do nothing!");
|
||||
return -1;
|
||||
}
|
||||
// TODO
|
||||
throw new UnsupportedOperationException("Burst capture implemented yet");
|
||||
|
||||
return submitCaptureRequest(requests, listener, handler, /*streaming*/false);
|
||||
}
|
||||
|
||||
private int submitCaptureRequest(CaptureRequest request, CaptureListener listener,
|
||||
private int submitCaptureRequest(List<CaptureRequest> requestList, CaptureListener listener,
|
||||
Handler handler, boolean repeating) throws CameraAccessException {
|
||||
|
||||
// Need a valid handler, or current thread needs to have a looper, if
|
||||
@@ -281,8 +309,13 @@ public class CameraDevice implements android.hardware.camera2.CameraDevice {
|
||||
stopRepeating();
|
||||
}
|
||||
|
||||
LongParcelable lastFrameNumberRef = new LongParcelable();
|
||||
try {
|
||||
requestId = mRemoteDevice.submitRequest(request, repeating);
|
||||
requestId = mRemoteDevice.submitRequestList(requestList, repeating,
|
||||
/*out*/lastFrameNumberRef);
|
||||
if (!repeating) {
|
||||
Log.v(TAG, "last frame number " + lastFrameNumberRef.getNumber());
|
||||
}
|
||||
} catch (CameraRuntimeException e) {
|
||||
throw e.asChecked();
|
||||
} catch (RemoteException e) {
|
||||
@@ -290,12 +323,29 @@ public class CameraDevice implements android.hardware.camera2.CameraDevice {
|
||||
return -1;
|
||||
}
|
||||
if (listener != null) {
|
||||
mCaptureListenerMap.put(requestId, new CaptureListenerHolder(listener, request,
|
||||
handler, repeating));
|
||||
mCaptureListenerMap.put(requestId, new CaptureListenerHolder(listener,
|
||||
requestList, handler, repeating));
|
||||
}
|
||||
|
||||
long lastFrameNumber = lastFrameNumberRef.getNumber();
|
||||
/**
|
||||
* If it's the first repeating request, then returned lastFrameNumber can be
|
||||
* negative. Otherwise, it should always be non-negative.
|
||||
*/
|
||||
if (((lastFrameNumber < 0) && (requestId > 0))
|
||||
|| ((lastFrameNumber < 0) && (!repeating))) {
|
||||
throw new AssertionError(String.format("returned bad frame number %d",
|
||||
lastFrameNumber));
|
||||
}
|
||||
if (repeating) {
|
||||
if (mRepeatingRequestId != REQUEST_ID_NONE) {
|
||||
mFrameNumberRequestPairs.add(
|
||||
new SimpleEntry<Long, Integer>(lastFrameNumber, mRepeatingRequestId));
|
||||
}
|
||||
mRepeatingRequestId = requestId;
|
||||
} else {
|
||||
mFrameNumberRequestPairs.add(
|
||||
new SimpleEntry<Long, Integer>(lastFrameNumber, requestId));
|
||||
}
|
||||
|
||||
if (mIdle) {
|
||||
@@ -310,18 +360,20 @@ public class CameraDevice implements android.hardware.camera2.CameraDevice {
|
||||
@Override
|
||||
public int setRepeatingRequest(CaptureRequest request, CaptureListener listener,
|
||||
Handler handler) throws CameraAccessException {
|
||||
return submitCaptureRequest(request, listener, handler, /*streaming*/true);
|
||||
List<CaptureRequest> requestList = new ArrayList<CaptureRequest>();
|
||||
requestList.add(request);
|
||||
return submitCaptureRequest(requestList, listener, handler, /*streaming*/true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int setRepeatingBurst(List<CaptureRequest> requests, CaptureListener listener,
|
||||
Handler handler) throws CameraAccessException {
|
||||
// TODO: remove this. Throw IAE if the request is null or empty. Need to update API doc.
|
||||
if (requests.isEmpty()) {
|
||||
Log.w(TAG, "Set Repeating burst request list is empty, do nothing!");
|
||||
return -1;
|
||||
}
|
||||
// TODO
|
||||
throw new UnsupportedOperationException("Burst capture implemented yet");
|
||||
return submitCaptureRequest(requests, listener, handler, /*streaming*/true);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -340,7 +392,15 @@ public class CameraDevice implements android.hardware.camera2.CameraDevice {
|
||||
}
|
||||
|
||||
try {
|
||||
mRemoteDevice.cancelRequest(requestId);
|
||||
LongParcelable lastFrameNumberRef = new LongParcelable();
|
||||
mRemoteDevice.cancelRequest(requestId, /*out*/lastFrameNumberRef);
|
||||
long lastFrameNumber = lastFrameNumberRef.getNumber();
|
||||
if ((lastFrameNumber < 0) && (requestId > 0)) {
|
||||
throw new AssertionError(String.format("returned bad frame number %d",
|
||||
lastFrameNumber));
|
||||
}
|
||||
mFrameNumberRequestPairs.add(
|
||||
new SimpleEntry<Long, Integer>(lastFrameNumber, requestId));
|
||||
} catch (CameraRuntimeException e) {
|
||||
throw e.asChecked();
|
||||
} catch (RemoteException e) {
|
||||
@@ -379,7 +439,17 @@ public class CameraDevice implements android.hardware.camera2.CameraDevice {
|
||||
|
||||
mDeviceHandler.post(mCallOnBusy);
|
||||
try {
|
||||
mRemoteDevice.flush();
|
||||
LongParcelable lastFrameNumberRef = new LongParcelable();
|
||||
mRemoteDevice.flush(/*out*/lastFrameNumberRef);
|
||||
if (mRepeatingRequestId != REQUEST_ID_NONE) {
|
||||
long lastFrameNumber = lastFrameNumberRef.getNumber();
|
||||
if (lastFrameNumber < 0) {
|
||||
Log.e(TAG, String.format("returned bad frame number %d", lastFrameNumber));
|
||||
}
|
||||
mFrameNumberRequestPairs.add(
|
||||
new SimpleEntry<Long, Integer>(lastFrameNumber, mRepeatingRequestId));
|
||||
mRepeatingRequestId = REQUEST_ID_NONE;
|
||||
}
|
||||
} catch (CameraRuntimeException e) {
|
||||
throw e.asChecked();
|
||||
} catch (RemoteException e) {
|
||||
@@ -425,18 +495,18 @@ public class CameraDevice implements android.hardware.camera2.CameraDevice {
|
||||
|
||||
private final boolean mRepeating;
|
||||
private final CaptureListener mListener;
|
||||
private final CaptureRequest mRequest;
|
||||
private final List<CaptureRequest> mRequestList;
|
||||
private final Handler mHandler;
|
||||
|
||||
CaptureListenerHolder(CaptureListener listener, CaptureRequest request, Handler handler,
|
||||
boolean repeating) {
|
||||
CaptureListenerHolder(CaptureListener listener, List<CaptureRequest> requestList,
|
||||
Handler handler, boolean repeating) {
|
||||
if (listener == null || handler == null) {
|
||||
throw new UnsupportedOperationException(
|
||||
"Must have a valid handler and a valid listener");
|
||||
}
|
||||
mRepeating = repeating;
|
||||
mHandler = handler;
|
||||
mRequest = request;
|
||||
mRequestList = new ArrayList<CaptureRequest>(requestList);
|
||||
mListener = listener;
|
||||
}
|
||||
|
||||
@@ -448,8 +518,24 @@ public class CameraDevice implements android.hardware.camera2.CameraDevice {
|
||||
return mListener;
|
||||
}
|
||||
|
||||
public CaptureRequest getRequest(int subsequenceId) {
|
||||
if (subsequenceId >= mRequestList.size()) {
|
||||
throw new IllegalArgumentException(
|
||||
String.format(
|
||||
"Requested subsequenceId %d is larger than request list size %d.",
|
||||
subsequenceId, mRequestList.size()));
|
||||
} else {
|
||||
if (subsequenceId < 0) {
|
||||
throw new IllegalArgumentException(String.format(
|
||||
"Requested subsequenceId %d is negative", subsequenceId));
|
||||
} else {
|
||||
return mRequestList.get(subsequenceId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public CaptureRequest getRequest() {
|
||||
return mRequest;
|
||||
return getRequest(0);
|
||||
}
|
||||
|
||||
public Handler getHandler() {
|
||||
@@ -458,6 +544,105 @@ public class CameraDevice implements android.hardware.camera2.CameraDevice {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* This class tracks the last frame number for submitted requests.
|
||||
*/
|
||||
public class FrameNumberTracker {
|
||||
|
||||
private long mCompletedFrameNumber = -1;
|
||||
private final TreeSet<Long> mFutureErrorSet = new TreeSet<Long>();
|
||||
|
||||
private void update() {
|
||||
Iterator<Long> iter = mFutureErrorSet.iterator();
|
||||
while (iter.hasNext()) {
|
||||
long errorFrameNumber = iter.next();
|
||||
if (errorFrameNumber == mCompletedFrameNumber + 1) {
|
||||
mCompletedFrameNumber++;
|
||||
iter.remove();
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This function is called every time when a result or an error is received.
|
||||
* @param frameNumber: the frame number corresponding to the result or error
|
||||
* @param isError: true if it is an error, false if it is not an error
|
||||
*/
|
||||
public void updateTracker(long frameNumber, boolean isError) {
|
||||
if (isError) {
|
||||
mFutureErrorSet.add(frameNumber);
|
||||
} else {
|
||||
/**
|
||||
* HAL cannot send an OnResultReceived for frame N unless it knows for
|
||||
* sure that all frames prior to N have either errored out or completed.
|
||||
* So if the current frame is not an error, then all previous frames
|
||||
* should have arrived. The following line checks whether this holds.
|
||||
*/
|
||||
if (frameNumber != mCompletedFrameNumber + 1) {
|
||||
throw new AssertionError(String.format(
|
||||
"result frame number %d comes out of order",
|
||||
frameNumber));
|
||||
}
|
||||
mCompletedFrameNumber++;
|
||||
}
|
||||
update();
|
||||
}
|
||||
|
||||
public long getCompletedFrameNumber() {
|
||||
return mCompletedFrameNumber;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void checkAndFireSequenceComplete() {
|
||||
long completedFrameNumber = mFrameNumberTracker.getCompletedFrameNumber();
|
||||
Iterator<SimpleEntry<Long, Integer> > iter = mFrameNumberRequestPairs.iterator();
|
||||
while (iter.hasNext()) {
|
||||
final SimpleEntry<Long, Integer> frameNumberRequestPair = iter.next();
|
||||
if (frameNumberRequestPair.getKey() <= completedFrameNumber) {
|
||||
|
||||
// remove request from mCaptureListenerMap
|
||||
final int requestId = frameNumberRequestPair.getValue();
|
||||
final CaptureListenerHolder holder;
|
||||
synchronized (mLock) {
|
||||
int index = CameraDevice.this.mCaptureListenerMap.indexOfKey(requestId);
|
||||
holder = (index >= 0) ? CameraDevice.this.mCaptureListenerMap.valueAt(index)
|
||||
: null;
|
||||
if (holder != null) {
|
||||
CameraDevice.this.mCaptureListenerMap.removeAt(index);
|
||||
}
|
||||
}
|
||||
iter.remove();
|
||||
|
||||
// Call onCaptureSequenceCompleted
|
||||
if (holder != null) {
|
||||
Runnable resultDispatch = new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (!CameraDevice.this.isClosed()){
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, String.format(
|
||||
"fire sequence complete for request %d",
|
||||
requestId));
|
||||
}
|
||||
|
||||
holder.getListener().onCaptureSequenceCompleted(
|
||||
CameraDevice.this,
|
||||
requestId,
|
||||
// TODO: this is problematic, crop long to int
|
||||
frameNumberRequestPair.getKey().intValue());
|
||||
}
|
||||
}
|
||||
};
|
||||
holder.getHandler().post(resultDispatch);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class CameraDeviceCallbacks extends ICameraDeviceCallbacks.Stub {
|
||||
|
||||
//
|
||||
@@ -492,7 +677,7 @@ public class CameraDevice implements android.hardware.camera2.CameraDevice {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCameraError(final int errorCode) {
|
||||
public void onCameraError(final int errorCode, CaptureResultExtras resultExtras) {
|
||||
Runnable r = null;
|
||||
if (isClosed()) return;
|
||||
|
||||
@@ -507,6 +692,7 @@ public class CameraDevice implements android.hardware.camera2.CameraDevice {
|
||||
case ERROR_CAMERA_DEVICE:
|
||||
case ERROR_CAMERA_SERVICE:
|
||||
r = new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (!CameraDevice.this.isClosed()) {
|
||||
mDeviceListener.onError(CameraDevice.this, errorCode);
|
||||
@@ -517,6 +703,11 @@ public class CameraDevice implements android.hardware.camera2.CameraDevice {
|
||||
}
|
||||
CameraDevice.this.mDeviceHandler.post(r);
|
||||
}
|
||||
|
||||
// Fire onCaptureSequenceCompleted
|
||||
mFrameNumberTracker.updateTracker(resultExtras.getFrameNumber(), /*error*/true);
|
||||
checkAndFireSequenceComplete();
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -535,7 +726,8 @@ public class CameraDevice implements android.hardware.camera2.CameraDevice {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCaptureStarted(int requestId, final long timestamp) {
|
||||
public void onCaptureStarted(final CaptureResultExtras resultExtras, final long timestamp) {
|
||||
int requestId = resultExtras.getRequestId();
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "Capture started for id " + requestId);
|
||||
}
|
||||
@@ -555,11 +747,12 @@ public class CameraDevice implements android.hardware.camera2.CameraDevice {
|
||||
// Dispatch capture start notice
|
||||
holder.getHandler().post(
|
||||
new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (!CameraDevice.this.isClosed()) {
|
||||
holder.getListener().onCaptureStarted(
|
||||
CameraDevice.this,
|
||||
holder.getRequest(),
|
||||
holder.getRequest(resultExtras.getSubsequenceId()),
|
||||
timestamp);
|
||||
}
|
||||
}
|
||||
@@ -567,48 +760,18 @@ public class CameraDevice implements android.hardware.camera2.CameraDevice {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResultReceived(int requestId, CameraMetadataNative result)
|
||||
throws RemoteException {
|
||||
public void onResultReceived(CameraMetadataNative result,
|
||||
CaptureResultExtras resultExtras) throws RemoteException {
|
||||
int requestId = resultExtras.getRequestId();
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "Received result for id " + requestId);
|
||||
}
|
||||
final CaptureListenerHolder holder;
|
||||
final CaptureListenerHolder holder =
|
||||
CameraDevice.this.mCaptureListenerMap.get(requestId);
|
||||
|
||||
Boolean quirkPartial = result.get(CaptureResult.QUIRKS_PARTIAL_RESULT);
|
||||
boolean quirkIsPartialResult = (quirkPartial != null && quirkPartial);
|
||||
|
||||
synchronized (mLock) {
|
||||
// TODO: move this whole map into this class to make it more testable,
|
||||
// exposing the methods necessary like subscribeToRequest, unsubscribe..
|
||||
// TODO: make class static class
|
||||
|
||||
holder = CameraDevice.this.mCaptureListenerMap.get(requestId);
|
||||
|
||||
// Clean up listener once we no longer expect to see it.
|
||||
if (holder != null && !holder.isRepeating() && !quirkIsPartialResult) {
|
||||
CameraDevice.this.mCaptureListenerMap.remove(requestId);
|
||||
}
|
||||
|
||||
// TODO: add 'capture sequence completed' callback to the
|
||||
// service, and clean up repeating requests there instead.
|
||||
|
||||
// If we received a result for a repeating request and have
|
||||
// prior repeating requests queued for deletion, remove those
|
||||
// requests from mCaptureListenerMap.
|
||||
if (holder != null && holder.isRepeating() && !quirkIsPartialResult
|
||||
&& mRepeatingRequestIdDeletedList.size() > 0) {
|
||||
Iterator<Integer> iter = mRepeatingRequestIdDeletedList.iterator();
|
||||
while (iter.hasNext()) {
|
||||
int deletedRequestId = iter.next();
|
||||
if (deletedRequestId < requestId) {
|
||||
CameraDevice.this.mCaptureListenerMap.remove(deletedRequestId);
|
||||
iter.remove();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Check if we have a listener for this
|
||||
if (holder == null) {
|
||||
return;
|
||||
@@ -616,7 +779,7 @@ public class CameraDevice implements android.hardware.camera2.CameraDevice {
|
||||
|
||||
if (isClosed()) return;
|
||||
|
||||
final CaptureRequest request = holder.getRequest();
|
||||
final CaptureRequest request = holder.getRequest(resultExtras.getSubsequenceId());
|
||||
final CaptureResult resultAsCapture = new CaptureResult(result, request, requestId);
|
||||
|
||||
Runnable resultDispatch = null;
|
||||
@@ -651,6 +814,12 @@ public class CameraDevice implements android.hardware.camera2.CameraDevice {
|
||||
}
|
||||
|
||||
holder.getHandler().post(resultDispatch);
|
||||
|
||||
// Fire onCaptureSequenceCompleted
|
||||
if (!quirkIsPartialResult) {
|
||||
mFrameNumberTracker.updateTracker(resultExtras.getFrameNumber(), /*error*/false);
|
||||
checkAndFireSequenceComplete();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user