Android MediaPlayer + GLSurfaceView 播放视频

Android使用OpenGL 播放视频

    • 概述
      • TextureView的优缺点
      • OpenGL的优缺点
    • 实现
    • 复杂图形效果的场景
    • 参考

概述

    在Android开发中,使用OpenGL ES来渲染视频是一种常见的需求,尤其是在需要实现自定义的视频播放界面或者视频特效时。结合MediaPlayer,我们可以实现一个功能强大的视频播放器。以下是一个简单的示例,展示如何在Android应用中使用OpenGL ES和MediaPlayer播放本地视频。
    常规的视频播放方式有VideoView, MediaPlayer + SurfaceView / TextureView, 以TextureView为例, 它与OpenGL(GLSurfaceView)播放的一些比较:

TextureView的优缺点

  • 优点
    • 灵活性高:TextureView可以与其他View叠加使用,非常适合在复杂的视图层次结构中使用。
    • 硬件加速支持:由于它在硬件加速层进行渲染,其性能也较优。
    • 支持绘制操作:可以从其他线程更新内容,适合用于播放视频、显示实时特效等。
  • 缺点
    • 内存占用较高:TextureView的内部缓冲队列导致比SurfaceView使用更多的内存。
    • 在5.0以前在主线程渲染:在5.0版本之前,TextureView在主线程渲染,可能会导致性能问题。

OpenGL的优缺点

  • 优点
    • 高度定制化:OpenGL提供低级别的图形渲染接口,允许开发者高度定制视频播放界面和特效。
    • 性能优化:通过优化渲染代码,可以在一定程度上提高视频播放的效率和性能。
  • 缺点
    • 开发复杂度较高:使用OpenGL需要编写大量的底层代码,包括顶点着色器和片段着色器的编写,这增加了开发的复杂度和难度。

    TextureView在灵活性、硬件加速支持和多线程更新方面具有优势,适合需要与其他视图交互的场景。而OpenGL则提供了更高的定制化程度,适合需要实现复杂图形效果的场景

实现

    梳理收集来的参考代码, 实现视频播放效果如下:
在这里插入图片描述

GLVideoActivity.java

package com.ansondroider.sdktester.activity;import android.media.MediaPlayer;
import android.opengl.GLSurfaceView;
import android.os.Bundle;import com.ansondroider.acore.BaseActivity;
import com.ansondroider.sdktester.gl.GLVideoView;import java.io.IOException;public class GLVideoActivity extends BaseActivity {MediaPlayer mmp;GLVideoView glView;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);glView = new GLVideoView(this);setContentView(glView);}@Overrideprotected void onStart() {super.onStart();//postDelayed(new Runnable() {//    @Override//    public void run() {mmp = new MediaPlayer();try {mmp.setDataSource("/sdcard/Movies/376463.mp4");mmp.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {@Overridepublic void onPrepared(MediaPlayer mediaPlayer) {glView.onVideoPrepared(mediaPlayer);}});mmp.prepare();} catch (IOException e) {e.printStackTrace();}//    }//}, 100);}@Overrideprotected void onStop() {super.onStop();mmp.stop();mmp.release();}
}

GLVideoView.java

