【owt】WebrtcNode, publish-sdp offer 流程(2)

news2024/11/23 0:12:16

流程图

在这里插入图片描述

  1. 创建MediaStream, MediaStream一方面作为从客户端接收到媒体数据,另外一方面做为视频源;
  2. 创建VideoFrameConstruc=tor,VideoFrameConstructor 把sink 注册到MediaStream,这样MediaStream(继承了MediaSource)的视频数据会流向到VideoFrameConstructor。即MediaStream作为视频源,VideoFrameConstructor作为接收者。

4. new WrtcStream

dist/webrtc_agent/webrtc/wrtcConnection.js

/*
 * WrtcStream represents a stream object
 * of WrtcConnection. It has media source
 * functions (addDestination) and media sink
 * functions (receiver) which will be used
 * in connection link-up. Each rtp-stream-id
 * in simulcast refers to one WrtcStream.
 */
class WrtcStream extends EventEmitter {

  /*
   * audio: { format, ssrc, mid, midExtId }
   * video: {
   *   format, ssrcs, mid, midExtId,
   *   transportcc, red, ulpfec, scalabilityMode
   * }
   */
  // Connection wrtc
  //  id = mid
  constructor(id, wrtc, direction, {audio, video, owner, enableBWE}) {
    super();
    this.id = id;
    this.wrtc = wrtc;
    this.direction = direction;
    this.audioFormat = audio ? audio.format : null;
    this.videoFormat = video ? video.format : null;
    this.audio = audio;
    this.video = video;
    this.audioFrameConstructor = null;
    this.audioFramePacketizer = null;
    this.videoFrameConstructor = null;
    this.videoFramePacketizer = null;
    this.closed = false;
    this.owner = owner;
    if (video && video.scalabilityMode) {
      this.scalabilityMode = video.scalabilityMode;
      // string => LayerStream
      this.layerStreams = new Map();
    }

     // 设立sendOnly, 所以是in 
    if (direction === 'in') {
      // 1. Connection wrtc, 创建MediaStream
      wrtc.addMediaStream(id, {label: id}, true);

      if (audio) {
        ...
      }
      if (video) {
        // 2. 
        //  wrtc 是Connction
        //  wrtc.callBase = new CallBase();
        //  在创建Connction的时候创建,dist/webrtc_agent/webrtc/wrtcConnection.js
        this.videoFrameConstructor = new VideoFrameConstructor(
          this._onMediaUpdate.bind(this), video.transportcc, wrtc.callBase);
        // 3.wrtc.getMediaStream 就是 5.1 创建的addon.MediaStream
        this.videoFrameConstructor.bindTransport(wrtc.getMediaStream(id));
        // 4. 
        wrtc.setVideoSsrcList(id, video.ssrcs);
      }

    } else {
        ...
    }
  }

...
}

4.1 Connection.addMediaStream

小节5 , 创建Mediastream,同时把mediastream 添加到 WebrtcConnection

4.2 new addon.VideoFrameConstructor

小节6, 创建addon.VideoFrameConstructor

4.3 addon.VideoFrameConstructor.bindTransport

小节7, source的赋值,把MediaStream 作为source,视频数据从MediaStream 流向到VideoFrameConstructor。

4.4 Connection.setVideoSsrcList

小节8,

5. Connection.addMediaStream——创建Mediastream,同时把stream 添加到 WebrtcConnection

dist/webrtc_agent/webrtc/connection.js

  //  id = mid
  addMediaStream(id, options, isPublisher) {
  // this.id 就是 transportId(或者connect id)
    log.info(`message: addMediaStream, connectionId: ${this.id}, mediaStreamId: ${id}`);
    if (this.mediaStreams.get(id) === undefined) {
      // 1. 
      const mediaStream = this._createMediaStream(id, options, isPublisher);
      // 2. wrtc 就是 addon.WebRtcConnection    
      this.wrtc.addMediaStream(mediaStream);
      // 3. map,  存放是 mid->MediaStream
      this.mediaStreams.set(id, mediaStream);
    }
  }

