Android 12(S) 圖形顯示系統 – Surface 一點補充知識(十二)
- 2022 年 3 月 24 日
- 筆記
- Android 圖形影像, Android圖形影像
必讀:
Android 12(S) 圖形顯示系統 – 開篇
一、前言
因為個人工作主要是Android多媒體播放的內容,在工作中查看源碼或設計程式經常會遇到調用API:
static inline int native_window_api_connect(struct ANativeWindow* window, int api)
static inline int native_window_api_disconnect(struct ANativeWindow* window, int api)
所以也一直好奇這兩個方法都做了什麼事情?這篇文章就來一探究竟。
二、native_window_api_connect 解析
Android系統中,開始播放影片並設置Surface後,都會做一次 connectToSurface 的操作,比如MediaCodec中,在初始化階段setSurface後就會調用方法:
status_t MediaCodec::connectToSurface(const sp<Surface> &surface) {
...
err = nativeWindowConnect(surface.get(), "connectToSurface");
...
}
這裡就是去調用了 /frameworks/av/media/libstagefright/SurfaceUtils.cpp 中的方法:
status_t nativeWindowConnect(ANativeWindow *surface, const char *reason) {
ALOGD("connecting to surface %p, reason %s", surface, reason);
status_t err = native_window_api_connect(surface, NATIVE_WINDOW_API_MEDIA);
ALOGE_IF(err != OK, "Failed to connect to surface %p, err %d", surface, err);
return err;
}
是不是看到了 native_window_api_connect ,其中參數 NATIVE_WINDOW_API_MEDIA 表明 video decoder會作為生產者來生成buffer數據。
再接著往下走,看看到底做了什麼?
大體的調用流程如下:
/frameworks/native/libs/gui/Surface.cpp
/frameworks/native/libs/gui/BufferQueueProducer.cpp
>>> static inline int native_window_api_connect(struct ANativeWindow* window, int api)
>>> int Surface::hook_perform(ANativeWindow* window, int operation, …)
>>> int Surface::perform(int operation, va_list args)
>>> int Surface::dispatchConnect(va_list args)
>>> int Surface::connect(int api)
>>> int Surface::connect(int api, const sp<IProducerListener>& listener)
>>> int Surface::connect(int api, const sp<IProducerListener>& listener, bool reportBufferRemoval)
>>> status_t BufferQueueProducer::connect(const sp<IProducerListener>& listener,
int api, bool producerControlledByApp, QueueBufferOutput *output)
最終進入到了BufferQueueProducer::connect函數中,看起來這裡應該就是做具體事情的地方了
老規矩,看源碼:
status_t BufferQueueProducer::connect(const sp<IProducerListener>& listener,
int api, bool producerControlledByApp, QueueBufferOutput *output) {
ATRACE_CALL();
std::lock_guard<std::mutex> lock(mCore->mMutex);
mConsumerName = mCore->mConsumerName; // 消費者名字
BQ_LOGV("connect: api=%d producerControlledByApp=%s", api,
producerControlledByApp ? "true" : "false");
// 對一些條件進行判斷,必要時直接返回return
if (mCore->mIsAbandoned) {
BQ_LOGE("connect: BufferQueue has been abandoned");
return NO_INIT;
}
if (mCore->mConsumerListener == nullptr) {
BQ_LOGE("connect: BufferQueue has no consumer");
return NO_INIT;
}
if (output == nullptr) {
BQ_LOGE("connect: output was NULL");
return BAD_VALUE;
}
if (mCore->mConnectedApi != BufferQueueCore::NO_CONNECTED_API) {
BQ_LOGE("connect: already connected (cur=%d req=%d)",
mCore->mConnectedApi, api);
return BAD_VALUE;
}
int delta = mCore->getMaxBufferCountLocked(mCore->mAsyncMode,
mDequeueTimeout < 0 ?
mCore->mConsumerControlledByApp && producerControlledByApp : false,
mCore->mMaxBufferCount) -
mCore->getMaxBufferCountLocked();
if (!mCore->adjustAvailableSlotsLocked(delta)) {
BQ_LOGE("connect: BufferQueue failed to adjust the number of available "
"slots. Delta = %d", delta);
return BAD_VALUE;
}
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->width = mCore->mDefaultWidth;
output->height = mCore->mDefaultHeight;
output->transformHint = mCore->mTransformHintInUse = mCore->mTransformHint;
output->numPendingBuffers =
static_cast<uint32_t>(mCore->mQueue.size());
output->nextFrameNumber = mCore->mFrameCounter + 1;
output->bufferReplaced = false;
output->maxBufferCount = mCore->mMaxBufferCount;
if (listener != nullptr) {
// Set up a death notification so that we can disconnect
// automatically if the remote producer dies
#ifndef NO_BINDER
if (IInterface::asBinder(listener)->remoteBinder() != nullptr) {
status = IInterface::asBinder(listener)->linkToDeath(
static_cast<IBinder::DeathRecipient*>(this));
if (status != NO_ERROR) {
BQ_LOGE("connect: linkToDeath failed: %s (%d)",
strerror(-status), status);
}
mCore->mLinkedToDeath = listener;
}
#endif
mCore->mConnectedProducerListener = listener; // 設置producer listener
mCore->mBufferReleasedCbEnabled = listener->needsReleaseNotify();
}
break;
default:
BQ_LOGE("connect: unknown API %d", api);
status = BAD_VALUE;
break;
}
mCore->mConnectedPid = BufferQueueThreadState::getCallingPid();
mCore->mBufferHasBeenQueued = false;
mCore->mDequeueBufferCannotBlock = false;
mCore->mQueueBufferCanDrop = false;
mCore->mLegacyBufferDrop = true;
if (mCore->mConsumerControlledByApp && producerControlledByApp) {
mCore->mDequeueBufferCannotBlock = mDequeueTimeout < 0;
mCore->mQueueBufferCanDrop = mDequeueTimeout <= 0;
}
mCore->mAllowAllocation = true; // 允許分配 graphic buffer
VALIDATE_CONSISTENCY();
return status;
}
我才疏學淺,按我理解,就是完成了一些初始化的操作,貌似也沒啥了,這之後應該produder就可以 dequeue buffer 了
另外一點,這裡有設置producer listener,之前文章中也講過,貌似也沒啥作用(也許我錯了)
mCore->mConnectedProducerListener = listener;
mCore->mBufferReleasedCbEnabled = listener->needsReleaseNotify();
三、native_window_api_disconnect 解析
disconnect的調用流程和connect的流程類似。
先看 Surface::disconnect 中做了啥
int Surface::disconnect(int api, IGraphicBufferProducer::DisconnectMode mode) {
ATRACE_CALL();
ALOGV("Surface::disconnect");
Mutex::Autolock lock(mMutex);
mRemovedBuffers.clear();
mSharedBufferSlot = BufferItem::INVALID_BUFFER_SLOT;
mSharedBufferHasBeenQueued = false;
freeAllBuffers();
int err = mGraphicBufferProducer->disconnect(api, mode);
if (!err) {
mReqFormat = 0;
mReqWidth = 0;
mReqHeight = 0;
mReqUsage = 0;
mCrop.clear();
mScalingMode = NATIVE_WINDOW_SCALING_MODE_FREEZE;
mTransform = 0;
mStickyTransform = 0;
mAutoPrerotation = false;
mEnableFrameTimestamps = false;
mMaxBufferCount = NUM_BUFFER_SLOTS;
if (api == NATIVE_WINDOW_API_CPU) {
mConnectedToCpu = false;
}
}
return err;
}
看起來主要是把reset一些變數和release一些資源,看到調用
freeAllBuffers();
int err = mGraphicBufferProducer->disconnect(api, mode)
freeAllBuffers是把mSlots裡面的元素都置為 nullptr
void Surface::freeAllBuffers() {
if (!mDequeuedSlots.empty()) {
ALOGE("%s: %zu buffers were freed while being dequeued!",
__FUNCTION__, mDequeuedSlots.size());
}
for (int i = 0; i < NUM_BUFFER_SLOTS; i++) {
mSlots[i].buffer = nullptr;
}
}
本文作者@二的次方 2022-03-24 發佈於部落格園
BufferQueueProducer::disconnect源碼如下
status_t BufferQueueProducer::disconnect(int api, DisconnectMode mode) {
ATRACE_CALL();
BQ_LOGV("disconnect: api %d", api);
int status = NO_ERROR;
sp<IConsumerListener> listener;
{ // Autolock scope
std::unique_lock<std::mutex> lock(mCore->mMutex);
if (mode == DisconnectMode::AllLocal) {
if (BufferQueueThreadState::getCallingPid() != mCore->mConnectedPid) {
return NO_ERROR;
}
api = BufferQueueCore::CURRENTLY_CONNECTED_API;
}
mCore->waitWhileAllocatingLocked(lock);
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;
}
if (api == BufferQueueCore::CURRENTLY_CONNECTED_API) {
if (mCore->mConnectedApi == NATIVE_WINDOW_API_MEDIA) {
ALOGD("About to force-disconnect API_MEDIA, mode=%d", mode);
}
api = mCore->mConnectedApi;
// If we're asked to disconnect the currently connected api but
// nobody is connected, it's not really an error.
if (api == BufferQueueCore::NO_CONNECTED_API) {
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();
#ifndef NO_BINDER
// Remove our death notification callback if we have one
if (mCore->mLinkedToDeath != nullptr) {
sp<IBinder> token =
IInterface::asBinder(mCore->mLinkedToDeath);
// This can fail if we're here because of the death
// notification, but we just ignore it
token->unlinkToDeath(
static_cast<IBinder::DeathRecipient*>(this));
}
#endif
mCore->mSharedBufferSlot =
BufferQueueCore::INVALID_BUFFER_SLOT;
mCore->mLinkedToDeath = nullptr;
mCore->mConnectedProducerListener = nullptr;
mCore->mConnectedApi = BufferQueueCore::NO_CONNECTED_API;
mCore->mConnectedPid = -1;
mCore->mSidebandStream.clear();
mCore->mDequeueCondition.notify_all();
mCore->mAutoPrerotation = false;
listener = mCore->mConsumerListener;
} else if (mCore->mConnectedApi == BufferQueueCore::NO_CONNECTED_API) {
BQ_LOGE("disconnect: not connected (req=%d)", api);
status = NO_INIT;
} else {
BQ_LOGE("disconnect: still connected to another API "
"(cur=%d req=%d)", mCore->mConnectedApi, api);
status = BAD_VALUE;
}
break;
default:
BQ_LOGE("disconnect: unknown API %d", api);
status = BAD_VALUE;
break;
}
} // Autolock scope
// Call back without lock held
if (listener != nullptr) {
listener->onBuffersReleased();
listener->onDisconnect();
}
return status;
}
看樣子也是reset一些變數和release資源。
調用了mCore->freeAllBuffersLocked()
void BufferQueueCore::freeAllBuffersLocked() {
for (int s : mFreeSlots) {
clearBufferSlotLocked(s);
}
for (int s : mFreeBuffers) {
mFreeSlots.insert(s);
clearBufferSlotLocked(s);
}
mFreeBuffers.clear();
for (int s : mActiveBuffers) {
mFreeSlots.insert(s);
clearBufferSlotLocked(s);
}
mActiveBuffers.clear();
for (auto& b : mQueue) {
b.mIsStale = true;
b.mAcquireCalled = false;
}
VALIDATE_CONSISTENCY();
}
通知了消費者 listener->onBuffersReleased() and listener->onDisconnect()
消費者響應 onBuffersReleased 也是去free buffer
void ConsumerBase::onBuffersReleased() {
Mutex::Autolock lock(mMutex);
CB_LOGV("onBuffersReleased");
if (mAbandoned) {
// Nothing to do if we're already abandoned.
return;
}
uint64_t mask = 0;
mConsumer->getReleasedBuffers(&mask);
for (int i = 0; i < BufferQueue::NUM_BUFFER_SLOTS; i++) {
if (mask & (1ULL << i)) {
freeBufferLocked(i);
}
}
}
消費者響應 onDisconnect
void BLASTBufferItemConsumer::onDisconnect() {
Mutex::Autolock lock(mMutex);
mPreviouslyConnected = mCurrentlyConnected;
mCurrentlyConnected = false;
if (mPreviouslyConnected) {
mDisconnectEvents.push(mCurrentFrameNumber);
}
mFrameEventHistory.onDisconnect();
}
到此,就講完了,感覺好乏味…,沒有什麼實質的東西
不過我還是有個疑問:disconnect過程中,可以看到producer/consumer/surface都有去free buffer,此時,為GraphicBuffer分配的記憶體真的就釋放了嗎?
(mSlots[slot].mGraphicBuffer.clear() or mSlots[slotIndex].mGraphicBuffer = nullptr)
四、小結
感覺沒啥說的了,就此結束吧