package com.ansondroider.sdktester.gl;
import android.content.Context;
import android.graphics.SurfaceTexture;
import android.media.MediaPlayer;
import android.opengl.GLES11Ext;
import android.opengl.GLES20;
import android.opengl.GLSurfaceView;
import android.opengl.Matrix;
import android.util.AttributeSet;
import android.util.Log;
import android.view.Surface;import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.opengles.GL10;
/*** @ProjectName: TheSimpllestplayer* @Package: com.yw.thesimpllestplayer.renderview* @ClassName: VideoDrawer* @Description: 视频渲染器* @Author: wei.yang* @CreateDate: 2021/11/6 14:23* @UpdateUser: 更新者:wei.yang* @UpdateDate: 2021/11/6 14:23* @UpdateRemark: 更新说明:* @Version: 1.0*/
public class GLVideoView extends GLSurfaceView {final String TAG = "GLVideoView";//顶点坐标,此处的坐标系是物体坐标系:中心店坐标是(0,0)private float[] mVertexCoors = new float[]{-1f, -1f,1f, -1f,-1f, 1f,1f, 1f};//纹理坐标系,中心坐标点为(0.5,0.5),上方向为t从0~1,右边方向为s,从0~1.刚好和计算器物理坐标系是反过来的。private float[] mTextureCoors = new float[]{0f, 1f,1f, 1f,0f, 0f,1f, 0f};private String vertextShaderSource = "attribute vec4 aPosition;" +"precision mediump float;" +"uniform mat4 uMatrix;" +"attribute vec2 aCoordinate;" +"varying vec2 vCoordinate;" +"attribute float alpha;" +"varying float inAlpha;" +"void main(){" +"gl_Position = uMatrix*aPosition;" +"vCoordinate = aCoordinate;" +"inAlpha = alpha;" +"}";private String fragmentShaderSource = "#extension GL_OES_EGL_image_external : require\n" +"precision mediump float;" +"varying vec2 vCoordinate;" +"varying float inAlpha;" +"uniform samplerExternalOES uTexture;" +"void main() {" +"vec4 color = texture2D(uTexture, vCoordinate);" +"gl_FragColor = vec4(color.r, color.g, color.b, inAlpha);" +"}";//视频宽高private int mVideoWidth = -1;private int mVideoHeight = -1;//物理屏幕的宽高private int mWorldWidth = -1;private int mWorldHeight = -1;//纹理IDprivate int mTextureId = -1;//定义SurfaceTexture 为显示视频做准备;private SurfaceTexture mSurfaceTexture = null;// 定义OpenGL 程序IDprivate int mProgram = -1;//矩阵变换接受者(shader中)private int mVertexMatrixHandler = -1;//顶点坐标接收者private int mVertexPosHandler = -1;//纹理坐标接受者private int mTexturePosHandler = -1;//纹理接受者private int mTextureHandler = -1;//半透明值接受者private int mAlphaHandler = -1;//顶点缓冲private FloatBuffer mVertexBuffer = null;//纹理缓冲private FloatBuffer mTextureBuffer = null;//矩阵private float[] mMatrix = null;//透明度private float mAlpha = 1f;//旋转角度private float mWidthRatio = 1f;private float mHeightRatio = 1f;private int floatLength = 16;public GLVideoView(Context context) {super(context);init();}public GLVideoView(Context context, AttributeSet attrs) {super(context, attrs);init();}private void init(){setEGLContextClientVersion(2);setRenderer(new VideoRender());setRenderMode(RENDERMODE_WHEN_DIRTY);initPos();}/*** 初始化顶点坐标*/private void initPos() {ByteBuffer vByteBuffer = ByteBuffer.allocateDirect(mVertexCoors.length * 4);vByteBuffer.order(ByteOrder.nativeOrder());//将坐标转换为floatbuffer,用以传给opengl程序mVertexBuffer = vByteBuffer.asFloatBuffer();mVertexBuffer.put(mVertexCoors);mVertexBuffer.position(0);ByteBuffer tByteBuffer = ByteBuffer.allocateDirect(mTextureCoors.length * 4);tByteBuffer.order(ByteOrder.nativeOrder());mTextureBuffer = tByteBuffer.asFloatBuffer();mTextureBuffer.put(mTextureCoors);mTextureBuffer.position(0);}/*** 初始化矩阵变换,主要是防止视频拉伸变形*/private void initDefMatrix() {//Log.d(TAG, "initDefMatrix");if (mMatrix != null) return;if (mVideoWidth != -1 && mVideoHeight != -1 &&mWorldWidth != -1 && mWorldHeight != -1) {mMatrix = new float[floatLength];float[] prjMatrix = new float[floatLength];float originRatio = mVideoWidth / (float) mVideoHeight;float worldRatio = mWorldWidth / (float) mWorldHeight;if (mWorldWidth > mWorldHeight) {if (originRatio > worldRatio) {mHeightRatio = originRatio / worldRatio;Matrix.orthoM(prjMatrix, 0,-mWidthRatio, mWidthRatio,-mHeightRatio, mHeightRatio,3f, 5f);} else {// 原始比例小于窗口比例,缩放高度度会导致高度超出,因此,高度以窗口为准,缩放宽度mWidthRatio = worldRatio / originRatio;Matrix.orthoM(prjMatrix, 0,-mWidthRatio, mWidthRatio,-mHeightRatio, mHeightRatio,3f, 5f);}} else {if (originRatio > worldRatio) {mHeightRatio = originRatio / worldRatio;Matrix.orthoM(prjMatrix, 0,-mWidthRatio, mWidthRatio,-mHeightRatio, mHeightRatio,3f, 5f);} else {// 原始比例小于窗口比例,缩放高度会导致高度超出,因此,高度以窗口为准,缩放宽度mWidthRatio = worldRatio / originRatio;Matrix.orthoM(prjMatrix, 0,-mWidthRatio, mWidthRatio,-mHeightRatio, mHeightRatio,3f, 5f);}}//设置相机位置float[] viewMatrix = new float[floatLength];Matrix.setLookAtM(viewMatrix, 0,0f, 0f, 5.0f,0f, 0f, 0f,0f, 1.0f, 0f);//计算变换矩阵Matrix.multiplyMM(mMatrix, 0, prjMatrix, 0, viewMatrix, 0);}}private Surface mSurface = null;MediaPlayer mMediaPlayer;public void onVideoPrepared(MediaPlayer mp){Log.d(TAG, "onVideoPrepared");mMediaPlayer = mp;if(mSurfaceTexture != null) {int videoWidth = mMediaPlayer.getVideoWidth();int videoHeight = mMediaPlayer.getVideoHeight();setVideoSize(videoWidth, videoHeight);mSurface = new Surface(mSurfaceTexture);mMediaPlayer.setSurface(mSurface);mMediaPlayer.start();}}private void setVideoSize(int videoWidth, int videoHeight) {Log.d(TAG, "setVideoSize " + videoWidth + "x" + videoHeight);mVideoWidth = videoWidth;mVideoHeight = videoHeight;}private void setWorldSize(int worldWidth, int worldHeight) {mWorldWidth = worldWidth;mWorldHeight = worldHeight;}@Overridepublic void setAlpha(float alpha) {super.setAlpha(alpha);mAlpha = alpha;}private SurfaceTexture getSurfaceTexture() {return mSurfaceTexture;}/*** 创建并使用opengles程序*/private void createGLPrg() {if (mProgram == -1) {int vertexShader = loadShader(GLES20.GL_VERTEX_SHADER, vertextShaderSource);int fragmentShader = loadShader(GLES20.GL_FRAGMENT_SHADER, fragmentShaderSource);//创建programe陈谷mProgram = GLES20.glCreateProgram();//将顶点着色器加入程序GLES20.glAttachShader(mProgram, vertexShader);//将片元着色器加入程序GLES20.glAttachShader(mProgram, fragmentShader);GLES20.glLinkProgram(mProgram);//从程序中获取句柄mVertexMatrixHandler = GLES20.glGetUniformLocation(mProgram, "uMatrix");mVertexPosHandler = GLES20.glGetAttribLocation(mProgram, "aPosition");mTextureHandler = GLES20.glGetUniformLocation(mProgram, "uTexture");mTexturePosHandler = GLES20.glGetAttribLocation(mProgram, "aCoordinate");mAlphaHandler = GLES20.glGetAttribLocation(mProgram, "alpha");}//使用opengl程序GLES20.glUseProgram(mProgram);}/*** 激活并绑定纹理单元*/private void activateTexture() {//激活指定纹理单元GLES20.glActiveTexture(GLES20.GL_TEXTURE0);//绑定纹理ID到纹理单元GLES20.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, mTextureId);//将激活并绑定的纹理id传递到着色器里面GLES20.glUniform1i(mTextureHandler, 0);//配置边缘过滤参数GLES20.glTexParameterf(GLES11Ext.GL_TEXTURE_EXTERNAL_OES,GLES20.GL_TEXTURE_MIN_FILTER,GLES20.GL_LINEAR);GLES20.glTexParameterf(GLES11Ext.GL_TEXTURE_EXTERNAL_OES,GLES20.GL_TEXTURE_MAG_FILTER,GLES20.GL_LINEAR);//配置s轴和t轴的方式GLES20.glTexParameteri(GLES11Ext.GL_TEXTURE_EXTERNAL_OES,GLES20.GL_TEXTURE_WRAP_S,GLES20.GL_CLAMP_TO_EDGE);GLES20.glTexParameteri(GLES11Ext.GL_TEXTURE_EXTERNAL_OES,GLES20.GL_TEXTURE_WRAP_T,GLES20.GL_CLAMP_TO_EDGE);}private void updateTexture() {mSurfaceTexture.updateTexImage();}/*** 加载着色器** @param shaderType 着色器类型* @param shaderCode 着色器代码* @return*/private int loadShader(int shaderType, String shaderCode) {//根据着色器类型创建着色器int shader = GLES20.glCreateShader(shaderType);//将着色其代码加入到着色器GLES20.glShaderSource(shader, shaderCode);//编译zhuoseqGLES20.glCompileShader(shader);return shader;}/*** 开始绘制渲染*/public void doDraw() {if(mMatrix == null)return;//启用顶点坐标句柄GLES20.glEnableVertexAttribArray(mVertexPosHandler);GLES20.glEnableVertexAttribArray(mTexturePosHandler);GLES20.glUniformMatrix4fv(mVertexMatrixHandler, 1, false, mMatrix, 0);//设置着色器参数, 第二个参数表示一个顶点包含的数据数量,这里为xy,所以为2GLES20.glVertexAttribPointer(mVertexPosHandler, 2, GLES20.GL_FLOAT, false, 0, mVertexBuffer);GLES20.glVertexAttribPointer(mTexturePosHandler,2,GLES20.GL_FLOAT,false,0,mTextureBuffer);GLES20.glVertexAttrib1f(mAlphaHandler, mAlpha);//开始绘制GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, mVertexCoors.length / 2);}@Overrideprotected void onDetachedFromWindow() {Log.d(TAG, "onDetachedFromWindow");super.onDetachedFromWindow();GLES20.glDisableVertexAttribArray(mVertexPosHandler);GLES20.glDisableVertexAttribArray(mTexturePosHandler);GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, 0);GLES20.glDeleteTextures(1, new int[]{mTextureId}, 0);GLES20.glDeleteProgram(mProgram);if(mMediaPlayer != null){//mMediaPlayer.setSurface(null);mMediaPlayer.release();mSurface.release();}}public void translate(float dx, float dy) {Matrix.translateM(mMatrix, 0, dx * mWidthRatio * 2, -dy * mHeightRatio * 2, 0f);}public void scale(float sx, float sy) {Matrix.scaleM(mMatrix, 0, sx, sy, 1f);mWidthRatio /= sx;mHeightRatio /= sy;}public class VideoRender implements GLSurfaceView.Renderer {@Overridepublic void onSurfaceCreated(GL10 gl, EGLConfig config) {Log.d(TAG, "onSurfaceCreated");GLES20.glClearColor(0f, 0f, 0f, 0f);//开启混合,即半透明GLES20.glEnable(GLES20.GL_BLEND);GLES20.glBlendFunc(GLES20.GL_SRC_ALPHA, GLES20.GL_ONE_MINUS_SRC_ALPHA);int[] textureIds = new int[1];GLES20.glGenTextures(1, textureIds, 0);mTextureId = textureIds[0];//根据textureId初始化一个SurfaceTexturemSurfaceTexture = new SurfaceTexture(mTextureId);mSurfaceTexture.setOnFrameAvailableListener(new SurfaceTexture.OnFrameAvailableListener() {@Overridepublic void onFrameAvailable(SurfaceTexture surfaceTexture) {requestRender();}});if(mMediaPlayer != null){onVideoPrepared(mMediaPlayer);}}@Overridepublic void onSurfaceChanged(GL10 gl, int width, int height) {Log.d(TAG, "onSurfaceChanged");GLES20.glViewport(0, 0, width, height);setWorldSize(width, height);}@Overridepublic void onDrawFrame(GL10 gl) {Log.d(TAG, "onDrawFrame");if(mMediaPlayer == null || !mMediaPlayer.isPlaying())return;//清除颜色缓冲和深度缓冲GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT | GLES20.GL_DEPTH_BUFFER_BIT);if (mTextureId != -1) {initDefMatrix();//2/创建、编译、启动opengles着色器createGLPrg();//3.激活并绑定纹理单元activateTexture();//4.绑定图元到纹理单元updateTexture();//5.开始绘制渲染doDraw();}}}
}

