当前位置: 首页 > news >正文

通过WebRTC源码入门OpenGL ES

文章目录

  • 基本概念
    • Vertex和Fragment
    • 着色器程序
  • 准备工作
    • getUniformLocation/getAttribLocation
    • glVertexAttribPointer
  • 开始绘制
  • Demo实现

OpenGL SE是一套适用于嵌入式设备的图形API,本文主要介绍如何通过OpenGL SE在Android设备上进行图形绘制,同时我会通过WebRTC视频帧绘制部分的源码让读者加深整个绘制流程的印象,最后修改WebRTC源码实现一个在视频预览画面随机绘制矩形边框的小demo。

基本概念

Vertex和Fragment

OpenGL最基本的两个概念就是:Vertex(顶点) 和 Fragment(片段),试想一下当我们在绘画时,都需要准备什么东西,首先是图案形状,然后是上色。

类比之下Vertex顶点就是用于描述形状,例如两点代表一条线、三点代表一个三角形。需要注意的是,在OpenGL中只有点、线、三角形3种图形,所有的复杂图形都是由这3个基本图形组成,那么如果要描述一个正方形,岂不是要定义例如[(0,0), (0,1), (1,0)]和[(0,1), (1,0),(1,1)]两组顶点,事实上OpenGL有对应的优化,只需定义4个顶点即可,这里后面我们会从代码中看到。

而Fragment片段就是用于描绘如何上色的,片段代表在这组顶点所围绕的范围内,每个像素对应的颜色。

着色器程序

着色器程序是用于定义Vertex和Fragment的,它是通过GLSL这门语言实现的,每个顶点都会执行一次顶点着色器,通过顶点着色器可以确定顶点的位置(gl_Position)。在顶点确定后,就会执行片段着色器,每一个像素都会执行一遍片段着色器以确定最终颜色(gl_FragColor)

以下是一个描绘三角形的着色器程序:

// 顶点着色器
attribute vec4 vPosition;
void main() {gl_Position = vPosition;
}// 片段着色器
precision mediump float; // 所有没有明确指定精度的float变量,默认使用 mediump(中等精度)。
void main() {gl_FragColor = vec4(0.5, 0, 0, 1);
}

所有的着色器程序都从main函数开始执行,其中gl_Position和gl_FragColor都类似于系统变量,他们代表顶点的最终位置和最终颜色。vec4代表定义一个4维向量,包括xyzw4个分量,常用于表示齐次坐标、RGB颜色等。attribute定义一个只用于顶点着色器的变量,该变量由用户输入,后面我们会看到这部分由用户输入的代码。类似的变量定义还有uniform也是由用户输入的变量,还有varying变量是用于顶点着色器和片段着色器共享之间传递的变量。

准备工作

我们可以通过WebRTC源码来看OpenGL ES是如何使用的:

private static int compileShader(int shaderType, String source) {// 1. 创建着色器程序final int shader = GLES20.glCreateShader(shaderType);// 2. 为着色器设置GLSL源代码,并加载GLES20.glShaderSource(shader, source);GLES20.glCompileShader(shader);// 检查是否设置成功int[] compileStatus = new int[] {GLES20.GL_FALSE};GLES20.glGetShaderiv(shader, GLES20.GL_COMPILE_STATUS, compileStatus, 0);if (compileStatus[0] != GLES20.GL_TRUE) {Logging.e(TAG, "Compile error " + GLES20.glGetShaderInfoLog(shader) + " in shader:\n" + source);throw new RuntimeException(GLES20.glGetShaderInfoLog(shader));}GlUtil.checkNoGLES2Error("compileShader");return shader;
}public GlShader(String vertexSource, String fragmentSource) {final int vertexShader = compileShader(GLES20.GL_VERTEX_SHADER, vertexSource);final int fragmentShader = compileShader(GLES20.GL_FRAGMENT_SHADER, fragmentSource);// 3. 创建GLSL程序program = GLES20.glCreateProgram();if (program == 0) {throw new RuntimeException("glCreateProgram() failed. GLES20 error: " + GLES20.glGetError());}// 4. 为GLSL程序添加着色器GLES20.glAttachShader(program, vertexShader);GLES20.glAttachShader(program, fragmentShader);// 5. 连接程序GLES20.glLinkProgram(program);// 检查连接情况int[] linkStatus = new int[] {GLES20.GL_FALSE};GLES20.glGetProgramiv(program, GLES20.GL_LINK_STATUS, linkStatus, 0);if (linkStatus[0] != GLES20.GL_TRUE) {Logging.e(TAG, "Could not link program: " + GLES20.glGetProgramInfoLog(program));throw new RuntimeException(GLES20.glGetProgramInfoLog(program));}GLES20.glDeleteShader(vertexShader);GLES20.glDeleteShader(fragmentShader);GlUtil.checkNoGLES2Error("Creating GlShader");
}private void prepareShader(ShaderType shaderType, float[] texMatrix, int frameWidth,int frameHeight, int viewportWidth, int viewportHeight) {final GlShader shader;if (shaderType.equals(currentShaderType)) {// Same shader type as before, reuse exising shader.shader = currentShader;} else {// Allocate new shader.currentShaderType = null;if (currentShader != null) {currentShader.release();currentShader = null;}shader = createShader(shaderType);currentShaderType = shaderType;currentShader = shader;// 6. 正式使用GLSL程序shader.useProgram();// Set input texture units.if (shaderType == ShaderType.YUV) {GLES20.glUniform1i(shader.getUniformLocation("y_tex"), 0);GLES20.glUniform1i(shader.getUniformLocation("u_tex"), 1);GLES20.glUniform1i(shader.getUniformLocation("v_tex"), 2);} else {GLES20.glUniform1i(shader.getUniformLocation("tex"), 0);}GlUtil.checkNoGLES2Error("Create shader");shaderCallbacks.onNewShader(shader);// 7. 获取uniform变量的索引,后续对uniform变量赋值通过这个索引texMatrixLocation = shader.getUniformLocation(TEXTURE_MATRIX_NAME);// 8. 获取attribute变量索引,后续对attribute变量赋值通过这个索引inPosLocation = shader.getAttribLocation(INPUT_VERTEX_COORDINATE_NAME);inTcLocation = shader.getAttribLocation(INPUT_TEXTURE_COORDINATE_NAME);// 9. 通过uniform变量的索引对其赋值GLES20.glUniform4f(shader.getUniformLocation("uBorderColor"), 1.0f, 0.0f,0.0f,1.0f);GLES20.glUniform1f(shader.getUniformLocation("uBorderWidth"), 0.005f);}shader.useProgram();// 10. attribute变量使用前需要先激活GLES20.glEnableVertexAttribArray(inPosLocation);// 11. 对attribute变量进行赋值GLES20.glVertexAttribPointer(inPosLocation, /* size= */ 2,/* type= */ GLES20.GL_FLOAT, /* normalized= */ false, /* stride= */ 0,FULL_RECTANGLE_BUFFER);// Upload the texture coordinates.GLES20.glEnableVertexAttribArray(inTcLocation);GLES20.glVertexAttribPointer(inTcLocation, /* size= */ 2,/* type= */ GLES20.GL_FLOAT, /* normalized= */ false, /* stride= */ 0,FULL_RECTANGLE_TEXTURE_BUFFER);// 9.1 通过uniform变量的索引对其赋值。但是uniform变量是4维向量类型(vec4)GLES20.glUniformMatrix4fv(texMatrixLocation, 1 /* count= */, false /* transpose= */, texMatrix, 0 /* offset= */);
}

以上是WebRTC在对视频帧渲染时初始化OpenGL部分的代码,重点部分都进行了注释,我们总结下整个调用流程:

  1. 创建着色器:GLES20.glCreateShader、GLES20.glShaderSource、GLES20.glCompileShader
  2. 初始化GLSL程序并绑定着色器:GLES20.glCreateProgram、GLES20.glAttachShader、GLES20.glLinkProgram、shader.useProgram()
  3. 对着色器GLSL源代码定义的变量进行赋值:对于Uniform变量使用shader.getUniformLocation、GLES20.glUniform4f。对于attribute变量使用shader.getAttribLocation、GLES20.glEnableVertexAttribArray、GLES20.glVertexAttribPointer

在对GLSL源代码定义变量进行赋值时,可以把通过getUniformLocation或者getAttribLocation得到的索引保存起来,这个是不会变的

接下来重点讲一下几个函数各个参数的含义:

getUniformLocation/getAttribLocation

此函数用于获取GLSL变量的索引,所以它需要传入的变量,必须和我们GLSL源码定义的变量是相同的,否则会找不到,以上面绘制三角形为例子,应该这个使用:

// 顶点着色器
attribute vec4 vPosition;
void main() {gl_Position = vPosition;
}
// 获取着色器变量
getAttribLocation("vPosition")

glVertexAttribPointer

此函数用于给attribute变量进行赋值,他的参数分别是:

  1. int index:变量索引
  2. int size:变量维度,例如vec2代表2个分量,所以要填2,同理vec4填4
  3. int type:变量类型:例如GLES20.GL_FLOAT代表每个分量都是float
  4. boolean normalized:是否将整数类型数据归一化到[0,1]或[-1,1]
  5. int stride:连续顶点之间的字节跨度(0表示紧密排列)
  6. java.nio.Buffer ptr:数据指针

开始绘制

同样的我们通过WebRTC源代码来学习:

@Override
public void drawOes(int oesTextureId, float[] texMatrix, int frameWidth, int frameHeight,int viewportX, int viewportY, int viewportWidth, int viewportHeight, boolean showTestRect) {// 这里是上面写的初始化操作prepareShader(ShaderType.OES, texMatrix, frameWidth, frameHeight, viewportWidth, viewportHeight);// 1.激活一个纹理单元,GL_TEXTURE0代表第一个GLES20.glActiveTexture(GLES20.GL_TEXTURE0);// 2.绑定外部纹理(OES)GLES20.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, oesTextureId);// 3.设置画布大小GLES20.glViewport(viewportX, viewportY, viewportWidth, viewportHeight);// 4.绘制顶点GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4);// 5.解绑textureGLES20.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, 0);
}

通过GLES20.glViewport可以设置绘制画布的大小,GLES20.glDrawArrays用于绘制顶点,其第一个参数为绘制类型,GLES20.GL_TRIANGLE_STRIP代表指定顶点如何连接成三角形。它的作用是通过一组顶点绘制一系列相连的三角形,减少顶点数据的冗余,从而提高渲染效率。我们前文提到的如果要绘制矩形要定义6个顶点,通过这个类型,只需要定义4个即可。第二个参数为第一个顶点的index, 第三个参数为绘制顶点数量。

glActiveTexture、glBindTexture分别用户激活和绑定纹理,纹理(Texture)​ 是一张存储在显存中的图像数据,用于为3D模型或2D图形添加表面细节、颜色、光照效果等。简单来说,纹理就是“贴”在物体表面的图片,可以让简单的几何形状(如立方体、球体)呈现出更真实的视觉效果。GL_TEXTURE_EXTERNAL_OES代表外部纹理例如从相机或视频流中获取的纹理。在执行片段着色器的时候,就可以通过纹理获取每个片段应该渲染的颜色。

讲到这里,我们在看看WebRTC这里是如何定义着色器GLSL代码的,以下代码来自org.webrtc.GlGenericDrawer#DEFAULT_VERTEX_SHADER_STRINGorg.webrtc.GlGenericDrawer#createFragmentShaderString我做了整合,方便大家观看

//顶点着色器如下:
varying vec2 tc;
attribute vec4 in_pos;
attribute vec4 in_tc;
uniform mat4 tex_mat;
void main() {// in_pos 由外部输入,代表顶点最终坐标gl_Position = in_pos;// in_tc由外部输入,为纹理坐标,通过tex_mat变化矩阵(例如缩放、裁剪等),也由外部输入// in_tc左乘tex_mat后取其XY向量赋值给tc,传递给片段着色器tc = (tex_mat * in_tc).xy;
}
//片段着色器如下:
#extension GL_OES_EGL_image_external : require
precision mediump float;
varying vec2 tc;
// 纹理ID,由外部传入
uniform samplerExternalOES tex;
void main() {// texture2D系统方法,根据纹理ID和tc纹理坐标,计算该坐标应该渲染什么颜色,赋值给gl_FragColorgl_FragColor = texture2D(tex, tc);
}

Demo实现

想要实现在WebRTC预览画面随机绘制一个矩形,首先应该要把矩形的顶点、矩形边框的颜色、矩形的边框粗细传入到GLSL中,其次要在片段着色器中,对当前绘制的坐标进行判断,是否处于随机矩阵上,如果是则绘制矩形边框的颜色,如果不是则继续绘制纹理颜色,因此我们要修改着色器代码如下:

//顶点着色器如下:
varying vec2 tc;
attribute vec4 in_pos;
attribute vec4 in_tc;
uniform mat4 tex_mat;
// 输入矩阵顶点
attribute vec4 in_rect;
// 于片段着色器共享输入矩阵顶点
varying vec4 vRect;
void main() {gl_Position = in_pos;tc = (tex_mat * in_tc).xy;vRect = in_rect;
}
//片段着色器如下:
#extension GL_OES_EGL_image_external : require
precision mediump float;
varying vec2 tc;
uniform samplerExternalOES tex;// 共享矩阵顶点
varying vec4 vRect;
// 定义矩阵边框颜色
uniform vec4 uBorderColor;
// 定义矩阵边框粗细
uniform float uBorderWidth;void main() {vec4 texColor = texture2D(tex, tc);// 判断当前xy是否在随机矩阵上vec2 pixelPos = tc - vRect.xy;vec2 rectSize = vRect.zw;float distLeft = pixelPos.x;float distRight = rectSize.x - pixelPos.x;float distTop = pixelPos.y;float distBottom = rectSize.y - pixelPos.y;float minDist = min(min(distLeft, distRight), min(distTop, distBottom));// 如果在就用矩阵边框的颜色if (minDist < uBorderWidth && pixelPos.x >= 0.0 && pixelPos.x <= rectSize.x &&pixelPos.y >= 0.0 && pixelPos.y <= rectSize.y) {gl_FragColor = uBorderColor;} else {// 不在就用原来纹理颜色gl_FragColor = texColor;}
}

修改完GLSL代码后,就需要改绘制代码, 首先是把GLSL需要传入的参数传递:

// uniform 变量赋值
GLES20.glUniform4f(shader.getUniformLocation("uBorderColor"), 1.0f, 0.0f,0.0f,1.0f);
GLES20.glUniform1f(shader.getUniformLocation("uBorderWidth"), 0.005f);// attribute 变量赋值
inRect = shader.getAttribLocation("in_rect");
GLES20.glEnableVertexAttribArray(inRect);float x = (float) Math.random();
float y = (float) Math.random();
FloatBuffer rect = GlUtil.createFloatBuffer(new float[] {x, y, 0.2f, 0.2f,x, y, 0.2f, 0.2f,x, y, 0.2f, 0.2f,x, y, 0.2f, 0.2f,
});
GLES20.glVertexAttribPointer(inRect,4, GLES20.GL_FLOAT, false, 0, rect);

定义的随机矩阵为(x, y, width, height)这里xy用的随机数,宽高固定0.2f,这里的顶点数据中重复定义了4次相同的矩形参数(vRect),是因为每个顶点需要独立携带完整的矩形信息,以便在片段着色器中正确计算边框。当然也可以不定义vRect直接使用uniform实现。

相关文章:

通过WebRTC源码入门OpenGL ES

文章目录 基本概念Vertex和Fragment着色器程序 准备工作getUniformLocation/getAttribLocationglVertexAttribPointer 开始绘制Demo实现 OpenGL SE是一套适用于嵌入式设备的图形API&#xff0c;本文主要介绍如何通过OpenGL SE在Android设备上进行图形绘制&#xff0c;同时我会通…...

面试题:请描述一下你在项目中是如何进行性能优化的?针对哪些方面进行了优化,采取了哪些具体的措施?

目录 1.算法和数据结构优化 2.内存管理优化 3.并发编程优化 4.数据库优化 5.网络优化 6.持续优化与监控 7.总结 现在是企业招聘和求职者的金三银四&#xff0c;每每问到这个主观性问题的时候&#xff0c;都不知道怎么回答&#xff0c;下面就我知道的一些总结一下&#x…...

从零实现富文本编辑器#2-基于MVC模式的编辑器架构设计

在先前的规划中我们是需要实现MVC架构的编辑器&#xff0c;将应用程序分为控制器、模型、视图三个核心组件&#xff0c;通过控制器执行命令时会修改当前的数据模型&#xff0c;进而表现到视图的渲染上。简单来说就是构建一个描述文档结构与内容的数据模型&#xff0c;并且使用自…...

SAP S4HANA embedded analytics

SAP S4HANA embedded analytics...

linux多线(进)程编程——(7)消息队列

前言 现在修真界大家的沟通手段已经越来越丰富了&#xff0c;有了匿名管道&#xff0c;命名管道&#xff0c;共享内存等多种方式。但是随着深入使用人们逐渐发现了这些传音术的局限性。 匿名管道&#xff1a;只能在有血缘关系的修真者&#xff08;进程&#xff09;间使用&…...

STM32 HAL库 实现485通信

一、引言 在工业自动化、智能家居等众多领域中&#xff0c;RS - 485 通信因其长距离、高抗干扰能力等优点被广泛应用。STM32F407 是一款性能强大的微控制器&#xff0c;其丰富的外设资源为实现 RS - 485 通信提供了良好的硬件基础。本文将详细介绍基于 STM32F407 HAL 库实现 R…...

用 Vue 3 + OpenAI API 实现一个智能对话助手(支持上下文、多角色)

文章目录 一、项目背景与功能介绍二、技术选型与准备工作环境准备 三、智能对话助手的实现第一节&#xff1a;封装 OpenAI 接口请求第二节&#xff1a;构建消息上下文结构第三节&#xff1a;构建对话 UI 组件第四节&#xff1a;滚动自动到底部&#xff08;可选优化&#xff09;…...

ollama修改配置使用多GPU,使用EvalScope进行模型压力测试,查看使用负载均衡前后的性能区别

文章目录 省流结论机器配置不同量化模型占用显存1. 创建虚拟环境2. 创建测试jsonl文件3. 新建测试脚本3. 默认加载方式&#xff0c;单卡运行模型3.1 7b模型输出213 tok/s3.1 32b模型输出81 tok/s3.1 70b模型输出43tok/s 4. 使用负载均衡&#xff0c;多卡运行4.1 7b模型输出217t…...

vue3 setup vite 配置跨域了proxy,部署正式环境的替换

在开发环境中使用 Vite 的 proxy 配置来解决跨域问题是一种常见的做法。然而&#xff0c;在部署到正式环境时&#xff0c;通常需要对接口地址进行调整&#xff0c;具体是否需要更改接口名称取决于你的部署环境和后端服务的配置。以下是几种常见的情况和建议&#xff1a; 1. 正…...

目标检测:YOLOv11(Ultralytics)环境配置

1、前言 YOLO11是Ultralytics公司YOLO系列实时目标检测器的最新迭代版本&#xff0c;它以尖端的准确性、速度和效率重新定义了可能实现的性能。在之前YOLO版本取得的显著进步基础上&#xff0c;YOLO11在架构和训练方法上进行了重大改进&#xff0c;使其成为各种计算机视觉任务中…...

如何高效压缩GIF动图?

GIF动图因其兼容性强、易于传播的特点&#xff0c;成为网络交流的热门选择。然而&#xff0c;过大的文件体积常常导致加载缓慢、分享困难等问题。本文将为您详细介绍几种实用的GIF压缩技巧&#xff0c;帮助您在保持画面质量的同时显著减小文件大小。 压缩方法 1. 在线压缩工具…...

视频融合平台EasyCVR可视化AI+视频管理系统,打造轧钢厂智慧安全管理体系

一、背景分析 在轧钢厂&#xff0c;打包机负责线材打包&#xff0c;操作人员需频繁进入内部添加护垫、整理包装、检修调试等。例如&#xff0c;每班产线超过300件&#xff0c;12小时内人员进出打包机区域超过300次。若员工安全意识薄弱、违规操作&#xff0c;未落实安全措施就…...

通过命令行操作把 本地IDE 项目上传到 GitHub(小白快速版)

通过命令行操作把 本地IDE 项目上传到 GitHub&#xff08;小白版&#xff09; 你是不是在用 本地IDE 做项目&#xff0c;但不知道怎么把自己的代码上传到 GitHub&#xff1f;今天我们用最简单的命令行方式&#xff08;不用 SSH、不用复杂配置&#xff09;教你一步一步把本地项…...

【c语言基础学习】qsort快速排序函数介绍与使用

在C语言中&#xff0c;qsort 函数用于对数组进行快速排序。以下是详细的使用方法及示例&#xff1a; 一、函数原型 #include <stdlib.h>void qsort(void *base, size_t nmemb, size_t size, int (*compar)(const void *, const void *) );二、参数说明 参数说明base指向…...

​​​​​​​今日github AI科技工具汇总(20250415更新)

以下是2025年4月15日GitHub上值得关注的AI科技工具汇总及趋势分析,结合最新开源动态与开发者社区热点整理: 一、AI编程工具重大更新 GitHub Copilot Agent Mode 全量发布 核心功能:在VS Code中启用后,可自主完成多文件代码重构、测试驱动开发(TDD)及自修复编译错误,支持…...

程序化广告行业(88/89):广告创意审核的法律红线与平台规则

程序化广告行业&#xff08;88/89&#xff09;&#xff1a;广告创意审核的法律红线与平台规则 在程序化广告的广阔领域中&#xff0c;不断学习和掌握行业规范是我们稳步前行的基石。一直以来&#xff0c;我都期望与大家携手共进&#xff0c;深入探索这个行业的奥秘。今天&…...

前端VUE框架理论与应用(4)

一、计算属性 模板内的表达式非常便利,但是设计它们的初衷是用于简单运算的。在模板中放入太多的逻辑会让模板过重且难以维护。例如: <div id="example">{{ message.split().reverse().join() }}</div> 在这个地方,模板不再是简单的声明式逻辑。你…...

【经验分享】基于Calcite+MyBatis实现多数据库SQL自动适配:从原理到生产实践

基于CalciteMyBatis实现多数据库SQL自动适配&#xff1a;从原理到生产实践 一、引言&#xff1a;多数据库适配的行业痛点 在当今企业IT环境中&#xff0c;数据库异构性已成为常态。根据DB-Engines最新调研&#xff0c;超过78%的企业同时使用两种以上数据库系统。这种多样性带…...

通信算法之265: 无人机系统中的C2链路

在无人机系统设计中&#xff0c;我们经常听到C2链路这个名词&#xff0c;到底什么是C2链路呢&#xff1f;为什么说C2链路是无人机系统中非常重要的环节。 转载&#xff1a; 无人机技术是各种科技技术水平综合发展的结果&#xff0c;包括空气动力&#xff0c;机械设计&#xf…...

浙江大学:DeepSeek如何引领智慧医疗的革新之路?|48页PPT下载方法

导 读INTRODUCTION 随着人工智能技术的飞速发展&#xff0c;DeepSeek等大模型正在引领医疗行业进入一个全新的智慧医疗时代。这些先进的技术不仅正在改变医疗服务的提供方式&#xff0c;还在提高医疗质量和效率方面展现出巨大潜力。 想象一下&#xff0c;当你走进医院&#xff…...

Codeforces Round 1017 (Div. 4)

Codeforces Round 1017 (Div. 4) A. Trippi Troppi AC code: void solve() { string a, b, c; cin >> a >> b >> c;cout << a[0] << b[0] << c[0] << endl; } B. Bobritto Bandito 思路&#xff1a; 倒推模拟即可&#xff0c…...

bash的特性-bash中的引号

在Linux或Unix系统中&#xff0c;Bash&#xff08;Bourne Again SHell&#xff09;作为最常用的命令行解释器之一&#xff0c;提供了强大的功能来处理各种任务。正确使用引号是掌握Bash脚本编写的基础技能之一&#xff0c;它决定了如何解析字符串、变量替换以及特殊字符的行为。…...

ubuntu上SSH防止暴力破解帐号密码

在知道设备ip的情况下&#xff0c;使用 Fail2Ban防止暴力破解 sudo apt install fail2ban 配置 SSH 防护规则 sudo gedit /etc/fail2ban/jail.local jail.local内容如下&#xff1a; [sshd] enabled true port ssh logpath /var/log/auth.log # Ubuntu/Debian maxret…...

【Bluedroid】A2DP Sink播放流程源码分析(二)

接上一篇继续分析&#xff1a;【Bluedroid】A2DP Sink播放流程源码分析(一)_安卓a2dp sink播放流程-CSDN博客 AVDTP接收端&#xff08;Sink&#xff09;流事件处理 bta_av_sink_data_cback 是 Bluedroid 中 A2DP Sink 角色的 AVDTP 数据回调函数&#xff0c;负责处理接收端的…...

【Code】《代码整洁之道》笔记-Chapter16-重构SerialDate

第16章 重构SerialDate 如果你找到JCommon类库&#xff0c;深入该类库&#xff0c;其中有个名为org.jfree.date的程序包。在该程序包中&#xff0c;有个名为SerialDate的类&#xff0c;我们即将剖析这个类。 SerialDate的作者是David Gilbert。David显然是一位经验丰富、能力…...

redis 内存中放哪些数据?

在 Java 开发中,Redis 作为高性能内存数据库,通常用于存储高频访问、低延迟要求、短期有效或需要原子操作的数据。以下是 Redis 内存中常见的数据类型及对应的使用场景,适合面试回答: 1. 缓存数据(高频访问,降低数据库压力) 用户会话(Session):存储用户登录状态、临时…...

【Python使用】嘿马云课堂web完整实战项目第4篇:封装异常处理,封装JSON返回值【附代码文档】

教程总体简介&#xff1a;项目概述 项目背景 项目的功能构架 项目的技术架构 CMS 什么是CMS CMS需求分析与工程搭建 静态门户工程搭建 SSI服务端包含技术 页面预览开发 4 添加“页面预览”链接 页面发布 需求分析 技术方案 测试 环境搭建 数据字典 服务端 前端 数据模型 页面原…...

「数据可视化 D3系列」入门第三章:深入理解 Update-Enter-Exit 模式

深入理解 Update-Enter-Exit 模式 一、数据绑定三态&#xff1a;Update、Enter、Exit三种状态的直观理解 二、基础概念1. Update 选区 - 处理已有元素2. Enter 选区 - 处理新增数据3. Exit 选区 - 处理多余元素 三、完整工作流程四、三种状态的底层原理数据绑定过程解析键函数&…...

中间件--ClickHouse-5--架构设计(分布式架构,列式压缩存储、并行计算)

1、整体架构设计 ClickHouse 采用MPP&#xff08;大规模并行处理&#xff09;架构&#xff0c;支持分布式计算和存储&#xff0c;其核心设计目标是高性能列式分析。 &#xff08;1&#xff09;、存储层 列式存储&#xff1a; 数据按列存储&#xff08;而非传统行式存储&#…...

AgentGPT 在浏览器中组装、配置和部署自主 AI 代理 入门介绍

AI MCP 系列 AgentGPT-01-入门介绍 Browser-use 是连接你的AI代理与浏览器的最简单方式 AI MCP(大模型上下文)-01-入门介绍 AI MCP(大模型上下文)-02-awesome-mcp-servers 精选的 MCP 服务器 AI MCP(大模型上下文)-03-open webui 介绍 是一个可扩展、功能丰富且用户友好的…...

【开源项目】Excel手撕AI算法深入理解(三):Backpropagation、mamba、RNN

项目源码地址&#xff1a;https://github.com/ImagineAILab/ai-by-hand-excel.git 一、Backpropagation 1. 反向传播的本质 反向传播是通过链式法则计算损失函数对网络参数的梯度的高效算法&#xff0c;目的是用梯度下降优化参数。其核心思想是&#xff1a; 前向传播&#xf…...

uniapp的通用页面及组件基本封装

1.基本布局页面 适用于自定义Navbar头部 <template><view class"bar" :style"{height : systemInfo.statusBarHeight px, background: param.barBgColor }"></view><view class"headBox" :style"{ height: param.h…...

Ubuntu和Debian 操作系统的同与异

首先需要说明&#xff1a;Ubuntu 是基于 Debian 操作系统开发的。它们之间的关系如下 起源与发展&#xff1a;Debian 是一个社区驱动的开源 Linux 发行版&#xff0c;始于 1993 年&#xff0c;是最早的 Linux 发行版之一&#xff0c;以其稳定性和自由软件政策著称。Ubuntu 是基…...

【android bluetooth 协议分析 21】【ble 介绍 1】【什么是RPA】

通俗易懂地讲解一下 BLE&#xff08;低功耗蓝牙&#xff09;中的 Resolvable Private Address&#xff08;RPA&#xff0c;可解析私有地址&#xff09;。 1. 一句话理解 RPA 是一种“临时的、隐私保护的蓝牙设备地址”&#xff0c;别人无法随便追踪你&#xff0c;但“授权的设…...

狂神SQL学习笔记九:MyISAM 和 lnnoDB 区别

show create database school –查看创建数据库的语句 show create table student – 查看student数据表的定义语句 desc student –显示表的结构 MYISAMINNODB事务支持不支持支持数据行锁定不支持支持行锁定外键不支持支持全文索引支持不支持表空间的大小较小较大&#x…...

深度学习--神经网络的构造

在当今数字化时代&#xff0c;深度学习已然成为人工智能领域中最为耀眼的明星。而神经网络作为深度学习的核心架构&#xff0c;其构造方式决定了模型的性能与应用效果。本文将深入探讨深度学习神经网络的构造&#xff0c;带您领略这一前沿技术的奥秘。​ 一、神经网络基础概念…...

Jenkins 代理自动化-dotnet程序

两种方式 容器部署 本地部署 容器部署 可自动实现&#xff0c;服务器重启&#xff0c;容器自动运行 主要将dockerfile 写好 本地部署 1.服务器重启自动运行代理 参考下面的链接&#xff0c;只是把程序换成 java程序&#xff0c;提前确认好需要的jdk版本 Ubuntu20.04 设置开机…...

【区块链+ 人才服务】“CERX Network”——基于 FISCO BCOS 的研学资源交换网络 | FISCO BCOS 应用案例

CERX Network &#xff08;Consortium-based Education Resource Exchanging Network&#xff09; 是定位于面向高校科学研究与教学 的分布式研学资产交换网络&#xff0c; 构建一个用于数据、 算法模型、 论文和课程的研学资源价值流转平台。项目以 FISCO BCOS 联盟链为底层平…...

中间件--ClickHouse-6--SQL基础(类似Mysql,存在差异)

ClickHouse语言类似Mysql&#xff0c;如果熟悉Mysql&#xff0c;那么学习ClickHouse的语言还是比较容易上手的。 1、建表语法&#xff08;CREATE TABLE&#xff09; &#xff08;1&#xff09;、表引擎&#xff08;Engine&#xff09; MySQL&#xff1a; 默认使用 InnoDB 引…...

[MSPM0开发]MSPM0G3507番外一:关于使用外部高速晶振HFXT后程序可能不运行的问题

一、问题描述 如下图所示&#xff0c;MSPM0G3507时钟树配置为使用外部HFXT&#xff08;外部高速晶振&#xff09;作为HSCLK时钟源。 配置结果MCLK 40MHz。 另外配置PB22为输出模式&#xff0c;控制外部LED亮灭。 在main.c中主要代码如下&#xff1a; 主要完成延时并翻转LED控…...

2025年计算机领域重大技术突破与行业动态综述

——前沿技术重塑未来&#xff0c;开发者如何把握机遇&#xff1f; 2025年第一季度&#xff0c;全球计算机领域迎来多项里程碑式进展&#xff0c;从量子计算到人工智能&#xff0c;从芯片设计到网络安全&#xff0c;技术革新与产业融合持续加速。本文梳理近三个月内最具影响力…...

我的机器学习之路(初稿)

文章目录 一、机器学习定义二、核心三要素三、算法类型详解1. 监督学习&#xff08;带标签数据&#xff09;2. 无监督学习&#xff08;无标签数据&#xff09;3. 强化学习&#xff08;决策优化&#xff09;(我之后主攻的方向) 四、典型应用场景五、学习路线图六、常见误区警示七…...

交易模式革新:Eagle Trader APP上线,助力自营交易考试效率提升

近年来&#xff0c;金融行业随着投资者需求的日益多样化&#xff0c;衍生出了众多不同的交易方式。例如&#xff0c;为了帮助新手小白建立交易基础&#xff0c;诞生了各类跟单社区&#xff1b;而与此同时&#xff0c;一种备受瞩目的交易方式 —— 自营交易模式&#xff0c;正吸…...

emotn ui桌面tv版官网-emotn ui桌面使用教程

在智能电视和盒子的使用中&#xff0c;出色的桌面系统能大幅提升体验&#xff0c;Emotn UI桌面TV版便是其中的佼佼者。 访问Emotn UI桌面TV版官网&#xff0c;首页简洁清晰&#xff0c;“产品介绍”“下载中心”等板块一目了然。官网对其功能优势详细阐述&#xff0c;在“下载中…...

Django之modelform使用

Django新增修改数据功能优化 目录 1.新增数据功能优化 2.修改数据功能优化 在我们做数据优化处理之前, 我们先回顾下传统的写法, 是如何实现增加修改的。 我们需要在templates里面新建前端的页面, 需要有新增还要删除, 比如说员工数据的新增, 那需要有很多个输入框, 那html…...

Hadoop:大数据时代的基石

在当今数字化浪潮中&#xff0c;数据量呈爆炸式增长&#xff0c;企业和组织面临着前所未有的数据处理挑战。从社交媒体的海量信息到物联网设备的实时数据&#xff0c;如何高效地存储、管理和分析这些数据成为了一个关键问题。Apache Hadoop 作为大数据处理领域的核心框架&#…...

定制开发还是源码搭建?如何快速上线同城外卖跑腿APP?

在“万物皆可同城配送”的时代&#xff0c;同城外卖跑腿APP成为众多创业者和本地服务商的热门选择。无论是打造本地生活服务平台&#xff0c;还是拓展快送业务&#xff0c;拥有一款功能完善、体验流畅的外卖跑腿APP&#xff0c;已经成为进入市场的标配。 然而&#xff0c;对于…...

How AI could empower any business - Andrew Ng

How AI could empower any business - Andrew Ng References 人工智能如何为任何业务提供支持 empower /ɪmˈpaʊə(r)/ vt. 授权&#xff1b;给 (某人) ...的权力&#xff1b;使控制局势&#xff1b;增加 (某人的) 自主权When I think about the rise of AI, I’m reminded …...

SpringBoot-基础特性

1.SpringApplication 1.1.自定义banner 类路径添加banner.txt或设置spring.banner.location就可以定制 banner 1.2.自定义 SpringApplication import org.springframework.boot.Banner; import org.springframework.boot.SpringApplication; import org.springframework.bo…...

系统环境变量有什么实际作用,为什么要配置它

系统环境变量有什么实际作用,为什么要配置它 系统环境变量具有以下重要实际作用: 指定程序路径:操作系统通过环境变量来知晓可执行文件、库文件等的存储位置例如,当你在命令提示符或终端中输入一个命令时,系统会根据环境变量PATH中指定的路径去查找对应的可执行文件。如果…...