SurfaceTexture OnFrameAvailableListener 调用流程分析

news2024/12/23 13:14:33

背景:

最近项目中遇到一个问题, 需要搞清楚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*/) {
                @Override
                public 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 lock
        Mutex::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.coloradmin.cn/o/2135757.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

102.游戏安全项目-显示人物属性

免责声明&#xff1a;内容仅供学习参考&#xff0c;请合法利用知识&#xff0c;禁止进行违法犯罪活动&#xff01; 内容参考于&#xff1a;易道云信息技术研究院 上一个内容&#xff1a;101.游戏安全项目-创建人物对象结构 效果图&#xff1a; 以 101.游戏安全项目-创建人物…

[产品管理-15]:NPDP新产品开发 - 13 - 产品创新流程 - 具体产品的创新流程:精益生产与敏捷开发

目录 前言&#xff1a;​ 一、集成产品开发IPD模型——集成跨功能团队的产品开发 1.1 概述 1、IPD模型的核心思想 2、IPD模型的主要组成部分 3、IPD模型的实施步骤 4、IPD模型的优点 1.2 基于IPD系统的组织实践等级 1.3 IPD的优缺点 二、瀑布开发模型 1、定义与特点…

【一分钟学C++】std::memory_order

竹杖芒鞋轻胜马,谁怕?一蓑烟雨任平生~ 公众号&#xff1a; C学习与探索 | 个人主页&#xff1a; rainInSunny | 个人专栏&#xff1a; Learn OpenGL In Qt 文章目录 写在前面为什么需要Memory OrderMemory OrderRelaxed OrderRelease-Acquire Order 写在前面 使用std::mem…

day45-测试平台搭建之前端vue学习-基础4

目录 一、生命周期 1.1.概念 1.2.常用的生命周期钩子 1.3.关于销毁Vue实例 1.4.原理​编辑 1.5.代码 二、非单文件组件 2.1.组件 2.2.使用组件的三大步骤 2.3.注意点 2.4.关于VueComponent 2.5.一个重要的内置关系 三、今日学习思维导图 一、生命周期 1.1.概念 1).又名&…

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

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

【机器学习】10——logistic的直观理解

机器学习10——logistic的直观理解 logistic 目录 机器学习10——logistic的直观理解训练过程具体例子 训练过程 数据集: 特征: 学习时间&#xff08;例如&#xff0c;1小时、2小时等&#xff09;。 标签: 是否通过考试&#xff08;0 或 1&#xff09;。 模型结构: 输入: 学习…

OpenAI推出o1系列模型:AI思考力爆表,带来全新智能体验

OpenAI的——o1系列模型&#xff0c;传说中的「草莓」&#xff0c;终于来与大家见面了&#xff01; 这个新模型可不一般&#xff0c;它可以进行复杂的推理&#xff0c;就像在认真思考一样&#xff0c;不再是简单的回答问题。CEO奥特曼称&#xff0c;这是一个全新的开始。它不仅…

智能照明监控系统在发电厂的应用

0前言 国内发电厂普遍使用传统照明控制方法&#xff0c;包括配电箱集中控制、就地开关控制和自动控制。然而&#xff0c;随着技术进步&#xff0c;这些方法已无法满足对安全、舒适、便捷、信息交互和节能环保的需求。因此&#xff0c;实施智能照明控制系统变得必要&#xff0c…

考研报名确认上传身份证户口本学历证明照片如何压缩裁剪

随着考研季节的到来&#xff0c;数以万计的考生开始准备报名所需的各种材料。在这一过程中&#xff0c;证件照片的上传无疑是一个关键环节。正确的照片格式和尺寸不仅能确保报名流程的顺利进行&#xff0c;还能避免因材料不合格而造成的不必要麻烦。本文将详细介绍如何在考研报…

密码学基础--ECDSA算法入门

目录 1.ECDSA签名长度的疑惑 2.ECDSA原理 2.1 生成签名 2.2 验签过程 2.3 签名编码问题 3.小结 1.ECDSA签名长度的疑惑 我们来看看ECDSA签名长什么样子&#xff0c;使用MuscleV02自动生成密钥对&#xff0c;并对message"0x11223344”进行签名&#xff0c;结果如下&a…