5.1 Connection._createMediaStream——创建addon.MediaStream

dist/webrtc_agent/webrtc/connection.js

   //  id = mid
  _createMediaStream(id, options = {}, isPublisher = true) {
    log.debug(`message: _createMediaStream, connectionId: ${this.id}, ` +
              `mediaStreamId: ${id}, isPublisher: ${isPublisher}`);
   // this.wrtc 就是 addon.WebRtcConnection
    const mediaStream = new addon.MediaStream(this.threadPool, this.wrtc, id,
      options.label, this._getMediaConfiguration(this.mediaConfiguration), isPublisher);
    mediaStream.id = id;
    // 这里的label 就是id
    mediaStream.label = options.label;
    if (options.metadata) {
      // mediaStream.metadata = options.metadata;
      // mediaStream.setMetadata(JSON.stringify(options.metadata));
    }
    mediaStream.onMediaStreamEvent((type, message) => {
      this._onMediaStreamEvent(type, message, mediaStream.id);
    });
    return mediaStream;
  }

======5.1.1 NAN_METHOD(MediaStream::New)

source/agent/webrtc/rtcConn/MediaStream.cc

NAN_METHOD(MediaStream::New) {
  if (info.Length() < 3) {
    Nan::ThrowError("Wrong number of arguments");
  }

  if (info.IsConstructCall()) {
    // Invoked as a constructor with 'new MediaStream()'
    ThreadPool* thread_pool = Nan::ObjectWrap::Unwrap<ThreadPool>(Nan::To<v8::Object>(info[0]).ToLocalChecked());
    // 就是在source/agent/webrtc/rtcConn/WebRtcConnection.cc的类 class WebRtcConnection
    WebRtcConnection* connection =
     Nan::ObjectWrap::Unwrap<WebRtcConnection>(Nan::To<v8::Object>(info[1]).ToLocalChecked());

    std::shared_ptr<erizo::WebRtcConnection> wrtc = connection->me;
        // wrtc_id=id=mid
    std::string wrtc_id = getString(info[2]);
    std::string stream_label = getString(info[3]);

    bool is_publisher = Nan::To<bool>(info[5]).FromJust();

///
    // source/agent/webrtc/rtcConn/MediaStream.cc
    // MediaStream 结构关系图,参考小节9
    MediaStream* obj = new MediaStream();
    // source/agent/webrtc/rtcConn/WebRtcConnection.cc
    // Share same worker with connection
    obj->me = std::make_shared<erizo::MediaStream>(wrtc->getWorker(), wrtc, wrtc_id, stream_label, is_publisher);
    // erizo::MediaSink* msink; MediaStream 集成于erizo::MediaSink
    obj->msink = obj->me.get();
    // erizo::MediaSource* msource; MediaStream 集成于erizo::MediaSource
    obj->msource = obj->me.get();
    obj->id_ = wrtc_id;
    obj->label_ = stream_label;
///
    ELOG_DEBUG("%s, message: Created", obj->toLog());
    obj->Wrap(info.This());
    info.GetReturnValue().Set(info.This());
    obj->asyncResource_ = new Nan::AsyncResource("MediaStreamCallback");
  } else {
    // TODO(pedro) Check what happens here
  }
}
log——MediaStreamWrapper
MediaStreamWrapper - id: 1, message: Created
MediaStream 和 erizo::MediaStream 注意区分

参考小节9

5.1.2 addon.MediaStream::MediaStream

source/agent/webrtc/rtcConn/MediaStream.cc

MediaStream::MediaStream() : closed_{false}, id_{"undefined"} {
  // 异步回调事情
  async_stats_ = new uv_async_t;
  async_event_ = new uv_async_t;
  uv_async_init(uv_default_loop(), async_stats_, &MediaStream::statsCallback);
  uv_async_init(uv_default_loop(), async_event_, &MediaStream::eventCallback);
}

