WebRTC学习笔记二 基础概念

news2024/12/23 22:33:22

一、WebRTC与架构

    简单来说,WebRTC 是一个可以在 Web 应用程序中实现音频,视频和数据的实时通信的开源项目。在实时通信中,音视频的采集和处理是一个很复杂的过程。比如音视频流的编解码、降噪和回声消除等,但是在 WebRTC 中,这一切都交由浏览器的底层封装来完成。我们可以直接拿到优化后的媒体流,然后将其输出到本地屏幕和扬声器,或者转发给其对等端。

  • Web API层:面向开发者提供标准API(javascirpt),前端应用通过这一层接入使用WebRTC能力。

  • C++ API层:面向浏览器开发者,使浏览器制造商能够轻松地实现Web API方案。

  • 音频引擎(VoiceEngine):音频引擎是一系列音频多媒体处理的框架,包括从视频采集卡到网络传输端等整个解决方案。

    • iSAC/iLBC/Opus等编解码

    • NetEQ语音信号处理

    • 回声消除和降噪

  • 视频引擎(VideoEngine): 是一系列视频处理的整体框架,从摄像头采集视频、视频信息网络传输到视频显示整个完整过程的解决方案。

    • VP8编解码

    • jitter buffer:动态抖动缓冲

    • Image enhancements:图像增益

  • 传输(Transport):传输 / 会话层,会话协商 + NAT穿透组件

    • RTP 实时协议

    • P2P传输 STUN+TRUN+ICE实现的网络穿越

  • 硬件模块:音视频的硬件捕获以及NetWork IO相关

    虽然浏览器给我们解决了大部分音视频处理问题,但是从浏览器请求音频和视频时,我们还是需要特别注意流的大小和质量。因为即便硬件能够捕获高清质量流,CPU 和带宽也不一定可以跟上,这也是我们在建立多个对等连接时,不得不考虑的问题。

二、WebRTC的重要的类和API

1.MediaStream(媒体流)和 MediaStreamTrack(媒体轨道)

    这个类并不完全属于WebRTC的范畴,但是在本地媒体流获取,及远端流传到vedio标签播放都与WebRTC相关。 MS 由两部分构成: MediaStreamTrack 和 MediaStream。

  • MediaStreamTrack 媒体轨,代表一种单类型数据流,可以是音频轨或者视频轨。

  • MediaStream 是一个完整的音视频流。它可以包含 >=0 个 MediaStreamTrack。它主要的作用就是确保几个媒体轨道是同步播放。

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

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

 

2.Constraints 媒体约束

    关于MediaStream,还有一个重要的概念叫做: Constraints(约束)。它是用来规范当前采集的数据是否符合需要,并可以通过参数来设置。

// 基本
const constraint1 = {
    "audio": true,  // 是否捕获音频
    "video": true   // 是否捕获视频
}
​
// 详细
const constraint2 = {
    "audio": {
      "sampleSize": 8,
      "echoCancellation": true //回声消除
    },
    "video": {  // 视频相关设置
        "width": {
            "min": "381", // 当前视频的最小宽度
            "max": "640" 
        },
        "height": {
            "min": "200", // 最小高度
            "max": "480"
        },
        "frameRate": {
            "min": "28", // 最小帧率
             "max": "10"
        }
    }
}
​
    // 获取指定宽高,这里需要注意:在改变视频流的宽高时,
    // 如果宽高比和采集到的不一样,会直接截掉某部分
    { audio: false, 
      video: { width: 1280, height: 720 } 
    }
    // 设定理想值、最大值、最小值
    {
      audio: true,
      video: {
        width: { min: 1024, ideal: 1280, max: 1920 },
        height: { min: 776, ideal: 720, max: 1080 }
      }
    }

    对于移动设备来说,还可以指定获取前摄像头,或者后置摄像头:

{ audio: true, video: { facingMode: "user" } } // 前置
    { audio: true, video: { facingMode: { exact: "environment" } } } // 后置
    // 也可以指定设备 id,
    // 通过 navigator.mediaDevices.enumerateDevices() 可以获取到支持的设备
    { video: { deviceId: myCameraDeviceId } }

    还有一个比较有意思的就是设置视频源为屏幕,但是目前只有火狐支持了这个属性。

