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

news2024/11/25 7:11:22

流程图

在这里插入图片描述

  • 创建MediaStream, MediaStream一方面作为从VideoFramePacketizer接收到媒体数据;

  • 创建VideoFramePacketizer,MediaStream 把sink 注册到VideoFramePacketizer,这样VideoFramePacketizer(继承了MediaSource)的视频数据会流向到MediaStream。即MediaStream作为视频源,VideoFramePacketizer作为接收者。VideoFramePacketizer的数据是哪里接收到的???

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
  //  {audio, video, owner, enableBWE} 是从SdoInfo.getMediaSettings 得到
  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();
    }

     // 设立recvOnly, 所以是out 
    if (direction === 'in') {
            ...
    } else {
      // 1. Connection wrtc, 创建MediaStream, 最后一个参数,isPublisher = false
      wrtc.addMediaStream(id, {label: id}, false);

      if (audio) {
        ...
      }
      if (video) {
        // 2. 
        //  wrtc 是Connction
        //  wrtc.callBase = new CallBase();
        //  在创建Connction的时候创建,dist/webrtc_agent/webrtc/wrtcConnection.js
        this.videoFramePacketizer = new VideoFramePacketizer(
          video.red, video.ulpfec, video.transportcc, video.mid,
          video.midExtId, false, wrtc.callBase, enableBWE);
        // 3.wrtc.getMediaStream 就是 5.1 创建的addon.MediaStream
        this.videoFramePacketizer.bindTransport(wrtc.getMediaStream(id));
      }
    }
  }

...
}

4.1 Connection.addMediaStream

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

4.2 new addon.VideoFramePacketizer

小节6, 创建

4.3 addon.VideoFramePacketizer.bindTransport

小节7, source的赋值

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

dist/webrtc_agent/webrtc/wrtcConnection.js

2023-04-26T21:54:19.799  - INFO: Connection - message: addMediaStream, connectionId: b149e44bb10d4e91bd162a8c6806ae7b, mediaStreamId: 1
  //  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. create media stream
      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/wrtcConnection.js

2023-04-26T21:54:19.799  - DEBUG: Connection - message: _createMediaStream, connectionId: b149e44bb10d4e91bd162a8c6806ae7b, mediaStreamId: 1, isPublisher: false
   //  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));
    }
    // 2. ???
    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
    WebRtcConnection* connection =
     Nan::ObjectWrap::Unwrap<WebRtcConnection>(Nan::To<v8::Object>(info[1]).ToLocalChecked());

    erizo::WebRtcConnection
    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
2023-04-26 21:54:19,800  - DEBUG: 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

2023-04-26 21:54:19,799  - INFO: MediaStream - 
id: 1, 
role:subscriber,  
message: constructor, 
id: 1
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} {
  ///   
  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;
}

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);

  // erizo::WebRtcConnection me,addMediaStream
  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 WebRtcConnection::addMediaStream

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

2023-04-26 21:54:19,800  - DEBUG: WebRtcConnection - id: b149e44bb10d4e91bd162a8c6806ae7b,  message: Adding mediaStream, id: 1
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.VideoFramePacketizer——创建

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

6.1 NAN_METHOD(addon.VideoFramePacketizer::New)

source/agent/webrtc/rtcFrame/VideoFramePacketizerWrapper.cc

void VideoFramePacketizer::New(const FunctionCallbackInfo<Value>& args) {
  Isolate* isolate = Isolate::GetCurrent();
  HandleScope scope(isolate);

  bool supportRED = args.Length() > 0 ? Nan::To<bool>(args[0]).FromJust() : false;
  bool supportULPFEC = args.Length() > 1 ? Nan::To<bool>(args[1]).FromJust() : false;
  int transportccExt = (args.Length() > 2) ? Nan::To<int32_t>(args[2]).FromJust() : -1;
  std::string mid;
  int midExtId = -1;
  if (args.Length() >= 5) {
    Nan::Utf8String param4(Nan::To<v8::String>(args[3]).ToLocalChecked());
    mid = std::string(*param4);
    midExtId = args[4]->IntegerValue(Nan::GetCurrentContext()).ToChecked();
  }
  bool selfRequestKeyframe = (args.Length() >= 6)
      ? Nan::To<bool>(args[5]).FromJust() : false;
  CallBase* baseWrapper = (args.Length() >= 7)
      ? Nan::ObjectWrap::Unwrap<CallBase>(Nan::To<v8::Object>(args[6]).ToLocalChecked())
      : nullptr;
  bool enableBandwidthEstimation = (args.Length() >= 8)
      ? Nan::To<bool>(args[7]).FromJust() : false;


  // 创建addon::VideoFramePacketizer
  VideoFramePacketizer* obj = new VideoFramePacketizer();
  owt_base::VideoFramePacketizer::Config config;
  config.enableRed = supportRED;
  config.enableUlpfec = supportULPFEC;
  config.transportccExt = transportccExt;
  config.mid = mid;
  config.midExtId = midExtId;
  config.enableBandwidthEstimation = enableBandwidthEstimation;
  if (baseWrapper) {
    config.rtcAdapter = baseWrapper->rtcAdapter;
  }

  // 创建owt_base::VideoFramePacketizer
  if (transportccExt > 0) {
    config.enableTransportcc = true;
    config.selfRequestKeyframe = false;
    obj->me = new owt_base::VideoFramePacketizer(config);
  } else if (selfRequestKeyframe) {
    config.enableTransportcc = false;
    config.selfRequestKeyframe = true;
    obj->me = new owt_base::VideoFramePacketizer(config);
  } else {
    config.enableTransportcc = false;
    obj->me = new owt_base::VideoFramePacketizer(config);
  }
  obj->dest = obj->me;

  obj->Wrap(args.This());
  args.GetReturnValue().Set(args.This());
}