复杂图形效果的场景

普通的视频播放方式很难实现 曲面 百叶窗 这类的效果, 如:
在这里插入图片描述
而使用OpenGL播放, 只需要调整下顶点和纹理的坐标即可:

    //顶点坐标,此处的坐标系是物体坐标系:中心店坐标是(0,0)private float[] mVertexCoors = new float[]{-1f, 1f,-1f, -1f,0, 0.5f,0, -0.5f,1f, 1f,1f, -1f,};//纹理坐标系,中心坐标点为(0.5,0.5),上方向为t从0~1,右边方向为s,从0~1.刚好和计算器物理坐标系是反过来的。private float[] mTextureCoors = new float[]{0f, 0f,0f, 1f,0.5f, 0,0.5f, 1f,1f, 0f,1f, 1f};

参考

  1. 10.GLSurfaceView+MediaPlayer播放视频.md
  2. 【Android 音视频开发打怪升级:OpenGL渲染视频画面篇】二、使用OpenGL渲染视频画面
  3. Android 最简单的视频播放器之OpenGL ES视频渲染工具封装(三)

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

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

相关文章

吸浮毛宠物空气净化器推荐,希喂、小米、有哈宠物空气净化器测评

养猫需谨慎,不然就要做猫奴一辈子啦!上次堂妹来我家住几天,刚开始还担心和猫处不来,不敢去摸它,走的时候已经约好下次来看它的时间,笑死我了。毕竟猫咪这么可爱,很少有人可以抵抗它的魅力。 这不…