{ audio: true, video: {mediaSource: 'screen'} }

3.获取设备本地音视频

    其中本地媒体流获取用到的是navigator.getUserMedia(),它提供了访问用户本地相机/麦克风媒体流的手段。

var video = document.querySelector('video');
navigator.getUserMedia({
    audio : true,
    video : true
    }, function (stream) {
            //拿到本地媒体流
            video.src = window.URL.creatObjectURL(stream);
    }, function (error) {
            console.log(error);
});

    getUserMedia的第一个参数就是Constraint,第二个参数传入回调函数拿到视频流。当然你可以使用如下Promise的写法:

navigator.mediaDevices.getUserMedia(constraints).
then(successCallback).catch(errorCallback);

4.RTCPeerConnection

    RTCPeerConnection,用于实现peer跟peer之间的NAT穿透,继而无需服务器就能传输音视频数据流的连接通道。

    这么说过于抽象,为了帮助理解,可以用一个不太恰当但有助于理解的比喻:RTCPeerConnection就是一个高级且功能强大的用于传输音视频数据而建立类似Websocket链接通道,只不过它可以用来建立浏览器

    之所以说是高级且强大,是因为它作为WebRTC web层核心API,让你无须关注数据传输延迟抖动、音视频编解码,音画同步等问题。直接使用PeerConnection 就能用上这些浏览器提供的底层封装好的能力。

5.Peer-to-peer Data API

    RTCDataChannel可以建立浏览器之间的点对点通讯。常用的通讯方式有websocket, ajax和等方式。websocket虽然是双向通讯,但是无论是websocket还是ajax都是客户端和服务器之间的通讯,你必须配置服务器才可以进行通讯。

    而由于RTCDATAChannel借助RTCPeerConnection无需经过服务器,就可以提供点对点之间的通讯,无需/(避免)服务器了这个中间件。

var pc = new RTCPeerConnection();
var dc = pc.createDataChannel("my channel");
​
dc.onmessage = function (event) {
  console.log("received: " + event.data);
};
​
dc.onopen = function () {
  console.log("datachannel open");
};
​
dc.onclose = function () {
  console.log("datachannel close");
};

6.信令Signaling

    我们说WebRTC的RTCPeerConnection是可以做到浏览器间(无服务)的通信。

    但这里有个问题,当两个浏览器不通过服务器建立PeerConnection时,它们怎么知道彼此的存在呢?进一步讲,它们该怎么知道对方的网络连接位置(IP/端口等)呢?支持何种编解码器?甚至于什么时候开始媒体流传输、又该什么时候结束呢?

    因此在建立WebRTC的RTCPeerConnection前,必须建立️另一条通道来交这些协商信息,这些也被称为信令,这条通道成为信令通道(Signaling Channel)。

两个客户端浏览器交换的信令具有以下功能:

  • 协商媒体功能和设置

  • 标识和验证会话参与者的身份(交换SDP对象中的信息:媒体类型、编解码器、带宽等元数据)

  • 控制媒体会话、指示进度、更改会话、终止会话等

  • 其中主要涉及SDP(offer、answer)会话描述协议,以及ICE candidate的交换。

    这里需要注意的一点:WebRTC标准本身没有规定信令交换的通讯方式,信令服务根据自身的情况实现。一般会使用websocket通道来做信令通道,比如可以基于http://socket.io来搭建信令服务。当然业界也有很多开源且稳定成熟的信令服务方案可供选择。

    显而易见,在上述连接的过程中:呼叫端(在这里都是指代浏览器)需要给接收端发送一条名为offer的信息。 接收端在接收到请求后,则返回一条 answer 信息给呼叫端。

    这便是上述任务之一 ,SDP 格式的本地媒体元数据的交换。sdp 信息一般长这样:

    v=0
    o=- 1837933589686018726 2 IN IP4 127.0.0.1
    s=-
    t=0 0
    a=group:BUNDLE audio video
    a=msid-semantic: WMS yvKeJMUSZzvJlAJHn4unfj6q9DMqmb6CrCOT
    m=audio 9 UDP/TLS/RTP/SAVPF 111 103 104 9 0 8 106 105 13 110 112 113 126
    ...
    ...

    但是任务不仅仅是交换,还需要分别保存自己和对方的信息,所以我们再加点料:

  • 呼叫端 创建 offer 信息后,先调用 setLocalDescription 存储本地 offer 描述,再将其发送给 接收端

  • 接收端 收到 offer 后,先调用 setRemoteDescription 存储远端 offer 描述;然后又创建 answer 信息,同样需要调用 setLocalDescription 存储本地 answer 描述,再返回给 接收端

  • 呼叫端 拿到 answer 后,再次调用 setRemoteDescription 设置远端 answer 描述。

    到这里点对点连接还缺一步,也就是网络信息 ICE 候选交换。不过这一步和 offer、answer 信息的交换并没有先后顺序,流程也是一样的。即:在呼叫端和接收端的 ICE 候选信息准备完成后,进行交换,并互相保存对方的信息,这样就完成了一次连接。

    这张图是我认为比较完善的了,详细的描述了整个连接的过程。正好我们再来小结一下:

  • 基础设施:必要的信令服务和 NAT 穿越服务

  • clientA 和 clientB 分别创建 RTCPeerConnection 并为输出端添加本地媒体流。如果是视频通话类型,则意味着,两端都需要添加媒体流进行输出。

  • 本地 ICE 候选信息采集完成后,通过信令服务进行交换。

  • 呼叫端(好比 A 给 B 打视频电话,A 为呼叫端)发起 offer 信息,接收端接收并返回一个 answer 信息,呼叫端保存,完成连接。

