MAC环境编译Android环境下的FFmpeg6.0版本

news2024/11/23 4:07:22

文章目录

  • 一. 下载FFmpeg源码
  • 二、对FFmpeg进行安装编译
  • 三、进行JNI接口编写代码
  • 四、在Android 项目中调用.so库
  • 五、FFmpeg的代码学习技巧
    • 1、整体学习步骤
    • 2、FFmpeg的代码学习步骤
  • 六、参考链接:

一. 下载FFmpeg源码

该项目是基于FFmpeg6.0环境编写。文中涉及代码在不同版本可能会有变动
从以下两个地址任选其一下载源码:

  1. https://github.com/FFmpeg/FFmpeg
  2. https://ffmpeg.org/

二、对FFmpeg进行安装编译

执行根目录的configure文件,

./configure 

该方式会生成相关文件,否则项目不可运行,
参考如下:
https://ffmpeg.org/doxygen/6.0/md_INSTALL.html

编译过程中会出现错误,根据错误进行修改,通常会缺少pkg-config安装包,这个会在编译时候提示缺少这个,不提示就是不缺少。安装如下:
brew install pkg-config
整体错误大概分为缺少依赖的安装包和环境。这个根据提示进行安装即可。没办法绕过

编译成.so参考如下
Android 集成 FFmpeg (一) 基础知识及简单调用
https://blog.csdn.net/yhaolpz/article/details/76408829

如果使用sh文件进行编译的话会出现权限拒绝的情况,可以使用以下命令:

chmod +x build_android.sh

然后再执行sh文件。

以下为sh配置的文件,不需要在项目中配置环境变量,但是所有文件中用到的具体路径需要进行修改。
需要注意的是每次添加新的命令需要使用\ 进行分割。在进行配置的时候对于高级选项的命令,比如优化部分--disable-neon最好不要使用,否则容易出现错误。相关命令可以通过configure --help进行查看

#!/bin/bash 
NDK=/Users/c/Documents/sdk/android/ndk/25.1.8937393
TOOLCHAIN_ROOT_DIR=darwin-x86_64
TOOLCHAIN=$NDK/toolchains/llvm/prebuilt/$TOOLCHAIN_ROOT_DIR/
API=21
#要编译的ffmpeg内容方法
function build_android {
	echo "Compiling FFmpeg for $CPU" 
	./configure \
	--prefix=$PREFIX \
	--disable-hwaccels \
	--disable-gpl \
	--disable-postproc \
	--disable-programs \
	--disable-mediacodec \
	--disable-decoder=h264_mediacodec \
	--disable-static \
	--disable-vulkan \
	--disable-doc \
	--disable-ffmpeg \
	--disable-ffplay \
	--disable-ffprobe \
	--disable-avdevice \
	--disable-doc \
	--disable-symver \
	--disable-x86asm \
	--disable-filters \
	--enable-cross-compile \
	--enable-jni \
	--enable-shared \
	--cross-prefix=$CROSS_PREFIX \
	--nm=$NM \
	--strip=$STRIP \
	--pkgconfigdir=$PKG_CONFIG_DIR \
	--pkg-config=$PKG_CONFIG \
	--target-os=android \
	--arch=$ARCH \
	--cpu=$CPU \
	--cc=$CC \
	--cxx=$CXX \
	--sysroot=$SYSROOT \
	--extra-cflags="-Os -fpic $OPTIMIZE_CFLAGS" \
	--extra-ldflags="$ADDI_LDFLAGS" \
	$ADDITIONAL_CONFIGURE_FLAG 
	make clean 
	make 
	make install 
	echo "The Compilation of FFmpeg for $CPU is completed" 
}
#接下来是根据需要来决定
#armv8-a 
ARCH=arm64 
CPU=armv8-a 
CC=$TOOLCHAIN/bin/aarch64-linux-android$API-clang 
CXX=$TOOLCHAIN/bin/aarch64-linux-android$API-clang++
LLVM_TOOLCHAIN=$TOOLCHAIN/bin
PKG_CONFIG_DIR=/opt/homebrew/Cellar/pkg-config/0.29.2_3
PKG_CONFIG=$PKG_CONFIG_DIR/bin/pkg-config
NM=$LLVM_TOOLCHAIN/llvm-nm
STRIP=$LLVM_TOOLCHAIN/llvm-strip
SYSROOT=$TOOLCHAIN/sysroot 
CROSS_PREFIX=$TOOLCHAIN/bin/aarch64-linux-android-
PREFIX=$(pwd)/android/$CPU 
OPTIMIZE_CFLAGS="-march=$CPU" 
build_android

