WebrtcNode, publish-sdp offer 流程(1)

news2024/11/20 18:31:55

1. AmqpClient - New message received

sdp offer 的消息

AmqpClient - RpcServer New message received {
  method: 'onTransportSignaling',
  args: [
    'aa230ce0863e42baa8bae5c14e91e809',
    {
      sdp: 'v=0\r\n' +
        'o=- 2367615733001925388 2 IN IP4 127.0.0.1\r\n' +
        's=-\r\n' +
        't=0 0\r\n' +
        'a=group:BUNDLE 0 1\r\n' +
        'a=msid-semantic: WMS MediaStream-0176f9fc-a754-4838-b002-0bdda6d4deaa\r\n' +
        'm=audio 9 UDP/TLS/RTP/SAVPF 111 103 104 9 102 0 8 106 105 13 110 112 113 126\r\n' +
        'c=IN IP4 0.0.0.0\r\n' +
        'a=rtcp:9 IN IP4 0.0.0.0\r\n' +
        'a=ice-ufrag:YH25\r\n' +
        'a=ice-pwd:l47tWnjyoR1kn3nR0JWjuotU\r\n' +
        'a=ice-options:trickle\r\n' +
        'a=fingerprint:sha-256 65:8A:AE:75:0E:6B:50:51:A4:93:A4:01:76:43:3D:83:26:31:E7:75:B8:8A:DC:D4:13:78:14:4E:4D:0B:93:3E\r\n' +
        'a=setup:actpass\r\n' +
        'a=mid:0\r\n' +
        'a=extmap:1 urn:ietf:params:rtp-hdrext:ssrc-audio-level\r\n' +
        'a=extmap:2 http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time\r\n' +
        'a=extmap:3 http://www.ietf.org/id/draft-holmer-rmcat-transport-wide-cc-extensions-01\r\n' +
        'a=extmap:4 urn:ietf:params:rtp-hdrext:sdes:mid\r\n' +
        'a=extmap:5 urn:ietf:params:rtp-hdrext:sdes:rtp-stream-id\r\n' +
        'a=extmap:6 urn:ietf:params:rtp-hdrext:sdes:repaired-rtp-stream-id\r\n' +
        'a=sendonly\r\n' +
        'a=msid:MediaStream-0176f9fc-a754-4838-b002-0bdda6d4deaa AudioTrack-abd86f8a-1f74-4c73-b6fa-c20f5b175a1d\r\n' +
        'a=rtcp-mux\r\n' +
        'a=rtpmap:111 opus/48000/2\r\n' +
        'a=rtcp-fb:111 transport-cc\r\n' +
        'a=fmtp:111 minptime=10;useinbandfec=1\r\n' +
        'a=rtpmap:103 ISAC/16000\r\n' +
        'a=rtpmap:104 ISAC/32000\r\n' +
        'a=rtpmap:9 G722/8000\r\n' +
        'a=rtpmap:102 ILBC/8000\r\n' +
        'a=rtpmap:0 PCMU/8000\r\n' +
        'a=rtpmap:8 PCMA/8000\r\n' +
        'a=rtpmap:106 CN/32000\r\n' +
        'a=rtpmap:105 CN/16000\r\n' +
        'a=rtpmap:13 CN/8000\r\n' +
        'a=rtpmap:110 telephone-event/48000\r\n' +
        'a=rtpmap:112 telephone-event/32000\r\n' +
        'a=rtpmap:113 telephone-event/16000\r\n' +
        'a=rtpmap:126 telephone-event/8000\r\n' +
        'a=ssrc:3582341671 cname:JTBK6RkjYuislL5i\r\n' +
        'a=ssrc:3582341671 msid:MediaStream-0176f9fc-a754-4838-b002-0bdda6d4deaa AudioTrack-abd86f8a-1f74-4c73-b6fa-c20f5b175a1d\r\n' +
        'a=ssrc:3582341671 mslabel:MediaStream-0176f9fc-a754-4838-b002-0bdda6d4deaa\r\n' +
        'a=ssrc:3582341671 label:AudioTrack-abd86f8a-1f74-4c73-b6fa-c20f5b175a1d\r\n' +
        'm=video 9 UDP/TLS/RTP/SAVPF 123 114 115 116 119\r\n' +
        'c=IN IP4 0.0.0.0\r\n' +
        'a=rtcp:9 IN IP4 0.0.0.0\r\n' +
        'a=ice-ufrag:YH25\r\n' +
        'a=ice-pwd:l47tWnjyoR1kn3nR0JWjuotU\r\n' +
        'a=ice-options:trickle\r\n' +
        'a=fingerprint:sha-256 65:8A:AE:75:0E:6B:50:51:A4:93:A4:01:76:43:3D:83:26:31:E7:75:B8:8A:DC:D4:13:78:14:4E:4D:0B:93:3E\r\n' +
        'a=setup:actpass\r\n' +
        'a=mid:1\r\n' +
        'a=extmap:14 urn:ietf:params:rtp-hdrext:toffset\r\n' +
        'a=extmap:2 http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time\r\n' +
        'a=extmap:13 urn:3gpp:video-orientation\r\n' +
        'a=extmap:3 http://www.ietf.org/id/draft-holmer-rmcat-transport-wide-cc-extensions-01\r\n' +
        'a=extmap:12 http://www.webrtc.org/experiments/rtp-hdrext/playout-delay\r\n' +
        'a=extmap:11 http://www.webrtc.org/experiments/rtp-hdrext/video-content-type\r\n' +
        'a=extmap:7 http://www.webrtc.org/experiments/rtp-hdrext/video-timing\r\n' +
        'a=extmap:8 http://tools.ietf.org/html/draft-ietf-avtext-framemarking-07\r\n' +
        'a=extmap:9 http://www.webrtc.org/experiments/rtp-hdrext/color-space\r\n' +
        'a=extmap:4 urn:ietf:params:rtp-hdrext:sdes:mid\r\n' +
        'a=extmap:5 urn:ietf:params:rtp-hdrext:sdes:rtp-stream-id\r\n' +
        'a=extmap:6 urn:ietf:params:rtp-hdrext:sdes:repaired-rtp-stream-id\r\n' +
        'a=sendonly\r\n' +
        'a=msid:MediaStream-0176f9fc-a754-4838-b002-0bdda6d4deaa VideoTrack-3e9cd783-7906-49e1-8887-99736156a1d2\r\n' +
        'a=rtcp-mux\r\n' +
        'a=rtcp-rsize\r\n' +
        'a=rtpmap:123 AV1/90000\r\n' +
        'a=rtcp-fb:123 goog-remb\r\n' +
        'a=rtcp-fb:123 transport-cc\r\n' +
        'a=rtcp-fb:123 ccm fir\r\n' +
        'a=rtcp-fb:123 nack\r\n' +
        'a=rtcp-fb:123 nack pli\r\n' +
        'a=rtpmap:114 red/90000\r\n' +
        'a=rtpmap:115 rtx/90000\r\n' +
        'a=fmtp:115 apt=114\r\n' +
        'a=rtpmap:116 ulpfec/90000\r\n' +
        'a=rtpmap:119 rtx/90000\r\n' +
        'a=fmtp:119 apt=123\r\n' +
        'a=ssrc-group:FID 4143402920 1935417042\r\n' +
        'a=ssrc:4143402920 cname:JTBK6RkjYuislL5i\r\n' +
        'a=ssrc:4143402920 msid:MediaStream-0176f9fc-a754-4838-b002-0bdda6d4deaa VideoTrack-3e9cd783-7906-49e1-8887-99736156a1d2\r\n' +
        'a=ssrc:4143402920 mslabel:MediaStream-0176f9fc-a754-4838-b002-0bdda6d4deaa\r\n' +
        'a=ssrc:4143402920 label:VideoTrack-3e9cd783-7906-49e1-8887-99736156a1d2\r\n' +
        'a=ssrc:1935417042 cname:JTBK6RkjYuislL5i\r\n' +
        'a=ssrc:1935417042 msid:MediaStream-0176f9fc-a754-4838-b002-0bdda6d4deaa VideoTrack-3e9cd783-7906-49e1-8887-99736156a1d2\r\n' +
        'a=ssrc:1935417042 mslabel:MediaStream-0176f9fc-a754-4838-b002-0bdda6d4deaa\r\n' +
        'a=ssrc:1935417042 label:VideoTrack-3e9cd783-7906-49e1-8887-99736156a1d2\r\n',
      type: 'offer'
    }
  ],
  corrID: 14,
  replyTo: 'amq.gen-pj6CYWZ4AOsOkYmlAePDEA'
}

