Android使用OpenGL和MediaCodec录制
目录
一,什么是opengl
二,什么是Android OpenGL ES
三, OpenGL 绘制流程
四, OpenGL坐标系
五, OpenGL 着色器
六, GLSL编程语言
七,使用MediaCodec录制在Opengl中渲染架构
八,代码实现
8.1 自定义渲染view继承GLSurfaceView
8.2 自定义渲染器TigerRender
8.3 创建编码器MediaRecorder
8.4 配置egl环境
8.5 配置shader
一,什么是opengl
Open Graphics Library 图形领域的工业标准,是一套跨编程语言、跨平台的、专业的图形编程(软件)接口。它用于二维、三维图像,是一个功能强大,调用方便的底层图形库。 与硬件无关。可以在不同的平台如Windows、Linux、Mac、Android、IOS之间进行移植。因此,支持OpenGL的软件具有很好的移植性,可以获得非常广泛的应用。
OpenGL ES 1.0 和 1.1 :Android 1.0和更高的版本支持这个API规范。 OpenGL ES 2.0 :Android 2.2(API 8)和更高的版本支持这个API规范。 OpenGL ES 3.0 :Android 4.3(API 18)和更高的版本支持这个API规范。 OpenGL ES 3.1 : Android 5.0(API 21)和更高的版本支持这个API规范
还须要由设备制造商提供了实现支持 目前广泛支持的是2.0 <uses-feature android:glEsVersion="0x00020000" android:required="true"/>
二,什么是Android OpenGL ES
针对手机、PDA和游戏主机等嵌入式设备而设计的OpenGL API 子集。
GLSurfaceView
继承至SurfaceView,它内嵌的surface专门负责OpenGL渲染。 管理Surface与EGL 允许自定义渲染器(render)。 让渲染器在独立的线程里运作,和UI线程分离。 支持按需渲染(on-demand)和连续渲染(continuous)。
OpenGL是一个跨平台的操作GPU的API,但OpenGL需要本地视窗系统进行交互,这就需要一个中间控制层, EGL就是连接OpenGL ES和本地窗口系统的接口,引入EGL就是为了屏蔽不同平台上的区别。
三, OpenGL 绘制流程

其实就是一个可编程管线pipline

渲染管线做的事情就是让计算机完成图形功能。 固定管线:程序员只能设置参数。比如 f(x)=axx + bx + c程序员只能设置a,b,c的值,却不能修改这个公式。 可编程管线:程序员掌控一切。
四, OpenGL坐标系



五, OpenGL 着色器
着色器(Shader)是运行在GPU上的小程序。顶点着色器(vertex shader) 如何处理顶点、法线等数据的小程序。片元着色器(fragment shader) 如何处理光、阴影、遮挡、环境等等对物体表面的影响,最终生成一副图像的小程序

六, GLSL编程语言


七,使用MediaCodec录制在Opengl中渲染架构

