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;
|
package android.hardware.camera2;
|
||||||
|
|
||||||
import android.hardware.camera2.impl.CameraMetadataNative;
|
import android.hardware.camera2.impl.CameraMetadataNative;
|
||||||
|
import android.hardware.camera2.CaptureResultExtras;
|
||||||
|
|
||||||
/** @hide */
|
/** @hide */
|
||||||
interface ICameraDeviceCallbacks
|
interface ICameraDeviceCallbacks
|
||||||
@@ -25,8 +26,9 @@ interface ICameraDeviceCallbacks
|
|||||||
* Keep up-to-date with frameworks/av/include/camera/camera2/ICameraDeviceCallbacks.h
|
* 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 onCameraIdle();
|
||||||
oneway void onCaptureStarted(int requestId, long timestamp);
|
oneway void onCaptureStarted(in CaptureResultExtras resultExtras, long timestamp);
|
||||||
oneway void onResultReceived(int requestId, in CameraMetadataNative result);
|
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.impl.CameraMetadataNative;
|
||||||
import android.hardware.camera2.CaptureRequest;
|
import android.hardware.camera2.CaptureRequest;
|
||||||
|
|
||||||
|
import android.hardware.camera2.LongParcelable;
|
||||||
|
|
||||||
/** @hide */
|
/** @hide */
|
||||||
interface ICameraDeviceUser
|
interface ICameraDeviceUser
|
||||||
{
|
{
|
||||||
@@ -31,9 +33,13 @@ interface ICameraDeviceUser
|
|||||||
// ints here are status_t
|
// ints here are status_t
|
||||||
|
|
||||||
// non-negative value is the requestId. negative value is 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);
|
int deleteStream(int streamId);
|
||||||
|
|
||||||
@@ -46,5 +52,5 @@ interface ICameraDeviceUser
|
|||||||
|
|
||||||
int waitUntilIdle();
|
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.CameraAccessException;
|
||||||
import android.hardware.camera2.CaptureRequest;
|
import android.hardware.camera2.CaptureRequest;
|
||||||
import android.hardware.camera2.CaptureResult;
|
import android.hardware.camera2.CaptureResult;
|
||||||
|
import android.hardware.camera2.CaptureResultExtras;
|
||||||
import android.hardware.camera2.ICameraDeviceCallbacks;
|
import android.hardware.camera2.ICameraDeviceCallbacks;
|
||||||
import android.hardware.camera2.ICameraDeviceUser;
|
import android.hardware.camera2.ICameraDeviceUser;
|
||||||
|
import android.hardware.camera2.LongParcelable;
|
||||||
import android.hardware.camera2.utils.CameraBinderDecorator;
|
import android.hardware.camera2.utils.CameraBinderDecorator;
|
||||||
import android.hardware.camera2.utils.CameraRuntimeException;
|
import android.hardware.camera2.utils.CameraRuntimeException;
|
||||||
import android.os.Handler;
|
import android.os.Handler;
|
||||||
@@ -33,10 +35,12 @@ import android.util.Log;
|
|||||||
import android.util.SparseArray;
|
import android.util.SparseArray;
|
||||||
import android.view.Surface;
|
import android.view.Surface;
|
||||||
|
|
||||||
|
import java.util.AbstractMap.SimpleEntry;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.TreeSet;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* HAL2.1+ implementation of CameraDevice. Use CameraManager#open to instantiate
|
* 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;
|
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
|
// Runnables for all state transitions, except error, which needs the
|
||||||
// error code argument
|
// error code argument
|
||||||
|
|
||||||
private final Runnable mCallOnOpened = new Runnable() {
|
private final Runnable mCallOnOpened = new Runnable() {
|
||||||
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
if (!CameraDevice.this.isClosed()) {
|
if (!CameraDevice.this.isClosed()) {
|
||||||
mDeviceListener.onOpened(CameraDevice.this);
|
mDeviceListener.onOpened(CameraDevice.this);
|
||||||
@@ -81,6 +99,7 @@ public class CameraDevice implements android.hardware.camera2.CameraDevice {
|
|||||||
};
|
};
|
||||||
|
|
||||||
private final Runnable mCallOnUnconfigured = new Runnable() {
|
private final Runnable mCallOnUnconfigured = new Runnable() {
|
||||||
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
if (!CameraDevice.this.isClosed()) {
|
if (!CameraDevice.this.isClosed()) {
|
||||||
mDeviceListener.onUnconfigured(CameraDevice.this);
|
mDeviceListener.onUnconfigured(CameraDevice.this);
|
||||||
@@ -89,6 +108,7 @@ public class CameraDevice implements android.hardware.camera2.CameraDevice {
|
|||||||
};
|
};
|
||||||
|
|
||||||
private final Runnable mCallOnActive = new Runnable() {
|
private final Runnable mCallOnActive = new Runnable() {
|
||||||
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
if (!CameraDevice.this.isClosed()) {
|
if (!CameraDevice.this.isClosed()) {
|
||||||
mDeviceListener.onActive(CameraDevice.this);
|
mDeviceListener.onActive(CameraDevice.this);
|
||||||
@@ -97,6 +117,7 @@ public class CameraDevice implements android.hardware.camera2.CameraDevice {
|
|||||||
};
|
};
|
||||||
|
|
||||||
private final Runnable mCallOnBusy = new Runnable() {
|
private final Runnable mCallOnBusy = new Runnable() {
|
||||||
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
if (!CameraDevice.this.isClosed()) {
|
if (!CameraDevice.this.isClosed()) {
|
||||||
mDeviceListener.onBusy(CameraDevice.this);
|
mDeviceListener.onBusy(CameraDevice.this);
|
||||||
@@ -105,12 +126,14 @@ public class CameraDevice implements android.hardware.camera2.CameraDevice {
|
|||||||
};
|
};
|
||||||
|
|
||||||
private final Runnable mCallOnClosed = new Runnable() {
|
private final Runnable mCallOnClosed = new Runnable() {
|
||||||
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
mDeviceListener.onClosed(CameraDevice.this);
|
mDeviceListener.onClosed(CameraDevice.this);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
private final Runnable mCallOnIdle = new Runnable() {
|
private final Runnable mCallOnIdle = new Runnable() {
|
||||||
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
if (!CameraDevice.this.isClosed()) {
|
if (!CameraDevice.this.isClosed()) {
|
||||||
mDeviceListener.onIdle(CameraDevice.this);
|
mDeviceListener.onIdle(CameraDevice.this);
|
||||||
@@ -119,6 +142,7 @@ public class CameraDevice implements android.hardware.camera2.CameraDevice {
|
|||||||
};
|
};
|
||||||
|
|
||||||
private final Runnable mCallOnDisconnected = new Runnable() {
|
private final Runnable mCallOnDisconnected = new Runnable() {
|
||||||
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
if (!CameraDevice.this.isClosed()) {
|
if (!CameraDevice.this.isClosed()) {
|
||||||
mDeviceListener.onDisconnected(CameraDevice.this);
|
mDeviceListener.onDisconnected(CameraDevice.this);
|
||||||
@@ -249,22 +273,26 @@ public class CameraDevice implements android.hardware.camera2.CameraDevice {
|
|||||||
@Override
|
@Override
|
||||||
public int capture(CaptureRequest request, CaptureListener listener, Handler handler)
|
public int capture(CaptureRequest request, CaptureListener listener, Handler handler)
|
||||||
throws CameraAccessException {
|
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
|
@Override
|
||||||
public int captureBurst(List<CaptureRequest> requests, CaptureListener listener,
|
public int captureBurst(List<CaptureRequest> requests, CaptureListener listener,
|
||||||
Handler handler) throws CameraAccessException {
|
Handler handler) throws CameraAccessException {
|
||||||
|
// TODO: remove this. Throw IAE if the request is null or empty. Need to update API doc.
|
||||||
if (requests.isEmpty()) {
|
if (requests.isEmpty()) {
|
||||||
Log.w(TAG, "Capture burst request list is empty, do nothing!");
|
Log.w(TAG, "Capture burst request list is empty, do nothing!");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
// TODO
|
return submitCaptureRequest(requests, listener, handler, /*streaming*/false);
|
||||||
throw new UnsupportedOperationException("Burst capture implemented yet");
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private int submitCaptureRequest(CaptureRequest request, CaptureListener listener,
|
private int submitCaptureRequest(List<CaptureRequest> requestList, CaptureListener listener,
|
||||||
Handler handler, boolean repeating) throws CameraAccessException {
|
Handler handler, boolean repeating) throws CameraAccessException {
|
||||||
|
|
||||||
// Need a valid handler, or current thread needs to have a looper, if
|
// 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();
|
stopRepeating();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
LongParcelable lastFrameNumberRef = new LongParcelable();
|
||||||
try {
|
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) {
|
} catch (CameraRuntimeException e) {
|
||||||
throw e.asChecked();
|
throw e.asChecked();
|
||||||
} catch (RemoteException e) {
|
} catch (RemoteException e) {
|
||||||
@@ -290,12 +323,29 @@ public class CameraDevice implements android.hardware.camera2.CameraDevice {
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
if (listener != null) {
|
if (listener != null) {
|
||||||
mCaptureListenerMap.put(requestId, new CaptureListenerHolder(listener, request,
|
mCaptureListenerMap.put(requestId, new CaptureListenerHolder(listener,
|
||||||
handler, repeating));
|
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 (repeating) {
|
||||||
|
if (mRepeatingRequestId != REQUEST_ID_NONE) {
|
||||||
|
mFrameNumberRequestPairs.add(
|
||||||
|
new SimpleEntry<Long, Integer>(lastFrameNumber, mRepeatingRequestId));
|
||||||
|
}
|
||||||
mRepeatingRequestId = requestId;
|
mRepeatingRequestId = requestId;
|
||||||
|
} else {
|
||||||
|
mFrameNumberRequestPairs.add(
|
||||||
|
new SimpleEntry<Long, Integer>(lastFrameNumber, requestId));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mIdle) {
|
if (mIdle) {
|
||||||
@@ -310,18 +360,20 @@ public class CameraDevice implements android.hardware.camera2.CameraDevice {
|
|||||||
@Override
|
@Override
|
||||||
public int setRepeatingRequest(CaptureRequest request, CaptureListener listener,
|
public int setRepeatingRequest(CaptureRequest request, CaptureListener listener,
|
||||||
Handler handler) throws CameraAccessException {
|
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
|
@Override
|
||||||
public int setRepeatingBurst(List<CaptureRequest> requests, CaptureListener listener,
|
public int setRepeatingBurst(List<CaptureRequest> requests, CaptureListener listener,
|
||||||
Handler handler) throws CameraAccessException {
|
Handler handler) throws CameraAccessException {
|
||||||
|
// TODO: remove this. Throw IAE if the request is null or empty. Need to update API doc.
|
||||||
if (requests.isEmpty()) {
|
if (requests.isEmpty()) {
|
||||||
Log.w(TAG, "Set Repeating burst request list is empty, do nothing!");
|
Log.w(TAG, "Set Repeating burst request list is empty, do nothing!");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
// TODO
|
return submitCaptureRequest(requests, listener, handler, /*streaming*/true);
|
||||||
throw new UnsupportedOperationException("Burst capture implemented yet");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -340,7 +392,15 @@ public class CameraDevice implements android.hardware.camera2.CameraDevice {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
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) {
|
} catch (CameraRuntimeException e) {
|
||||||
throw e.asChecked();
|
throw e.asChecked();
|
||||||
} catch (RemoteException e) {
|
} catch (RemoteException e) {
|
||||||
@@ -379,7 +439,17 @@ public class CameraDevice implements android.hardware.camera2.CameraDevice {
|
|||||||
|
|
||||||
mDeviceHandler.post(mCallOnBusy);
|
mDeviceHandler.post(mCallOnBusy);
|
||||||
try {
|
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) {
|
} catch (CameraRuntimeException e) {
|
||||||
throw e.asChecked();
|
throw e.asChecked();
|
||||||
} catch (RemoteException e) {
|
} catch (RemoteException e) {
|
||||||
@@ -425,18 +495,18 @@ public class CameraDevice implements android.hardware.camera2.CameraDevice {
|
|||||||
|
|
||||||
private final boolean mRepeating;
|
private final boolean mRepeating;
|
||||||
private final CaptureListener mListener;
|
private final CaptureListener mListener;
|
||||||
private final CaptureRequest mRequest;
|
private final List<CaptureRequest> mRequestList;
|
||||||
private final Handler mHandler;
|
private final Handler mHandler;
|
||||||
|
|
||||||
CaptureListenerHolder(CaptureListener listener, CaptureRequest request, Handler handler,
|
CaptureListenerHolder(CaptureListener listener, List<CaptureRequest> requestList,
|
||||||
boolean repeating) {
|
Handler handler, boolean repeating) {
|
||||||
if (listener == null || handler == null) {
|
if (listener == null || handler == null) {
|
||||||
throw new UnsupportedOperationException(
|
throw new UnsupportedOperationException(
|
||||||
"Must have a valid handler and a valid listener");
|
"Must have a valid handler and a valid listener");
|
||||||
}
|
}
|
||||||
mRepeating = repeating;
|
mRepeating = repeating;
|
||||||
mHandler = handler;
|
mHandler = handler;
|
||||||
mRequest = request;
|
mRequestList = new ArrayList<CaptureRequest>(requestList);
|
||||||
mListener = listener;
|
mListener = listener;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -448,8 +518,24 @@ public class CameraDevice implements android.hardware.camera2.CameraDevice {
|
|||||||
return mListener;
|
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() {
|
public CaptureRequest getRequest() {
|
||||||
return mRequest;
|
return getRequest(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Handler getHandler() {
|
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 {
|
public class CameraDeviceCallbacks extends ICameraDeviceCallbacks.Stub {
|
||||||
|
|
||||||
//
|
//
|
||||||
@@ -492,7 +677,7 @@ public class CameraDevice implements android.hardware.camera2.CameraDevice {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onCameraError(final int errorCode) {
|
public void onCameraError(final int errorCode, CaptureResultExtras resultExtras) {
|
||||||
Runnable r = null;
|
Runnable r = null;
|
||||||
if (isClosed()) return;
|
if (isClosed()) return;
|
||||||
|
|
||||||
@@ -507,6 +692,7 @@ public class CameraDevice implements android.hardware.camera2.CameraDevice {
|
|||||||
case ERROR_CAMERA_DEVICE:
|
case ERROR_CAMERA_DEVICE:
|
||||||
case ERROR_CAMERA_SERVICE:
|
case ERROR_CAMERA_SERVICE:
|
||||||
r = new Runnable() {
|
r = new Runnable() {
|
||||||
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
if (!CameraDevice.this.isClosed()) {
|
if (!CameraDevice.this.isClosed()) {
|
||||||
mDeviceListener.onError(CameraDevice.this, errorCode);
|
mDeviceListener.onError(CameraDevice.this, errorCode);
|
||||||
@@ -517,6 +703,11 @@ public class CameraDevice implements android.hardware.camera2.CameraDevice {
|
|||||||
}
|
}
|
||||||
CameraDevice.this.mDeviceHandler.post(r);
|
CameraDevice.this.mDeviceHandler.post(r);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Fire onCaptureSequenceCompleted
|
||||||
|
mFrameNumberTracker.updateTracker(resultExtras.getFrameNumber(), /*error*/true);
|
||||||
|
checkAndFireSequenceComplete();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -535,7 +726,8 @@ public class CameraDevice implements android.hardware.camera2.CameraDevice {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onCaptureStarted(int requestId, final long timestamp) {
|
public void onCaptureStarted(final CaptureResultExtras resultExtras, final long timestamp) {
|
||||||
|
int requestId = resultExtras.getRequestId();
|
||||||
if (DEBUG) {
|
if (DEBUG) {
|
||||||
Log.d(TAG, "Capture started for id " + requestId);
|
Log.d(TAG, "Capture started for id " + requestId);
|
||||||
}
|
}
|
||||||
@@ -555,11 +747,12 @@ public class CameraDevice implements android.hardware.camera2.CameraDevice {
|
|||||||
// Dispatch capture start notice
|
// Dispatch capture start notice
|
||||||
holder.getHandler().post(
|
holder.getHandler().post(
|
||||||
new Runnable() {
|
new Runnable() {
|
||||||
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
if (!CameraDevice.this.isClosed()) {
|
if (!CameraDevice.this.isClosed()) {
|
||||||
holder.getListener().onCaptureStarted(
|
holder.getListener().onCaptureStarted(
|
||||||
CameraDevice.this,
|
CameraDevice.this,
|
||||||
holder.getRequest(),
|
holder.getRequest(resultExtras.getSubsequenceId()),
|
||||||
timestamp);
|
timestamp);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -567,48 +760,18 @@ public class CameraDevice implements android.hardware.camera2.CameraDevice {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onResultReceived(int requestId, CameraMetadataNative result)
|
public void onResultReceived(CameraMetadataNative result,
|
||||||
throws RemoteException {
|
CaptureResultExtras resultExtras) throws RemoteException {
|
||||||
|
int requestId = resultExtras.getRequestId();
|
||||||
if (DEBUG) {
|
if (DEBUG) {
|
||||||
Log.d(TAG, "Received result for id " + requestId);
|
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 quirkPartial = result.get(CaptureResult.QUIRKS_PARTIAL_RESULT);
|
||||||
boolean quirkIsPartialResult = (quirkPartial != null && quirkPartial);
|
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
|
// Check if we have a listener for this
|
||||||
if (holder == null) {
|
if (holder == null) {
|
||||||
return;
|
return;
|
||||||
@@ -616,7 +779,7 @@ public class CameraDevice implements android.hardware.camera2.CameraDevice {
|
|||||||
|
|
||||||
if (isClosed()) return;
|
if (isClosed()) return;
|
||||||
|
|
||||||
final CaptureRequest request = holder.getRequest();
|
final CaptureRequest request = holder.getRequest(resultExtras.getSubsequenceId());
|
||||||
final CaptureResult resultAsCapture = new CaptureResult(result, request, requestId);
|
final CaptureResult resultAsCapture = new CaptureResult(result, request, requestId);
|
||||||
|
|
||||||
Runnable resultDispatch = null;
|
Runnable resultDispatch = null;
|
||||||
@@ -651,6 +814,12 @@ public class CameraDevice implements android.hardware.camera2.CameraDevice {
|
|||||||
}
|
}
|
||||||
|
|
||||||
holder.getHandler().post(resultDispatch);
|
holder.getHandler().post(resultDispatch);
|
||||||
|
|
||||||
|
// Fire onCaptureSequenceCompleted
|
||||||
|
if (!quirkIsPartialResult) {
|
||||||
|
mFrameNumberTracker.updateTracker(resultExtras.getFrameNumber(), /*error*/false);
|
||||||
|
checkAndFireSequenceComplete();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user