Android T(13) 源码分析 — BufferQueue 的分析
Android T(13) 源码分析 — BufferQueue 的分析
文章目录
- Android T(13) 源码分析 — BufferQueue 的分析
- 前言
- 摘要
- 一、Java 层的 BufferQueue 分析
- 二、原生层的 BufferQueue 分析
- 1、BLASTBufferQueue 的创建
- 2、BLASTBufferQueue 的更新
- 3、Surface 的创建
- 总结
前言
该系列文章基于 Android T(13) 的源码进行分析,内容会逐渐丰富,欢迎大家点赞关注。
摘要
BufferQueue 是 Android 图形系统中的一个关键组件,它连接了生成图形数据缓冲区的组件(生产者)和接收这些数据以进行显示或进一步处理的组件(消费者)。几乎所有的图形数据缓冲区在系统中的传递都依赖于 BufferQueue。
当生产者需要一个缓冲区时,通过调用 dequeueBuffer() 从 BufferQueue 请求一个空闲缓冲区,指定缓冲区的宽度、高度、像素格式和使用标志。生产者填充缓冲区后,再调用 queueBuffer() 将缓冲区返回队列。消费者通过 acquireBuffer() 获取缓冲区,并在使用完毕后通过 releaseBuffer() 将缓冲区返回队列,本篇重点介绍一下 BufferQueue 的创建。
一、Java 层的 BufferQueue 分析
BufferQueue 是生产者和消费者的链接纽带,其依托于 SurfaceControl,与 Surface 联系紧密,之前我们分析过 Java 层 SurfaceControl 的创建,当 SurfaceControl 创建成功之后,接下来就进行 BufferQueue 创建。我们还是从 relayoutWindow 开始,看一下生产者端的 BufferQueue 是如何创建的:
//frameworks/base/core/java/android/view/ViewRootImpl.javaprivate int relayoutWindow(WindowManager.LayoutParams params, int viewVisibility,boolean insetsPending) throws RemoteException {//...if (mSurfaceControl.isValid()) {if (!useBLAST()) {mSurface.copyFrom(mSurfaceControl);} else {updateBlastSurfaceIfNeeded();}//...
BLAST特性是 Android 12 新推出的,默认开启,具体看一下 updateBlastSurfaceIfNeeded 实现:
//frameworks/base/core/java/android/view/ViewRootImpl.javavoid updateBlastSurfaceIfNeeded() {if (!mSurfaceControl.isValid()) {return;}if (mBlastBufferQueue != null && mBlastBufferQueue.isSameSurfaceControl(mSurfaceControl)) {mBlastBufferQueue.update(mSurfaceControl,mSurfaceSize.x, mSurfaceSize.y,mWindowAttributes.format);return;}// If the SurfaceControl has been updated, destroy and recreate the BBQ to reset the BQ and// BBQ states.if (mBlastBufferQueue != null) {mBlastBufferQueue.destroy();}mBlastBufferQueue = new BLASTBufferQueue(mTag, mSurfaceControl,mSurfaceSize.x, mSurfaceSize.y, mWindowAttributes.format);mBlastBufferQueue.setTransactionHangCallback(sTransactionHangCallback);Surface blastSurface = mBlastBufferQueue.createSurface();// Only call transferFrom if the surface has changed to prevent inc the generation ID and// causing EGL resources to be recreated.mSurface.transferFrom(blastSurface);}
初始 mBlastBufferQueue 是空,会新建一个 BLASTBufferQueue 对象,我们看一下 BLASTBufferQueue 是如何定义的:
//frameworks/base/graphics/java/android/graphics/BLASTBufferQueue.java/** Create a new connection with the surface flinger. */public BLASTBufferQueue(String name, SurfaceControl sc, int width, int height,@PixelFormat.Format int format) {this(name, true /* updateDestinationFrame */);update(sc, width, height, format);}public BLASTBufferQueue(String name, boolean updateDestinationFrame) {mNativeObject = nativeCreate(name, updateDestinationFrame);}
BLASTBufferQueue 会新建一个 mNativeObject 对象,继续看一下 nativeCreate 实现:
//frameworks/base/core/jni/android_graphics_BLASTBufferQueue.cpp
static jlong nativeCreate(JNIEnv* env, jclass clazz, jstring jName,jboolean updateDestinationFrame) {ScopedUtfChars name(env, jName);sp<BLASTBufferQueue> queue = new BLASTBufferQueue(name.c_str(), updateDestinationFrame);queue->incStrong((void*)nativeCreate);return reinterpret_cast<jlong>(queue.get());
}
这里通过JNI,新建了一个原生层的 BLASTBufferQueue 对象,我们稍后分析原生层的代码。在此之前,我们再看一下 BLASTBufferQueue 函数中 update 的实现:
//frameworks/base/graphics/java/android/graphics/BLASTBufferQueue.javapublic void update(SurfaceControl sc, int width, int height, @PixelFormat.Format int format) {nativeUpdate(mNativeObject, sc.mNativeObject, width, height, format);}
这里 nativeUpdate 同样会转入 JNI,具体实现:
//frameworks/base/core/jni/android_graphics_BLASTBufferQueue.cpp
static void nativeUpdate(JNIEnv* env, jclass clazz, jlong ptr, jlong surfaceControl, jlong width,jlong height, jint format) {sp<BLASTBufferQueue> queue = reinterpret_cast<BLASTBufferQueue*>(ptr);queue->update(reinterpret_cast<SurfaceControl*>(surfaceControl), width, height, format);
}
nativeUpdate 函数中的 update 最后也会通过 JNI 链接到原生层,把 Buffer 的宽度,高度以及像素格式传递给原生层,进一步丰富 BLASTBufferQueue。最后,我们回到 updateBlastSurfaceIfNeeded 函数,看一下 blastSurface 的创建,具体 createSurface 实现 :
//frameworks/base/graphics/java/android/graphics/BLASTBufferQueue.java/*** @return a new Surface instance from the IGraphicsBufferProducer of the adapter.*/public Surface createSurface() {return nativeGetSurface(mNativeObject, false /* includeSurfaceControlHandle */);}
可以看到,createSurface 同样调到了 JNI,函数 nativeGetSurface 实现:
//frameworks/base/core/jni/android_graphics_BLASTBufferQueue.cpp
static jobject nativeGetSurface(JNIEnv* env, jclass clazz, jlong ptr,jboolean includeSurfaceControlHandle) {sp<BLASTBufferQueue> queue = reinterpret_cast<BLASTBufferQueue*>(ptr);return android_view_Surface_createFromSurface(env,queue->getSurface(includeSurfaceControlHandle));
}
queue 是一个 BLASTBufferQueue 对象,通过 android_view_Surface_createFromSurface 进行复制:
//frameworks/base/core/jni/android_view_Surface.cpp
jobject android_view_Surface_createFromSurface(JNIEnv* env, const sp<Surface>& surface) {jobject surfaceObj = env->NewObject(gSurfaceClassInfo.clazz,gSurfaceClassInfo.ctor, (jlong)surface.get());if (surfaceObj == NULL) {if (env->ExceptionCheck()) {ALOGE("Could not create instance of Surface from IGraphicBufferProducer.");LOGE_EX(env);env->ExceptionClear();}return NULL;}surface->incStrong(&sRefBaseOwner);return surfaceObj;
}
总结下来,relayoutWindow 会在 Java 层创建一个 BLASTBufferQueue 对象,通过 JNI 同步在原生层同步创建一个 BLASTBufferQueue 对象;并且把具体的宽度,高度和像素格式也通过 JNI 传递给原生层,并且从原生层返回一个 blastSurface(Surface) 对象,函数时序图:

二、原生层的 BufferQueue 分析
上一章节我们分析过,Java 层通过调用 JNI 的 nativeCreate 在原生层创建 BufferQueue 对象,然后通过 nativeUpdate 更新 Buffer 的宽度,高度以及像素格式,最后通过 nativeGetSurface 来获取创建的 blastSurface,本节我们按照这个顺序逐一分析原生层的 BufferQueue 创建过程。
1、BLASTBufferQueue 的创建
先看一下,原生层 BLASTBufferQueue 的定义:
//frameworks/native/libs/gui/BLASTBufferQueue.cpp
BLASTBufferQueue::BLASTBufferQueue(const std::string& name, bool updateDestinationFrame): mSurfaceControl(nullptr),mSize(1, 1),mRequestedSize(mSize),mFormat(PIXEL_FORMAT_RGBA_8888),mTransactionReadyCallback(nullptr),mSyncTransaction(nullptr),mUpdateDestinationFrame(updateDestinationFrame) {createBufferQueue(&mProducer, &mConsumer);//...mBufferItemConsumer = new BLASTBufferItemConsumer(mConsumer,GraphicBuffer::USAGE_HW_COMPOSER |GraphicBuffer::USAGE_HW_TEXTURE,1, false, this);//...
}
在 BLASTBufferQueue 函数中,createBufferQueue 创建了 BufferQueue 对象,并且定义了当前 Buffer 的生产者和消费者:
//frameworks/native/libs/gui/BLASTBufferQueue.cpp
void BLASTBufferQueue::createBufferQueue(sp<IGraphicBufferProducer>* outProducer,sp<IGraphicBufferConsumer>* outConsumer) {//...sp<BufferQueueCore> core(new BufferQueueCore());//...sp<IGraphicBufferProducer> producer(new BBQBufferQueueProducer(core));//...sp<BufferQueueConsumer> consumer(new BufferQueueConsumer(core));consumer->setAllowExtraAcquire(true);//...*outProducer = producer;*outConsumer = consumer;
}
在 createBufferQueue 函数中,新建了一个 BufferQueueCore 对象,IGraphicBufferProducer 对象和 BufferQueueConsumer 对象。其中 core(BufferQueueCore) 是 BufferQueue 的核心部分,是生产者和消费者的纽带,主要负责 BufferQueue 的状态管理, 从定义上也能看出,它涵盖了 BufferQueue 的诸多状态:
//frameworks/native/libs/gui/BufferQueueCore.cpp
BufferQueueCore::BufferQueueCore(): mMutex(),mIsAbandoned(false),mConsumerControlledByApp(false),mConsumerName(getUniqueName()),mConsumerListener(),mConsumerUsageBits(0),mConsumerIsProtected(false),mConnectedApi(NO_CONNECTED_API),mLinkedToDeath(),mConnectedProducerListener(),mBufferReleasedCbEnabled(false),mSlots(),mQueue(),mFreeSlots(),mFreeBuffers(),mUnusedSlots(),mActiveBuffers(),mDequeueCondition(),mDequeueBufferCannotBlock(false),mQueueBufferCanDrop(false),mLegacyBufferDrop(true),mDefaultBufferFormat(PIXEL_FORMAT_RGBA_8888),mDefaultWidth(1),mDefaultHeight(1),mDefaultBufferDataSpace(HAL_DATASPACE_UNKNOWN),mMaxBufferCount(BufferQueueDefs::NUM_BUFFER_SLOTS),mMaxAcquiredBufferCount(1),mMaxDequeuedBufferCount(1),mBufferHasBeenQueued(false),mFrameCounter(0),mTransformHint(0),mIsAllocating(false),mIsAllocatingCondition(),mAllowAllocation(true),mBufferAge(0),mGenerationNumber(0),mAsyncMode(false),mSharedBufferMode(false),mAutoRefresh(false),mSharedBufferSlot(INVALID_BUFFER_SLOT),mSharedBufferCache(Rect::INVALID_RECT, 0, NATIVE_WINDOW_SCALING_MODE_FREEZE,HAL_DATASPACE_UNKNOWN),mLastQueuedSlot(INVALID_BUFFER_SLOT),mUniqueId(getUniqueId()),mAutoPrerotation(false),mTransformHintInUse(0) {
//...
}
producer(IGraphicBufferProducer)是 BufferQueue 的生产者,主要负责 Buffer 的申请,调用 dequeueBuffer() 获取一个空闲缓冲区,填充数据后通过 queueBuffer() 将其返回队列,producer 是一个 BBQBufferQueueProducer 对象:
//frameworks/native/libs/gui/BLASTBufferQueue.cppBBQBufferQueueProducer(const sp<BufferQueueCore>& core): BufferQueueProducer(core, false /* consumerIsSurfaceFlinger*/) {}
而 BBQBufferQueueProducer 又指向了一个 BufferQueueProducer 对象:
//frameworks/native/libs/gui/BufferQueueProducer.cpp
BufferQueueProducer::BufferQueueProducer(const sp<BufferQueueCore>& core,bool consumerIsSurfaceFlinger) :mCore(core),mSlots(core->mSlots),mConsumerName(),mStickyTransform(0),mConsumerIsSurfaceFlinger(consumerIsSurfaceFlinger),mLastQueueBufferFence(Fence::NO_FENCE),mLastQueuedTransform(0),mCallbackMutex(),mNextCallbackTicket(0),mCurrentCallbackTicket(0),mCallbackCondition(),mDequeueTimeout(-1),mDequeueWaitingForAllocation(false) {}
consumer(BufferQueueConsumer) 是 BufferQueue 的消费者,主要负责 Buffer 的使用,调用 acquireBuffer() 获取缓冲区,并在使用完毕后通过 releaseBuffer() 将其返回队列,BufferQueueConsumer 的初始化:
//frameworks/native/libs/gui/BufferQueueConsumer.cpp
BufferQueueConsumer::BufferQueueConsumer(const sp<BufferQueueCore>& core) :mCore(core),mSlots(core->mSlots),mConsumerName() {}
总结一下,core 是 BufferQueue 的核心,主要负责 Buffer 的状态管理,是生产者和消费者之间的重要纽带。producer 是生产者,主要负责 Buffer 空间的申请,consumer 是消费者,主要负责 Buffer 的使用。
2、BLASTBufferQueue 的更新
JNI 通过 nativeGetSurface 更新 Buffer 的宽度,高度以及像素格式,这一小节我们看一下,原生层的 update 函数实现:
//frameworks/native/libs/gui/BLASTBufferQueue.cpp
void BLASTBufferQueue::update(const sp<SurfaceControl>& surface, uint32_t width, uint32_t height,int32_t format) {//...if (mFormat != format) {mFormat = format;mBufferItemConsumer->setDefaultBufferFormat(convertBufferFormat(format));}const bool surfaceControlChanged = !SurfaceControl::isSameSurface(mSurfaceControl, surface);if (surfaceControlChanged && mSurfaceControl != nullptr) {BQA_LOGD("Updating SurfaceControl without recreating BBQ");}bool applyTransaction = false;// Always update the native object even though they might have the same layer handle, so we can// get the updated transform hint from WM.mSurfaceControl = surface;SurfaceComposerClient::Transaction t;if (surfaceControlChanged) {t.setFlags(mSurfaceControl, layer_state_t::eEnableBackpressure,layer_state_t::eEnableBackpressure);applyTransaction = true;}mTransformHint = mSurfaceControl->getTransformHint();mBufferItemConsumer->setTransformHint(mTransformHint);BQA_LOGV("update width=%d height=%d format=%d mTransformHint=%d", width, height, format,mTransformHint);ui::Size newSize(width, height);if (mRequestedSize != newSize) {mRequestedSize.set(newSize);mBufferItemConsumer->setDefaultBufferSize(mRequestedSize.width, mRequestedSize.height);if (mLastBufferInfo.scalingMode != NATIVE_WINDOW_SCALING_MODE_FREEZE) {// If the buffer supports scaling, update the frame immediately since the client may// want to scale the existing buffer to the new size.mSize = mRequestedSize;if (mUpdateDestinationFrame) {t.setDestinationFrame(mSurfaceControl, Rect(newSize));applyTransaction = true;}}}if (applyTransaction) {// All transactions on our apply token are one-way. See comment on mAppliedLastTransactiont.setApplyToken(mApplyToken).apply(false, true);}
}
当高度,宽度等有变化的时候,会更新 BufferQueue。注意,这里 mBufferItemConsumer 对应的是上一小节中创建的 consumer。
3、Surface 的创建
JNI 通过 nativeGetSurface 来获取一块 blastSurface,在 nativeGetSurface 函数中,Surface 是由 BLASTBufferQueue 类中的 getSurface 函数返回的:
//frameworks/native/libs/gui/BLASTBufferQueue.cpp
sp<Surface> BLASTBufferQueue::getSurface(bool includeSurfaceControlHandle) {std::unique_lock _lock{mMutex};sp<IBinder> scHandle = nullptr;if (includeSurfaceControlHandle && mSurfaceControl) {scHandle = mSurfaceControl->getHandle();}return new BBQSurface(mProducer, true, scHandle, this);
}
在 Java 层,传入的 includeSurfaceControlHandle 是 false,所以这里直接新建一个 BBQSurface 对象:
//frameworks/native/libs/gui/BLASTBufferQueue.cpp
BBQSurface(const sp<IGraphicBufferProducer>& igbp, bool controlledByApp,const sp<IBinder>& scHandle, const sp<BLASTBufferQueue>& bbq): Surface(igbp, controlledByApp, scHandle), mBbq(bbq) {}
BBQSurface 初始化了一个 Surface 对象,并将指针返回给 getSurface。这里 Surface 初始化并没有分配空间,还是一块空的 Buffer:
//frameworks/native/libs/gui/Surface.cpp
Surface::Surface(const sp<IGraphicBufferProducer>& bufferProducer, bool controlledByApp,const sp<IBinder>& surfaceControlHandle): mGraphicBufferProducer(bufferProducer),mCrop(Rect::EMPTY_RECT),mBufferAge(0),mGenerationNumber(0),mSharedBufferMode(false),mAutoRefresh(false),mAutoPrerotation(false),mSharedBufferSlot(BufferItem::INVALID_BUFFER_SLOT),mSharedBufferHasBeenQueued(false),mQueriedSupportedTimestamps(false),mFrameTimestampsSupportsPresent(false),mEnableFrameTimestamps(false),mFrameEventHistory(std::make_unique<ProducerFrameEventHistory>()) {// Initialize the ANativeWindow function pointers.ANativeWindow::setSwapInterval = hook_setSwapInterval;ANativeWindow::dequeueBuffer = hook_dequeueBuffer;ANativeWindow::cancelBuffer = hook_cancelBuffer;ANativeWindow::queueBuffer = hook_queueBuffer;ANativeWindow::query = hook_query;ANativeWindow::perform = hook_perform;ANativeWindow::dequeueBuffer_DEPRECATED = hook_dequeueBuffer_DEPRECATED;ANativeWindow::cancelBuffer_DEPRECATED = hook_cancelBuffer_DEPRECATED;ANativeWindow::lockBuffer_DEPRECATED = hook_lockBuffer_DEPRECATED;ANativeWindow::queueBuffer_DEPRECATED = hook_queueBuffer_DEPRECATED;const_cast<int&>(ANativeWindow::minSwapInterval) = 0;const_cast<int&>(ANativeWindow::maxSwapInterval) = 1;mReqWidth = 0;mReqHeight = 0;mReqFormat = 0;mReqUsage = 0;mTimestamp = NATIVE_WINDOW_TIMESTAMP_AUTO;mDataSpace = Dataspace::UNKNOWN;mScalingMode = NATIVE_WINDOW_SCALING_MODE_FREEZE;mTransform = 0;mStickyTransform = 0;mDefaultWidth = 0;mDefaultHeight = 0;mUserWidth = 0;mUserHeight = 0;mTransformHint = 0;mConsumerRunningBehind = false;mConnectedToCpu = false;mProducerControlledByApp = controlledByApp;mSwapIntervalZero = false;mMaxBufferCount = NUM_BUFFER_SLOTS;mSurfaceControlHandle = surfaceControlHandle;
}
在文章第一部分,我们分析了 blastSurface 是一个 Surface 对象,这一小节通过原生层的代码分析,最后新建了一个原生层的 Surface 对象。函数时序图:

