前端视频播放技术概览

转眼间,2023 年已进入下半场,在这样一个时间节点下,长视频平台如爱奇艺、优酷、腾讯视频等,以及短视频平台如抖音、快手等,对大家来说早已是司空见惯的事物。然而,在我们追剧、刷弹幕的时候,很少有人会去深入思考这些平台背后的技术奥秘。直到最近,我需要在前端项目中实现视频播放时,我终于意识到,这些视频不仅在格式上存在着差异,甚至连播放形式都各有不同。举个例子,当下最为火热的 “直播”,通常是指实时的视频播放。相对应地,非实时的视频播放则被称为 “点播”。如果你有接触过 Netflix,你或许还听说过 “流媒体” 这个词汇。为了更好地理解这些概念,我利用空闲时间整理了一个相对完整的技术体系,并以此为基础撰写了今天这篇文章。

从 HTML5 说起

好了,现在让我们从最简单的视频播放方案开始说起。在 HTML5 标准发布前,主流的视频播放方案是使用 Adobe 的 Flash Player 插件,国内的优酷、土豆等视频网站创立初期都经历过这个阶段。后来,随着乔布斯那封 “关于 Flash 的思考” 的公开信的发表,某种意义上加速了整个 Flash 技术的 “消亡”。再后来,随着 HTML5 标准发布,我们可以使用 video 或者 audio 标签在网页中呈现音/视频内容。如图所示,下面是 video 标签的基本用法:

<video controls><source src="myVideo.mp4" type="video/mp4"><source src="myVideo.webm" type="video/webm"><p>Your browser doesn't support HTML5 video. Here isa <a href="myVideo.mp4">link to the video</a> instead.</p>
</video>

具体来讲,这个 video 标签可以支持 OggMPEG4WebM 三种视频格式。可惜,并不是所有的浏览器都支持这些格式,因此,你可以在 video 标签内指定多个视频源,并且当这些视频源都不被支持的时候,你可以使用一个自定义的 HTML 结构来进行降级处理。需要注意的是,MPEG-4MP4 格式,实际上是一组格式,因为在视频处理中通常还涉及到编码器的问题。可不幸的是,浏览器目前唯一支持的编码器是 H.264,在这种情况下,可选择的视频格式将会变得非常有限。

在这里插入图片描述

综上所述,这种方案的缺点主要体现在:可选择的视频格式、以及这些视频格式的兼容性问题上。在大多数情况下,你可以使用一个静态文件服务来承载这些视频文件,这意味着每次浏览器都会下载完整的视频文件。

[HttpGet("download")]
public ActionResult Download([FromQuery] string fileName)
{var filePath = Path.Combine(_hostEnvironment.ContentRootPath, "Assets", fileName);if (!System.IO.File.Exists(filePath)) return NotFound();var contentType = GetContentType(filePath);var fileStream = System.IO.File.OpenRead(filePath);return File(fileStream, contentType, enableRangeProcessing: true);
}

事实上,你还可以使用带 Range 请求头的请求方式,来实现类似断点续传或者分段下载的效果。如图所示,可以注意到,分段下载返回的状态码为 206,客户端通过 Range字段告知服务端自己希望下载文件的哪一部分,而服务器则通过 Content-Range 字段告知客户端当前返回是文件的哪一部分,以及整个文件的大小:

在这里插入图片描述

RTMP 还是 RTSP?

虽然,从易用性的角度来看,HTML5 中的 video 标签显得平易近人,可遗憾的是,我们到现在为止仅仅看到是视频播放领域的冰山一角。举一个简单的例子,当我们通过电视收看电视台的节目时,有时会遇到现场直播的场景。在此场景下,人们对视频播放有了实时性的要求。如下图所示,常见的流媒体协议,大体上可以分为三类:

  • 传统的视频流协议:以 RTMP 和 RTSP 为代表
  • 基于 HTTP 的自适应协议:以 HTTP-FLV、HLS、MPEG-DASH 为代表
  • 新技术:以 WebRTC 和 SRT 为代表