addon.VideoFramePacketizer::VideoFramePacketizer

VideoFramePacketizer::VideoFramePacketizer() {};

6.2 owt_base::VideoFramePacketizer::VideoFramePacketizer

source/core/owt_base/VideoFramePacketizer.cpp

VideoFramePacketizer::VideoFramePacketizer(VideoFramePacketizer::Config& config)
    : m_enabled(true)
    , m_frameFormat(FRAME_FORMAT_UNKNOWN)
    , m_frameWidth(0)
    , m_frameHeight(0)
    , m_ssrc(0)
    , m_sendFrameCount(0)
    , m_rtcAdapter(config.rtcAdapter)
    , m_videoSend(nullptr)
{
    video_sink_ = nullptr;
    if (!m_rtcAdapter) {
        ELOG_DEBUG("Create RtcAdapter");
        m_rtcAdapter.reset(RtcAdapterFactory::CreateRtcAdapter());
    }
    if (config.enableBandwidthEstimation) {
        m_feedbackTimer = SharedJobTimer::GetSharedFrequencyTimer(
            kBitrateEstimationInterval);
        m_feedbackTimer->addListener(this);
    }
    init(config);
}

??? RtcAdapterFactory::CreateRtcAdapter

VideoFramePacketizer::init

source/core/owt_base/VideoFramePacketizer.cpp

bool VideoFramePacketizer::init(VideoFramePacketizer::Config& config)
{
    if (!m_videoSend) {
        // Create Send Video Stream
        rtc_adapter::RtcAdapter::Config sendConfig;
        if (config.enableTransportcc) {
            sendConfig.transport_cc = config.transportccExt;
        }
        if (config.enableRed) {
            sendConfig.red_payload = RED_90000_PT;
        }
        if (config.enableUlpfec) {
            sendConfig.ulpfec_payload = ULP_90000_PT;
        }
        if (!config.mid.empty()) {
            memset(sendConfig.mid, 0, sizeof(sendConfig.mid));
            strncat(sendConfig.mid, config.mid.c_str(), sizeof(sendConfig.mid) - 1);
            sendConfig.mid_ext = config.midExtId;
        }
        if (config.enableBandwidthEstimation) {
            sendConfig.bandwidth_estimation = true;
        }
        sendConfig.feedback_listener = this;
        sendConfig.rtp_listener = this;
        sendConfig.stats_listener = this;
      /
        m_videoSend = m_rtcAdapter->createVideoSender(sendConfig);
        m_ssrc = m_videoSend->ssrc();
      /
        return true;
    }

    return false;
}

7. NAN_METHOD(VideoFramePacketizer::bindTransport)——erizo::MediaSink赋值

source/agent/webrtc/rtcFrame/VideoFramePacketizerWrapper.cc

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

void VideoFramePacketizer::bindTransport(const FunctionCallbackInfo<Value>& args) {
  Isolate* isolate = Isolate::GetCurrent();
  HandleScope scope(isolate);

  VideoFramePacketizer* obj = ObjectWrap::Unwrap<VideoFramePacketizer>(args.Holder());
  owt_base::VideoFramePacketizer* me = obj->me;

 // 传入的参数就是addon.MediaStream 继承于 MediaFilter
// 在 5.1.1 的时候对msource和msink 赋值
  MediaFilter* param = Nan::ObjectWrap::Unwrap<MediaFilter>(Nan::To<v8::Object>(args[0]).ToLocalChecked());
  erizo::MediaSink* transport = param->msink;

  // owt_base::VideoFramePacketizer* me
  me->bindTransport(transport);
}

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

