《WebRTC系列》实战 Web 端支持 h265 硬解

2023-11-16

1、背景

Web 端实时预览 H.265 需求一直存在,但由于之前 Chrome 本身不支持 H.265 硬解,软解性能消耗大,仅能支持一路播放,该需求被搁置。

去年 9 月份,Chrome 发布 M106 版本,默认开启 H.265 硬解,使得实时预览支持 H.265 硬解具备可行性。

然而 WebRTC 本身支持的视频编码格式仅包括 VP8、VP9、H.264、AV1,并不包含 H.265。根据 w3c 发布的 2023 WebRTC Next Version Use Cases 来看,近期也没有打算支持 H.265 的迹象,因而决定自研实现 WebRTC 对 H.265 的支持。

2、DataChannel

背景说到 chrome 支持了 h265 的硬解,但 WebRTC 并不支持直接传输 h265 视频流。但可以通过 datachannel 来绕过这个限制

WebRTC 的数据通道 DataChannel 是专门用来传输除音视频数据之外的任何数据的(但并不意味着不可以传输音视频数据,本质上它就是一条 socket 通道),如短消息、实时文字聊天、文件传输、远程桌面、游戏控制、P2P加速等。

1)SCTP协议

DataChannel 使用的协议是 SCTP(Stream Control Transport Protocol) (是一种与TCP、UDP同级的传输协议),可以直接在 IP 协议之上运行。

但在 WebRTC 的情况下,SCTP 通过安全的 DTLS 隧道进行隧道传输,该隧道本身在 UDP 之上运行,同时支持流控、拥塞控制、按消息传输、传输模式可配置等特性。需注意单次发送消息大小不能超过 maxMessageSize(只读, 默认65535字节)。

2)可配置传输模式

DataChannel 可以配置在不同模式中,一种是使用重传机制的可靠传输模式(默认模式),可以确保数据成功传输到对等端;另一种是不可靠传输模式,该模式下可以通过设置 maxRetransmits 指定最大传输次数,或通过 maxPacketLife 设置传输间隔时间实现;

这两种配置项是互斥的,不可同时设置,当同为null 时使用可靠传输模式,有一个值不为 null 时开启不可靠传输模式。

3)支持数据类型

数据通道支持 string 类型或 ArrayBuffer 类型,即二进制流或字符串数据。

后续两种方案,都是基于 datachannel 来做

3、方案一 WebCodecs

官方文档: github.com/w3c/webcode…

思路: DataChannel 传输 H.265 裸流 + Webcodecs 解码 + Canvas 渲染。即 WebRTC 的音视频传输通道(PeerConnection) 不支持 H.265 编码格式,但可采用其数据通道(DataChannel)来传输 H.265数据,前端收到后使用 Wecodecs 解码、Canvas 渲染。

优点:

  • 直接传输 H.265 裸码流,无需额外封装,实现简单方便;无冗余数据,传输效率高

  • Wecodecs 解码延迟低,实时性很高

缺点:

  • 音频需额外单独传输、解码和播放,需处理音视频同步问题

  • 既有 sdk 基于 video 封装,webcodes 方案依赖 canvas,既有 video 相关操作,需要全部重写,比如截图,录像等操作

  • 由于线上各项目等历史原因,既有 sdk 改动大,时间上不允许

4、方案二 MSE

官方例子: github.com/bitmovin/ms…

思路:Fmp4封装 + DataChannel 传输 + MSE 解码播放。即先将 H.265 视频数据封装成 Fmp4 格式,再通过 WebRTC DataChannel 通道进行传输,前端收到后采用 MSE 解码, video 进行播放。

优点:

  • 复用 video 标签播放,无需单独实现渲染

  • 音视频已封装到 Fmp4 中,web 端无需考虑音视频同步问题

  • 整体工作量相比 Wecodecs 小,可快速上线

缺点:

  • 设备端实现 Fmp4 封装可能存在性能问题,因此需要云端转发实时进行解封装,或者前端解封装

  • MSE 解码实时性不好(云端首次切片会有 1~2 秒延迟)