总结
至此,BufferQueue 的创建过程已经分析完成,后续我们会继续分析 BufferQueue 如何通过 dequeueBuffer(),queueBuffer(),acquireBuffer() ,releaseBuffer() 进行 Buffer 管理;以及如何通过 Gralloc 申请具体的 Buffer 空间。
相关文章:
Android T(13) 源码分析 — BufferQueue 的分析
Android T(13) 源码分析 — BufferQueue 的分析 文章目录 Android T(13) 源码分析 — BufferQueue 的分析前言摘要一、Java 层的 BufferQueue 分析二、原生层的 BufferQueue 分析1、BLASTBufferQueue 的创建2、BLASTBufferQueue 的更新3、Surface 的创建 总结 前言 该系列文章…...
Vite+TS项目中配置路径别名
在使用 Vite 和 TypeScript 的项目中配置路径别名,可以简化模块导入路径,提高代码的可读性和维护性。以下是详细的步骤和示例代码: 1. 配置 Vite 别名 前置条件 下载types/node 下面引入的path会用到 npm install types/node --save-dev原…...
看盘细节系列 篇二:集合竞价的9点18分大单打到3%以下或以上,9点19分撤单
文章目录 系列文章现象原因分析时间点含义正常情况测试市场反应诱导跟风操纵股价意图系列文章 看盘细节系列 篇一:集合竞价尾盘突变 现象 集合竞价中 9 点 18 分通过一笔大单或连续几笔大单将股价打到 3% 以下或以上,9 点 19 分又迅速撤单。从而在分时图上留下一根长长的上…...
Java继承简介
继承的本质:是代码的复用,重复使用已经定义好的方法和域(即全局变量) 要掌握继承首先要了解Java方法的重载和重写 方法的重载和重写 方法的重载 当前方法名相同,但是参数类型不同,发生重载 类比数学函…...
redis之哨兵集群搭建
一:哨兵集群工作概览图 1.监控:sentinel通过心跳监控redis的master和slave实例是否正常工作 2.故障转移:假如master出现故障,sentinel会选举一个slave作为新的master,当故障实例恢复后身份会变成slave,会以…...
保姆级AI开发环境搭建
目录 windows下环境搭建1. Python环境搭建2. 下载vLLM2.1 安装CUDA2.2 安装Pytorch2.3 安装vllm 3. 部署Deepseek(huggingface)3.1 DeepSeek的优化建议 4. ollama快速部署Deepseek4.1 下载Ollama4.2 配置Ollma4.2 运行模型4.3 其他Ollama命令 linux下环境…...
Arduino 型号的对比
常见 Arduino 型号的对比表格 涵盖了不同型号的关键参数和特点,方便你根据项目需求进行选择: 型号Arduino UnoArduino Mega 2560Arduino LeonardoArduino NanoArduino Due微控制器ATmega328PATmega2560ATmega32U4ATmega328P 或 ATmega168SAM3X8E&#…...
Kafka系列之:定位topic只能保存最新数据的原因
Kafka系列之:定位topic只能保存最新数据的原因 一、背景二、定位排查方向三、深入排查一、背景 kafka topic保存的数据少,topic只能保存最新的数据二、定位排查方向 能想到的定位排查方向:topic能存储的数据量、topic数据保存的时间、topic数据大小./bin/kafka-configs.sh -…...
AtCoder Beginner Contest 391(A~E题题解)
A - Lucky Direction 思路:纯模拟的一个水题 #include <bits/stdc.h> using namespace std; #define int long long string s; signed main() { cin>>s;for(int i0;i<s.size();i){char cs[i];if(cN){cout<<"S";}else if(c…...
mysql mvcc 锁 关系
多版本并发控制(MVCC)是一种用于数据库并发控制的机制,它可以在保证数据一致性的同时,提高数据库的并发性能。下面结合 MVCC 机制,详细阐述常见的四种事务隔离级别(读未提交、读已提交、可重复读、串行化&a…...
安卓手机基于 Termux 安装 AList 并设置开机自启的详细教程
安装 AList 安装 Termux: 点击下载 更新软件包:打开 Termux,运行以下命令以更新软件包列表并升级已安装的软件包: bash复制 pkg update && pkg upgrade安装 AList:运行以下命令安装 AList: bash复…...
LeetCode:503.下一个更大元素II
跟着carl学算法,本系列博客仅做个人记录,建议大家都去看carl本人的博客,写的真的很好的! 代码随想录 LeetCode:503.下一个更大元素II 给定一个循环数组 nums ( nums[nums.length - 1] 的下一个元素是 nums[…...
实验5 配置OSPFv2验证
实验5 配置OSPFv2验证 1.实验目的 (1)OSPFv2 验证的类型和意义。 (2)配置基于区域的 OSPFv2 简单口令验证和 MD5 验证的方法。 (3)配置基于链路的 OSPFv2 简单口令验证和 MD5 验证的方法。 2.实验准备 配置…...
第二节 docker基础之---镜像构建及挂载
查看当前镜像: [rootdocker ~]# docker images REPOSITORY TAG IMAGE ID CREATED SIZE [rootdocker ~]#很明显docker是咱们新搭建的所以目前还没有镜像 1,搜索镜像: [rootdocker ~]# docker search centos 搜索镜像并过滤是官…...
论文阅读:MGMAE : Motion Guided Masking for Video Masked Autoencoding
MGMAE:Motion Guided Masking for Video Masked Autoencoding Abstract 掩蔽自编码(Masked Autoencoding)在自监督视频表示学习中展现了出色的表现。时间冗余导致了VideoMAE中高掩蔽比率和定制的掩蔽策略。本文旨在通过引入运动引导掩蔽策略࿰…...
记录一下 在Mac下用pyinstallter 打包 Django项目
安装: pip install pyinstaller 在urls.py from SheepMasterOneToOne import settings from django.conf.urls.static import staticurlpatterns [path("admin/", admin.site.urls),path(generate_report/export/, ReportAdmin(models.Report, admin.site).generat…...
【漫话机器学习系列】084.偏差和方差的权衡(Bias-Variance Tradeoff)
偏差和方差的权衡(Bias-Variance Tradeoff) 1. 引言 在机器学习模型的训练过程中,我们常常面临一个重要的挑战:如何平衡 偏差(Bias) 和 方差(Variance),以提升模型的泛…...
deepseek本地部署-linux
1、官网推荐安装方法(使用脚本,我绕不过github,未采用) 登录ollama下载网站https://ollama.com/download/linux,linux下有下载脚本。 正常来说,在OS系统下直接执行脚本即可。 2、手动安装方法 2.1获取ol…...
解决使用python提取word文档中所有的图片时图片丢失的问题
python解析word文档,提取文档中所有的图片并保存,并将原图位置用占位符替换。 问题描述 利用python-dox库解析word文档,并提取里面的所有图片时发现会出现一摸一样的图片只解析一次,导致图片丢失,数量不对的情况。 …...
【Spring相关知识】Spring应用如何优雅使用消息队列
文章目录 概述**核心概念****使用场景****快速入门**1. 添加依赖2. 配置 Binder3. 定义消息通道4. 发送和接收消息5. 运行应用 **高级特性****优点****适用场景** 概述 Spring Cloud Stream 是一个用于构建消息驱动微服务的框架,它基于 Spring Boot 和 Spring Inte…...
C++实现分布式网络通信框架RPC(3)--rpc调用端
目录 一、前言 二、UserServiceRpc_Stub 三、 CallMethod方法的重写 头文件 实现 四、rpc调用端的调用 实现 五、 google::protobuf::RpcController *controller 头文件 实现 六、总结 一、前言 在前边的文章中,我们已经大致实现了rpc服务端的各项功能代…...
Caliper 配置文件解析:config.yaml
Caliper 是一个区块链性能基准测试工具,用于评估不同区块链平台的性能。下面我将详细解释你提供的 fisco-bcos.json 文件结构,并说明它与 config.yaml 文件的关系。 fisco-bcos.json 文件解析 这个文件是针对 FISCO-BCOS 区块链网络的 Caliper 配置文件,主要包含以下几个部…...
dify打造数据可视化图表
一、概述 在日常工作和学习中,我们经常需要和数据打交道。无论是分析报告、项目展示,还是简单的数据洞察,一个清晰直观的图表,往往能胜过千言万语。 一款能让数据可视化变得超级简单的 MCP Server,由蚂蚁集团 AntV 团队…...
A2A JS SDK 完整教程:快速入门指南
目录 什么是 A2A JS SDK?A2A JS 安装与设置A2A JS 核心概念创建你的第一个 A2A JS 代理A2A JS 服务端开发A2A JS 客户端使用A2A JS 高级特性A2A JS 最佳实践A2A JS 故障排除 什么是 A2A JS SDK? A2A JS SDK 是一个专为 JavaScript/TypeScript 开发者设计的强大库ÿ…...
4. TypeScript 类型推断与类型组合
一、类型推断 (一) 什么是类型推断 TypeScript 的类型推断会根据变量、函数返回值、对象和数组的赋值和使用方式,自动确定它们的类型。 这一特性减少了显式类型注解的需要,在保持类型安全的同时简化了代码。通过分析上下文和初始值,TypeSc…...
【Veristand】Veristand环境安装教程-Linux RT / Windows
首先声明,此教程是针对Simulink编译模型并导入Veristand中编写的,同时需要注意的是老用户编译可能用的是Veristand Model Framework,那个是历史版本,且NI不会再维护,新版本编译支持为VeriStand Model Generation Suppo…...
ubuntu系统文件误删(/lib/x86_64-linux-gnu/libc.so.6)修复方案 [成功解决]
报错信息:libc.so.6: cannot open shared object file: No such file or directory: #ls, ln, sudo...命令都不能用 error while loading shared libraries: libc.so.6: cannot open shared object file: No such file or directory重启后报错信息&…...
自然语言处理——文本分类
文本分类 传统机器学习方法文本表示向量空间模型 特征选择文档频率互信息信息增益(IG) 分类器设计贝叶斯理论:线性判别函数 文本分类性能评估P-R曲线ROC曲线 将文本文档或句子分类为预定义的类或类别, 有单标签多类别文本分类和多…...
2025年低延迟业务DDoS防护全攻略:高可用架构与实战方案
一、延迟敏感行业面临的DDoS攻击新挑战 2025年,金融交易、实时竞技游戏、工业物联网等低延迟业务成为DDoS攻击的首要目标。攻击呈现三大特征: AI驱动的自适应攻击:攻击流量模拟真实用户行为,差异率低至0.5%,传统规则引…...
数据结构:泰勒展开式:霍纳法则(Horner‘s Rule)
目录 🔍 若用递归计算每一项,会发生什么? Horners Rule(霍纳法则) 第一步:我们从最原始的泰勒公式出发 第二步:从形式上重新观察展开式 🌟 第三步:引出霍纳法则&…...
