Camera: Rework StreamSplitter for camera use cases
- Merge notifyRequestedSurfaces into getBufferLocked, so that during
getBufferLocked, the stream splitter gets to know which outputs the
current request is on.
- Reserve buffer slot in the output queue during getBufferLocked instead
of during onFrameAvailable. So if there is no slot/buffer available, no
new request is sent to HAL. This aligns with current cameraservice logic.
Do not hold the lock while calling attachBuffer to output queue because
it could block for a slow consumer.
- Instead of setting the consumer buffer count of input buffer queue to
maximum of all shared outputs, set it to sum of them. By doing this,
In the case of a slow consumer, other consumers sharing the same stream
won't be impacted.
- Handle the case where onBufferReleased not being fired for buffer
replaced by attachBuffer/queueBuffer.
- Add function to check the return value of onFrameAvailable so that
when output is abandoned, the error code is propagated back to
queueBuffer.
Test: Camera CTS, and CTS with StreamSplitter enabled for all
implementation defined use cases.
Bug: 33777818
Change-Id: I863f501d5283bbe70c71e66b4d37d690484b90fa
diff --git a/services/camera/libcameraservice/api2/CameraDeviceClient.cpp b/services/camera/libcameraservice/api2/CameraDeviceClient.cpp
index e710f0a..79e7ff0 100644
--- a/services/camera/libcameraservice/api2/CameraDeviceClient.cpp
+++ b/services/camera/libcameraservice/api2/CameraDeviceClient.cpp
@@ -507,6 +507,14 @@
return res;
if (!isStreamInfoValid) {
+ // Streaming sharing is only supported for IMPLEMENTATION_DEFINED
+ // formats.
+ if (isShared && streamInfo.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());
+ }
isStreamInfoValid = true;
}
diff --git a/services/camera/libcameraservice/device3/Camera3Device.cpp b/services/camera/libcameraservice/device3/Camera3Device.cpp
index 068a2b3..f20556d 100644
--- a/services/camera/libcameraservice/device3/Camera3Device.cpp
+++ b/services/camera/libcameraservice/device3/Camera3Device.cpp
@@ -3956,7 +3956,8 @@
}
}
- res = outputStream->getBuffer(&outputBuffers->editItemAt(i));
+ res = outputStream->getBuffer(&outputBuffers->editItemAt(i),
+ captureRequest->mOutputSurfaces[i]);
if (res != OK) {
// Can't get output buffer from gralloc queue - this could be due to
// abandoned queue or other consumer misbehavior, so not a fatal
@@ -3968,13 +3969,6 @@
}
halRequest->num_output_buffers++;
- res = outputStream->notifyRequestedSurfaces(halRequest->frame_number,
- captureRequest->mOutputSurfaces[i]);
- if (res != OK) {
- ALOGE("RequestThread: Cannot register output surfaces: %s (%d)",
- strerror(-res), res);
- return INVALID_OPERATION;
- }
}
totalNumBuffers += halRequest->num_output_buffers;
diff --git a/services/camera/libcameraservice/device3/Camera3DummyStream.cpp b/services/camera/libcameraservice/device3/Camera3DummyStream.cpp
index 1a730d6..9c951b7 100644
--- a/services/camera/libcameraservice/device3/Camera3DummyStream.cpp
+++ b/services/camera/libcameraservice/device3/Camera3DummyStream.cpp
@@ -36,7 +36,8 @@
}
-status_t Camera3DummyStream::getBufferLocked(camera3_stream_buffer *) {
+status_t Camera3DummyStream::getBufferLocked(camera3_stream_buffer *,
+ const std::vector<size_t>&) {
ATRACE_CALL();
ALOGE("%s: Stream %d: Dummy stream cannot produce buffers!", __FUNCTION__, mId);
return INVALID_OPERATION;
@@ -83,14 +84,6 @@
return OK;
}
-status_t Camera3DummyStream::notifyRequestedSurfaces(uint32_t frame_number,
- const std::vector<size_t>& surface_ids) {
- (void) frame_number;
- (void) surface_ids;
- // Do nothing
- return OK;
-}
-
status_t Camera3DummyStream::configureQueueLocked() {
// Do nothing
return OK;
diff --git a/services/camera/libcameraservice/device3/Camera3DummyStream.h b/services/camera/libcameraservice/device3/Camera3DummyStream.h
index b6ec99c..35a6a18 100644
--- a/services/camera/libcameraservice/device3/Camera3DummyStream.h
+++ b/services/camera/libcameraservice/device3/Camera3DummyStream.h
@@ -56,9 +56,6 @@
virtual status_t detachBuffer(sp<GraphicBuffer>* buffer, int* fenceFd);
- virtual status_t notifyRequestedSurfaces(uint32_t frame_number,
- const std::vector<size_t>& surface_ids);
-
/**
* Return if this output stream is for video encoding.
*/
@@ -102,7 +99,8 @@
/**
* Internal Camera3Stream interface
*/
- virtual status_t getBufferLocked(camera3_stream_buffer *buffer);
+ virtual status_t getBufferLocked(camera3_stream_buffer *buffer,
+ const std::vector<size_t>& surface_ids = std::vector<size_t>());
virtual status_t returnBufferLocked(
const camera3_stream_buffer &buffer,
nsecs_t timestamp);
diff --git a/services/camera/libcameraservice/device3/Camera3OutputStream.cpp b/services/camera/libcameraservice/device3/Camera3OutputStream.cpp
index f971116..51dc20a 100644
--- a/services/camera/libcameraservice/device3/Camera3OutputStream.cpp
+++ b/services/camera/libcameraservice/device3/Camera3OutputStream.cpp
@@ -148,73 +148,17 @@
disconnectLocked();
}
-status_t Camera3OutputStream::getBufferLocked(camera3_stream_buffer *buffer) {
+status_t Camera3OutputStream::getBufferLocked(camera3_stream_buffer *buffer,
+ const std::vector<size_t>&) {
ATRACE_CALL();
- status_t res;
-
- if ((res = getBufferPreconditionCheckLocked()) != OK) {
- return res;
- }
ANativeWindowBuffer* anb;
int fenceFd = -1;
- bool gotBufferFromManager = false;
- if (mUseBufferManager) {
- sp<GraphicBuffer> gb;
- res = mBufferManager->getBufferForStream(getId(), getStreamSetId(), &gb, &fenceFd);
- if (res == OK) {
- // Attach this buffer to the bufferQueue: the buffer will be in dequeue state after a
- // successful return.
- anb = gb.get();
- res = mConsumer->attachBuffer(anb);
- if (res != OK) {
- ALOGE("%s: Stream %d: Can't attach the output buffer to this surface: %s (%d)",
- __FUNCTION__, mId, strerror(-res), res);
- return res;
- }
- gotBufferFromManager = true;
- ALOGV("Stream %d: Attached new buffer", getId());
- } else if (res == ALREADY_EXISTS) {
- // Have sufficient free buffers already attached, can just
- // dequeue from buffer queue
- ALOGV("Stream %d: Reusing attached buffer", getId());
- gotBufferFromManager = false;
- } else if (res != OK) {
- ALOGE("%s: Stream %d: Can't get next output buffer from buffer manager: %s (%d)",
- __FUNCTION__, mId, strerror(-res), res);
- return res;
- }
- }
- if (!gotBufferFromManager) {
- /**
- * Release the lock briefly to avoid deadlock for below scenario:
- * Thread 1: StreamingProcessor::startStream -> Camera3Stream::isConfiguring().
- * This thread acquired StreamingProcessor lock and try to lock Camera3Stream lock.
- * Thread 2: Camera3Stream::returnBuffer->StreamingProcessor::onFrameAvailable().
- * This thread acquired Camera3Stream lock and bufferQueue lock, and try to lock
- * StreamingProcessor lock.
- * Thread 3: Camera3Stream::getBuffer(). This thread acquired Camera3Stream lock
- * and try to lock bufferQueue lock.
- * Then there is circular locking dependency.
- */
- sp<ANativeWindow> currentConsumer = mConsumer;
- mLock.unlock();
-
- res = currentConsumer->dequeueBuffer(currentConsumer.get(), &anb, &fenceFd);
- mLock.lock();
- if (res != OK) {
- ALOGE("%s: Stream %d: Can't dequeue next output buffer: %s (%d)",
- __FUNCTION__, mId, strerror(-res), res);
-
- // Only transition to STATE_ABANDONED from STATE_CONFIGURED. (If it is STATE_PREPARING,
- // let prepareNextBuffer handle the error.)
- if (res == NO_INIT && mState == STATE_CONFIGURED) {
- mState = STATE_ABANDONED;
- }
-
- return res;
- }
+ status_t res;
+ res = getBufferLockedCommon(&anb, &fenceFd);
+ if (res != OK) {
+ return res;
}
/**
@@ -227,6 +171,11 @@
return OK;
}
+status_t Camera3OutputStream::queueBufferToConsumer(sp<ANativeWindow>& consumer,
+ ANativeWindowBuffer* buffer, int anwReleaseFence) {
+ return consumer->queueBuffer(consumer.get(), buffer, anwReleaseFence);
+}
+
status_t Camera3OutputStream::returnBufferLocked(
const camera3_stream_buffer &buffer,
nsecs_t timestamp) {
@@ -269,6 +218,7 @@
sp<ANativeWindow> currentConsumer = mConsumer;
mLock.unlock();
+ ANativeWindowBuffer *anwBuffer = container_of(buffer.buffer, ANativeWindowBuffer, handle);
/**
* Return buffer back to ANativeWindow
*/
@@ -276,13 +226,14 @@
// Cancel buffer
ALOGW("A frame is dropped for stream %d", mId);
res = currentConsumer->cancelBuffer(currentConsumer.get(),
- container_of(buffer.buffer, ANativeWindowBuffer, handle),
+ anwBuffer,
anwReleaseFence);
if (res != OK) {
ALOGE("%s: Stream %d: Error cancelling buffer to native window:"
" %s (%d)", __FUNCTION__, mId, strerror(-res), res);
}
+ notifyBufferReleased(anwBuffer);
if (mUseBufferManager) {
// Return this buffer back to buffer manager.
mBufferReleasedListener->onBufferReleased();
@@ -308,9 +259,7 @@
return res;
}
- res = currentConsumer->queueBuffer(currentConsumer.get(),
- container_of(buffer.buffer, ANativeWindowBuffer, handle),
- anwReleaseFence);
+ res = queueBufferToConsumer(currentConsumer, anwBuffer, anwReleaseFence);
if (res != OK) {
ALOGE("%s: Stream %d: Error queueing buffer to native window: "
"%s (%d)", __FUNCTION__, mId, strerror(-res), res);
@@ -527,6 +476,76 @@
return OK;
}
+status_t Camera3OutputStream::getBufferLockedCommon(ANativeWindowBuffer** anb, int* fenceFd) {
+ ATRACE_CALL();
+ status_t res;
+
+ if ((res = getBufferPreconditionCheckLocked()) != OK) {
+ return res;
+ }
+
+ bool gotBufferFromManager = false;
+
+ if (mUseBufferManager) {
+ sp<GraphicBuffer> gb;
+ res = mBufferManager->getBufferForStream(getId(), getStreamSetId(), &gb, fenceFd);
+ if (res == OK) {
+ // Attach this buffer to the bufferQueue: the buffer will be in dequeue state after a
+ // successful return.
+ *anb = gb.get();
+ res = mConsumer->attachBuffer(*anb);
+ if (res != OK) {
+ ALOGE("%s: Stream %d: Can't attach the output buffer to this surface: %s (%d)",
+ __FUNCTION__, mId, strerror(-res), res);
+ return res;
+ }
+ gotBufferFromManager = true;
+ ALOGV("Stream %d: Attached new buffer", getId());
+ } else if (res == ALREADY_EXISTS) {
+ // Have sufficient free buffers already attached, can just
+ // dequeue from buffer queue
+ ALOGV("Stream %d: Reusing attached buffer", getId());
+ gotBufferFromManager = false;
+ } else if (res != OK) {
+ ALOGE("%s: Stream %d: Can't get next output buffer from buffer manager: %s (%d)",
+ __FUNCTION__, mId, strerror(-res), res);
+ return res;
+ }
+ }
+ if (!gotBufferFromManager) {
+ /**
+ * Release the lock briefly to avoid deadlock for below scenario:
+ * Thread 1: StreamingProcessor::startStream -> Camera3Stream::isConfiguring().
+ * This thread acquired StreamingProcessor lock and try to lock Camera3Stream lock.
+ * Thread 2: Camera3Stream::returnBuffer->StreamingProcessor::onFrameAvailable().
+ * This thread acquired Camera3Stream lock and bufferQueue lock, and try to lock
+ * StreamingProcessor lock.
+ * Thread 3: Camera3Stream::getBuffer(). This thread acquired Camera3Stream lock
+ * and try to lock bufferQueue lock.
+ * Then there is circular locking dependency.
+ */
+ sp<ANativeWindow> currentConsumer = mConsumer;
+ mLock.unlock();
+
+ res = currentConsumer->dequeueBuffer(currentConsumer.get(), anb, fenceFd);
+ mLock.lock();
+ if (res != OK) {
+ ALOGE("%s: Stream %d: Can't dequeue next output buffer: %s (%d)",
+ __FUNCTION__, mId, strerror(-res), res);
+
+ // Only transition to STATE_ABANDONED from STATE_CONFIGURED. (If it is STATE_PREPARING,
+ // let prepareNextBuffer handle the error.)
+ if (res == NO_INIT && mState == STATE_CONFIGURED) {
+ mState = STATE_ABANDONED;
+ }
+
+ return res;
+ }
+ }
+
+ return res;
+}
+
status_t Camera3OutputStream::disconnectLocked() {
status_t res;
@@ -702,8 +721,7 @@
return OK;
}
-status_t Camera3OutputStream::notifyRequestedSurfaces(uint32_t /*frame_number*/,
- const std::vector<size_t>& /*surface_ids*/) {
+status_t Camera3OutputStream::notifyBufferReleased(ANativeWindowBuffer* /*anwBuffer*/) {
return OK;
}
@@ -717,6 +735,7 @@
}
status_t Camera3OutputStream::setConsumers(const std::vector<sp<Surface>>& consumers) {
+ Mutex::Autolock l(mLock);
if (consumers.size() != 1) {
ALOGE("%s: it's illegal to set %zu consumer surfaces!",
__FUNCTION__, consumers.size());
diff --git a/services/camera/libcameraservice/device3/Camera3OutputStream.h b/services/camera/libcameraservice/device3/Camera3OutputStream.h
index 080c721..24e4e05 100644
--- a/services/camera/libcameraservice/device3/Camera3OutputStream.h
+++ b/services/camera/libcameraservice/device3/Camera3OutputStream.h
@@ -158,8 +158,11 @@
virtual status_t detachBuffer(sp<GraphicBuffer>* buffer, int* fenceFd);
- virtual status_t notifyRequestedSurfaces(uint32_t frame_number,
- const std::vector<size_t>& surface_ids);
+ /**
+ * Notify that the buffer is being released to the buffer queue instead of
+ * being queued to the consumer.
+ */
+ virtual status_t notifyBufferReleased(ANativeWindowBuffer *anwBuffer);
/**
* Set the graphic buffer manager to get/return the stream buffers.
@@ -198,6 +201,9 @@
static const nsecs_t kDequeueBufferTimeout = 1000000000; // 1 sec
+ status_t getBufferLockedCommon(ANativeWindowBuffer** anb, int* fenceFd);
+
+
private:
int mTransform;
@@ -243,11 +249,16 @@
/**
* Internal Camera3Stream interface
*/
- virtual status_t getBufferLocked(camera3_stream_buffer *buffer);
+ virtual status_t getBufferLocked(camera3_stream_buffer *buffer,
+ const std::vector<size_t>& surface_ids);
+
virtual status_t returnBufferLocked(
const camera3_stream_buffer &buffer,
nsecs_t timestamp);
+ virtual status_t queueBufferToConsumer(sp<ANativeWindow>& consumer,
+ ANativeWindowBuffer* buffer, int anwReleaseFence);
+
virtual status_t configureQueueLocked();
virtual status_t getEndpointUsage(uint32_t *usage) const;
diff --git a/services/camera/libcameraservice/device3/Camera3OutputStreamInterface.h b/services/camera/libcameraservice/device3/Camera3OutputStreamInterface.h
index 11868e7..8107dd0 100644
--- a/services/camera/libcameraservice/device3/Camera3OutputStreamInterface.h
+++ b/services/camera/libcameraservice/device3/Camera3OutputStreamInterface.h
@@ -59,20 +59,6 @@
*
*/
virtual status_t detachBuffer(sp<GraphicBuffer>* buffer, int* fenceFd) = 0;
-
- /**
- * Notify which surfaces are requested for a particular frame number.
- *
- * Mulitple surfaces could share the same output stream, but a request may
- * be only for a subset of surfaces. In this case, the
- * Camera3OutputStreamInterface object needs to manage the output surfaces on
- * a per request basis.
- *
- * If there is only one surface for this output stream, calling this
- * function is a no-op.
- */
- virtual status_t notifyRequestedSurfaces(uint32_t frame_number,
- const std::vector<size_t>& surface_ids) = 0;
};
} // namespace camera3
diff --git a/services/camera/libcameraservice/device3/Camera3SharedOutputStream.cpp b/services/camera/libcameraservice/device3/Camera3SharedOutputStream.cpp
index 0d6a96c..2ae5660 100644
--- a/services/camera/libcameraservice/device3/Camera3SharedOutputStream.cpp
+++ b/services/camera/libcameraservice/device3/Camera3SharedOutputStream.cpp
@@ -44,7 +44,7 @@
uint32_t usage;
getEndpointUsage(&usage);
- res = mStreamSplitter->connect(mSurfaces, usage, camera3_stream::max_buffers, mConsumer);
+ res = mStreamSplitter->connect(mSurfaces, usage, camera3_stream::max_buffers, &mConsumer);
if (res != OK) {
ALOGE("%s: Failed to connect to stream splitter: %s(%d)",
__FUNCTION__, strerror(-res), res);
@@ -54,13 +54,13 @@
return res;
}
-status_t Camera3SharedOutputStream::notifyRequestedSurfaces(uint32_t /*frame_number*/,
- const std::vector<size_t>& surface_ids) {
+status_t Camera3SharedOutputStream::notifyBufferReleased(ANativeWindowBuffer *anwBuffer) {
Mutex::Autolock l(mLock);
status_t res = OK;
+ const sp<GraphicBuffer> buffer(static_cast<GraphicBuffer*>(anwBuffer));
if (mStreamSplitter != nullptr) {
- res = mStreamSplitter->notifyRequestedSurfaces(surface_ids);
+ res = mStreamSplitter->notifyBufferReleased(buffer);
}
return res;
@@ -72,6 +72,7 @@
}
status_t Camera3SharedOutputStream::setConsumers(const std::vector<sp<Surface>>& surfaces) {
+ Mutex::Autolock l(mLock);
if (surfaces.size() == 0) {
ALOGE("%s: it's illegal to set zero consumer surfaces!", __FUNCTION__);
return INVALID_OPERATION;
@@ -88,7 +89,7 @@
// Only call addOutput if the splitter has been connected.
if (mStreamSplitter != nullptr) {
- ret = mStreamSplitter->addOutput(surface, camera3_stream::max_buffers);
+ ret = mStreamSplitter->addOutput(surface);
if (ret != OK) {
ALOGE("%s: addOutput failed with error code %d", __FUNCTION__, ret);
return ret;
@@ -99,6 +100,64 @@
return ret;
}
+status_t Camera3SharedOutputStream::getBufferLocked(camera3_stream_buffer *buffer,
+ const std::vector<size_t>& surface_ids) {
+ ANativeWindowBuffer* anb;
+ int fenceFd = -1;
+
+ status_t res;
+ res = getBufferLockedCommon(&anb, &fenceFd);
+ if (res != OK) {
+ return res;
+ }
+
+ // Attach the buffer to the splitter output queues. This could block if
+ // the output queue doesn't have any empty slot. So unlock during the course
+ // of attachBufferToOutputs.
+ sp<Camera3StreamSplitter> splitter = mStreamSplitter;
+ mLock.unlock();
+ res = splitter->attachBufferToOutputs(anb, surface_ids);
+ mLock.lock();
+ if (res != OK) {
+ ALOGE("%s: Stream %d: Cannot attach stream splitter buffer to outputs: %s (%d)",
+ __FUNCTION__, mId, strerror(-res), res);
+ // Only transition to STATE_ABANDONED from STATE_CONFIGURED. (If it is STATE_PREPARING,
+ // let prepareNextBuffer handle the error.)
+ if (res == NO_INIT && mState == STATE_CONFIGURED) {
+ mState = STATE_ABANDONED;
+ }
+
+ return res;
+ }
+
+ /**
+ * FenceFD now owned by HAL except in case of error,
+ * in which case we reassign it to acquire_fence
+ */
+ handoutBufferLocked(*buffer, &(anb->handle), /*acquireFence*/fenceFd,
+ /*releaseFence*/-1, CAMERA3_BUFFER_STATUS_OK, /*output*/true);
+
+ return OK;
+}
+
+status_t Camera3SharedOutputStream::queueBufferToConsumer(sp<ANativeWindow>& consumer,
+ ANativeWindowBuffer* buffer, int anwReleaseFence) {
+ status_t res = consumer->queueBuffer(consumer.get(), buffer, anwReleaseFence);
+
+ // After queuing buffer to the internal consumer queue, check whether the buffer is
+ // successfully queued to the output queues.
+ if (res == OK) {
+ res = mStreamSplitter->getOnFrameAvailableResult();
+ if (res != OK) {
+ ALOGE("%s: getOnFrameAvailable returns %d", __FUNCTION__, res);
+ }
+ } else {
+ ALOGE("%s: queueBufer failed %d", __FUNCTION__, res);
+ }
+
+ return res;
+}
+
status_t Camera3SharedOutputStream::configureQueueLocked() {
status_t res;
diff --git a/services/camera/libcameraservice/device3/Camera3SharedOutputStream.h b/services/camera/libcameraservice/device3/Camera3SharedOutputStream.h
index cc96076..7be0940 100644
--- a/services/camera/libcameraservice/device3/Camera3SharedOutputStream.h
+++ b/services/camera/libcameraservice/device3/Camera3SharedOutputStream.h
@@ -40,8 +40,7 @@
virtual ~Camera3SharedOutputStream();
- virtual status_t notifyRequestedSurfaces(uint32_t frame_number,
- const std::vector<size_t>& surface_ids);
+ virtual status_t notifyBufferReleased(ANativeWindowBuffer *buffer);
virtual bool isConsumerConfigurationDeferred(size_t surface_id) const;
@@ -62,6 +61,15 @@
*/
status_t connectStreamSplitterLocked();
+ /**
+ * Internal Camera3Stream interface
+ */
+ virtual status_t getBufferLocked(camera3_stream_buffer *buffer,
+ const std::vector<size_t>& surface_ids);
+
+ virtual status_t queueBufferToConsumer(sp<ANativeWindow>& consumer,
+ ANativeWindowBuffer* buffer, int anwReleaseFence);
+
virtual status_t configureQueueLocked();
virtual status_t disconnectLocked();
diff --git a/services/camera/libcameraservice/device3/Camera3Stream.cpp b/services/camera/libcameraservice/device3/Camera3Stream.cpp
index c3b7565..53a3168 100644
--- a/services/camera/libcameraservice/device3/Camera3Stream.cpp
+++ b/services/camera/libcameraservice/device3/Camera3Stream.cpp
@@ -443,7 +443,8 @@
return OK;
}
-status_t Camera3Stream::getBuffer(camera3_stream_buffer *buffer) {
+status_t Camera3Stream::getBuffer(camera3_stream_buffer *buffer,
+ const std::vector<size_t>& surface_ids) {
ATRACE_CALL();
Mutex::Autolock l(mLock);
status_t res = OK;
@@ -470,7 +471,7 @@
}
}
- res = getBufferLocked(buffer);
+ res = getBufferLocked(buffer, surface_ids);
if (res == OK) {
fireBufferListenersLocked(*buffer, /*acquired*/true, /*output*/true);
if (buffer->buffer) {
@@ -745,7 +746,8 @@
return res;
}
-status_t Camera3Stream::getBufferLocked(camera3_stream_buffer *) {
+status_t Camera3Stream::getBufferLocked(camera3_stream_buffer *,
+ const std::vector<size_t>&) {
ALOGE("%s: This type of stream does not support output", __FUNCTION__);
return INVALID_OPERATION;
}
diff --git a/services/camera/libcameraservice/device3/Camera3Stream.h b/services/camera/libcameraservice/device3/Camera3Stream.h
index 471b393..56cb827 100644
--- a/services/camera/libcameraservice/device3/Camera3Stream.h
+++ b/services/camera/libcameraservice/device3/Camera3Stream.h
@@ -277,12 +277,18 @@
* Fill in the camera3_stream_buffer with the next valid buffer for this
* stream, to hand over to the HAL.
*
+ * Multiple surfaces could share the same HAL stream, but a request may
+ * be only for a subset of surfaces. In this case, the
+ * Camera3StreamInterface object needs the surface ID information to acquire
+ * buffers for those surfaces.
+ *
* This method may only be called once finishConfiguration has been called.
* For bidirectional streams, this method applies to the output-side
* buffers.
*
*/
- status_t getBuffer(camera3_stream_buffer *buffer);
+ status_t getBuffer(camera3_stream_buffer *buffer,
+ const std::vector<size_t>& surface_ids = std::vector<size_t>());
/**
* Return a buffer to the stream after use by the HAL.
@@ -412,7 +418,8 @@
// cast to camera3_stream*, implementations must increment the
// refcount of the stream manually in getBufferLocked, and decrement it in
// returnBufferLocked.
- virtual status_t getBufferLocked(camera3_stream_buffer *buffer);
+ virtual status_t getBufferLocked(camera3_stream_buffer *buffer,
+ const std::vector<size_t>& surface_ids = std::vector<size_t>());
virtual status_t returnBufferLocked(const camera3_stream_buffer &buffer,
nsecs_t timestamp);
virtual status_t getInputBufferLocked(camera3_stream_buffer *buffer);
diff --git a/services/camera/libcameraservice/device3/Camera3StreamInterface.h b/services/camera/libcameraservice/device3/Camera3StreamInterface.h
index ceea08a..f7b092f 100644
--- a/services/camera/libcameraservice/device3/Camera3StreamInterface.h
+++ b/services/camera/libcameraservice/device3/Camera3StreamInterface.h
@@ -200,12 +200,19 @@
* Fill in the camera3_stream_buffer with the next valid buffer for this
* stream, to hand over to the HAL.
*
+ * Multiple surfaces could share the same HAL stream, but a request may
+ * be only for a subset of surfaces. In this case, the
+ * Camera3StreamInterface object needs the surface ID information to acquire
+ * buffers for those surfaces. For the case of single surface for a HAL
+ * stream, surface_ids parameter has no effect.
+ *
* This method may only be called once finishConfiguration has been called.
* For bidirectional streams, this method applies to the output-side
* buffers.
*
*/
- virtual status_t getBuffer(camera3_stream_buffer *buffer) = 0;
+ virtual status_t getBuffer(camera3_stream_buffer *buffer,
+ const std::vector<size_t>& surface_ids = std::vector<size_t>()) = 0;
/**
* Return a buffer to the stream after use by the HAL.
diff --git a/services/camera/libcameraservice/device3/Camera3StreamSplitter.cpp b/services/camera/libcameraservice/device3/Camera3StreamSplitter.cpp
index c9f43aa..deb6735 100644
--- a/services/camera/libcameraservice/device3/Camera3StreamSplitter.cpp
+++ b/services/camera/libcameraservice/device3/Camera3StreamSplitter.cpp
@@ -37,10 +37,10 @@
namespace android {
status_t Camera3StreamSplitter::connect(const std::vector<sp<Surface> >& surfaces,
- uint32_t consumerUsage, size_t hal_max_buffers,
- sp<Surface>& consumer) {
- if (consumer != nullptr) {
- ALOGE("%s: output Surface is not NULL", __FUNCTION__);
+ uint32_t consumerUsage, size_t halMaxBuffers, sp<Surface>* consumer) {
+ ATRACE_CALL();
+ if (consumer == nullptr) {
+ SP_LOGE("%s: consumer pointer is NULL", __FUNCTION__);
return BAD_VALUE;
}
@@ -48,129 +48,147 @@
status_t res = OK;
if (mOutputs.size() > 0 || mConsumer != nullptr) {
- ALOGE("%s: StreamSplitter already connected", __FUNCTION__);
+ SP_LOGE("%s: already connected", __FUNCTION__);
+ return BAD_VALUE;
+ }
+ if (mBuffers.size() > 0) {
+ SP_LOGE("%s: still has %zu pending buffers", __FUNCTION__, mBuffers.size());
return BAD_VALUE;
}
+ mMaxHalBuffers = halMaxBuffers;
+ 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) {
- ALOGE("%s: Fatal: surface is NULL", __FUNCTION__);
+ SP_LOGE("%s: Fatal: surface is NULL", __FUNCTION__);
return BAD_VALUE;
}
- res = addOutputLocked(surfaces[i], hal_max_buffers, OutputType::NonDeferred);
+ res = addOutputLocked(surfaces[i]);
if (res != OK) {
- ALOGE("%s: Failed to add output surface: %s(%d)",
+ SP_LOGE("%s: Failed to add output surface: %s(%d)",
__FUNCTION__, strerror(-res), res);
return res;
}
}
- // Create buffer queue for input
+ // Create BufferQueue for input
BufferQueue::createBufferQueue(&mProducer, &mConsumer);
+ // Allocate 1 extra buffer to handle the case where all buffers are detached
+ // from input, and attached to the outputs. In this case, the input queue's
+ // dequeueBuffer can still allocate 1 extra buffer before being blocked by
+ // the output's attachBuffer().
mBufferItemConsumer = new BufferItemConsumer(mConsumer, consumerUsage,
- mMaxConsumerBuffers);
+ mMaxConsumerBuffers+1);
if (mBufferItemConsumer == nullptr) {
return NO_MEMORY;
}
- mConsumer->setConsumerName(getUniqueConsumerName());
+ mConsumer->setConsumerName(mConsumerName);
- mSurface = new Surface(mProducer);
- if (mSurface == nullptr) {
+ *consumer = new Surface(mProducer);
+ if (*consumer == nullptr) {
return NO_MEMORY;
}
- consumer = mSurface;
res = mConsumer->consumerConnect(this, /* controlledByApp */ false);
+ SP_LOGV("%s: connected", __FUNCTION__);
return res;
}
+status_t Camera3StreamSplitter::getOnFrameAvailableResult() {
+ ATRACE_CALL();
+ return mOnFrameAvailableRes.load();
+}
+
void Camera3StreamSplitter::disconnect() {
+ ATRACE_CALL();
Mutex::Autolock lock(mMutex);
+ for (auto& notifier : mNotifiers) {
+ sp<IGraphicBufferProducer> producer = notifier.first;
+ sp<OutputListener> listener = notifier.second;
+ IInterface::asBinder(producer)->unlinkToDeath(listener);
+ }
+ mNotifiers.clear();
+
for (auto& output : mOutputs) {
output->disconnect(NATIVE_WINDOW_API_CAMERA);
}
mOutputs.clear();
+ mOutputSlots.clear();
- if (mConsumer != nullptr) {
- mConsumer->consumerDisconnect();
- mConsumer.clear();
- }
+ mConsumer->consumerDisconnect();
if (mBuffers.size() > 0) {
- ALOGI("%zu buffers still being tracked", mBuffers.size());
+ SP_LOGW("%zu buffers still being tracked", mBuffers.size());
+ mBuffers.clear();
}
+
+ mMaxHalBuffers = 0;
+ mMaxConsumerBuffers = 0;
+ SP_LOGV("%s: Disconnected", __FUNCTION__);
}
+
Camera3StreamSplitter::~Camera3StreamSplitter() {
disconnect();
}
-status_t Camera3StreamSplitter::addOutput(
- const sp<Surface>& outputQueue, size_t hal_max_buffers) {
+status_t Camera3StreamSplitter::addOutput(const sp<Surface>& outputQueue) {
+ ATRACE_CALL();
Mutex::Autolock lock(mMutex);
- return addOutputLocked(outputQueue, hal_max_buffers, OutputType::Deferred);
+ status_t res = addOutputLocked(outputQueue);
+
+ if (res != OK) {
+ SP_LOGE("%s: addOutputLocked failed %d", __FUNCTION__, res);
+ return res;
+ }
+
+ res = mConsumer->setMaxAcquiredBufferCount(mMaxConsumerBuffers+1);
+
+ return res;
}
-status_t Camera3StreamSplitter::addOutputLocked(
- const sp<Surface>& outputQueue, size_t hal_max_buffers,
- OutputType outputType) {
+status_t Camera3StreamSplitter::addOutputLocked(const sp<Surface>& outputQueue) {
+ ATRACE_CALL();
if (outputQueue == nullptr) {
- ALOGE("addOutput: outputQueue must not be NULL");
- return BAD_VALUE;
- }
- if (hal_max_buffers < 1) {
- ALOGE("%s: Camera HAL requested max_buffer count: %zu, requires at least 1",
- __FUNCTION__, hal_max_buffers);
+ SP_LOGE("addOutput: outputQueue must not be NULL");
return BAD_VALUE;
}
sp<IGraphicBufferProducer> gbp = outputQueue->getIGraphicBufferProducer();
// Connect to the buffer producer
- IGraphicBufferProducer::QueueBufferOutput queueBufferOutput;
sp<OutputListener> listener(new OutputListener(this, gbp));
IInterface::asBinder(gbp)->linkToDeath(listener);
- status_t status = gbp->connect(listener, NATIVE_WINDOW_API_CAMERA,
- /* producerControlledByApp */ true, &queueBufferOutput);
- if (status != NO_ERROR) {
- ALOGE("addOutput: failed to connect (%d)", status);
- return status;
+ status_t res = outputQueue->connect(NATIVE_WINDOW_API_CAMERA, listener);
+ if (res != NO_ERROR) {
+ SP_LOGE("addOutput: failed to connect (%d)", res);
+ return res;
}
// Query consumer side buffer count, and update overall buffer count
int maxConsumerBuffers = 0;
- status = static_cast<ANativeWindow*>(outputQueue.get())->query(
+ res = static_cast<ANativeWindow*>(outputQueue.get())->query(
outputQueue.get(),
NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS, &maxConsumerBuffers);
- if (status != OK) {
- ALOGE("%s: Unable to query consumer undequeued buffer count"
+ if (res != OK) {
+ SP_LOGE("%s: Unable to query consumer undequeued buffer count"
" for surface", __FUNCTION__);
- return status;
+ return res;
}
- if (maxConsumerBuffers > mMaxConsumerBuffers) {
- if (outputType == OutputType::Deferred) {
- ALOGE("%s: Fatal: Deferred surface has higher consumer buffer count"
- " %d than what's already configured %d", __FUNCTION__,
- maxConsumerBuffers, mMaxConsumerBuffers);
- return BAD_VALUE;
- }
- mMaxConsumerBuffers = maxConsumerBuffers;
- }
-
- ALOGV("%s: Consumer wants %d buffers, HAL wants %zu", __FUNCTION__,
- maxConsumerBuffers, hal_max_buffers);
- size_t totalBufferCount = maxConsumerBuffers + hal_max_buffers;
- status = native_window_set_buffer_count(outputQueue.get(),
+ SP_LOGV("%s: Consumer wants %d buffers, Producer wants %zu", __FUNCTION__,
+ maxConsumerBuffers, mMaxHalBuffers);
+ size_t totalBufferCount = maxConsumerBuffers + mMaxHalBuffers;
+ res = native_window_set_buffer_count(outputQueue.get(),
totalBufferCount);
- if (status != OK) {
- ALOGE("%s: Unable to set buffer count for surface %p",
+ if (res != OK) {
+ SP_LOGE("%s: Unable to set buffer count for surface %p",
__FUNCTION__, outputQueue.get());
- return status;
+ return res;
}
// Set dequeueBuffer/attachBuffer timeout if the consumer is not hw composer or hw texture.
@@ -183,157 +201,239 @@
outputQueue->setDequeueTimeout(kDequeueBufferTimeout);
}
- status = gbp->allowAllocation(false);
- if (status != OK) {
- ALOGE("%s: Failed to turn off allocation for outputQueue", __FUNCTION__);
- return status;
+ res = gbp->allowAllocation(false);
+ if (res != OK) {
+ SP_LOGE("%s: Failed to turn off allocation for outputQueue", __FUNCTION__);
+ return res;
}
// Add new entry into mOutputs
mOutputs.push_back(gbp);
+ mNotifiers[gbp] = listener;
+ mOutputSlots[gbp] = std::make_unique<OutputSlots>(totalBufferCount);
+
+ mMaxConsumerBuffers += maxConsumerBuffers;
return NO_ERROR;
}
+status_t Camera3StreamSplitter::outputBufferLocked(const sp<IGraphicBufferProducer>& output,
+ const BufferItem& bufferItem) {
+ ATRACE_CALL();
+ status_t res;
+ IGraphicBufferProducer::QueueBufferInput queueInput(
+ bufferItem.mTimestamp, bufferItem.mIsAutoTimestamp,
+ bufferItem.mDataSpace, bufferItem.mCrop,
+ static_cast<int32_t>(bufferItem.mScalingMode),
+ bufferItem.mTransform, bufferItem.mFence);
+
+ IGraphicBufferProducer::QueueBufferOutput queueOutput;
+
+ uint64_t bufferId = bufferItem.mGraphicBuffer->getId();
+ const BufferTracker& tracker = *(mBuffers[bufferId]);
+ int slot = getSlotForOutputLocked(output, tracker.getBuffer());
+
+ // In case the output BufferQueue has its own lock, if we hold splitter lock while calling
+ // queueBuffer (which will try to acquire the output lock), the output could be holding its
+ // own lock calling releaseBuffer (which will try to acquire the splitter lock), running into
+ // circular lock situation.
+ mMutex.unlock();
+ res = output->queueBuffer(slot, queueInput, &queueOutput);
+ mMutex.lock();
+
+ SP_LOGV("%s: Queuing buffer to buffer queue %p slot %d returns %d",
+ __FUNCTION__, output.get(), slot, res);
+ if (res != OK) {
+ if (res != NO_INIT && res != DEAD_OBJECT) {
+ SP_LOGE("Queuing buffer to output failed (%d)", res);
+ }
+ // If we just discovered that this output has been abandoned, note
+ // 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);
+ return res;
+ }
+
+ // If the queued buffer replaces a pending buffer in the async
+ // queue, no onBufferReleased is called by the buffer queue.
+ // Proactively trigger the callback to avoid buffer loss.
+ if (queueOutput.bufferReplaced) {
+ onBufferReleasedByOutputLocked(output);
+ }
+
+ return res;
+}
+
String8 Camera3StreamSplitter::getUniqueConsumerName() {
static volatile int32_t counter = 0;
return String8::format("Camera3StreamSplitter-%d", android_atomic_inc(&counter));
}
-status_t Camera3StreamSplitter::notifyRequestedSurfaces(
- const std::vector<size_t>& surfaces) {
+status_t Camera3StreamSplitter::notifyBufferReleased(const sp<GraphicBuffer>& buffer) {
ATRACE_CALL();
+ status_t res = OK;
+
Mutex::Autolock lock(mMutex);
- mRequestedSurfaces.push_back(surfaces);
- return OK;
-}
+ uint64_t bufferId = buffer->getId();
+ std::unique_ptr<BufferTracker> tracker_ptr = std::move(mBuffers[bufferId]);
+ mBuffers.erase(bufferId);
-
-void Camera3StreamSplitter::onFrameAvailable(const BufferItem& /* item */) {
- ATRACE_CALL();
- Mutex::Autolock lock(mMutex);
-
- // The current policy is that if any one consumer is consuming buffers too
- // slowly, the splitter will stall the rest of the outputs by not acquiring
- // any more buffers from the input. This will cause back pressure on the
- // input queue, slowing down its producer.
-
- // If there are too many outstanding buffers, we block until a buffer is
- // released back to the input in onBufferReleased
- while (mOutstandingBuffers >= mMaxConsumerBuffers) {
- mReleaseCondition.wait(mMutex);
-
- // If the splitter is abandoned while we are waiting, the release
- // condition variable will be broadcast, and we should just return
- // without attempting to do anything more (since the input queue will
- // also be abandoned).
- if (mIsAbandoned) {
- return;
+ 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();
}
}
- // If the splitter is abandoned without reaching mMaxConsumerBuffers, just
- // return without attempting to do anything more.
- if (mIsAbandoned) {
- return;
+
+ return res;
+}
+
+status_t Camera3StreamSplitter::attachBufferToOutputs(ANativeWindowBuffer* anb,
+ const std::vector<size_t>& surface_ids) {
+ ATRACE_CALL();
+ status_t res = OK;
+
+ Mutex::Autolock lock(mMutex);
+
+ sp<GraphicBuffer> gb(static_cast<GraphicBuffer*>(anb));
+ uint64_t bufferId = gb->getId();
+
+ // Initialize buffer tracker for this input buffer
+ auto tracker = std::make_unique<BufferTracker>(gb, surface_ids);
+
+ for (auto& surface_id : surface_ids) {
+ sp<IGraphicBufferProducer>& gbp = mOutputs[surface_id];
+ int slot = BufferItem::INVALID_BUFFER_SLOT;
+ //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
+ //will block as well because they need to acquire the same lock.
+ mMutex.unlock();
+ res = gbp->attachBuffer(&slot, gb);
+ mMutex.lock();
+ if (res != OK) {
+ SP_LOGE("%s: Cannot acquireBuffer from GraphicBufferProducer %p: %s (%d)",
+ __FUNCTION__, gbp.get(), strerror(-res), res);
+ return res;
+ }
+ 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);
+ }
+ SP_LOGV("%s: Attached buffer %p to slot %d on output %p.",__FUNCTION__, gb.get(),
+ slot, gbp.get());
+ outputSlots[slot] = gb;
}
- ++mOutstandingBuffers;
+ mBuffers[bufferId] = std::move(tracker);
+
+ return res;
+}
+
+void Camera3StreamSplitter::onFrameAvailable(const BufferItem& /*item*/) {
+ ATRACE_CALL();
+ Mutex::Autolock lock(mMutex);
// Acquire and detach the buffer from the input
BufferItem bufferItem;
- status_t status = mConsumer->acquireBuffer(&bufferItem, /* presentWhen */ 0);
- LOG_ALWAYS_FATAL_IF(status != NO_ERROR,
- "acquiring buffer from input failed (%d)", status);
+ status_t res = mConsumer->acquireBuffer(&bufferItem, /* presentWhen */ 0);
+ if (res != NO_ERROR) {
+ SP_LOGE("%s: Acquiring buffer from input failed (%d)", __FUNCTION__, res);
+ mOnFrameAvailableRes.store(res);
+ return;
+ }
+ if (mBuffers.find(bufferItem.mGraphicBuffer->getId()) == mBuffers.end()) {
+ SP_LOGE("%s: Acquired buffer doesn't exist in attached buffer map",
+ __FUNCTION__);
+ mOnFrameAvailableRes.store(INVALID_OPERATION);
+ return;
+ }
- ALOGV("acquired buffer %#" PRIx64 " from input",
- bufferItem.mGraphicBuffer->getId());
+ SP_LOGV("acquired buffer %" PRId64 " from input at slot %d",
+ bufferItem.mGraphicBuffer->getId(), bufferItem.mSlot);
- status = mConsumer->detachBuffer(bufferItem.mSlot);
- LOG_ALWAYS_FATAL_IF(status != NO_ERROR,
- "detaching buffer from input failed (%d)", status);
-
- IGraphicBufferProducer::QueueBufferInput queueInput(
- bufferItem.mTimestamp, bufferItem.mIsAutoTimestamp,
- bufferItem.mDataSpace, bufferItem.mCrop,
- static_cast<int32_t>(bufferItem.mScalingMode),
- bufferItem.mTransform, bufferItem.mFence);
+ 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
- std::vector<std::vector<size_t> >::iterator surfaces = mRequestedSurfaces.begin();
- if (surfaces != mRequestedSurfaces.end()) {
+ BufferTracker& tracker = *(mBuffers[bufferItem.mGraphicBuffer->getId()]);
- LOG_ALWAYS_FATAL_IF(surfaces->size() == 0,
- "requested surface ids shouldn't be empty");
+ SP_LOGV("%s: BufferTracker for buffer %" PRId64 ", number of requests %zu",
+ __FUNCTION__, bufferItem.mGraphicBuffer->getId(), tracker.requestedSurfaces().size());
+ for (const auto id : tracker.requestedSurfaces()) {
- // Initialize our reference count for this buffer
- mBuffers[bufferItem.mGraphicBuffer->getId()] =
- std::unique_ptr<BufferTracker>(
- new BufferTracker(bufferItem.mGraphicBuffer, surfaces->size()));
+ LOG_ALWAYS_FATAL_IF(id >= mOutputs.size(),
+ "requested surface id exceeding max registered ids");
- for (auto id : *surfaces) {
-
- LOG_ALWAYS_FATAL_IF(id >= mOutputs.size(),
- "requested surface id exceeding max registered ids");
-
- int slot = BufferItem::INVALID_BUFFER_SLOT;
- status = mOutputs[id]->attachBuffer(&slot, bufferItem.mGraphicBuffer);
- if (status == NO_INIT) {
- // If we just discovered that this output has been abandoned, note
- // that, decrement the reference count so that we still release this
- // buffer eventually, and move on to the next output
- onAbandonedLocked();
- mBuffers[bufferItem.mGraphicBuffer->getId()]->
- decrementReferenceCountLocked();
- continue;
- } else if (status == WOULD_BLOCK) {
- // If the output is async, attachBuffer may return WOULD_BLOCK
- // indicating number of dequeued buffers has reached limit. In
- // this case, simply decrement the reference count, and move on
- // to the next output.
- // TODO: Do we need to report BUFFER_ERROR for this result?
- mBuffers[bufferItem.mGraphicBuffer->getId()]->
- decrementReferenceCountLocked();
- continue;
- } else if (status == TIMED_OUT) {
- // If attachBuffer times out due to the value set by
- // setDequeueTimeout, simply decrement the reference count, and
- // move on to the next output.
- // TODO: Do we need to report BUFFER_ERROR for this result?
- mBuffers[bufferItem.mGraphicBuffer->getId()]->
- decrementReferenceCountLocked();
- continue;
- } else {
- LOG_ALWAYS_FATAL_IF(status != NO_ERROR,
- "attaching buffer to output failed (%d)", status);
- }
-
- IGraphicBufferProducer::QueueBufferOutput queueOutput;
- status = mOutputs[id]->queueBuffer(slot, queueInput, &queueOutput);
- if (status == NO_INIT) {
- // If we just discovered that this output has been abandoned, note
- // that, increment the release count so that we still release this
- // buffer eventually, and move on to the next output
- onAbandonedLocked();
- mBuffers[bufferItem.mGraphicBuffer->getId()]->
- decrementReferenceCountLocked();
- continue;
- } else {
- LOG_ALWAYS_FATAL_IF(status != NO_ERROR,
- "queueing buffer to output failed (%d)", status);
- }
-
- // If the queued buffer replaces a pending buffer in the async
- // queue, no onBufferReleased is called by the buffer queue.
- // Proactively trigger the callback to avoid buffer loss.
- if (queueOutput.bufferReplaced) {
- onBufferReleasedByOutputLocked(mOutputs[id]);
- }
-
- ALOGV("queued buffer %#" PRIx64 " to output %p",
- bufferItem.mGraphicBuffer->getId(), mOutputs[id].get());
+ res = outputBufferLocked(mOutputs[id], bufferItem);
+ if (res != OK) {
+ SP_LOGE("%s: outputBufferLocked failed %d", __FUNCTION__, res);
+ mOnFrameAvailableRes.store(res);
+ // If we fail to send buffer to certain output, keep sending to
+ // other outputs.
+ continue;
}
+ }
- mRequestedSurfaces.erase(surfaces);
+ mOnFrameAvailableRes.store(res);
+}
+
+void Camera3StreamSplitter::decrementBufRefCountLocked(uint64_t id,
+ const sp<IGraphicBufferProducer>& from) {
+ ATRACE_CALL();
+ size_t referenceCount = mBuffers[id]->decrementReferenceCountLocked();
+
+ removeSlotForOutputLocked(from, mBuffers[id]->getBuffer());
+ if (referenceCount > 0) {
+ return;
+ }
+
+ // We no longer need to track the buffer now that it is being returned to the
+ // input. Note that this should happen before we unlock the mutex and call
+ // releaseBuffer, to avoid the case where the same bufferId is acquired in
+ // attachBufferToOutputs resulting in a new BufferTracker with same bufferId
+ // overwrites the current one.
+ 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);
+ return;
+ }
+
+ // Temporarily unlock mutex to avoid circular lock:
+ // 1. This function holds splitter lock, calls releaseBuffer which triggers
+ // onBufferReleased in Camera3OutputStream. onBufferReleased waits on the
+ // OutputStream lock
+ // 2. Camera3SharedOutputStream::getBufferLocked calls
+ // attachBufferToOutputs, which holds the stream lock, and waits for the
+ // splitter lock.
+ sp<IGraphicBufferConsumer> consumer(mConsumer);
+ mMutex.unlock();
+ if (consumer != nullptr) {
+ res = consumer->releaseBuffer(consumerSlot, /* frameNumber */ 0,
+ EGL_NO_DISPLAY, EGL_NO_SYNC_KHR, tracker_ptr->getMergedFence());
+ } else {
+ SP_LOGE("%s: consumer has become null!", __FUNCTION__);
+ }
+ mMutex.lock();
+ // If the producer of this queue is disconnected, -22 error will occur
+ if (res != NO_ERROR) {
+ SP_LOGE("%s: releaseBuffer returns %d", __FUNCTION__, res);
}
}
@@ -347,73 +447,79 @@
void Camera3StreamSplitter::onBufferReleasedByOutputLocked(
const sp<IGraphicBufferProducer>& from) {
-
+ ATRACE_CALL();
sp<GraphicBuffer> buffer;
sp<Fence> fence;
- status_t status = from->detachNextBuffer(&buffer, &fence);
- if (status == NO_INIT) {
+ status_t res = from->detachNextBuffer(&buffer, &fence);
+ 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__);
+ return;
} else {
- LOG_ALWAYS_FATAL_IF(status != NO_ERROR,
- "detaching buffer from output failed (%d)", status);
+ LOG_ALWAYS_FATAL_IF(res != NO_ERROR,
+ "detaching buffer from output failed (%d)", res);
}
- ALOGV("detached buffer %#" PRIx64 " from output %p",
- buffer->getId(), from.get());
-
BufferTracker& tracker = *(mBuffers[buffer->getId()]);
-
// Merge the release fence of the incoming buffer so that the fence we send
// back to the input includes all of the outputs' fences
- tracker.mergeFence(fence);
+ if (fence != nullptr && fence->isValid()) {
+ tracker.mergeFence(fence);
+ }
+ SP_LOGV("detached buffer %" PRId64 " %p from output %p",
+ buffer->getId(), buffer.get(), from.get());
// Check to see if this is the last outstanding reference to this buffer
- size_t referenceCount = tracker.decrementReferenceCountLocked();
- ALOGV("buffer %#" PRIx64 " reference count %zu", buffer->getId(),
- referenceCount);
- if (referenceCount > 0) {
- return;
- }
-
- // If we've been abandoned, we can't return the buffer to the input, so just
- // stop tracking it and move on
- if (mIsAbandoned) {
- mBuffers.erase(buffer->getId());
- return;
- }
-
- // Attach and release the buffer back to the input
- int consumerSlot = BufferItem::INVALID_BUFFER_SLOT;
- status = mConsumer->attachBuffer(&consumerSlot, tracker.getBuffer());
- LOG_ALWAYS_FATAL_IF(status != NO_ERROR,
- "attaching buffer to input failed (%d)", status);
-
- status = mConsumer->releaseBuffer(consumerSlot, /* frameNumber */ 0,
- EGL_NO_DISPLAY, EGL_NO_SYNC_KHR, tracker.getMergedFence());
- LOG_ALWAYS_FATAL_IF(status != NO_ERROR,
- "releasing buffer to input failed (%d)", status);
-
- ALOGV("released buffer %#" PRIx64 " to input", buffer->getId());
-
- // We no longer need to track the buffer once it has been returned to the
- // input
- mBuffers.erase(buffer->getId());
-
- // Notify any waiting onFrameAvailable calls
- --mOutstandingBuffers;
- mReleaseCondition.signal();
+ decrementBufRefCountLocked(buffer->getId(), from);
}
void Camera3StreamSplitter::onAbandonedLocked() {
- ALOGE("one of my outputs has abandoned me");
- if (!mIsAbandoned && mConsumer != nullptr) {
- mConsumer->consumerDisconnect();
+ // If this is called from binderDied callback, it means the app process
+ // holding the binder has died. CameraService will be notified of the binder
+ // death, and camera device will be closed, which in turn calls
+ // disconnect().
+ //
+ // If this is called from onBufferReleasedByOutput or onFrameAvailable, one
+ // consumer being abanoned shouldn't impact the other consumer. So we won't
+ // stop the buffer flow.
+ //
+ // In both cases, we don't need to do anything here.
+ SP_LOGV("One of my outputs has abandoned me");
+}
+
+int Camera3StreamSplitter::getSlotForOutputLocked(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) {
+ return (int)i;
+ }
}
- mIsAbandoned = true;
- mReleaseCondition.broadcast();
+
+ SP_LOGE("%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(
@@ -422,6 +528,7 @@
: mSplitter(splitter), mOutput(output) {}
void Camera3StreamSplitter::OutputListener::onBufferReleased() {
+ ATRACE_CALL();
sp<Camera3StreamSplitter> splitter = mSplitter.promote();
sp<IGraphicBufferProducer> output = mOutput.promote();
if (splitter != nullptr && output != nullptr) {
@@ -438,9 +545,9 @@
}
Camera3StreamSplitter::BufferTracker::BufferTracker(
- const sp<GraphicBuffer>& buffer, size_t referenceCount)
- : mBuffer(buffer), mMergedFence(Fence::NO_FENCE),
- mReferenceCount(referenceCount) {}
+ const sp<GraphicBuffer>& buffer, const std::vector<size_t>& requestedSurfaces)
+ : mBuffer(buffer), mMergedFence(Fence::NO_FENCE), mRequestedSurfaces(requestedSurfaces),
+ mReferenceCount(requestedSurfaces.size()) {}
void Camera3StreamSplitter::BufferTracker::mergeFence(const sp<Fence>& with) {
mMergedFence = Fence::merge(String8("Camera3StreamSplitter"), mMergedFence, with);
diff --git a/services/camera/libcameraservice/device3/Camera3StreamSplitter.h b/services/camera/libcameraservice/device3/Camera3StreamSplitter.h
index 92371ff..cc623e0 100644
--- a/services/camera/libcameraservice/device3/Camera3StreamSplitter.h
+++ b/services/camera/libcameraservice/device3/Camera3StreamSplitter.h
@@ -26,6 +26,11 @@
#include <utils/StrongPointer.h>
#include <utils/Timers.h>
+#define SP_LOGV(x, ...) ALOGV("[%s] " x, mConsumerName.string(), ##__VA_ARGS__)
+#define SP_LOGI(x, ...) ALOGI("[%s] " x, mConsumerName.string(), ##__VA_ARGS__)
+#define SP_LOGW(x, ...) ALOGW("[%s] " x, mConsumerName.string(), ##__VA_ARGS__)
+#define SP_LOGE(x, ...) ALOGE("[%s] " x, mConsumerName.string(), ##__VA_ARGS__)
+
namespace android {
class GraphicBuffer;
@@ -47,8 +52,8 @@
// Connect to the stream splitter by creating buffer queue and connecting it
// with output surfaces.
status_t connect(const std::vector<sp<Surface> >& surfaces,
- uint32_t consumerUsage, size_t hal_max_buffers,
- sp<Surface>& consumer);
+ uint32_t consumerUsage, size_t halMaxBuffers,
+ sp<Surface>* consumer);
// addOutput adds an output BufferQueue to the splitter. The splitter
// connects to outputQueue as a CPU producer, and any buffers queued
@@ -61,13 +66,22 @@
// 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, size_t hal_max_buffers);
+ status_t addOutput(const sp<Surface>& outputQueue);
- // Request surfaces for a particular frame number. The requested surfaces
- // are stored in a FIFO queue. And when the buffer becomes available from the
- // input queue, the registered surfaces are used to decide which output is
- // the buffer sent to.
- status_t notifyRequestedSurfaces(const std::vector<size_t>& surfaces);
+ // Notification that the graphic buffer has been released to the input
+ // BufferQueue. The buffer should be reused by the camera device instead of
+ // queuing to the outputs.
+ status_t notifyBufferReleased(const sp<GraphicBuffer>& buffer);
+
+ // Attach a buffer to the specified outputs. This call reserves a buffer
+ // slot in the output queue.
+ status_t attachBufferToOutputs(ANativeWindowBuffer* anb,
+ const std::vector<size_t>& surface_ids);
+
+ // Get return value of onFrameAvailable to work around problem that
+ // onFrameAvailable is void. This function should be called by the producer
+ // right after calling queueBuffer().
+ status_t getOnFrameAvailableResult();
// Disconnect the buffer queue from output surfaces.
void disconnect();
@@ -115,6 +129,10 @@
// acquire. This must be called with mMutex locked.
void onAbandonedLocked();
+ // 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);
+
// This is a thin wrapper class that lets us determine which BufferQueue
// the IProducerListener::onBufferReleased callback is associated with. We
// create one of these per output BufferQueue, and then pass the producer
@@ -139,7 +157,8 @@
class BufferTracker {
public:
- BufferTracker(const sp<GraphicBuffer>& buffer, size_t referenceCount);
+ BufferTracker(const sp<GraphicBuffer>& buffer,
+ const std::vector<size_t>& requestedSurfaces);
~BufferTracker() = default;
const sp<GraphicBuffer>& getBuffer() const { return mBuffer; }
@@ -151,6 +170,8 @@
// Only called while mMutex is held
size_t decrementReferenceCountLocked();
+ const std::vector<size_t> requestedSurfaces() const { return mRequestedSurfaces; }
+
private:
// Disallow copying
@@ -159,39 +180,43 @@
sp<GraphicBuffer> mBuffer; // One instance that holds this native handle
sp<Fence> mMergedFence;
+
+ // Request surfaces for a particular buffer. And when the buffer becomes
+ // available from the input queue, the registered surfaces are used to decide
+ // which output is the buffer sent to.
+ std::vector<size_t> mRequestedSurfaces;
size_t mReferenceCount;
};
- // A deferred output is an output being added to the splitter after
- // connect() call, whereas a non deferred output is added within connect()
- // call.
- enum class OutputType { NonDeferred, Deferred };
-
// Must be accessed through RefBase
virtual ~Camera3StreamSplitter();
- status_t addOutputLocked(const sp<Surface>& outputQueue,
- size_t hal_max_buffers, OutputType outputType);
+ status_t addOutputLocked(const sp<Surface>& outputQueue);
+
+ // 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);
// Get unique name for the buffer queue consumer
- static String8 getUniqueConsumerName();
+ String8 getUniqueConsumerName();
- // Max consumer side buffers for deferred surface. This will be used as a
- // lower bound for overall consumer side max buffers.
- static const int MAX_BUFFERS_DEFERRED_OUTPUT = 2;
- int mMaxConsumerBuffers = MAX_BUFFERS_DEFERRED_OUTPUT;
+ // 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;
static const nsecs_t kDequeueBufferTimeout = s2ns(1); // 1 sec
- // mIsAbandoned is set to true when an output dies. Once the Camera3StreamSplitter
- // has been abandoned, it will continue to detach buffers from other
- // outputs, but it will disconnect from the input and not attempt to
- // communicate with it further.
- bool mIsAbandoned = false;
-
Mutex mMutex;
- Condition mReleaseCondition;
- int mOutstandingBuffers = 0;
sp<IGraphicBufferProducer> mProducer;
sp<IGraphicBufferConsumer> mConsumer;
@@ -199,14 +224,28 @@
sp<Surface> mSurface;
std::vector<sp<IGraphicBufferProducer> > mOutputs;
- // Tracking which outputs should the buffer be attached and queued
- // to for each input buffer.
- std::vector<std::vector<size_t> > mRequestedSurfaces;
-
// 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).
std::unordered_map<uint64_t, std::unique_ptr<BufferTracker> > mBuffers;
+
+ struct GBPHash {
+ std::size_t operator()(const sp<IGraphicBufferProducer>& producer) const {
+ return std::hash<IGraphicBufferProducer *>{}(producer.get());
+ }
+ };
+
+ std::unordered_map<sp<IGraphicBufferProducer>, sp<OutputListener>,
+ GBPHash> mNotifiers;
+
+ typedef std::vector<sp<GraphicBuffer>> OutputSlots;
+ std::unordered_map<sp<IGraphicBufferProducer>, std::unique_ptr<OutputSlots>,
+ GBPHash> mOutputSlots;
+
+ // Latest onFrameAvailable return value
+ std::atomic<status_t> mOnFrameAvailableRes{0};
+
+ String8 mConsumerName;
};
} // namespace android