5、方案抉择

第一版本先以 MSE 上线。云端,前端开发量相对少,roi 高。

计划第二版上 wecodecs,不仅低延迟,而且可以避免云端耗流量的问题,节省成本。假设在第二版期间,WebRTC 官方支持了 H.265,那么直接兼容官方方案即可。

5.1 细说 Mse 及第一版 sdk 改造

Media Source Extensions, 媒体源扩展。官方文档: developer.mozilla.org/zh-CN/docs/…

引入 MSE 之后,支持 HTML5 的 Web 浏览器就变成了能够解析流协议的播放器了。

从另一个角度来说,通过引入 MSE,HTML5 标签不仅可以直接播放其默认支持的 mp4、m3u8、webm、ogg 等格式,还可以支持能够被 (具备MSE功能的)JS 处理的视频流格式。如此一来,我们就可以通过 (具备MSE功能的)JS,把一些原本不支持的视频流格式,转化为其支持的格式(如 H.264 的 mp4,H.265 的 fmp4)。

比如 B站开源的 flv.js 就是一个典型应用场景。B站的 HTML5 播放器,通过使用 MSE 技术,将 FLV源用 JS(flv.js) 实时转码成 HTML5 支持的视频流编码格式,提供给 HTML5 播放器播放。

// 此 demo 来自下面链接的官方示例, 可以直接跑起来,比较直观
// https://github.com/bitmovin/mse-demo/blob/main/index.html
​
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>MSE Demo</title>
</head>
<body>
  <h1>MSE Demo</h1>
  <div>
    <video controls width="80%"></video>
  </div>
​
  <script type="text/javascript">
    (function() {
      var baseUrl = 'https://bitdash-a.akamaihd.net/content/MI201109210084_1/video/720_2400000/dash/';
      var initUrl = baseUrl + 'init.mp4';
      var templateUrl = baseUrl + 'segment_$Number$.m4s';
      var sourceBuffer;
      var index = 0;
      var numberOfChunks = 52;
      var video = document.querySelector('video');
​
      if (!window.MediaSource) {
        console.error('No Media Source API available');
        return;
      }
        
      // 初始化 mse
      var ms = new MediaSource();
      video.src = window.URL.createObjectURL(ms);
      ms.addEventListener('sourceopen', onMediaSourceOpen);
​
      function onMediaSourceOpen() {
        // codecs,初始化 sourceBuffer
        sourceBuffer = ms.addSourceBuffer('video/mp4; codecs="avc1.4d401f"');
        sourceBuffer.addEventListener('updateend', nextSegment);
​
        GET(initUrl, appendToBuffer);
        
        // 播放
        video.play();
      }
​
      function nextSegment() {
        var url = templateUrl.replace('$Number$', index);
        GET(url, appendToBuffer);
        index++;
        if (index > numberOfChunks) {
          sourceBuffer.removeEventListener('updateend', nextSegment);
        }
      }
​
      function appendToBuffer(videoChunk) {
        if (videoChunk) {
          // 二进制流转换为 Uint8Array,sourceBuffer 进行消费
          sourceBuffer.appendBuffer(new Uint8Array(videoChunk));
        }
      }
​
      function GET(url, callback) {
        var xhr = new XMLHttpRequest();
        xhr.open('GET', url);
        xhr.responseType = 'arraybuffer';
​
        xhr.onload = function(e) {
          if (xhr.status != 200) {
            console.warn('Unexpected status code ' + xhr.status + ' for ' + url);
            return false;
          }
          // 获取 mp4 二进制流
          callback(xhr.response);
        };
​
        xhr.send();
      }
    })();
  </script>
</body>
</html>

通过上面的 demo,以及测试(将 dmeo 中的 fmp4 片段换成我们自己的 IPC 设备(摄像头),H.265 类型的)得知,chrome 可以硬解 H.265 类型的 fmp4 片段。So,事情变得明朗了起来。大方向有了,无非就是 H.265 裸流,转换成 fmp4 片段,chrome 底层硬解。