source/agent/webrtc/rtcConn/MediaStream.h

source/core/owt_base/MediaWrapper.h

小节9

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

source/core/owt_base/VideoFramePacketizer.cpp

void VideoFramePacketizer::bindTransport(erizo::MediaSink* sink)
{
    boost::unique_lock<boost::shared_mutex> lock(m_transportMutex);
      //  MediaSink* video_sink_;
    video_sink_ = sink;
    video_sink_->setVideoSinkSSRC(m_videoSend->ssrc());
    erizo::FeedbackSource* fbSource = video_sink_->getFeedbackSource();
    if (fbSource)
        fbSource->setFeedbackSink(this);
}

setVideoSinkSSRC

getFeedbackSource

setFeedbackSink

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

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

相关文章

upload靶场通关(12-21关)

Pass-12&#xff08;白名单校验&#xff08;GET 型 0x00 截断&#xff09;&#xff09; 先看提示&#xff1a; 一头雾水&#xff0c;只知道了上传路径是可控的 查看源码&#xff1a; 能看懂就看看&#xff0c;反正我是看了同学的笔记得知这是白名单&#xff0c;按照文件名进行…

Customizable constraint systems for succinct arguments学习笔记(1)

1. 引言 微软研究中心Srinath Setty、a16z crypto research 和 Georgetown University Justin Thaler、Carnegie Mellon University Riad Wahby 20203年论文《Customizable constraint systems for succinct arguments》。 在该论文中&#xff0c;介绍了Customizable constra…

CMake学习(1): CMake基本使用

https://subingwen.cn/cmake/CMake-primer/ 1. CMake 概述 CMake是一个项目构建工具&#xff0c;并且是跨平台的。Cmake跟Makefile其实是差不多的&#xff0c;只不过makefile更底层些。大多是 IDE 软件都集成了 make&#xff0c;比如&#xff1a;VS 的 nmake、linux 下的 GNU…

单链表OJ题:LeetCode--141.环形链表

朋友们、伙计们&#xff0c;我们又见面了&#xff0c;本期来给大家解读一下LeetCode中的第141道单链表OJ题&#xff0c;如果看完之后对你有一定的启发&#xff0c;那么请留下你的三连&#xff0c;祝大家心想事成&#xff01; 数据结构与算法专栏&#xff1a;数据结构与算法 个 …

龙岗区五联土地整备利益统筹项目,集体物业集中签约仪式

五联土地整备利益统筹项目启动以来街道高度重视相关工作开专题会、建工作组、设党支部把征拆工作一项项推进 截至6月1日已完成&#xff1a; 清租签约73户&#xff0c;面积9.55万m&#xff08;意向签约17户&#xff0c;约1.66万m&#xff09; 私人住宅业主补偿安置签约8户&…

Java语言----反射、枚举以及lambda表达式

目录 一.反射 1.1反射的基本情况 1.2反射中最重要的类 1.2.1 Class类 1.2.2Field类 1.2.3Constructor类 1.2.4Methood类 1.3反射优缺点 二.枚举 2.1概念 2.2枚举&#xff08;enum&#xff09;类方法 2.3枚举的构造 三.Lambda表达式 3.1Lambda介绍 3.2 函数式接…

AI实战营:深度学习预训练与MMPreTrain

目录 一、MMPretrain算法库介绍 二、经典主干网络 残差网络ResNet&#xff08;2015&#xff09; Vision Transformer(2020) 三、自监督学习 四、多模态算法 一、MMPretrain算法库介绍 算法库与任务组成&框架概览 预训练工具箱MMPretrain Python推理API 环境搭建 O…

数据结构与算法·第4章【串】

串是有限长的字符序列&#xff0c;由一对单引号相括&#xff0c;如: “a string” 可以理解为c的 s t r i n g string string 基本操作 S t r A s s i g n , S t r C o m p a r e , S t r L e n g t h , C o n c a t , S u b S t r i n g StrAssign,StrCompare,StrLength,Conc…

大数据技术——使用IDEA开发Scala程序

目录 一、使用IDEA开发WordCount程序... 3 二、实验目的... 3 三、实验要求... 3 四、实验环境... 3 五、实验步骤... 3 4.1.1启动IDEA并创建一个新项目WordCount 3 4.1.2为WordCount项目添加Scala框架支持... 7 4.1.3数据准备... 8 4.1.4设置项目目录... 9 4.1.5新建…