5.1.3 erizo::MediaStream::MediaStream

source/agent/webrtc/rtcConn/erizo/src/erizo/MediaStream.cpp

MediaStream::MediaStream(std::shared_ptr<Worker> worker,
  std::shared_ptr<WebRtcConnection> connection,
  const std::string& media_stream_id, // media_stream_id = mid
  const std::string& media_stream_label,
  bool is_publisher) :
    audio_enabled_{false}, video_enabled_{false},
    media_stream_event_listener_{nullptr},
    connection_{std::move(connection)},
    stream_id_{media_stream_id},
    mslabel_ {media_stream_label},
    bundle_{false},
    pipeline_{Pipeline::create()},
    worker_{std::move(worker)},
    audio_muted_{false}, video_muted_{false},
    pipeline_initialized_{false},
    is_publisher_{is_publisher},
    simulcast_{false},
    bitrate_from_max_quality_layer_{0},
    video_bitrate_{0} {
  ///   
  //constexpr uint32_t kDefaultVideoSinkSSRC = 55543;
  //constexpr uint32_t kDefaultAudioSinkSSRC = 44444;

  setVideoSinkSSRC(kDefaultVideoSinkSSRC);
  setAudioSinkSSRC(kDefaultAudioSinkSSRC);
  ///   
  ELOG_INFO("%s message: constructor, id: %s",
      toLog(), media_stream_id.c_str());

  ///   
  // FeedbackSink
  source_fb_sink_ = this;
  // FeedbackSource
  sink_fb_source_ = this;
  ///   
  stats_ = std::make_shared<Stats>();
  log_stats_ = std::make_shared<Stats>();
  quality_manager_ = std::make_shared<QualityManager>();
  packet_buffer_ = std::make_shared<PacketBufferService>();
  std::srand(std::time(nullptr));

  ///   
  audio_sink_ssrc_ = std::rand();
  video_sink_ssrc_ = std::rand();
  ///   

  rtcp_processor_ = nullptr;

  should_send_feedback_ = true;
  slide_show_mode_ = false;

  mark_ = clock::now();

  rate_control_ = 0;
  sending_ = true;
}
2023-05-31 16:44:11,153  - INFO: MediaStream - id: 1, role:publisher,  message: constructor, id: 1

5.2 NAN_METHOD(WebRtcConnection::addMediaStream)——添加MediaStream到WebRtcConnection

source/agent/webrtc/rtcConn/WebRtcConnection.cc

NAN_METHOD(WebRtcConnection::addMediaStream) {
  WebRtcConnection* obj = Nan::ObjectWrap::Unwrap<WebRtcConnection>(info.Holder());
  std::shared_ptr<erizo::WebRtcConnection> me = obj->me;
  if (!me) {
    return;
  }

  MediaStream* param = Nan::ObjectWrap::Unwrap<MediaStream>(
    Nan::To<v8::Object>(info[0]).ToLocalChecked());
   // param->me就是个erizo::MediaStream 指针
  auto wr = std::shared_ptr<erizo::MediaStream>(param->me);

  me->addMediaStream(wr);
}

about me

source/agent/webrtc/rtcConn/MediaStream.h

class MediaStream : public MediaFilter, public erizo::MediaStreamStatsListener, public erizo::MediaStreamEventListener {
 public:
...
    std::shared_ptr<erizo::MediaStream> me;
...

}

5.2.1 erizo::WebRtcConnection::addMediaStream

source/agent/webrtc/rtcConn/erizo/src/erizo/WebRtcConnection.cpp