其实最初文件可以写成以下方式:

export ANDROID_NDK=<NDK路径>
export TOOLCHAIN=$ANDROID_NDK/toolchains/llvm/prebuilt/darwin-x86_64
export SYSROOT=$ANDROID_NDK/toolchains/llvm/prebuilt/darwin-x86_64/sysroot

./configure \
--prefix=$PWD/build \
--enable-shared \
--disable-static \
--disable-doc \
--disable-programs \
--disable-symver \
--arch=arm64 \
--target-os=android \
--cc=$TOOLCHAIN/bin/aarch64-linux-android21-clang \
--cxx=$TOOLCHAIN/bin/aarch64-linux-android21-clang++ \
--cross-prefix=$TOOLCHAIN/bin/aarch64-linux-android- \
--sysroot=$SYSROOT

但是编译中会出现各种错误,最终要么重新指定路径,要么禁止使用,就改成了如此。
关于命令的含义参考如下:
FFmpeg中configure的参数配置解释

对于pkg-config的配置如果编译时候找不到,要么禁用--disable-pkgconfig,要么使用以下方式进行配置

./configure --pkg-config=/path/to/pkg-config --pkgconfigdir=/path/to/pkgconfigdir

最终会在ffmpeg的根目录生成android/<CPU>目录(这里是android/<armv8-a >),里面共有三个文件夹includelibshare。.so位于lib文件夹下:
在这里插入图片描述
shell脚本参考如下:
shell脚本语言(超全超详细)

三、进行JNI接口编写代码

在FFmpeg编译成.so完成后,需要添加JNI编码,否则的话无法在Android项目中直接使用。该操作需要配置ndk-build环境
参考链接:
Android 集成 FFmpeg (一) 基础知识及简单调用

这里使用文本工具进行代码编写和编译,不使用Android Studio进行编译。
首先在上述的FFmpeg根目录下后面生成的文件夹android中创建文件ndkBuild\com\jni\FFmpeg.javandkBuild\jni文件夹
FFmpeg.java代码如下:

package com.jni;

public class FFmpeg {
    
    public static native void run();

}

然后在ndkBuild目录下执行命令生成com_jni_FFmpeg.h文件
该命令的中间的.表示当前路径,可以更改为其余路径,后面是具体的类的名字

javah -classpath .  com.jni.FFmpeg

然后在ndkBuild\jni文件夹下创建com_jni_FFmpeg.cAndroid.mkApplication.mk
代码如下:
com_jni_FFmpeg.c

#include <android/log.h>
#include "com_jni_FFmpeg.h"

#include <stdlib.h>
#include <stdbool.h>
#include <stdio.h>
#include <libavformat/avformat.h>
#include <libavcodec/avcodec.h>
#include <libavutil/frame.h>
#include <libavutil/mem.h>
# 这里是把AV_CODEC_ID_MP2的编码信息打印一下
JNIEXPORT void JNICALL Java_com_jni_FFmpeg_run(JNIEnv *env, jclass obj) {
	char info[40000] = {0};
	const AVCodec *codec = avcodec_find_decoder(AV_CODEC_ID_MP2);
	AVCodecContext *avcodecContext = avcodec_alloc_context3(codec);
	const char *license = avcodec_license();
	if(avcodecContext->codec_type == AVMEDIA_TYPE_VIDEO){//音频
		sprintf(info,"%s[Video]",info);
	}else{//视频	
		sprintf(info,"%s[Audio]",info);
	}
	sprintf(info,"%s[%10s]\n",info,codec->name);
	avcodec_free_context(&avcodecContext); //释放内存
	__android_log_print(ANDROID_LOG_INFO,"myTag","info:\n%s",info);
}

Android.mk

LOCAL_PATH:= $(call my-dir)

FFMPEG_PATH:=/Users/c/Documents/CTest/ffmpeg-6.0
FFMPEG_ANDROID:=$(FFMPEG_PATH)/android/armv8-a
INCLUDE_PATH:=$(FFMPEG_ANDROID)/include
FFMPEG_LIB_PATH:=$(FFMPEG_ANDROID)/lib

include $(CLEAR_VARS)
LOCAL_MODULE:= libavcodec
LOCAL_SRC_FILES:= $(FFMPEG_LIB_PATH)/libavcodec.so
LOCAL_EXPORT_C_INCLUDES := $(INCLUDE_PATH)
include $(PREBUILT_SHARED_LIBRARY)
 