什么是科技与艺术相结合的异形创意圆形(饼/盘)LED显示屏

在当今数字化与创意并重的时代,科技与艺术的融合已成为推动社会进步与文化创新的重要力量。其中,晶锐创显异形创意圆形LED显示屏作为这一趋势下的杰出代表,不仅打破了传统显示设备的形态束缚,更以其独特的造型、卓越的显示效果和广…

3谐振功率放大器的实际电路设计

1原理电路 下图是谐振功率放大器的原理电路,如果我们照着下图搭一个电路,会发现它可能实现不了功率放大?这是为什么? 2实际电路设计 2.1要注意直流馈电线路 馈电原则(馈电供电): 1)保证直流电流分量流过直流电源&…

基于SpringBoot+Vue的校园礼服装租赁系统

作者:计算机学姐 开发技术:SpringBoot、SSM、Vue、MySQL、JSP、ElementUI、Python、小程序等,“文末源码”。 专栏推荐:前后端分离项目源码、SpringBoot项目源码、SSM项目源码 系统展示 【2025最新】基于JavaSpringBootVueMySQL的…

【Java 优选算法】双指针(下)

欢迎关注个人主页:逸狼 创造不易,可以点点赞吗~ 如有错误,欢迎指出~ 有效三角形的个数 题目链接 解法 解法1:暴力枚举--->O(n^3) 解法2:利用单调性,使用双指针来解决---->O(n^2) 优化:对整个数组进行排序先固定最大数在最大数的左…