在这里插入图片描述

在通常的认知中,直播使用 RTMP 或者 RTSP,点播使用 HTTP。当然,工程上的问题没有绝对,随着近年来直播行业的持续火爆,理论上上述协议中的任意一种都可以实现直播。不过,在此之前,我们最好还是来了解一下,目前应用最为广泛的 RTMP 和 RTSP 。在这个过程中,你或许会深刻地感受到技术更迭带来的那种沧桑感。

RTMP,即:Real Time Messaging Protocol,是一种以 TCP 协议作为底层协议的实时消息协议,采用了 H.264 视频编解码器 以及 ACC 音频编解码器。时间追溯到 2005 年,Macromedia 这家公司制定并开发了 RTMP 协议,其目的是为了在 RTMP 服务器和用户设备上的 Flash 播放器之间传输流式数据。后来,Adobe 收购了 Macromedia,Flash 技术得到了进一步的发展,相继诞生了 ActionScript、Adobe AIR 等产品。再后来,乔布斯那封 “关于 Flash 的思考” 的公开信,像一篇檄文一样直指 Flash 的种种缺点,冥冥中 “加速” 了 Flash 的消亡。当然,从 “事后诸葛亮” 的角度来看,我们可以说那是乔帮主的又一次的成功预言。可这世上的事情总是难以捉摸,就像 Flash 早已于 2020 年宣布死亡,可 RTMP 如今还活着,孰是孰非,有时候真的有没有那么重要吗?

在这里插入图片描述

如图所示,是一个 RTMP 的基本流程。可以注意到,其核心在于 “推流” 和 “拉流”,简单来说,我们可以通过 FFmpeg 或者 OBS 来向一个 RTMP 服务器推送视频流。此时,运行在用户设备上的 Flash 播放器或者流媒体播放器就可以从 RTMP 服务器上拉取视频流,进而实现视频播放的效果。相较于 HTTP 这种 “平民化” 的协议,RTMP 这种视频流协议明显要更复杂一点。幸运的是,我们可以借助 Nginx 及其第三方模块 nginx-http-flv-module、nginx-rtmp-module 快速搭建一个 RTMP 服务器,这里以 nginx-http-flv-module 为例来进行说明:

rtmp {server {listen 1935;application pushLive {live on;gop_cache on;}}
}

首先,通过 rtmp 节点下的一系列配置,我们让 Nginx 摇身一变成为一个 RTMP 服务器,如前文所述,RTMP 服务器使用 1935 端口。在此基础上,我们增加一个名为 pushLive 的应用,这一步的目的是对外暴露一个推送视频流的路由。一个 RTMP 服务器下可以同时配置多个应用,其中,live on; 指令表示开启直播,gop_cache on; 指令表示开启 GOP 的缓存。那么,GOP 又是什么呢?通常情况下,它是视频流中两个关键帧之间的距离。考虑到解码器必须拿到 GOP 才能开始解码、播放,因此,缓存 GOP 一定程度上可以降低延迟。

http {server {location /pullLive {flv_live on;chunked_transfer_encoding  on;add_header Access-Control-Allow-Credentials true;add_header Access-Control-Allow-Origin *;add_header Access-Control-Allow-Headers X-Requested-With;add_header Access-Control-Allow-Methods GET,POST,OPTIONS;add_header Cache-Control no-cache;}}
}

接下来,我们需要在 Nginx 服务器里配置一个拉取视频流的路由,这里拉取的是 FLV 格式的视频流。考虑到视频流的推送方和拉取方通常位于不同的域,因此,还需要为 Nginx 配置跨域所需的头部字段。至此,我们搭建出了一个简单的 RTMP 服务器。

在这里插入图片描述

在这里插入图片描述