八,代码实现
8.1 自定义渲染view继承GLSurfaceView
public class TigerView extends GLSurfaceView {private TigerRender mTigerRender;//默认正常速度private Speed mSpeed = Speed.MODE_NORMAL;public void setSpeed(Speed speed) {mSpeed = speed;}public enum Speed {MODE_EXTRA_SLOW, MODE_SLOW, MODE_NORMAL, MODE_FAST, MODE_EXTRA_FAST}public TigerView(Context context) {super(context);}public TigerView(Context context, AttributeSet attrs) {super(context, attrs);/*** 设置egl版本*/setEGLContextClientVersion(2);/*** 设置渲染器*/mTigerRender = new TigerRender(this);setRenderer(mTigerRender);/*** 设置按需渲染,当我们调用requestRender()的时候就会调用GlThread回调一次onDrawFrame()*/setRenderMode(RENDERMODE_WHEN_DIRTY);}public void startRecord() {float speed = 1.f;switch (mSpeed) {case MODE_EXTRA_SLOW:speed = 0.3f;break;case MODE_SLOW:speed = 0.5f;break;case MODE_NORMAL:speed = 1.f;break;case MODE_FAST:speed = 1.5f;break;case MODE_EXTRA_FAST:speed = 3.f;break;}mTigerRender.startRecord(speed);}public void stopRecord() {mTigerRender.stopRecord();}
}
8.2 自定义渲染器TigerRender
public class TigerRender implements GLSurfaceView.Renderer, SurfaceTexture.OnFrameAvailableListener {private final TigerView mView;private CameraHelper mCameraHelper;private SurfaceTexture mSurfaceTexture;float[] mtx = new float[16];private ScreeFilter mScreeFilter;private int[] mTextures;private CameraFilter mCameraFilter;private MediaRecorder mMediaRecorder;public TigerRender(TigerView tigerView) {mView = tigerView;}/*** 画布创建好了** @param gl the GL interface. Use <code>instanceof</code> to* test if the interface supports GL11 or higher interfaces.* @param config the EGLConfig of the created surface. Can be used* to create matching pbuffers.*/@Overridepublic void onSurfaceCreated(GL10 gl, EGLConfig config) {mCameraHelper = new CameraHelper(Camera.CameraInfo.CAMERA_FACING_BACK);//准备好摄像头绘制的画布//通过open gl创建一个纹理idmTextures = new int[1];GLES20.glGenTextures(mTextures.length, mTextures, 0);mSurfaceTexture = new SurfaceTexture(mTextures[0]);//设置有一帧新的数据到来的时候,回调监听mSurfaceTexture.setOnFrameAvailableListener(this);//必须要在GlThread里面创建着色器程序mCameraFilter = new CameraFilter(mView.getContext());mScreeFilter = new ScreeFilter(mView.getContext());EGLContext eglContext = EGL14.eglGetCurrentContext();mMediaRecorder = new MediaRecorder(mView.getContext(), "/mnt/sdcard/test.mp4", CameraHelper.HEIGHT, CameraHelper.WIDTH, eglContext);}/*** 画布发生改变** @param gl the GL interface. Use <code>instanceof</code> to* test if the interface supports GL11 or higher interfaces.* @param width* @param height*/@Overridepublic void onSurfaceChanged(GL10 gl, int width, int height) {mCameraHelper.startPreview(mSurfaceTexture);mCameraFilter.onReady(width, height);mScreeFilter.onReady(width, height);}/*** 画画** @param gl the GL interface. Use <code>instanceof</code> to* test if the interface supports GL11 or higher interfaces.*/@Overridepublic void onDrawFrame(GL10 gl) {//告诉open gl需要把屏幕清理成 什么样子的颜色GLES20.glClearColor(0, 0, 0, 0);//开始真正的屏幕颜色清理,也就是上一次设置的屏幕颜色GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);//把摄像头采集的数据输出出来//更新纹理,然后我们才可以使用opengl从SurfaceTexure当中获取数据,进行渲染mSurfaceTexture.updateTexImage();//mSurfaceTexture比较特殊,在设置坐标的时候,需要一个变换矩阵,使用的是特殊的采样器samplerExternalOES//这种采样器,正常的是sample2DmSurfaceTexture.getTransformMatrix(mtx);mCameraFilter.setMatrix(mtx);int id = mCameraFilter.onDrawFrame(mTextures[0]);//在这里添加各种效果,相当于责任链//开始画画mScreeFilter.onDrawFrame(id);mMediaRecorder.encodeFrame(id, mSurfaceTexture.getTimestamp());}/*** SurfaceTexture有一个新的有效的图片的时候会被回调,此时可以把这个数据回调给GLSurfaceView的onDrawFrame** @param surfaceTexture*/@Overridepublic void onFrameAvailable(SurfaceTexture surfaceTexture) {if (mView != null) {//开始渲染,有一帧新的图像,就开始调用GLSurfaceView的onDrawFrame进行绘制mView.requestRender();}}public void startRecord(float speed) {try {mMediaRecorder.start(speed);} catch (IOException e) {throw new RuntimeException(e);}}public void stopRecord() {mMediaRecorder.stop();}
}
8.3 创建编码器MediaRecorder
/*** 视频录制*/
public class MediaRecorder {private final Context mContext;private final String mPath;private final int mWidth;private final int mHeight;private final EGLContext mEglContext;private MediaCodec mMediaCodec;private Surface mInputSurface;private MediaMuxer mMediaMuxer;private Handler mHandler;private EGLBase mEglBase;private boolean isStart;private int index;private float mSpeed;/*** @param context* @param path 视频保存地址* @param width 视频宽* @param height 视频高*/public MediaRecorder(Context context, String path, int width, int height, EGLContext eglContext) {mContext = context.getApplicationContext();mPath = path;mWidth = width;mHeight = height;mEglContext = eglContext;}/*** 开始录制视频*/public void start(float speed) throws IOException {/*** 配置MediaCodec编码器,视频编码的宽,高,帧率,码率* 录制成mp4格式,视频编码格式是h264 MIMETYPE_VIDEO_AVC 高级编码*/mSpeed = speed;MediaFormat videoFormat = MediaFormat.createVideoFormat(MediaFormat.MIMETYPE_VIDEO_AVC, mWidth, mHeight);//配置码率 1500kbsvideoFormat.setInteger(MediaFormat.KEY_BIT_RATE, 1500_000);//帧率videoFormat.setInteger(MediaFormat.KEY_FRAME_RATE, 20);//关键字间隔videoFormat.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, 20);//创建视频高级编码器mMediaCodec = MediaCodec.createEncoderByType(MediaFormat.MIMETYPE_VIDEO_AVC);//因为是从Surface中读取的,所以不需要设置这个颜色格式videoFormat.setInteger(MediaFormat.KEY_COLOR_FORMAT, MediaCodecInfo.CodecCapabilities.COLOR_FormatSurface);//配置编码器,CONFIGURE_FLAG_ENCODE,mMediaCodec.configure(videoFormat, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE);//交给虚拟屏幕,通过opengl 将预览的纹理绘制到这一个虚拟屏幕中,这样子Mediacodc 就会自动编码这一帧图像mInputSurface = mMediaCodec.createInputSurface();//mp4 播放流程 解复用--》解码 》绘制//mp4 编码流程 封装器--》编码//MUXER_OUTPUT_MPEG_4 MP4格式封装器,将h.264通过他写出到文件就可以了mMediaMuxer = new MediaMuxer(mPath, MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4);/*** 配置EGL环境,也就是配置我们的虚拟屏幕环境*/HandlerThread handlerThread = new HandlerThread("ViewoCodec");handlerThread.start();Looper looper = handlerThread.getLooper();//子线程和子线程之间的通信mHandler = new Handler(looper);//EGl的绑定线程,对我们自己创建的EGl环境,都是在这个线程里面进行mHandler.post(() -> {//创建我们的EGL环境(虚拟设备,EGL上下文 )mEglBase = new EGLBase(mContext, mWidth, mHeight, mInputSurface, mEglContext);//启动编码器mMediaCodec.start();isStart = true;});}/*** textureId 纹理id* 调用一次,就有一个新的图片需要编码*/public void encodeFrame(int textureId, long timesnap) {if (!isStart) {return;}//切换到子线程中编码mHandler.post(() -> {//把图像纹理画到虚拟屏幕里面mEglBase.draw(textureId, timesnap);//此时我们需要从编码器里面的输出缓冲区获取编码以后的数据就可以了,getCodec(false);});}private void getCodec(boolean endOfStream) {if (endOfStream) {//表示停止录制,此时我们不录制了,需要给mediacoic 通知mMediaCodec.signalEndOfInputStream();}MediaCodec.BufferInfo bufferInfo = new MediaCodec.BufferInfo();//希望将已经编码完成的数据都获取到,然后写出到mp4文件中while (true) {//并不是传给mediacodec一帧数据就表示可以编码出一帧数据,有可能需要好多帧数据才可以同时编码出数据出来//输出缓冲区//传递-1表示一直等到输出缓冲区有一个编码好的有效的数据以后才会继续向下走,不然就会一直卡在127行,//10_000超时时间int status = mMediaCodec.dequeueOutputBuffer(bufferInfo, 10_000);if (status == MediaCodec.INFO_TRY_AGAIN_LATER) {//如果是停止,就继续循环,// 继续循环就表示不会接收到新的等待编码的图像了//相当于保证mediacodic中所有待编码的数据都编码完成// 标记不是停止,我们退出,下一轮接收到更多的数据才来输出编码以后的数据,我们就让继续走// 表示需要更多数据才可以编码出图像 false是继续录制,未来还有机会在调用getCodecif (!endOfStream) {//结束录制了break;}//否则继续} else if (status == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) {//开始编码,就会调用一次MediaFormat outputFormat = mMediaCodec.getOutputFormat();//配置封装器,增加一路指定格式的媒体流index = mMediaMuxer.addTrack(outputFormat);//启动封装器mMediaMuxer.start();} else if (status == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED) {//忽略} else {//成功取出一个有效的输出ByteBuffer outputBuffer = mMediaCodec.getOutputBuffer(status);if ((bufferInfo.flags & MediaCodec.BUFFER_FLAG_CODEC_CONFIG) != 0) {//如果获取的ByteBuffer是配置信息,那么就不需要写出到mp4文件中bufferInfo.size = 0;}if (bufferInfo.size != 0) {bufferInfo.presentationTimeUs = (long) (bufferInfo.presentationTimeUs / mSpeed);//写出到 mp4文件中//根据偏移定位去获取数据,而不是从0开始outputBuffer.position(bufferInfo.offset);//设置可读可写的总长度outputBuffer.limit(bufferInfo.offset + bufferInfo.size);mMediaMuxer.writeSampleData(index, outputBuffer, bufferInfo);}//输出缓冲区使用完毕了, 此时就可以回收了,让mediacodec继续使用mMediaCodec.releaseOutputBuffer(status, false);if ((bufferInfo.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) {//结束了,break;}}}}public void stop() {isStart = false;mHandler.post(() -> {getCodec(true);mMediaCodec.stop();mMediaCodec.release();mMediaCodec = null;mMediaMuxer.stop();mMediaMuxer.release();mMediaMuxer = null;mEglBase.release();mEglBase = null;mInputSurface = null;mHandler.getLooper().quitSafely();mHandler = null;});}
}
8.4 配置egl环境
/*** EGL配置 和录制opengl的操作*/
public class EGLBase {private final EGLSurface mEglSurface;private final ScreeFilter mScreeFilter;private EGLDisplay mEglDisplay;private EGLConfig mEglConfig;private EGLContext mEGLContext;/*** @param context* @param width* @param height* @param surface MediaCodec创建的surface, 我们需要将这个surface贴到虚拟屏幕里面*/public EGLBase(Context context, int width, int height, Surface surface, EGLContext eglContext) {createEGL(eglContext);//把surface贴到EGLDisplay 虚拟屏幕里面int[] attrib_list = {//不需要配置什么属性EGL14.EGL_NONE};//就是向mEglDisplay这个虚拟屏幕上面画画mEglSurface = EGL14.eglCreateWindowSurface(mEglDisplay, mEglConfig, surface, attrib_list, 0);//必须要绑定当前线程的显示上下文,不然就绘制不上去,这样子之后操作的opelgl就是在这个虚拟屏幕上操作,读和写都是在同一个surface里面if (!EGL14.eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, mEGLContext)) {throw new RuntimeException("eglMakeCurrent failed");}//向虚拟屏幕画画mScreeFilter = new ScreeFilter(context);mScreeFilter.onReady(width, height);}private void createEGL(EGLContext eglContext) {//创建虚拟显示器mEglDisplay = EGL14.eglGetDisplay(EGL14.EGL_DEFAULT_DISPLAY);if (mEglDisplay == EGL14.EGL_NO_DISPLAY) {throw new RuntimeException("eglGetDisplay failed");}int[] version = new int[2];//初始化虚拟设备if (!EGL14.eglInitialize(mEglDisplay, version, 0, version, 1)) {throw new RuntimeException("eglInitialize failed");}int[] attrib_list = new int[]{//rgba 红绿蓝透明度EGL14.EGL_RED_SIZE, 8, EGL14.EGL_GREEN_SIZE, 8, EGL14.EGL_BLUE_SIZE, 8, EGL14.EGL_ALPHA_SIZE, 8, EGL14.EGL_RENDERABLE_TYPE, EGL14.EGL_OPENGL_ES2_BIT,//和egl的版本有关系EGL14.EGL_NONE//这个很重要,一定要配置为NONE,表示配置结束了};EGLConfig[] configs = new EGLConfig[1];int[] num_config = new int[1];boolean eglChooseConfig = EGL14.eglChooseConfig(mEglDisplay, attrib_list, 0, configs, 0, configs.length, num_config, 0);if (!eglChooseConfig) {//如果配置失败throw new IllegalArgumentException("eglChooseConfig failed");}mEglConfig = configs[0];int[] attriblist = {EGL14.EGL_CONTEXT_CLIENT_VERSION, 2, EGL14.EGL_NONE};//创建EGL上下文//share_context 共享上下,传递绘制线程的(GLThread)EGL上下文,达到共享资源的目的mEGLContext = EGL14.eglCreateContext(mEglDisplay, mEglConfig, eglContext, attriblist, 0);if (mEGLContext == null || mEGLContext == EGL14.EGL_NO_CONTEXT) {mEGLContext = null;throw new RuntimeException("createContex error !");}}/*** @param textureId 纹理id,代表一张图片* @param timesnap 时间戳*/public void draw(int textureId, long timesnap) {//必须要绑定当前线程的显示上下文,不然就绘制不上去,这样子之后操作的opelgl就是在这个虚拟屏幕上操作,读和写都是在同一个surface里面//画画之前也必须要绑定if (!EGL14.eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, mEGLContext)) {throw new RuntimeException("eglMakeCurrent failed");}//向虚拟屏幕画画mScreeFilter.onDrawFrame(textureId);//刷新eglSurface时间戳EGLExt.eglPresentationTimeANDROID(mEglDisplay, mEglSurface, timesnap);//交换数据,EGL工作模式,双缓存模式,内部有两个frameBuff,当EGL将一个frame显示到屏幕上以后,// 另一个frame就在后台等待opengl进行交换//也就是画完一次,交换一次EGL14.eglSwapBuffers(mEglDisplay, mEglSurface);}public void release() {EGL14.eglDestroySurface(mEglDisplay, mEglSurface);EGL14.eglMakeCurrent(mEglDisplay, EGL14.EGL_NO_SURFACE, EGL14.EGL_NO_SURFACE, EGL14.EGL_NO_CONTEXT);EGL14.eglDestroyContext(mEglDisplay, mEGLContext);EGL14.eglReleaseThread();EGL14.eglTerminate(mEglDisplay);}
}
8.5 配置shader
public class AbstractFilter {protected FloatBuffer mGLVertexBuffer;protected FloatBuffer mGLTextureBuffer;//顶点着色protected int mVertexShaderId;//片段着色protected int mFragmentShaderId;protected int mGLProgramId;/*** 顶点着色器* attribute vec4 position;* 赋值给gl_Position(顶点)*/protected int vPosition;/*** varying vec2 textureCoordinate;*/protected int vCoord;/*** uniform mat4 vMatrix;*/protected int vMatrix;/*** 片元着色器* Samlpe2D 扩展 samplerExternalOES*/protected int vTexture;protected int mOutputWidth;protected int mOutputHeight;public AbstractFilter(Context context, int vertexShaderId, int fragmentShaderId) {this.mVertexShaderId = vertexShaderId;this.mFragmentShaderId = fragmentShaderId;// 4个点 x,y = 4*2 float 4字节 所以 4*2*4mGLVertexBuffer = ByteBuffer.allocateDirect(4 * 2 * 4).order(ByteOrder.nativeOrder()).asFloatBuffer();mGLVertexBuffer.clear();float[] VERTEX = {-1.0f, -1.0f, 1.0f, -1.0f, -1.0f, 1.0f, 1.0f, 1.0f};mGLVertexBuffer.put(VERTEX);mGLTextureBuffer = ByteBuffer.allocateDirect(4 * 2 * 4).order(ByteOrder.nativeOrder()).asFloatBuffer();mGLTextureBuffer.clear();float[] TEXTURE = {0.0f, 1.0f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f};mGLTextureBuffer.put(TEXTURE);initilize(context);initCoordinate();}protected void initilize(Context context) {String vertexSharder = OpenGLUtils.readRawTextFile(context, mVertexShaderId);String framentShader = OpenGLUtils.readRawTextFile(context, mFragmentShaderId);mGLProgramId = OpenGLUtils.loadProgram(vertexSharder, framentShader);// 获得着色器中的 attribute 变量 position 的索引值vPosition = GLES20.glGetAttribLocation(mGLProgramId, "vPosition");vCoord = GLES20.glGetAttribLocation(mGLProgramId, "vCoord");vMatrix = GLES20.glGetUniformLocation(mGLProgramId, "vMatrix");// 获得Uniform变量的索引值vTexture = GLES20.glGetUniformLocation(mGLProgramId, "vTexture");}public void onReady(int width, int height) {mOutputWidth = width;mOutputHeight = height;}public void release() {GLES20.glDeleteProgram(mGLProgramId);}public int onDrawFrame(int textureId) {//设置显示窗口GLES20.glViewport(0, 0, mOutputWidth, mOutputHeight);//使用着色器GLES20.glUseProgram(mGLProgramId);//传递坐标mGLVertexBuffer.position(0);GLES20.glVertexAttribPointer(vPosition, 2, GLES20.GL_FLOAT, false, 0, mGLVertexBuffer);GLES20.glEnableVertexAttribArray(vPosition);mGLTextureBuffer.position(0);GLES20.glVertexAttribPointer(vCoord, 2, GLES20.GL_FLOAT, false, 0, mGLTextureBuffer);GLES20.glEnableVertexAttribArray(vCoord);GLES20.glActiveTexture(GLES20.GL_TEXTURE0);//绑定GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textureId);GLES20.glUniform1i(vTexture, 0);GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4);//解绑GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, 0);return textureId;}//修改坐标protected void initCoordinate() {}
}
相关文章:
Android使用OpenGL和MediaCodec录制
目录 一,什么是opengl 二,什么是Android OpenGL ES 三, OpenGL 绘制流程 四, OpenGL坐标系 五, OpenGL 着色器 六, GLSL编程语言 七,使用MediaCodec录制在Opengl中渲染架构 八,代码实现 8.1 自定义渲染view继承GLSurfaceView 8.2 自定义渲染器TigerRender 8.3 创建编…...
《如何避免虚无》速读笔记
文章目录 书籍信息概览躺派(出世)卷派(入世)虚无篇:直面虚无自我篇:认识自我孤独篇:应对孤独幸福篇:追寻幸福超越篇:超越自我 书籍信息 书名:《如何避免虚无…...
哈尔滨工业大学:大模型时代的具身智能
大家好,我是樱木。 机器人在工业领域,已经逐渐成熟。具身容易,智能难。 机器人-》智能机器人,需要自主能力,加上通用能力。 智能机器人-》人类,这个阶段就太有想象空间了。而最受关注的-类人机器人。 如何…...
19.go日志包log
核心功能与接口 基础日志输出 Print 系列:支持 Print()、Println()、Printf(),输出日志不中断程序。 log.Print("常规日志") // 输出: 2025/03/18 14:47:13 常规日志 log.Printf("格式化: %s", "数据") Fatal…...
理解OSPF 特殊区域NSSA和各类LSA特点
本文基于上文 理解OSPF Stub区域和各类LSA特点 在理解了Stub区域之后,我们再来理解一下NSSA区域,NSSA区域用于需要引入少量外部路由,同时又需要保持Stub区域特性的情况 一、 网络总拓扑图 我们在R1上配置黑洞路由,来模拟NSSA区域…...
如何通过优化HMI设计大幅提升产品竞争力?
一、HMI设计的重要性与竞争力提升 HMI(人机交互界面)设计在现代产品开发中扮演着至关重要的角色。良好的HMI设计不仅能够提升用户体验,还能显著增强产品的竞争力。在功能趋同的市场环境中,用户体验成为产品竞争的关键。HMI设计通…...
Linux信号——信号的处理(3)
信号是什么时候被处理? 进程从内核态,切换到用户态的时候,信号会被检测处理。 内核态:操作系统的状态,权限级别高 用户态:你自己的状态 内核态和用户态 进程地址空间第三次 所谓的系统调用本质其实是一堆…...
Pod的调度
在默认情况下,一个Pod在哪个Node节点上运行,是由Scheduler组件采用相应的算法计算出来的,这个过程是不受人工控制的。但是在实际使用中,这并不满足的需求,因为很多情况下,我们想控制某些Pod到达某些节点上&…...
LabVIEW面向对象编程设计方法
一、概述 面向对象编程(OOP)在软件开发中占据重要地位,尤其是在大规模软件项目中。它与小型程序开发思路不同,更注重未来功能的升级与扩展。在设计阶段,需思考如何构建既灵活又稳定的系统,这涉及众多设计方…...
Spring常见问题复习
############Spring############# Bean的生命周期是什么? BeanFactory和FactoryBean的区别? ApplicationContext和BeanFactory的区别? BeanFactoryAware注解,还有什么其它的Aware注解 BeanFactoryAware方法和Bean注解的方法执行顺…...
JJJ:generic netlink例程分析
接嵌入式毕设、课设辅导、技术咨询,欢迎私信 完整代码:github代码仓链接 若想要和指定的generic netlink family通信,如: 994 static struct genl_family genl_ctrl __ro_after_init { // generic netlink子协议995 .module THIS_MODU…...
Dify票据识别遇到的分支判断不准确问题
已测试这篇文章中 https://zhuanlan.zhihu.com/p/5465385787 使用多分支条件判断使用不同的大模型识别图片内容 发现了细节问题。在使用时若不注意,分支会出现走向不准的问题。 需要关注部分 下方红框处。1,2后不能跟点。否则会出问。除此之外࿰…...
《全栈+双客户端Turnkey方案》架构设计图
今天分享一些全栈双客户端Turnkey方案的架构与结构图。 1:三种分布式部署方案:网关方案,超级服务器单服方案,直连逻辑服方案 2: 单服多线程核心架构: 系统服务逻辑服服务 3: 系统服务的多线程池调度设计 4:LogicServer Update与ECS架构&…...
某碰瓷国赛美赛,号称第三赛事的数模竞赛
首先我非常不能理解的就是怎么好意思自称第三赛事的呢?下面我们进行一个简单讨论,当然这里不对国赛和美赛进行讨论。首先我们来明确一点,比赛的含金量由什么来定?这个可能大家的评价指标可能不唯一,我通过DeepSeek选取…...
【代码模板】如何用FILE操作符打开文件?fopen、fclose
#include "stdio.h" #include "unistd.h"int main(int argc, char *argv[]) {FILE *fp fopen("1.log", "wb");if (!fp) {perror("Failed open 1.log");return -1;}fclose(fp); }关于权限部分参考兄弟篇【代码模板】C语言中…...
【大模型深度学习】如何估算大模型需要的显存
一、模型参数量 参数量的单位 参数量指的是模型中所有权重和偏置的数量总和。在大模型中,参数量的单位通常以“百万”(M)或“亿”(B,也常说十亿)来表示。 百万(M):表示…...
Mysql 数据库编程技术01
一、数据库基础 1.1 认识数据库 为什么学习数据库 瞬时数据:比如内存中的数据,是不能永久保存的。持久化数据:比如持久化至数据库中或者文档中,能够长久保存。 数据库是“按照数据结构来组织、存储和管理数据的仓库”。是一个长…...
Class<?> 和Class<T >有什么区别
Class<?> 和 Class<T> 在 Java 中都表示 Class 类型的对象,但它们的使用方式和作用略有不同。让我们详细分析它们的区别: 1. Class<?>(通配符 Class 类型) ? 代表一个未知类型(Wildcard…...
[自制调试工具]利用模板函数打造通用调试工具
引言 上一篇文章 我们介绍了调式类工具,这篇文章我们补充一下 点击这里查看 在软件开发的过程中,调试是必不可少的环节。为了能更高效地定位和解决问题,我们常常需要在代码中插入一些调试信息,来输出变量的值、函数的执行状态等。传统的调试…...
Python地理数据处理 28:基于Arcpy批量操作实现——按属性提取和分区统计
Arcpy批量操作 1. 批量按属性提取2. 批量分区统计(最大值、最小值和像元个数等) 1. 批量按属性提取 # -*- coding: cp936 -*- """ PROJECT_NAME: ArcPy FILE_NAME: batch_attribute_extract AUTHOR: JacksonZhao DATE: 2025/04/05 &qu…...
Mysql慢查询设置 和 建立索引
1 .mysql慢查询的设置 slow_query_log ON //或 slow_query_log_file /usr/local/mysql/data/slow.log long_query_time 2 修改后重启动mysql 1.1 查看设置后的参数 mysql> show variables like slow_query%; --------------------------------------------------…...
【Android】界面布局-相对布局RelativeLayout-例子
题目 完成下面相对布局,要求: 中间的button在整个屏幕的中央,其他的以它为基准排列。Hints:利用layout_toEndof,_toRightof,_toLeftof,_toStartof完成。 结果演示 代码实现 <?xml version"1.0" encoding"u…...
Spring Boot 中使用 Redis:从入门到实战
🌟 前言 欢迎来到我的技术小宇宙!🌌 这里不仅是我记录技术点滴的后花园,也是我分享学习心得和项目经验的乐园。📚 无论你是技术小白还是资深大牛,这里总有一些内容能触动你的好奇心。🔍 &#x…...
【ROS】 CMakeLists 文件详解
【ROS】 CMakeLists文件详解 前言标准的CMAKELIST.TXT文件的组成部分CMake 版本要求和项目名称指定编译器和设置构建规则查找 ROS 依赖消息和服务文件catkin_package设置头文件目录路径添加可执行文件的构建规则设置编译依赖关系(构建顺序)设置目标文件的…...
【每日算法】Day 17-1:位图(Bitmap)——十亿级数据去重与快速检索的终极方案(C++实现)
解锁海量数据处理的极致空间效率!今日深入解析位图的核心原理与实战应用,从基础操作到分块优化,彻底掌握仅用1bit存储一个数据的压缩艺术。 一、位图核心思想 位图(Bitmap) 是一种通过比特位表示数据存在性的数据结构…...
7-1 素数求和(线性筛实现)
7-1 素数求和。 分数 10 中等 全屏浏览 切换布局 作者 魏英 单位 浙江科技大学 输入两个正整数m和n(1<m<n<500)统计并输出m和n之间的素数个数以及这些素数的和。 输入格式: 输入两个正整数m和n(1<m<n<500࿰…...
NLP简介及其发展历史
自然语言处理(Natural Language Processing,简称NLP)是人工智能和计算机科学领域中的一个重要分支,致力于实现人与计算机之间自然、高效的语言交流。本文将介绍NLP的基本概念以及其发展历史。 一、什么是自然语言处理?…...
ZKmall开源商城多云高可用架构方案:AWS/Azure/阿里云全栈实践
随着企业数字化转型的加速,云计算服务已成为IT战略中的核心部分。ZKmall开源商城作为一款高性能的开源商城系统,其在多云环境下的高可用架构方案备受关注。下面将结合AWS、Azure和阿里云三大主流云平台,探讨ZKmall的多云高可用架构全栈实践。…...
优化 Web 性能:处理非合成动画(Non-Composited Animations)
在 Web 开发中,动画能够增强用户体验,但低效的动画实现可能导致性能问题。Google 的 Lighthouse 工具在性能审计中特别关注“非合成动画”(Non-Composited Animations),指出这些动画可能增加主线程负担,影响…...
Eliet Chat开发日志:信令服务器注册与通信过程
目录 1. 架构设计:信令服务器与客户端 2. 选择技术栈 3. 实现信令服务器 4. 客户端实现 5. 测试 6. 下一步计划 日期:2025年4月5日 今天的工作重点是实现两个设备通过信令服务器注册并请求对方公网地址信息,以便能够进行点对点通信。我…...
