《WebKit 技术内幕》学习之十一(4):多媒体

 4 WebRTC

4.1 历史

        相信读者都有过使用Tencent QQ或者FaceTime进行视频通话的经历,这样的应用场景相当典型和流行,但是基本上来说它们都是每个公司推出的私有产品,而且通信等协议也都是保密的,这使得一种产品的用户基本上不可能同其他产品的用户进行视频通信。还有一些更大的应用场景,那就是众多用户一起召开视频会议,这比简单的点对点更为复杂,很多公司已投身其中,因为这一市场非常广大。

        几年前,笔者是很难想象这么复杂的需求和应用场景能够在Web领域中被实现,但是现在Chromium和Firefox浏览器都支持Web视频通信,而且神奇的是它们之间也可以相互通信,听起来非常不可思议——只是需要Web开发者使用JavaScript和HTML5技术就可以完成,还是免费的,而且还不需要额外安装应用软件,不需要额外安装插件。现在真是互联网和HTML5技术的好时代,这也是HTML5的多媒体领域的一个重大进展。

        WebRTC(Web Real Time Communication)技术,中文全称为Web实时通信技术,它是一种提供实时视频通信的规范,目前是W3C推荐的规范。它是一个开放的规范,任何人都可以免费使用,目前Chromium/Chrome和Firefox浏览器都支持了该规范。WebRTC是HTML5对多媒体支持的一个重大进展,因为该技术不仅使用了音视频的输入和输出,而且还涉及连接等网络连接,是一个非常复杂但非常有用的技术。图11-19是一个简单的示意图,说明两个支持WebRTC规范的浏览器之间是如何进行视频通信的。事实上,WebRTC既允许使用服务器来进行通信,也支持点对点(Peer-to-Peer)通信,当然需要服务器的辅助。

                图11-19 Chromium/Chrome和Firefox浏览器的视频通信示例图

        不过,很多事情并非是一蹴而就的,何况这么复杂的WebRTC技术,WebRTC发展至今也经历了很长的过程。首先得从一个音视频的捕获需求开始。最初,HTML5希望能够提供一种捕获用户音频和视频的技术,这就是getUserMedia,当然刚开始也不是它,而是使用下面的语句来完成音频和视频的捕获。

    <input type="file" accept="video/*;capture=camcorder">
    <input type="file" accept="audio/*;capture=microphone">

        但是它们太简单了,只能将捕获的信息保存为一个文件或者一个快照,这显然不能满足很多实际的需求,之后一个新的元素定义诞生,就是使用“device”元素,这一元素很快被抛弃。这就是getUserMedia的前身,但标准化组织很快从“device”元素转向getUserMedia,它的基本使用方式是:

    navigator.getUserMedia({video: true, audio: true}, function (videostream) { … });

        思想很简单,就是在navigator这个全局对象下加入一个新接口,该接口使用两个参数,第一表示它需要捕获视频或者音频或者两者都需要,第二个是一个回调函数,当捕获成功后,将捕获的视频流可以输出到一个视频元素,这就像是一个从服务器加载的视频文件,当然它是一个视频流,所以某些查找(Seek)操作不能工作。这里,不再使用新元素,而是利用原有的“video”元素,实在是一个好的设计。

        在这之后,一个更为大胆和激进的想法诞生了,就是将RTC技术引入到HTML5中来,这就是WebRTC技术,因为getUserMedia是入口,所以自然而然地被使用到WebRTC技术的规范中来。