include $(CLEAR_VARS)
LOCAL_MODULE:= libavformat
LOCAL_SRC_FILES:= $(FFMPEG_LIB_PATH)/libavformat.so
LOCAL_EXPORT_C_INCLUDES := $(INCLUDE_PATH)
include $(PREBUILT_SHARED_LIBRARY)
 
include $(CLEAR_VARS)
LOCAL_MODULE:= libswscale
LOCAL_SRC_FILES:= $(FFMPEG_LIB_PATH)/libswscale.so
LOCAL_EXPORT_C_INCLUDES := $(INCLUDE_PATH)
include $(PREBUILT_SHARED_LIBRARY)
 
include $(CLEAR_VARS)
LOCAL_MODULE:= libavutil
LOCAL_SRC_FILES:= $(FFMPEG_LIB_PATH)/libavutil.so
LOCAL_EXPORT_C_INCLUDES := $(INCLUDE_PATH)
include $(PREBUILT_SHARED_LIBRARY)
 
include $(CLEAR_VARS)
LOCAL_MODULE:= libavfilter
LOCAL_SRC_FILES:= $(FFMPEG_LIB_PATH)/libavfilter.so
LOCAL_EXPORT_C_INCLUDES := $(INCLUDE_PATH)
include $(PREBUILT_SHARED_LIBRARY)
 
include $(CLEAR_VARS)
LOCAL_MODULE:= libswresample
LOCAL_SRC_FILES:= $(FFMPEG_LIB_PATH)/libswresample.so
LOCAL_EXPORT_C_INCLUDES := $(INCLUDE_PATH)
include $(PREBUILT_SHARED_LIBRARY)

include $(CLEAR_VARS)
LOCAL_MODULE := ffmpeg
LOCAL_SRC_FILES := com_jni_FFmpeg.c 
LOCAL_C_INCLUDES := $(FFMPEG_PATH)
LOCAL_LDLIBS := -lm -llog
LOCAL_SHARED_LIBRARIES := libavcodec libavfilter libavformat libavutil libswresample libswscale
include $(BUILD_SHARED_LIBRARY)

Application.mk

APP_ABI := arm64-v8a
APP_PLATFORM=android-21

然后在当前目录下执行命令:

ndk-build

生成Android.mk中定义的.so文件。最终会在ndkBuild下面生成两个文件夹libsobj。在libs\arm64-v8a下面会有生成的.so库。共七个libavcodec.so libavfilter.so libavformat.so libavutil.so libswresample.so libswscale.so libffmpeg.so。其中libffmpeg.so为编写的JNI生成的so库,其余为依赖库。

四、在Android 项目中调用.so库

将上述的七个.so库复制到项目的libs文件夹下,可以将整个libs文件夹对Android项目下的libs进行覆盖。app目录下的build.gradle修改如下:

android{
sourceSets {
        main {
            jniLibs.srcDirs = ['libs']
        }
    }
}
dependencies {
    implementation fileTree(include: ['*.jar'], dir: 'libs')
}

将刚才编写的java文件连同com.jni的文件夹一起复制到src目录下。整体结构如下:
在这里插入图片描述
如果将.so连同arm64-v8a文件夹复制到main下面的jniLibs文件夹下面则不需要对build.gradle进行修改。如下
在这里插入图片描述

然后在项目中调用代码

class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        val btn: Button = findViewById(R.id.button)
        btn.setOnClickListener {
            FFmpeg.run()
        }
        FFmpeg.test()
    }
}

可以在logcat下看到结果

2023-06-08 11:36:01.783  5346-5346  myTag                   com.test.ffmpeg                      I  info:
                                                                                                    [Audio][       mp2]

五、FFmpeg的代码学习技巧

1、整体学习步骤

在学习一个技巧的时候最好根据官方来进行学习,因为其余人的教程可能存在无法实时更新导致的api变动问题。在学习ffmpeg的开发过程中主要有以下几个步骤

  1. 编译ffmpeg为.so。参考:https://ffmpeg.org/doxygen/6.0/md_INSTALL.html,编译过程需要的命令可以通过configure --help进行查看
  2. 将编译成功的结果.so和本地的环境比如Android进行编写桥接代码JNI,这里可以参考Android官方网站: https://developer.android.com/ndk?hl=zh-cn
  3. 在编写JNI时候需要使用ffmpeg的api来完成我们的目的,这里的学习方式最好使用官方教程: https://ffmpeg.org/documentation.html , 以及相关论坛