2. WebrtcNode - onTransportSignaling

dist/webrtc_agent/webrtc/index.js

// connectionId 应该就是operationId
that.onTransportSignaling = function (connectionId, msg, callback) {
        log.debug('onTransportSignaling, connection id:', connectionId, 'msg:', msg);
        // 通过 operationId 从mappingTransports 获取 transportId,
        // 接着通过transportId从peerConnections 获取WrtcConnection
        // 参考小结 2.1
        var conn = getWebRTCConnection(connectionId);
        // WrtcConnection conn
        if (conn) {
            // 参考小结 2.2
            conn.onSignalling(msg, connectionId);
            callback('callback', 'ok');
        } else {
          callback('callback', 'error', 'Connection does NOT exist:' + connectionId);
        }
    };

2.1 WebrtcNode - getWebRTCConnection——返回WrtcConnection

dist/webrtc_agent/webrtc/index.js

    // 返回的是WrtcConnection
    var getWebRTCConnection = function (operationId) {
        // 在WebrtcNode-createWebRTCConnection 创建
        var transportId = mappingTransports.get(operationId);
        if (peerConnections.has(transportId)) {
            return peerConnections.get(transportId);
        }
        return null;
    };

2.2 WrtcConnection.onSignalling

dist/webrtc_agent/webrtc/wrtcConnection.js