void WebRtcConnection::addMediaStream(std::shared_ptr<MediaStream> media_stream) {
  asyncTask([media_stream] (std::shared_ptr<WebRtcConnection> connection) {
    boost::mutex::scoped_lock lock(connection->update_state_mutex_);
    ELOG_DEBUG("%s message: Adding mediaStream, id: %s", connection->toLog(), media_stream->getId().c_str());
    connection->media_streams_.push_back(media_stream);
  });
}

5.3 mediaStreams

    this.mediaStreams = new Map();

   //mid->MediaStream
   this.mediaStreams.set(id, mediaStream);

以id=mid为key,mediaStream 为value

6. new addon.VideoFrameConstructor——创建

this._onMediaUpdate.bind(this)

把function 传递下去,给native层调用

Javascript中function函数bind方法实例用法详解-js教程-PHP中文网

???========wrtc.callBase

6.1 NAN_METHOD(addon.VideoFrameConstructor::New)

source/agent/webrtc/rtcFrame/VideoFrameConstructorWrapper.cc

NAN_METHOD(VideoFrameConstructor::New) {
  if (info.IsConstructCall()) {
    VideoFrameConstructor* obj = new VideoFrameConstructor();
    if (info.Length() <= 3) {
      int transportccExt = (info.Length() >= 2) ? Nan::To<int32_t>(info[1]).FromMaybe(-1) : -1;
      // CallBase 的创建流程,后面会详细说明
      CallBase* baseWrapper = (info.Length() >= 3)
        ? Nan::ObjectWrap::Unwrap<CallBase>(Nan::To<v8::Object>(info[2]).ToLocalChecked())
        : nullptr;

      if (baseWrapper) {
        // 走了这里的流程
        obj->me = new owt_base::VideoFrameConstructor(baseWrapper->rtcAdapter, obj, transportccExt);
      } else if (transportccExt > 0) {
        obj->me = new owt_base::VideoFrameConstructor(obj, transportccExt);
      } else {
        obj->me = new owt_base::VideoFrameConstructor(obj);
      }
    } else {
      VideoFrameConstructor* parent =
        Nan::ObjectWrap::Unwrap<VideoFrameConstructor>(
          Nan::To<v8::Object>(info[1]).ToLocalChecked());
      Nan::Utf8String param2(Nan::To<v8::String>(info[2]).ToLocalChecked());
      std::string layerId = std::string(*param2);
      int spatialId = info[3]->IntegerValue(Nan::GetCurrentContext()).ToChecked();
      int temporalId = info[4]->IntegerValue(Nan::GetCurrentContext()).ToChecked();

      obj->me = new owt_base::VideoFrameConstructor(parent->me);
      obj->me->setPreferredLayers(spatialId, temporalId);
      obj->layerId = layerId;
      obj->parent = parent->me;
      obj->parent->addChildProcessor(layerId, obj->me);
    }

/有用到吗///
    // owt_base::VideoFrameConstructor me 
    // owt_base::FrameSource* src;
    obj->src = obj->me;
    // erizo::MediaSink* msink;
    obj->msink = obj->me;
///
    // info[0].As<Function>() 就是 js中WrtcStream._onMediaUpdate
    obj->Callback_ = new Nan::Callback(info[0].As<Function>());
    obj->asyncResource_ = new Nan::AsyncResource("VideoFrameCallback");
    uv_async_init(uv_default_loop(), &obj->async_, &VideoFrameConstructor::Callback);
    obj->Wrap(info.This());
    info.GetReturnValue().Set(info.This());
  } else {
    // const int argc = 1;
    // v8::Local<v8::Value> argv[argc] = {info[0]};
    // v8::Local<v8::Function> cons = Nan::New(constructor);
    // info.GetReturnValue().Set(cons->NewInstance(argc, argv));
  }
}

???CallBase

???baseWrapper->rtcAdapter, rtc_adapter::RtcAdapter

???obj->src,obj->msink

6.2 owt_base::VideoFrameConstructor::VideoFrameConstructor

source/core/owt_base/VideoFrameConstructor.cpp

