Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit bee0f0a5 authored by Shuzhen Wang's avatar Shuzhen Wang
Browse files

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
parent 92a4bab4
Loading
Loading
Loading
Loading
+8 −0
Original line number Diff line number Diff line
@@ -507,6 +507,14 @@ binder::Status CameraDeviceClient::createStream(
            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;
        }

+2 −8
Original line number Diff line number Diff line
@@ -3956,7 +3956,8 @@ status_t Camera3Device::RequestThread::prepareHalRequests() {
                }
            }

            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 @@ status_t Camera3Device::RequestThread::prepareHalRequests() {
            }
            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;

+2 −9
Original line number Diff line number Diff line
@@ -36,7 +36,8 @@ Camera3DummyStream::~Camera3DummyStream() {

}

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 @@ status_t Camera3DummyStream::detachBuffer(sp<GraphicBuffer>* buffer, int* fenceF
    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;
+2 −4
Original line number Diff line number Diff line
@@ -56,9 +56,6 @@ class Camera3DummyStream :

    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 @@ class Camera3DummyStream :
    /**
     * 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);
+87 −68
Original line number Diff line number Diff line
@@ -148,74 +148,18 @@ Camera3OutputStream::~Camera3OutputStream() {
    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();
    status_t res;
    res = getBufferLockedCommon(&anb, &fenceFd);
    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;
    }
    }

    /**
     * FenceFD now owned by HAL except in case of error,
@@ -227,6 +171,11 @@ status_t Camera3OutputStream::getBufferLocked(camera3_stream_buffer *buffer) {
    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 @@ status_t Camera3OutputStream::returnBufferCheckedLocked(
    sp<ANativeWindow> currentConsumer = mConsumer;
    mLock.unlock();

    ANativeWindowBuffer *anwBuffer = container_of(buffer.buffer, ANativeWindowBuffer, handle);
    /**
     * Return buffer back to ANativeWindow
     */
@@ -276,13 +226,14 @@ status_t Camera3OutputStream::returnBufferCheckedLocked(
        // 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 @@ status_t Camera3OutputStream::returnBufferCheckedLocked(
            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 @@ status_t Camera3OutputStream::configureConsumerQueueLocked() {
    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 @@ status_t Camera3OutputStream::detachBuffer(sp<GraphicBuffer>* buffer, int* fence
    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 @@ bool Camera3OutputStream::isConsumerConfigurationDeferred(size_t surface_id) con
}

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());
Loading