三、WebRTC建立连接的关键-ICE连接

    在交换SDP后,webrtc就开始真正的连接来传输音视频数据。这个建立连接的过程相当复杂,原因是webrtc既要保证高效的传输性,又要保证稳定的连通性。

    由于浏览器客户端之间所处的位置往往是相当复杂的,可能处于同一个内网段内,也可能处于两个不同的位置,所处的NAT网关也可能很复杂。因此需要一种机制找到一条传输质量最优的道路,而WebRTC正具备这种能力。

    首先简单了解以下三个概念。

  • ICE Canidate(ICE 候选者):包含远端通信时使用的协议、IP 地址和端口、候选者类型等信息。

  • STUN/TURN:STUN实现P2P型连接,TRUN实现中继型连接。两者实现均有标准协议。(参考下图)

  • NAT穿越:NAT即网络地址转换,由于客户端并不能分配到公网IP,需要内网IP与公网IP端口做映射才能与外网通信。而NAT穿越就是位于层层Nat网关背后的客户端之间发现对方并建立连接。

ICE连接大致的原理及步骤如下:

  • 发起收集ICE Canidate任务。

  • 本机能收集host类型(内网IP端口)的candidate。

  • 通过STUN服务器收集srflx类型(NAT映射到外网的IP端口)的candiate。

  • 通过TURN服务器收集relay类型的(中继服务器的 IP 和端口)的candidate。

  • 开始尝试NAT穿越,按照host类型、srflx类型、relay类型的优先级去连接。

     以上,WebRTC便能找到一条传输质量最优的连接道路。 当然实际情况并不是这么简单,整个过程包含着更复杂的底层细节。

以下参考 ICE协议下NAT穿越的实现(STUN&TURN)

1.首先来简单讲讲什么是NAT?

     原来这是因为IPV4引起的,我们上网很可能会处在一个NAT设备(无线路由器之类)之后。 NAT设备会在IP封包通过设备时修改源/目的IP地址. 对于家用路由器来说, 使用的是网络地址端口转换(NAPT), 它不仅改IP, 还修改TCP和UDP协议的端口号, 这样就能让内网中的设备共用同一个外网IP. 举个例子, NAPT维护一个类似下表的NAT表:

     NAT设备会根据NAT表对出去和进来的数据做修改, 比如将192.168.0.3:8888发出去的封包改成120.132.92.21:9202, 外部就认为他们是在和120.132.92.21:9202通信. 同时NAT设备会将120.132.92.21:9202收到的封包的IP和端口改成192.168.0.3:8888, 再发给内网的主机, 这样内部和外部就能双向通信了, 但如果其中192.168.0.3:8888 == 120.132.92.21:9202这一映射因为某些原因被NAT设备淘汰了, 那么外部设备就无法直接与192.168.0.3:8888通信了。 我们的设备经常是处在NAT设备的后面, 比如在大学里的校园网, 查一下自己分配到的IP, 其实是内网IP, 表明我们在NAT设备后面, 如果我们在寝室再接个路由器, 那么我们发出的数据包会多经过一次NAT.

