Add support for dynamic shared output surfaces
The Camera API needs to support the dynamic attach/detach of extra
output surfaces to a given camera stream.
Bug: 63912484
Change-Id: I18809aea31f78fb9e125bd18b58951ade4fad3c5
diff --git a/camera/aidl/android/hardware/camera2/ICameraDeviceUser.aidl b/camera/aidl/android/hardware/camera2/ICameraDeviceUser.aidl
index 0771fc8..07d4e7e 100644
--- a/camera/aidl/android/hardware/camera2/ICameraDeviceUser.aidl
+++ b/camera/aidl/android/hardware/camera2/ICameraDeviceUser.aidl
@@ -140,5 +140,7 @@
void prepare2(int maxCount, int streamId);
+ void updateOutputConfiguration(int streamId, in OutputConfiguration outputConfiguration);
+
void finalizeOutputConfigurations(int streamId, in OutputConfiguration outputConfiguration);
}
diff --git a/camera/camera2/OutputConfiguration.cpp b/camera/camera2/OutputConfiguration.cpp
index 468a1eb..813d6c9 100644
--- a/camera/camera2/OutputConfiguration.cpp
+++ b/camera/camera2/OutputConfiguration.cpp
@@ -160,12 +160,12 @@
}
OutputConfiguration::OutputConfiguration(sp<IGraphicBufferProducer>& gbp, int rotation,
- int surfaceSetID) {
+ int surfaceSetID, bool isShared) {
mGbps.push_back(gbp);
mRotation = rotation;
mSurfaceSetID = surfaceSetID;
mIsDeferred = false;
- mIsShared = false;
+ mIsShared = isShared;
}
status_t OutputConfiguration::writeToParcel(android::Parcel* parcel) const {
diff --git a/camera/include/camera/camera2/OutputConfiguration.h b/camera/include/camera/camera2/OutputConfiguration.h
index 8e641c7..3599604 100644
--- a/camera/include/camera/camera2/OutputConfiguration.h
+++ b/camera/include/camera/camera2/OutputConfiguration.h
@@ -64,7 +64,7 @@
OutputConfiguration(const android::Parcel& parcel);
OutputConfiguration(sp<IGraphicBufferProducer>& gbp, int rotation,
- int surfaceSetID = INVALID_SET_ID);
+ int surfaceSetID = INVALID_SET_ID, bool isShared = false);
bool operator == (const OutputConfiguration& other) const {
return ( mRotation == other.mRotation &&
@@ -110,6 +110,7 @@
bool gbpsEqual(const OutputConfiguration& other) const;
bool gbpsLessThan(const OutputConfiguration& other) const;
+ void addGraphicProducer(sp<IGraphicBufferProducer> gbp) {mGbps.push_back(gbp);}
private:
std::vector<sp<IGraphicBufferProducer>> mGbps;
int mRotation;
diff --git a/camera/ndk/NdkCameraCaptureSession.cpp b/camera/ndk/NdkCameraCaptureSession.cpp
index 2a6b182..fd95296 100644
--- a/camera/ndk/NdkCameraCaptureSession.cpp
+++ b/camera/ndk/NdkCameraCaptureSession.cpp
@@ -135,3 +135,19 @@
}
return session->abortCaptures();
}
+
+EXPORT
+camera_status_t ACameraCaptureSession_updateSharedOutput(ACameraCaptureSession* session,
+ ACaptureSessionOutput* output) {
+ ATRACE_CALL();
+ if (session == nullptr) {
+ ALOGE("%s: Error: session is null", __FUNCTION__);
+ return ACAMERA_ERROR_INVALID_PARAMETER;
+ }
+
+ if (session->isClosed()) {
+ ALOGE("%s: session %p is already closed", __FUNCTION__, session);
+ return ACAMERA_ERROR_SESSION_CLOSED;
+ }
+ return session->updateOutputConfiguration(output);
+}
diff --git a/camera/ndk/NdkCameraDevice.cpp b/camera/ndk/NdkCameraDevice.cpp
index 9f6d1f7..ac4beea 100644
--- a/camera/ndk/NdkCameraDevice.cpp
+++ b/camera/ndk/NdkCameraDevice.cpp
@@ -103,11 +103,74 @@
__FUNCTION__, window, out);
return ACAMERA_ERROR_INVALID_PARAMETER;
}
- *out = new ACaptureSessionOutput(window);
+ *out = new ACaptureSessionOutput(window, false);
return ACAMERA_OK;
}
EXPORT
+camera_status_t ACaptureSessionSharedOutput_create(
+ ANativeWindow* window, /*out*/ACaptureSessionOutput** out) {
+ ATRACE_CALL();
+ if (window == nullptr || out == nullptr) {
+ ALOGE("%s: Error: bad argument. window %p, out %p",
+ __FUNCTION__, window, out);
+ return ACAMERA_ERROR_INVALID_PARAMETER;
+ }
+ *out = new ACaptureSessionOutput(window, true);
+ return ACAMERA_OK;
+}
+
+EXPORT
+camera_status_t ACaptureSessionSharedOutput_add(ACaptureSessionOutput *out,
+ ANativeWindow* window) {
+ ATRACE_CALL();
+ if ((window == nullptr) || (out == nullptr)) {
+ ALOGE("%s: Error: bad argument. window %p, out %p",
+ __FUNCTION__, window, out);
+ return ACAMERA_ERROR_INVALID_PARAMETER;
+ }
+ if (!out->mIsShared) {
+ ALOGE("%s: Error trying to insert a new window in non-shared output configuration",
+ __FUNCTION__);
+ return ACAMERA_ERROR_INVALID_OPERATION;
+ }
+ if (out->mWindow == window) {
+ ALOGE("%s: Error trying to add the same window associated with the output configuration",
+ __FUNCTION__);
+ return ACAMERA_ERROR_INVALID_PARAMETER;
+ }
+
+ auto insert = out->mSharedWindows.insert(window);
+ camera_status_t ret = (insert.second) ? ACAMERA_OK : ACAMERA_ERROR_INVALID_PARAMETER;
+ return ret;
+}
+
+EXPORT
+camera_status_t ACaptureSessionSharedOutput_remove(ACaptureSessionOutput *out,
+ ANativeWindow* window) {
+ ATRACE_CALL();
+ if ((window == nullptr) || (out == nullptr)) {
+ ALOGE("%s: Error: bad argument. window %p, out %p",
+ __FUNCTION__, window, out);
+ return ACAMERA_ERROR_INVALID_PARAMETER;
+ }
+ if (!out->mIsShared) {
+ ALOGE("%s: Error trying to remove a window in non-shared output configuration",
+ __FUNCTION__);
+ return ACAMERA_ERROR_INVALID_OPERATION;
+ }
+ if (out->mWindow == window) {
+ ALOGE("%s: Error trying to remove the same window associated with the output configuration",
+ __FUNCTION__);
+ return ACAMERA_ERROR_INVALID_PARAMETER;
+ }
+
+ auto remove = out->mSharedWindows.erase(window);
+ camera_status_t ret = (remove) ? ACAMERA_OK : ACAMERA_ERROR_INVALID_PARAMETER;
+ return ret;
+}
+
+EXPORT
void ACaptureSessionOutput_free(ACaptureSessionOutput* output) {
ATRACE_CALL();
if (output != nullptr) {
diff --git a/camera/ndk/impl/ACameraCaptureSession.cpp b/camera/ndk/impl/ACameraCaptureSession.cpp
index b9c159d..6d1d5ce 100644
--- a/camera/ndk/impl/ACameraCaptureSession.cpp
+++ b/camera/ndk/impl/ACameraCaptureSession.cpp
@@ -148,6 +148,23 @@
return ret;
}
+camera_status_t ACameraCaptureSession::updateOutputConfiguration(ACaptureSessionOutput *output) {
+ sp<CameraDevice> dev = getDeviceSp();
+ if (dev == nullptr) {
+ ALOGE("Error: Device associated with session %p has been closed!", this);
+ return ACAMERA_ERROR_SESSION_CLOSED;
+ }
+
+ camera_status_t ret;
+ dev->lockDeviceForSessionOps();
+ {
+ Mutex::Autolock _l(mSessionLock);
+ ret = dev->updateOutputConfiguration(output);
+ }
+ dev->unlockDevice();
+ return ret;
+}
+
ACameraDevice*
ACameraCaptureSession::getDevice() {
Mutex::Autolock _l(mSessionLock);
diff --git a/camera/ndk/impl/ACameraCaptureSession.h b/camera/ndk/impl/ACameraCaptureSession.h
index 339c665..a2068e7 100644
--- a/camera/ndk/impl/ACameraCaptureSession.h
+++ b/camera/ndk/impl/ACameraCaptureSession.h
@@ -24,7 +24,8 @@
using namespace android;
struct ACaptureSessionOutput {
- explicit ACaptureSessionOutput(ANativeWindow* window) : mWindow(window) {};
+ explicit ACaptureSessionOutput(ANativeWindow* window, bool isShared = false) :
+ mWindow(window), mIsShared(isShared) {};
bool operator == (const ACaptureSessionOutput& other) const {
return mWindow == other.mWindow;
@@ -40,6 +41,8 @@
}
ANativeWindow* mWindow;
+ std::set<ANativeWindow *> mSharedWindows;
+ bool mIsShared;
int mRotation = CAMERA3_STREAM_ROTATION_0;
};
@@ -89,6 +92,8 @@
int numRequests, ACaptureRequest** requests,
/*optional*/int* captureSequenceId);
+ camera_status_t updateOutputConfiguration(ACaptureSessionOutput *output);
+
ACameraDevice* getDevice();
private:
diff --git a/camera/ndk/impl/ACameraDevice.cpp b/camera/ndk/impl/ACameraDevice.cpp
index 907802c..9ce0ac8 100644
--- a/camera/ndk/impl/ACameraDevice.cpp
+++ b/camera/ndk/impl/ACameraDevice.cpp
@@ -289,6 +289,82 @@
return ACAMERA_OK;
}
+camera_status_t CameraDevice::updateOutputConfiguration(ACaptureSessionOutput *output) {
+ camera_status_t ret = checkCameraClosedOrErrorLocked();
+ if (ret != ACAMERA_OK) {
+ return ret;
+ }
+
+ if (output == nullptr) {
+ return ACAMERA_ERROR_INVALID_PARAMETER;
+ }
+
+ if (!output->mIsShared) {
+ ALOGE("Error output configuration is not shared");
+ return ACAMERA_ERROR_INVALID_OPERATION;
+ }
+
+ int32_t streamId = -1;
+ for (auto& kvPair : mConfiguredOutputs) {
+ if (kvPair.second.first == output->mWindow) {
+ streamId = kvPair.first;
+ break;
+ }
+ }
+ if (streamId < 0) {
+ ALOGE("Error: Invalid output configuration");
+ return ACAMERA_ERROR_INVALID_PARAMETER;
+ }
+
+ sp<IGraphicBufferProducer> iGBP(nullptr);
+ ret = getIGBPfromAnw(output->mWindow, iGBP);
+ if (ret != ACAMERA_OK) {
+ ALOGE("Camera device %s failed to extract graphic producer from native window",
+ getId());
+ return ret;
+ }
+
+ OutputConfiguration outConfig(iGBP, output->mRotation, OutputConfiguration::INVALID_SET_ID,
+ true);
+
+ for (auto& anw : output->mSharedWindows) {
+ ret = getIGBPfromAnw(anw, iGBP);
+ if (ret != ACAMERA_OK) {
+ ALOGE("Camera device %s failed to extract graphic producer from native window",
+ getId());
+ return ret;
+ }
+ outConfig.addGraphicProducer(iGBP);
+ }
+
+ auto remoteRet = mRemote->updateOutputConfiguration(streamId, outConfig);
+ if (!remoteRet.isOk()) {
+ switch (remoteRet.serviceSpecificErrorCode()) {
+ case hardware::ICameraService::ERROR_INVALID_OPERATION:
+ ALOGE("Camera device %s invalid operation: %s", getId(),
+ remoteRet.toString8().string());
+ return ACAMERA_ERROR_INVALID_OPERATION;
+ break;
+ case hardware::ICameraService::ERROR_ALREADY_EXISTS:
+ ALOGE("Camera device %s output surface already exists: %s", getId(),
+ remoteRet.toString8().string());
+ return ACAMERA_ERROR_INVALID_PARAMETER;
+ break;
+ case hardware::ICameraService::ERROR_ILLEGAL_ARGUMENT:
+ ALOGE("Camera device %s invalid input argument: %s", getId(),
+ remoteRet.toString8().string());
+ return ACAMERA_ERROR_INVALID_PARAMETER;
+ break;
+ default:
+ ALOGE("Camera device %s failed to add shared output: %s", getId(),
+ remoteRet.toString8().string());
+ return ACAMERA_ERROR_UNKNOWN;
+ }
+ }
+
+ return ACAMERA_OK;
+}
+
camera_status_t
CameraDevice::allocateCaptureRequest(
const ACaptureRequest* request, /*out*/sp<CaptureRequest>& outReq) {
@@ -540,7 +616,8 @@
return ret;
}
outputSet.insert(std::make_pair(
- anw, OutputConfiguration(iGBP, outConfig.mRotation)));
+ anw, OutputConfiguration(iGBP, outConfig.mRotation,
+ OutputConfiguration::INVALID_SET_ID, outConfig.mIsShared)));
}
auto addSet = outputSet;
std::vector<int> deleteList;
diff --git a/camera/ndk/impl/ACameraDevice.h b/camera/ndk/impl/ACameraDevice.h
index 6ed3881..23cc1a1 100644
--- a/camera/ndk/impl/ACameraDevice.h
+++ b/camera/ndk/impl/ACameraDevice.h
@@ -36,7 +36,8 @@
#include <camera/camera2/OutputConfiguration.h>
#include <camera/camera2/CaptureRequest.h>
-#include <camera/NdkCameraDevice.h>
+#include <camera/NdkCameraManager.h>
+#include <camera/NdkCameraCaptureSession.h>
#include "ACameraMetadata.h"
namespace android {
@@ -122,6 +123,8 @@
/*out*/int* captureSequenceId,
bool isRepeating);
+ camera_status_t updateOutputConfiguration(ACaptureSessionOutput *output);
+
static camera_status_t allocateCaptureRequest(
const ACaptureRequest* request, sp<CaptureRequest>& outReq);
diff --git a/camera/ndk/include/camera/NdkCameraCaptureSession.h b/camera/ndk/include/camera/NdkCameraCaptureSession.h
index d96f538..67003c1 100644
--- a/camera/ndk/include/camera/NdkCameraCaptureSession.h
+++ b/camera/ndk/include/camera/NdkCameraCaptureSession.h
@@ -591,6 +591,54 @@
#endif /* __ANDROID_API__ >= 24 */
+#if __ANDROID_API__ >= 28
+
+typedef struct ACaptureSessionOutput ACaptureSessionOutput;
+
+/**
+ * Update shared ACaptureSessionOutput.
+ *
+ * <p>A shared ACaptureSessionOutput (see {@link ACaptureSessionSharedOutput_create}) that
+ * was modified via calls to {@link ACaptureSessionSharedOutput_add} or
+ * {@link ACaptureSessionSharedOutput_remove} must be updated by calling this method before its
+ * changes take effect. After the update call returns with {@link ACAMERA_OK}, any newly added
+ * native windows can be used as a target in subsequent capture requests.</p>
+ *
+ * <p>Native windows that get removed must not be part of any active repeating or single/burst
+ * request or have any pending results. Consider updating repeating requests via
+ * {@link ACaptureSessionOutput_setRepeatingRequest} and then wait for the last frame number
+ * when the sequence completes
+ * {@link ACameraCaptureSession_captureCallback#onCaptureSequenceCompleted}.</p>
+ *
+ * <p>Native windows that get added must not be part of any other registered ACaptureSessionOutput
+ * and must be compatible. Compatible windows must have matching format, rotation and
+ * consumer usage.</p>
+ *
+ * <p>A shared ACameraCaptureSession can support up to 4 additional native windows.</p>
+ *
+ * @param session the capture session of interest
+ * @param output the modified output configuration
+ *
+ * @return <ul><li>
+ * {@link ACAMERA_OK} if the method succeeds.</li>
+ * <li>{@link ACAMERA_ERROR_INVALID_PARAMETER} if session or output is NULL; or output
+ * contains invalid native windows; or if an attempt was made to add
+ * a native window to a different output configuration; or new native window is not
+ * compatible; or any removed native window still has pending requests;</li>
+ * <li>{@link ACAMERA_ERROR_INVALID_OPERATION} if output configuration is not shared (see
+ * {@link ACaptureSessionSharedOutput_create}; or the number of additional
+ * native windows goes beyond the supported limit.</li>
+ * <li>{@link ACAMERA_ERROR_SESSION_CLOSED} if the capture session has been closed</li>
+ * <li>{@link ACAMERA_ERROR_CAMERA_DISCONNECTED} if the camera device is closed</li>
+ * <li>{@link ACAMERA_ERROR_CAMERA_DEVICE} if the camera device encounters fatal error</li>
+ * <li>{@link ACAMERA_ERROR_CAMERA_SERVICE} if the camera service encounters fatal
+ * error</li>
+ * <li>{@link ACAMERA_ERROR_UNKNOWN} if the method fails for some other reasons</li></ul>
+ */
+camera_status_t ACameraCaptureSession_updateSharedOutput(ACameraCaptureSession* session,
+ ACaptureSessionOutput* output);
+#endif /* __ANDROID_API__ >= 28 */
+
__END_DECLS
#endif /* _NDK_CAMERA_CAPTURE_SESSION_H */
diff --git a/camera/ndk/include/camera/NdkCameraDevice.h b/camera/ndk/include/camera/NdkCameraDevice.h
index 6c9e85a..3a6f3ef 100644
--- a/camera/ndk/include/camera/NdkCameraDevice.h
+++ b/camera/ndk/include/camera/NdkCameraDevice.h
@@ -661,6 +661,67 @@
#endif /* __ANDROID_API__ >= 24 */
+#if __ANDROID_API__ >= 28
+
+/**
+ * Create a shared ACaptureSessionOutput object.
+ *
+ * <p>The ACaptureSessionOutput is used in {@link ACaptureSessionOutputContainer_add} method to add
+ * an output {@link ANativeWindow} to ACaptureSessionOutputContainer. Use
+ * {@link ACaptureSessionOutput_free} to free the object and its memory after application no longer
+ * needs the {@link ACaptureSessionOutput}. A shared ACaptureSessionOutput can be further modified
+ * via {@link ACaptureSessionSharedOutput_add} or {@link ACaptureSessionSharedOutput_remove} and
+ * must be updated via {@link ACameraCaptureSession_updateSharedOutput}.</p>
+ *
+ * @param anw the {@link ANativeWindow} to be associated with the {@link ACaptureSessionOutput}
+ * @param output the output {@link ACaptureSessionOutput} will be stored here if the
+ * method call succeeds.
+ *
+ * @return <ul>
+ * <li>{@link ACAMERA_OK} if the method call succeeds. The created container will be
+ * filled in the output argument.</li>
+ * <li>{@link ACAMERA_ERROR_INVALID_PARAMETER} if anw or output is NULL.</li></ul>
+ *
+ * @see ACaptureSessionOutputContainer_add
+ */
+camera_status_t ACaptureSessionSharedOutput_create(
+ ANativeWindow* anw, /*out*/ACaptureSessionOutput** output);
+
+/**
+ * Add a native window to shared ACaptureSessionOutput.
+ *
+ * The ACaptureSessionOutput must be created via {@link ACaptureSessionSharedOutput_create}.
+ *
+ * @param output the shared ACaptureSessionOutput to be extended.
+ * @param anw The new native window.
+ *
+ * @return <ul>
+ * <li>{@link ACAMERA_OK} if the method call succeeds.</li>
+ * <li>{@link ACAMERA_ERROR_INVALID_PARAMETER} if anw or output is NULL; or output is not
+ * shared see {@link ACaptureSessionSharedOutput_create}; or anw matches with the native
+ * window associated with ACaptureSessionOutput; or anw is already present inside
+ * ACaptureSessionOutput.</li></ul>
+ */
+camera_status_t ACaptureSessionSharedOutput_add(ACaptureSessionOutput *output, ANativeWindow *anw);
+
+/**
+ * Remove a native window from shared ACaptureSessionOutput.
+ *
+ * @param output the {@link ACaptureSessionOutput} to be modified.
+ * @param anw The native window to be removed.
+ *
+ * @return <ul>
+ * <li>{@link ACAMERA_OK} if the method call succeeds.</li>
+ * <li>{@link ACAMERA_ERROR_INVALID_PARAMETER} if anw or output is NULL; or output is not
+ * shared see {@link ACaptureSessionSharedOutput_create}; or anw matches with the native
+ * window associated with ACaptureSessionOutput; or anw is not present inside
+ * ACaptureSessionOutput.</li></ul>
+ */
+camera_status_t ACaptureSessionSharedOutput_remove(ACaptureSessionOutput *output,
+ ANativeWindow* anw);
+
+#endif /* __ANDROID_API__ >= 28 */
+
__END_DECLS
#endif /* _NDK_CAMERA_DEVICE_H */
diff --git a/camera/ndk/libcamera2ndk.map.txt b/camera/ndk/libcamera2ndk.map.txt
index 41bb22b..0a8a6e9 100644
--- a/camera/ndk/libcamera2ndk.map.txt
+++ b/camera/ndk/libcamera2ndk.map.txt
@@ -6,6 +6,7 @@
ACameraCaptureSession_getDevice;
ACameraCaptureSession_setRepeatingRequest;
ACameraCaptureSession_stopRepeating;
+ ACameraCaptureSession_updateSharedOutput;
ACameraDevice_close;
ACameraDevice_createCaptureRequest;
ACameraDevice_createCaptureSession;
@@ -40,6 +41,9 @@
ACaptureSessionOutputContainer_free;
ACaptureSessionOutputContainer_remove;
ACaptureSessionOutput_create;
+ ACaptureSessionSharedOutput_create;
+ ACaptureSessionSharedOutput_add;
+ ACaptureSessionSharedOutput_remove;
ACaptureSessionOutput_free;
local:
*;
diff --git a/services/camera/libcameraservice/api2/CameraDeviceClient.cpp b/services/camera/libcameraservice/api2/CameraDeviceClient.cpp
index f985382..a376ab4 100644
--- a/services/camera/libcameraservice/api2/CameraDeviceClient.cpp
+++ b/services/camera/libcameraservice/api2/CameraDeviceClient.cpp
@@ -530,10 +530,11 @@
}
int streamId = camera3::CAMERA3_STREAM_ID_INVALID;
+ std::vector<int> surfaceIds;
err = mDevice->createStream(surfaces, deferredConsumer, streamInfo.width,
streamInfo.height, streamInfo.format, streamInfo.dataSpace,
static_cast<camera3_stream_rotation_t>(outputConfiguration.getRotation()),
- &streamId, outputConfiguration.getSurfaceSetID(), isShared);
+ &streamId, &surfaceIds, outputConfiguration.getSurfaceSetID(), isShared);
if (err != OK) {
res = STATUS_ERROR_FMT(CameraService::ERROR_INVALID_OPERATION,
@@ -545,7 +546,8 @@
for (auto& binder : binders) {
ALOGV("%s: mStreamMap add binder %p streamId %d, surfaceId %d",
__FUNCTION__, binder.get(), streamId, i);
- mStreamMap.add(binder, StreamSurfaceId(streamId, i++));
+ mStreamMap.add(binder, StreamSurfaceId(streamId, surfaceIds[i]));
+ i++;
}
mStreamInfoMap[streamId] = streamInfo;
@@ -592,10 +594,12 @@
}
int streamId = camera3::CAMERA3_STREAM_ID_INVALID;
std::vector<sp<Surface>> noSurface;
+ std::vector<int> surfaceIds;
err = mDevice->createStream(noSurface, /*hasDeferredConsumer*/true, width,
height, format, dataSpace,
static_cast<camera3_stream_rotation_t>(outputConfiguration.getRotation()),
- &streamId, outputConfiguration.getSurfaceSetID(), isShared, consumerUsage);
+ &streamId, &surfaceIds, outputConfiguration.getSurfaceSetID(), isShared,
+ consumerUsage);
if (err != OK) {
res = STATUS_ERROR_FMT(CameraService::ERROR_INVALID_OPERATION,
@@ -721,6 +725,130 @@
return res;
}
+binder::Status CameraDeviceClient::updateOutputConfiguration(int streamId,
+ const hardware::camera2::params::OutputConfiguration &outputConfiguration) {
+ ATRACE_CALL();
+
+ binder::Status res;
+ if (!(res = checkPidStatus(__FUNCTION__)).isOk()) return res;
+
+ Mutex::Autolock icl(mBinderSerializationLock);
+
+ if (!mDevice.get()) {
+ return STATUS_ERROR(CameraService::ERROR_DISCONNECTED, "Camera device no longer alive");
+ }
+
+ const std::vector<sp<IGraphicBufferProducer> >& bufferProducers =
+ outputConfiguration.getGraphicBufferProducers();
+ auto producerCount = bufferProducers.size();
+ if (producerCount == 0) {
+ ALOGE("%s: bufferProducers must not be empty", __FUNCTION__);
+ return STATUS_ERROR(CameraService::ERROR_ILLEGAL_ARGUMENT,
+ "bufferProducers must not be empty");
+ }
+
+ // The first output is the one associated with the output configuration.
+ // It should always be present, valid and the corresponding stream id should match.
+ sp<IBinder> binder = IInterface::asBinder(bufferProducers[0]);
+ ssize_t index = mStreamMap.indexOfKey(binder);
+ if (index == NAME_NOT_FOUND) {
+ ALOGE("%s: Outputconfiguration is invalid", __FUNCTION__);
+ return STATUS_ERROR(CameraService::ERROR_ILLEGAL_ARGUMENT,
+ "OutputConfiguration is invalid");
+ }
+ if (mStreamMap.valueFor(binder).streamId() != streamId) {
+ ALOGE("%s: Stream Id: %d provided doesn't match the id: %d in the stream map",
+ __FUNCTION__, streamId, mStreamMap.valueFor(binder).streamId());
+ return STATUS_ERROR(CameraService::ERROR_ILLEGAL_ARGUMENT,
+ "Stream id is invalid");
+ }
+
+ std::vector<size_t> removedSurfaceIds;
+ std::vector<sp<IBinder>> removedOutputs;
+ std::vector<sp<Surface>> newOutputs;
+ std::vector<OutputStreamInfo> streamInfos;
+ KeyedVector<sp<IBinder>, sp<IGraphicBufferProducer>> newOutputsMap;
+ for (auto &it : bufferProducers) {
+ newOutputsMap.add(IInterface::asBinder(it), it);
+ }
+
+ for (size_t i = 0; i < mStreamMap.size(); i++) {
+ ssize_t idx = newOutputsMap.indexOfKey(mStreamMap.keyAt(i));
+ if (idx == NAME_NOT_FOUND) {
+ if (mStreamMap[i].streamId() == streamId) {
+ removedSurfaceIds.push_back(mStreamMap[i].surfaceId());
+ removedOutputs.push_back(mStreamMap.keyAt(i));
+ }
+ } else {
+ if (mStreamMap[i].streamId() != streamId) {
+ ALOGE("%s: Output surface already part of a different stream", __FUNCTION__);
+ return STATUS_ERROR(CameraService::ERROR_ILLEGAL_ARGUMENT,
+ "Target Surface is invalid");
+ }
+ newOutputsMap.removeItemsAt(idx);
+ }
+ }
+
+ for (size_t i = 0; i < newOutputsMap.size(); i++) {
+ OutputStreamInfo outInfo;
+ sp<Surface> surface;
+ res = createSurfaceFromGbp(outInfo, /*isStreamInfoValid*/ false, surface,
+ newOutputsMap.valueAt(i));
+ if (!res.isOk())
+ return res;
+
+ // Stream sharing is only supported for IMPLEMENTATION_DEFINED
+ // formats.
+ if (outInfo.format != HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED) {
+ String8 msg = String8::format("Camera %s: Stream sharing is only supported for "
+ "IMPLEMENTATION_DEFINED format", mCameraIdStr.string());
+ ALOGW("%s: %s", __FUNCTION__, msg.string());
+ return STATUS_ERROR(CameraService::ERROR_ILLEGAL_ARGUMENT, msg.string());
+ }
+ streamInfos.push_back(outInfo);
+ newOutputs.push_back(surface);
+ }
+
+ //Trivial case no changes required
+ if (removedSurfaceIds.empty() && newOutputs.empty()) {
+ return binder::Status::ok();
+ }
+
+ KeyedVector<sp<Surface>, size_t> outputMap;
+ auto ret = mDevice->updateStream(streamId, newOutputs, streamInfos, removedSurfaceIds,
+ &outputMap);
+ if (ret != OK) {
+ switch (ret) {
+ case NAME_NOT_FOUND:
+ case BAD_VALUE:
+ case -EBUSY:
+ res = STATUS_ERROR_FMT(CameraService::ERROR_ILLEGAL_ARGUMENT,
+ "Camera %s: Error updating stream: %s (%d)",
+ mCameraIdStr.string(), strerror(ret), ret);
+ break;
+ default:
+ res = STATUS_ERROR_FMT(CameraService::ERROR_INVALID_OPERATION,
+ "Camera %s: Error updating stream: %s (%d)",
+ mCameraIdStr.string(), strerror(ret), ret);
+ break;
+ }
+ } else {
+ for (const auto &it : removedOutputs) {
+ mStreamMap.removeItem(it);
+ }
+
+ for (size_t i = 0; i < outputMap.size(); i++) {
+ mStreamMap.add(IInterface::asBinder(outputMap.keyAt(i)->getIGraphicBufferProducer()),
+ StreamSurfaceId(streamId, outputMap.valueAt(i)));
+ }
+
+ ALOGV("%s: Camera %s: Successful stream ID %d update",
+ __FUNCTION__, mCameraIdStr.string(), streamId);
+ }
+
+ return res;
+}
+
bool CameraDeviceClient::isPublicFormat(int32_t format)
{
switch(format) {
@@ -1242,15 +1370,12 @@
}
std::vector<sp<Surface>> consumerSurfaces;
- std::vector<size_t> consumerSurfaceIds;
- size_t surfaceId = 0;
for (auto& bufferProducer : bufferProducers) {
// Don't create multiple streams for the same target surface
ssize_t index = mStreamMap.indexOfKey(IInterface::asBinder(bufferProducer));
if (index != NAME_NOT_FOUND) {
ALOGV("Camera %s: Surface already has a stream created "
" for it (ID %zd)", mCameraIdStr.string(), index);
- surfaceId++;
continue;
}
@@ -1262,8 +1387,6 @@
return res;
consumerSurfaces.push_back(surface);
- consumerSurfaceIds.push_back(surfaceId);
- surfaceId++;
}
// Gracefully handle case where finalizeOutputConfigurations is called
@@ -1275,12 +1398,13 @@
// Finish the deferred stream configuration with the surface.
status_t err;
- err = mDevice->setConsumerSurfaces(streamId, consumerSurfaces);
+ std::vector<int> consumerSurfaceIds;
+ err = mDevice->setConsumerSurfaces(streamId, consumerSurfaces, &consumerSurfaceIds);
if (err == OK) {
for (size_t i = 0; i < consumerSurfaces.size(); i++) {
sp<IBinder> binder = IInterface::asBinder(
consumerSurfaces[i]->getIGraphicBufferProducer());
- ALOGV("%s: mStreamMap add binder %p streamId %d, surfaceId %zu", __FUNCTION__,
+ ALOGV("%s: mStreamMap add binder %p streamId %d, surfaceId %d", __FUNCTION__,
binder.get(), streamId, consumerSurfaceIds[i]);
mStreamMap.add(binder, StreamSurfaceId(streamId, consumerSurfaceIds[i]));
}
diff --git a/services/camera/libcameraservice/api2/CameraDeviceClient.h b/services/camera/libcameraservice/api2/CameraDeviceClient.h
index 50661cb..e1a11db 100644
--- a/services/camera/libcameraservice/api2/CameraDeviceClient.h
+++ b/services/camera/libcameraservice/api2/CameraDeviceClient.h
@@ -26,6 +26,8 @@
#include "common/FrameProcessorBase.h"
#include "common/Camera2ClientBase.h"
+using android::camera3::OutputStreamInfo;
+
namespace android {
struct CameraDeviceClientBase :
@@ -131,6 +133,10 @@
// Prepare stream by preallocating up to maxCount of its buffers
virtual binder::Status prepare2(int32_t maxCount, int32_t streamId) override;
+ // Update an output configuration
+ virtual binder::Status updateOutputConfiguration(int streamId,
+ const hardware::camera2::params::OutputConfiguration &outputConfiguration) override;
+
// Finalize the output configurations with surfaces not added before.
virtual binder::Status finalizeOutputConfigurations(int32_t streamId,
const hardware::camera2::params::OutputConfiguration &outputConfiguration) override;
@@ -206,24 +212,6 @@
}; // class StreamSurfaceId
- // OutputStreamInfo describes the property of a camera stream.
- class OutputStreamInfo {
- public:
- int width;
- int height;
- int format;
- android_dataspace dataSpace;
- uint64_t consumerUsage;
- bool finalized = false;
- OutputStreamInfo() :
- width(-1), height(-1), format(-1), dataSpace(HAL_DATASPACE_UNKNOWN),
- consumerUsage(0) {}
- OutputStreamInfo(int _width, int _height, int _format, android_dataspace _dataSpace,
- uint64_t _consumerUsage) :
- width(_width), height(_height), format(_format),
- dataSpace(_dataSpace), consumerUsage(_consumerUsage) {}
- };
-
private:
/** ICameraDeviceUser interface-related private members */
diff --git a/services/camera/libcameraservice/common/CameraDeviceBase.h b/services/camera/libcameraservice/common/CameraDeviceBase.h
index 3919bfa..68384b0 100644
--- a/services/camera/libcameraservice/common/CameraDeviceBase.h
+++ b/services/camera/libcameraservice/common/CameraDeviceBase.h
@@ -23,6 +23,7 @@
#include <utils/String8.h>
#include <utils/String16.h>
#include <utils/Vector.h>
+#include <utils/KeyedVector.h>
#include <utils/Timers.h>
#include <utils/List.h>
@@ -118,6 +119,7 @@
virtual status_t createStream(sp<Surface> consumer,
uint32_t width, uint32_t height, int format,
android_dataspace dataSpace, camera3_stream_rotation_t rotation, int *id,
+ std::vector<int> *surfaceIds = nullptr,
int streamSetId = camera3::CAMERA3_STREAM_SET_ID_INVALID,
bool isShared = false, uint64_t consumerUsage = 0) = 0;
@@ -131,6 +133,7 @@
virtual status_t createStream(const std::vector<sp<Surface>>& consumers,
bool hasDeferredConsumer, uint32_t width, uint32_t height, int format,
android_dataspace dataSpace, camera3_stream_rotation_t rotation, int *id,
+ std::vector<int> *surfaceIds = nullptr,
int streamSetId = camera3::CAMERA3_STREAM_SET_ID_INVALID,
bool isShared = false, uint64_t consumerUsage = 0) = 0;
@@ -347,8 +350,15 @@
* Set the deferred consumer surface and finish the rest of the stream configuration.
*/
virtual status_t setConsumerSurfaces(int streamId,
- const std::vector<sp<Surface>>& consumers) = 0;
+ const std::vector<sp<Surface>>& consumers, std::vector<int> *surfaceIds /*out*/) = 0;
+ /**
+ * Update a given stream.
+ */
+ virtual status_t updateStream(int streamId, const std::vector<sp<Surface>> &newSurfaces,
+ const std::vector<android::camera3::OutputStreamInfo> &outputInfo,
+ const std::vector<size_t> &removedSurfaceIds,
+ KeyedVector<sp<Surface>, size_t> *outputMap/*out*/) = 0;
};
}; // namespace android
diff --git a/services/camera/libcameraservice/device3/Camera3Device.cpp b/services/camera/libcameraservice/device3/Camera3Device.cpp
index ced1d3a..e0a2dd4 100644
--- a/services/camera/libcameraservice/device3/Camera3Device.cpp
+++ b/services/camera/libcameraservice/device3/Camera3Device.cpp
@@ -1222,7 +1222,7 @@
status_t Camera3Device::createStream(sp<Surface> consumer,
uint32_t width, uint32_t height, int format,
android_dataspace dataSpace, camera3_stream_rotation_t rotation, int *id,
- int streamSetId, bool isShared, uint64_t consumerUsage) {
+ std::vector<int> *surfaceIds, int streamSetId, bool isShared, uint64_t consumerUsage) {
ATRACE_CALL();
if (consumer == nullptr) {
@@ -1234,14 +1234,15 @@
consumers.push_back(consumer);
return createStream(consumers, /*hasDeferredConsumer*/ false, width, height,
- format, dataSpace, rotation, id, streamSetId, isShared, consumerUsage);
+ format, dataSpace, rotation, id, surfaceIds, streamSetId, isShared, consumerUsage);
}
status_t Camera3Device::createStream(const std::vector<sp<Surface>>& consumers,
bool hasDeferredConsumer, uint32_t width, uint32_t height, int format,
android_dataspace dataSpace, camera3_stream_rotation_t rotation, int *id,
- int streamSetId, bool isShared, uint64_t consumerUsage) {
+ std::vector<int> *surfaceIds, int streamSetId, bool isShared, uint64_t consumerUsage) {
ATRACE_CALL();
+
Mutex::Autolock il(mInterfaceLock);
nsecs_t maxExpectedDuration = getExpectedInFlightDuration();
Mutex::Autolock l(mLock);
@@ -1330,6 +1331,19 @@
width, height, format, dataSpace, rotation,
mTimestampOffset, streamSetId);
}
+
+ size_t consumerCount = consumers.size();
+ for (size_t i = 0; i < consumerCount; i++) {
+ int id = newStream->getSurfaceId(consumers[i]);
+ if (id < 0) {
+ SET_ERR_L("Invalid surface id");
+ return BAD_VALUE;
+ }
+ if (surfaceIds != nullptr) {
+ surfaceIds->push_back(id);
+ }
+ }
+
newStream->setStatusTracker(mStatusTracker);
newStream->setBufferManager(mBufferManager);
@@ -1936,10 +1950,15 @@
}
status_t Camera3Device::setConsumerSurfaces(int streamId,
- const std::vector<sp<Surface>>& consumers) {
+ const std::vector<sp<Surface>>& consumers, std::vector<int> *surfaceIds) {
ATRACE_CALL();
ALOGV("%s: Camera %s: set consumer surface for stream %d",
__FUNCTION__, mId.string(), streamId);
+
+ if (surfaceIds == nullptr) {
+ return BAD_VALUE;
+ }
+
Mutex::Autolock il(mInterfaceLock);
Mutex::Autolock l(mLock);
@@ -1960,6 +1979,15 @@
return res;
}
+ for (auto &consumer : consumers) {
+ int id = stream->getSurfaceId(consumer);
+ if (id < 0) {
+ CLOGE("Invalid surface id!");
+ return BAD_VALUE;
+ }
+ surfaceIds->push_back(id);
+ }
+
if (stream->isConsumerConfigurationDeferred()) {
if (!stream->isConfiguring()) {
CLOGE("Stream %d was already fully configured.", streamId);
@@ -1977,6 +2005,40 @@
return OK;
}
+status_t Camera3Device::updateStream(int streamId, const std::vector<sp<Surface>> &newSurfaces,
+ const std::vector<OutputStreamInfo> &outputInfo,
+ const std::vector<size_t> &removedSurfaceIds, KeyedVector<sp<Surface>, size_t> *outputMap) {
+ Mutex::Autolock il(mInterfaceLock);
+ Mutex::Autolock l(mLock);
+
+ ssize_t idx = mOutputStreams.indexOfKey(streamId);
+ if (idx == NAME_NOT_FOUND) {
+ CLOGE("Stream %d is unknown", streamId);
+ return idx;
+ }
+
+ for (const auto &it : removedSurfaceIds) {
+ if (mRequestThread->isOutputSurfacePending(streamId, it)) {
+ CLOGE("Shared surface still part of a pending request!");
+ return -EBUSY;
+ }
+ }
+
+ sp<Camera3OutputStreamInterface> stream = mOutputStreams[idx];
+ status_t res = stream->updateStream(newSurfaces, outputInfo, removedSurfaceIds, outputMap);
+ if (res != OK) {
+ CLOGE("Stream %d failed to update stream (error %d %s) ",
+ streamId, res, strerror(-res));
+ if (res == UNKNOWN_ERROR) {
+ SET_ERR_L("%s: Stream update failed to revert to previous output configuration!",
+ __FUNCTION__);
+ }
+ return res;
+ }
+
+ return res;
+}
+
/**
* Camera3Device private methods
*/
@@ -4342,6 +4404,46 @@
return false;
}
+bool Camera3Device::RequestThread::isOutputSurfacePending(int streamId, size_t surfaceId) {
+ ATRACE_CALL();
+ Mutex::Autolock l(mRequestLock);
+
+ for (const auto& nextRequest : mNextRequests) {
+ for (const auto& s : nextRequest.captureRequest->mOutputSurfaces) {
+ if (s.first == streamId) {
+ const auto &it = std::find(s.second.begin(), s.second.end(), surfaceId);
+ if (it != s.second.end()) {
+ return true;
+ }
+ }
+ }
+ }
+
+ for (const auto& request : mRequestQueue) {
+ for (const auto& s : request->mOutputSurfaces) {
+ if (s.first == streamId) {
+ const auto &it = std::find(s.second.begin(), s.second.end(), surfaceId);
+ if (it != s.second.end()) {
+ return true;
+ }
+ }
+ }
+ }
+
+ for (const auto& request : mRepeatingRequests) {
+ for (const auto& s : request->mOutputSurfaces) {
+ if (s.first == streamId) {
+ const auto &it = std::find(s.second.begin(), s.second.end(), surfaceId);
+ if (it != s.second.end()) {
+ return true;
+ }
+ }
+ }
+ }
+
+ return false;
+}
+
nsecs_t Camera3Device::getExpectedInFlightDuration() {
ATRACE_CALL();
Mutex::Autolock al(mInFlightLock);
diff --git a/services/camera/libcameraservice/device3/Camera3Device.h b/services/camera/libcameraservice/device3/Camera3Device.h
index fbbbd08..357b893 100644
--- a/services/camera/libcameraservice/device3/Camera3Device.h
+++ b/services/camera/libcameraservice/device3/Camera3Device.h
@@ -44,6 +44,8 @@
#include "utils/LatencyHistogram.h"
#include <camera_metadata_hidden.h>
+using android::camera3::OutputStreamInfo;
+
/**
* Function pointer types with C calling convention to
* use for HAL callback functions.
@@ -117,11 +119,13 @@
status_t createStream(sp<Surface> consumer,
uint32_t width, uint32_t height, int format,
android_dataspace dataSpace, camera3_stream_rotation_t rotation, int *id,
+ std::vector<int> *surfaceIds = nullptr,
int streamSetId = camera3::CAMERA3_STREAM_SET_ID_INVALID,
bool isShared = false, uint64_t consumerUsage = 0) override;
status_t createStream(const std::vector<sp<Surface>>& consumers,
bool hasDeferredConsumer, uint32_t width, uint32_t height, int format,
android_dataspace dataSpace, camera3_stream_rotation_t rotation, int *id,
+ std::vector<int> *surfaceIds = nullptr,
int streamSetId = camera3::CAMERA3_STREAM_SET_ID_INVALID,
bool isShared = false, uint64_t consumerUsage = 0) override;
@@ -176,7 +180,17 @@
* Set the deferred consumer surfaces to the output stream and finish the deferred
* consumer configuration.
*/
- status_t setConsumerSurfaces(int streamId, const std::vector<sp<Surface>>& consumers) override;
+ status_t setConsumerSurfaces(
+ int streamId, const std::vector<sp<Surface>>& consumers,
+ std::vector<int> *surfaceIds /*out*/) override;
+
+ /**
+ * Update a given stream.
+ */
+ status_t updateStream(int streamId, const std::vector<sp<Surface>> &newSurfaces,
+ const std::vector<OutputStreamInfo> &outputInfo,
+ const std::vector<size_t> &removedSurfaceIds,
+ KeyedVector<sp<Surface>, size_t> *outputMap/*out*/);
private:
@@ -705,6 +719,12 @@
*/
bool isStreamPending(sp<camera3::Camera3StreamInterface>& stream);
+ /**
+ * Returns true if the surface is a target of any queued or repeating
+ * capture request
+ */
+ bool isOutputSurfacePending(int streamId, size_t surfaceId);
+
// dump processCaptureRequest latency
void dumpCaptureRequestLatency(int fd, const char* name) {
mRequestLatency.dump(fd, name);
diff --git a/services/camera/libcameraservice/device3/Camera3DummyStream.cpp b/services/camera/libcameraservice/device3/Camera3DummyStream.cpp
index 6e2978f..4b36ea2 100644
--- a/services/camera/libcameraservice/device3/Camera3DummyStream.cpp
+++ b/services/camera/libcameraservice/device3/Camera3DummyStream.cpp
@@ -113,6 +113,15 @@
__FUNCTION__, mId);
return INVALID_OPERATION;
}
+
+status_t Camera3DummyStream::updateStream(const std::vector<sp<Surface>> &/*outputSurfaces*/,
+ const std::vector<OutputStreamInfo> &/*outputInfo*/,
+ const std::vector<size_t> &/*removedSurfaceIds*/,
+ KeyedVector<sp<Surface>, size_t> * /*outputMap*/) {
+ ALOGE("%s: this method is not supported!", __FUNCTION__);
+ return INVALID_OPERATION;
+}
+
}; // namespace camera3
}; // namespace android
diff --git a/services/camera/libcameraservice/device3/Camera3DummyStream.h b/services/camera/libcameraservice/device3/Camera3DummyStream.h
index 492fb49..3212031 100644
--- a/services/camera/libcameraservice/device3/Camera3DummyStream.h
+++ b/services/camera/libcameraservice/device3/Camera3DummyStream.h
@@ -71,6 +71,19 @@
*/
virtual status_t setConsumers(const std::vector<sp<Surface>>& consumers);
+ /**
+ * Query the output surface id.
+ */
+ virtual ssize_t getSurfaceId(const sp<Surface> &/*surface*/) { return 0; }
+
+ /**
+ * Update the stream output surfaces.
+ */
+ virtual status_t updateStream(const std::vector<sp<Surface>> &outputSurfaces,
+ const std::vector<OutputStreamInfo> &outputInfo,
+ const std::vector<size_t> &removedSurfaceIds,
+ KeyedVector<sp<Surface>, size_t> *outputMap/*out*/);
+
protected:
/**
diff --git a/services/camera/libcameraservice/device3/Camera3OutputStream.cpp b/services/camera/libcameraservice/device3/Camera3OutputStream.cpp
index 0a02a32..329172a 100644
--- a/services/camera/libcameraservice/device3/Camera3OutputStream.cpp
+++ b/services/camera/libcameraservice/device3/Camera3OutputStream.cpp
@@ -691,6 +691,14 @@
return OK;
}
+status_t Camera3OutputStream::updateStream(const std::vector<sp<Surface>> &/*outputSurfaces*/,
+ const std::vector<OutputStreamInfo> &/*outputInfo*/,
+ const std::vector<size_t> &/*removedSurfaceIds*/,
+ KeyedVector<sp<Surface>, size_t> * /*outputMapo*/) {
+ ALOGE("%s: this method is not supported!", __FUNCTION__);
+ return INVALID_OPERATION;
+}
+
void Camera3OutputStream::BufferReleasedListener::onBufferReleased() {
sp<Camera3OutputStream> stream = mParent.promote();
if (stream == nullptr) {
diff --git a/services/camera/libcameraservice/device3/Camera3OutputStream.h b/services/camera/libcameraservice/device3/Camera3OutputStream.h
index 7023d5d..fbb14fe 100644
--- a/services/camera/libcameraservice/device3/Camera3OutputStream.h
+++ b/services/camera/libcameraservice/device3/Camera3OutputStream.h
@@ -172,6 +172,19 @@
*/
status_t setBufferManager(sp<Camera3BufferManager> bufferManager);
+ /**
+ * Query the ouput surface id.
+ */
+ virtual ssize_t getSurfaceId(const sp<Surface> &/*surface*/) { return 0; }
+
+ /**
+ * Update the stream output surfaces.
+ */
+ virtual status_t updateStream(const std::vector<sp<Surface>> &outputSurfaces,
+ const std::vector<OutputStreamInfo> &outputInfo,
+ const std::vector<size_t> &removedSurfaceIds,
+ KeyedVector<sp<Surface>, size_t> *outputMap/*out*/);
+
protected:
Camera3OutputStream(int id, camera3_stream_type_t type,
uint32_t width, uint32_t height, int format,
diff --git a/services/camera/libcameraservice/device3/Camera3OutputStreamInterface.h b/services/camera/libcameraservice/device3/Camera3OutputStreamInterface.h
index 8107dd0..edfbab1 100644
--- a/services/camera/libcameraservice/device3/Camera3OutputStreamInterface.h
+++ b/services/camera/libcameraservice/device3/Camera3OutputStreamInterface.h
@@ -18,6 +18,7 @@
#define ANDROID_SERVERS_CAMERA3_OUTPUT_STREAM_INTERFACE_H
#include "Camera3StreamInterface.h"
+#include <utils/KeyedVector.h>
namespace android {
@@ -59,6 +60,19 @@
*
*/
virtual status_t detachBuffer(sp<GraphicBuffer>* buffer, int* fenceFd) = 0;
+
+ /**
+ * Query the surface id.
+ */
+ virtual ssize_t getSurfaceId(const sp<Surface> &surface) = 0;
+
+ /**
+ * Update the stream output surfaces.
+ */
+ virtual status_t updateStream(const std::vector<sp<Surface>> &outputSurfaces,
+ const std::vector<OutputStreamInfo> &outputInfo,
+ const std::vector<size_t> &removedSurfaceIds,
+ KeyedVector<sp<Surface>, size_t> *outputMap/*out*/) = 0;
};
} // namespace camera3
diff --git a/services/camera/libcameraservice/device3/Camera3SharedOutputStream.cpp b/services/camera/libcameraservice/device3/Camera3SharedOutputStream.cpp
index fb7472b..1c9417b 100644
--- a/services/camera/libcameraservice/device3/Camera3SharedOutputStream.cpp
+++ b/services/camera/libcameraservice/device3/Camera3SharedOutputStream.cpp
@@ -20,6 +20,8 @@
namespace camera3 {
+const size_t Camera3SharedOutputStream::kMaxOutputs;
+
Camera3SharedOutputStream::Camera3SharedOutputStream(int id,
const std::vector<sp<Surface>>& surfaces,
uint32_t width, uint32_t height, int format,
@@ -28,8 +30,14 @@
nsecs_t timestampOffset, int setId) :
Camera3OutputStream(id, CAMERA3_STREAM_OUTPUT, width, height,
format, dataSpace, rotation, consumerUsage,
- timestampOffset, setId),
- mSurfaces(surfaces) {
+ timestampOffset, setId) {
+ size_t consumerCount = std::min(surfaces.size(), kMaxOutputs);
+ if (surfaces.size() > consumerCount) {
+ ALOGE("%s: Trying to add more consumers than the maximum ", __func__);
+ }
+ for (size_t i = 0; i < consumerCount; i++) {
+ mSurfaces[i] = surfaces[i];
+ }
}
Camera3SharedOutputStream::~Camera3SharedOutputStream() {
@@ -44,7 +52,16 @@
uint64_t usage;
getEndpointUsage(&usage);
- res = mStreamSplitter->connect(mSurfaces, usage, camera3_stream::max_buffers, &mConsumer);
+ std::unordered_map<size_t, sp<Surface>> initialSurfaces;
+ for (size_t i = 0; i < kMaxOutputs; i++) {
+ if (mSurfaces[i] != nullptr) {
+ initialSurfaces.emplace(i, mSurfaces[i]);
+ }
+ }
+
+ android::PixelFormat format = isFormatOverridden() ? getOriginalFormat() : getFormat();
+ res = mStreamSplitter->connect(initialSurfaces, usage, mUsage, camera3_stream::max_buffers,
+ getWidth(), getHeight(), format, &mConsumer);
if (res != OK) {
ALOGE("%s: Failed to connect to stream splitter: %s(%d)",
__FUNCTION__, strerror(-res), res);
@@ -68,7 +85,11 @@
bool Camera3SharedOutputStream::isConsumerConfigurationDeferred(size_t surface_id) const {
Mutex::Autolock l(mLock);
- return (surface_id >= mSurfaces.size());
+ if (surface_id >= kMaxOutputs) {
+ return true;
+ }
+
+ return (mSurfaces[surface_id] == nullptr);
}
status_t Camera3SharedOutputStream::setConsumers(const std::vector<sp<Surface>>& surfaces) {
@@ -85,11 +106,17 @@
return INVALID_OPERATION;
}
- mSurfaces.push_back(surface);
+ ssize_t id = getNextSurfaceIdLocked();
+ if (id < 0) {
+ ALOGE("%s: No surface ids available!", __func__);
+ return NO_MEMORY;
+ }
+
+ mSurfaces[id] = surface;
// Only call addOutput if the splitter has been connected.
if (mStreamSplitter != nullptr) {
- ret = mStreamSplitter->addOutput(surface);
+ ret = mStreamSplitter->addOutput(id, surface);
if (ret != OK) {
ALOGE("%s: addOutput failed with error code %d", __FUNCTION__, ret);
return ret;
@@ -200,9 +227,9 @@
// Called before shared buffer queue is constructed.
*usage = getPresetConsumerUsage();
- for (const auto& surface : mSurfaces) {
- if (surface != nullptr) {
- res = getEndpointUsageForSurface(&u, surface);
+ for (size_t id = 0; id < kMaxOutputs; id++) {
+ if (mSurfaces[id] != nullptr) {
+ res = getEndpointUsageForSurface(&u, mSurfaces[id]);
*usage |= u;
}
}
@@ -215,6 +242,140 @@
return res;
}
+ssize_t Camera3SharedOutputStream::getNextSurfaceIdLocked() {
+ ssize_t id = -1;
+ for (size_t i = 0; i < kMaxOutputs; i++) {
+ if (mSurfaces[i] == nullptr) {
+ id = i;
+ break;
+ }
+ }
+
+ return id;
+}
+
+ssize_t Camera3SharedOutputStream::getSurfaceId(const sp<Surface> &surface) {
+ Mutex::Autolock l(mLock);
+ ssize_t id = -1;
+ for (size_t i = 0; i < kMaxOutputs; i++) {
+ if (mSurfaces[i] == surface) {
+ id = i;
+ break;
+ }
+ }
+
+ return id;
+}
+
+status_t Camera3SharedOutputStream::revertPartialUpdateLocked(
+ const KeyedVector<sp<Surface>, size_t> &removedSurfaces,
+ const KeyedVector<sp<Surface>, size_t> &attachedSurfaces) {
+ status_t ret = OK;
+
+ for (size_t i = 0; i < attachedSurfaces.size(); i++) {
+ size_t index = attachedSurfaces.valueAt(i);
+ if (mStreamSplitter != nullptr) {
+ ret = mStreamSplitter->removeOutput(index);
+ if (ret != OK) {
+ return UNKNOWN_ERROR;
+ }
+ }
+ mSurfaces[index] = nullptr;
+ }
+
+ for (size_t i = 0; i < removedSurfaces.size(); i++) {
+ size_t index = removedSurfaces.valueAt(i);
+ if (mStreamSplitter != nullptr) {
+ ret = mStreamSplitter->addOutput(index, removedSurfaces.keyAt(i));
+ if (ret != OK) {
+ return UNKNOWN_ERROR;
+ }
+ }
+ mSurfaces[index] = removedSurfaces.keyAt(i);
+ }
+
+ return ret;
+}
+
+status_t Camera3SharedOutputStream::updateStream(const std::vector<sp<Surface>> &outputSurfaces,
+ const std::vector<OutputStreamInfo> &outputInfo,
+ const std::vector<size_t> &removedSurfaceIds,
+ KeyedVector<sp<Surface>, size_t> *outputMap) {
+ status_t ret = OK;
+ Mutex::Autolock l(mLock);
+
+ if ((outputMap == nullptr) || (outputInfo.size() != outputSurfaces.size()) ||
+ (outputSurfaces.size() > kMaxOutputs)) {
+ return BAD_VALUE;
+ }
+
+ uint64_t usage;
+ getEndpointUsage(&usage);
+ KeyedVector<sp<Surface>, size_t> removedSurfaces;
+ //Check whether the new surfaces are compatible.
+ for (const auto &infoIt : outputInfo) {
+ bool imgReaderUsage = (infoIt.consumerUsage & GRALLOC_USAGE_SW_READ_OFTEN) ? true : false;
+ bool sizeMismatch = ((static_cast<uint32_t>(infoIt.width) != getWidth()) ||
+ (static_cast<uint32_t> (infoIt.height) != getHeight())) ?
+ true : false;
+ if ((imgReaderUsage && sizeMismatch) ||
+ (infoIt.format != getOriginalFormat() && infoIt.format != getFormat()) ||
+ (infoIt.dataSpace != getDataSpace() &&
+ infoIt.dataSpace != getOriginalDataSpace())) {
+ ALOGE("%s: Shared surface parameters format: 0x%x dataSpace: 0x%x "
+ " don't match source stream format: 0x%x dataSpace: 0x%x", __FUNCTION__,
+ infoIt.format, infoIt.dataSpace, getFormat(), getDataSpace());
+ return BAD_VALUE;
+ }
+ }
+
+ //First remove all absent outputs
+ for (const auto &it : removedSurfaceIds) {
+ if (mStreamSplitter != nullptr) {
+ ret = mStreamSplitter->removeOutput(it);
+ if (ret != OK) {
+ ALOGE("%s: failed with error code %d", __FUNCTION__, ret);
+ status_t res = revertPartialUpdateLocked(removedSurfaces, *outputMap);
+ if (res != OK) {
+ return res;
+ }
+ return ret;
+
+ }
+ }
+ mSurfaces[it] = nullptr;
+ removedSurfaces.add(mSurfaces[it], it);
+ }
+
+ //Next add the new outputs
+ for (const auto &it : outputSurfaces) {
+ ssize_t surfaceId = getNextSurfaceIdLocked();
+ if (surfaceId < 0) {
+ ALOGE("%s: No more available output slots!", __FUNCTION__);
+ status_t res = revertPartialUpdateLocked(removedSurfaces, *outputMap);
+ if (res != OK) {
+ return res;
+ }
+ return NO_MEMORY;
+ }
+ if (mStreamSplitter != nullptr) {
+ ret = mStreamSplitter->addOutput(surfaceId, it);
+ if (ret != OK) {
+ ALOGE("%s: failed with error code %d", __FUNCTION__, ret);
+ status_t res = revertPartialUpdateLocked(removedSurfaces, *outputMap);
+ if (res != OK) {
+ return res;
+ }
+ return ret;
+ }
+ }
+ mSurfaces[surfaceId] = it;
+ outputMap->add(it, surfaceId);
+ }
+
+ return ret;
+}
+
} // namespace camera3
} // namespace android
diff --git a/services/camera/libcameraservice/device3/Camera3SharedOutputStream.h b/services/camera/libcameraservice/device3/Camera3SharedOutputStream.h
index 22bb2fc..6eab8bd 100644
--- a/services/camera/libcameraservice/device3/Camera3SharedOutputStream.h
+++ b/services/camera/libcameraservice/device3/Camera3SharedOutputStream.h
@@ -46,9 +46,24 @@
virtual status_t setConsumers(const std::vector<sp<Surface>>& consumers);
+ virtual ssize_t getSurfaceId(const sp<Surface> &surface);
+
+ virtual status_t updateStream(const std::vector<sp<Surface>> &outputSurfaces,
+ const std::vector<OutputStreamInfo> &outputInfo,
+ const std::vector<size_t> &removedSurfaceIds,
+ KeyedVector<sp<Surface>, size_t> *outputMap/*out*/);
+
private:
- // Surfaces passed in constructor from app
- std::vector<sp<Surface> > mSurfaces;
+
+ static const size_t kMaxOutputs = 4;
+
+ // Map surfaceId -> output surfaces
+ sp<Surface> mSurfaces[kMaxOutputs];
+
+ ssize_t getNextSurfaceIdLocked();
+
+ status_t revertPartialUpdateLocked(const KeyedVector<sp<Surface>, size_t> &removedSurfaces,
+ const KeyedVector<sp<Surface>, size_t> &attachedSurfaces);
/**
* The Camera3StreamSplitter object this stream uses for stream
diff --git a/services/camera/libcameraservice/device3/Camera3StreamInterface.h b/services/camera/libcameraservice/device3/Camera3StreamInterface.h
index cc9bf8e..9ed7184 100644
--- a/services/camera/libcameraservice/device3/Camera3StreamInterface.h
+++ b/services/camera/libcameraservice/device3/Camera3StreamInterface.h
@@ -43,6 +43,24 @@
class StatusTracker;
+// OutputStreamInfo describes the property of a camera stream.
+class OutputStreamInfo {
+ public:
+ int width;
+ int height;
+ int format;
+ android_dataspace dataSpace;
+ uint64_t consumerUsage;
+ bool finalized = false;
+ OutputStreamInfo() :
+ width(-1), height(-1), format(-1), dataSpace(HAL_DATASPACE_UNKNOWN),
+ consumerUsage(0) {}
+ OutputStreamInfo(int _width, int _height, int _format, android_dataspace _dataSpace,
+ uint64_t _consumerUsage) :
+ width(_width), height(_height), format(_format),
+ dataSpace(_dataSpace), consumerUsage(_consumerUsage) {}
+};
+
/**
* An interface for managing a single stream of input and/or output data from
* the camera device.
diff --git a/services/camera/libcameraservice/device3/Camera3StreamSplitter.cpp b/services/camera/libcameraservice/device3/Camera3StreamSplitter.cpp
index a0a50c2..49a10dc 100644
--- a/services/camera/libcameraservice/device3/Camera3StreamSplitter.cpp
+++ b/services/camera/libcameraservice/device3/Camera3StreamSplitter.cpp
@@ -38,8 +38,9 @@
namespace android {
-status_t Camera3StreamSplitter::connect(const std::vector<sp<Surface> >& surfaces,
- uint64_t consumerUsage, size_t halMaxBuffers, sp<Surface>* consumer) {
+status_t Camera3StreamSplitter::connect(const std::unordered_map<size_t, sp<Surface>> &surfaces,
+ uint64_t consumerUsage, uint64_t producerUsage, size_t halMaxBuffers, uint32_t width,
+ uint32_t height, android::PixelFormat format, sp<Surface>* consumer) {
ATRACE_CALL();
if (consumer == nullptr) {
SP_LOGE("%s: consumer pointer is NULL", __FUNCTION__);
@@ -62,12 +63,12 @@
mConsumerName = getUniqueConsumerName();
// Add output surfaces. This has to be before creating internal buffer queue
// in order to get max consumer side buffers.
- for (size_t i = 0; i < surfaces.size(); i++) {
- if (surfaces[i] == nullptr) {
+ for (auto &it : surfaces) {
+ if (it.second == nullptr) {
SP_LOGE("%s: Fatal: surface is NULL", __FUNCTION__);
return BAD_VALUE;
}
- res = addOutputLocked(surfaces[i]);
+ res = addOutputLocked(it.first, it.second);
if (res != OK) {
SP_LOGE("%s: Failed to add output surface: %s(%d)",
__FUNCTION__, strerror(-res), res);
@@ -94,8 +95,20 @@
return NO_MEMORY;
}
+ res = mProducer->setAsyncMode(true);
+ if (res != OK) {
+ SP_LOGE("%s: Failed to enable input queue async mode: %s(%d)", __FUNCTION__,
+ strerror(-res), res);
+ return res;
+ }
+
res = mConsumer->consumerConnect(this, /* controlledByApp */ false);
+ mWidth = width;
+ mHeight = height;
+ mFormat = format;
+ mProducerUsage = producerUsage;
+
SP_LOGV("%s: connected", __FUNCTION__);
return res;
}
@@ -117,10 +130,13 @@
mNotifiers.clear();
for (auto& output : mOutputs) {
- output->disconnect(NATIVE_WINDOW_API_CAMERA);
+ if (output.second != nullptr) {
+ output.second->disconnect(NATIVE_WINDOW_API_CAMERA);
+ }
}
mOutputs.clear();
mOutputSlots.clear();
+ mConsumerBufferCount.clear();
mConsumer->consumerDisconnect();
@@ -139,10 +155,10 @@
disconnect();
}
-status_t Camera3StreamSplitter::addOutput(const sp<Surface>& outputQueue) {
+status_t Camera3StreamSplitter::addOutput(size_t surfaceId, const sp<Surface>& outputQueue) {
ATRACE_CALL();
Mutex::Autolock lock(mMutex);
- status_t res = addOutputLocked(outputQueue);
+ status_t res = addOutputLocked(surfaceId, outputQueue);
if (res != OK) {
SP_LOGE("%s: addOutputLocked failed %d", __FUNCTION__, res);
@@ -154,18 +170,30 @@
return res;
}
-status_t Camera3StreamSplitter::addOutputLocked(const sp<Surface>& outputQueue) {
+status_t Camera3StreamSplitter::addOutputLocked(size_t surfaceId, const sp<Surface>& outputQueue) {
ATRACE_CALL();
if (outputQueue == nullptr) {
SP_LOGE("addOutput: outputQueue must not be NULL");
return BAD_VALUE;
}
+ if (mOutputs[surfaceId] != nullptr) {
+ SP_LOGE("%s: surfaceId: %u already taken!", __FUNCTION__, (unsigned) surfaceId);
+ return BAD_VALUE;
+ }
+
+ status_t res = native_window_set_buffers_dimensions(outputQueue.get(),
+ mWidth, mHeight);
+ if (res != NO_ERROR) {
+ SP_LOGE("addOutput: failed to set buffer dimensions (%d)", res);
+ return res;
+ }
+
sp<IGraphicBufferProducer> gbp = outputQueue->getIGraphicBufferProducer();
// Connect to the buffer producer
sp<OutputListener> listener(new OutputListener(this, gbp));
IInterface::asBinder(gbp)->linkToDeath(listener);
- status_t res = outputQueue->connect(NATIVE_WINDOW_API_CAMERA, listener);
+ res = outputQueue->connect(NATIVE_WINDOW_API_CAMERA, listener);
if (res != NO_ERROR) {
SP_LOGE("addOutput: failed to connect (%d)", res);
return res;
@@ -208,7 +236,8 @@
}
// Add new entry into mOutputs
- mOutputs.push_back(gbp);
+ mOutputs[surfaceId] = gbp;
+ mConsumerBufferCount[surfaceId] = maxConsumerBuffers;
mNotifiers[gbp] = listener;
mOutputSlots[gbp] = std::make_unique<OutputSlots>(totalBufferCount);
@@ -216,8 +245,72 @@
return NO_ERROR;
}
+status_t Camera3StreamSplitter::removeOutput(size_t surfaceId) {
+ ATRACE_CALL();
+ Mutex::Autolock lock(mMutex);
+
+ status_t res = removeOutputLocked(surfaceId);
+ if (res != OK) {
+ SP_LOGE("%s: removeOutputLocked failed %d", __FUNCTION__, res);
+ return res;
+ }
+
+ res = mConsumer->setMaxAcquiredBufferCount(mMaxConsumerBuffers+1);
+ if (res != OK) {
+ SP_LOGE("%s: setMaxAcquiredBufferCount failed %d", __FUNCTION__, res);
+ return res;
+ }
+
+ return res;
+}
+
+status_t Camera3StreamSplitter::removeOutputLocked(size_t surfaceId) {
+ if (mOutputs[surfaceId] == nullptr) {
+ SP_LOGE("%s: output surface is not present!", __FUNCTION__);
+ return BAD_VALUE;
+ }
+
+ sp<IGraphicBufferProducer> gbp = mOutputs[surfaceId];
+ //Search and decrement the ref. count of any buffers that are
+ //still attached to the removed surface.
+ std::vector<uint64_t> pendingBufferIds;
+ auto& outputSlots = *mOutputSlots[gbp];
+ for (const auto &it : outputSlots) {
+ if (it.get() != nullptr) {
+ pendingBufferIds.push_back(it->getId());
+ }
+ }
+ mOutputs[surfaceId] = nullptr;
+ mOutputSlots[gbp] = nullptr;
+ for (const auto &id : pendingBufferIds) {
+ decrementBufRefCountLocked(id, surfaceId);
+ }
+
+ auto res = IInterface::asBinder(gbp)->unlinkToDeath(mNotifiers[gbp]);
+ if (res != OK) {
+ SP_LOGE("%s: Failed to unlink producer death listener: %d ", __FUNCTION__, res);
+ return res;
+ }
+
+ res = gbp->disconnect(NATIVE_WINDOW_API_CAMERA);
+ if (res != OK) {
+ SP_LOGE("%s: Unable disconnect from producer interface: %d ", __FUNCTION__, res);
+ return res;
+ }
+
+ mNotifiers[gbp] = nullptr;
+ if (mConsumerBufferCount[surfaceId] < mMaxHalBuffers) {
+ mMaxConsumerBuffers -= mConsumerBufferCount[surfaceId];
+ } else {
+ SP_LOGE("%s: Cached consumer buffer count mismatch!", __FUNCTION__);
+ }
+ mConsumerBufferCount[surfaceId] = 0;
+
+ return res;
+}
+
status_t Camera3StreamSplitter::outputBufferLocked(const sp<IGraphicBufferProducer>& output,
- const BufferItem& bufferItem) {
+ const BufferItem& bufferItem, size_t surfaceId) {
ATRACE_CALL();
status_t res;
IGraphicBufferProducer::QueueBufferInput queueInput(
@@ -242,6 +335,11 @@
SP_LOGV("%s: Queuing buffer to buffer queue %p slot %d returns %d",
__FUNCTION__, output.get(), slot, res);
+ //During buffer queue 'mMutex' is not held which makes the removal of
+ //"output" possible. Check whether this is the case and return.
+ if (mOutputSlots[output] == nullptr) {
+ return res;
+ }
if (res != OK) {
if (res != NO_INIT && res != DEAD_OBJECT) {
SP_LOGE("Queuing buffer to output failed (%d)", res);
@@ -250,7 +348,7 @@
// that, increment the release count so that we still release this
// buffer eventually, and move on to the next output
onAbandonedLocked();
- decrementBufRefCountLocked(bufferItem.mGraphicBuffer->getId(), output);
+ decrementBufRefCountLocked(bufferItem.mGraphicBuffer->getId(), surfaceId);
return res;
}
@@ -258,7 +356,7 @@
// queue, no onBufferReleased is called by the buffer queue.
// Proactively trigger the callback to avoid buffer loss.
if (queueOutput.bufferReplaced) {
- onBufferReleasedByOutputLocked(output);
+ onBufferReleasedByOutputLocked(output, surfaceId);
}
return res;
@@ -271,7 +369,6 @@
status_t Camera3StreamSplitter::notifyBufferReleased(const sp<GraphicBuffer>& buffer) {
ATRACE_CALL();
- status_t res = OK;
Mutex::Autolock lock(mMutex);
@@ -279,17 +376,7 @@
std::unique_ptr<BufferTracker> tracker_ptr = std::move(mBuffers[bufferId]);
mBuffers.erase(bufferId);
- for (const auto surface : tracker_ptr->requestedSurfaces()) {
- sp<IGraphicBufferProducer>& gbp = mOutputs[surface];
- OutputSlots& outputSlots = *(mOutputSlots[gbp]);
- int slot = getSlotForOutputLocked(gbp, buffer);
- if (slot != BufferItem::INVALID_BUFFER_SLOT) {
- gbp->detachBuffer(slot);
- outputSlots[slot].clear();
- }
- }
-
- return res;
+ return OK;
}
status_t Camera3StreamSplitter::attachBufferToOutputs(ANativeWindowBuffer* anb,
@@ -307,7 +394,15 @@
for (auto& surface_id : surface_ids) {
sp<IGraphicBufferProducer>& gbp = mOutputs[surface_id];
- int slot = BufferItem::INVALID_BUFFER_SLOT;
+ if (gbp.get() == nullptr) {
+ //Output surface got likely removed by client.
+ continue;
+ }
+ int slot = getSlotForOutputLocked(gbp, gb);
+ if (slot != BufferItem::INVALID_BUFFER_SLOT) {
+ //Buffer is already attached to this output surface.
+ continue;
+ }
//Temporarly Unlock the mutex when trying to attachBuffer to the output
//queue, because attachBuffer could block in case of a slow consumer. If
//we block while holding the lock, onFrameAvailable and onBufferReleased
@@ -320,12 +415,17 @@
__FUNCTION__, gbp.get(), strerror(-res), res);
return res;
}
+ //During buffer attach 'mMutex' is not held which makes the removal of
+ //"gbp" possible. Check whether this is the case and continue.
+ if (mOutputSlots[gbp] == nullptr) {
+ continue;
+ }
auto& outputSlots = *mOutputSlots[gbp];
if (outputSlots[slot] != nullptr) {
// If the buffer is attached to a slot which already contains a buffer,
// the previous buffer will be removed from the output queue. Decrement
// the reference count accordingly.
- decrementBufRefCountLocked(outputSlots[slot]->getId(), gbp);
+ decrementBufRefCountLocked(outputSlots[slot]->getId(), surface_id);
}
SP_LOGV("%s: Attached buffer %p to slot %d on output %p.",__FUNCTION__, gb.get(),
slot, gbp.get());
@@ -349,7 +449,21 @@
mOnFrameAvailableRes.store(res);
return;
}
- if (mBuffers.find(bufferItem.mGraphicBuffer->getId()) == mBuffers.end()) {
+
+ uint64_t bufferId;
+ if (bufferItem.mGraphicBuffer != nullptr) {
+ mInputSlots[bufferItem.mSlot] = bufferItem;
+ } else if (bufferItem.mAcquireCalled) {
+ bufferItem.mGraphicBuffer = mInputSlots[bufferItem.mSlot].mGraphicBuffer;
+ mInputSlots[bufferItem.mSlot].mFrameNumber = bufferItem.mFrameNumber;
+ } else {
+ SP_LOGE("%s: Invalid input graphic buffer!", __FUNCTION__);
+ res = BAD_VALUE;
+ return;
+ }
+ bufferId = bufferItem.mGraphicBuffer->getId();
+
+ if (mBuffers.find(bufferId) == mBuffers.end()) {
SP_LOGE("%s: Acquired buffer doesn't exist in attached buffer map",
__FUNCTION__);
mOnFrameAvailableRes.store(INVALID_OPERATION);
@@ -359,24 +473,19 @@
SP_LOGV("acquired buffer %" PRId64 " from input at slot %d",
bufferItem.mGraphicBuffer->getId(), bufferItem.mSlot);
- res = mConsumer->detachBuffer(bufferItem.mSlot);
- if (res != NO_ERROR) {
- SP_LOGE("%s: detaching buffer from input failed (%d)", __FUNCTION__, res);
- mOnFrameAvailableRes.store(res);
- return;
- }
-
// Attach and queue the buffer to each of the outputs
- BufferTracker& tracker = *(mBuffers[bufferItem.mGraphicBuffer->getId()]);
+ BufferTracker& tracker = *(mBuffers[bufferId]);
SP_LOGV("%s: BufferTracker for buffer %" PRId64 ", number of requests %zu",
__FUNCTION__, bufferItem.mGraphicBuffer->getId(), tracker.requestedSurfaces().size());
for (const auto id : tracker.requestedSurfaces()) {
- LOG_ALWAYS_FATAL_IF(id >= mOutputs.size(),
- "requested surface id exceeding max registered ids");
+ if (mOutputs[id] == nullptr) {
+ //Output surface got likely removed by client.
+ continue;
+ }
- res = outputBufferLocked(mOutputs[id], bufferItem);
+ res = outputBufferLocked(mOutputs[id], bufferItem, id);
if (res != OK) {
SP_LOGE("%s: outputBufferLocked failed %d", __FUNCTION__, res);
mOnFrameAvailableRes.store(res);
@@ -389,12 +498,14 @@
mOnFrameAvailableRes.store(res);
}
-void Camera3StreamSplitter::decrementBufRefCountLocked(uint64_t id,
- const sp<IGraphicBufferProducer>& from) {
+void Camera3StreamSplitter::decrementBufRefCountLocked(uint64_t id, size_t surfaceId) {
ATRACE_CALL();
- size_t referenceCount = mBuffers[id]->decrementReferenceCountLocked();
- removeSlotForOutputLocked(from, mBuffers[id]->getBuffer());
+ if (mBuffers[id] == nullptr) {
+ return;
+ }
+
+ size_t referenceCount = mBuffers[id]->decrementReferenceCountLocked(surfaceId);
if (referenceCount > 0) {
return;
}
@@ -407,11 +518,18 @@
std::unique_ptr<BufferTracker> tracker_ptr = std::move(mBuffers[id]);
mBuffers.erase(id);
- // Attach and release the buffer back to the input
- int consumerSlot = BufferItem::INVALID_BUFFER_SLOT;
- status_t res = mConsumer->attachBuffer(&consumerSlot, tracker_ptr->getBuffer());
- if (res != NO_ERROR) {
- SP_LOGE("%s: attaching buffer to input failed (%d)", __FUNCTION__, res);
+ uint64_t bufferId = tracker_ptr->getBuffer()->getId();
+ int consumerSlot = -1;
+ uint64_t frameNumber;
+ for (const auto &it : mInputSlots) {
+ if (it.second.mGraphicBuffer->getId() == bufferId) {
+ consumerSlot = it.second.mSlot;
+ frameNumber = it.second.mFrameNumber;
+ break;
+ }
+ }
+ if (consumerSlot == -1) {
+ SP_LOGE("%s: Buffer missing inside input slots!", __FUNCTION__);
return;
}
@@ -424,8 +542,9 @@
// splitter lock.
sp<IGraphicBufferConsumer> consumer(mConsumer);
mMutex.unlock();
+ int res = NO_ERROR;
if (consumer != nullptr) {
- res = consumer->releaseBuffer(consumerSlot, /* frameNumber */ 0,
+ res = consumer->releaseBuffer(consumerSlot, frameNumber,
EGL_NO_DISPLAY, EGL_NO_SYNC_KHR, tracker_ptr->getMergedFence());
} else {
SP_LOGE("%s: consumer has become null!", __FUNCTION__);
@@ -442,27 +561,61 @@
ATRACE_CALL();
Mutex::Autolock lock(mMutex);
- onBufferReleasedByOutputLocked(from);
+ size_t surfaceId = 0;
+ bool found = false;
+ for (const auto& it : mOutputs) {
+ if (it.second == from) {
+ found = true;
+ surfaceId = it.first;
+ break;
+ }
+ }
+ if (!found) {
+ SP_LOGV("%s: output surface not registered anymore!", __FUNCTION__);
+ return;
+ }
+
+ onBufferReleasedByOutputLocked(from, surfaceId);
}
void Camera3StreamSplitter::onBufferReleasedByOutputLocked(
- const sp<IGraphicBufferProducer>& from) {
+ const sp<IGraphicBufferProducer>& from, size_t surfaceId) {
ATRACE_CALL();
sp<GraphicBuffer> buffer;
sp<Fence> fence;
- status_t res = from->detachNextBuffer(&buffer, &fence);
+ if (mOutputSlots[from] == nullptr) {
+ //Output surface got likely removed by client.
+ return;
+ }
+ auto outputSlots = *mOutputSlots[from];
+
+ int slot = BufferItem::INVALID_BUFFER_SLOT;
+ auto res = from->dequeueBuffer(&slot, &fence, mWidth, mHeight, mFormat, mProducerUsage,
+ nullptr, nullptr);
if (res == NO_INIT) {
// If we just discovered that this output has been abandoned, note that,
// but we can't do anything else, since buffer is invalid
onAbandonedLocked();
return;
- } else if (res == NO_MEMORY) {
- SP_LOGV("%s: No free buffers", __FUNCTION__);
+ } else if (res == IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION) {
+ SP_LOGE("%s: Producer needs to re-allocate buffer!", __FUNCTION__);
+ SP_LOGE("%s: This should not happen with buffer allocation disabled!", __FUNCTION__);
return;
- } else if (res != OK) {
- SP_LOGE("%s: detaching buffer from output failed (%d)", __FUNCTION__, res);
+ } else if (res == IGraphicBufferProducer::RELEASE_ALL_BUFFERS) {
+ SP_LOGE("%s: All slot->buffer mapping should be released!", __FUNCTION__);
+ SP_LOGE("%s: This should not happen with buffer allocation disabled!", __FUNCTION__);
+ return;
+ } else if (res == NO_MEMORY) {
+ SP_LOGE("%s: No free buffers", __FUNCTION__);
+ return;
+ } else if (res == WOULD_BLOCK) {
+ SP_LOGE("%s: Dequeue call will block", __FUNCTION__);
+ return;
+ } else if (res != OK || (slot == BufferItem::INVALID_BUFFER_SLOT)) {
+ SP_LOGE("%s: dequeue buffer from output failed (%d)", __FUNCTION__, res);
return;
}
+ buffer = outputSlots[slot];
BufferTracker& tracker = *(mBuffers[buffer->getId()]);
// Merge the release fence of the incoming buffer so that the fence we send
@@ -470,11 +623,11 @@
if (fence != nullptr && fence->isValid()) {
tracker.mergeFence(fence);
}
- SP_LOGV("detached buffer %" PRId64 " %p from output %p",
+ SP_LOGV("%s: dequeued buffer %" PRId64 " %p from output %p", __FUNCTION__,
buffer->getId(), buffer.get(), from.get());
// Check to see if this is the last outstanding reference to this buffer
- decrementBufRefCountLocked(buffer->getId(), from);
+ decrementBufRefCountLocked(buffer->getId(), surfaceId);
}
void Camera3StreamSplitter::onAbandonedLocked() {
@@ -501,27 +654,11 @@
}
}
- SP_LOGE("%s: Cannot find slot for gb %p on output %p", __FUNCTION__, gb.get(),
+ SP_LOGV("%s: Cannot find slot for gb %p on output %p", __FUNCTION__, gb.get(),
gbp.get());
return BufferItem::INVALID_BUFFER_SLOT;
}
-status_t Camera3StreamSplitter::removeSlotForOutputLocked(const sp<IGraphicBufferProducer>& gbp,
- const sp<GraphicBuffer>& gb) {
- auto& outputSlots = *mOutputSlots[gbp];
-
- for (size_t i = 0; i < outputSlots.size(); i++) {
- if (outputSlots[i] == gb) {
- outputSlots[i].clear();
- return NO_ERROR;
- }
- }
-
- SP_LOGE("%s: Cannot find slot for gb %p on output %p", __FUNCTION__, gb.get(),
- gbp.get());
- return BAD_VALUE;
-}
-
Camera3StreamSplitter::OutputListener::OutputListener(
wp<Camera3StreamSplitter> splitter,
wp<IGraphicBufferProducer> output)
@@ -553,7 +690,14 @@
mMergedFence = Fence::merge(String8("Camera3StreamSplitter"), mMergedFence, with);
}
-size_t Camera3StreamSplitter::BufferTracker::decrementReferenceCountLocked() {
+size_t Camera3StreamSplitter::BufferTracker::decrementReferenceCountLocked(size_t surfaceId) {
+ const auto& it = std::find(mRequestedSurfaces.begin(), mRequestedSurfaces.end(), surfaceId);
+ if (it == mRequestedSurfaces.end()) {
+ return mReferenceCount;
+ } else {
+ mRequestedSurfaces.erase(it);
+ }
+
if (mReferenceCount > 0)
--mReferenceCount;
return mReferenceCount;
diff --git a/services/camera/libcameraservice/device3/Camera3StreamSplitter.h b/services/camera/libcameraservice/device3/Camera3StreamSplitter.h
index 3b8839e..704f88f 100644
--- a/services/camera/libcameraservice/device3/Camera3StreamSplitter.h
+++ b/services/camera/libcameraservice/device3/Camera3StreamSplitter.h
@@ -51,22 +51,25 @@
// Connect to the stream splitter by creating buffer queue and connecting it
// with output surfaces.
- status_t connect(const std::vector<sp<Surface> >& surfaces,
- uint64_t consumerUsage, size_t halMaxBuffers,
- sp<Surface>* consumer);
+ status_t connect(const std::unordered_map<size_t, sp<Surface>> &surfaces,
+ uint64_t consumerUsage, uint64_t producerUsage, size_t halMaxBuffers, uint32_t width,
+ uint32_t height, android::PixelFormat format, sp<Surface>* consumer);
// addOutput adds an output BufferQueue to the splitter. The splitter
// connects to outputQueue as a CPU producer, and any buffers queued
- // to the input will be queued to each output. It is assumed that all of the
- // outputs are added before any buffers are queued on the input. If any
- // output is abandoned by its consumer, the splitter will abandon its input
- // queue (see onAbandoned).
+ // to the input will be queued to each output. If any output is abandoned
+ // by its consumer, the splitter will abandon its input queue (see onAbandoned).
//
// A return value other than NO_ERROR means that an error has occurred and
// outputQueue has not been added to the splitter. BAD_VALUE is returned if
// outputQueue is NULL. See IGraphicBufferProducer::connect for explanations
// of other error codes.
- status_t addOutput(const sp<Surface>& outputQueue);
+ status_t addOutput(size_t surfaceId, const sp<Surface>& outputQueue);
+
+ //removeOutput will remove a BufferQueue that was previously added to
+ //the splitter outputs. Any pending buffers in the BufferQueue will get
+ //reclaimed.
+ status_t removeOutput(size_t surfaceId);
// Notification that the graphic buffer has been released to the input
// BufferQueue. The buffer should be reused by the camera device instead of
@@ -120,7 +123,7 @@
// This is the implementation of onBufferReleasedByOutput without the mutex locked.
// It could either be called from onBufferReleasedByOutput or from
// onFrameAvailable when a buffer in the async buffer queue is overwritten.
- void onBufferReleasedByOutputLocked(const sp<IGraphicBufferProducer>& from);
+ void onBufferReleasedByOutputLocked(const sp<IGraphicBufferProducer>& from, size_t surfaceId);
// When this is called, the splitter disconnects from (i.e., abandons) its
// input queue and signals any waiting onFrameAvailable calls to wake up.
@@ -131,7 +134,7 @@
// Decrement the buffer's reference count. Once the reference count becomes
// 0, return the buffer back to the input BufferQueue.
- void decrementBufRefCountLocked(uint64_t id, const sp<IGraphicBufferProducer>& from);
+ void decrementBufRefCountLocked(uint64_t id, size_t surfaceId);
// This is a thin wrapper class that lets us determine which BufferQueue
// the IProducerListener::onBufferReleased callback is associated with. We
@@ -168,7 +171,7 @@
// Returns the new value
// Only called while mMutex is held
- size_t decrementReferenceCountLocked();
+ size_t decrementReferenceCountLocked(size_t surfaceId);
const std::vector<size_t> requestedSurfaces() const { return mRequestedSurfaces; }
@@ -191,13 +194,15 @@
// Must be accessed through RefBase
virtual ~Camera3StreamSplitter();
- status_t addOutputLocked(const sp<Surface>& outputQueue);
+ status_t addOutputLocked(size_t surfaceId, const sp<Surface>& outputQueue);
+
+ status_t removeOutputLocked(size_t surfaceId);
// Send a buffer to particular output, and increment the reference count
// of the buffer. If this output is abandoned, the buffer's reference count
// won't be incremented.
status_t outputBufferLocked(const sp<IGraphicBufferProducer>& output,
- const BufferItem& bufferItem);
+ const BufferItem& bufferItem, size_t surfaceId);
// Get unique name for the buffer queue consumer
String8 getUniqueConsumerName();
@@ -205,14 +210,14 @@
// Helper function to get the BufferQueue slot where a particular buffer is attached to.
int getSlotForOutputLocked(const sp<IGraphicBufferProducer>& gbp,
const sp<GraphicBuffer>& gb);
- // Helper function to remove the buffer from the BufferQueue slot
- status_t removeSlotForOutputLocked(const sp<IGraphicBufferProducer>& gbp,
- const sp<GraphicBuffer>& gb);
-
// Sum of max consumer buffers for all outputs
size_t mMaxConsumerBuffers = 0;
size_t mMaxHalBuffers = 0;
+ uint32_t mWidth = 0;
+ uint32_t mHeight = 0;
+ android::PixelFormat mFormat = android::PIXEL_FORMAT_NONE;
+ uint64_t mProducerUsage = 0;
static const nsecs_t kDequeueBufferTimeout = s2ns(1); // 1 sec
@@ -223,7 +228,15 @@
sp<BufferItemConsumer> mBufferItemConsumer;
sp<Surface> mSurface;
- std::vector<sp<IGraphicBufferProducer> > mOutputs;
+ //Map graphic buffer ids -> buffer items
+ std::unordered_map<uint64_t, BufferItem> mInputSlots;
+
+ //Map surface ids -> gbp outputs
+ std::unordered_map<int, sp<IGraphicBufferProducer> > mOutputs;
+
+ //Map surface ids -> consumer buffer count
+ std::unordered_map<int, size_t > mConsumerBufferCount;
+
// Map of GraphicBuffer IDs (GraphicBuffer::getId()) to buffer tracking
// objects (which are mostly for counting how many outputs have released the
// buffer, but also contain merged release fences).