处理 offer sdp。

  that.onSignalling = function (msg, operationId) {
    var processSignalling = function () {
  // 这里type=’offer‘
      if (msg.type === 'offer') {
        log.debug('on offer:', msg.sdp);
        processOffer(msg.sdp);
      } else if (msg.type === 'candidate') {
      ...
      } else if (msg.type === 'removed-candidates') {
      ...
      }
    };
    // wrtc 是 Connection
    if (wrtc) {
      processSignalling();
    } else {
      // should not reach here
      log.error('wrtc is not ready');
    }
  };

2.3 ==========WrtcConnection.processOffer

dist/webrtc_agent/webrtc/wrtcConnection.js

这里的 remoteSdp 是null,

 const processOffer = function (sdp) {
 // 第一进来,remoteSdp 是null
    if (!remoteSdp) {
      // First offer
      remoteSdp = new SdpInfo(sdp);
      // Check mid, 获取到mid 数组
      const mids = remoteSdp.mids();
      for (const mid of mids) {
         // 设置finalformat
        processOfferMedia(mid);
      }
      // 创建answer sdp
      localSdp = remoteSdp.answer();

      // Setup transport
      let opId = null;
      for (const mid of mids) {
        if (remoteSdp.getMediaPort(mid) !== 0) {
          opId = setupTransport(mid);
        }
      }
      if (opId) {
        on_track({
          type: 'tracks-complete',
          operationId: opId
        });
      }

    } else {
     ...
    }
  };
  var remoteSdp = null;
  var localSdp = null;

2.3.1 SdpInfo.SdpInfo

dist/webrtc_agent/webrtc/sdpInfo.js

2.3.2 WrtcConnection.processOfferMedia——设置finalformat

WrtcConnection.addTrackOperation, 创建了元素operationMap

设置finalFormat

 const processOfferMedia = function (mid) {
    ...

    // Determine media format in offer
    if (remoteSdp.mediaType(mid) === 'audio') {
      const audioPreference = operationMap.get(mid).formatPreference;
      const audioFormat = remoteSdp.filterAudioPayload(mid, audioPreference);
      operationMap.get(mid).finalFormat = audioFormat;
    } else if (remoteSdp.mediaType(mid) === 'video') {
      const videoPreference = operationMap.get(mid).formatPreference;
      const videoFormat = remoteSdp.filterVideoPayload(mid, videoPreference);
      operationMap.get(mid).finalFormat = videoFormat;
    }
  };