2.NAT的副作用以及解决方案

     国内移动无线网络运营商在链路上一段时间内没有数据通讯后, 会淘汰NAT表中的对应项, 造成链路中断。这是NAT带来的第一个副作用:NAT超时。而国内的运营商一般NAT超时的时间为5分钟,所以通常我们TCP长连接的心跳设置的时间间隔为3-5分钟。

    而第二个副作用就是:我们这边文章要提到的NAT墙。NAT会有一个机制,所有外界对内网的请求,到达NAT的时候,都会被NAT所丢弃,这样如果我们处于一个NAT设备后面,我们将无法得到任何外界的数据。

    但是这种机制有一个解决方案:就是如果我们A主动往B发送一条信息,这样A就在自己的NAT上打了一个B的洞。这样A的这条消息到达B的NAT的时候,虽然被丢掉了,但是如果B这个时候在给A发信息,到达A的NAT的时候,就可以从A之前打的那个洞中,发送给到A手上了。

    简单来讲,就是如果A和B要进行通信,那么得事先A发一条信息给B,B发一条信息给A。这样提前在各自的NAT上打了对方的洞,这样下一次A和B之间就可以进行通信了。

3.四种NAT类型:

RFC3489 中将 NAT 的实现分为四大类:

  • Full Cone NAT (完全锥形 NAT)

  • Restricted Cone NAT (限制锥形 NAT ,可以理解为 IP 限制,Port不限制)

  • Port Restricted Cone NAT (端口限制锥形 NAT,IP+Port 限制)

  • Symmetric NAT (对称 NAT)

其中完全最上层的完全锥形NAT的穿透性最好,而最下层的对称形NAT的安全性最高。

简单来讲讲这4种类型的NAT代表什么:

  • 如果一个NAT是Full Cone NAT,那么无论什么IP地址访问,都不会被NAT墙掉(这种基本很少)。

  • Restricted Cone NAT,仅仅是经过打洞的IP能穿越NAT,但是不限于Port。

  • Port Restricted Cone NAT,仅仅是经过打洞的IP+端口号能穿越NAT。

  • Symmetric NAT 这种也是仅仅是经过打洞的IP+端口号能穿越NAT,但是它有一个最大的和Cone类型的NAT的区别,它对外的公网Port是不停的变化的:比如A是一个对称NAT,那么A给B发信息,经过NAT映射到一个Port:10000,A给C发信息,经过NAT映射到一个Port:10001,这样会导致一个问题,我们服务器根本无法协调进行NAT打洞。

至于为什么无法协调打洞,下面我们会从STUN和TURN的工作原理来讲。

