| /* |
| * Copyright 2014 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| #define LOG_TAG "BufferQueueProducer" |
| #define ATRACE_TAG ATRACE_TAG_GRAPHICS |
| //#define LOG_NDEBUG 0 |
| |
| #define EGL_EGLEXT_PROTOTYPES |
| |
| #include <gui/BufferItem.h> |
| #include <gui/BufferQueueCore.h> |
| #include <gui/BufferQueueProducer.h> |
| #include <gui/IConsumerListener.h> |
| #include <gui/IGraphicBufferAlloc.h> |
| |
| #include <utils/Log.h> |
| #include <utils/Trace.h> |
| |
| namespace android { |
| |
| BufferQueueProducer::BufferQueueProducer(const sp<BufferQueueCore>& core) : |
| mCore(core), |
| mSlots(core->mSlots), |
| mConsumerName() {} |
| |
| BufferQueueProducer::~BufferQueueProducer() {} |
| |
| status_t BufferQueueProducer::requestBuffer(int slot, sp<GraphicBuffer>* buf) { |
| ATRACE_CALL(); |
| BQ_LOGV("requestBuffer: slot %d", slot); |
| Mutex::Autolock lock(mCore->mMutex); |
| |
| if (mCore->mIsAbandoned) { |
| BQ_LOGE("requestBuffer: BufferQueue has been abandoned"); |
| return NO_INIT; |
| } |
| |
| if (slot < 0 || slot >= BufferQueueDefs::NUM_BUFFER_SLOTS) { |
| BQ_LOGE("requestBuffer: slot index %d out of range [0, %d)", |
| slot, BufferQueueDefs::NUM_BUFFER_SLOTS); |
| return BAD_VALUE; |
| } else if (mSlots[slot].mBufferState != BufferSlot::DEQUEUED) { |
| BQ_LOGE("requestBuffer: slot %d is not owned by the producer " |
| "(state = %d)", slot, mSlots[slot].mBufferState); |
| return BAD_VALUE; |
| } |
| |
| mSlots[slot].mRequestBufferCalled = true; |
| *buf = mSlots[slot].mGraphicBuffer; |
| return NO_ERROR; |
| } |
| |
| status_t BufferQueueProducer::setBufferCount(int bufferCount) { |
| ATRACE_CALL(); |
| BQ_LOGV("setBufferCount: count = %d", bufferCount); |
| |
| sp<IConsumerListener> listener; |
| { // Autolock scope |
| Mutex::Autolock lock(mCore->mMutex); |
| |
| if (mCore->mIsAbandoned) { |
| BQ_LOGE("setBufferCount: BufferQueue has been abandoned"); |
| return NO_INIT; |
| } |
| |
| if (bufferCount > BufferQueueDefs::NUM_BUFFER_SLOTS) { |
| BQ_LOGE("setBufferCount: bufferCount %d too large (max %d)", |
| bufferCount, BufferQueueDefs::NUM_BUFFER_SLOTS); |
| return BAD_VALUE; |
| } |
| |
| // There must be no dequeued buffers when changing the buffer count. |
| for (int s = 0; s < BufferQueueDefs::NUM_BUFFER_SLOTS; ++s) { |
| if (mSlots[s].mBufferState == BufferSlot::DEQUEUED) { |
| BQ_LOGE("setBufferCount: buffer owned by producer"); |
| return BAD_VALUE; |
| } |
| } |
| |
| if (bufferCount == 0) { |
| mCore->mOverrideMaxBufferCount = 0; |
| mCore->mDequeueCondition.broadcast(); |
| return NO_ERROR; |
| } |
| |
| const int minBufferSlots = mCore->getMinMaxBufferCountLocked(false); |
| if (bufferCount < minBufferSlots) { |
| BQ_LOGE("setBufferCount: requested buffer count %d is less than " |
| "minimum %d", bufferCount, minBufferSlots); |
| return BAD_VALUE; |
| } |
| |
| // Here we are guaranteed that the producer doesn't have any dequeued |
| // buffers and will release all of its buffer references. We don't |
| // clear the queue, however, so that currently queued buffers still |
| // get displayed. |
| mCore->freeAllBuffersLocked(); |
| mCore->mOverrideMaxBufferCount = bufferCount; |
| mCore->mDequeueCondition.broadcast(); |
| listener = mCore->mConsumerListener; |
| } // Autolock scope |
| |
| // Call back without lock held |
| if (listener != NULL) { |
| listener->onBuffersReleased(); |
| } |
| |
| return NO_ERROR; |
| } |
| |
| status_t BufferQueueProducer::waitForFreeSlotThenRelock(const char* caller, |
| bool async, int* found, status_t* returnFlags) const { |
| bool tryAgain = true; |
| while (tryAgain) { |
| if (mCore->mIsAbandoned) { |
| BQ_LOGE("%s: BufferQueue has been abandoned", caller); |
| return NO_INIT; |
| } |
| |
| const int maxBufferCount = mCore->getMaxBufferCountLocked(async); |
| if (async && mCore->mOverrideMaxBufferCount) { |
| // FIXME: Some drivers are manually setting the buffer count |
| // (which they shouldn't), so we do this extra test here to |
| // handle that case. This is TEMPORARY until we get this fixed. |
| if (mCore->mOverrideMaxBufferCount < maxBufferCount) { |
| BQ_LOGE("%s: async mode is invalid with buffer count override", |
| caller); |
| return BAD_VALUE; |
| } |
| } |
| |
| // Free up any buffers that are in slots beyond the max buffer count |
| for (int s = maxBufferCount; s < BufferQueueDefs::NUM_BUFFER_SLOTS; ++s) { |
| assert(mSlots[s].mBufferState == BufferSlot::FREE); |
| if (mSlots[s].mGraphicBuffer != NULL) { |
| mCore->freeBufferLocked(s); |
| *returnFlags |= RELEASE_ALL_BUFFERS; |
| } |
| } |
| |
| // Look for a free buffer to give to the client |
| *found = BufferQueueCore::INVALID_BUFFER_SLOT; |
| int dequeuedCount = 0; |
| int acquiredCount = 0; |
| for (int s = 0; s < maxBufferCount; ++s) { |
| switch (mSlots[s].mBufferState) { |
| case BufferSlot::DEQUEUED: |
| ++dequeuedCount; |
| break; |
| case BufferSlot::ACQUIRED: |
| ++acquiredCount; |
| break; |
| case BufferSlot::FREE: |
| // We return the oldest of the free buffers to avoid |
| // stalling the producer if possible, since the consumer |
| // may still have pending reads of in-flight buffers |
| if (*found == BufferQueueCore::INVALID_BUFFER_SLOT || |
| mSlots[s].mFrameNumber < mSlots[*found].mFrameNumber) { |
| *found = s; |
| } |
| break; |
| default: |
| break; |
| } |
| } |
| |
| // Producers are not allowed to dequeue more than one buffer if they |
| // did not set a buffer count |
| if (!mCore->mOverrideMaxBufferCount && dequeuedCount) { |
| BQ_LOGE("%s: can't dequeue multiple buffers without setting the " |
| "buffer count", caller); |
| return INVALID_OPERATION; |
| } |
| |
| // See whether a buffer has been queued since the last |
| // setBufferCount so we know whether to perform the min undequeued |
| // buffers check below |
| if (mCore->mBufferHasBeenQueued) { |
| // Make sure the producer is not trying to dequeue more buffers |
| // than allowed |
| const int newUndequeuedCount = |
| maxBufferCount - (dequeuedCount + 1); |
| const int minUndequeuedCount = |
| mCore->getMinUndequeuedBufferCountLocked(async); |
| if (newUndequeuedCount < minUndequeuedCount) { |
| BQ_LOGE("%s: min undequeued buffer count (%d) exceeded " |
| "(dequeued=%d undequeued=%d)", |
| caller, minUndequeuedCount, |
| dequeuedCount, newUndequeuedCount); |
| return INVALID_OPERATION; |
| } |
| } |
| |
| // If no buffer is found, wait for a buffer to be released or for |
| // the max buffer count to change |
| tryAgain = (*found == BufferQueueCore::INVALID_BUFFER_SLOT); |
| if (tryAgain) { |
| // Return an error if we're in non-blocking mode (producer and |
| // consumer are controlled by the application). |
| // However, the consumer is allowed to briefly acquire an extra |
| // buffer (which could cause us to have to wait here), which is |
| // okay, since it is only used to implement an atomic acquire + |
| // release (e.g., in GLConsumer::updateTexImage()) |
| if (mCore->mDequeueBufferCannotBlock && |
| (acquiredCount <= mCore->mMaxAcquiredBufferCount)) { |
| return WOULD_BLOCK; |
| } |
| mCore->mDequeueCondition.wait(mCore->mMutex); |
| } |
| } // while (tryAgain) |
| |
| return NO_ERROR; |
| } |
| |
| status_t BufferQueueProducer::dequeueBuffer(int *outSlot, |
| sp<android::Fence> *outFence, bool async, |
| uint32_t width, uint32_t height, uint32_t format, uint32_t usage) { |
| ATRACE_CALL(); |
| { // Autolock scope |
| Mutex::Autolock lock(mCore->mMutex); |
| mConsumerName = mCore->mConsumerName; |
| } // Autolock scope |
| |
| BQ_LOGV("dequeueBuffer: async=%s w=%u h=%u format=%#x, usage=%#x", |
| async ? "true" : "false", width, height, format, usage); |
| |
| if ((width && !height) || (!width && height)) { |
| BQ_LOGE("dequeueBuffer: invalid size: w=%u h=%u", width, height); |
| return BAD_VALUE; |
| } |
| |
| status_t returnFlags = NO_ERROR; |
| EGLDisplay eglDisplay = EGL_NO_DISPLAY; |
| EGLSyncKHR eglFence = EGL_NO_SYNC_KHR; |
| bool attachedByConsumer = false; |
| |
| { // Autolock scope |
| Mutex::Autolock lock(mCore->mMutex); |
| |
| if (format == 0) { |
| format = mCore->mDefaultBufferFormat; |
| } |
| |
| // Enable the usage bits the consumer requested |
| usage |= mCore->mConsumerUsageBits; |
| |
| int found; |
| status_t status = waitForFreeSlotThenRelock("dequeueBuffer", async, |
| &found, &returnFlags); |
| if (status != NO_ERROR) { |
| return status; |
| } |
| |
| // This should not happen |
| if (found == BufferQueueCore::INVALID_BUFFER_SLOT) { |
| BQ_LOGE("dequeueBuffer: no available buffer slots"); |
| return -EBUSY; |
| } |
| |
| *outSlot = found; |
| ATRACE_BUFFER_INDEX(found); |
| |
| attachedByConsumer = mSlots[found].mAttachedByConsumer; |
| |
| const bool useDefaultSize = !width && !height; |
| if (useDefaultSize) { |
| width = mCore->mDefaultWidth; |
| height = mCore->mDefaultHeight; |
| } |
| |
| mSlots[found].mBufferState = BufferSlot::DEQUEUED; |
| |
| const sp<GraphicBuffer>& buffer(mSlots[found].mGraphicBuffer); |
| if ((buffer == NULL) || |
| (static_cast<uint32_t>(buffer->width) != width) || |
| (static_cast<uint32_t>(buffer->height) != height) || |
| (static_cast<uint32_t>(buffer->format) != format) || |
| ((static_cast<uint32_t>(buffer->usage) & usage) != usage)) |
| { |
| mSlots[found].mAcquireCalled = false; |
| mSlots[found].mGraphicBuffer = NULL; |
| mSlots[found].mRequestBufferCalled = false; |
| mSlots[found].mEglDisplay = EGL_NO_DISPLAY; |
| mSlots[found].mEglFence = EGL_NO_SYNC_KHR; |
| mSlots[found].mFence = Fence::NO_FENCE; |
| |
| returnFlags |= BUFFER_NEEDS_REALLOCATION; |
| } |
| |
| if (CC_UNLIKELY(mSlots[found].mFence == NULL)) { |
| BQ_LOGE("dequeueBuffer: about to return a NULL fence - " |
| "slot=%d w=%d h=%d format=%u", |
| found, buffer->width, buffer->height, buffer->format); |
| } |
| |
| eglDisplay = mSlots[found].mEglDisplay; |
| eglFence = mSlots[found].mEglFence; |
| *outFence = mSlots[found].mFence; |
| mSlots[found].mEglFence = EGL_NO_SYNC_KHR; |
| mSlots[found].mFence = Fence::NO_FENCE; |
| } // Autolock scope |
| |
| if (returnFlags & BUFFER_NEEDS_REALLOCATION) { |
| status_t error; |
| sp<GraphicBuffer> graphicBuffer(mCore->mAllocator->createGraphicBuffer( |
| width, height, format, usage, &error)); |
| if (graphicBuffer == NULL) { |
| BQ_LOGE("dequeueBuffer: createGraphicBuffer failed"); |
| return error; |
| } |
| |
| { // Autolock scope |
| Mutex::Autolock lock(mCore->mMutex); |
| |
| if (mCore->mIsAbandoned) { |
| BQ_LOGE("dequeueBuffer: BufferQueue has been abandoned"); |
| return NO_INIT; |
| } |
| |
| mSlots[*outSlot].mFrameNumber = UINT32_MAX; |
| mSlots[*outSlot].mGraphicBuffer = graphicBuffer; |
| } // Autolock scope |
| } |
| |
| if (attachedByConsumer) { |
| returnFlags |= BUFFER_NEEDS_REALLOCATION; |
| } |
| |
| if (eglFence != EGL_NO_SYNC_KHR) { |
| EGLint result = eglClientWaitSyncKHR(eglDisplay, eglFence, 0, |
| 1000000000); |
| // If something goes wrong, log the error, but return the buffer without |
| // synchronizing access to it. It's too late at this point to abort the |
| // dequeue operation. |
| if (result == EGL_FALSE) { |
| BQ_LOGE("dequeueBuffer: error %#x waiting for fence", |
| eglGetError()); |
| } else if (result == EGL_TIMEOUT_EXPIRED_KHR) { |
| BQ_LOGE("dequeueBuffer: timeout waiting for fence"); |
| } |
| eglDestroySyncKHR(eglDisplay, eglFence); |
| } |
| |
| BQ_LOGV("dequeueBuffer: returning slot=%d/%llu buf=%p flags=%#x", *outSlot, |
| mSlots[*outSlot].mFrameNumber, |
| mSlots[*outSlot].mGraphicBuffer->handle, returnFlags); |
| |
| return returnFlags; |
| } |
| |
| status_t BufferQueueProducer::detachBuffer(int slot) { |
| ATRACE_CALL(); |
| ATRACE_BUFFER_INDEX(slot); |
| BQ_LOGV("detachBuffer(P): slot %d", slot); |
| Mutex::Autolock lock(mCore->mMutex); |
| |
| if (mCore->mIsAbandoned) { |
| BQ_LOGE("detachBuffer(P): BufferQueue has been abandoned"); |
| return NO_INIT; |
| } |
| |
| if (slot < 0 || slot >= BufferQueueDefs::NUM_BUFFER_SLOTS) { |
| BQ_LOGE("detachBuffer(P): slot index %d out of range [0, %d)", |
| slot, BufferQueueDefs::NUM_BUFFER_SLOTS); |
| return BAD_VALUE; |
| } else if (mSlots[slot].mBufferState != BufferSlot::DEQUEUED) { |
| BQ_LOGE("detachBuffer(P): slot %d is not owned by the producer " |
| "(state = %d)", slot, mSlots[slot].mBufferState); |
| return BAD_VALUE; |
| } else if (!mSlots[slot].mRequestBufferCalled) { |
| BQ_LOGE("detachBuffer(P): buffer in slot %d has not been requested", |
| slot); |
| return BAD_VALUE; |
| } |
| |
| mCore->freeBufferLocked(slot); |
| mCore->mDequeueCondition.broadcast(); |
| |
| return NO_ERROR; |
| } |
| |
| status_t BufferQueueProducer::attachBuffer(int* outSlot, |
| const sp<android::GraphicBuffer>& buffer) { |
| ATRACE_CALL(); |
| |
| if (outSlot == NULL) { |
| BQ_LOGE("attachBuffer(P): outSlot must not be NULL"); |
| return BAD_VALUE; |
| } else if (buffer == NULL) { |
| BQ_LOGE("attachBuffer(P): cannot attach NULL buffer"); |
| return BAD_VALUE; |
| } |
| |
| Mutex::Autolock lock(mCore->mMutex); |
| |
| status_t returnFlags = NO_ERROR; |
| int found; |
| // TODO: Should we provide an async flag to attachBuffer? It seems |
| // unlikely that buffers which we are attaching to a BufferQueue will |
| // be asynchronous (droppable), but it may not be impossible. |
| status_t status = waitForFreeSlotThenRelock("attachBuffer(P)", false, |
| &found, &returnFlags); |
| if (status != NO_ERROR) { |
| return status; |
| } |
| |
| // This should not happen |
| if (found == BufferQueueCore::INVALID_BUFFER_SLOT) { |
| BQ_LOGE("attachBuffer(P): no available buffer slots"); |
| return -EBUSY; |
| } |
| |
| *outSlot = found; |
| ATRACE_BUFFER_INDEX(*outSlot); |
| BQ_LOGV("attachBuffer(P): returning slot %d flags=%#x", |
| *outSlot, returnFlags); |
| |
| mSlots[*outSlot].mGraphicBuffer = buffer; |
| mSlots[*outSlot].mBufferState = BufferSlot::DEQUEUED; |
| mSlots[*outSlot].mEglFence = EGL_NO_SYNC_KHR; |
| mSlots[*outSlot].mFence = Fence::NO_FENCE; |
| |
| return returnFlags; |
| } |
| |
| status_t BufferQueueProducer::queueBuffer(int slot, |
| const QueueBufferInput &input, QueueBufferOutput *output) { |
| ATRACE_CALL(); |
| ATRACE_BUFFER_INDEX(slot); |
| |
| int64_t timestamp; |
| bool isAutoTimestamp; |
| Rect crop; |
| int scalingMode; |
| uint32_t transform; |
| bool async; |
| sp<Fence> fence; |
| input.deflate(×tamp, &isAutoTimestamp, &crop, &scalingMode, &transform, |
| &async, &fence); |
| |
| if (fence == NULL) { |
| BQ_LOGE("queueBuffer: fence is NULL"); |
| return BAD_VALUE; |
| } |
| |
| switch (scalingMode) { |
| case NATIVE_WINDOW_SCALING_MODE_FREEZE: |
| case NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW: |
| case NATIVE_WINDOW_SCALING_MODE_SCALE_CROP: |
| case NATIVE_WINDOW_SCALING_MODE_NO_SCALE_CROP: |
| break; |
| default: |
| BQ_LOGE("queueBuffer: unknown scaling mode %d", scalingMode); |
| return BAD_VALUE; |
| } |
| |
| sp<IConsumerListener> listener; |
| { // Autolock scope |
| Mutex::Autolock lock(mCore->mMutex); |
| |
| if (mCore->mIsAbandoned) { |
| BQ_LOGE("queueBuffer: BufferQueue has been abandoned"); |
| return NO_INIT; |
| } |
| |
| const int maxBufferCount = mCore->getMaxBufferCountLocked(async); |
| if (async && mCore->mOverrideMaxBufferCount) { |
| // FIXME: Some drivers are manually setting the buffer count |
| // (which they shouldn't), so we do this extra test here to |
| // handle that case. This is TEMPORARY until we get this fixed. |
| if (mCore->mOverrideMaxBufferCount < maxBufferCount) { |
| BQ_LOGE("queueBuffer: async mode is invalid with " |
| "buffer count override"); |
| return BAD_VALUE; |
| } |
| } |
| |
| if (slot < 0 || slot >= maxBufferCount) { |
| BQ_LOGE("queueBuffer: slot index %d out of range [0, %d)", |
| slot, maxBufferCount); |
| return BAD_VALUE; |
| } else if (mSlots[slot].mBufferState != BufferSlot::DEQUEUED) { |
| BQ_LOGE("queueBuffer: slot %d is not owned by the producer " |
| "(state = %d)", slot, mSlots[slot].mBufferState); |
| return BAD_VALUE; |
| } else if (!mSlots[slot].mRequestBufferCalled) { |
| BQ_LOGE("queueBuffer: slot %d was queued without requesting " |
| "a buffer", slot); |
| return BAD_VALUE; |
| } |
| |
| BQ_LOGV("queueBuffer: slot=%d/%llu time=%llu crop=[%d,%d,%d,%d] " |
| "transform=%#x scale=%s", |
| slot, mCore->mFrameCounter + 1, timestamp, |
| crop.left, crop.top, crop.right, crop.bottom, |
| transform, BufferItem::scalingModeName(scalingMode)); |
| |
| const sp<GraphicBuffer>& graphicBuffer(mSlots[slot].mGraphicBuffer); |
| Rect bufferRect(graphicBuffer->getWidth(), graphicBuffer->getHeight()); |
| Rect croppedRect; |
| crop.intersect(bufferRect, &croppedRect); |
| if (croppedRect != crop) { |
| BQ_LOGE("queueBuffer: crop rect is not contained within the " |
| "buffer in slot %d", slot); |
| return BAD_VALUE; |
| } |
| |
| mSlots[slot].mFence = fence; |
| mSlots[slot].mBufferState = BufferSlot::QUEUED; |
| ++mCore->mFrameCounter; |
| mSlots[slot].mFrameNumber = mCore->mFrameCounter; |
| |
| BufferItem item; |
| item.mAcquireCalled = mSlots[slot].mAcquireCalled; |
| item.mGraphicBuffer = mSlots[slot].mGraphicBuffer; |
| item.mCrop = crop; |
| item.mTransform = transform & ~NATIVE_WINDOW_TRANSFORM_INVERSE_DISPLAY; |
| item.mTransformToDisplayInverse = |
| bool(transform & NATIVE_WINDOW_TRANSFORM_INVERSE_DISPLAY); |
| item.mScalingMode = scalingMode; |
| item.mTimestamp = timestamp; |
| item.mIsAutoTimestamp = isAutoTimestamp; |
| item.mFrameNumber = mCore->mFrameCounter; |
| item.mSlot = slot; |
| item.mFence = fence; |
| item.mIsDroppable = mCore->mDequeueBufferCannotBlock || async; |
| |
| if (mCore->mQueue.empty()) { |
| // When the queue is empty, we can ignore mDequeueBufferCannotBlock |
| // and simply queue this buffer |
| mCore->mQueue.push_back(item); |
| listener = mCore->mConsumerListener; |
| } else { |
| // When the queue is not empty, we need to look at the front buffer |
| // state to see if we need to replace it |
| BufferQueueCore::Fifo::iterator front(mCore->mQueue.begin()); |
| if (front->mIsDroppable) { |
| // If the front queued buffer is still being tracked, we first |
| // mark it as freed |
| if (mCore->stillTracking(front)) { |
| mSlots[front->mSlot].mBufferState = BufferSlot::FREE; |
| // Reset the frame number of the freed buffer so that it is |
| // the first in line to be dequeued again |
| mSlots[front->mSlot].mFrameNumber = 0; |
| } |
| // Overwrite the droppable buffer with the incoming one |
| *front = item; |
| } else { |
| mCore->mQueue.push_back(item); |
| listener = mCore->mConsumerListener; |
| } |
| } |
| |
| mCore->mBufferHasBeenQueued = true; |
| mCore->mDequeueCondition.broadcast(); |
| |
| output->inflate(mCore->mDefaultWidth, mCore->mDefaultHeight, |
| mCore->mTransformHint, mCore->mQueue.size()); |
| |
| ATRACE_INT(mCore->mConsumerName.string(), mCore->mQueue.size()); |
| } // Autolock scope |
| |
| // Call back without lock held |
| if (listener != NULL) { |
| listener->onFrameAvailable(); |
| } |
| |
| return NO_ERROR; |
| } |
| |
| void BufferQueueProducer::cancelBuffer(int slot, const sp<Fence>& fence) { |
| ATRACE_CALL(); |
| BQ_LOGV("cancelBuffer: slot %d", slot); |
| Mutex::Autolock lock(mCore->mMutex); |
| |
| if (mCore->mIsAbandoned) { |
| BQ_LOGE("cancelBuffer: BufferQueue has been abandoned"); |
| return; |
| } |
| |
| if (slot < 0 || slot >= BufferQueueDefs::NUM_BUFFER_SLOTS) { |
| BQ_LOGE("cancelBuffer: slot index %d out of range [0, %d)", |
| slot, BufferQueueDefs::NUM_BUFFER_SLOTS); |
| return; |
| } else if (mSlots[slot].mBufferState != BufferSlot::DEQUEUED) { |
| BQ_LOGE("cancelBuffer: slot %d is not owned by the producer " |
| "(state = %d)", slot, mSlots[slot].mBufferState); |
| return; |
| } else if (fence == NULL) { |
| BQ_LOGE("cancelBuffer: fence is NULL"); |
| return; |
| } |
| |
| mSlots[slot].mBufferState = BufferSlot::FREE; |
| mSlots[slot].mFrameNumber = 0; |
| mSlots[slot].mFence = fence; |
| mCore->mDequeueCondition.broadcast(); |
| } |
| |
| int BufferQueueProducer::query(int what, int *outValue) { |
| ATRACE_CALL(); |
| Mutex::Autolock lock(mCore->mMutex); |
| |
| if (outValue == NULL) { |
| BQ_LOGE("query: outValue was NULL"); |
| return BAD_VALUE; |
| } |
| |
| if (mCore->mIsAbandoned) { |
| BQ_LOGE("query: BufferQueue has been abandoned"); |
| return NO_INIT; |
| } |
| |
| int value; |
| switch (what) { |
| case NATIVE_WINDOW_WIDTH: |
| value = mCore->mDefaultWidth; |
| break; |
| case NATIVE_WINDOW_HEIGHT: |
| value = mCore->mDefaultHeight; |
| break; |
| case NATIVE_WINDOW_FORMAT: |
| value = mCore->mDefaultBufferFormat; |
| break; |
| case NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS: |
| value = mCore->getMinUndequeuedBufferCountLocked(false); |
| break; |
| case NATIVE_WINDOW_CONSUMER_RUNNING_BEHIND: |
| value = (mCore->mQueue.size() > 1); |
| break; |
| case NATIVE_WINDOW_CONSUMER_USAGE_BITS: |
| value = mCore->mConsumerUsageBits; |
| break; |
| default: |
| return BAD_VALUE; |
| } |
| |
| BQ_LOGV("query: %d? %d", what, value); |
| *outValue = value; |
| return NO_ERROR; |
| } |
| |
| status_t BufferQueueProducer::connect(const sp<android::IBinder> &token, |
| int api, bool producerControlledByApp, QueueBufferOutput *output) { |
| ATRACE_CALL(); |
| Mutex::Autolock lock(mCore->mMutex); |
| mConsumerName = mCore->mConsumerName; |
| BQ_LOGV("connect(P): api=%d producerControlledByApp=%s", api, |
| producerControlledByApp ? "true" : "false"); |
| |
| // If we disconnect and reconnect quickly, we can be in a state where our |
| // slots are empty but we have many buffers in the queue. This can cause us |
| // to run out of memory if we outrun the consumer. Wait here if it looks |
| // like we have too many buffers queued up. |
| while (true) { |
| if (mCore->mIsAbandoned) { |
| BQ_LOGE("connect(P): BufferQueue has been abandoned"); |
| return NO_INIT; |
| } |
| |
| if (mCore->mConsumerListener == NULL) { |
| BQ_LOGE("connect(P): BufferQueue has no consumer"); |
| return NO_INIT; |
| } |
| |
| if (output == NULL) { |
| BQ_LOGE("connect(P): output was NULL"); |
| return BAD_VALUE; |
| } |
| |
| if (mCore->mConnectedApi != BufferQueueCore::NO_CONNECTED_API) { |
| BQ_LOGE("connect(P): already connected (cur=%d req=%d)", |
| mCore->mConnectedApi, api); |
| return BAD_VALUE; |
| } |
| |
| size_t maxBufferCount = mCore->getMaxBufferCountLocked(false); |
| if (mCore->mQueue.size() <= maxBufferCount) { |
| // The queue size seems small enough to proceed |
| // TODO: Make this bound tighter? |
| break; |
| } |
| |
| BQ_LOGV("connect(P): queue size is %d, waiting", mCore->mQueue.size()); |
| mCore->mDequeueCondition.wait(mCore->mMutex); |
| } |
| |
| int status = NO_ERROR; |
| switch (api) { |
| case NATIVE_WINDOW_API_EGL: |
| case NATIVE_WINDOW_API_CPU: |
| case NATIVE_WINDOW_API_MEDIA: |
| case NATIVE_WINDOW_API_CAMERA: |
| mCore->mConnectedApi = api; |
| output->inflate(mCore->mDefaultWidth, mCore->mDefaultHeight, |
| mCore->mTransformHint, mCore->mQueue.size()); |
| |
| // Set up a death notification so that we can disconnect |
| // automatically if the remote producer dies |
| if (token != NULL && token->remoteBinder() != NULL) { |
| status = token->linkToDeath( |
| static_cast<IBinder::DeathRecipient*>(this)); |
| if (status == NO_ERROR) { |
| mCore->mConnectedProducerToken = token; |
| } else { |
| BQ_LOGE("connect(P): linkToDeath failed: %s (%d)", |
| strerror(-status), status); |
| } |
| } |
| break; |
| default: |
| BQ_LOGE("connect(P): unknown API %d", api); |
| status = BAD_VALUE; |
| break; |
| } |
| |
| mCore->mBufferHasBeenQueued = false; |
| mCore->mDequeueBufferCannotBlock = |
| mCore->mConsumerControlledByApp && producerControlledByApp; |
| |
| return status; |
| } |
| |
| status_t BufferQueueProducer::disconnect(int api) { |
| ATRACE_CALL(); |
| BQ_LOGV("disconnect(P): api %d", api); |
| |
| int status = NO_ERROR; |
| sp<IConsumerListener> listener; |
| { // Autolock scope |
| Mutex::Autolock lock(mCore->mMutex); |
| |
| if (mCore->mIsAbandoned) { |
| // It's not really an error to disconnect after the surface has |
| // been abandoned; it should just be a no-op. |
| return NO_ERROR; |
| } |
| |
| switch (api) { |
| case NATIVE_WINDOW_API_EGL: |
| case NATIVE_WINDOW_API_CPU: |
| case NATIVE_WINDOW_API_MEDIA: |
| case NATIVE_WINDOW_API_CAMERA: |
| if (mCore->mConnectedApi == api) { |
| mCore->freeAllBuffersLocked(); |
| |
| // Remove our death notification callback if we have one |
| sp<IBinder> token = mCore->mConnectedProducerToken; |
| if (token != NULL) { |
| // This can fail if we're here because of the death |
| // notification, but we just ignore it |
| token->unlinkToDeath( |
| static_cast<IBinder::DeathRecipient*>(this)); |
| } |
| mCore->mConnectedProducerToken = NULL; |
| mCore->mConnectedApi = BufferQueueCore::NO_CONNECTED_API; |
| mCore->mDequeueCondition.broadcast(); |
| listener = mCore->mConsumerListener; |
| } else { |
| BQ_LOGE("disconnect(P): connected to another API " |
| "(cur=%d req=%d)", mCore->mConnectedApi, api); |
| status = BAD_VALUE; |
| } |
| break; |
| default: |
| BQ_LOGE("disconnect(P): unknown API %d", api); |
| status = BAD_VALUE; |
| break; |
| } |
| } // Autolock scope |
| |
| // Call back without lock held |
| if (listener != NULL) { |
| listener->onBuffersReleased(); |
| } |
| |
| return status; |
| } |
| |
| void BufferQueueProducer::binderDied(const wp<android::IBinder>& /* who */) { |
| // If we're here, it means that a producer we were connected to died. |
| // We're guaranteed that we are still connected to it because we remove |
| // this callback upon disconnect. It's therefore safe to read mConnectedApi |
| // without synchronization here. |
| int api = mCore->mConnectedApi; |
| disconnect(api); |
| } |
| |
| } // namespace android |