前端视频播放技术概览

转眼间,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.rhkb.cn/news/75122.html

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

相关文章

掌握好视频翻译软件的使用方法,帮你跨越语言障碍

嘿&#xff0c;翻译小达人们&#xff0c;你知道吗&#xff0c;当你看到一段充满神秘符号的英语视频&#xff0c;脑袋里冒出一大片问号的时候&#xff0c;别慌&#xff01;我们有比手动翻译更妙的解决办法——视频翻译。嗯&#xff0c;这货可不一般&#xff0c;它能帮你解读视频…

【2023年电赛国一必备】C题报告模板--可直接使用

任务 图1 任务内容 要求 图2 基本要求内容 图3 发挥部分内容 说明 图4 说明内容 评分标准 图5 评分内容 正文 &#xff08;部分&#xff09; 摘要 本实验基于TI公司的TM4C123GH6PM主控&#xff0c;结合OPA2337芯片和其他硬件模块&#xff0c;设计并制作了一种单相逆变器…

信号的频谱分析与信号滤波

信号的频谱分析与信号滤波 试验目的&#xff1a;熟悉信号的频谱分析与信号滤波。 信号的频谱分析 例、建立一个含50Hz和120Hz幅值为2的正弦信号&#xff08;sin&#xff09;&#xff0c;然后叠加一个幅值为1的随机信号&#xff0c;利用Matlab分析其频谱。并滤除噪声信号和12…

Go语言并发编程(千锋教育)

Go语言并发编程&#xff08;千锋教育&#xff09; 视频地址&#xff1a;https://www.bilibili.com/video/BV1t541147Bc?p14 作者B站&#xff1a;https://space.bilibili.com/353694001 源代码&#xff1a;https://github.com/rubyhan1314/go_goroutine 1、基本概念 1.1、…

【1++的C++进阶】之多态

&#x1f44d;作者主页&#xff1a;进击的1 &#x1f929; 专栏链接&#xff1a;【1的C进阶】 文章目录 一&#xff0c;什么是多态&#xff1f;二&#xff0c;剖析多态的调用原理三&#xff0c;抽象类四&#xff0c;多继承中的虚函数表 一&#xff0c;什么是多态&#xff1f; …

谈谈对Android音视频开发的探究

在日常生活中&#xff0c;视频类应用占据了我们越来越多的时间&#xff0c;各大公司也纷纷杀入这个战场&#xff0c;不管是抖音、快手等短视频类型&#xff0c;虎牙、斗鱼等直播类型&#xff0c;腾讯视频、爱奇艺、优酷等长视频类型&#xff0c;还是Vue、美拍等视频编辑美颜类型…

list模拟

之前模拟了string,vector&#xff0c;再到现在的list&#xff0c;list的迭代器封装最让我影响深刻。本次模拟的list是双向带头节点的循环链表&#xff0c;该结构虽然看起来比较复杂&#xff0c;但是却非常有利于我们做删除节点的操作&#xff0c;结构图如下。 由于其节点结构特…

【etcd】docker 启动单点 etcd

etcd: v3.5.9 etcd-browser: rustyx/etcdv3-browser:latest 本文档主要描述用 docker 部署单点的 etcd&#xff0c; 用 etcd-browser 来查看注册到 etcd 的 key 默认配置启动 docker run -d --name ai-etcd --networkhost --restart always \-v $PWD/etcd.conf.yml:/opt/bitn…

从gRPC入门到放弃

文章目录 gRPCgRPC是什么为什么要用gRPC安装gRPC安装gRPC安装Protocol Buffers v3安装插件检查 gRPC的开发方式编写.proto文件定义服务生成指定语言的代码编写业务逻辑代码 gRPC入门示例编写proto代码编写Server端Go代码编写Client端Go代码gRPC跨语言调用生成Python代码编写Pyt…

K8s安全配置:CIS基准与kube-bench工具

01、概述 K8s集群往往会因为配置不当导致存在入侵风险&#xff0c;如K8S组件的未授权访问、容器逃逸和横向攻击等。为了保护K8s集群的安全&#xff0c;我们必须仔细检查安全配置。 CIS Kubernetes基准提供了集群安全配置的最佳实践&#xff0c;主要聚焦在两个方面&#xff1a;主…