4.STUN Server主要做了两件事

  • 接受客户端的请求,并且把客户端的公网IP、Port封装到ICE Candidate中。

  • 通过一个复杂的机制,得到客户端的NAT类型。

    完成了这些STUN Server就会这些基本信息发送回客户端,然后根据NAT类型,来判断是否需要TURN服务器协调进行下一步工作。

    我们来讲讲这两步具体做了什么吧: 第一件事就不用说了,其实就是得到客户端的请求,把源IP和Port拿到,添加到ICE Candidate中。

    来讲讲第二件事,STUN是如何判断NAT的类型的: 假设B是客户端,C是STUN服务器,C有两个IP分别为IP1和IP2(至于为什么要两个IP,接着往下看):

    STEP1.判断客户端是否在NAT后: B向C的IP1的pot1端口发送一个UDP 包。C收到这个包后,会把它收到包的源IP和port写到UDP包中,然后把此包通过IP1和port1发还给B。这个IP和port也就是NAT的外网 IP和port(如果你不理解,那么请你去看我的BLOG里面的NAT的原理和分类),也就是说你在STEP1中就得到了NAT的外网IP。

    熟悉NAT工作原理的朋友可以知道,C返回给B的这个UDP包B一定收到。如果在你的应用中,向一个STUN服务器发送数据包后,你没有收到STUN的任何回应包,那只有两种可能:1、STUN服务器不存在,或者你弄错了port。2、你的NAT拒绝一切UDP包从外部向内部通过。

    当B收到此UDP后,把此UDP中的IP和自己的IP做比较,如果是一样的,就说明自己是在公网,下步NAT将去探测防火墙类型,我不想多说。如果不一样,说明有NAT的存在,系统进行STEP2的操作。

    STEP2.判断是否处于Full Cone Nat下: B向C的IP1发送一个UDP包,请求C通过另外一个IP2和PORT(不同与SETP1的IP1)向B返回一个UDP数据包(现在知道为什么C要有两个IP了吧,虽然还不理解为什么,呵呵)。

    我们来分析一下,如果B收到了这个数据包,那说明什么?说明NAT来着不拒,不对数据包进行任何过滤,这也就是STUN标准中的full cone NAT。遗憾的是,Full Cone Nat太少了,这也意味着你能收到这个数据包的可能性不大。如果没收到,那么系统进行STEP3的操作。

    STEP3.判断是否处于对称NAT下: B向C的IP2的port2发送一个数据包,C收到数据包后,把它收到包的源IP和port写到UDP包中,然后通过自己的IP2和port2把此包发还给B。

    和step1一样,B肯定能收到这个回应UDP包。此包中的port是我们最关心的数据,下面我们来分析: 如果这个port和step1中的port一样,那么可以肯定这个NAT是个CONE NAT,否则是对称NAT。道理很简单:根据对称NAT的规则,当目的地址的IP和port有任何一个改变,那么NAT都会重新分配一个port使用,而在step3中,和step1对应,我们改变了IP和port。因此,如果是对称NAT,那这两个port肯定是不同的。

    如果在你的应用中,到此步的时候PORT是不同的,那么这个它就是处在一个对称NAT下了。如果相同,那么只剩下了restrict cone 和port restrict cone。系统用step4探测是是那一种。

    STEP4.判断是处于Restrict Cone NAT还是Port Restrict NAT之下: B向C的IP2的一个端口PD发送一个数据请求包,要求C用IP2和不同于PD的port返回一个数据包给B。

    我们来分析结果:如果B收到了,那也就意味着只要IP相同,即使port不同,NAT也允许UDP包通过。显然这是Restrict Cone NAT。如果没收到,没别的好说,Port Restrict NAT.

    到这里STUN Server一共通过这4步,判断出客户端处于什么类型的NAT下,然后去做后续的处理: 这4步都会返回给客户端它的公网IP、Port和NAT类型,除此之外:

    (a)如果A处于公网或者Full Cone Nat下,STUN不做其他的了,因为其他客户端可以直接和A进行通信。

(b)如果A处于Restrict Cone或者Port Restrict NAT下,STUN还会协调TURN进行NAT打洞。

(c)如果A处于对称NAT下,那么点对点连接下,NAT是无法进行打洞的。所以为了通信,只能采取最后的手段了,就是转成C/S架构了,STUN会协调TURN进行消息转发。

5.TURN Server也主要做了两件事:

  • 为NAT打洞:如果A和B要互相通信,那么TURN Server,会命令A和B互相发一条信息,这样各自的NAT就留下了对方的洞,下次他们就可以之间进行通信了。

  • 为对称NAT提供消息转发:当A或者B其中一方是对称NAT时,那么给这一方发信息,就只能通过TURN Server来转发了。

6.为什么对称NAT无法打洞:

    假如A、B进行通信,而B处于对称NAT之下,那么A与B通信,STUN拿到A,B的公网地址和端口号都为10000,然后去协调TURN打洞,那么TURN去命令A发信息给B,则A就在NAT打了个B的洞,但是这个B的洞是端口号为10000的洞,但是下次B如果给A发信息,因为B是对称NAT,它给每个新的IP发送信息时,都重新对应一个公网端口,所以给A发送请求可能是公网10001端口,但是A只有B的10000端口被打洞过,所以B的请求就被丢弃了。 显然Server是无法协调客户端打洞的,因为协调客户端打得洞仅仅是上次对端为Server发送端口的洞,并不适用于另一个请求。

    最后的最后再补充一点,就是NAT打的洞也是具有时效性的,如果NAT超时了,那么还是需要重新打洞的。