2、FFmpeg的代码学习步骤

FFmpeg本身是一个工具系列的库,是一个开源性质的库,所以教程相对于那些商业化的框架会比较难以上手,比如OkHttp框架。最初通过浏览器搜索的结果,代码基本上都难以运行,大部分都是api过时,这样其实不利于入门,毕竟通过别人的代码学会了,但是api过时了,又不知道怎么更新api。通过仔细查阅官方网站整理了一条路径(不一定可行)。
首先官网提供了必要的api文档和一些基本的示例(虽然示例可能不是我们想象中的示例程序)。通过官方文档我们知道整体文档分为: 命令行工具文档组件文档图书馆文件一般文件API文档社区贡献文档。通过每一个都点进去后可以简单知道,内容很多,但是作为初学者来说,实在是难以接受,茫然无措,不知道从何开始。如果是用命令行就可以完成需求的话,当然命令行工具文档是很好的选择,但是如果需要自己通过api开发,就只能查看api文档了。其余的文档可以等后面熟悉了再进行阅读。
api文档位于:https://ffmpeg.org/doxygen/trunk/index.html。每天都会更新。整体页面如下
在这里插入图片描述
通过此我们知道的有用信息如下:
整个库分为八个部分

  1. libavcodec编码/解码库
  2. libavfilter基于图形的帧编辑库
  3. libavformat I/O 和 muxing/demuxing 库
  4. libavdevice特殊设备复用/解复用库
  5. libavutil通用实用程序库
  6. libswresample音频重采样、格式转换和混合
  7. libpostproc后处理库
  8. libswscale颜色转换和缩放库

在上面的Modules选项下面可以看到都有哪些模块:
在这里插入图片描述
通过Examples选项下面可以看到有哪些例子
在这里插入图片描述
如果恰巧自己想实现的需求是例子中所有的,那就非常完美,可是点进去例子后发现,例子对初学者来说写的很复杂。我们最初只想写一个简单的测试例子,测试程序是否编译通过。打印下基本的信息,那么为此再去编写一套文件读写代码,会显得特别麻烦。所以这里要有一个简答的目的,打印下ffmpeg的基础信息。通过上述信息搜集,可以知道我们打印下ffmpeg支持的格式会比较简单,也不需要进行文件读写。这里的话就锁定使用libavformat。点击进去后发现内容如下:
在这里插入图片描述
模块如下,文件有三个头文件,一个avformat.h,剩下两个是版本信息,其余的库也是如此,我们的主要参考api就位于第一个文件中,点击进去后发现,里面包含了定义的常量和函数。
在这里插入图片描述
被标记的就是会用到的遍历函数。我们点击第一个函数。
在这里插入图片描述
这里是函数的定义,解释了需要从传参数为NULL开始便利,下面是引用该函数的位置(有的api下面还要示例程序的引用地址,不过这个api没有)。选择第一个引用的位置点击进去查看:
在这里插入图片描述
这里是一个简单的使用方式。将该代码拷贝到实际项目中去,然后别忘了添加相应的头文件,格式如下:<库名/头文件名.h> #include <libavformat/avformat.h>。相应的库名和头文件名在上述步骤已经给出。
所以整体代码如下:

#include <android/log.h>
#include "com_jni_FFmpeg.h"

#include <stdlib.h>
#include <stdbool.h>
#include <stdio.h>
#include <libavformat/avformat.h>

JNIEXPORT void JNICALL Java_com_jni_FFmpeg_run(JNIEnv *env, jclass obj) {
	 const AVOutputFormat *fmt = NULL;
	     const AVOutputFormat *fmt_found = NULL;
	     void *i = 0;
	     int score_max, score;
	 while ((fmt = av_muxer_iterate(&i))) {
	         score = 0;
			 const char *fmtName = fmt->name;
			 __android_log_print(ANDROID_LOG_INFO,"myTag","fmtName:\n%s",fmtName);
	     }
}

运行程序后可再logcat控制台看到如下内容:

2023-06-08 14:56:30.886  8824-8824  myTag                   com.test.ffmpeg                      I  fmtName:
                                                                                                    a64
2023-06-08 14:56:30.886  8824-8824  myTag                   com.test.ffmpeg                      I  fmtName:
                                                                                                    ac3
2023-06-08 14:56:30.886  8824-8824  myTag                   com.test.ffmpeg                      I  fmtName:
                                                                                                    adts
2023-06-08 14:56:30.886  8824-8824  myTag                   com.test.ffmpeg                      I  fmtName:
                                                                                                    adx