operationMap 的元素就是WrtcConnection.addTrackOperation中添加的,这里是给finalFormat赋值

// mid => { operationId, sdpDirection, type, formatPreference, rids, enabled, finalFormat }
  var operationMap = new Map();

dist/webrtc_agent/webrtc/wrtcConnection.js,在WebrtcNode-publish流程.md 的addTrackOperation 小节 有详细说明。

2.3.3 SdpInfo.answer——localSdp

通过offer到sdp,进行对应的修改,作为answer的sdp。赋值给localSdp中,在WrtcConnection.setupTransport用到。

 answer() {
    const answer = new SdpInfo(this.toString());
    answer.obj.origin = {
      username: '-',
      sessionId: '0',
      sessionVersion: 0,
      netType: 'IN',
      ipVer: 4,
      address: '127.0.0.1'
    };
    answer.obj.media.forEach(mediaInfo => {
      mediaInfo.port = 1;
      mediaInfo.rtcp = {
        port: 1,
        netType: 'IN',
        ipVer: 4,
        address: '0.0.0.0'
      };
      if (mediaInfo.setup === 'active') {
        mediaInfo.setup = 'passive';
      } else {
        mediaInfo.setup = 'active';
      }

      delete mediaInfo.iceOptions;  
      delete mediaInfo.rtcpRsize;
      if (mediaInfo.direction === 'recvonly') {
        mediaInfo.direction = 'sendonly';
      } else if (mediaInfo.direction === 'sendonly') {
        delete mediaInfo.msid;
        delete mediaInfo.ssrcGroups;
        delete mediaInfo.ssrcs;
        mediaInfo.direction = 'recvonly';
      }

      if (mediaInfo.ext && Array.isArray(mediaInfo.ext)) {
        const extMappings = [
          'urn:ietf:params:rtp-hdrext:ssrc-audio-level',
          // 'http://www.ietf.org/id/draft-holmer-rmcat-transport-wide-cc-extensions-01',
          'urn:ietf:params:rtp-hdrext:sdes:mid',
          'urn:ietf:params:rtp-hdrext:sdes:rtp-stream-id',
          'urn:ietf:params:rtp-hdrext:sdes:repaired-rtp-stream-id',
          'urn:ietf:params:rtp-hdrext:toffset',
          'http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time',
          // 'urn:3gpp:video-orientation',
          // 'http://www.webrtc.org/experiments/rtp-hdrext/playout-delay',
        ];
        if (mediaInfo.type === 'video') {
          extMappings.push(
            'http://www.ietf.org/id/draft-holmer-rmcat-transport-wide-cc-extensions-01'
          );
        }
        mediaInfo.ext = mediaInfo.ext.filter((e) => {
          return extMappings.includes(e.uri);
        });
      }

      if (mediaInfo.rids && Array.isArray(mediaInfo.rids)) {
        // Reverse rids direction
        mediaInfo.rids.forEach(r => {
          r.direction = (r.direction === 'send') ? 'recv' : 'send';
        });
      }
      if (mediaInfo.simulcast) {
        // Reverse simulcast direction
        if (mediaInfo.simulcast.dir1 === 'send') {
          mediaInfo.simulcast.dir1 = 'recv';
        } else {
          mediaInfo.simulcast.dir1 = 'send';
        }
      }
    });

    return answer;
  }

SdpInfo.getMediaPort

SdpInfo.mediaType

operationMap——存放的是track相关的内容

dist/webrtc_agent/webrtc/wrtcConnection.js

WebrtcNode-publish流程.md 的addTrackOperation 小节 有详细说明

  // mid => { operationId, sdpDirection, type, formatPreference, rids, enabled, finalFormat }
  var operationMap = new Map();

2.3.4 WrtcConnection.setupTransport——创建WrtcStream

dist/webrtc_agent/webrtc/wrtcConnection.js

小节 3