现在,我们只需要分别在 OBS 和 VLC 配置推流、拉流的地址。这里的推流码以及 stream 字段,可以理解为直播的频道或者房间号。只要双方都在一个频道或者房间里,即可实现直播效果:

在这里插入图片描述

好的,介绍完 RTMP,现在再来介绍一下 RTSP,它表示的是实时流协议,即:Real Time Streaming Protocol。该协议于 1996 年问世,由 RealNetworks、Netscape和哥伦比亚大学的专家共同开发。RTSP 使用 TCP 和 UDP 作为底层协议,其中,TCP 负责传输控制指令,如播放和停止,而 UDP 负责传输音频、视频和其他数据。与 RTMP 类似,RTSP 采用了 ACC 作为音频编码器,采用 H.265 作为视频编码器。考虑到,RTMP 是Adobe 的专有协议,而 RTSP 是一种公共协议。因此,在传统的安防、监控等垂直领域中,人们更倾向于使用 RTSP ,通常在集成这类产品时,都需要集成厂商提供的 SDK 或安装相应的浏览器插件。

ffmpeg -re  -rtsp_transport tcp -i <rtsp地址> -vcodec libx264 -vprofile baseline -acodec aac -ar 44100 -strict -2 -ac 1 -f flv -s 1280x720 -q 10 <rtmp推流地址>

我个人认为,RTSP 唯一的优势在于更低的延迟,如果让我来选的话,我还是会选择 RTMP。数学上有种重要的思维,将一个从未解决过的问题,转化为一个解决过的问题。此时,我们就可以使用上面的命令将一个 RTSP 视频流转换为 RTMP 视频流,这样,我们就可以完全不用理会厂商提供的 SDK 或者浏览器插件。毕竟,ActiveX、OLE 控件早就该扫进历史的垃圾堆啦。当然,除了 FFmpeg,你还可以使用 VLC 播放器进行推流转换,大家有兴趣的话可以自行尝试。

Flash 的折戟沉沙

对于喜欢怀旧的人来说,回顾历史时总不免一阵唏嘘。因为一同被扫进历史垃圾堆的,不单单有的 ActiveX、OLE 控件、Sliverlight、MFC、CGI、SOAP… ,还有一度被 Adobe 寄予厚望的 Flash。2020 年 12 月以后,Adobe Flash Player 将不再受到浏览器的支持,这大抵正式宣告了 Flash 的死亡。有趣的是,当年微软为了对抗 Flash 而推出的 Sliverlight,如今早已是 “王图霸业,血海深仇,尽归尘土”,唯有 HTML5 “桃花依旧笑春风”,这一幕,与金庸先生《天龙八部》里逍遥三老的经历何其相似耶!无独有偶,微软的 IE 浏览器与谷歌的 Chrome 浏览器相爱相杀多年,直到去年 6 月 IE 浏览器正式退役,微软打不过就加入的做法,不免让人担忧,“屠龙少年终成恶龙” 这一幕会再次上演。如果注定要这样的话,那么,我只能选择 Firefox🤦。

在这里插入图片描述

言归正传,Flash 的折戟沉沙,直接导致 RTMP 和 RTSP 这对难兄难弟失去了赖以生存的 “土壤”。对于 RTMP 来说,其缺点主要是 FLV 与 HTML5 的兼容性问题、带宽问题,人们依然可以使用那些支持 FLV 格式的播放器软件,例如 PotPlayer、KMPlayer、VLC 等等;可对于 RTSP 来说,其本身与 HTTP 不兼容,必须要借助第三方插件,例如 NPAPI/PPAPI、ActiveX 等等。事实上,这些插件在不同浏览上的兼容性并不能得到充分的保证,这样就造成了一个相对混乱的局面。我以为,最重要的原因是,浏览器或者说前端生态圈,缺少像 FFmpeg 一样强大的视频编/解码工具。不过,随着 WebAssembly 等技术的成熟,我相信这一切终有一天会得到改善!