【模板代码的组织结构与模板的显式实例化和声明】模板代码的组织结构与模板的显式实例化和声明

一、模板的组织结构 之前对于模板,我们都是写在同一个 . c p p .cpp .cpp文件下,那如果我们将模板分开,单独开一个 . h .h .h和 . c p p .cpp .cpp来创建模板,会发生什么? 首先,我们创建一个 m y c l a s…

【2024.08】图模互补:知识图谱与大模型融合综述-笔记

阅读目的:假设已有一个知识图谱,如何利用图谱增强模型的问答,如何检索知识图谱、知识图谱与模型的文本如何相互交互、如何利用知识图谱增强模型回答的可解释性。 从综述中抽取感兴趣的论文进一步阅读。 来源:图模互补&#xff1…

阿里云 Quick BI使用介绍

Quick BI使用介绍 文章目录 阿里云 Quick BI使用介绍1. 创建自己的quick bi服务器2. 新建数据源3. 上传文件和 使用4. 开始分析 -选仪表盘5. 提供的图表6. 一个图表的设置使用小结 阿里云 Quick BI使用介绍 Quick BI是一款全场景数据消费式的BI平台,秉承全场景消费…

【梯度下降|链式法则】卷积神经网络中的参数是如何传输和更新的?

【梯度下降|链式法则】卷积神经网络中的参数是如何传输和更新的? 【梯度下降|链式法则】卷积神经网络中的参数是如何传输和更新的? 文章目录 【梯度下降|链式法则】卷积神经网络中的参数是如何传输和更新的?1. 什么是梯度?2.梯度…