2.3.5 on_track——这是创建WrtcConnection 传入的callback,tracks-complete

WebrtcNode-publish流程.md3.WebrtcNode - createWebRTCConnection——返回WrtcConnection 中传入了

function onTrack(trackInfo) {
            handleTrackInfo(transportId, trackInfo, controller);
        }

调用的代码

        on_track({
          type: 'tracks-complete',
          operationId: opId
        });

详细的看3.3, 3.4 小节

3. WrtcConnection.setupTransport——创建WrtcStream

主要是创建MediaStream。

dist/webrtc_agent/webrtc/wrtcConnection.js

const setupTransport = function (mid) {
    let rids = remoteSdp.rids(mid);
    const opSettings = operationMap.get(mid);
    const direction = (opSettings.sdpDirection === 'sendonly') ? 'in' : 'out';
    const simSsrcs = remoteSdp.getLegacySimulcast(mid);
    const trackSettings = remoteSdp.getMediaSettings(mid);
    const mediaType = remoteSdp.mediaType(mid);

    trackSettings.owner = owner;
    trackSettings.enableBWE = that.enableBWE;
    if (opSettings.finalFormat) {
      trackSettings[mediaType].format = opSettings.finalFormat;
      if (opSettings.finalFormat.codec === 'vp9' && simSsrcs) {
        // Legacy simulcast for VP9 SVC
        rids = null;
        trackSettings['video'].scalabilityMode = opSettings.scalabilityMode;
      }
    }

    if (rids) {
        ...
    } else {
        // 走这里的代码
      // No simulcast
      if (!trackMap.has(mid)) {
        // 1. Connection wrtc
        trackMap.set(mid, new WrtcStream(mid, wrtc, direction, trackSettings));
        // Set ssrc in local sdp for out direction
        // direct 是in
        if (direction === 'out') {
          ...
        }
        // 2. Connection wrtc
        // remoteSdp 是在processOffer 创建的对象
        wrtc.setRemoteSdp(remoteSdp.singleMediaSdp(mid).toString(), mid);
        // 3. Notify new track, 详细见小节 3.3, 3.4
        on_track({
          type: 'track-added',
          track: trackMap.get(mid),
          operationId: opSettings.operationId,
          mid: mid
        });
      } else {
        log.warn(`Conflict trackId ${mid} for ${wrtcId}`);
      }
    }

    return opSettings.operationId;
  };

SdpInfo.rids

trackMap 属性——存放了WrtcStream

  // composedId => WrtcStream
  var trackMap = new Map();

composeId

  const composeId = function (mid, rid) {
    return mid + ':' + rid;
  };

3.1 new WrtcStream

小节 4
创建了MediaStream

3.2 Connection.setRemoteSdp

// streamId = mid
setRemoteSdp(sdp, streamId) {
    // webRtcConnection wrtc
    this.wrtc.setRemoteSdp(sdp, streamId || this.id);
  }

3.2.1 NAN_METHOD(WebRtcConnection::setRemoteSdp)

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

  std::string sdp = getString(info[0]);
  std::string stream_id = getString(info[1]);

  bool r = me->setRemoteSdp(sdp, stream_id);

  info.GetReturnValue().Set(Nan::New(r));
}

3.2.2 WebRtcConnection::setRemoteSdp

bool WebRtcConnection::setRemoteSdpInfo(std::shared_ptr<SdpInfo> sdp, std::string stream_id) {
  asyncTask([sdp, stream_id] (std::shared_ptr<WebRtcConnection> connection) {
    ELOG_DEBUG("%s message: setting remote SDPInfo", connection->toLog());

    if (!connection->sending_) {
      return;
    }

    connection->remote_sdp_ = sdp;
    // mid 
    connection->processRemoteSdp(stream_id);
  });
  return true;
}

??? WebRtcConnection::processRemoteSdp

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

SdpInfo.singleMediaSdp

dist-debug/webrtc_agent/webrtc/sdpInfo.js

  singleMediaSdp(mid) {
    const sdp = new SdpInfo(this.toString());
    sdp.obj.media = sdp.obj.media.filter(m => m.mid.toString() === mid);
    sdp.setBundleMids([mid]);
    return sdp;
  }