五、垫片-兼容性

    一开始各个浏览器厂商,都会实现自己的一套API,诸如webkitRTCPeerConnectionmozRTCPeerConnection 这样的差异,对于前端开发者当然是苦不堪言。而adapter.js正是为了消除这种差异,帮助我们可以按照规范来写我们的WebRTC代码。

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

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

相关文章

C. Strange Test(位运算或)

Problem - 1632C - Codeforces 伊戈尔正在读11年级。明天他将不得不写一份信息学测试&#xff0c;由学校最严格的老师帕维尔-杰尼索维奇负责。 伊戈尔知道测试将如何进行&#xff1a;首先&#xff0c;老师会给每个学生两个正整数a和b&#xff08;a<b&#xff09;。之后&…

[2022-11-28]神经网络与深度学习 hw10 - LSTM和GRU

contentshw10 - LSTM 和GRU相关习题task 1题目内容题目分析题目解答题目总结task 2题目内容题目分析题目解答题目总结task 3题目内容题目分析题目解答题目总结task 4题目内容题目分析题目解答问题总结hw10 - LSTM 和GRU相关习题 task 1 题目内容 当使用公式htht−1g(xt,ht−…

Linux系统中利用C语言控制LED的方法

大家好&#xff0c; 今天主要和大家聊一聊&#xff0c;如何利用C语言控制LED灯的实验。 目录 ​第一&#xff1a;C语言板控制LED灯简介 第二&#xff1a;实验程序实现 ​第三&#xff1a;C语言实验控制程序 ​第一&#xff1a;C语言板控制LED灯简介 实际工作中很少会使用到汇…

分离变数法

今天是2022年11月28号 我的方程学的不太好&#xff0c;一些讲宇宙的&#xff0c;讲技术的&#xff0c;方程实在是看不懂很多方程的解与参数不可分割期末来了 有的人回去了有的人要看光学了我呢&#xff0c;已经废物了&#xff0c;节日快乐&#xff0c;大家伙节日快乐啊&#xf…

Spirng 痛苦源码学习(二)——手写spring大致总框架(一)

文章目录前言一、总体步骤如下1、spring 文件夹2、myProject 文件夹二、主要coding1、配置文件2、容器3、一些spring中的重要的注解4、项目中的使用5.重要的bean定义信息6、postProcessor重要&#xff0c;前置通知和后置主要项目的截图前言 本文主要基于spring的注解的方式完成…

Spring相关源码解读

框架1.ApplicationContext refresh的流程2.spring bean 的生命周期3.spring事务失效的几种场景以及原因4.springMVC执行流程5.一些注解&#xff08;1&#xff09;Configuration&#xff08;2&#xff09;Import&#xff08;3&#xff09;SpringBootApplication6.spring中有哪些…

BP神经网络详解,Python实现求解异或问题

BP神经网络 符号及其含义 nln_lnl​表示第lll层神经元的个数&#xff1b;f(⋅)f()f(⋅)表示神经元的激活函数&#xff1b;W(l)∈Rni∗ni−1W^{(l)}\in\mathbb R^{n_i*n_{i-1}}W(l)∈Rni​∗ni−1​表示第l−1l-1l−1层到第lll层的权重矩阵&#xff1b;wij(l)w_{ij}^{(l)}wij(l…

基于tensorflow的ResNet50V2网络识别动物

前言 之前很多人在&#xff0c;如何进行XXX的识别&#xff0c;对应的神经网络如何搭建。对应神经网络怎么搭建&#xff0c;我也是照本宣科&#xff0c;只能说看得懂而已&#xff0c;没有对这块进行深入的研究&#xff0c;但是现在tensorflow&#xff0c;paddle这些工具&#x…

长期稳定的项目—steam搬砖

大家好&#xff0c;我是阿阳 steam搬砖项目一直稳稳定定的进行着&#xff0c;有些朋友基本都观察了近2年 所以很多人问我公众号的项目是不能做了吗&#xff1f;怎么最近做新的去了&#xff1f;很明显这是几乎不可能的事情&#xff0c;steam做2年了&#xff0c;本公众号都能翻到…

这几个数据分析项目,让我看到了什么才叫专业!!

大家好&#xff0c;我是小一 新的一周又来了&#xff0c;从今天开始&#xff0c;会出一个新的系列《数分实验室》 实验室会介绍一些有内核、有科技的数据分析实战项目。 项目数据集、源代码都是公开的&#xff0c;非常适合想练手但是又没数据、没参考案例的同学 今天先热热…

ES的基础概念

1、ES是什么 Elasticsearch 是一个分布式可扩展的实时搜索和分析引擎,一个建立在全文搜索引擎 Apache Lucene(TM) 基础上的搜索引擎.当然 Elasticsearch 并不仅仅是 Lucene 那么简单&#xff0c;它不仅包括了全文搜索功能&#xff0c;还可以进行以下工作:分布式实时文件存储&am…

6-1分支限界法

6-1分支限界法 1.分支限界法与回溯法的不同 &#xff08;1&#xff09;求解目标: 回溯法的求解目标是找出解空间树中满足约束条件的所有解&#xff08;或一个最优解&#xff09;&#xff0c; 而分支限界法的求解目标则是找出满足约束条件的一个解&#xff08;或最优解&#x…

组织机器学习代码

组织机器学习代码 从note本转移到 Python 脚本时组织代码。 Intuition 有组织的代码就是有可读的、可重现的、健壮的代码。您的团队、经理&#xff0c;最重要的是&#xff0c;您未来的自己&#xff0c;将感谢您为组织工作付出的最初努力。在本课中&#xff0c;将讨论如何将代码…

pytest测试框架入门1

pytest单元测试框架 单元测试是指在软件开发当中&#xff0c;针对软件的最小单位&#xff08;函数&#xff0c;方法&#xff09;进行正确性的检查测试 单元测试框架主要做什么 测试发现&#xff1a;从多个文件里面找到我们的测试用例测试执行&#xff1a;按照一定的顺序和规则…

初学者指南: 使用NumPy数组进行图像处理

这里写自定义目录标题初学者指南: 使用NumPy数组进行图像处理1、加载图像2、裁剪图像3、分离颜色4、转换5、灰度转换6、图像分割结语初学者指南: 使用NumPy数组进行图像处理 由于图像也可以被视为由数组组成&#xff0c;因此我们也可以使用NumPy执行不同的图像处理任务。在本文…

【Lilishop商城】No2-6.确定软件架构搭建五(本篇包括定时任务xxl-job)

仅涉及后端&#xff0c;全部目录看顶部专栏&#xff0c;代码、文档、接口路径在&#xff1a; 【Lilishop商城】记录一下B2B2C商城系统学习笔记~_清晨敲代码的博客-CSDN博客 全篇只介绍重点架构逻辑&#xff0c;具体编写看源代码就行&#xff0c;读起来也不复杂~ 谨慎&#xf…

如何配置一台适合oc渲染器的电脑?

众所周知&#xff0c;Octane 是最流行的渲染引擎之一。此外&#xff0c;Octane 是一个 GPU 渲染引擎&#xff0c;它使用一种计算最终生成的图片的方法&#xff0c;试图达到照片般的真实感。Octane 是一种利用 GPU 技术的无偏渲染引擎&#xff0c;非常接近物理精度。一台好的 PC…

计算机组成原理习题课第三章-2(唐朔飞)

计算机组成原理习题课第三章-2&#xff08;唐朔飞&#xff09; ✨欢迎关注&#x1f5b1;点赞&#x1f380;收藏⭐留言✒ &#x1f52e;本文由京与旧铺原创&#xff0c;csdn首发&#xff01; &#x1f618;系列专栏&#xff1a;java学习 &#x1f4bb;首发时间&#xff1a;&…

天宇优配|平台助企“抱团出海” “小而美”中觅“先机”

天津华图轿车物流有限公司一批二手新能源车从连云港装船发往阿联酋迪拜。&#xff08;采访方针供图&#xff09; 最近&#xff0c;一笔100.8万美元的出口信誉稳妥保单融资借款&#xff0c;被划到了天津华图轿车物流有限公司的账户上。正值客户“下单”高峰期&#xff0c;这笔及…

Three.js实例详解___旋转的精灵女孩(附完整代码和资源)(一)

Three.js实例详解___旋转的精灵女孩(附完整代码和资源)&#xff08;一&#xff09; 本文目录&#xff1a; 一、【旋转的精灵女孩】案例运行效果 二、Three.js简介 三、Three.js代码正常运行显示条件 &#xff08;1&#xff09;不载入任何纹理贴图的网页 &#xff08;2&…