必读:

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

到此,就讲完了,感觉好乏味...,没有什么实质的东西

Android 12 Google将buffer queue组件从SurfaceFlinger端移动到了客户端(旧版本是在BufferQueueLayer中去createBufferQueue的)。
buffer queue组件的创建和初始化也放在BLASTBufferQueue中。通过类名可以看出BLASTBufferQueue更像是buffer queue组件的一层封装或装饰。
通过前面系列文章的分析,可以看到,整个生产消费模型都在客户端,图形缓冲区的出队、入队、获取等操作都在客户端完成,预示着producer -- buffer queue -- consumer 间的通信都变成了本地通信。
BLASTBufferQueue需要通过事务Transaction来向SurfaceFlinger端提交Buffer与图层的属性。
 
如本文讲的 disconnect event 在12平台就无法传递到SurfaceFlinger了。如要传递信息,就要使用 Transaction 传递过去。

不过我还是有个疑问:disconnect过程中,可以看到producer/consumer/surface都有去free buffer,此时,为GraphicBuffer分配的内存真的就释放了吗?

(mSlots[slot].mGraphicBuffer.clear() or  mSlots[slotIndex].mGraphicBuffer = nullptr)

四、小结


感觉没啥说的了,就此结束吧

Android 12(S) 图形显示系统 - Surface 一点补充知识(十二)的更多相关文章

  1. Android 12(S) 图形显示系统 - 初识ANativeWindow/Surface/SurfaceControl(七)

    题外话 "行百里者半九十",是说步行一百里路,走过九十里,只能算是走了一半.因为步行越接近目的地,走起来越困难.借指凡事到了接近成功,往往是最吃力.最艰难的时段.劝人做事贵在坚持, ...

  2. Android 12(S) 图形显示系统 - 示例应用(二)

    1 前言 为了更深刻的理解Android图形系统抽象的概念和BufferQueue的工作机制,这篇文章我们将从Native Level入手,基于Android图形系统API写作一个简单的图形处理小程序 ...

  3. Android 12(S) 图形显示系统 - SurfaceFlinger的启动和消息队列处理机制(四)

    1 前言 SurfaceFlinger作为Android图形显示系统处理逻辑的核心单元,我们有必要去了解其是如何启动,初始化及进行消息处理的.这篇文章我们就来简单分析SurfaceFlinger这个B ...

  4. Android 12(S) 图形显示系统 - createSurface的流程(五)

    题外话 刚刚开始着笔写作这篇文章时,正好看电视在采访一位92岁的考古学家,在他的日记中有这样一句话,写在这里与君共勉"不要等待幸运的降临,要去努力的掌握知识".如此朴实的一句话,此 ...

  5. Android 12(S) 图形显示系统 - BufferQueue的工作流程(九)

    题外话 Covid-19疫情的强烈反弹,小区里检测出了无症状感染者.小区封闭管理,我也不得不居家办公了.既然这么大把的时间可以光明正大的宅家里,自然要好好利用,八个字 == 努力工作,好好学习 一.前 ...

  6. Android 12(S) 图形显示系统 - 解读Gralloc架构及GraphicBuffer创建/传递/释放(十四)

    必读: Android 12(S) 图形显示系统 - 开篇 一.前言 在前面的文章中,已经出现过 GraphicBuffer 的身影,GraphicBuffer 是Android图形显示系统中的一个重 ...

  7. Android 12(S) 图形显示系统 - 基本概念(一)

    1 前言 Android图形系统是系统框架中一个非常重要的子系统,与其它子系统一样,Android 框架提供了各种用于 2D 和 3D 图形渲染的 API供开发者使用来创建绚丽多彩的应用APP.图形渲 ...

  8. Android 12(S) 图形显示系统 - 应用建立和SurfaceFlinger的沟通桥梁(三)

    1 前言 上一篇文章中我们已经创建了一个Native示例应用,从使用者的角度了解了图形显示系统API的基本使用,从这篇文章开始我们将基于这个示例应用深入图形显示系统API的内部实现逻辑,分析运作流程. ...

  9. Android 12(S) 图形显示系统 - BufferQueue/BLASTBufferQueue之初识(六)

    题外话 你有没有听见,心里有一声咆哮,那一声咆哮,它好像在说:我就是要从后面追上去! 写文章真的好痛苦,特别是自己对这方面的知识也一知半解就更加痛苦了.这已经是这个系列的第六篇了,很多次都想放弃了,但 ...

随机推荐

  1. 5.1 Paralles 为虚拟机设置静态ip

    一.配置独立ip的环境 前面研究了k8s的组件,核心概念,网络模型,都了解了一遍,现在就要进入实战了,摆在面前的一座大山,必须克服才行---虚拟机搭建,及网络环境配置. 先说说本文的运行环境: mac ...

  2. Solution -「JOISC 2021」「LOJ #3489」饮食区

    \(\mathcal{Description}\)   Link.   呐--不想概括题意,自己去读叭~ \(\mathcal{Solution}\)   如果仅有 1. 3. 操作,能不能做?    ...

  3. 通过修改注册表将右alt键映射为application键

    通过修改注册表将右alt键映射为application键的方法有许多键盘没有APPLICATION(上下文菜单)键,本文将教您如何把右ALT键映射为apps键.1.映射请将以下注册表信息用记事本保存为 ...

  4. 深入MySQL(一):MySQL的组织架构

    今天开始将自己所学过的MySQL的知识都尝试融会贯通,并且用写博客的方式记录分享下来. 今天讲的主题是MySQL的组织架构,对于学习一个中间件或者开源项目而言,我觉得最重要的便是先知晓其组织架构,以一 ...

  5. 2.go-GIN快速入门

    一.go mod安装 1.为什么要安装go mod 在项目中需要某个依赖包,必须使用go get安装,否则运行go run main.go会报错.这种方式在依赖包较多时不便于管理,于是在go1.11版 ...

  6. k8s中运行busybox

    简介 参考百度百科 BusyBox 是一个集成了三百多个最常用Linux命令和工具的软件. BusyBox 包含了一些简单的工具,例如ls.cat和echo等等,还包含了一些更大.更复杂的工具,例gr ...

  7. ElasticSearch学习笔记(详细)

    目录 ElasticSearch概述 ElasticSearch入门 安装 基本操作 查看es相关信息 索引操作 文档操作 bulk批量API 进阶检索 Search API Query DSL 分词 ...

  8. Pandas:将DataFrame中的一列转化为List

    #假设data是一个DataFrame对象,如果要把它的第二列转换为List print(data.iloc[:,1].to_list())

  9. Qt:输出为CSV文件时汉字乱码

    参考 (18条消息) QT5写csv文件,文件打开后中文显示乱码的问题解决_yanzi150207348的博客-CSDN博客 解决方法 1.在文件开头写一段: #if _MSC_VER >= 1 ...

  10. omnet++:官方文档翻译总结(三)

    翻译总结自:Turning it Into a Real Network - OMNeT++ Technical Articles 接官方文档翻译总结(二),本节主要是真实网络的搭建 Part 4 - ...