5.2 fmp4 前端实时解封装

H.265 裸流解封装 fmp4,调研下来,如果纯 js 进行封装,工作量挺大。尝试用 wasm 调 c++ 的库,发现即使解封装性能也不大好。所以放在前端被 pass 掉了。

【学习地址】:FFmpeg/WebRTC/RTMP/NDK/Android音视频流媒体高级开发

【文章福利】:免费领取更多音视频学习资料包、大厂面试题、技术视频和学习路线图,资料包括(C/C++,Linux,FFmpeg webRTC rtmp hls rtsp ffplay srs 等等)有需要的可以点击1079654574加群领取哦~

5.3 fmp4 云端实时解封装

性能好,对前端 0 侵入。确定了云端解封装,接下来讲讲这段时间开发遇到的核心链路演变,及最终的流程方案。

6、阶段一

云端实时解封装 Fmp4,写死 codecs(音视频编码类型) -> 前端 MSE 解码播放 -> 播放几秒后,失败,MSE 会抛异常,大概意思就是你的数据不对了,前后衔接不上。

排查下来,是 MSE 处于 updating 的时候,不能进行消费,数据直接被丢掉,导致后续数据衔接不上。那既然不能丢,我们就缓存下来。具体可以看下面的代码注释。

具体可以看代码注释:

const updating = this.sourceBuffer?.updating === true;
const bufferQueueEmpty = this.bufferQueue.length === 0;
​
  if (!updating) {
    if (bufferQueueEmpty) {
      // 缓存队列为空: 仅消费本次 buffer
      this.appendBuffer(curBuffer);
    } else {
      // 缓存队列不为空: 消费队列 + 本次 buffer
      this.bufferQueue.push(curBuffer);
​
      // 队列中多个 buffer 的合并
      const totalBufferByteLen = this.bufferQueue.reduce(
        (pre, cur) => pre + cur.byteLength,
        0
      );
      const combinedBuffer = new Uint8Array(totalBufferByteLen);
      let offset = 0;
      this.bufferQueue.forEach((array, index) => {
        offset += index > 0 ? this.bufferQueue[index - 1].length : 0;
        combinedBuffer.set(array, offset);
      });
​
      this.appendBuffer(combinedBuffer);
      this.bufferQueue = [];
    }
  } else {
    // mse 还在消费上一次 buffer(处于 updating 中), 缓存本次 buffer, 否则会有丢帧问题
    this.bufferQueue.push(curBuffer);
  }

考虑到 Fmp4 数据每一帧都不可丢失,因此 datachannel 走的是可靠传输。

但是测试下来,发现了新的问题。随着时间的增长,延迟会累积增大。因为丢包后,网络层会进行重试,重试的时间会累积进延时。我们测试下来,网络情况不好的时候,延迟会高达 30 秒及以上,理论上会一直增加,如果你拉流时间足够久的话

7、阶段二

ok,换个思路,既然不丢帧 + 可靠传输带来的延时问题完全不能接受,那么如果换用不可靠传输呢?

不可靠传输,意味着会丢帧。调研下来,Fmp4 可以丢掉一整个切片(一个切片包含多帧),既然如此,我们可以设计一套丢帧算法,只要判断到一个切片是不完整的,我们就把整个切片丢掉。

这样的话,理论上来讲,最多只会有一个切片的延迟,大概在2秒左右,业务层可以接受。

丢帧算法设计思路:在每一帧数据头部增加 4 个字节的数据,用来标识每一帧的具体信息。

  • segNum: 2个字节,大端模式,Fmp4片段序列号,从1开始,每次加1

  • fragCount: 1个字节,Fmp4片段分片总数,最小为1

  • fragSeq: 1个字节,Fmp4片段分片序列号,从1开始