3.3 on__track——track add

dist/webrtc_agent/webrtc/wrtcConnection.js

通知到WebrtcNode(/dist/webrtc_agent/webrtc/index.js)的createWebRTCConnection 中注册的callback, 回调一个对象,

        // Notify new track
        on_track({
          type: 'track-added',
          track: trackMap.get(mid), // WrtcStream
          operationId: opSettings.operationId,
          mid: mid
        })

3.4 ======WebrtcNode.handleTrackInfo

dist/webrtc_agent/webrtc/index.js

var connection = new WrtcConnection({
            connectionId: transportId,
            threadPool: threadPool,
            ioThreadPool: ioThreadPool,
            network_interfaces: global.config.webrtc.network_interfaces,
            owner,
        }, function onTransportStatus(status) {
            notifyTransportStatus(controller, transportId, status);
        }, function onTrack(trackInfo) {
            handleTrackInfo(transportId, trackInfo, controller);
        });
// trackInfo 就是on__track 回调回来
var handleTrackInfo = function (transportId, trackInfo, controller) {
        var publicTrackId;
        var updateInfo;
        if (trackInfo.type === 'track-added') {
            // Generate public track ID
            const track = trackInfo.track; // WrtcStream
            publicTrackId = transportId + '-' + track.id;
            if (mediaTracks.has(publicTrackId)) {
                log.error('Conflict public track id:', publicTrackId, transportId, track.id);
                return;
            }
            mediaTracks.set(publicTrackId, track);
            mappingPublicId.get(transportId).set(track.id, publicTrackId);
            if (track.direction === 'in') {
                const trackSource = track.sender();
                router.addLocalSource(publicTrackId, 'webrtc', trackSource)
                .catch(e => log.warn('Unexpected error during track add:', e));
            } else {
                router.addLocalDestination(publicTrackId, 'webrtc', track)
                .catch(e => log.warn('Unexpected error during track add:', e));
            }

            // Bind media-update handler
            track.on('media-update', (jsonUpdate) => {
                log.debug('notifyMediaUpdate:', publicTrackId, jsonUpdate);
                notifyMediaUpdate(controller, publicTrackId, track.direction, JSON.parse(jsonUpdate));
            });
            // Notify controller
            const mediaType = track.format('audio') ? 'audio' : 'video';
            updateInfo = {
                type: 'track-added',
                trackId: publicTrackId,
                mediaType: track.format('audio') ? 'audio' : 'video',
                mediaFormat: track.format(mediaType),
                direction: track.direction,
                operationId: trackInfo.operationId,
                mid: trackInfo.mid,
                rid: trackInfo.rid,
                active: true,
            };
            log.debug('notifyTrackUpdate', controller, publicTrackId, updateInfo);
            notifyTrackUpdate(controller, transportId, updateInfo);

        } else if (trackInfo.type === 'track-removed') {
            publicTrackId = mappingPublicId.get(transportId).get(trackInfo.trackId);
            if (!mediaTracks.has(publicTrackId)) {
                log.error('Non-exist public track id:', publicTrackId, transportId, trackInfo.trackId);
                return;
            }
            log.debug('track removed:', publicTrackId);
            router.removeConnection(publicTrackId)
            .then(ok => {
                mediaTracks.get(publicTrackId).close();
                mediaTracks.delete(publicTrackId);
                mappingPublicId.get(transportId).delete(trackInfo.trackId);
            })
            .catch(e => log.warn('Unexpected error during track remove:', e));

            // Notify controller
            updateInfo = {
                type: 'track-removed',
                trackId: publicTrackId,
            };
            notifyTrackUpdate(controller, transportId, updateInfo);

        } else if (trackInfo.type === 'tracks-complete') {
            updateInfo = {
                type: 'tracks-complete',
                operationId: trackInfo.operationId
            };
            notifyTrackUpdate(controller, transportId, updateInfo);
        }
    };

时序图

在这里插入图片描述

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

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

相关文章

quickstart Guide快速入门