苹果公司在成功 “驱逐” Flash 以后,转头就开始开发自家的流媒体协议,这便是后来的 HLS 协议,其全称为:HTTP Live Streaming,是一种基于 HTTP 的流媒体传输协议,主要用于实时音/视频流的传输。其基本原理是:首先,它会将整个流切割为无数个可以通过 HTTP 下载的小文件。然后,它会向客户端提供一个配套的媒体文件列表。此时,客户端只需要按照顺序拉取和播放媒体文件,即可实现看起来像是在播放一条完整流媒体的效果。也许,你此前从来没有听说过这些内容,可当你在移动设备上播放 m3u8 格式的视频时,我想告诉你,你已经同 HLS 见过面啦!实际上,这个说法并不太严谨,因为 m3u8 本身就只是一个播放列表,真正的媒体文件其实是那些 ts文件。考虑到整个传输层是标准的 HTTP 协议,因此,这个方案可以非常方便地利用 CDN 进行分发、加速。

FLV 还是 HLS?

感谢苹果公司,感谢乔布斯,现在我们有了新的选择。类似地,我们可以使用 Nginx 及其第三方模块 nginx-rtmp-module 来搭建一个支持 HLS 协议的流媒体服务器。其基本思路是:通过 OBS 或者 FFmpeg 向 RTMP 服务器推送视频流,服务器端生成对应的 m3u8 以及 ts 文件,此时,只需要像访问静态资源一样访问 m3u8 文件即可。除此之外,你同样可以将事先生成好的 m3u8 以及 ts 文件放置在指定的目录。首先,我们来定义一个用于推送视频流的路由:

rtmp {server {listen 1935;application pushLive {live on;hls on;hls_path /tmp/hls;hls_fragment 15s;meta off;}}
}

接下来,我们需要定义用于访问 m3u8 资源的路由。其中:alias 指令需要和上文中的 hls_path 指令保持一致。否则,Nginx 可能会找不到这些 m3u8 资源:

http {server {location /hls {types {application/vnd.apple.mpegurl m3u8;}alias /tmp/hls;add_header Access-Control-Allow-Credentials true;add_header Access-Control-Allow-Origin *;add_header Access-Control-Allow-Headers X-Requested-With;add_header Access-Control-Allow-Methods GET,POST,OPTIONS;add_header Cache-Control no-cache;}}
}

显然,接下来的事情就顺理成章啦!我们通过 OBS 推流,再通过 VLC 播放即可,如下图所示。此时此刻,屏幕前的你,是不是觉得刘海柱更像奇异博士了呢?😺

在这里插入图片描述

在这里插入图片描述

为了证明我前面讲过的理论,这里放一张容器内 /tmp/hls/ 目录下的内容截图,可以注意到,它的确生成了相应的 m3u8 以及 ts文件:

在这里插入图片描述

事实上,关于 ts 文件,其正式的格式名称为 MPEG-TS,视频编码采用了 H.264 格式,音频编码则采用了 AAC、MP3、AC-3 或者 EC-3 格式,这意味着它同样可以支持纯音频格式,即 MPEG 格式的基本音频文件。整体而言,HLS 和上文中提到过的 MPEG-DASH 非常相似,均采用了这种 “切片播放” 的思想,由于每个切片文件都非常小,因此,它在客户端产生的缓存更小、起播更快、响应更快。HLS 最初主要在苹果的 iOS 中流行,后来,逐渐获得安卓平台系统层面的支持,整体来看,在移动端的兼容性相当不错。

曾几何时,乔帮主炮轰 Flash,主要的观点是:Flash 在移动设备上性能差影响电池续航;Flash 是为个人电脑和鼠标设计的,并不适用于触屏和手指。多年以后,回头来再看这段,当年乔布斯的确做了一个正确的决定,越来越多的原生应用被 Web 技术代替,React Native、Flutter、Electron、Taro…等技术的流行,大抵都在证明 Web/HTML5 是大势所趋、是不可违逆的历史潮流。如今,当我们用前端技术去构建 App 和小程序时,这些曾经游走在原生应用中的历史遗留问题,终于要再次浮出水面。