前端拿到每帧数据后,对前 4 个字节进行解析,就能获取到每帧数据的详细信息。举个例子,假如我要判断当前帧是否为最后一帧,只需要判断 fragCount 是否等于 fragSeq 即可。

算法大致流程图:

具体解释一下:

  • frameQueue, 用来缓存每一帧的数据,用来跟后面一帧数据进行对比,判断是否为完整帧

  • bufferQueue, 此队列中的数据,都是完整的切片数据,保证 MSE 进行消费时,数据没有缺失

  /**
   * fmp4 切片队列 frameQueue,处理丢帧,生产 bufferQueue 内容
   *
   * @param frameObj 每一帧的相关数据
   *      每来一帧进行判断
   *      buffer中加上当前帧是否为连续帧(从第一帧开始的连续帧)
   *        是
   *          当前帧是否为最后一帧
   *            是 拼接buffer帧以及当前帧,组成完整帧,放入另外一个待消费 buffer
   *            否 当前帧入 buffer
   *        否 清空 buffer,当前帧入 buffer
   */
​
const frameQueueLen = this.frameQueue.length;
const frameQueueEmpty = frameQueueLen === 0;
​
  // 单一完整分片帧单独处理,直接进行消费
  if (frameObj.fragCount === 1) {
    if (!frameQueueEmpty) {
      this.frameQueue = [];
    }
    this.bufferQueue.push(frameObj.value);
    return;
  }
​
  if (frameQueueEmpty) {
    this.frameQueue.push(frameObj);
    return;
  }
​
  // 是否为首帧
  let isFirstFragSeq = this.frameQueue[0].fragSeq === 1;
  // 当前帧加上queue帧是否为连续帧
  let isContinuousFragSeq = true;
  for (let i = 0; i < frameQueueLen; i++) {
    const isLast = i === frameQueueLen - 1;
​
    const curFragSeq = this.frameQueue[i].fragSeq;
    const nextFragSeq = isLast
      ? frameObj.fragSeq
      : this.frameQueue[i + 1].fragSeq;
​
    const curSegNum = this.frameQueue[i].segNum;
    const nextSeqNum = isLast
      ? frameObj.segNum
      : this.frameQueue[i + 1].segNum;
​
    if (curFragSeq + 1 !== nextFragSeq || curSegNum !== nextSeqNum) {
      isContinuousFragSeq = false;
      break;
    }
  }
​
  if (isFirstFragSeq && isContinuousFragSeq) {
    // 是否为最后一帧
    const isLastFrame = frameObj.fragCount === frameObj.fragSeq;
    if (isLastFrame) {
      this.frameQueue.forEach((item) => {
        this.bufferQueue.push(item.value);
      });
      this.frameQueue = [];
      this.bufferQueue.push(frameObj.value);
    } else {
      this.frameQueue.push(frameObj);
    }
  } else {
    // 丢帧则清空 frameQueue,则代表直接丢弃整个 segment 切片
    this.emit(EVENTS_ERROR.frameDropError);
    this.frameQueue = [];
    this.frameQueue.push(frameObj);
  }

原本以为大功告成,结果意想不到的事情发生了。

当出现丢帧时,通过上面的算法,确实是把整个切片的数据丢弃掉了,但是 MSE 此时居然再次异常了,意思也是说数据序列不对,导致解析失败。

可是用 ffplay 在本地测试(丢掉一整个切片后,是可以继续播放的),陷入僵局,继续排查。

8、阶段三

话说最近 chatgpt 不是挺火,尝试着用了下,确实找到了原因。MSE 在消费 fmp4 数据时,需要根据内部序列号进行索引标识,因此即使是丢掉整个切片数据,还是会播放失败。怎么办?难道要回到不可靠传输?

经过一番权衡,最终决定,当出现丢帧时,前端通知云端,重新进行切片,并且此时前端重新初始化 MSE。

改造下来发现,效果还不错,我们把不可靠传输,datachannel 重传次数设置为 5。

出现丢帧的概率大大减小,就算出现丢帧,也只会有不到 2 秒的 loading,然后继续出画面,业务层可以接受。