基于双层优化的微电网系统规划设计方法(Matlab代码实现)

目录 &#x1f4a5;1 概述 1.1 微电网系统结构 1.2 微电网系统双层规划设计结构 1.3 双层优化模型 1.4 上层容量优化模型 1.5 下层调度优化模型 &#x1f4da;2 运行结果 &#x1f389;3 文献来源 &#x1f308;4 Matlab代码、数据、文章讲解 &#x1f4a5;1 概述 文献来源&…

牛客网Verilog刷题——VL51

牛客网Verilog刷题——VL51 题目答案 题目 请编写一个十六进制计数器模块&#xff0c;计数器输出信号递增每次到达0&#xff0c;给出指示信号zero&#xff0c;当置位信号set 有效时&#xff0c;将当前输出置为输入的数值set_num。模块的接口信号图如下&#xff1a; 模块的时序图…

作者推荐 | 【底层服务/编程功底系列】「底层技术原理」史上最清晰的采用程序员的视角方式进行深入探索Linux零拷贝技术原理及实现

采用程序员的视角方式进行深入探索Linux零拷贝技术原理及实现 背景介绍什么是零拷贝第一步&#xff1a;用户空间数据复制到内核空间第二步&#xff1a;用户空间数据复制到内核空间第三步&#xff1a;用户空间数据再次复制到内核空间第四步&#xff1a;内核态数据buffer写回到So…

html5播放器视频切换和连续播放的实例

当前播放器实例可以使用changeVid接口切换正在播放的视频。当有多个视频&#xff0c;在上一个视频播放完毕时&#xff0c;自动播放下一个视频时也可采用该处理方式。 const option {vid: 88083abbf5bcf1356e05d39666be527a_8,//autoplay: true,//playsafe: , //PC端播放加密视…

超详细|ChatGPT论文润色教程

本文讲述使用中科大开源ChatGPT论文辅助工具&#xff0c;对论文进行润色 祝看到本教程的小伙伴们都完成论文&#xff0c;顺利毕业。 可以加QQ群交流&#xff0c;一群&#xff1a; 123589938 第一章 介绍 今天给大家分享一款非常不错的ChatGPT论文辅助工具&#xff0c;使用了专…

电脑更新win10黑屏解决方法

电脑更新win10黑屏解决方法 电脑黑屏出现原因解决步骤 彻底解决 电脑黑屏 出现原因 系统未更新成功就关机&#xff0c;导致系统出故障无法关机 解决步骤 首先长安电源键10s关机 按电源键开机&#xff0c;出现logo时按F8进入安全模式。 进入自动修复环境后&#xff0c;单击…

ElasticSearch 7.x

前言 elastic表示可伸缩&#xff0c;search表示查询。所以es的核心即为查询。通常情况下&#xff0c;我们的数据可以分为三类&#xff1a;结构化数据、非结构化数据、半结构化数据。 结构化数据&#xff1a;一般会用特定的结构来组织和管理数据&#xff0c;表现为二维表结构。…

Spring Bean的生命周期

文章目录 Spring Bean的生命周期加载Bean对象创建Bean对象构造对象填充属性初始化实例注册销毁 销毁 Spring Bean的生命周期 Spring Bean的生命周期就是指Bean对象从创建到销毁的过程&#xff0c;大体可以分为&#xff1a;实例化、属性赋值、初始化、使用、销毁。 加载Bean对象…

【数据分析】numpy (二)

numpy作为数据分析&#xff0c;深度学习常用的库&#xff0c;本篇博客我们来介绍numpy的一些进阶用法&#xff1a; 一&#xff0c;numpy的常用简单内置函数&#xff1a; 1.1求和&#xff1a; a np.array([[1, 2],[3, 4]]) np.sum(a)10 1.2求平均值&#xff1a; np.mean(a…

向 Maven 中央仓库上传一个修改过的基于jeecg的autoPOI的 jar包记录

1、注册https://issues.sonatype.org/账号 下面就代表注册好了&#xff0c;同时提交的工单也通过了 2、这里主要是goupId 需要进行认证&#xff0c;需要到域名注册商近一个txt的解析&#xff0c;以便确保这个是你的 通过下面来验证你的域名信息&#xff0c;这里主要是上面的工…