本文档参考backtrader官方文档&#xff0c;是官方文档的完整中文翻译&#xff0c;可作为backtrader中文教程、backtrader中文参考手册、backtrader中文开发手册、backtrader入门资料使用。 快速入门章节目录 快速入门使用平台从0到100&#xff1a;一步一步的演示基本设置设置现…

C++ set类成员函数介绍 (set和multiset)

目录 &#x1f914;set模板介绍&#xff1a; &#x1f914;特点&#xff1a; &#x1f914;set的成员函数&#xff1a; &#x1f60a;set构造函数&#xff1a; &#x1f50d;代码实例&#xff1a; &#x1f50d;运行结果&#xff1a; &#x1f60a; set赋值函数&#xf…

IMX6ULL裸机篇之I2C相关寄存器

一. I2C实验 I2C时钟选择与传输速率 1. IMX6ULL的 I2C频率标准模式 100kbit/S&#xff0c;快速模式为 400Kbit/S 2. 时钟源选择 perclk_clk_rootipg_clk_root66MHz&#xff08;由之前的时钟实验章节可以知道是 66MHz&#xff09;。 二. I2C 寄存器配置 I2Cx_IFDR寄存器&…

《计算机组成原理》唐朔飞 第5章 输入输出系统 - 学习笔记

写在前面的话&#xff1a;此系列文章为笔者学习计算机组成原理时的个人笔记&#xff0c;分享出来与大家学习交流。使用教材为唐朔飞第3版&#xff0c;笔记目录大体与教材相同。 网课 计算机组成原理&#xff08;哈工大刘宏伟&#xff09;135讲&#xff08;全&#xff09;高清_…

chatgpt赋能python:Python中用什么表示空值?

Python中用什么表示空值&#xff1f; 在Python编程中&#xff0c;我们经常会遇到处理空值的场景。空值通常表示缺失的或未定义的值&#xff0c;这在数据处理和分析中尤其常见。那么&#xff0c;在Python中&#xff0c;究竟用什么来表示空值呢&#xff1f; None 在Python中&a…

6G显存玩转130亿参数大模型,仅需13行命令,RTX2060用户发来贺电

羊驼家族的Alpaca和Vicuna也都能运行&#xff0c;显存最低只需要6G&#xff0c;简直是低VRAM用户的福音有木有。 GitHub上的搭建教程火了之后&#xff0c;网友们纷纷跑来问苹果M2是不是也能跑。 这通操作的大致原理是利用最新版CUDA&#xff0c;可以将Transformer中任意数量的…

什么是先进存力?曙光存储:内铸数字底座,外成实践底气

5月26日&#xff0c;由DOIT联合中国电子学会共同举办的2023数据基础设施技术峰会在苏州举办。中科曙光存储产品事业部副总经理张新凤受邀参会&#xff0c;并在主论坛发表主题演讲&#xff0c;与数百位业内专业嘉宾伙伴共探存力发展未来。 什么样的存力能打造数字经济底座&#…

【笔记】【Javascript】javascript实现继承

前言 之前写过关于面向对象编程的文章&#xff0c;通过阅读别人的博客了解了一下Javascript实现继承的方法&#xff0c;并且使用图画的形式帮助了解&#xff0c;图是自己做的&#xff0c;若有偏差请读者帮忙指出&#xff0c;谢谢。笔记中有些个人理解后整理的笔记&#xff0c;…

基于STM32的ADC采样及各式滤波实现(HAL库,含VOFA+教程)

前言&#xff1a;本文为手把手教学ADC采样及各式滤波算法的教程&#xff0c;本教程的MCU采用STM32F103ZET6。以HAL库的ADC采样函数为基础进行教学&#xff0c;通过各式常见滤波的实验结果进行分析对比&#xff0c;搭配VOFA工具直观的展示滤波效果。ADC与滤波算法都是嵌入式较为…

MySQL进阶- Linux安装 和 索引

目录 Linux安装索引索引的概述索引的结构索引结构的介绍BtreeBtreeHash 索引的分类索引的语法&#xff08;创建&#xff0c;查看&#xff0c;删除等&#xff09;SQL性能分析SQL的执行频率&#xff08;查看SQL的执行频率&#xff09;慢查询日志show profilesexplain执行计划 索引…