最终,经过上面 3 个阶段的改造,就有了整个链路图。当然其实还有很多细节,没有讲到,比如利用 mp4box 获取 codec, 前端定时检查 datachannel 状态等,就不展开细说了。有兴趣的可以留言讨论

完整的链路图,简单画了下。

9、总结

目前 datachannel + MSE 的方案已经上线,测试下来,线上同时硬解 16 路没有性能问题。

后续会尝试用 webcodes 来进行 H.265 的解析,并处理音视频同步等问题。彻底解决掉延时的问题。

下一篇准备写日常排查 WebRTC 问题的一些思路,也欢迎评论区聊一下日常遇到的一些问题,下篇一起汇总。

原文链接:《WebRTC系列》实战 Web 端支持 h265 硬解 - 掘金

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

《WebRTC系列》实战 Web 端支持 h265 硬解 的相关文章

  • 每天10个前端小知识 <Day 14>

    前端面试基础知识题 1 CSSOM树和DOM树是同时解析的吗 浏览器会下载HTML解析页面生成DOM树 遇到CSS标签就开始解析CSS 这个过程不会阻塞 但是如果遇到了JS脚本 此时假如CSSOM还没有构建完 需要等待CSSOM构建完 再去
  • 每日变更的最佳实践

    在优维公司内部 我们采用发布单的方式进行每天的应用变更管理 这里给各位介绍优维的最佳实践 变更是需要多角色合作的 而且他是整体研发流程的一部分 在优维内部 我们坚持每日变更 打通开发环节到最终发布上线的全过程 在保证质量的前提下 尽可能提升
  • 【js学习之路】遍历数组api之 `filter `和 `map`的区别

    一 前言 数组是我们在项目中经常使用的数据类型 今天我们主要简述作用于遍历数组的api filter 和 map 的区别 二 filter和map的共同点 首先 我们主要阐述一下 filter 和 map 的共同点 api的参数都是回调函数
  • 获取年与年之间的所有年份

    function getYearsBetween startYear endYear var years 存放结果的数组 for var year startYear year lt endYear year years push year
  • 如何在 Python 脚本中使用 Google OAuth2

    在使用 Python 脚本将视频上传到 YouTube 频道时 若希望将视频上传到第二个频道 需要解决 OAuth2 授权的问题 解决方案 创建新的 Google Cloud 项目 from google oauth2 import ser
  • 如何使用webrtc录制视频

    我需要在使用 Nodejs 构建的网站上使用笔记本电脑摄像头录制视频 为此 我使用 webRTC 到目前为止 我可以使用笔记本电脑摄像头拍照 但我需要录制视频 有人可以帮忙解释一下代码将如何进行吗 我当前的代码如下
  • 将 PCM 波形数据转换为 numpy 数组,反之亦然

    情况 我正在使用 WebRTC 中的 VAD 语音活动检测 WebRTC VAD https github com wiseman py webrtcvad Python 适配器 这示例实现 https github com wiseman
  • WebRTC、捕获屏幕

    我当前的问题是 我想找到一种在 Android 上的 webrtc 连接期间捕获帧 屏幕截图的方法 我知道这里已经有一些解决方案 但没有一个对我有用 按照我目前的方法 我遵循了这个Gist https gist github com Eri
  • iceConnectionState 已断开连接(通过互联网使用时)

    这个问题以前曾被问过 但我还没有找到答案 我基本上遇到了与描述相同的问题here https stackoverflow com questions 34405816 peerjs webrtc iceconnectionstate fai
  • 如何与本机桌面 (win) 应用程序建立对等连接

    我需要与本机桌面 win 应用程序和网络浏览器建立对等连接 只是为了传输原始数据 从理论上讲 WebRTC 似乎是实现这一目标的唯一方法 如果您想使用 WebRTC 在浏览器和桌面之间交换数据 您可以使用此库将桌面部分编码为 C 语言 ht
  • WebRTC - 禁用所有音频处理

    我目前正在尝试通过 webrtc 获得尽可能干净的音频通道 通过 getUserMedia mediaconstraints 对象 我设置了以下选项 constraints audio mandatory echoCancellation
  • 为 webRTC 使用特定端口

    当使用 webRTC 创建点对点音频连接时 如果用户位于路由器后面 我们使用的 STUN 服务器将返回公共 IP 现在在 ICE 对象中 我可以看到 rport 始终介于 50000 及以上 有没有办法使用特定端口 以便用户不必打开所有这些
  • 在 iOS 上读取实时摄像头流的 getUserMedia() 替代方案?

    我们正在使用 QuaggaJS 构建一个移动条形码扫描仪 它可以直接从设备的摄像头流中读取条形码 在桌面 Android 上运行良好 但在 iOS 上根本不起作用 从最初的研究来看 我发现 iOS 根本不支持 WebRTC getUserM
  • iOS Webrtc - 捕获本地视频流时崩溃

    我正在尝试使用 Google 存储库中的 webrtc 库 我按照这些步骤创建了一个单独的项目 其中包含类似于 APPRTC 的说明和代码 并且我能够让它工作 我能够在两台设备之间进行会议 但是当我尝试与旧项目集成时 Webrtc 崩溃了
  • 在 Heroku 上部署 PeerJS 服务器

    我的 PeerJS 服务器有问题 我从这里使用了 部署到 Heroku 按钮 https github com peers peerjs server https github com peers peerjs server 我不知道如何连
  • 部署站点后,我无权访问 navigator.mediaDevices。我该如何解决?

    我目前正在尝试使用 WebRTC api 并让一切在本地运行 当我部署到surge sh我无法访问navigator mediaDevices目的 我该如何解决这个问题 以下代码行是我遇到问题的地方 const stream await n
  • 如何使用 MediaRecorder 作为 MediaSource

    作为学习 WebRTC 的练习 我试图并排显示本地网络摄像头和网络摄像头的延迟播放 为了实现这一目标 我尝试将记录的 blob 传递到 BufferSource 并使用相应的 MediaSource 作为视频元素的源 the ondataa
  • 未捕获的 ReferenceError:进程未定义简单对等 Javascript

    我目前正在学习 WebRTC 并使用 simple peer 和 React 和 Socket io 的 npm 包 当我尝试致电用户时 就会显示此错误 stream readable js 529 Uncaught ReferenceEr
  • SignalR WebRTC WebSocket 已处于 CLOSING 或 CLOSED 状态

    我想测试运行 SignalR 和 WebRTC 的最简单实现 我将代码托管在https github com foyzulkarim WebRtc https github com foyzulkarim WebRtc 我制作了两个不同的文
  • 是否可以从具有跨源数据的元素中捕获?

    我在 webRTC 文档中找到了这个简单的脚本 我尝试运行它 但似乎我遗漏了一些东西 const leftVideo document getElementById leftVideo const rightVideo document g