VideoFrameConstructor::VideoFrameConstructor(
    std::shared_ptr<rtc_adapter::RtcAdapter> rtcAdapter,
    VideoInfoListener* vil, uint32_t transportccExtId)
    : m_enabled(true)
    , m_ssrc(0)
    , m_transport(nullptr)
    , m_pendingKeyFrameRequests(0)
    , m_videoInfoListener(vil)
    , m_videoReceive(nullptr)
{
    ELOG_DEBUG("VideoFrameConstructor2 %p", this);
    m_config.transport_cc = transportccExtId;
    assert(rtcAdapter.get());
    m_feedbackTimer = SharedJobTimer::GetSharedFrequencyTimer(1);
    m_feedbackTimer->addListener(this);
    m_rtcAdapter = rtcAdapter;
}

7. NAN_METHOD(VideoFrameConstructor::bindTransport)——erizo::MediaSource赋值

source/agent/webrtc/rtcFrame/VideoFrameConstructorWrapper.cc

参数addon.MediaStream 就是在5.1 小节创建。

NAN_METHOD(VideoFrameConstructor::bindTransport) {
  VideoFrameConstructor* obj = Nan::ObjectWrap::Unwrap<VideoFrameConstructor>(info.Holder());
  owt_base::VideoFrameConstructor* me = obj->me;

// addon.MediaStream 继承于 MediaFilter
// 在 5.1.1 的时候对msource和msink 赋值
  MediaFilter* param = Nan::ObjectWrap::Unwrap<MediaFilter>(Nan::To<v8::Object>(info[0]).ToLocalChecked());
  erizo::MediaSource* source = param->msource;

  me->bindTransport(source, source->getFeedbackSink());
}

7.1 addon.MediaFilter,addon.MediaSink,addon.MediaSource

source/agent/webrtc/rtcConn/MediaStream.h

source/core/owt_base/MediaWrapper.h

小节9

7.2 erizo::MediaSource.getFeedbackSink

source/agent/webrtc/rtcConn/erizo/src/erizo/MediaDefinitions.h

  FeedbackSink* getFeedbackSink() {
        boost::mutex::scoped_lock lock(monitor_mutex_);
        return source_fb_sink_;
    }

是在MediaStream 创建的时候 赋值

source/agent/webrtc/rtcConn/erizo/src/erizo/MediaStream.cpp

MediaStream::MediaStream(std::shared_ptr<Worker> worker,
  std::shared_ptr<WebRtcConnection> connection,
  const std::string& media_stream_id,
  const std::string& media_stream_label,
  bool is_publisher) 
...
{
...
  source_fb_sink_ = this;
  sink_fb_source_ = this;
}

7.3 ======== owt_base::VideoFrameConstructor.bindTransport——向source注册回调,从erizo::MediaStream获取视频数据

source/core/owt_base/VideoFrameConstructor.cpp

void VideoFrameConstructor::bindTransport(erizo::MediaSource* source, erizo::FeedbackSink* fbSink)
{
    boost::unique_lock<boost::shared_mutex> lock(m_transportMutex);
    // erizo::MediaSource* source, 对应的对象就是erizo::MediaStream。
      // erizo::MediaStream继承erizo::MediaSource
    m_transport = source;
      // 设置MediaSink
    m_transport->setVideoSink(this);
    m_transport->setEventSink(this);
    setFeedbackSink(fbSink);
}

setVideoSink

setEventSink

setFeedbackSink

8. ????Connection.setVideoSsrcList

 setVideoSsrcList(label, videoSsrcList) {
    this.wrtc.setVideoSsrcList(label, videoSsrcList);
  }

8.1 NAN_METHOD(WebRtcConnection::setVideoSsrcList)