2023-06-08 14:56:30.886  8824-8824  myTag                   com.test.ffmpeg                      I  fmtName:
                                                                                                    aiff
2023-06-08 14:56:30.886  8824-8824  myTag                   com.test.ffmpeg                      I  fmtName:
                                                                                                    alp
2023-06-08 14:56:30.886  8824-8824  myTag                   com.test.ffmpeg                      I  fmtName:
                                                                                                    amr
2023-06-08 14:56:30.886  8824-8824  myTag                   com.test.ffmpeg                      I  fmtName:
                                                                                                    amv
                                                                                                    ...
                                                                                                    ...

不过这种代码编写方式不太好,没办法发挥ide的特性,后面再进行完善

六、参考链接:

  1. ndk-build 脚本
  2. Android studio添加第三方库和so

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

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

相关文章

配置MySQL保存Hive元数据

Hive默认使用自带&#xff08;内嵌&#xff09;的Derby进行元数据存储&#xff0c;这个就意味着无法实现多个hive shell并发连接Hive。如果需要支持多用户登录Hive&#xff0c;必须配置一个独立的数据库。上一节我们已经将MySQL数据库安装到Linux上&#xff0c;本节我们将讲解一…

微服务架构基础--第1章认识微服务架构

第1章认识微服务架构 一.预习笔记 1.为什么需要微服务架构 传统的单体应用架构都是模块化的设计逻辑&#xff0c;易于开发和调试&#xff0c;也易于部署。但是随着用户量的增加&#xff0c;就无法满足系统的负载&#xff0c;虽然也可以进行单体架构的拓展&#xff0c;但是随着…

设备指纹系列--基础篇

基础概念 618还没开始&#xff0c;但是又好像已经结束了…在这种电商大促的大节日前&#xff0c;电商行业客户一般会提前找到合适的设备指纹产品&#xff0c;去防止被“薅秃”。因为&#xff0c;黑灰产拥有专业的设备牧场&#xff0c;通过使用模拟器、刷机改机等手段&#xff…

算法与数据结构-数组

文章目录 什么是数组线性表连续的内存空间和相同类型的数据 为什么数组的插入和删除是低效的插入删除 容器与数组的区别 什么是数组 数组&#xff08;Array&#xff09;是一种线性表数据结构。它用一组连续的内存空间&#xff0c;来存储一组具有相同类型的数据。 这个定义里有几…

Pytest自动化测试框架生成allure的报告

一、前言 最近通过群友了解到了allure这个报告&#xff0c;开始还不以为然&#xff0c;但还是逃不过真香定律。 经过试用之后&#xff0c;发现这个报告真的很好&#xff0c;很适合自动化测试结果的展示。下面说说我的探索历程吧。 选用的项目为Selenium自动化测试Pytest框架实…

IntelliJ IDEA彻底删除

我们在用idea的时候&#xff0c;idea的环境出现了莫名其妙的问题&#xff0c;怎么也找不到问题原因的时候可以试着把idea卸载重新安装。但是如果你卸载的时候没有删除干净注册表里的信息。就算再次安装回来也还是解决不了问题。 如何删除干净IDEA 将所有jetbrains软件关闭退出…

嵌入式软件工程师招聘

当您招聘嵌入式软件工程师时&#xff0c;以下是一些建议和关键要点&#xff0c;可以帮助您吸引和筛选合适的候选人&#xff1a; 职位描述&#xff1a;清晰地定义嵌入式软件工程师的职责和要求。包括对硬件和软件开发经验的要求、熟练掌握的编程语言&#xff08;如C、C、Python等…

一文学会MoveIt Setup Assistant搭建moveit2机械臂模型

文章目录 前言一、MoveIt Setup Assistant 是什么&#xff1f;二、搭建步骤拉取相关repo创建项目文档编辑moveit_description文件夹编辑moveit_config文件夹 MoveIt Setup Assistant 配置Launch moveit_setup_assistant配置干涉关系配置planning groups配置ros2 controller配置…

一个 TCP 连接可以发送多少个 HTTP 请求

第一个问题 第二个问题 第三个问题 第四个问题 第五个问题 曾经有这么一道面试题&#xff1a;从 URL 在浏览器被被输入到页面展现的过程中发生了什么&#xff1f; 相信大多数准备过的同学都能回答出来&#xff0c;但是如果继续问&#xff1a;收到的 HTML 如果包含几十个图…

优质高效!阿里内部超高质量的k8s+Jenkins笔记,技术与实战齐飞

