安卓框架,分析專案中surfaceFlinger出現的bug ---queueBuffer: BufferQueue has been abandoned
播放視訊切換頁面後返回發現surfaceview黑屏了,錯誤日誌如下
E/BufferQueueProducer: queueBuffer: BufferQueue has been abandoned
看下日誌來源
//BufferQueueProducer.cpp 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); mCore->waitWhileAllocatingLocked(); 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; BQ_LOGV("dequeueBuffer: allocating a new buffer for slot %d", *outSlot); 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 } //dequeue
if (mCore->mIsAbandoned) {
BQ_LOGE("dequeueBuffer: BufferQueue has been abandoned");
return NO_INIT;
}
看看mCore->mIsAbandoned 是什麼,機翻下
// mIsAbandoned indicates that the BufferQueue will no longer be used to // consume image buffers pushed to it using the IGraphicBufferProducer // interface. It is initialized to false, and set to true in the // consumerDisconnect method. A BufferQueue that is abandoned will return // the NO_INIT error from all IGraphicBufferProducer methods capable of // returning an error. mIsAbandoned 指示 BufferQueue 將不再用於 使用 IGraphicBufferProducerinterface將image buffers推送到BufferQueue mIsAbandoned 被初始化為false,並在consumerDisconnect method中設定為真 被丟棄的BufferQueue 會返回 NO_INIT error,這個錯誤來自可以返回返回 error 的所有IGraphicBufferProducer methods bool mIsAbandoned;
翻譯不太瞭解,但是有關鍵的點 並在consumerDisconnect method中設定為真,就是說必須經過consumerDisconnect 才會設定為true。
看下consumerDisconnect
IGraphicBufferConsumer.h
// consumerDisconnect disconnects a consumer from the BufferQueue. All // buffers will be freed and the BufferQueue is placed in the "abandoned" // state, causing most interactions with the BufferQueue by the producer to // fail. // // Return of a value other than NO_ERROR means an error has occurred: // * BAD_VALUE - no consumer is currently connected consumerDisconnect 將consumer 從緩衝佇列中分離出來 所有BufferQueue 將被釋放,BufferQueue 被放置在“abandoned”狀態中。 導致生產者與BufferQueue 的大部分互動作用失敗了。 除了NO_ERROR 以外的值的返回意味著發生了一個錯誤: * BAD_VALUE - no消費者是當前連線 virtual status_t consumerDisconnect() = 0;
注意“所有BufferQueue 將被釋放”,我們知道建立surface過程就涉及到buferr的建立,那麼buffer被釋放時也應當是surface被銷燬了。
BufferQueueConsumer.h
virtual status_t consumerDisconnect() { return disconnect(); }
BufferQueueConsumer.cpp
status_t BufferQueueConsumer::disconnect() {
ATRACE_CALL();
BQ_LOGV("disconnect(C)");
Mutex::Autolock lock(mCore->mMutex);
if (mCore->mConsumerListener == NULL) {
BQ_LOGE("disconnect(C): no consumer is connected");
return BAD_VALUE;
}
//找到了mIsAbandoned
mCore->mIsAbandoned = true;
mCore->mConsumerListener = NULL;
mCore->mQueue.clear();
mCore->freeAllBuffersLocked();
mCore->mDequeueCondition.broadcast();
return NO_ERROR;
}
找到了mCore->mIsAbandoned = true 接下來分析consumerDisconnect是何時被呼叫,前面猜測是銷燬surface(移除layer)的時候呼叫,接下來看下surfaceflinger移除layer流程
參考https://blog.csdn.net/woai110120130/article/details/79112528
void SurfaceFlinger::onMessageReceived(int32_t what) {
ATRACE_CALL();
switch (what) {
case MessageQueue::TRANSACTION: {
handleMessageTransaction();
break;
}
case MessageQueue::INVALIDATE: {
bool refreshNeeded = handleMessageTransaction();
refreshNeeded |= handleMessageInvalidate();
refreshNeeded |= mRepaintEverything;
if (refreshNeeded) {
// Signal a refresh if a transaction modified the window state,
// a new buffer was latched, or if HWC has requested a full
// repaint
signalRefresh();
}
break;
}
case MessageQueue::REFRESH: {
handleMessageRefresh();
break;
}
}
}
bool SurfaceFlinger::handleMessageTransaction() {
uint32_t transactionFlags = peekTransactionFlags(eTransactionMask);
if (transactionFlags) {
handleTransaction(transactionFlags);
return true;
}
return false;
}
void SurfaceFlinger::handleTransaction(uint32_t transactionFlags)
{
ATRACE_CALL();
// here we keep a copy of the drawing state (that is the state that's
// going to be overwritten by handleTransactionLocked()) outside of
// mStateLock so that the side-effects of the State assignment
// don't happen with mStateLock held (which can cause deadlocks).
State drawingState(mDrawingState);
Mutex::Autolock _l(mStateLock);
const nsecs_t now = systemTime();
mDebugInTransaction = now;
// Here we're guaranteed that some transaction flags are set
// so we can call handleTransactionLocked() unconditionally.
// We call getTransactionFlags(), which will also clear the flags,
// with mStateLock held to guarantee that mCurrentState won't change
// until the transaction is committed.
transactionFlags = getTransactionFlags(eTransactionMask);
handleTransactionLocked(transactionFlags);
mLastTransactionTime = systemTime() - now;
mDebugInTransaction = 0;
invalidateHwcGeometry();
// here the transaction has been committed
}
void SurfaceFlinger::handleTransactionLocked(uint32_t transactionFlags)
{
//省略
commitTransaction();
updateCursorAsync();
}
void SurfaceFlinger::commitTransaction()
{
if (!mLayersPendingRemoval.isEmpty()) {
// Notify removed layers now that they can't be drawn from
for (size_t i = 0; i < mLayersPendingRemoval.size(); i++) {
mLayersPendingRemoval[i]->onRemoved();
}
mLayersPendingRemoval.clear();
}
// If this transaction is part of a window animation then the next frame
// we composite should be considered an animation as well.
mAnimCompositionPending = mAnimTransactionPending;
mDrawingState = mCurrentState;
mTransactionPending = false;
mAnimTransactionPending = false;
mTransactionCV.broadcast();
}
其實移除layer的點就在commitTransaction mLayersPendingRemoval[i]->onRemoved();呼叫Layer.cpp的OnRemoved
void Layer::onRemoved() {
mSurfaceFlingerConsumer->abandon();
}
void ConsumerBase::abandon() {
CB_LOGV("abandon");
Mutex::Autolock lock(mMutex);
if (!mAbandoned) {
abandonLocked();
mAbandoned = true;
}
}
void ConsumerBase::abandonLocked() {
CB_LOGV("abandonLocked");
for (int i =0; i < BufferQueue::NUM_BUFFER_SLOTS; i++) {
freeBufferLocked(i);
}
// disconnect from the BufferQueue
//發現consumerDisconnect
mConsumer->consumerDisconnect();
mConsumer.clear();
}
通過以上分析我們現在知道E/BufferQueueProducer: queueBuffer: BufferQueue has been abandoned這個日誌的觸發條件是
1.app還在呼叫dequebuffer,即Mediaplayer還在作用。
2.surface的銷燬,layer的移除。
那樣解決方案很明瞭,在不使用視屏播放時必須把Mediaplayer和surface銷燬,或者說surface銷燬前Mediaplayer必須先銷燬。