必读:

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. Solution -「BZOJ 3812」主旋律

    \(\mathcal{Description}\)   Link.   给定含 \(n\) 个点 \(m\) 条边的简单有向图 \(G=(V,E)\),求 \(H=(V,E'\subseteq E)\ ...

  2. 实战OutOfMemoryError异常

    深入理解JVM的一个重要目的就是解决实际生产中的出现的异常,并能从根上处理问题.JVM定义的异常有哪些呢?见下表: 除了计数器未定义异常,其他都会有OutofmemoryError的异常,下面就实战这 ...

  3. blender导入灰度图生成地形模型

    安装软件 在此处下载blender并安装. 添加平面 1.打开blender,右键删除初始的立方体. 2.shift+a选择平面添加进场景: 3.按下s键鼠标拖动调节平面大小确定后按下鼠标左键: 4. ...

  4. [杂记]LeTeX中使用tikz画图的一些实例

    出处:http://www.texample.net/tikz/examples/all/ 上述网站中的实例非常丰富,将代码修改一下再编译就能明白是什么意思,故不再赘述.

  5. [题解]RQNOJ PID87 过河

    链接:http://www.rqnoj.cn/problem/87 思路:动态规划 定义f[i][j]表示到达第 i 块给定石头用了 j 块添加石头的最少步数. 转移方程:f[i][j]=min{f[ ...

  6. 8款国内外主流商业智能BI工具分析,助你轻松选型!

    BI(Business Intelligence),即商业智能或商务智能,它是一套完整的解决方案,用来将企业中现有的数据进行有效的整合,快速准确的提供报表并提出决策依据,帮助企业做出明智的业务经营决策 ...

  7. centos网络配置、虚拟机克隆

    查看网卡命令: ifconfig 查看网络配置 ifconfig -a 查看隐藏网卡 window下使用ipconfig 网络配置 设置静态IP,修改/etc/sysconfig/network-sc ...

  8. 使用Xshell连接VMware上的Linux虚拟机

    转至:https://www.cnblogs.com/yenengfeng/p/13684265.html 虚拟机自己安装好,这边直接说步骤.有借鉴这篇文章 https://www.cnblogs.c ...

  9. 用RecyclerView实现列表视图

    RecyclerView能够灵活实现大数据集的展示,视图的复用管理比ListView更好,能够显示列表.网格.瀑布流等形式,且不同的ViewHolder能够实现item多元化的功能.但是使用起来会稍微 ...

  10. 在vue项目中MD5加密的使用方法

    1:安装 npm install --save js-md5 2.引入及使用 方法一:在需要的项目文件中引入 import md5 from 'js-md5'; 使用例子:md5('hello wor ...