video标签学习 xgplayer视频播放器分段播放mp4

文章目录 学习链接目标video标签自带视频和制作的视频区别video标签的src属性本地视频文件前端代码播放效果 服务器视频文件示例1后端代码前端代码播放效果 示例2后端代码前端代码播放效果 示例3后端配置前端代码播放效果 video对象video对象创建和获取video的属性video的方法v…

chatgpt赋能python:Python模块安装方法全解析

Python模块安装方法全解析 Python是一种功能强大的编程语言&#xff0c;拥有大量的开源库&#xff0c;这些库是在各种应用程序中使用的重要组件&#xff0c;它们能加速开发过程。不管你是初学者、中级者还是高级者&#xff0c;总会遇到需要安装第三方库的情况。但是安装库是一…

《Java并发编程实战》课程笔记(四)

互斥锁 原子性问题到底该如何解决呢&#xff1f; “同一时刻只有一个线程执行”这个条件非常重要&#xff0c;我们称之为互斥。如果我们能够保证对共享变量的修改是互斥的&#xff0c;那么&#xff0c;无论是单核 CPU 还是多核 CPU&#xff0c;就都能保证原子性了。 锁模型 …

Python连接达梦数据库

python如果想连接达梦数据库&#xff0c;必须要安装dmPython。 简介&#xff1a;dmPython 是 DM 提供的依据 Python DB API version 2.0 中 API 使用规定而开发的数据库访问接口。dmPython 实现这些 API&#xff0c;使 Python 应用程序能够对 DM 数据库进行访问。 dmPython 通…

数据库服务器

数据库服务器&#xff0c;联系Web服务器与DBMS的中间件是负责处理所有的应用程序服务器&#xff0c;包括在web服务器和后台的应用程序或数据库之间的事务处理和数据访问。 基本信息 中文名 数据库服务器 外文名 database server 功能 数据库服务器建立在数据库系统基础上&a…

系统漏洞利用与提权

任务二&#xff1a;系统漏洞利用与提权 任务环境说明&#xff1a; 服务器场景&#xff1a;PYsystem0033 服务器场景操作系统&#xff1a;Ubuntu 服务器场景用户名:未知 密码&#xff1a;未知 1.使用nmap扫描靶机系统&#xff0c;将靶机开放的端口号按从小到大的顺序作为F…

解决Vmware上的kali找不到virtualbox上的靶机的问题

解决kali找不到靶场ip问题的完整方法 1.配置靶机2.配置kali的虚拟网络3.配置kali中的eth0网络 1.配置靶机 靶机部署在Virtualbox上对其进行网络配置&#xff0c;选择连接方式为仅主机&#xff08;Host-Only&#xff09;网络。 2.配置kali的虚拟网络 在编辑中选择虚拟网络配…

chatgpt赋能python:Python中浮点数的表示方法

Python中浮点数的表示方法 在Python中&#xff0c;浮点数是一种数字类型&#xff0c;用于表示带有小数点的数值。但是&#xff0c;由于计算机在表示浮点数时存在精度限制&#xff0c;因此需要特别注意。本文将介绍Python中浮点数的表示方法及其可能导致的错误。 Python中浮点…

陕西发布!陕西省重点实验室申报条件类别、认定程序要求

本文整理了陕西省重点实验室申报条件&#xff0c;认定材料等相关内容&#xff0c;感兴趣的朋友快跟小编一起来看看吧&#xff01; 一、总体思路 本次省重点实验室布局建设工作以填补我省优势学科领域下无省级及以上科学与工程研究类科技创新基地的空白为主,同时兼顾前沿、新兴、…

MySQL基础- 多表查询 和 事务

目录 多表查询多表关系多表查询概述多表查询的分类内连接外连接自连接联合查询union&#xff0c;union all子查询标量子查询列子查询行子查询表子查询 综合练习小结 事务事务简介事务的操作四大特性ACID并发事务问题事务的隔离级别小结 多表查询 之前的SQL语句里的DQL只能进行…