华为eNSP使用详解

eNSP(Enterprise Network Simulation Platform)是华为提供的一款网络仿真平台,它允许用户在没有真实设备的情况下进行网络实验和学习网络技术。eNSP可以模拟各种网络设备,如交换机、路由器、防火墙等,并支持创建多种网…

Linux 环境打包 QT 程序

目录 前言 一、安装Qt库和编译器 二、编译生成可执行文件 三、准备脚本 四、添加程序依赖库 五、添加 Qt 相关依赖库 六、发布 deb 安装包 参考文章 前言 Qt是一个跨平台的GUI框架,可以在多种操作系统上运行,包括Linux、Windows和macOS。因此&am…

ASP.NET Core高效管理字符串集合

我们在开发 Web 项目时经常遇到需要管理各种来源的字符串集合(例如HTTP 标头、查询字符串、设置的值等)的情况。合理的管理这些字符串集合不仅可以减少出bug的几率,也能提高应用程序的性能。ASP.NET Core 为我们提供了一种特殊的只读结构体 S…

C/C++实现植物大战僵尸(PVZ)(打地鼠版)

🚀欢迎互三👉:程序猿方梓燚 💎💎 🚀关注博主,后期持续更新系列文章 🚀如果有错误感谢请大家批评指出,及时修改 🚀感谢大家点赞👍收藏⭐评论✍ 游戏…

【数据结构】字符串与JSON字符串、JSON字符串及相应数据结构(如对象与数组)之间的相互转换

前言&#xff1a; 下面打印日志用的是FastJSON依赖库中的 Log4j2。依赖&#xff1a; <!-- Alibaba Fastjson --> <dependency><groupId>com.alibaba</groupId><artifactId>fastjson</artifactId><version>1.2.80</version> …

[Redis] Redis中的set和zset类型

&#x1f338;个人主页:https://blog.csdn.net/2301_80050796?spm1000.2115.3001.5343 &#x1f3f5;️热门专栏: &#x1f9ca; Java基本语法(97平均质量分)https://blog.csdn.net/2301_80050796/category_12615970.html?spm1001.2014.3001.5482 &#x1f355; Collection与…

OpenCV运动分析和目标跟踪(1)累积操作函数accumulate()的使用

操作系统&#xff1a;ubuntu22.04 OpenCV版本&#xff1a;OpenCV4.9 IDE:Visual Studio Code 编程语言&#xff1a;C11 算法描述 将一个图像添加到累积图像中。 该函数将 src 或其部分元素添加到 dst 中&#xff1a; dst ( x , y ) ← dst ( x , y ) src ( x , y ) if mask…

mysql笔记8(多表查询)

文章目录 1. union联合查询可能会用到去重操作 2. inner join 内连接3. left join 左连接4. right join 右连接5. cross join 交叉连接6. natural join 自然连接natural left join 自然左连接natural right join 自然右连接自然连接的两张表没有同名字段怎么办&#xff1f; 7. …

自定义EPICS在LabVIEW中的测试

继续上一篇&#xff1a;LabVIEW中EPICS客户端/服务端的测试 变量定义 You can use CaLabSoftIOC.vi to create new EPICS variables and start them. CA Lab - LabVIEW (Realtime) EPICS INPUT: PV set Cluster-array of names, data types and field definitions to crea…

VMware Fusion虚拟机Mac版 安装Ubuntu操作系统教程

Mac分享吧 文章目录 下载镜像地址&#xff1a;[www.macfxb.cn](http://www.macfxb.cn)一、Ubuntu安装完成&#xff0c;软件打开效果二、Mac中安装Ubuntu虚拟机1️⃣&#xff1a;下载镜像2️⃣&#xff1a;创建虚拟机3️⃣&#xff1a;虚拟机设置4️⃣&#xff1a;虚拟机安装5️…

3.信号量与互斥量

队列:用来传递数据 如果不想传递数据,只是"通知"呢? 这个时候,我们就可以使用"信号量","信号量"的本质:item_size等于 0 的队列 信号量里面有什么呢? 1.一个计数值 2.一个"队伍",就是一个链表:用来记录等待的对应信号量的任务 …