11.4.2 原理和规范

        大家可以在脑海中想象一下如何要构建一个网络视频通信、需要哪些部分的参与及共同工作才能完成整个过程。总体上,这一过程中需要三种类型的技术,其一是视频,其二是音频,其三是网络传输。在这三种技术上,具体需要以下一些部分。

  • 音视频输入和输出设备 :同音视频播放不同,因为它们只是需要输出设备,这里需要输入和输出设备(麦克风和摄像头)。同时,输入使用getUserMedia技术,而输出,基本上可以采用音视频播放的基本框架,当然,需要一些额外的支持。
  • 网络连接的建立 :因为视频通信需要不停地传送大量数据,所以需要建立一种可靠的网络连接来让各个参与方传输数据。
  • 数据捕获、编码和发送 :当用户打开设备之后,需要捕获这些数据并对它们进行编码,因为原始数据的数据量太大,然后需要将编码后的数据通过连接传输出去。
  • 数据接收、解码和显示 :接收来自其他方的数据流并进行解码,然后显示出来,这个需求跟播放媒体文件的需求比较类似。

        根据上面的解释,不难理解图11-20所描述的过程,结合这些主要组成部分,构成了一个比较完整的音视频通信过程。

                                        图11-20 使用WebRTC技术的视频通信详细过程

        下面了解一下规范中如何针对上面的描述来定义相应的JavaScript接口的。根据目前W3C推荐的规范草案,主要包括以下几个部分。

  • Media Capture and Streams规范和WebRTC对它的扩展,这个主要是依赖摄像头和麦克风来捕获多媒体流,WebRTC对它进行扩展,使得多媒体流可以满足网络传输用途,也就是“video”元素可以来源于多媒体流而不仅仅是资源文件。
  • 点到点的连接,也就是规范中的RTCPeerConnection接口,它能够建立端到端的连接,两者直接通过某种方式传输控制信息,至于方式并没有进行规定。
  • RTCDataChannel接口,通过该接口,通信双方可以发送任何类型的消息,例如文本或者二进制数据,这个不是必须的。不过这一功能极大地方便了开发者,其主要思想来源于WebSocket。

11.4.3 实践——一个WebRTC例子

        在介绍内部原理之前,笔者希望通过剖析W3C规范中的一个例子来进一步加深对它的理解。示例代码11-5来源于W3C WebRTC规范中的示例代码,笔者稍微作了一些修改以简化理解,并加入注释作进一步说明。

        这里主要是点对点的直接通信,当然需要借助于网络提供的NAT服务,其中包括三个文件,第一个是双方共享的JavaScript代码,第二个是发起端的HTML代码,第三个是接收端的HTML代码。通常第二个和第三个可以是一样的,这里为了方便理解作了少许区别。因为代码中已经作了较为详细的讲解,所以后面不再赘述其中的原理。