什么是k8s? Kubernetes是一个用于容器集群的自动化部署、扩容以及运维的开源平台。 k8s孕育的初衷是培育出一个组件及工具的生态&#xff0c;帮助大家减轻在公有云及私有云上运行应用的负担&#xff0c;换言之&#xff0c;使得大型分布式应用的构建和运维变得更加简单&#…

⛳前端进阶:SEO 全方位解决方案

&#x1f33b; 前言 SEO 代表搜寻引擎最佳化/搜寻引擎优化(英文全名Search Engine Optimization&#xff0c;简称SEO)&#xff0c;是指通过了解搜寻引擎的自然排名的算法逻辑&#xff0c;以提高目标网站在有关搜寻引擎内排名的方式。 网站的 SEO 至关重要&#xff0c;它可以让…

基于springboot的大学外卖系统源码数据库

源码资源在这里 https://download.csdn.net/download/2301_76965813/87881744 1 系统分析 1.1 需求分析 该系统的开发&#xff0c;主要是用在了各个订餐网站的系统&#xff0c;用户可以通过网站进行外卖订餐的信息挂了&#xff0c;并能够对客户进行外卖菜品的订单提交等。该…

X509证书解析详解

上传的证书文件&#xff0c;如何判断这个证书是否满足X509标准格式的呢&#xff1f; 下面是我阅读jdk源码&#xff0c;总结出的解析步骤。 jdk版本&#xff1a;jdk11 方法的入口 这是整个方法的起点&#xff0c;下面的步骤是根据这里方法调用跟踪到的&#xff0c;需要注意的是…

100种思维模型之细节效率思维模型-74

提及细节效率&#xff1f;也许很多人会有疑问&#xff0c;“效率”怎么跟“细节”挂上钩&#xff0c;注重“细节”了&#xff0c;还能有“高率”&#xff1f; 是的&#xff0c;细节能提高效率&#xff0c;注意某些细节&#xff0c;效率事半功倍。 01、何谓细节效率思维模型 一…

truncate导致慢查询根因竟然是“多此一举”

基本信息&#xff1a; 线上一个库5.7.25库经常出现大量慢查询&#xff0c;在再次出现时登陆数据库进行分析&#xff0c;通过show engine innodb status 内容&#xff0c;发线程全部在等一个锁&#xff0c;这个锁极可能来源于这个truncate table动作&#xff1a; ---TRANSACTIO…

【Flutter】widgets (5) Flutter 理解 Flutter 的 Stateful Widget 有状态组件

文章目录 一、前言二、Stateful Widget三、StatefulWidget和State类的关系四、创建StatefulWidget五、完整代码示例六、总结一、前言 在之前的教程中,我们掌握了Stateless Widgets,也就是无状态组件的基本用法。 但是,应用程序不是静态不变的,我们需要界面中用户的操作,…

OCP浸没式液冷基本规范(概述和信号完整性部分)

液冷技术概述和浸没式液体标准化的需求 数据中心行业主要考虑两种类型的液体冷却技术来推动节能和可持续发展&#xff0c;分别是冷板式和浸没式&#xff0c;每一种技术里的液体又包含单相和双相两种规格&#xff1a; 冷板技术与浸没技术的主要区别之一是&#xff0c;在浸没的情…

操作系统原理 —— 什么是基本分页存储管理?(二十二)

在操作系统中&#xff0c;一个新的进程需要载入内存当中执行&#xff0c;在装入的时候需要给该进程分配一定的运行内存&#xff0c;在之前的章节中讲解了连续分配的几种方式&#xff0c;比如&#xff1a;单一连续分配、固定分区分配、动态分区分配&#xff0c;还讲解了对应的动…

MySQL表相关操作

一、存储引擎介绍 存储引擎即表类型&#xff0c;mysql根据不同的表类型会有不同的处理机制 1、什么是存储引擎 mysql中建立的库 > 文件夹 库中建立的表 > 文件 现实生活中我们用来存储数据的文件有不同的类型&#xff0c;每种文件类型对应各自不同的处理机制&#x…

基于LPP算法实现MNIST数据集降维

目录 1、作者介绍2、LPP算法简介2.1 基本概念及原理2.2 算法流程 3、LPP算法实现3.1 数据集简介3.2 代码实现3.2.1 完整代码3.2.2 运行结果 4、参考链接 1、作者介绍 刘晨雨&#xff0c;男&#xff0c;西安工程大学电子信息学院&#xff0c;2022级研究生 研究方向&#xff1a;…