幸运的是,我们生活在一个技术高度发展的年代,作为 Flash 时代的遗产,浏览器本身是不支持 FLV 格式的,直到盛行二次元文化的 B 站开源了 flv.js,这让在前端播放 FLV 格式的视频成为可能,我们继续使用前面 RTMP 直播的示例来演示这部分功能。首先,我们准备一个简单的 HTML 结构,同时在页面中引入 flv.js :

<script src="https://cdn.bootcdn.net/ajax/libs/flv.js/1.5.0/flv.min.js"></script>
<video id="videoElement" controls autoplay width="100%" height="100%"></video>

感谢 flv.js,我们只需要下面几行代码就可以播放一个来自 RTMP 视频流:

<script>if (flvjs.isSupported()) {var videoElement = document.getElementById('videoElement');var flvPlayer = flvjs.createPlayer({type: 'flv',isLive:true,url: 'http://127.0.0.1:50001/pullLive?port=1935&app=pushLive&stream=test'});flvPlayer.attachMediaElement(videoElement);flvPlayer.load();flvPlayer.play();}
</script>

视频流在网页中的播放效果,如下图所示,经过博主测试,延迟大概在 15 秒左右:

在这里插入图片描述

类似地,对于 HLS 协议的视频流,我们可以使用 hls.js 从前端承载一个实时的视频流:

<script src="https://cdn.jsdelivr.net/npm/hls.js@latest"></script>
<video id="video"></video>
<script>if (Hls.isSupported()) {var video = document.getElementById('video');hls.attachMedia(video);hls.on(Hls.Events.MEDIA_ATTACHED, function () {hls.loadSource('http://127.0.0.1:50002/hls/test.m3u8');hls.on(Hls.Events.MANIFEST_PARSED, function (event, data) {console.log('manifest loaded, found ' + data.levels.length + ' quality level');});});}
</script>

此时,视频流在网页中的播放效果,如下图所示,测试发现,延迟大概在 15 秒左右:

在这里插入图片描述

理论上,HLS 比 FLV 的延迟要高一点,从目前这个结果来看,两者似乎平分秋色,所以,这道题该怎么选呢?我的想法是:在 PC 端选 FLV,在移动端选 HLS。因为国内主流的视频网站,基本上都在用 FLV。诚然,这有些人云亦云,可工程上的解决方案,有哪一个不是一点点摸索出来的,最重要的是适合自己!

WebRTC 的期冀

行文至此,关于流媒体的前世今生,其实已经基本厘清,甚至我们还能从尘埃往事中读出历史惊人的相似性,正如苹果公司开发 HLS 协议是为了挣脱 Flash 的摆布,可即便有乔布斯那样极具远见的战略眼光,依然阻挡不了 HTML5 那如潮水一般涌来的 “大势所趋”。也许,后人有一天会像嫌弃 Flash 一样嫌弃 HLS。技术的进步,始终是一件掺杂着期许和遗憾的事情,甚至于连同此时此刻都永远不是终点。所以,接下来我想聊一聊实时音/视频通信的未来——WebRTC。

回想过去三年的疫情,催生出大量网课/在线教育、视频会议、远程办公……等方面的需求,而 WebRTC 这样一个支持实时音/视频通信的开放标准,天然地拟合着这些业务方向,如前文所述,虽然利用 Nginx 搭建一个流媒体服务器并不算太难,可如果想要在当下的网络环境中做到低延迟,还是需要投入大量的精力去做进一步的优化。关于 WebRTC 的细节,大家可以自行检索,这里推荐一个更成熟的流媒体服务器 SRS,它可以支持本文中提及的所有协议,最最最重要的是它支持 WebRTC 协议。所以,下面我们利用 SRS 来快速搭建一个 WebRTC 的示例,可以让大家提前感受一下 WebRTC 的魅力:

docker run --rm --env CANDIDATE=<Your_IP> -p 1935:1935 -p 8080:8080 -p 1985:1985 -p 8000:8000/udp registry.cn-hangzhou.aliyuncs.com/ossrs/srs:4 objs/srs -c conf/rtmp2rtc.conf

首先,参照官方文档,我们将运行一个支持从 RTMP 转换到 WebRTC 的 SRS 容器实例。顾名思义,它可以将接收到的 RTMP 视频流,以 WebRTC 的形式发布出来。此时,我们只需要在浏览器中建立 WebRTC 连接即可。

docker run --rm -it registry.cn-hangzhou.aliyuncs.com/ossrs/srs:encoder ffmpeg -stream_loop -1 -re -i doc/source.flv -c copy -f flv rtmp://host.docker.internal/live/livestream

接下来,我们同样参照官方示例,运行一个 FFmpeg 的容器示例,它负责将内置的一个 FLV 格式的视频文件推送到 RTMP 服务器上。这里的 RTMP 服务器,其实就是 SRS 容器实例。可以注意到,FFmpeg 确实指向了一个 rtmp:// 开头的地址。当然,在掌握了这些原理以后,你可以继续使用 OBS 推送视频流。对程序员来说,不会/记不住命令行这件小事,实在是无伤大雅。因为在真理面前,一切不过都是工具罢了,甚至连你我都是,不是吗?

在这里插入图片描述

此时,我们打开浏览器,就可以在 WebRTC 播放器中看到对应的视频。因为主流的浏览器基本都支持 WebRTC ,所以,你不需要再像以前一样借助第三方库或者插件,就可以直接播放视频。从实际的效果来看,除了画质有点模糊以外,整体表现要比 FLV 和 HLS 出色。博主认为,前端值得探索的方向还有更多,譬如 WebAssembly、WebGL、IoT、跨平台/跨端技术等等,如果始终局限在写页面,则永远是没有出路的。最近这几年 “前端已死” 的声音不绝于耳,不知道屏幕前的诸位作何感想呢?

写文章写到凌晨的时候,我的疲倦早已所剩无几,起初选择这个方向的时候,我更多的是关注前端、关注我需要解决的问题。可等我梳理清这些知识脉络以后,我发现它的知识点远比我想象中的密集、庞杂。因此,整篇博客基本上就是在查资料和做实验交替进行的情况下写出来的。本文先后介绍了 HTML5 中的视频播放技术、RTMP/RTSP这类传统的视频流协议、HTTP-FLV/HLS这类基于 HTTP 的自适应协议,以及属于未来的新技术 WebRTC。在这个过程中,博主提到了 Flash 的衰落、HTML5 的崛起,即便你对于技术话题百分百无感,这些互联网世界里的奇闻佚事,或许还有机会成为一种怀念。而对于我说,如何在专业和活泼两者间完成取舍,同样是一件值得思考的事情,就像视频播放总要在带宽、画质、延迟、流量等因素上综合权衡一样。

参考文章

  • 实时传输 Web 音频与视频
  • 秒懂流媒体协议 RTMP 与 RTSP
  • 流媒体协议 HLS
  • SRS - 介绍
  • 有了 WebRTC ,直播可以这样玩!
  • 用 WebRTC 和 Node.js 开发实时视频聊天应用

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

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

相关文章

电压放大器工作在什么状态

电压放大器是一种广泛应用于电子电路中的基本电路元件&#xff0c;其主要功能是将输入信号的电压放大到所需的输出电压幅值&#xff0c;并且保持信号的形状不变。在实际电路设计中&#xff0c;电压放大器的工作状态会受到多种因素的影响&#xff0c;比如输入信号的频率、放大倍…