webAPI学习笔记4——PC端网页特效

目录 1. 元素偏移量 offset 系列 1.1 offset 概述 1.2 offset 与 style 区别 offset style !!&#xff01;&#xff01;&#xff01;&#xff01;&#xff01;案例&#xff1a;获取鼠标在盒子内的坐标 &#xff01;&#xff01;&#xff01;&#xff01;&#xff01;&…

Jeston Orin Nnao 安装pytorch与torchvision环境

大家好&#xff0c;我是虎哥&#xff0c;Jeston Orin nano 8G模块&#xff0c;提供高达 40 TOPS 的 AI 算力&#xff0c;安装好了Jetpack5.1之后&#xff0c;我们需要配置一些支持环境&#xff0c;来为我们后续的深度学习开发提供支持。本章内容&#xff0c;我将主要围绕安装对…

OCR图片文字识别,人工手动图片标注软件安装过程

OCR图片文字识别&#xff0c;人工手动图片标注软件安装过程&#xff0c;本章关注标注软件的安装&#xff0c;启动过程 1. 下载 anaconda anaconda 下载慢的问题&#xff1a; 使用国内镜像地址下载&#xff1a; https://mirrors.bfsu.edu.cn/anaconda/archive/ https://www.ana…

OpenMMLab-AI实战营第二期——4.深度学习预训练与MMPretrain

文章目录 1. MMPreTrain算法库介绍1.1 算法库与任务组成1.2 框架概览 2. 经典主干网络2.1 ResNet2.2 Vision Transformer 3. 自监督学习3.1 SimCLR3.2 MAE3.3 iBOT 4. 多模态算法4.1 CLIP4.2 BLIP 视频链接&#xff1a;b站-深度学习预训练与MMPretrain 1. MMPreTrain算法库介绍…

NvM学习笔记(一)以TC387为例,分析为什么要使用Flash 模拟 EEPROM ?

在嵌入式软件系统中&#xff0c;管理非易失性的数据存储&#xff0c;如在系统启动时&#xff0c;从非易失性存储器中读取一些关键数据&#xff0c;在系统关闭阶段&#xff0c;同步应用程序RAM区的数据到非易失性存储器中&#xff0c;是一项十分必要的工作。 目前行业内用的最广…

Linux :: 时间日历指令【3】:cal 指令:查询当日是今年的第几天、输出当前月历、指定月历、输出当前年历、指定年历

前言&#xff1a;本篇是 Linux 基本操作篇章的内容&#xff01; 笔者使用的环境是基于腾讯云服务器&#xff1a;CentOS 7.6 64bit。 学习集&#xff1a; C 入门到入土&#xff01;&#xff01;&#xff01;学习合集Linux 从命令到网络再到内核&#xff01;学习合集 目录索引&am…

Redis分段锁实现超高并发秒杀

参考尼恩著&#xff1a;《Java高并发核心编程》 技术自由圈 业务情景 还是秒杀场景&#xff0c;假设抖音直播间小杨哥上架6000单螺蛳粉&#xff0c;价格9.9买10包&#xff0c;限时1分钟秒杀&#xff0c;此时准备秒杀的人有1万人。我们首先从技术上来看看如果不进行优化是否可以…

控制四旋翼飞行器以进行多目标航点导航的MPC算法(Matlab代码实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…

信不信,3招就能测出你的网工水平

大家好&#xff0c;我的网工朋友 老话常谈一下&#xff0c;在网工这行发展&#xff0c;技术肯定是第一位的。 从你入行的第一步起&#xff0c;就很看重你的技术水平了&#xff0c;越往后&#xff0c;就更注重技术水平和项目经验的融合度。 再往后&#xff0c;技术要有&#…

网络安全实验室|网络信息安全攻防学习平台(脚本关1-6)

传送门&#xff1a; http://hackinglab.cn/ 1. key又又不见了 点击此处开启抓包&#xff0c;send ti repeater 模块 yougotit_script_now2. 快速口算 脚本来源&#xff1a; https://blog.csdn.net/hzxtjx/article/details/125692349 import requests,re# 自动处理cookies&a…

【PCB专题】案例:PCB封装中哪些禁布区我们最常容易忘记增加

USB等直插器件焊接面禁布区 USB器件面那大家都知道有座子有自己的Place_Bound。不同的器件如果Place_Bound重叠的话会报DRC。 但是因为器件是只在一个面上,另一个面是焊接脚,或是另一面不是焊接脚,但PCB封装上为通孔。比如有一些器件从PCB上看是通孔,但PCB Layout人员没见…