SurfaceTexture OnFrameAvailableListener 调用流程分析

背景:

最近项目中遇到一个问题, 需要搞清楚OnFrameAvailableListener 回调流程, 本文借此机会做个记录, 巩固印象, 有相关困惑的同学也可以参考下.

本文基于Android 14 framework 源码进行分析

SurfaceTexture.java

OnFrameAvailableListener 设置过程

 public void setOnFrameAvailableListener(@Nullable final OnFrameAvailableListener listener,@Nullable Handler handler) {if (listener != null) {// Although we claim the thread is arbitrary, earlier implementation would// prefer to send the callback on the creating looper or the main looper// so we preserve this behavior here.Looper looper = handler != null ? handler.getLooper() :mCreatorLooper != null ? mCreatorLooper : Looper.getMainLooper();mOnFrameAvailableHandler = new Handler(looper, null, true /*async*/) {@Overridepublic void handleMessage(Message msg) {listener.onFrameAvailable(SurfaceTexture.this);}};} else {mOnFrameAvailableHandler = null;}}

一直很好奇,硬件解码后 回调是怎么调用的, 首先java在设置回调的时候,回看调用方是不是有handler, 如果没有handler 会发送到主线程执行, 如果有handler,就会把回调发送 这个handler执行,接下来来看下这个mOnFrameAvailableHandler在什么地方调用

mOnFrameAvailableHandler

private static void postEventFromNative(WeakReference<SurfaceTexture> weakSelf) {SurfaceTexture st = weakSelf.get();if (st != null) {Handler handler = st.mOnFrameAvailableHandler;if (handler != null) {handler.sendEmptyMessage(0);}}
}

native 会调用这个方法, 然后这个方法在handler里边发送消息,通知回调,接下来看下native是怎么调用的

postEventFromNative

static void SurfaceTexture_classInit(JNIEnv* env, jclass clazz)
{fields.postEvent = env->GetStaticMethodID(clazz, "postEventFromNative","(Ljava/lang/ref/WeakReference;)V");if (fields.postEvent == NULL) {ALOGE("can't find android/graphics/SurfaceTexture.postEventFromNative");}
}
class JNISurfaceTextureContextCommon {
public:JNISurfaceTextureContextCommon(JNIEnv* env, jobject weakThiz, jclass clazz): mWeakThiz(env->NewGlobalRef(weakThiz)), mClazz((jclass)env->NewGlobalRef(clazz)) {}...void onFrameAvailable(const BufferItem& item) {JNIEnv* env = getJNIEnv();if (env != NULL) {env->CallStaticVoidMethod(mClazz, fields.postEvent, mWeakThiz);} else {ALOGW("onFrameAvailable event will not posted");}}protected:....jobject mWeakThiz;jclass mClazz;
};
class JNISurfaceTextureContextFrameAvailableListener: public JNISurfaceTextureContextCommon,public SurfaceTexture::FrameAvailableListener {
public:JNISurfaceTextureContextFrameAvailableListener(JNIEnv* env, jobject weakThiz, jclass clazz): JNISurfaceTextureContextCommon(env, weakThiz, clazz) {}void onFrameAvailable(const BufferItem& item) override {JNISurfaceTextureContextCommon::onFrameAvailable(item);}
};

JNISurfaceTextureContextFrameAvailableListener 集成了JNISurfaceTextureContextCommon,SurfaceTexture::FrameAvailableListener, 并实现了方法onFrameAvailable,再通过jni去调用java的postEventFromNative, 接下来看 JNISurfaceTextureContextFrameAvailableListener 设置到那里的

JNISurfaceTextureContextFrameAvailableListener

static void SurfaceTexture_init(JNIEnv* env, jobject thiz, jboolean isDetached,jint texName, jboolean singleBufferMode, jobject weakThiz)
{sp<IGraphicBufferProducer> producer;sp<IGraphicBufferConsumer> consumer;BufferQueue::createBufferQueue(&producer, &consumer);sp<SurfaceTexture> surfaceTexture;surfaceTexture = new SurfaceTexture(consumer, texName,GL_TEXTURE_EXTERNAL_OES, true, !singleBufferMode);surfaceTexture->setName(String8::format("SurfaceTexture-%d-%d-%d",(isDetached ? 0 : texName),getpid(),createProcessUniqueId()));SurfaceTexture_setSurfaceTexture(env, thiz, surfaceTexture);SurfaceTexture_setProducer(env, thiz, producer);jclass clazz = env->GetObjectClass(thiz);sp<JNISurfaceTextureContextFrameAvailableListener> ctx(new JNISurfaceTextureContextFrameAvailableListener(env, weakThiz, clazz));surfaceTexture->setFrameAvailableListener(ctx);SurfaceTexture_setFrameAvailableListener(env, thiz, ctx);}

看的出来SurfaceTexture在初始化时候, 会创建生产者(producer),与消费者(consumer), 接着会把ctx设置到surfaceTexture->setFrameAvailableListener, 接下来看下内部代码

surfaceTexture::setFrameAvailableListener

SurfaceTexture 继承了ConsumerBase, SurfaceTexture并没有实现setFrameAvailableListener, 在其父类实现的,接下来看下父类的代码


class ConsumerListener : public virtual RefBase {
public:ConsumerListener() {}virtual ~ConsumerListener();...virtual void onFrameAvailable(const BufferItem& item) = 0;...}
ConsumerBase::ConsumerBase(const sp<IGraphicBufferConsumer>& bufferQueue, bool controlledByApp) :mAbandoned(false),mConsumer(bufferQueue),mPrevFinalReleaseFence(Fence::NO_FENCE) {...wp<ConsumerListener> listener = static_cast<ConsumerListener*>(this);status_t err = mConsumer->consumerConnect(proxy, controlledByApp);...
}void ConsumerBase::setFrameAvailableListener(const wp<FrameAvailableListener>& listener) {CB_LOGV("setFrameAvailableListener");Mutex::Autolock lock(mFrameAvailableMutex);mFrameAvailableListener = listener;
}void ConsumerBase::onFrameAvailable(const BufferItem& item) {sp<FrameAvailableListener> listener;{ // scope for the lockMutex::Autolock lock(mFrameAvailableMutex);listener = mFrameAvailableListener.promote();}if (listener != nullptr) {listener->onFrameAvailable(item);}
}

ConsumerBase 继承了ConsumerListener, 并重写了onFrameAvailable, 在ConsumerBase 构造调用了consumerConnect, 这个方法很关键,接下来看下这个方法在干什么

BufferQueueConsumer::consumerConnect

class BufferQueueConsumer : public BnGraphicBufferConsumer {virtual status_t consumerConnect(const sp<IConsumerListener>& consumer,bool controlledByApp) {return connect(consumer, controlledByApp);}
}
status_t BufferQueueConsumer::connect(const sp<IConsumerListener>& consumerListener, bool controlledByApp) {std::lock_guard<std::mutex> lock(mCore->mMutex);mCore->mConsumerListener = consumerListener;mCore->mConsumerControlledByApp = controlledByApp;return NO_ERROR;
}

这里就很清晰了, consumerConnect 会把上边的回调ConsumerBase::onFrameAvailable 回调设置到mConsumer(BufferQueueConsumer) 的 mCore->mConsumerListener中去, 

我们知道BufferQueueProducer与BufferQueueConsumer共享一个BufferQueueCore 如下图所示:

也就说到这里生成者Producer通过Core就可以获取到Consumer的回调了

接下来看下回调是怎么调用的

回调调用

我们已经知道了SurfaceTexture 创建成功后,还需要创建Surface作为其生产者, 接下来从生产者的角度 看下回调调用流程是怎么样的.

Surface作为生产者在dequeuebuffer获取一帧数据后, 在填充后, 会调用queuebuffer,我们就从这个queuebuffer 去跟下源码

int Surface::hook_queueBuffer(ANativeWindow* window,ANativeWindowBuffer* buffer, int fenceFd) {Surface* c = getSelf(window);{std::shared_lock<std::shared_mutex> lock(c->mInterceptorMutex);if (c->mQueueInterceptor != nullptr) {auto interceptor = c->mQueueInterceptor;auto data = c->mQueueInterceptorData;return interceptor(window, Surface::queueBufferInternal, data, buffer, fenceFd);}}return c->queueBuffer(buffer, fenceFd);
}

int Surface::queueBuffer(android_native_buffer_t* buffer, int fenceFd) {ATRACE_CALL();ALOGV("Surface::queueBuffer");Mutex::Autolock lock(mMutex);int i = getSlotFromBufferLocked(buffer);IGraphicBufferProducer::QueueBufferOutput output;IGraphicBufferProducer::QueueBufferInput input;getQueueBufferInputLocked(buffer, fenceFd, mTimestamp, &input);applyGrallocMetadataLocked(buffer, input);sp<Fence> fence = input.fence;nsecs_t now = systemTime();status_t err = mGraphicBufferProducer->queueBuffer(i, input, &output);onBufferQueuedLocked(i, fence, output);return err;
}
status_t BufferQueueProducer::queueBuffer(int slot,const QueueBufferInput &input, QueueBufferOutput *output) {ATRACE_CALL();ATRACE_BUFFER_INDEX(slot);//...frameAvailableListener = mCore->mConsumerListener;//..if (frameAvailableListener != nullptr) {frameAvailableListener->onFrameAvailable(item);} //..} // Autolock scope

这个BufferQueueProducer::queueBuffer 代码很多, 这里删除了无关代码, 看到在queueBuffer 入队的时候会获取mCore->mConsumerListener, 这就是上边ConsumerBase::onFrameAvailable回调, 调用这个最终就会到消费者回调中去,从而一层一层通知,最终到SurfaceTexture.java 往handler发送消息, 最终回调出去

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/423819.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

html+css+js网页设计 旅游 龙门石窟4个页面

htmlcssjs网页设计 旅游 龙门石窟4个页面 网页作品代码简单&#xff0c;可使用任意HTML辑软件&#xff08;如&#xff1a;Dreamweaver、HBuilder、Vscode 、Sublime 、Webstorm、Text 、Notepad 等任意html编辑软件进行运行及修改编辑等操作&#xff09;。 获取源码 1&#…

【CSS】选择器(基本选择器、复合选择器、属性匹配选择器、结构伪类选择器、伪元素选择器)

选择器 引入方式基础选择器复合选择器属性匹配选择器结构伪类选择器伪元素选择器 引入方式 1&#xff1a;外联 <!-- css引入方式1&#xff1a;外联 外联与内嵌优先级相同&#xff0c;取决于加载顺序 --><link rel"stylesheet" href"./样式.css"…

SpringBoot2:请求处理原理分析-利用内容协商功能实现接口的两种数据格式(JSON、XML)

文章目录 一、功能说明二、案例实现1、基于请求头实现2、基于请求参数实现 一、功能说明 我们知道&#xff0c;用ResponseBody注解标注的接口&#xff0c;默认返回给页面的是json数据。 其实&#xff0c;也可以返回xml结构的数据给页面。 这一篇就来实现一下这个小功能。 二、…

TI DSP下载器XDS100 V2.0无法使用问题

前言 TI DSP下载器XDS100 V2.0用着用着会突然报Error&#xff0c;特别是你想要用Code Composer Studio烧录下载程序的时候 查看设备管理器&#xff0c;发现XDS100 V2.0的设备端口莫名其妙消失了 问了淘宝的厂家&#xff0c;他说TI的开发板信号可能会导致调试器通信信号中断&a…

每日OJ_牛客_点击消除(栈)

目录 牛客_点击消除&#xff08;栈&#xff09; 解析代码 牛客_点击消除&#xff08;栈&#xff09; 点击消除_牛客题霸_牛客网 描述&#xff1a; 牛牛拿到了一个字符串。 他每次“点击”&#xff0c;可以把字符串中相邻两个相同字母消除&#xff0c;例如&#xff0c;字符…

LAMP环境下项目部署

目录 目录 1、创建一台虚拟机 centos 源的配置 备份源 修改源 重新加载缓存 安装软件 2、关闭防火墙和selinux 查看防火墙状态 关闭防火墙 查看SELinux的状态 临时关闭SELinux 永久关闭SELinux&#xff1a;编辑SELinux的配置文件 配置文件的修改内容 3、检查系统…

RAG 在企业应用中落地的难点与创新分享

在2024稀土开发者大会-AI Agent与应用创新分会上&#xff0c;我有幸分享了我们团队在企业应用中实施RAG&#xff08;检索增强生成&#xff09;的难点与创新。希望通过这篇文章&#xff0c;与大家探讨我们在实践中遇到的问题和解决方案&#xff0c;为从事相关工作的朋友提供一些…

python做游戏好用吗

Python做游戏是完全可以的&#xff0c;而且也非常简单&#xff0c;有一个专门针对游戏开发的平台&#xff08;模块&#xff09;—pygame&#xff0c;允许开发人员快速设计游戏而又摆脱了低级语言的束缚&#xff0c;下面我简单介绍一下这个模块的安装和使用&#xff1a; 1、首先…

lightdm , xrandr , startx 桌面管理器,窗口管理器

问题&#xff1a; 了解这几个的含义。 显示服务器 这个不是很明白 显示管理器&#xff0c; 知道就行了&#xff0c;也不是很明白。 窗口管理器。 桌面管理器。 这个其实就是 桌面环境了&#xff0c; 我们的板卡上使用的是xface 。 这个 xface 是一个集合&#xff0c;这里面…

CentOS安装Hadoop系列

安装Hadoop 1、安装SDK 2、Wget下载安装包到指定目录 3、选择hadoop的配置模式&#xff0c;单机模式&#xff0c;伪集群模式&#xff0c;集群模式 1、查找APACHE下载官网&#xff0c;搜索hadoop,进入目录&#xff0c;找到common目录&#xff0c;下载对应版本 国内&#xff0c;…

锁表导致系统挂了,谨慎DDL操作

作者&#xff1a;IT邦德 中国DBA联盟(ACDU)成员&#xff0c;10余年DBA工作经验&#xff0c; Oracle、PostgreSQL ACE CSDN博客专家及B站知名UP主&#xff0c;全网粉丝10万 擅长主流Oracle、MySQL、PG、 高斯及Greenplum备份恢复&#xff0c; 安装迁移&#xff0c;性能优化、故障…

整合Redis和RedisCacheManger

整合redis springboot在现在的版本中操作Redis数据库用到了lettuce&#xff0c;而不是Jedis&#xff0c;他们各有各的特点。Jedis以Redis命令作为方法名称&#xff0c;学习成本低&#xff0c;简单实用。但是Jedis实例是线程不安全的&#xff0c;多线程环境下需要基于连接池来使…

2-94 基于matlab的最佳维纳滤波器的盲解卷积算法

基于matlab的最佳维纳滤波器的盲解卷积算法。维纳滤波将地震子波转换为任意所需要的形态。维纳滤波不同于反滤波&#xff0c;它是在最小平方的意义上为最 佳。基于最佳纳滤波理论的滤波器算法是莱文逊(Wiener—Levinson)算法。程序提供了4种子波和4种期望输出&#xff1a;零延迟…

【鸿蒙HarmonyOS NEXT】页面之间相互传递参数

【鸿蒙HarmonyOS NEXT】页面之间相互传递参数 一、环境说明二、页面之间相互传参 一、环境说明 DevEco Studio 版本&#xff1a; API版本&#xff1a;以12为主 二、页面之间相互传参 说明&#xff1a; 页面间的导航可以通过页面路由router模块来实现。页面路由模块根据页…

用Kimi输出流程图

1.输入 我希望设计一个ERP系统&#xff0c;请帮我简单列一个流程图&#xff0c;用mermaid输出2.输出

这才几天,京东又又又又又又加薪了!

京东 今天的最新消息&#xff0c;京东又又又又又加薪了。 距离我们 京东宣布大幅上调校招薪资 的推文发布才一周多点的时间&#xff0c;京东又宣布加薪了。 好家伙&#xff0c;算上这次&#xff0c;光 2024 年京东就已经宣布了 6 次调薪了&#xff1a; 2024 年初&#xff0c;京…

全志T113方案OTA

1、目前使用的flash为128M&#xff0c;比较小&#xff0c;使用AB升级方式感觉空间不够&#xff0c;所以使用recovery升级linux 2、添加recovery分区 全志提供的SDK本来是不支持打包recovery分区的&#xff0c;需要修改sys_partition.fex&#xff0c;size也需要修改&#xff0c…

Springcould -第一个Eureka应用 --- day02

标题 Eureka工作原理Spring Cloud框架下的服务发现Eureka包含两个组件&#xff0c;分别是&#xff1a;Eureka Server与Eureka Client。Eureka Server&#xff1a;Eureka Client&#xff1a; 搭建Eureka Server步骤&#xff1a;步骤1&#xff1a;创建项目&#xff0c;引入依赖步…

第307题|快速掌握 反常积分敛散性判定的方法|武忠祥老师每日一题

解题思路&#xff1a;先判断这个反常积分的敛散性&#xff0c;再讨论a的取值范围; 判断反常积分的敛散性&#xff0c;我们通常有三个方法&#xff1a; &#xff08;1&#xff09;根据定义&#xff0c;通常在原函数比较好求的情况下&#xff0c;可以根据定义 &#xff08;2&am…

Web3的崛起与智能合约的角色

随着Web3的发展&#xff0c;去中心化网络逐渐取代了以往的集中控制互联网模式。这一转变不仅强调了用户的自治权和隐私保护&#xff0c;还引入了智能合约这一核心技术。智能合约基于区块链技术&#xff0c;能够自动执行合约条款&#xff0c;无需中介干预&#xff0c;从而确保了…