iOS--runtime

什么是Runtime runtime是由C和C、汇编实现的一套API&#xff0c;为OC语言加入了面向对象、运行时的功能运行时&#xff08;runtime&#xff09;将数据类型的确定由编译时推迟到了运行时平时编写的OC代码&#xff0c;在程序运行过程中&#xff0c;最终会转换成runtime的C语言代…

爱尔眼科四川省区“同心博爱 光明工程”“西部健康公益行”炉霍站启动

8月1日&#xff0c;“同心博爱 光明工程”“西部健康公益行”炉霍站出征仪式在四川爱尔眼科医院隆重举行。 此次公益活动由民革成都市委会、中共锦江区委统战部指导&#xff0c;如意树爱心促进会主办&#xff0c;民革锦江区总支部、爱尔眼科四川省区支持&#xff0c;四川爱尔眼…

手把手教你从零开始搭建个人博客

随着技术的进步和用户需求的变化&#xff0c;个人博客的形式和内容一直在不停地演变。为了给读者提供更丰富、有趣的阅读体验&#xff0c;搭建个人博客的网站一直在寻找更好的优化方法。所以现在出现了一批功能更完善的个人博客搭建软件&#xff0c;今天looklook就以HelpLook为…

C++设计模式之适配器设计模式

文章目录 C适配器设计模式什么是适配器设计模式该模式有什么优缺点优点缺点 如何使用 C适配器设计模式 什么是适配器设计模式 适配器设计模式是一种行为型设计模式&#xff0c;它允许你将两个不兼容的接口组合在一起&#xff0c;使它们能够协同工作。 该模式有什么优缺点 优…

Activiity跳转startActivity源码分析Activity启动流程(下)

调用ActivityThread子类ClientTranslationHandler的scheduleTranslation 注意上图有个sendMessage的 接着会执行translacationExecutor的execute方法。 都会走cycleToPath方法 cycleToPath方法对应的performLifecycleSequence 调用Actvitiy各个生命周期。 然后是第二种情况&am…

设计模式行为型——命令模式

目录 什么是命令模式 命令模式的实现 命令模式角色 命令模式类图 命令模式举例 命令模式代码实现 命令模式的特点 优点 缺点 使用场景 注意事项 什么是命令模式 命令模式&#xff08;Command Pattern&#xff09;是一种数据驱动的设计模式&#xff0c;它属…

【C#学习笔记】值类型(1)

虽然拥有编程基础的人可以很快地上手C#&#xff0c;但是依然需要学习C#的特性和基础。本系列是本人学习C#的笔记&#xff0c;完全按照微软官方文档编写&#xff0c;但是不适合没有编程基础的人。 文章目录 .NET 体系结构Hello&#xff0c;World类型和变量&#xff08;重要&…

docker容器创建私有仓库(第三篇)

目录 六、创建私有仓库 七、Docker资源限制 7.1、CPU使用率 7.2、CPU共享比例 7.3、CPU周期限制 7.4、CPU核心限制 7.5、CPU 配额控制参数的混合案例 7.6、内存限制 7.7、Block IO 的限制 7.8、限制bps 和iops 8、Docker数据持久化 8.1、数据持久化介绍 8.2、Volum…

操作系统的运行机制、中断和异常、系统调用

&#x1f40c;个人主页&#xff1a; &#x1f40c; 叶落闲庭 &#x1f4a8;我的专栏&#xff1a;&#x1f4a8; c语言 数据结构 javaweb 石可破也&#xff0c;而不可夺坚&#xff1b;丹可磨也&#xff0c;而不可夺赤。 操作系统 一、操作系统的运行机制1.1内核程序1.2应用程序1…

maven如何打包你会吗?

1.新建一个maven项目&#xff0c;在main/java中建立Main类 public class Main {public static void main(String[] args) {System.out.println("hello java ...");} } 2.添加依赖&#xff0c;使其成为可执行包 <build><plugins><!--打包成为可执行包-…