NAN_METHOD(WebRtcConnection::setVideoSsrcList) {
  WebRtcConnection* obj = Nan::ObjectWrap::Unwrap<WebRtcConnection>(info.Holder());
  std::shared_ptr<erizo::WebRtcConnection> me = obj->me;
  if (!me) {
    return;
  }

  std::string stream_id = getString(info[0]);
  v8::Local<v8::Array> video_ssrc_array = v8::Local<v8::Array>::Cast(info[1]);
  std::vector<uint32_t> video_ssrc_list;

  for (unsigned int i = 0; i < video_ssrc_array->Length(); i++) {
    v8::Local<v8::Value> val = Nan::Get(video_ssrc_array, i).ToLocalChecked();
    unsigned int numVal = Nan::To<int32_t>(val).FromJust();
    video_ssrc_list.push_back(numVal);
  }
  me->getLocalSdpInfo()->video_ssrc_map[stream_id] = video_ssrc_list;
}

8.2 WebRtcConnection::getLocalSdpInfo

std::shared_ptr<SdpInfo> WebRtcConnection::getLocalSdpInfo() {
  boost::mutex::scoped_lock lock(update_state_mutex_);
  ELOG_DEBUG("%s message: getting local SDPInfo", toLog());
  forEachMediaStream([this] (const std::shared_ptr<MediaStream> &media_stream) {
    if (!media_stream->isRunning() || media_stream->isPublisher()) {
      ELOG_DEBUG("%s message: getting local SDPInfo stream not running, stream_id: %s", toLog(), media_stream->getId());
      return;
    }
    std::vector<uint32_t> video_ssrc_list = std::vector<uint32_t>();
    if (media_stream->getVideoSinkSSRC() != kDefaultVideoSinkSSRC && media_stream->getVideoSinkSSRC() != 0) {
      video_ssrc_list.push_back(media_stream->getVideoSinkSSRC());
    }
    ELOG_DEBUG("%s message: getting local SDPInfo, stream_id: %s, audio_ssrc: %u",
               toLog(), media_stream->getId(), media_stream->getAudioSinkSSRC());
    if (!video_ssrc_list.empty()) {
      local_sdp_->video_ssrc_map[media_stream->getLabel()] = video_ssrc_list;
    }
    if (media_stream->getAudioSinkSSRC() != kDefaultAudioSinkSSRC && media_stream->getAudioSinkSSRC() != 0) {
      local_sdp_->audio_ssrc_map[media_stream->getLabel()] = media_stream->getAudioSinkSSRC();
    }
  });

  bool sending_audio = local_sdp_->audio_ssrc_map.size() > 0;
  bool sending_video = local_sdp_->video_ssrc_map.size() > 0;

  bool receiving_audio = remote_sdp_->audio_ssrc_map.size() > 0;
  bool receiving_video = remote_sdp_->video_ssrc_map.size() > 0;

  if (!sending_audio && receiving_audio) {
    local_sdp_->audioDirection = erizo::RECVONLY;
  } else if (sending_audio && !receiving_audio) {
    local_sdp_->audioDirection = erizo::SENDONLY;
  } else {
    local_sdp_->audioDirection = erizo::SENDRECV;
  }

  if (!sending_video && receiving_video) {
    local_sdp_->videoDirection = erizo::RECVONLY;
  } else if (sending_video && !receiving_video) {
    local_sdp_->videoDirection = erizo::SENDONLY;
  } else {
    local_sdp_->videoDirection = erizo::SENDRECV;
  }

  return local_sdp_;
}

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

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

相关文章

运维小白必学篇之基础篇第七集:磁盘管理实验

磁盘管理实验 实验作业&#xff1a; 1、添加1块磁盘&#xff0c;并查看&#xff08;lsblk&#xff09; 2、使用MBR分区表的格式对添加的磁盘划分分区&#xff0c;完成以下操作&#xff1a; 1、创建3个主分区&#xff0c;每个分区大小为2个GB 2、创建扩展分区&#xff0c;将剩…

前端技术搭建俄罗斯方块(内含源码)

