Android 屏幕采集并编码为H.264
前言
我们前面基于摄像机的图像采集以及编解码已经完成了,那么接下来计划后面的三篇博文分别实现Android屏幕采集实现并进行H.264编解码、MIC音频采集并编码为AAC以及AAC解码播放,希冀可以通过这六篇博文能够对Android上面的音视频编解码有一个初步的学习和了解,由于博主也是近期刚从0开始学习这部分的知识,因此博文中有不恰当的描述,希望大家能够指正,对于有想法进行Android音视频开发的同学,希望这6篇博文能够帮助您启蒙。
那么本篇,我们就先来看看Android屏幕采集实现并进行H.264编解码。
屏幕采集简介
在Android 5.0及以上版本中,可以使用系统提供的MediaProjection API进行屏幕采集,而无需root权限 。MediaProjection 允许应用程序捕获屏幕内容并进行处理。
在实际实现过程还需要用到下面两个类:
MediaProjectionManager:是一个系统服务,看名字可以理解为对MediaProjection进行管理,所以在使用时需要通过MediaProjectionManager获取MediaProjection。
VirtualDisplay:大家可以理解为安卓上面的虚拟显示器,而最终的屏幕显示采集就是通过这个虚拟显示器实现的,可以理解为在录屏时安卓系统会将主屏画面拷贝一份到这个虚拟显示,而虚拟显示器会将图像数据最终输出到Surface,Surface还是之前说的大家理解为队列或者缓冲区都可以。
具体的屏幕采集实现流程如下:
屏幕采集实现
还是与之前一样我们需要对屏幕采集完整的流程进行封装,感觉有点封装上瘾了,哈哈。新建一个ScreenCapture类,并添加如下代码。
class ScreenCapture {private val REQUEST_CODE: Int = 1000internal val act: Activity?internal var videoEncFormat:VideoEncFormat = VideoEncFormat()internal var dpi:Int = 320internal val callback:((ByteArray,Int)->Unit)?internal var data: Intent? = nullinternal var resultCode:Int = 0internal val mediaProjectionManager: MediaProjectionManager?private constructor(builder:Builder){act = builder.actvideoEncFormat = builder.videoEncFormatdpi = builder.dpicallback = builder.callbackmediaProjectionManager = act?.getSystemService(Context.MEDIA_PROJECTION_SERVICE) as MediaProjectionManager}
乍一看,是不是感觉全局变量有点多,不要怕,实际的比这个还要多一些,跟编码相关的一部分已经封装到了videoEncFormat中,这个就是我们上一篇中优化后的编码参数类。
ScreenCapture因为参数比较多,所以我使用了构造者模式,ScreenCapture不能直接创建必须通过构造器来创建。
然后屏幕录制时需要用到Activity,这里注意下是Activity,不是Context,因为屏幕录制权限申请需要通过startActivityForResult函数进行请求,权限申请结果会在onActivityResult中返回,所以这里的REQUEST_CODE就是Activity的请求码,data,resultCode则是onActivityResult中返回的Intent和结果码。
这里需要传入一个dpi,后面会设置到VirtualDisplay,可以认为就是虚拟显示器的dpi,我们任何屏幕都会有dpi,虚拟显示器也不例外。
这里的callback则是编码数据返回的回调接口。
全局变量作用介绍完了,那么下来我们看下这个构造器长什么样子。
companion object{fun newBuilder():Builder{return Builder()}
}class Builder{var act: Activity? = nullvar videoEncFormat:VideoEncFormat = VideoEncFormat()var dpi:Int = 320var callback:((ByteArray,Int)->Unit)? = nullfun with(act: Activity):Builder{this.act = actreturn this}fun resolution(width:Int,height:Int):Builder{videoEncFormat.setWidth(width)videoEncFormat.setHeight(height)return this}fun fps(fps:Int):Builder{videoEncFormat.setFrameRate(fps)return this}fun bitRate(bitRate:Int):Builder{videoEncFormat.setBitRate(bitRate)return this}fun keyInterval(keyInterval:Float):Builder{videoEncFormat.setKeyInterval(keyInterval)return this}fun rotation(rotation:Int):Builder{videoEncFormat.setRotation(rotation)return this}fun setCaptureCallback(cbk:(data:ByteArray,flag:Int)-> Unit):Builder{this.callback = cbkreturn this}fun dpi(dpi:Int){this.dpi = dpi}fun build():ScreenCapture{return ScreenCapture(this)}}
这个类很简单,就不再赘述,有疑问可以留言,我看到后会答复。
这里还添加了一个快捷函数,Java写习惯了,Kotlin貌似不需要,哈哈,留着吧。
接着我们来看下,如何启动屏幕采集:
fun start(){act?.startActivityForResult(mediaProjectionManager?.createScreenCaptureIntent(), REQUEST_CODE)}fun onActivityResult(requestCode:Int, resultCode:Int, data: Intent) {if (requestCode == REQUEST_CODE && resultCode == Activity.RESULT_OK) {this.resultCode = resultCodethis.data = dataScreenEncoderService.start(this)}}
start()这里通过Activity的startActivityForResult启动录屏权限请求,第一个参数是Intent,但不是我们自建的,而是通过mediaProjectionManager.createScreenCaptureIntent()直接获取的,第二个就是咱们上面定义的请求码了。
不管用户确认还是取消,最后权限结果都会通过onActivityResult返回,如果权限校验是OK的,那么先记录下返回的结果码和data(Intent),这两个数据后面录屏启动流程中还需要用到,之后是调用了ScreenEncoderService.start(this),这里是启动了名为ScreenEncoderService的Service,后续所有的录屏流程都在ScreenEncoderService中实现了。
这里之所以将后续录屏流程放到了Service中实现,是因为在targetSdkVersion大于等于29(Android 10)时,系统加强了对屏幕采集的限制,必须先启动相应的前台Service,才能正常调用getMediaProjection方法,否则会抛出异常。
我们再来看下ScreenCapture中的stop()。
fun stop(){ScreenEncoderService.stop()}
stop()中的ScreenEncoderService.stop()与start()中的类似,不过这里是停止ScreenEncoderService。
下来,我们来看录屏的核心服务ScreenEncoderService。
class ScreenEncoderService :ForegroundService(),VideoEncoder.EncoderCallback{private var mediaProjection: MediaProjection? = nullprivate var virtualDisplay: VirtualDisplay? = nullprivate var videoEncoder: VideoEncoder? = nulloverride fun onCallback(data: ByteArray, frameFlags: Int) {capture?.callback?.invoke(data,frameFlags)}
ScreenEncoderService继承了ForegroundService类,ForegroundService是一个封装的前台服务类,这个类大家可以在ForegroundService看到,这里就不过多扩充前台服务的知识了,有需要的可以自行查找了解。
ScreenEncoderService也实现了VideoEncoder编码器的EncoderCallback接口,通过这个接口间接的将编码数据回调给监听者。
这个三个全局变量,我就不介绍了,mediaProjection和virtualDisplay上面已经介绍过了,接下来只需要关注他们的怎么实例化即可,videoEncoder这个是之前我们封装的视频编码器,有需要了解的可以通过Android Camera2采集并编码为H.264文章进行了解。
companion object{private var capture:ScreenCapture? = nullfun start(capture:ScreenCapture){this.capture = capturevar intent:Intent = Intent(capture.act,ScreenEncoderService::class.java)if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {capture.act?.startForegroundService(intent)return}capture.act?.startService(intent)}fun stop(){capture?.act?.stopService(Intent(capture?.act,ScreenEncoderService::class.java))}}
start()函数中保存了我们外面调用的ScreenCapture,接着通过capture中保存的Activity对象启动了自己,启动的时候进行了版本校验,如果版本大于等于26就会启动为前台服务,否则就启动为后台服务。
override fun onCreate() {super.onCreate()startScreenCapture()}private fun startScreenCapture() {var inputSurface = startEncoder()capture?.let {mediaProjection = it.mediaProjectionManager?.getMediaProjection(it.resultCode, it.data!!)var dpi = it.dpivar width = it.videoEncFormat.getWidth()var height = it.videoEncFormat.getHeight()virtualDisplay = mediaProjection?.createVirtualDisplay("ScreenCapture",width, height, dpi,DisplayManager.VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR,inputSurface, null, null)}}
在Service生命周期的onCreate中调用了startScreenCapture(),这个startScreenCapture()就是我们最后的屏幕采集实现了。
startScreenCapture() 中先调用了startEncoder()返回了一个Surface,这个实际上就是VideoEncoder的输入Surface。startEncoder()等下再看,我们先将startScreenCapture()看完
通过mediaProjectionManager以及前面onActivityResult中返回过来的data和resultCode获取MediaProjection实例mediaProjection,接着通过mediaProjection又创建了VirtualDisplay的实例virtualDisplay,
createVirtualDisplay()中的参数比较多,我单独列了个表格,其作用如下:
参数名称 | 作用 |
---|---|
name | 虚拟显示的名称,必须非空。这是用于标识虚拟显示的一个字符串。 |
width | 虚拟显示的宽度(以像素为单位),必须大于0。这指定了虚拟显示的像素宽度。 |
height | 虚拟显示的高度(以像素为单位),必须大于0。这指定了虚拟显示的像素高度。 |
densityDpi | 虚拟显示的密度(以dpi为单位),必须大于0。这指定了虚拟显示的屏幕密度。 |
surface | 虚拟显示的内容应该被渲染到的 Surface,如果没有则为 null。这个 Surface 是应用提供的,用于渲染虚拟显示的内容。 |
flags | 虚拟显示标志的组合,可以是以下几种: VIRTUAL_DISPLAY_FLAG_PUBLIC:创建公共显示。 VIRTUAL_DISPLAY_FLAG_PRESENTATION:创建用于展示的显示。 VIRTUAL_DISPLAY_FLAG_SECURE:创建安全的显示,内容不会被截屏或录屏。 VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY:只显示应用自己的内容。 VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR:自动镜像主屏幕的内容。 |
callback | 当虚拟显示的状态改变时调用的回调,如果没有则为 null。这个回调用于监听虚拟显示的状态变化。 |
handler | 应该在哪个 Handler 上调用回调,如果没有则为 null,这意味着回调将在调用线程的主 Looper 上被调用。 |
虚拟显示器创建成功后,就标志着已经开始进行屏幕数据采集了,这些都是系统内部自行实现的。
那么让我们回过头来看看startEncoder()。
private fun startEncoder():Surface? {if(videoEncoder == null){ videoEncoder = VideoEncoder(capture!!.videoEncFormat).apply {setEncoderCallback(this@ScreenEncoderService)start()}}var inputSurface = videoEncoder?.getInputSurface()return inputSurface}
这块VideoEncoder创建就显得简单很多了,创建videoEncoder时将capture中传入的参数及编码VideoEncFormat直接传给了VideoEncoder,接着设置了编码后的数据回调并启动了编码器。
在之后获取了编码器的输入Surface并返回。
至此屏幕采集和编码部分的核心代码就已经编码完成,接着让我们再继续添加如下代码:
private fun stopScreenCapture(){videoEncoder?.stop()mediaProjection?.stop()virtualDisplay?.release()}override fun onDestroy() {super.onDestroy()stopScreenCapture()}
在Service销毁的时候同步停止了屏幕采集,停止屏幕采集的时候一并销毁了虚拟显示器,这一点大家一定要注意,这两个要同步销毁。
现在ScreenEncoderService已经编写完成,那么还有一个小点不要忘记了,将它添加到AndroidManifest中。
<service android:name="com.zlgspace.andcodec.codec.ScreenEncoderService"android:foregroundServiceType="mediaProjection"/>
foregroundServiceType中的mediaProjection应该是固定写法,没有深究,这个大家记着这么写即可。
至此我们屏幕采集编码的封装就已经全部完成,接下来让我们看看如何应用。
使用ScreenCapture
lateinit var screenCapture:ScreenCapturescreenCapture = ScreenCapture.newBuilder().with(this).fps(30).resolution(1920, 1080).setCaptureCallback{data,flag->//对编码后的数据进行处理}.build()screenCapture.start()screenCapture.stop()override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {super.onActivityResult(requestCode, resultCode, data)screenCapture.onActivityResult(requestCode, resultCode, data)
}
是不是还算比较简单,通过构造器实例化ScreenCapture后就可以对屏幕采集进行开始或者停止操作,不过还需要转发下Activity的onActivityResult到ScreenCapture的onActivityResult,这块略微有点繁琐,但是目前没有其他好的办法,只能这样。
写到最后
到这里,我们屏幕采集并编码就已经全部完成,整个实现还是有点粗糙,但是相信我们一定会将这些打磨的更加优秀,至此感谢大家观看,如果觉得对你有帮助希望能点下关注,博主是多年Android开发者,关于Android从应用到系统,多少都懂一些,对于安卓后续还会持续更新更多更高质量的博文,对自己的技能加强的的同时,也希望能够帮到有需要的同学。
相关文章:
Android 屏幕采集并编码为H.264
前言 我们前面基于摄像机的图像采集以及编解码已经完成了,那么接下来计划后面的三篇博文分别实现Android屏幕采集实现并进行H.264编解码、MIC音频采集并编码为AAC以及AAC解码播放,希冀可以通过这六篇博文能够对Android上面的音视频编解码有一个初步的学…...
xuggle操作视频
文章目录 xuggle操作视频 xuggle操作视频 有个需求是要读取视频的宽高,找到了Xuggle和FFmpeg两种方式,FFmpeg很强大,但是我并不需要那些功能,所以使用了轻量一点的Xuggle 引入依赖 <dependency><groupId>xuggle<…...
Python|Pyppeteer实现自动获取eBay商品数据【进阶版】(27)
前言 本文是该专栏的第27篇,结合优质项目案例持续分享Pyppeteer的干货知识,记得关注。 在本专栏的上一篇文章中,笔者有详细介绍基于python中的pyppeteer,“根据目标关键词,来实现自动获取eBay的商品数据”。而本文,笔者将在上一篇文章《Python|Pyppeteer实现自动获取eBa…...
Android Studio新版本的一个资源id无法找到的bug解决
Android Studio新版本的一个资源id无法找到的bug解决 文章目录 Android Studio新版本的一个资源id无法找到的bug解决一、前言二、Android Studio的无法获取到资源id的bug1、一段简单的Java代码1、错误现象2、错误解决方法 三、其他1、小结2、gradle.properties文件 其他相关属性…...
CAPL如何设置或修改CANoe TCP/IP协议栈的底层配置
在CANoe中创建网络节点作为以太网主机时,可以给其配置独立的TCP/IP Stack。 配置的协议栈有一些底层配置参数可以在界面上设置或修改,比如: MTU上图中MTU显示500只是图形界面显示错误,正确值是1500。 TCP延迟确认这些参数也可以通过CAPL动态配置,甚至CAPL还可以配置很多界…...
【git】--- 通过 git 和 gitolite 管理单仓库的 SDK
在编程的艺术世界里,代码和灵感需要寻找到最佳的交融点,才能打造出令人为之惊叹的作品。而在这座秋知叶i博客的殿堂里,我们将共同追寻这种完美结合,为未来的世界留下属于我们的独特印记。【git】--- 通过 git 和 gitolite 管理单仓库的 SDK 开发环境一、安装配置 gitolite二…...
Avalonia实战实例一:使用Prism创建项目,并创建窗口
文章目录 一、安装Avalonia的项目模板二、安装Prism框架三、简单更改App.axaml为Prism基类四、创建窗口一、安装Avalonia的项目模板 这里安装的是Avalonia 11.2.1,.Net 6.0 安装完成,创建Avalonia项目。 二、安装Prism框架 打开Nuget,搜索Prism: 不要安装Prism.Core: …...
单元测试,集成测试,系统测试的区别是什么?
实际的测试工作当中,我们会从不同的角度对软件测试的活动进行分类,题主说的“单元测试,集成测试,系统测试”,是按照开发阶段进行测试活动的划分。这种划分完整的分类,其实是分为四种 “单元测试,…...
Composer在安装的过程中经常找不到刚更新的包
明明有v2.1.0版本,安装就是找不到这个版本的包。 1. Composer 官方网址:https://getcomposer.org 中文网站:https://www.phpcomposer.com 官方文档:https://docs.phpcomposer.com 2. Packagist Packagist 是 Composer的组件仓库…...
ue5 motion matching
ue5.5 gameanimationsample 先看动画蓝图 核心两个node 第一个是根据数据选择当前的pose 第二个是缓存一段历史记录,为第一个node选择的时候提供数据。 在animinstance的update方法中 每帧都更新这个函数,每帧更新trajectory的数据 看看第一个node的…...
ThinkPHP知识库文档系统源码
知识库文档系统 一款基于ThinkPHP开发的知识库文档系统,可用于企业工作流程的文档管理,结构化记录沉淀高价值信息,形成完整的知识体系,能够轻松提升知识的流转和传播效率,更好地成就组织和个人。为部门、团队或项目搭…...
如何将自己的PHP类库发布到composer仓库
将自己的 PHP 类库发布到 Composer 仓库,需要经过一系列的准备和操作步骤,以下是详细说明: 准备工作 创建类库项目:确保你的 PHP 类库项目具有清晰的目录结构,遵循 PSR-4 等 PHP 编码规范。通常,类文件应…...
vue监听点击自己和点击其他元素;el-popover通过visible控制显隐;点击其他隐藏el-popover
场景: 一般点击元素自己,显示弹框;点击页面其他元素都是关闭 特别是处理el-popover通过visible控制显隐的时候。点击其他隐藏el-popover <el-popover :visible <template><div><div style"border: 1px solid #ccc; p…...
Strawberry Fields:探索学习量子光学编程的奇妙世界
一、Strawberry Fields 简介 Strawberry Fields 是由加拿大量子计算公司Xanadu开发的全栈 Python 库,在量子计算领域中占据着重要的地位。它为设计、模拟和优化连续变量(CV)量子光学电路提供强大工具,Strawberry Fields 的强大之…...
【初阶数据结构与算法】初阶数据结构总结之顺序表、单链表、双链表、栈、队列、二叉树顺序结构堆、二叉树链式结构(附源码)
文章目录 一、顺序表二、单链表三、双链表四、栈(先进后出)五、队列六、二叉树链式结构---堆七、二叉树链式结构接口实现 在之前我们学习了大部分初阶数据结构,我们现在从特点、优缺点以及应用场景大致总结一下,放出源码,如果想要看具体分析请…...
209. 长度最小的子数组 C++
文章目录 一、题目链接二、参考代码 一、题目链接 链接: 209. 长度最小的子数组 二、参考代码 暴力思路:两个for循环,找符合条件的子序列,复杂度(O(n^2)) int minSubArrayLen(const vector&l…...
基于python的一个简单的压力测试(DDoS)脚本
DDoS测试脚本 声明:本文所涉及代码仅供学习使用,任何人利用此造成的一切后果与本人无关 源码 import requests import threading# 目标URL target_url "http://47.121.xxx.xxx/"# 发送请求的函数 def send_request():while True:try:respo…...
异步操作、Promise和axios
1.Javascript是单线程的 什么是进程,什么是线程? 进程:进程是操作系统分配资源和调度的基本单位。它是一个程序的实例,包含了运行程序所需的代码和数据以及其它资源。 线程:线程是进程中的实际运行单位,也是…...
微信小程序开发简易教程
微信小程序文件结构详解 1. 项目配置文件 project.config.json 项目的配置文件包含项目名称、appid、编译选项等配置示例: {"description": "项目配置文件","packOptions": {"ignore": []},"setting": {&quo…...
基于 Java 实现的环形数组队列详解
1. 环形数组队列简介 队列(Queue)是一种常用的线性数据结构,具有先进先出(FIFO)的特点。在传统的线性队列中,随着出队操作,队列前端会出现空闲空间,但这些空间无法重复使用…...
opencv函数
1、二值化图 二值化图:就是将图像中的像素改成只有两种值,其操作的图像必须是灰度图。 2.1、阈值法(THRESH_BINARY) 通过设置一个阈值,将灰度图中的每一个像素值与该阈值进行比较,小于等于阈值的像素就被设置为0(黑&…...
fastadmin集成kindeditor编辑器解决段后距问题--全网唯一
背景 由于项目的需求使用fastadmin推荐的编辑器kindeditor,使用过程中发现没有段后距这个bug。查询搜索了所有的网上来源,都没有解决方案。鉴宇客户非常需要该功能,奋战几天写端代码实现了该功能。 插件实现 KindEditor.plugin(paragra…...
【Mybatis】Mybatis 魔法世界探秘:从配置起航,开启数据持久化的奇幻入门之旅
目录 1.JDBC回顾 1.1JDBC编程 2.Mybatis使用 2.1什么是Mybatis 2.2Mybatis环境配置 1.引入依赖 2.lombok的操作 2.3Mybatis编程 1.数据库创建 2.创建实体类 3.配置数据库 4.Mapper持久层编写 5.单元测试 2.4常见的问题日志 1.密码错误 2.SQL语句错误 3.数据库…...
uni-app在image上绘制点位并回显
在 Uni-app 中绘制多边形可以通过使用 Canvas API 来实现。Uni-app 是一个使用 Vue.js 开发所有前端应用的框架,同时支持编译为 H5、小程序等多个平台。由于 Canvas 是 H5 和小程序中都支持的 API,所以通过 Canvas 绘制多边形是一个比较通用的方法。 1.…...
top命令和系统负载
1 top中的字段说明 top是一个实时系统监视工具,可以动态展现出 CPU 使用率、内存使用情况、进程状态等信息,注意这些显示的文本不能直接使用 > 追加到文件中。 [rootvv~]# top -bn 1 | head top - 20:08:28 up 138 days, 10:29, 4 users, load av…...
算法之要求对任意的i,j,k三个位置,如果i < j < k,都有arr[i] + arr[k] != arr[j],返回构造出的arr。
目录 1. 题目2. 解释3. 思路4. 代码 Code06_MakeNo5. 总结 1. 题目 给定一个正整数M,请构造出一个长度为M的数组arr,要求对任意的i,j,k三个位置,如果i < j < k,都有arr[i] arr[k] ! arr[j]返回构造…...
Y3编辑器文档4:触发器
文章目录 一、触发器简介1.1 触发器界面1.2 ECA语句编辑及快捷键1.3 参数设置1.4 变量设置1.5 实体触发器1.6 函数库与触发器复用 二、触发器的多层结构2.1 子触发器(在游戏内对新的事件进行注册)2.2 触发器变量作用域2.3 复合条件2.4 循环2.5 计时器2.6…...
Ubuntu中安装配置交叉编译工具并进行测试
01-下载获取交叉编译工具的源码 按照博文 https://blog.csdn.net/wenhao_ir/article/details/144325141的方法,把imx6ull的BSP下载好后,其中就有交叉编译工具。 当然,为了将来使用方便,我已经把它压缩并传到了百度网盘ÿ…...
HCIA笔记7--OSPF协议入门
文章目录 0. 路由分类1. OSPF介绍1.1 概念1.2 报文类型 2. 邻接关系的建立2.1 邻居关系的建立2.2 邻接关系的形成2.3 ospf状态机 3. DR与BDR3.1 为什么要有DR和BDR?3.2 DR和BDR的选举原则 4. ospf的配置4.1 内部优先级 5. 问题5.1 三层环路如何解决? Ref…...
文件系统--底层架构(图文详解)
一、文件系统的底层存储与寻址 当我们谈到文件系统的底层结构时,最关键的问题是:文件的数据与元数据(属性)如何存储在磁盘上,以及系统是如何定位这些数据的?在谈及文件系统之前,我们要先对储存…...
温州医院儿童自闭症康复中心:为孩子打开光明未来
在自闭症这一神秘而复杂的神经发育障碍面前,无数家庭曾陷入迷茫与无助。然而,在中国的大地上,有两座灯塔般的存在,它们分别为温州医院儿童自闭症康复中心和广州星贝育园自闭症儿童寄宿制学校,它们用专业的技术和无尽的…...
Tr0ll: 1 Vulnhub靶机渗透笔记
Tr0ll: 1 本博客提供的所有信息仅供学习和研究目的,旨在提高读者的网络安全意识和技术能力。请在合法合规的前提下使用本文中提供的任何技术、方法或工具。如果您选择使用本博客中的任何信息进行非法活动,您将独自承担全部法律责任。本博客明确表示不支…...
网络通信技术
网络通信技术 IP路由基础 什么是路由 路由是指导报文转发的路径信息,通过路由可以确认转发IP报文的路径。路由设备是依据路由转发报文到目的网段的网络设备,最常见的路由设备:路由器。路由设备维护着一张路由表,保存着路由信息。路由的功能 路径选择数据转发、数据过滤维…...
十一、容器化 vs 虚拟化-Docker 使用
文章目录 前言一、Docker Hello World二、Docker 容器使用三、Docker 镜像使用四、Docker 容器连接五、Docker 仓库管理六、Docker Dockerfile七、Docker Compose八、Docker Machine九、Swarm 集群管理 前言 Docker 使用 Docker 容器使用、镜像使用、容器连接、仓库管理、Do…...
npm error Error: Command failed: F:\360Downloads\Software\nodejs\node.exe
前言: 电脑环境:win7 node版本:18.20.0 npm版本:10.9.2 情景再现:电脑上是存在的vuevite的项目且可以正常运行。想着摸鱼的时间复习一下ts语法,所以想创建一个demo。按照 开始 | Vite 官方中文文档 官网创建…...
html中,实现通过拖拽调整图像尺寸
<!DOCTYPE html> <html lang"en"> <head> <meta charset"UTF-8"> <meta name"viewport" content"widthdevice-width, initial-scale1.0"> <title>html中拖拽修改图像尺寸</title> <styl…...
sqlmap详解
一.sqlmap -u URL --forms sqlmap -u http://192.168.11.136:1337//978345210/index.php --forms 针对特定的 URL 进行 SQL 注入测试,特别是针对表单(form)的 POST 注入 forms:这个参数告诉 sqlmap 解析并测试目标 URL 中的表单…...
浏览器插件开发实战
浏览器插件开发实战 [1] 入门DEMO一、创建项目二、创建manifest.json三、加载插件四、配置 service-worker.js五、以书签管理器插件为例manifest.jsonpopup.htmlpopup.js查看效果 [2] Vue项目改造成插件一、复习Vue项目的结构二、删除、添加个别文件三、重写build [3] 高级开发…...
【特殊子序列 DP】力扣552. 学生出勤记录 II
可以用字符串表示一个学生的出勤记录,其中的每个字符用来标记当天的出勤情况(缺勤、迟到、到场)。记录中只含下面三种字符: ‘A’:Absent,缺勤 ‘L’:Late,迟到 ‘P’:Pr…...
C/C++流星雨
系列文章 序号直达链接1C/C爱心代码2C/C跳动的爱心3C/C李峋同款跳动的爱心代码4C/C满屏飘字表白代码5C/C大雪纷飞代码6C/C烟花代码7C/C黑客帝国同款字母雨8C/C樱花树代码9C/C奥特曼代码10C/C精美圣诞树11C/C俄罗斯方块12C/C贪吃蛇13C/C孤单又灿烂的神-鬼怪14C/C闪烁的爱心15C/C…...
Docker 安装 Jenkins:2.346.3
准备:已安装Docker,已配置服务器安全组规则 1581 1、拉取镜像 [rootTseng ~]# docker pull jenkins/jenkins:2.346.3 2.346.3: Pulling from jenkins/jenkins 001c52e26ad5: Pull complete 6b8dd635df38: Pull complete 2ba4c74fd680: Pull complet…...
枫清科技高雪峰:从数据到知识,重塑产业智能化的核心驱动力
2024 年 12 月 5 日,由智东西主办的“2024 中国生成式 AI 大会”在上海盛大开幕,汇聚了全球 AI 领域的顶尖专家、行业领袖与技术创新者。枫清科技(Fabarta)创始人兼 CEO 高雪峰应邀出席,并在大会上发表主题演讲&#x…...
【过滤器】.NET开源 ORM 框架 SqlSugar 系列
目录 0、 过滤器介绍 1、表过滤器 (推荐) 1.1 手动添加过滤器 1.2 禁用、清空、备份和还原 1.3 联表查询设置 1.4 动态添加 2、修改和删除用过滤器 2.1 局部设置 2.2 全局设置 (5.1.4.62) 3、子查询用过滤器 4、联表过滤…...
在 Ansys Q3D 中求解直流和交流电感
提取电缆的电感对于确保电气和电子系统的性能和可靠性至关重要。本篇博客文章将介绍使用 Ansys Q3D 求解直流和交流电感的过程。 概述 在这个例子中,我们将考虑一个由两组电缆组成的简单几何:正极和负极,如下所示: 可以使用“自…...
location重定向和nginx代理
文章目录 1 location重定向1.1 概述1.2 rewrite跳转1.3 用例1.4 实验1.4.1 基于域名的跳转1.4.2 基于ip的跳转1.4.3 基于后缀名的跳转 2 nginx的代理2.1 nginx内置变量2.2 实验2.2.1 前提条件2.2.2 正向代理2.2.3 自动代理 1 location重定向 1.1 概述 重定向:就是…...
币安移除铭文市场的深度解读:背后原因及其对区块链行业的影响
引言: 就在昨天,2024年12月10号,币安宣布将移除铭文市场(Inscriptions Market)。这一消息引发了全球加密货币社区的广泛关注,尤其是在比特币NFT和数字收藏品市场快速发展的背景下。铭文市场自诞生以来迅速…...
【论文复现】基于曲率的图重新布线
📝个人主页🌹:Eternity._ 🌹🌹期待您的关注 🌹🌹 ❀ 无基于曲率的图重新布线 论文概述核心算法算法说明关键代码 运行方法数据集配置文件训练和测试 运行结果 论文概述 论文链接 Topping, Jake,…...
scala的Array
特性 类型安全:Scala 中的数组是类型安全的,这意味着一旦声明了数组的类型,就只能存储该类型的元素。 大小固定:数组的大小在创建时确定,之后不能改变。 零索引:Scala 数组与 Java 数组一样,都…...
【HarmonyOS实战开发】鸿蒙JS崩溃分析
当未处理的JS异常导致应用意外退出时,应用会生成对应的JS崩溃日志文件,开发者可通过错误日志查看引起崩溃的代码位置及分析应用崩溃的原因。本文将分别介绍JS崩溃分析思路以及典型分析案例。 一、日志信息 以下是崩溃日志信息中对应字段解释。 Device…...
【Vue3】前端使用 FFmpeg.wasm 完成用户视频录制,并对视频进行压缩处理
强烈推荐这篇博客!非常全面的一篇文章,本文是对该博客的简要概括和补充,在不同技术栈中提供一种可行思路,可先阅读该篇文章再阅读本篇: FFmpeg——在Vue项目中使用FFmpeg(安装、配置、使用、SharedArrayBu…...