示例代码11-5 使用WebRTC技术的P2P网络视频通信

    JavaScript文件:common.js
    // 创建消息通道,例如使用XMLHttpRequest或者WebSocket。根据WebRTC规范的说明
    // 本身WebRTC不提供双方进行控制信息传输的通道,由开发者自行选择合适的方法
    // 这里,简单使用一个函数表示创建了一个通道,该通道包含一个能够发送消息的“send”
    var signalChannel = createSignalChannel();
    
    // 将ICE的Candidate发送给对方,这个是ICE定义的,主要是ICE协议用来建立连接需
    // 要的信息。双方需要交互这个信息
    function sendCandidate(candidate) {
      if (candidate) signalChannel.send(JSON.stringify({ "candidate": candidate }));
    }
    function sendDescription() {
      signalingChannel.send(JSON.stringify({ "sdp": conn.localDescription }));
    }
    signalingChannel.onmessage = function (event) {
      // 如果没有建立连接,需要创建,在这里表明这是接受者端
      if (conn == null) start();
      // 从控制信息中获取信息内容
      var message = JSON.parse(event.data);
      // 如果信息类型是设置Description相关的,就调用setRemoteDescription
      if (message.sdp) {
        conn.setRemoteDescription(new RTCSessionDescription(message.sdp), function () {
          // 如果受到一个请求(offer),需要答复它,这里应该是接收方处理的
          if (conn.remoteDescription.type == "offer") {
            conn.createAnswer(function(desc) {
            conn.setLocalDescription(desc, sendDescription);
            });
        } else if (message.candidate) {
            conn.addIceCandidate(new RTCIceCandidate(message.candidate));
        }
    };
    // 用来存放RTCPeerConnection对象
    var conn = null;
    // 用来显示从自身设备捕获的多媒体流
    var selfView = document.getElementById("selfView");
    // 用来显示从对方传送过来的多媒体流
    var remoteView = document.getElementById("remoteView");
    
    // 开始创建连接等
    function start() {
      // 创建连接
      conn = new RTCPeerConnection({ "iceServers": [{ "url": "stun:stun. example.org" }] });
      // 保存从自身捕获的多媒体流
      var capturedStream = null;
      // 捕获音视频
      navigator.getUserMedia({ "audio": true, "video": true }, function (stream) {
        // 将捕获的多媒体流使用“video”元素播放出来
        selfView.src = URL.createObjectURL(stream);
        capturedStream = stream;
      }
      // 将多媒体流加入连接
      conn.addStream(capturedStream);
    
      // 接收到ICE Candidate事件,需要将candidate信息传送给对方
      conn.onicecandidate = function (event) {
        sendCandidate(event.candidate);
      };
      // 这个是由发起者调用,因为接收者不会发送该事件
      conn.onnegotiationneeded = function() {
        // 创建一个Offer,然后发送给接收者
        conn.createOffer(function (desc) {
          conn.setLocalDescription(desc, sendDescription);
        );
      });
      // 将远端多媒体流使用“video”元素显示出来
      conn.onaddstream = function (evt) {
          remoteView.src = URL.createObjectURL(evt.stream);
      };
    }
    
    发起者HTML文件节选:
    <video id="selfView" autoplay ></video>
    <video id="remoteView" autoplay></video>
    <script src='common.js'></script>
    <script>
      // 上面的两个"video"元素分别用来显示自己捕获的多媒体流和对方的多媒体流
      // 实际情况中,可能是某个用户作为发起者单击了“开始”按钮,启动音视频通信
      start();
    </script>
    
    接受者HTML文件节选:
    <video id="selfView" autoplay ></video>
    <video id="remoteView" autoplay></video>
    <script src='common.js' type='javascript'></script>

        相信通过上面的代码介绍,读者应该理解使用WebRTC构建一个P2P视频通信的基本过程,这其中网络连接的部分主要基于ICE协议(NAT)和SDP协议,以及一些支持NAT的辅助设施(STUN和URN),有兴趣的读者可以自行查阅相关技术文档。

4.4 WebKit和Chromium的实现

        下面来看一看WebKit和Chromium是如何支持WebRTC规范的。笔者首先需要澄清一下关于WebRTC的两种解释,本节中会有两种WebRTC用法:一种是指WebRTC这项技术和规范;另一种是WebRTC这个项目,它是支持WebRTC规范的一个开源项目。默认情况下是指前者,如果是指WebRTC这个开源项目,笔者会明确指出。

        下面来了解一下从webrtc.org上介绍的关于支持WebRTC技术的内部框架和功能模块,图11-21来源于“http://www.webrtc.org/reference/architecture”的架构图,但是缩减了其中一些部分。这里所示的是实现了WebRTC功能的开源项目架构图。

        图11-21中主要包括三大方面,即语音、视频和传输,它们三个构成了WebRTC的主要组成部分。其中iSAC(internet Speech Audio Codec)和iLBC(internet Low Bitrate Codec)是两种不同的音频编码格式,是为了适应互联网的语音传输要求而存在的,前者是针对带宽比较大的情况,后者针对带宽较小的情况,目前都是可以免费使用的。其中VP8同样是Google提供免费视频格式,前面介绍过了。传输部分主要是加入了对前面协议的支持模块。在会话管理中,主要使用一个开源项目libjingle来进行管理。下面灰色部分主要是WebRTC工作时依赖的下层功能的接口,在Chromium浏览器中,它会提供相应接口和功能给WebRTC使用。

                                        图11-21 WebRTC项目的架构图

         上面是WebRTC开源项目的架构图,在Chromium中,通常使用WebRTC项目来完成WebRTC规范的功能,并使用libjingle项目来建立点到点的连接。所以,Chromium主要的目的是将WebRTC和libjingle的能力桥接到浏览器中来,先看WebRTC规范中建立连接所需要的相关基础设施,图11-22是WebKit、Chromium及Chromium中使用libjingle的类的层次图。

                                图11-22 WebKit和Chromium建立连接的基础设施

        基础设施主要分成三个层次,首先是WebKit,也就是最上面的部分。该部分最上面的类是RTCPeerConnection,从名字就可以猜出,该类是对WebRTC连接的接口类,实际上它就是从规范中定义的RTCPeerConnection接口文件生成的基本框架,当然真正和JavaScript引擎打交道还需要一个桥接类。该桥接类包含一个实际实现的连接类句柄m_peerHandler,它是这个连接所包含的本地多媒体流和远端对方的多媒体流。读者可以想象一下视频会议的场景,首先Webkit需要将本地的多媒体流收集起来,通过连接传输给对方,本地可以选择是否通过“video”播放。同时需要接收从对方传输过来的多媒体流,这也是WebRTC的主要部分。当然还包括DataChannel相关对象,这里没有标出。

        至于接下来的部分就是WebKit的实现类,该类能够满足RTCPeerConnection的功能要求,但是它需要通过不同移植的实现才能完成,因为本身WebKit的WebCore并没有这样的能力。在WebKit的Chromium中同样定义了两个类WebRTCPeerConnectionHandler和WebRTCPeerConnectionHandlerClient,根据WebKit的类名定义方式,前者是需要Chromium来实现,而后者则是由Chromium调用,并由WebKit来实现的,这里主要是应用连接事件的监听函数,所以WebKit能够将它们传递给JavaScript引擎。

        之后是Chromium的实现类。RTCPeerConnectionHandler类继承自WebKit的Chromium移植的接口类,并做了具体的实现,这就是content::RTCPeerConnectionHandler,它同时集成自PeerConnectionHandleBase类,而该类拥有了支持建立连接所需的能力,当然它是依赖于libjingle项目提供的连接能力。

        libjingle提供了建立和管理连接的能力,支持透过NAT和防火墙设备、代理等建立连接。libjingle不仅支持点到点的连接,也支持多用户连接。同时还包含了连接所使用的MediaStream接口,这是因为Chromium本身不直接使用WebRTC项目提供的接口,而是调用libjingle来建立连接,并使用libjingle提供的MediaStream接口,而libjingle本身则会使用WebRTC项目的音视频处理引擎。

        接下来要介绍的是多媒体流,它需要一个非常复杂的框架,首先来看WebKit是如何支持getUserMedia这个接口的。图11-23描述了WebKit,以及WebKit的Chromium移植中所定义的接口,图中虚线上半部分是WebKit中的类,下半部分是Chromium中的类。

                                图11-23 WebKit支持多媒体流的基础设施

        最上层是WebKit支持多媒体流编程接口提供的具体实现类,如NavigatorMediaStream类,而直接同V8 JavaScript引擎桥接的类是V8NavigatorUser-MediaSuccessCallback,它是一个绑定类。因为getUserMedia接口主要是返回一个MediaStream对象,而MediaStream类可以提供众多访问数据流的接口,而连接的目的就是需要将MediaStream对应的多媒体流传输出去。图中UserMediaRequest类负责请求创建一个MediaStream对象。在WebKit的Chromium移植中,定义WebUserMediaClient为一个接口类,Chromium需要新建子类来实现这一功能,这就是Chromium中的MediaStreamImpl类,它在后面的介绍中还会出现。

        WebKit中使用MediaStreamRegistry类来注册和管理对应的类,管理类根据ID信息来识别各个多媒体数据流。在接口层中,Chromium移植使用WebMediaStream类来表示多媒体流,使用WebMediaStreamRegistry类来表示注册管理类。

        下面的问题是MediaStream接口需要提供各种事件给网页,因为很多实际的工作是在Chromium中来完成的,所以MediaStreamImpl会将这些事件从Chromium传递给WebKit。同时因为Chromium的多进程和沙箱模型,一些工作需要在Browser进程中完成,所以可以见到如图11-24所描述的跨进程的基础设施。

                                图11-24 Chromium支持MediaStream接口基础设施

        IPC左侧部分是Browser进程中的两个主要类,分别是消息处理类和MediaStream的管理类,该管理类知道MediaStream对应的网页是什么,并将事件(如创建和销毁等)传回Renderer进程。右侧是消息派发类,主要帮助MediaStreamImpl类来完成与Browser进程相关的MediaStream消息的传递。

        实际上,MediaStream可以表示本地的多媒体流,也可以表示远端的多媒体流。对于本地的多媒体流,需要音频和视频的捕获机制,同时使用上面建立的连接传输给远端。对于远端的多媒体流,需要使用连接来接收数据,并使用到音频和视频的解码能力。下面分成四个部分来分别介绍。

        首先是音频的捕获机制,图11-25描述了该机制使用的主要类。当网页需要创建多媒体流的时候,MediaStreamImpl会创建音频捕获类,也就是WebRtcAudioCapturer类,如图中上半部分。因为捕获音频需要音频输入设备,所以使用AudioDeviceFactory工厂类创建一个逻辑上的AudioInputDevice对象。另外一个重要的类是WebRtcAudioDeviceImpl,用来表示音频的设备,该类继承自WebRtcAudioDeviceNotImpl类。这其实是继承自libjingle和WebRTC项目中的抽线接口的一个桥接类,用来表示它们需要的音频设备,当然包括输入设备。同样因为Renderer进程不能访问音频输入设备,所以需要IPC来完成这一功能,Browser进程的AudioInputController会控制和访问设备,而AudioInputDeviceManager可以管理和控制所有输入设备。

                                图11-25 Chromium本地捕获音频的基础设施

其次是处理远端多媒体流中的音频解码和播放功能。图11-26是Chromium处理远端音频流所需要的一些主要类及关系图。这里不涉及连接如何接收传输的数据,因为Chromium是使用libjingle和WebRTC项目来完成连接的功能。Chromium使用WebRtcAudioRender类来完成音频渲染,该桥接类会被WebMediaPlayer作为渲染音频的实现类,其作用主要是将MediaStream的数据同实际的音频渲染类结合起来。

                                图11-26 Chromium处理远端音频基础设施

再次是从视频输入设备请求捕获本地视频流,图11-27是该功能依赖的一些主要类。

                                图11-27 Chromium本地捕获视频的基础设施

        首先看虚线右侧Renderer进程中的设施。同样是MediaStreamImpl类发起,由辅助工厂类MeidaStreamDependencyFactory帮助创建一个RtcVideoCapaturer,用来获取视频。该类有两个作用,其一是实现libjingle和WebRTC项目中的接口类,因为需要视频输入的实现,这个同音频部分非常类似。另外就是将调用请求交给一个代理类来完成,这就是RtcVideoCaptureDelegate类。下面的就比较好理解了,分别是管理类VideoCaptureImplManager和视频捕获类VideoCaptureImpl,并包括一个发送消息到Browser进程的辅助类。在Browser进程使用控制类VideoCaptureController来获取VideoCaptureDevice,该类会使用摄像头等视频输入设备,效果都相对比较简单直观。

        最后是处理远端多媒体流中的视频解码和播放功能。当MediaStreamImpl对象接收到远端的多媒体流之后,它会使用WebRTC来对视频数据进行解码,因为可以使用硬件来解码,所以提高了处理的性能。

                                        图11-28 Chromium处理远端视频基础设施

把WebRTC整个过程综合起来分析,可以有一种更为整体和直观的感受,如图11-29所示。读者可以结合这个图来回味一下之前所描述的众多细节。图中没有本地捕获的音视频的播放过程,因为它们不是必须的,而且同播放远端多媒体流类似,这里便不再赘述。

                图11-29 WebKit、Chromium、libjingle和WebRTC等项目支持WebRTC规范的框架

        目前,在Chrome浏览器中,基于WebRTC的网页已经可以在移动操作系统(如Android)上获得支持,移动领域的进展必将推动该技术的进一步发展。回顾本章介绍的多媒体各个方面的技术,读者可以看出,HTML5技术不仅引入了多媒体的支持,而且加入了之前插件也不能支持的众多更新更复杂的技术,但是却极大提升了HTML5的应用范围。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

EXECL 单元格字符串链接 CONCAT :应用:将一行数据转为json

源&#xff1a; 目标 函数表示 CONCAT("data", CHAR(10), "{", CHAR(10), " ", "ulAlarmId : ", A5, CHAR(10), " ", "ulAlarmLevel : ", D5, CHAR(10)," ", "bBo…

Webpack5 基本使用 - 2

常用 loader loader 是辅助打包工具。webpack 默认只能打包 js 文件&#xff0c;打包其它模块就需要配置 loader 来告诉 webpack 该怎么去打包其它文件。loader 可以将文件从不同的语言转换为 JavaScript。一类文件如果需要多个 loader 处理&#xff0c;loader 的执行顺序是从…

从零开始学Python第02课:第一个Python程序

在上一课中&#xff0c;我们对 Python 语言的过去现在有了一些了解&#xff0c;我们准备好了运行 Python 程序所需要的解释器环境。相信大家已经迫不及待的想开始自己的 Python 编程之旅了&#xff0c;但是新问题来了&#xff0c;我们应该在什么地方书写 Python 程序&#xff0…

caj在线转换成word怎么转?几种在线转换方法详解

caj在线转换成word怎么转&#xff1f;在日常的学习和工作中&#xff0c;我们常常会遇到各种各样的文件格式。其中&#xff0c;CAJ格式的文件因其专业性强&#xff0c;常常让许多用户感到困扰。如果你手头恰好有这种格式的文件&#xff0c;却苦于无法编辑&#xff0c;那么接下来…

Centos7 两种方式安装 MySQL5.7 步骤 yum 、本地 tar 文件

一、使用 yum 源方式安装 1、卸载系统自带 mariadb MariaDB Server 是最流行的开源 关系型数据库 之一。它由 MySQL 的原始开发者制作&#xff0c;并保证保持开源。 在 CentOS 7 中默认安装有 MariaDB 可忽略&#xff0c;安装完成之后可以直接覆盖掉 MariaDB。 查看并卸载系统…

github无法访问此网站,github.com 的响应时间过长。

问题 点击之前书签页中保存的去github搜集的项目连接&#xff0c;出现github无法访问此网站&#xff0c;github.com 的响应时间过长。 解决办法 1、打开浏览器&#xff0c;点击百度&#xff1b; 2、搜索hub.nuaa.cf&#xff1b; 3、点击第一项&#xff0c;如下所示&#xf…

@RequestPart注解的REST API

RequestPart注解的REST API的做法 问题解决方案body设置header设置区别 问题 为了验证接口, 想使用apipost批量插入多条数据到服务器中, 但是一直没搜索到使用方法. 接口如下: ApiOperation("上传文件")PostMapping("/upload-file")public TeamResponseM…

【数据结构】 顺序栈的基本操作 (C语言版)

目录 一、顺序栈 1、顺序栈的定义&#xff1a; 2、顺序栈的优缺点 二、顺序栈的基本操作算法&#xff08;C语言&#xff09; 1、宏定义 2、创建结构体 3、顺序栈的初始化 4、顺序栈的入栈 5、顺序栈的出栈 6、取栈顶元素 7、栈的遍历输出 8、顺序栈的判空 9、顺…

模版——函数模版(方法),类模版(容器)

函数模版 之前我们要实现两个变量值的交换要写交换函数&#xff0c; 但是因为交换函数需要把要交换的变量的参数写出来&#xff0c;所以在不同的两个类型的变量值交换的时候我们就要写重载函数&#xff0c;会显得很麻烦&#xff0c;那么我们是不是可以弄个一交换函数的模版&a…

Nacos持久化配置文件到Mysql(全图文).

1. 确保Mysql版本是在8以下&#xff0c;如果是8或者以上请这一步请参考&#xff1a; 最直接有效的解决nacos配置mysql8.0以上版本后无法启动的问题_nacos 8848端口 和mysql冲突-CSDN博客https://blog.csdn.net/qq_42758288/article/details/108967808 2.初始化数据库在Mysql&a…

Linux下安装 Redis7

Linux下安装 Redis7 三、Linux下安装 Redis7【redis-7.2.4.tar.gz】3.1.下载redis的安装包3.1.1.手动下载Redis压缩包并上传【redis-7.2.4.tar.gz】3.1.2.wget工具下载redis-7.2.4.tar.gz 3.2.将安装包进行解压缩3.3.进入redis的安装包3.4.检查是否有gcc 环境3.5.编译和安装并指…

自然语言处理--概率最大中文分词

自然语言处理附加作业--概率最大中文分词 一、理论描述 中文分词是指将中文句子或文本按照语义和语法规则进行切分成词语的过程。在中文语言中&#xff0c;词语之间没有明显的空格或标点符号来分隔&#xff0c;因此需要通过分词工具或算法来实现对中文文本的分词处理。分词的…

大型语言模型基础知识的可视化指南

直观分解复杂人工智能概念的工具和文章汇总 如今&#xff0c;LLM&#xff08;大型语言模型的缩写&#xff09;在全世界都很流行。没有一天不在宣布新的语言模型&#xff0c;这加剧了人们对错过人工智能领域的恐惧。然而&#xff0c;许多人仍在为 LLM 的基本概念而苦苦挣扎&…

操作系统导论-课后作业-ch14

1. 代码如下&#xff1a; #include <stdio.h> #include <stdlib.h>int main() {int *i NULL;free(i);return 0; }执行结果如下&#xff1a; 可见&#xff0c;没有任何报错&#xff0c;执行完成。 2. 执行结果如下&#xff1a; 3. valgrind安装使用参考&a…

数据结构(Chapter Two -03)—线性表的链式表示

在这一部分&#xff08;数据结构(Chapter Two -01)—线性表及顺序表-CSDN博客&#xff09;里面&#xff0c;我们知道线性表包括顺序表和链表结构。前面写了顺序表的基本操作&#xff0c;那这部分就写一写线性表叭&#xff01; 链表特点&#xff1a;不需要使用地址连续的存储单…

【Flink-1.17-教程】-【四】Flink DataStream API(7)输出算子(Sink)

【Flink-1.17-教程】-【四】Flink DataStream API&#xff08;7&#xff09;输出算子&#xff08;Sink&#xff09; 1&#xff09;连接到外部系统2&#xff09;输出到文件3&#xff09;输出到 Kafka4&#xff09;输出到 MySQL&#xff08;JDBC&#xff09;5&#xff09;自定义 …

2024年第十二届亚洲机械与材料工程国际会议(ACMME 2024)即将召开!

时间&#xff1a;2024年6月14-17日 地点&#xff1a;日本京都先端科学大学太秦校区 会议官网&#xff1a;第11届ACMME |日本京都 2024年第十二届亚洲机械与材料工程会议 &#xff08;ACMME 2024&#xff09;将于2024年6月14日-17日在日本京都先端科学大学召开。亚洲机械与材料…

STL第二讲

第二讲 视频标准库源码版本&#xff1a;gnu c 2.9.1/4.9/Visual C OOP vs GP GP是将datas与methods分开&#xff0c;OOP相反&#xff1b; 为什么list不能使用全局的sort&#xff1f; 因为sort源代码&#xff1a; *(first (last - first)/2) // 此迭代器只能是随机访问迭代…

Jedis(一)与Redis的关系

一、Jedis介绍&#xff1a; 1、背景&#xff1a; Jedis是基于Java语言的Redis的客户端&#xff0c;Jedis Java Redis。Redis不仅可以使用命令来操作&#xff0c;现在基本上主流的语言都有API支持&#xff0c;比如Java、C#、C、PHP、Node.js、Go等。在官方网站里有一些Java的…

多维时序 | Matlab实现GWO-TCN-Multihead-Attention灰狼算法优化时间卷积网络结合多头注意力机制多变量时间序列预测

多维时序 | Matlab实现GWO-TCN-Multihead-Attention灰狼算法优化时间卷积网络结合多头注意力机制多变量时间序列预测 目录 多维时序 | Matlab实现GWO-TCN-Multihead-Attention灰狼算法优化时间卷积网络结合多头注意力机制多变量时间序列预测效果一览基本介绍程序设计参考资料 效…