The sand accumulates to form a pagoda ✨ 写在前面✨ 功能介绍✨ 页面搭建✨ 样式设置✨ 逻辑部分 ✨ 写在前面 上周我们实通过前端基础实现了扫雷游戏&#xff0c;今天还是继续按照我们原定的节奏来带领大家完成俄罗斯方块游戏&#xff0c;功能也比较简单简单&#xff0c;也…

【源码篇】基于SSM的办公管理系统

1、项目介绍 基于SSM的办公管理系统主要是对于办公用品的申领进行管理&#xff0c;系统分为三种角色&#xff0c;超级管理员、企业职工、审核员&#xff0c;每种角色拥有不同的权限菜单 主要功能模块有&#xff1a; 系统管理(用户管理、角色管理、菜单管理、个人信息管理、修…

ArrayList源码

介绍 ArrayList非线程安全。ArrayList基于动态数组&#xff0c;是一种线性表。随机访问友好&#xff0c;插入和删除效率低。 ​ 增删慢&#xff1a;每次删除元素&#xff0c;都需要改变数组长度、拷贝以及移动数组长度 ​ 查询快&#xff1a;由于数组在内存中是一块连续空间…

Python实战基础20-解密文件及目录操作

任务1 为泸州驰援湖北的89名白衣勇士点赞 【任务描述】 设计python程序&#xff0c;实现用户可以为泸州驰援湖北的89名白衣勇士点赞留言。用户点赞留言内容保存到本地txt文件中。 import os # 导入os模块 import random # 导入随机模块 import string # 导入string模块# 定义…

序列化与反序列化深入理解

序列化与反序列化深入理解 1 介绍1.1 概述1.2 序列化实现的需求 2 常用序列化实现函数序列化语言内置开源序列化实现 3 各序列化实现比较4 各序列化实现概述XMLJSONProtobufJava 内置TLVVLE&#xff08;Variable Length Encoding&#xff09; 5 flex & bison5.1 介绍应用解…

MyBatis-4

MyBatis 工作原理 形式上的应用为&#xff1a; UserMapper userMapper MyBatisSessionFactory.getMapper(UserMapper.class); List<User> userList userMapper.selectByExample(example)真正执行的操作为: SqlSession session MyBatisSessionFactory.getSession();…

聊天更有趣ChatGPT【再次更新】第三方插件

ChatGPT再次更新&#xff0c;第三方插件让你的聊天更有趣 你是否曾经想过&#xff0c;如果你能够和你最喜欢的明星、作家或者历史人物聊天&#xff0c;会是什么样的体验&#xff1f;你是否曾经想过&#xff0c;如果你能够和你的朋友一起玩一些有趣的游戏、挑战或者测试&#x…

spring注解驱动开发(BEAN注册方式与生命周期)