随机推荐

  • WEB自动化测试面试题及答案大全

    1 Selenium是否支持桌面应用软件的自动化测试 Selenium不支持桌面软件的自动化测试 Selenium是根据网页元素的属性才定位元素 而其他桌面软件自动化测试工具是根据桌面元素的位置来定位元素 当然现在也有根据桌面元素的属性来定
  • type_traits

    概述 type traits提供了丰富的编译期间计算 查询 判断 转换和选择的帮助类 其被定义在 include
  • React-50:setState

    对象形式 也是最经常用到的 import React Component from react export default class Demo extends Component state count 0 add gt 对象式的set
  • 计算机网络学习笔记四、http和https

    http和https 从本篇文章开始总结http协议相关的知识点 http协议相关的内容可以分为四个部分 HTTP报文 HTTP请求 HTTP发展历史 HTTPS 1 HTTP报文 HTTP全称Hyper Text Transfer Pro
  • 数据库连接出错?

    数据库连接为什么会出错呢 NO suitable driver found for jdbc 问题源有3 1 连接的url的格式有错 Connection conn DriverManager getConnection jdbc sqls
  • 30个 JS 实用技巧总结,助你提升工作效率

    英文 https javascript plainenglish io 35 javascript short hands coding methodologies 28ea2d7d0a5e 翻译 杨小二 我是Rakshit Shah 我在
  • 【Java】JDK 1.8新特性

    Lambda 表达式 在没有 Lambda 表达式的时候 在 Java 中只能使用匿名内部类代替 Lambda 表达式 以下面的代码为例 查看 Lambda 表达式的使用 匿名内部类方式排序 List
  • c语言输入输出函数printf与scanf的用法格式

    c语言输入输出函数printf与scanf的用法格式 格式化规则例如 5 4f等类似问题的说明 Turbo C2 0 标准库提供了两个控制台格式化输入 输出函数printf 和scanf 这两个函数可以在标准输入输出设备上以各种不同的格式读
  • SAP LSMW日志信息如何导出到Excel里?

    SAP LSMW日志信息如何导出到Excel里 在SAP系统中 数据迁移LSMW运行的日志 是可以下载到本地Excel文件里的 方式如下所示 双击某个会话 点击打印机图标 就可以导出到Excel文件里了 输入文件名 指定文件保存的目录 完
  • 送书

    今天是周三 又到了给大家送书的时刻啦 这次给大家带来的是 OpenCV图像处理入门与实践 文末查看送书规则 简介 OpenCV 是一个开源的计算机视觉库 可以实现计算机视觉算法 本书从 OpenCV 用 Python 实现的基础语法讲起 逐
  • C51定时器和计数器 timer and counter

    代码 include
  • Seven Different Linux/BSD Firewalls Reviewed

    Seven Different Linux BSD Firewalls Reviewed Firewall November 14th 2007 Did you know more than 500 million computers in
  • 鲸鱼优化算法论文【matlab代码与仿真】

    一 算法流程 通过回声定位并相互传递探寻猎物信息 研究表明 鲸鱼大脑的某些区域与人类相似 有一种叫做梭形细胞的共同细胞 这些细胞负责人类的判断 情感和社会行为 这种细胞的数量是成年人的两倍 事实证明 鲸鱼可以像人类一样思考 学习 判断 交流
  • 从Kubernetes 1.14 发布,看技术社区演进方向

    Kubernetes 1 14 正式发布已经过去了一段时间 相信你已经从不同渠道看过了各种版本的解读 不过 相比于代码 Release 马上就要迎来5周岁生日的Kubernetes 项目接下来如何演进 其实也是一个让人着迷的话题 而作为一个
  • 在linux中,&和&&,

    对应刚接触linux命令的小伙伴们来说 这些符号一定是很困扰的下面我们一起来看这些符号区别和用法 表示任务在后台执行 如要在后台运行 如 root localhost local java jar test jar gt log txt 运
  • 大型 SaaS 平台产品架构设计

    当我们去搜索 架构 可以得到很多的架构图片 比如组织架构 业务架构 数据架构 技术架构 安全架构 产品架构 部署架构等 什么是架构 通常大家说架构一般指软件架构 架构是指软件的基础结构 创造这些基础结构的准则 以及对这些结构的描述 在这个定
  • IAR常见报错

    右键进入函数出错 project gt clean
  • 利用python进行数据分析之数据聚合和分组运算--小白笔记

    GroupBy机制 split apply combine 拆分 应用 合并 import pandas as pd import numpy as np df pd DataFrame key1 a a b b a key2 one tw
  • 找不到匹配的host key算法

    vim etc ssh sshd config
  • 《WebRTC系列》实战 Web 端支持 h265 硬解

    1 背景 Web 端实时预览 H 265 需求一直存在 但由于之前 Chrome 本身不支持 H 265 硬解 软解性能消耗大 仅能支持一路播放 该需求被搁置 去年 9 月份 Chrome 发布 M106 版本 默认开启 H 265 硬解