/* * Copyright 2013 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. */ #undef LOG_TAG #define LOG_TAG "BQInterposer" #include "BufferQueueInterposer.h" // --------------------------------------------------------------------------- namespace android { // --------------------------------------------------------------------------- #define BQI_LOGV(x, ...) ALOGV("[%s] "x, mName.string(), ##__VA_ARGS__) #define BQI_LOGD(x, ...) ALOGD("[%s] "x, mName.string(), ##__VA_ARGS__) #define BQI_LOGI(x, ...) ALOGI("[%s] "x, mName.string(), ##__VA_ARGS__) #define BQI_LOGW(x, ...) ALOGW("[%s] "x, mName.string(), ##__VA_ARGS__) #define BQI_LOGE(x, ...) ALOGE("[%s] "x, mName.string(), ##__VA_ARGS__) // Get an ID that's unique within this process. static int32_t createProcessUniqueId() { static volatile int32_t globalCounter = 0; return android_atomic_inc(&globalCounter); } BufferQueueInterposer::BufferQueueInterposer( const sp& sink, const String8& name) : mSink(sink), mName(name), mAcquired(false) { BQI_LOGV("BufferQueueInterposer sink=%p", sink.get()); // We need one additional dequeued buffer beyond what the source needs. // To have more than one (the default), we must call setBufferCount. But // we have no way of knowing what the sink has set as the minimum buffer // count, so if we just call setBufferCount(3) it may fail (and does, on // one device using a video encoder sink). So far on the devices we care // about, this is the smallest value that works. // // TODO: Change IGraphicBufferProducer and implementations to support this. // Maybe change it so both the consumer and producer declare how many // buffers they need, and the IGBP adds them? Then BQInterposer would just // add 1 to the source's buffer count. mSink->setBufferCount(6); } BufferQueueInterposer::~BufferQueueInterposer() { Mutex::Autolock lock(mMutex); flushQueuedBuffersLocked(); BQI_LOGV("~BufferQueueInterposer"); } status_t BufferQueueInterposer::requestBuffer(int slot, sp* outBuf) { BQI_LOGV("requestBuffer slot=%d", slot); Mutex::Autolock lock(mMutex); if (size_t(slot) >= mBuffers.size()) { size_t size = mBuffers.size(); mBuffers.insertAt(size, size - slot + 1); } sp& buf = mBuffers.editItemAt(slot); status_t result = mSink->requestBuffer(slot, &buf); *outBuf = buf; return result; } status_t BufferQueueInterposer::setBufferCount(int bufferCount) { BQI_LOGV("setBufferCount count=%d", bufferCount); Mutex::Autolock lock(mMutex); bufferCount += 1; status_t result = flushQueuedBuffersLocked(); if (result != NO_ERROR) return result; result = mSink->setBufferCount(bufferCount); if (result != NO_ERROR) return result; for (size_t i = 0; i < mBuffers.size(); i++) mBuffers.editItemAt(i).clear(); ssize_t n = mBuffers.resize(bufferCount); result = (n < 0) ? n : result; return result; } status_t BufferQueueInterposer::dequeueBuffer(int* slot, sp* fence, uint32_t w, uint32_t h, uint32_t format, uint32_t usage) { BQI_LOGV("dequeueBuffer %ux%u fmt=%u usage=%#x", w, h, format, usage); return mSink->dequeueBuffer(slot, fence, w, h, format, usage); } status_t BufferQueueInterposer::queueBuffer(int slot, const QueueBufferInput& input, QueueBufferOutput* output) { BQI_LOGV("queueBuffer slot=%d", slot); Mutex::Autolock lock(mMutex); mQueue.push(QueuedBuffer(slot, input)); *output = mQueueBufferOutput; return NO_ERROR; } void BufferQueueInterposer::cancelBuffer(int slot, const sp& fence) { BQI_LOGV("cancelBuffer slot=%d", slot); mSink->cancelBuffer(slot, fence); } int BufferQueueInterposer::query(int what, int* value) { BQI_LOGV("query what=%d", what); return mSink->query(what, value); } status_t BufferQueueInterposer::setSynchronousMode(bool enabled) { BQI_LOGV("setSynchronousMode %s", enabled ? "true" : "false"); return mSink->setSynchronousMode(enabled); } status_t BufferQueueInterposer::connect(int api, QueueBufferOutput* output) { BQI_LOGV("connect api=%d", api); Mutex::Autolock lock(mMutex); status_t result = mSink->connect(api, &mQueueBufferOutput); if (result == NO_ERROR) { *output = mQueueBufferOutput; } return result; } status_t BufferQueueInterposer::disconnect(int api) { BQI_LOGV("disconnect: api=%d", api); Mutex::Autolock lock(mMutex); flushQueuedBuffersLocked(); return mSink->disconnect(api); } status_t BufferQueueInterposer::pullEmptyBuffer() { status_t result; int slot; sp fence; result = dequeueBuffer(&slot, &fence, 0, 0, 0, 0); if (result == IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION) { sp buffer; result = requestBuffer(slot, &buffer); } else if (result != NO_ERROR) { return result; } uint32_t w, h, transformHint, numPendingBuffers; mQueueBufferOutput.deflate(&w, &h, &transformHint, &numPendingBuffers); IGraphicBufferProducer::QueueBufferInput qbi(0, Rect(w, h), NATIVE_WINDOW_SCALING_MODE_FREEZE, 0, fence); IGraphicBufferProducer::QueueBufferOutput qbo; result = queueBuffer(slot, qbi, &qbo); if (result != NO_ERROR) return result; return NO_ERROR; } status_t BufferQueueInterposer::acquireBuffer(sp* buf, sp* fence) { Mutex::Autolock lock(mMutex); if (mQueue.empty()) { BQI_LOGV("acquireBuffer: no buffers available"); return NO_BUFFER_AVAILABLE; } if (mAcquired) { BQI_LOGE("acquireBuffer: buffer already acquired"); return BUFFER_ALREADY_ACQUIRED; } BQI_LOGV("acquireBuffer: acquiring slot %d", mQueue[0].slot); *buf = mBuffers[mQueue[0].slot]; *fence = mQueue[0].fence; mAcquired = true; return NO_ERROR; } status_t BufferQueueInterposer::releaseBuffer(const sp& fence) { Mutex::Autolock lock(mMutex); if (!mAcquired) { BQI_LOGE("releaseBuffer: releasing a non-acquired buffer"); return BUFFER_NOT_ACQUIRED; } BQI_LOGV("releaseBuffer: releasing slot %d to sink", mQueue[0].slot); const QueuedBuffer& b = mQueue[0]; status_t result = mSink->queueBuffer(b.slot, QueueBufferInput(b.timestamp, b.crop, b.scalingMode, b.transform, b.fence), &mQueueBufferOutput); mQueue.removeAt(0); mAcquired = false; return result; } status_t BufferQueueInterposer::flushQueuedBuffersLocked() { if (mAcquired) { BQI_LOGE("flushQueuedBuffersLocked: buffer acquired, can't flush"); return INVALID_OPERATION; } status_t result = NO_ERROR; for (size_t i = 0; i < mQueue.size(); i++) { const QueuedBuffer& b = mQueue[i]; BQI_LOGV("flushing queued slot %d to sink", b.slot); status_t err = mSink->queueBuffer(b.slot, QueueBufferInput(b.timestamp, b.crop, b.scalingMode, b.transform, b.fence), &mQueueBufferOutput); if (err != NO_ERROR && result == NO_ERROR) // latch first error result = err; } mQueue.clear(); return result; } // --------------------------------------------------------------------------- } // namespace android // ---------------------------------------------------------------------------