目录 容器中注册BEAN的方式 BEAN生命周期 容器中注册BEAN的方式 包扫描组件标注注解 ComponentScan(basePackages {"com.an.spring.condition"}) Service Component Controller RepositoryBEan方式【导入第三方包里面的组件】 ComponentScan(basePackages {&quo…

chatgpt赋能python:Python处理雷达数据

Python处理雷达数据 雷达技术是一种主要用于测量目标距离、速度和方位的技术。在雷达系统中&#xff0c;雷达接收器接收到的信号经过一系列的处理才能得到有效的数据。在这一过程中&#xff0c;Python语言得到了广泛应用。本文将介绍Python如何处理雷达数据。 雷达数据的格式…

linuxOPS基础_ssh概念详解

ssh 什么是SSH SSH&#xff08;Secure Shell&#xff0c;安全外壳&#xff09;是一种网络安全协议&#xff0c;通过加密和认证机制实现安全的访问和文件传输等业务。传统远程登录和文件传输方式&#xff0c;例如Telnet、FTP&#xff0c;使用明文传输数据&#xff0c;存在很多…

Openlayers 教程 - 基于 Openlayers api 实现空间查询(客户端):点选、范围查询

Openlayers 教程 - 基于 Openlayers api 实现空间查询&#xff08;客户端&#xff09;&#xff1a;点选、范围查询 客户端空间查询核心代码在线示例 客户端空间查询 在地理信息系统中&#xff0c;空间查询有的非常重要的作用&#xff0c;几乎所有地图相关的业务系统都需要空间…

青少年C++编程等考有这么多??机构到底该带孩子考哪个?

随着信息学的普及与发展&#xff0c;越来越多的孩子开始学习C&#xff0c;参加编程等考来检验C的学习成果、作为也逐渐成为了一个共识&#xff0c;跟C有关的等考究竟有哪些&#xff0c;哪个等考含金量够高&#xff0c;能够客观、有效地检验学习成果呢&#xff1f; 在这里整理了…

解决Fortify漏洞:Access Specifier Manipulation

目录 1. 什么是Fortify漏洞 2. 漏洞描述 示例&#xff1a; 3. 漏洞原因 4. 解决方法 示例&#xff1a; 1. 什么是Fortify漏洞 Fortify 是一种静态代码分析工具&#xff0c;可用于识别源代码中的安全漏洞和错误。Fortify 检查程序是否存在潜在的安全漏洞&#xff0c;例如 …

Vue-springboot大学生心理健康测试咨询与诊断平台设计与实现

心理健康咨询与诊断平台一直以来就是困扰医院提高服务水平的重要环节&#xff0c;特别是医疗水平高、门诊访问量高的综合型医院&#xff0c;门诊拥挤就成了普遍现象。因此&#xff0c;本文提出了心理健康咨询与诊断平台。在线预约挂号、医疗诊断、医生评价、排班信息、心理测试…

WWW 2023 | 量化交易相关论文(附论文链接)

写在前面 国际万维网会议&#xff08;Proceedings of the ACM Web Conference&#xff0c;简称 WWW&#xff09;是互联网技术领域最重要的国际会议之一。今年的 WWW 将在美国德克萨斯州举行。本届会议共收到了1900篇论文&#xff0c;接收365篇&#xff0c;录用率为19.2%。本文介…

单片机的系统移植

目录 一、uboot概述 Bootloader Bootloader基本功能&#xff1a; 二、SD卡启动盘制作 三、uboot的使用 3.1uboot模式 自启动模式 交互模式 3.2uboot帮助命令 3.3uboot环境变量命令 3.4常用环境变量 3.5网络传输命令 3.6u-boot访问存储器命令 3.7 u-boot自启动环境变量&#xff…

web3到底是什么?只是一场永远醒不来的梦

Hello大家好&#xff0c;我是ClonBrowser鱼鱼。 过去&#xff0c;我一直与大家分享Facebook运营和广告方面的干货&#xff0c;但今天我想和大家聊聊一个更加炙手可热的话题——Web3。 近年来&#xff0c;Web3成为了互联网行业的热门关键词&#xff0c;被各大媒体和技术界热议…

体验管理|关于客户旅程编排(CJO),你不知道的事!

Guofu 第 97⭐️ 篇原创文章分享 &#xff08;点击&#x1f446;&#x1f3fb;上方卡片关注我&#xff0c;加⭐️星标⭐️~&#xff09; 客户旅程地图大家已经很熟悉了&#xff0c;那客户旅程编排又是什么呢&#xff1f; 我们一起来看一下。&#x1f47b;&#x1f47b;&#x1…

for循环中的变量

此处点击时打印的i为5 因为i本身onclick内部没有,需要去全局变量找 此处点击时打印的i为5 此处按按钮i打印4 // 对每一个按钮的点击进行监听for (var i 0; i < btnEls.length; i) {var btnItemEl btnEls[i]btnItemEl.index ibtnItemEl.onclick function() {console.log…