ESP32cam系列教程003:ESP32cam实现远程 HTTP_OTA 自动升级

文章目录 1.什么是 OTA2. ESP32cam HTTP_OTA 本地准备2.1 HTTP OTA 升级原理2.2 开发板本地基准程序&#xff08;程序版本&#xff1a;1_0_0&#xff09;2.3 开发板升级程序&#xff08;程序版本&#xff1a;1_0_1&#xff09;2.4 本地 HTTP_OTA 升级测试2.4.1 本地运行一个 HT…

ARP协议请求

文章目录 作用请求与应答流程数据包ARP协议以太网帧协议具体应用 作用 通过 IP地址 查找 MAC地址。 请求与应答流程 A&#xff1a;数据发送主机 B&#xff1a;目标主机 目前只知道目标主机IP地址&#xff0c;想把数据发送过去&#xff0c;需要查询到目标主机的MAC地址&#x…

Git使用详细教程

1. cmd面板的常用命令 clear&#xff1a;清屏cd 文件夹名称----进入文件夹cd … 进入上一级目录(两个点)dir 查看当前目录下的文件和文件夹(全拼:directory)Is 查看当前目录下的文件和文件夹touch 文件名----创建文件echo 内容 > 创建文件名----创建文件并写入内容rm 文件名…

vue3.3-TinyMCE:TinyMCE富文本编辑器基础使用

一、TinyMCE官网 GitHub - tinymce/tinymce TinyMCE中文文档中文手册 二、官网介绍 TinyMCE是一款易用、且功能强大的所见即所得的富文本编辑器。同类程序有&#xff1a;UEditor、Kindeditor、Simditor、CKEditor、wangEditor、Suneditor、froala等等。 TinyMCE的优势&…

LabVIEW开发小型减阻试验平台

LabVIEW开发小型减阻试验平台 湍流摩擦在粘性流体的阻力中起着重要作用&#xff0c;减少湍流摩擦是流体力学领域的热门话题之一。在油气管道的长距离流体输送中&#xff0c;泵站提供的几乎所有动力都用于克服流体的胫骨摩擦。在流体输送领域&#xff0c;船舶的蒙皮摩擦阻力占总…

css实现水平居中

代码示例 <div class"box"><div class"box1"></div> </div>1.弹性布局&#xff1a;&#xff08;推荐&#xff09; display:flex&#xff1b; 这些要添加在父级的&#xff0c;是父级的属性 //父级添加display:flex; //父级添加jus…

SpringCloud Gateway 在微服务架构下的最佳实践

作者&#xff1a;徐靖峰&#xff08;岛风&#xff09; 前言 本文整理自云原生技术实践营广州站 Meetup 的分享&#xff0c;其中的经验来自于我们团队开发的阿里云 CSB 2.0 这款产品&#xff0c;其基于开源 SpringCloud Gateway 开发&#xff0c;在完全兼容开源用法的前提下&a…

腾讯云TencentOS Server镜像系统常见问题解答

腾讯云TencentOS Server镜像是腾讯云推出的Linux操作系统&#xff0c;完全兼容CentOS生态和操作方式&#xff0c;TencentOS Server操作系统为云上运行的应用程序提供稳定、安全和高性能的执行环境&#xff0c;TencentOS可以运行在腾讯云CVM全规格实例上&#xff0c;包括黑石物理…

【波浪动态特效】基于jquery实现页面底部波浪动画效果(附完整源码下载)

文章目录 写在前面涉及知识点实现效果1、搭建页面1.1、创建两个片区1.2、创建波浪区域1.3、静态页面源码 2、JS实现波浪效果2.1 动画原理2.2 动画源码 3、源码分享3.1 百度网盘3.2 123云盘3.3 邮箱留言 总结 写在前面 想必搭建过企业官网的大多数对这个效果不陌生吧&#xff0…