一款超级给力的DAW软件flstudio24.1.1.4285最新破解版!

嗨&#xff0c;音乐制作爱好者们&#xff01;今天要跟大家安利一款超级给力的DAW软件&#xff0c;它就是——fl studio24.1.1.4285最新破解版&#xff01; fl studio24.1.1.4285最新破解版简介&#xff1a;这款强大的数字音频工作站软件&#xff0c;是音乐人梦寐以求的工具。它…

Python+Pytest框架,“api_key.py文件怎么编写“?

1、在"api_keyword"文件夹下新增"api_key.py" import allure import requests import json import jsonpath from deepdiff import DeepDifffrom config import *allure.title("测试用例执行") class ApiKey:allure.step(">>>:开…

【win工具】win安装flameshot并设置截图快捷键

1.下载flameshot软件2.windows端配置flameshot快捷键3.取消win自带截图快捷键 1.下载flameshot软件 https://flameshot.org/#download installer版本为安装包 portable版本为免安装版 2.windows端配置flameshot快捷键 https://cloud.tencent.com/developer/article/2114952 W…

Java项目: 基于SpringBoot+mybatis+maven课程答疑系统(含源码+数据库+毕业论文)

一、项目简介 本项目是一套基于SpringBootmybatismaven课程答疑系统 包含&#xff1a;项目源码、数据库脚本等&#xff0c;该项目附带全部源码可作为毕设使用。 项目都经过严格调试&#xff0c;eclipse或者idea 确保可以运行&#xff01; 该系统功能完善、界面美观、操作简单、…

Pikachu靶场之csrf

CSRF 跨站请求伪造 CSRF入门及靶场实战 - FreeBuf网络安全行业门户 攻击者伪造恶意链接&#xff0c;诱使用户点击&#xff0c;这个链接附带了用户的认证凭据Cookie、Session等&#xff0c;执行操作如转账。 因为带了cookie、session&#xff0c;服务器认为是用户的行为。借用…

尚品汇-订单拆单、支付宝关闭交易、关闭过期订单整合(五十)

目录&#xff1a; &#xff08;1&#xff09;拆单接口 &#xff08;2&#xff09;取消订单业务补充关闭支付记录 &#xff08;3&#xff09;支付宝关闭交易 &#xff08;4&#xff09;查询支付交易记录 &#xff08;5&#xff09;PaymentFeignClient 远程接口 &#xff08…

玩转扩展库,温湿度传感器篇!—合宙Air201资产定位模组LuatOS快速入门05

随着LuatOS快速入门系列教程的推出&#xff0c;小伙伴们学习热情高涨。 合宙Air201不仅支持三种定位方式&#xff0c;还具有丰富的扩展功能&#xff0c;通过外扩BTB链接方案&#xff0c;最多可支持21个IO接口&#xff1a;SPI、I2C、UART等多种接口全部支持。 本期&#xff0c…

electron-vite vue3离线使用monaco-editor

目录 1.搭建一个 electron-vite 项目 2.安装monaco-editor和vite-plugin-monaco-editor 3.electron.vite.config.mjs配置 4.创建 worker.js并在main.js 引入 5.创建组件 MonacoVite.vue 组件 6. App.vue中引入组件 7.运行测试 1.搭建一个 electron-vite 项目 pnpm creat…

如何在算家云搭建TripoSR(三维重建)

一、模型介绍 TripoSR是由Tripo AI和Stability AI合作开发的先进开源模型&#xff0c;能在短时间内从单张图片生成高质量 3D 模型。 利用大型重建模型&#xff08;LRM&#xff09;的原理&#xff0c;TripoSR带来了关键的进步&#xff0c;大大提高了3D重建的速度和质量。模型的…

秒验HarmonyOS NEXT集成指南

开发工具&#xff1a;DevEco Studio 集成方式&#xff1a;在线集成 HarmonyOS API支持&#xff1a;> 12 集成前准备 注册账号 使用MobSDK之前&#xff0c;需要先在MobTech官网注册开发者账号&#xff0c;并获取MobTech提供的AppKey和AppSecret&#xff0c;详情可以点击查看…