【WebRTC】视频编码链路中各个类的简单分析——VideoStreamEncoder

目录

  • 1.视频流编码器(VideoStreamEncoder)
    • 1.1 视频流编码器接口(VideoStreamEncoderInterface)
    • 1.2 资源类(Resource)
    • 1.3 已编码图像的回调(EncodedImageCallback)
    • 1.4 视频资源监听器(VideoSourceRestrictionsListener)

1.视频流编码器(VideoStreamEncoder)

VideoStreamEncoder是编码流程当中至关重要的一个类,它实现的功能包括:
(1)开始编码器,暂停编码器,停止编码器
(2)设置编码器(根据config)
(3)调控编码参数,如码控参数,帧率等等
(4)调控前向纠错器
(5)视频帧的传输和接收
(6)根据网络环境,调控丢帧
(7)根据编码情况,调控屯帧
(8)根据情况,调控编码质量(因为网络环境,编码器情况,可能会调整编码策略,影响编码质量)
(9)对已编码图像进行后处理
(10)统计编码信息

可以说,在视频编码流程中,VideoStreamEncoder是获取了视频帧之后,第一个会使用到的类。这个类十分综合,大多数的编码过程都与这个类相关,所以其声明比较长,这里做简单记录,深入到每个模块之后再分析。

// VideoStreamEncoder represent a video encoder that accepts raw video frames as
// input and produces an encoded bit stream.
// Usage:
//  Instantiate.
//  Call SetSink.
//  Call SetSource.
//  Call ConfigureEncoder with the codec settings.
//  Call Stop() when done.
/*
	VideoStreamEncoder表示了一个视频编码器,它接收原始视频帧作为输入并且产生编码后的比特流
	用法:
	(1) 创建实例
	(2) SetSink,设置接收器
	(3) SetSource,设置资源
	(4) ConfigureEncoder,配置编码器参数
	(5) Stop(),结束流程
*/
class VideoStreamEncoder : public VideoStreamEncoderInterface,
                           private EncodedImageCallback,
                           public VideoSourceRestrictionsListener {
 public:
  // TODO(bugs.webrtc.org/12000): Reporting of VideoBitrateAllocation is being
  // deprecated. Instead VideoLayersAllocation should be reported.
  enum class BitrateAllocationCallbackType {
    kVideoBitrateAllocation,
    kVideoBitrateAllocationWhenScreenSharing,
    kVideoLayersAllocation
  };
  VideoStreamEncoder(
      const Environment& env,
      uint32_t number_of_cores,
      VideoStreamEncoderObserver* encoder_stats_observer,
      const VideoStreamEncoderSettings& settings,
      std::unique_ptr<OveruseFrameDetector> overuse_detector,
      std::unique_ptr<FrameCadenceAdapterInterface> frame_cadence_adapter,
      std::unique_ptr<webrtc::TaskQueueBase, webrtc::TaskQueueDeleter>
          encoder_queue,
      BitrateAllocationCallbackType allocation_cb_type,
      webrtc::VideoEncoderFactory::EncoderSelectorInterface* encoder_selector =
          nullptr);
  ~VideoStreamEncoder() override;

  VideoStreamEncoder(const VideoStreamEncoder&) = delete;
  VideoStreamEncoder& operator=(const VideoStreamEncoder&) = delete;
  // 添加自适应资源
  void AddAdaptationResource(rtc::scoped_refptr<Resource> resource) override;
  // 获取自适应资源
  std::vector<rtc::scoped_refptr<Resource>> GetAdaptationResources() override;
  // 设置资源
  void SetSource(rtc::VideoSourceInterface<VideoFrame>* source,
                 const DegradationPreference& degradation_preference) override;
  // 设置接收器
  void SetSink(EncoderSink* sink, bool rotation_applied) override;

  // TODO(perkj): Can we remove VideoCodec.startBitrate ?
  // 设置起始码率
  void SetStartBitrate(int start_bitrate_bps) override;
  // 设置前向纠错控制器
  void SetFecControllerOverride(
      FecControllerOverride* fec_controller_override) override;
  // 配置编码器
  void ConfigureEncoder(VideoEncoderConfig config,
                        size_t max_data_payload_length) override;
  void ConfigureEncoder(VideoEncoderConfig config,
                        size_t max_data_payload_length,
                        SetParametersCallback callback) override;

  // Permanently stop encoding. After this method has returned, it is
  // guaranteed that no encoded frames will be delivered to the sink.
  // 永久性停止编码,此方法返回后,保证不会有编码后的帧传递给接收器
  void Stop() override;
  // 传递关键帧
  void SendKeyFrame(const std::vector<VideoFrameType>& layers = {}) override;
  // 检测到丢包,需要调整编码策略,例如降低分辨率或帧率
  void OnLossNotification(
      const VideoEncoder::LossNotification& loss_notification) override;
  // 检测到码率变化,需要调整编码参数
  void OnBitrateUpdated(DataRate target_bitrate,
                        DataRate stable_target_bitrate,
                        DataRate target_headroom,
                        uint8_t fraction_lost,
                        int64_t round_trip_time_ms,
                        double cwnd_reduce_ratio) override;
  // 根据拥塞窗口减少比例,来调整目标比特率,避免丢包和网络拥塞
  DataRate UpdateTargetBitrate(DataRate target_bitrate,
                               double cwnd_reduce_ratio);

 protected:
  friend class VideoStreamEncoderFrameCadenceRestrictionTest;

  // Used for testing. For example the `ScalingObserverInterface` methods must
  // be called on `encoder_queue_`.
  TaskQueueBase* encoder_queue() { return encoder_queue_.get(); }
  // 检测到视频限制性资源发生变化
  void OnVideoSourceRestrictionsUpdated(
      VideoSourceRestrictions restrictions,
      const VideoAdaptationCounters& adaptation_counters,
      rtc::scoped_refptr<Resource> reason,
      const VideoSourceRestrictions& unfiltered_restrictions) override;

  // Used for injected test resources.
  // TODO(eshr): Move all adaptation tests out of VideoStreamEncoder tests.
  // 注入自适应资源,这里与AddAdaptationResource不同,还需要入参reason,即更加
  // 精准的调控
  void InjectAdaptationResource(rtc::scoped_refptr<Resource> resource,
                                VideoAdaptationReason reason);
  // 注入自适应限制
  void InjectAdaptationConstraint(AdaptationConstraint* adaptation_constraint);

  void AddRestrictionsListenerForTesting(
      VideoSourceRestrictionsListener* restrictions_listener);
  void RemoveRestrictionsListenerForTesting(
      VideoSourceRestrictionsListener* restrictions_listener);

 private:
  class CadenceCallback : public FrameCadenceAdapterInterface::Callback {
   public:
    explicit CadenceCallback(VideoStreamEncoder& video_stream_encoder)
        : video_stream_encoder_(video_stream_encoder) {}
    // FrameCadenceAdapterInterface::Callback overrides.
    // 下面是对FrameCadenceAdapterInterface::Callback当中函数的重载
    void OnFrame(Timestamp post_time,
                 bool queue_overload,
                 const VideoFrame& frame) override {
      // 使用VideoStreamEncoder当中的OnFrame()函数重载
      video_stream_encoder_.OnFrame(post_time, queue_overload, frame);
    }
    void OnDiscardedFrame() override {
      video_stream_encoder_.OnDiscardedFrame();
    }
    void RequestRefreshFrame() override {
      video_stream_encoder_.RequestRefreshFrame();
    }

   private:
    VideoStreamEncoder& video_stream_encoder_;
  };
  // VideoFrameInfo记录长宽,是否是纹理等信息
  class VideoFrameInfo {
   public:
    VideoFrameInfo(int width, int height, bool is_texture)
        : width(width), height(height), is_texture(is_texture) {}
    int width;
    int height;
    bool is_texture;
    int pixel_count() const { return width * height; }
  };
  // 编码速率设定
  struct EncoderRateSettings {
    EncoderRateSettings();
    EncoderRateSettings(const VideoBitrateAllocation& bitrate,
                        double framerate_fps,
                        DataRate bandwidth_allocation,
                        DataRate encoder_target,
                        DataRate stable_encoder_target);
    bool operator==(const EncoderRateSettings& rhs) const;
    bool operator!=(const EncoderRateSettings& rhs) const;
    // 码率控制参数
    VideoEncoder::RateControlParameters rate_control;
    // This is the scalar target bitrate before the VideoBitrateAllocator, i.e.
    // the `target_bitrate` argument of the OnBitrateUpdated() method. This is
    // needed because the bitrate allocator may truncate the total bitrate and a
    // later call to the same allocator instance, e.g.
    // |using last_encoder_rate_setings_->bitrate.get_sum_bps()|, may trick it
    // into thinking the available bitrate has decreased since the last call.
    /*
		这是`VideoBitrateAllocator`之前的标量目标比特率,即`OnBitrateUpdated()`方法
		的`target_bitrate`参数。这是因为比特率分配器可能会截断总比特率,而在稍后对同一
		分配器实例的调用中,例如`|using last_encoder_rate_setings_->bitrate.get_sum_bps()|`,
		可能会误导它认为自上次调用以来可用的比特率已经减少。
	*/
    DataRate encoder_target;
    DataRate stable_encoder_target;
  };

  class DegradationPreferenceManager;
  // 重新配置编码器
  void ReconfigureEncoder() RTC_RUN_ON(encoder_queue_);
  // 检测到编码器配置发生变化
  void OnEncoderSettingsChanged() RTC_RUN_ON(encoder_queue_);
  // 检测到帧
  void OnFrame(Timestamp post_time,
               bool queue_overload,
               const VideoFrame& video_frame);
  // 检测到丢弃帧
  void OnDiscardedFrame();
  // 需要刷新帧
  void RequestRefreshFrame();
  // 可能会编码视频帧
  void MaybeEncodeVideoFrame(const VideoFrame& frame,
                             int64_t time_when_posted_in_ms);
  // 编码视频帧
  void EncodeVideoFrame(const VideoFrame& frame,
                        int64_t time_when_posted_in_ms);
  // Indicates whether frame should be dropped because the pixel count is too
  // large for the current bitrate configuration.
  // 在当前码率配置下,根据pixle count来检查当前帧是否需要被丢弃
  bool DropDueToSize(uint32_t pixel_count) const RTC_RUN_ON(encoder_queue_);

  // Implements EncodedImageCallback.
  // 接收编码器编码之后的图像
  EncodedImageCallback::Result OnEncodedImage(
      const EncodedImage& encoded_image,
      const CodecSpecificInfo* codec_specific_info) override;
  // 丢帧的回调函数,需要给出reason
  void OnDroppedFrame(EncodedImageCallback::DropReason reason) override;
  // 暂停编码器
  bool EncoderPaused() const;
  // 标记丢帧开始
  void TraceFrameDropStart();
  // 标记丢帧结束
  void TraceFrameDropEnd();

  // Returns a copy of `rate_settings` with the `bitrate` field updated using
  // the current VideoBitrateAllocator.
  // 返回一个`rate_settings`的副本,其中`bitrate`字段已使用当前的`VideoBitrateAllocator`更新。
  EncoderRateSettings UpdateBitrateAllocation(
      const EncoderRateSettings& rate_settings) RTC_RUN_ON(encoder_queue_);
  // 获取输入帧率Fps
  uint32_t GetInputFramerateFps() RTC_RUN_ON(encoder_queue_);
  // 设置编码器速率
  void SetEncoderRates(const EncoderRateSettings& rate_settings)
      RTC_RUN_ON(encoder_queue_);
  // 进行编码后处理
  void RunPostEncode(const EncodedImage& encoded_image,
                     int64_t time_sent_us,
                     int temporal_index,
                     DataSize frame_size);
  // 释放编码器
  void ReleaseEncoder() RTC_RUN_ON(encoder_queue_);
  // After calling this function `resource_adaptation_processor_` will be null.
  // 关闭资源自适应队列,resource_adaptation_processor会设置为null
  void ShutdownResourceAdaptationQueue();
  // 申请更换编码器
  void RequestEncoderSwitch() RTC_RUN_ON(encoder_queue_);

  // Augments an EncodedImage received from an encoder with parsable
  // information.
  // 修改或增强已编码图像
  EncodedImage AugmentEncodedImage(
      const EncodedImage& encoded_image,
      const CodecSpecificInfo* codec_specific_info);
  // 处理已丢弃的帧
  void ProcessDroppedFrame(const VideoFrame& frame,
                           VideoStreamEncoderObserver::DropReason reason)
      RTC_RUN_ON(encoder_queue_);

  const Environment env_;
  TaskQueueBase* const worker_queue_;

  const int number_of_cores_;
  // sink是接收器
  EncoderSink* sink_ = nullptr;
  // VideoStreamEncoder配置
  const VideoStreamEncoderSettings settings_;
  // 比特分配回调的类型
  const BitrateAllocationCallbackType allocation_cb_type_;
  // 码率控制配置
  const RateControlSettings rate_control_settings_;

  webrtc::VideoEncoderFactory::EncoderSelectorInterface* const
      encoder_selector_from_constructor_;
  std::unique_ptr<VideoEncoderFactory::EncoderSelectorInterface> const
      encoder_selector_from_factory_;
  // Pointing to either encoder_selector_from_constructor_ or
  // encoder_selector_from_factory_ but can be nullptr.
  // 编码器选择器
  VideoEncoderFactory::EncoderSelectorInterface* const encoder_selector_;
  // 视频流编码器观察者的行为
  VideoStreamEncoderObserver* const encoder_stats_observer_;
  // Adapter that avoids public inheritance of the cadence adapter's callback
  // interface.
  // 适配器,避免了公开继承节拍适配器的回调接口。
  CadenceCallback cadence_callback_{*this};
  // Frame cadence encoder adapter. Frames enter this adapter first, and it then
  // forwards them to our OnFrame method.
  // 帧节拍编码器适配器。帧首先进入这个适配器,然后它将帧转发到我们的OnFrame方法。
  std::unique_ptr<FrameCadenceAdapterInterface> frame_cadence_adapter_
      RTC_GUARDED_BY(encoder_queue_) RTC_PT_GUARDED_BY(encoder_queue_);
  // 编码器配置项
  VideoEncoderConfig encoder_config_ RTC_GUARDED_BY(encoder_queue_);
  // 编码器
  std::unique_ptr<VideoEncoder> encoder_ RTC_GUARDED_BY(encoder_queue_)
      RTC_PT_GUARDED_BY(encoder_queue_);
  bool encoder_initialized_ = false;
  // 码率分配器
  std::unique_ptr<VideoBitrateAllocator> rate_allocator_
      RTC_GUARDED_BY(encoder_queue_) RTC_PT_GUARDED_BY(encoder_queue_);
  int max_framerate_ RTC_GUARDED_BY(encoder_queue_) = -1;

  // Set when ConfigureEncoder has been called in order to lazy reconfigure the
  // encoder on the next frame.
  // 当调用了ConfigureEncoder时设置,以便在下一帧上延迟重新配置编码器。
  bool pending_encoder_reconfiguration_ RTC_GUARDED_BY(encoder_queue_) = false;
  // Set when configuration must create a new encoder object, e.g.,
  // because of a codec change.
  // 当配置必须创建一个新的编码器对象时设置,例如,由于编解码器变更。
  bool pending_encoder_creation_ RTC_GUARDED_BY(encoder_queue_) = false;
  // 编码器配置的回调函数
  absl::InlinedVector<SetParametersCallback, 2> encoder_configuration_callbacks_
      RTC_GUARDED_BY(encoder_queue_);
  // 上一帧的信息
  std::optional<VideoFrameInfo> last_frame_info_ RTC_GUARDED_BY(encoder_queue_);
  // 边界裁剪的width和height
  int crop_width_ RTC_GUARDED_BY(encoder_queue_) = 0;
  int crop_height_ RTC_GUARDED_BY(encoder_queue_) = 0;
  // 编码器目标比特率
  std::optional<uint32_t> encoder_target_bitrate_bps_
      RTC_GUARDED_BY(encoder_queue_);
  // 最大数据负载长度
  size_t max_data_payload_length_ RTC_GUARDED_BY(encoder_queue_) = 0;
  // 上一个编码器的码率设定
  std::optional<EncoderRateSettings> last_encoder_rate_settings_
      RTC_GUARDED_BY(encoder_queue_);
  // 编码器是否停止并丢帧
  bool encoder_paused_and_dropped_frame_ RTC_GUARDED_BY(encoder_queue_) = false;

  // Set to true if at least one frame was sent to encoder since last encoder
  // initialization.
  // 如果自上次编码器初始化以来至少有一帧已发送到编码器,则设置为true
  bool was_encode_called_since_last_initialization_
      RTC_GUARDED_BY(encoder_queue_) = false;
  // 编码器是否失败
  bool encoder_failed_ RTC_GUARDED_BY(encoder_queue_) = false;

  // Used to make sure incoming time stamp is increasing for every frame.
  // 用于确保每个帧的到达时间戳都在增加。
  int64_t last_captured_timestamp_ RTC_GUARDED_BY(encoder_queue_) = 0;
  // Delta used for translating between NTP and internal timestamps.
  // 用于在NTP时间戳和内部时间戳之间转换的差值。
  const int64_t delta_ntp_internal_ms_ RTC_GUARDED_BY(encoder_queue_);
  // 最后一次记录帧日志的时间戳
  int64_t last_frame_log_ms_ RTC_GUARDED_BY(encoder_queue_);
  // 已捕捉帧的数量
  int captured_frame_count_ RTC_GUARDED_BY(encoder_queue_) = 0;
  // 由于网络阻塞丢弃的帧数量
  int dropped_frame_cwnd_pushback_count_ RTC_GUARDED_BY(encoder_queue_) = 0;
  // 由于编码器阻塞丢弃的帧数量
  int dropped_frame_encoder_block_count_ RTC_GUARDED_BY(encoder_queue_) = 0;
  // 屯帧的数量(待编码帧数量)
  std::optional<VideoFrame> pending_frame_ RTC_GUARDED_BY(encoder_queue_);
  // 屯帧的时间
  int64_t pending_frame_post_time_us_ RTC_GUARDED_BY(encoder_queue_) = 0;
  // 存储了视频帧中自上次更新以来发生变化的区域
  VideoFrame::UpdateRect accumulated_update_rect_
      RTC_GUARDED_BY(encoder_queue_);
  // 指示accumulated_update_rect_是否包含有效的更新区域信息
  bool accumulated_update_rect_is_valid_ RTC_GUARDED_BY(encoder_queue_) = true;
  // 前向纠错控制
  FecControllerOverride* fec_controller_override_
      RTC_GUARDED_BY(encoder_queue_) = nullptr;
  // 记录最近一次参数更新的时间
  std::optional<int64_t> last_parameters_update_ms_
      RTC_GUARDED_BY(encoder_queue_);
  // 记录最近一次编码操作的时间戳
  std::optional<int64_t> last_encode_info_ms_ RTC_GUARDED_BY(encoder_queue_);
  // 编码器信息
  VideoEncoder::EncoderInfo encoder_info_ RTC_GUARDED_BY(encoder_queue_);
  // 编码器,存储视频编解码器的相关配置信息,包括编解码器类型、编码设置、分辨率、帧率、比特率等参数
  VideoCodec send_codec_ RTC_GUARDED_BY(encoder_queue_);
  // 帧丢弃器
  FrameDropper frame_dropper_ RTC_GUARDED_BY(encoder_queue_);
  // If frame dropper is not force disabled, frame dropping might still be
  // disabled if VideoEncoder::GetEncoderInfo() indicates that the encoder has a
  // trusted rate controller. This is determined on a per-frame basis, as the
  // encoder behavior might dynamically change.
  /*
	如果帧丢弃器没有被强制禁用,那么即使 VideoEncoder::GetEncoderInfo() 表示编码器
	拥有一个可信的速率控制器,帧丢弃可能仍然被禁用。这是逐帧决定的,因为编码器的行为可能会动态变化。
  */
  // 强制禁用frameDropper
  bool force_disable_frame_dropper_ RTC_GUARDED_BY(encoder_queue_) = false;
  // Incremented on worker thread whenever `frame_dropper_` determines that a
  // frame should be dropped. Decremented on whichever thread runs
  // OnEncodedImage(), which is only called by one thread but not necessarily
  // the worker thread.
  /*
	每当 `frame_dropper_` 确定应该丢弃一帧时,在工作线程上增加。在运行 `OnEncodedImage()` 
	的线程上减少,这个函数只由一个线程调用,但不一定是工作线程。
  */
  std::atomic<int> pending_frame_drops_{0};

  // Congestion window frame drop ratio (drop 1 in every
  // cwnd_frame_drop_interval_ frames).
  // 拥塞窗口帧丢弃比例(每 cwnd_frame_drop_interval_ 帧丢弃 1 帧)。
  std::optional<int> cwnd_frame_drop_interval_ RTC_GUARDED_BY(encoder_queue_);
  // Frame counter for congestion window frame drop.
  // 用于拥塞窗口帧丢弃的帧计数器
  int cwnd_frame_counter_ RTC_GUARDED_BY(encoder_queue_) = 0;
  // 码率调节器
  std::unique_ptr<EncoderBitrateAdjuster> bitrate_adjuster_
      RTC_GUARDED_BY(encoder_queue_);

  // TODO(sprang): Change actually support keyframe per simulcast stream, or
  // turn this into a simple bool `pending_keyframe_request_`.
  // 下一帧的类型
  std::vector<VideoFrameType> next_frame_types_ RTC_GUARDED_BY(encoder_queue_);
  // 用于处理和写入与视频帧编码相关的元数据
  FrameEncodeMetadataWriter frame_encode_metadata_writer_{this};

  // Provides video stream input states: current resolution and frame rate.
  // 处理视频流输入状态
  VideoStreamInputStateProvider input_state_provider_;
  // 视频流适应器,用于适配不同的视频流需求,包括分辨率、帧率和比特率
  const std::unique_ptr<VideoStreamAdapter> video_stream_adapter_
      RTC_GUARDED_BY(encoder_queue_);
  // Responsible for adapting input resolution or frame rate to ensure resources
  // (e.g. CPU or bandwidth) are not overused. Adding resources can occur on any
  // thread.
  // 负责调整输入分辨率或帧率,以确保资源(如CPU或带宽)不被过度使用。增加资源可以在任何线程上进行。
  // 资源自适应处理器
  std::unique_ptr<ResourceAdaptationProcessorInterface>
      resource_adaptation_processor_ RTC_GUARDED_BY(encoder_queue_);
  // 管理质量下降的偏好设置,因为在调控编码策略时,很有可能会带来视频质量的下降,例如快速算法等
  std::unique_ptr<DegradationPreferenceManager> degradation_preference_manager_
      RTC_GUARDED_BY(encoder_queue_);
  // 自适应限制
  std::vector<AdaptationConstraint*> adaptation_constraints_
      RTC_GUARDED_BY(encoder_queue_);
  // Handles input, output and stats reporting related to VideoStreamEncoder
  // specific resources, such as "encode usage percent" measurements and "QP
  // scaling". Also involved with various mitigations such as initial frame
  // dropping.
  // The manager primarily operates on the `encoder_queue_` but its lifetime is
  // tied to the VideoStreamEncoder (which is destroyed off the encoder queue)
  // and its resource list is accessible from any thread.
  /*
	处理与VideoStreamEncoder相关的输入、输出和统计报告,特别是“编码使用百分比”测量和
	“QP缩放”等资源。还涉及各种缓解措施,如初始帧丢弃。该管理器主要在`encoder_queue_`
	上操作,但其生命周期与VideoStreamEncoder(在编码器队列外销毁)相关联,其资源列表
	可以从任何线程访问。
  */
  VideoStreamEncoderResourceManager stream_resource_manager_
      RTC_GUARDED_BY(encoder_queue_);
  // 额外的资源
  std::vector<rtc::scoped_refptr<Resource>> additional_resources_
      RTC_GUARDED_BY(encoder_queue_);
  // Carries out the VideoSourceRestrictions provided by the
  // ResourceAdaptationProcessor, i.e. reconfigures the source of video frames
  // to provide us with different resolution or frame rate.
  // This class is thread-safe.
  /*
    执行由ResourceAdaptationProcessor提供的VideoSourceRestrictions,
    即重新配置视频帧的源,为我们提供不同的分辨率或帧率。这个类是线程安全的。
  */
  VideoSourceSinkController video_source_sink_controller_
      RTC_GUARDED_BY(worker_queue_);

  // Default bitrate limits in EncoderInfoSettings allowed.
  // 默认的比特率限制
  const bool default_limits_allowed_;

  // QP parser is used to extract QP value from encoded frame when that is not
  // provided by encoder.
  // QP解析器用于在编码器未提供QP值时,从编码帧中提取QP值。
  QpParser qp_parser_;
  const bool qp_parsing_allowed_;

  // The quality convergence controller is used to determine if a codec has
  // reached its target quality. This is used for screenshare to determine when
  // there's no need to continue encoding the same repeated frame.
  /*
    质量收敛控制器用于确定一个编解码器是否已经达到了其目标质量。
    这在屏幕共享中用于判断何时不需要继续编码相同的重复帧。
  */
  QualityConvergenceController quality_convergence_controller_
      RTC_GUARDED_BY(encoder_queue_);

  // Enables encoder switching on initialization failures.
  // 在初始化失败时启用编码器切换。
  bool switch_encoder_on_init_failures_;

  const std::optional<int> vp9_low_tier_core_threshold_;
  const std::optional<int> experimental_encoder_thread_limit_;

  // This is a copy of restrictions (glorified max_pixel_count) set by
  // OnVideoSourceRestrictionsUpdated. It is used to scale down encoding
  // resolution if needed when using requested_resolution.
  //
  // TODO(webrtc:14451) Split video_source_sink_controller_
  // so that ownership on restrictions/wants is kept on &encoder_queue_, that
  // these extra copies would not be needed.
  /*
	这是由OnVideoSourceRestrictionsUpdated设置的限制(实际上是最大像素计数)的副本。
	当使用requested_resolution时,如果需要,它被用来降低编码分辨率。
	
	TODO(webrtc:14451) 分离video_source_sink_controller_,以便将限制/需求的所有权
	保留在&encoder_queue_上,这样就不需要这些额外的副本了。
  */
  std::optional<VideoSourceRestrictions> latest_restrictions_
      RTC_GUARDED_BY(encoder_queue_);

  // Used to cancel any potentially pending tasks to the worker thread.
  // Refrenced by tasks running on `encoder_queue_` so need to be destroyed
  // after stopping that queue. Must be created and destroyed on
  // `worker_queue_`.
  /*
	用于取消任何可能挂起的工作任务线程任务。被在`encoder_queue_`上运行的任务引用,
	因此需要在停止该队列后销毁。必须在`worker_queue_`上创建和销毁。
  */
  ScopedTaskSafety task_safety_;
  // 编码队列
  std::unique_ptr<TaskQueueBase, TaskQueueDeleter> encoder_queue_;

  //  Required for automatic corruption detection.
  // 自动检测数据损坏
  std::unique_ptr<FrameInstrumentationGenerator>
      frame_instrumentation_generator_;
};

1.1 视频流编码器接口(VideoStreamEncoderInterface)

VideoStreamEncoder的父类是VideoStreamEncoderInterface,这里定义了一些基础的函数。类的声明位于video/video_stream_encoder_interface.h中

// This interface represents a class responsible for creating and driving the
// encoder(s) for a single video stream. It is also responsible for adaptation
// decisions related to video quality, requesting reduced frame rate or
// resolution from the VideoSource when needed.
// TODO(bugs.webrtc.org/8830): This interface is under development. Changes
// under consideration include:
//
// 1. Taking out responsibility for adaptation decisions, instead only reporting
//    per-frame measurements to the decision maker.
//
// 2. Moving responsibility for simulcast and for software fallback into this
//    class.
/*
  这个接口代表一个负责创建和驱动单个视频流的编码器(encoder)的类。
  它还负责与视频质量相关的适应性决策,例如在需要时从 VideoSource 请求降低帧率或分辨率。
  
  TODO(bugs.webrtc.org/8830): 此接口正在开发中。正在考虑的变更包括:
  1. 移除适应性决策的责任,改为仅向决策者报告每帧的测量数据。
  2. 将simulcast(同时向多个接收者发送不同质量的视频流)和软件回退的责任转移到这个类中。
*/
class VideoStreamEncoderInterface {
 public:
  // Interface for receiving encoded video frames and notifications about
  // configuration changes.
  // 接收编码后的视频帧以及关于配置变更通知的接口
  class EncoderSink : public EncodedImageCallback {
   public:
    // 检测到编码器配置发生变化
    virtual void OnEncoderConfigurationChanged(
        std::vector<VideoStream> streams,
        bool is_svc,
        VideoEncoderConfig::ContentType content_type,
        int min_transmit_bitrate_bps) = 0;
    // 检测到码率分配发生变化
    virtual void OnBitrateAllocationUpdated(
        const VideoBitrateAllocation& allocation) = 0;
    // 检测到视频层级分配发生变化
    virtual void OnVideoLayersAllocationUpdated(
        VideoLayersAllocation allocation) = 0;
  };

  virtual ~VideoStreamEncoderInterface() = default;

  // If the resource is overusing, the VideoStreamEncoder will try to reduce
  // resolution or frame rate until no resource is overusing.
  // TODO(https://crbug.com/webrtc/11565): When the ResourceAdaptationProcessor
  // is moved to Call this method could be deleted altogether in favor of
  // Call-level APIs only
  
  // 如果资源使用过量,VideoStreamEncoder 将尝试降低分辨率或帧率,直到没有资源使用过量为止。
  // TODO(https://crbug.com/webrtc/11565): 当 ResourceAdaptationProcessor 被移动到 
  // Call 时,这个方法可能会被完全删除,转而只使用 Call 级别的 API。
  virtual void AddAdaptationResource(rtc::scoped_refptr<Resource> resource) = 0;
  virtual std::vector<rtc::scoped_refptr<Resource>>
  GetAdaptationResources() = 0;

  // Sets the source that will provide video frames to the VideoStreamEncoder's
  // OnFrame method. `degradation_preference` control whether or not resolution
  // or frame rate may be reduced. The VideoStreamEncoder registers itself with
  // `source`, and signals adaptation decisions to the source in the form of
  // VideoSinkWants.
  // TODO(bugs.webrtc.org/14246): When adaptation logic is extracted from this
  // class, it no longer needs to know the source.
  /*
	设置提供视频帧给 VideoStreamEncoder 的 OnFrame 方法的源。`degradation_preference` 
	控制是否允许降低分辨率或帧率。VideoStreamEncoder 在 `source` 中注册自己,并以 
	VideoSinkWants 的形式向源发出适应性决策信号。
    
    TODO(bugs.webrtc.org/14246): 当适应性逻辑从这个类中提取出来时,它就不再需要知道源。
  */
  virtual void SetSource(
      rtc::VideoSourceInterface<VideoFrame>* source,
      const DegradationPreference& degradation_preference) = 0;

  // Sets the `sink` that gets the encoded frames. `rotation_applied` means
  // that the source must support rotation. Only set `rotation_applied` if the
  // remote side does not support the rotation extension.
  /*
	设置接收编码帧的 `sink`。`rotation_applied` 表示源必须支持旋转。
	只有在远端不支持旋转扩展时才设置 `rotation_applied`。
  */
  virtual void SetSink(EncoderSink* sink, bool rotation_applied) = 0;

  // Sets an initial bitrate, later overriden by OnBitrateUpdated. Mainly
  // affects the resolution of the initial key frame: If incoming frames are
  // larger than reasonable for the start bitrate, and scaling is enabled,
  // VideoStreamEncoder asks the source to scale down and drops a few initial
  // frames.
  // TODO(nisse): This is a poor interface, and mixes bandwidth estimation and
  // codec configuration in an undesired way. For the actual send bandwidth, we
  // should always be somewhat conservative, but we may nevertheless want to let
  // the application configure a more optimistic quality for the initial
  // resolution. Should be replaced by a construction time setting.
  /*
	设置初始比特率,后续会被 OnBitrateUpdated 方法覆盖。主要影响初始关键帧的分辨率:
	如果传入的帧比初始比特率合理的大小要大,并且缩放被启用,VideoStreamEncoder 
	会请求源缩小尺寸并丢弃一些初始帧。
	
	TODO(nisse): 这是一个设计不佳的接口,它以不期望的方式混合了带宽估计和编解码器配置。
	对于实际的发送带宽,我们应该始终保持一定的保守性,但我们可能仍然希望允许应用程序为
	初始分辨率配置一个更乐观的质量。应该被构造时的设置所取代。
  */
  virtual void SetStartBitrate(int start_bitrate_bps) = 0;

  // Request a key frame. Used for signalling from the remote receiver with
  // no arguments and for RTCRtpSender.generateKeyFrame with a list of
  // rids/layers.
  /*
	请求一个关键帧。用于从远程接收方发出信号,无需参数,
	以及用于 RTCRtpSender.generateKeyFrame 方法,该方法带有一系列 rids/layers。
  */
  virtual void SendKeyFrame(const std::vector<VideoFrameType>& layers = {}) = 0;

  // Inform the encoder that a loss has occurred.
  // 通知编码器发生了丢包
  virtual void OnLossNotification(
      const VideoEncoder::LossNotification& loss_notification) = 0;

  // Set the currently estimated network properties. A `target_bitrate`
  // of zero pauses the encoder.
  // `stable_target_bitrate` is a filtered version of `target_bitrate`. It  is
  // always less or equal to it. It can be used to avoid rapid changes of
  // expensive encoding settings, such as resolution.
  // `link_allocation` is the bandwidth available for this video stream on the
  // network link. It is always at least `target_bitrate` but may be higher
  // if we are not network constrained.
  /*
	设置当前估计的网络属性。`target_bitrate` 为零时会暂停编码器。 `stable_target_bitrate` 
	是 `target_bitrate` 的一个过滤版本。它总是小于或等于 `target_bitrate`。它可以用来避免
	昂贵编码设置(如分辨率)的快速变化。`link_allocation` 是网络链路上可用于此视频流的带宽。
	它总是至少为 `target_bitrate`,但如果我们不受网络限制,可能会更高。
  */
  virtual void OnBitrateUpdated(DataRate target_bitrate,
                                DataRate stable_target_bitrate,
                                DataRate link_allocation,
                                uint8_t fraction_lost,
                                int64_t round_trip_time_ms,
                                double cwnd_reduce_ratio) = 0;

  // Set a FecControllerOverride, through which the encoder may override
  // decisions made by FecController.
  /*
	设置一个 FecControllerOverride,通过它编码器可以覆盖 FecController 所做的决策
  */
  virtual void SetFecControllerOverride(
      FecControllerOverride* fec_controller_override) = 0;

  // Creates and configures an encoder with the given `config`. The
  // `max_data_payload_length` is used to support single NAL unit
  // packetization for H.264.
  /*
	使用给定的 `config` 创建并配置一个编码器。`max_data_payload_length` 用于支持 H.264 
	的单个 NAL 单元打包。
  */ 
  virtual void ConfigureEncoder(VideoEncoderConfig config,
                                size_t max_data_payload_length) = 0;
  virtual void ConfigureEncoder(VideoEncoderConfig config,
                                size_t max_data_payload_length,
                                SetParametersCallback callback) = 0;

  // Permanently stop encoding. After this method has returned, it is
  // guaranteed that no encoded frames will be delivered to the sink.
  virtual void Stop() = 0;
};

1.2 资源类(Resource)

上面关于Resource类的声明位于api/adaptation/resource.h中,这里的可以表示帧率,码率等编码信息,这个类存在的意义是对这些信息进行监控,如果某个资源超出了预期范围,需要将这个资源进行调控。

// A Resource monitors an implementation-specific resource. It may report
// kOveruse or kUnderuse when resource usage is high or low enough that we
// should perform some sort of mitigation to fulfil the resource's constraints.
//
// The methods on this interface are invoked on the adaptation task queue.
// Resource usage measurements may be performed on an any task queue.
//
// The Resource is reference counted to prevent use-after-free when posting
// between task queues. As such, the implementation MUST NOT make any
// assumptions about which task queue Resource is destructed on.
/*
  1.一个资源监控特定实现的资源。当资源使用量足够高或足够低,以至于我们应该执行某种缓解
  措施以满足资源的约束时,它可能会报告kOveruse(过载)或kUnderuse(欠载)。

  2.这个接口上的方法在适应性任务队列上被调用。资源使用量的测量可以在任何任务队列上执行。

  3.资源被引用计数,以防止在任务队列之间发布时出现使用后释放的问题。因此,
  实现必须不对资源被销毁的任务队列做任何假设。
*/
class RTC_EXPORT Resource : public RefCountInterface {
 public:
  Resource();
  // Destruction may happen on any task queue.
  ~Resource() override;

  virtual std::string Name() const = 0;
  // The `listener` may be informed of resource usage measurements on any task
  // queue, but not after this method is invoked with the null argument.
  virtual void SetResourceListener(ResourceListener* listener) = 0;
};

// ResourceListenter和ResourceUsageState的声明如下
enum class ResourceUsageState {
  // Action is needed to minimze the load on this resource.
  // 过载,需要降低这个资源
  kOveruse,
  // Increasing the load on this resource is desired, if possible.
  // 欠载,需要增加这个资源
  kUnderuse,
};

RTC_EXPORT const char* ResourceUsageStateToString(
    ResourceUsageState usage_state);

class RTC_EXPORT ResourceListener {
 public:
  virtual ~ResourceListener();
  // 评估当前资源的状态
  virtual void OnResourceUsageStateMeasured(
      rtc::scoped_refptr<Resource> resource,
      ResourceUsageState usage_state) = 0;
};

1.3 已编码图像的回调(EncodedImageCallback)

EncodedImageCallback用于图像已经编码之后的回调,声明位于api/video_codecs/video_encoder.h中

class RTC_EXPORT EncodedImageCallback {
 public:
  virtual ~EncodedImageCallback() {}

  struct Result {
    enum Error {
      OK,

      // Failed to send the packet.
      ERROR_SEND_FAILED,
    };

    explicit Result(Error error) : error(error) {}
    Result(Error error, uint32_t frame_id) : error(error), frame_id(frame_id) {}

    Error error;

    // Frame ID assigned to the frame. The frame ID should be the same as the ID
    // seen by the receiver for this frame. RTP timestamp of the frame is used
    // as frame ID when RTP is used to send video. Must be used only when
    // error=OK.
    /*
		分配给帧的帧ID。该帧ID应与接收方看到的该帧的ID相同。当使用RTP发送视频时,
		帧的RTP时间戳被用作帧ID。仅当错误为OK时必须使用。
	*/
    uint32_t frame_id = 0;

    // Tells the encoder that the next frame is should be dropped.
    // 告诉编码器下一个帧应该被丢弃。
    bool drop_next_frame = false;
  };

  // Used to signal the encoder about reason a frame is dropped.
  // kDroppedByMediaOptimizations - dropped by MediaOptimizations (for rate
  // limiting purposes).
  // kDroppedByEncoder - dropped by encoder's internal rate limiter.
  // TODO(bugs.webrtc.org/10164): Delete this enum? It duplicates the more
  // general VideoStreamEncoderObserver::DropReason. Also,
  // kDroppedByMediaOptimizations is not produced by any encoder, but by
  // VideoStreamEncoder.
  /*
	用于向编码器发出信号,说明帧被丢弃的原因。
    kDroppedByMediaOptimizations: 由 MediaOptimizations 丢弃(出于速率限制的目的)。
    kDroppedByEncoder: 由编码器的内部速率限制器丢弃。
    
    TODO(bugs.webrtc.org/10164): 删除这个枚举?它与更通用的 VideoStreamEncoderObserver::DropReason 重复。此外,
  	kDroppedByMediaOptimizations 不是由任何编码器产生的,而是由 VideoStreamEncoder 产生的。
  */
  enum class DropReason : uint8_t {
    kDroppedByMediaOptimizations,
    kDroppedByEncoder
  };

  // Callback function which is called when an image has been encoded.
  // 在一帧被编码之后会被调用
  virtual Result OnEncodedImage(
      const EncodedImage& encoded_image,
      const CodecSpecificInfo* codec_specific_info) = 0;
  // 丢弃一帧
  virtual void OnDroppedFrame(DropReason /* reason */) {}
};

1.4 视频资源监听器(VideoSourceRestrictionsListener)

// The listener is responsible for carrying out the reconfiguration of the video
// source such that the VideoSourceRestrictions are fulfilled.
// 监听器负责执行视频源的重新配置,以满足 VideoSourceRestrictions 的要求。
class VideoSourceRestrictionsListener {
 public:
  virtual ~VideoSourceRestrictionsListener();

  // The `restrictions` are filtered by degradation preference but not the
  // `adaptation_counters`, which are currently only reported for legacy stats
  // calculation purposes.
  /*
	`restrictions` 会根据降级偏好进行过滤,但 `adaptation_counters` 不会,
	它们目前仅用于传统统计计算目的的报告。
  */
  virtual void OnVideoSourceRestrictionsUpdated(
      VideoSourceRestrictions restrictions,
      const VideoAdaptationCounters& adaptation_counters,
      rtc::scoped_refptr<Resource> reason,
      const VideoSourceRestrictions& unfiltered_restrictions) = 0;
};

总体来讲,VideoStreamEncoder完成了进行视频编码的第一道工序,包括获取视频帧,根据编码情况进行码率调整,根据网络情况丢帧等等,由于涵盖内容太广泛,一些其他很重要的类没有分析,例如DegradationPreferenceManager、QpParser等,后续单独记录

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

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

相关文章

各种数据库介绍

1. 关系型数据库&#xff08;RDBMS&#xff09; MySQL • 特点&#xff1a;开源、免费&#xff0c;社区版功能强大且稳定。支持大量的并发连接&#xff0c;常用于Web应用。 • 适用场景&#xff1a;中小型网站、博客、电商等。 PostgreSQL • 特点&#xff1a;功能丰富&#xf…

【linux】查看不同网络命名空间的端口

在部署harbor时&#xff0c;内部用的是数据库postgresql&#xff0c;端口默认是: 5432&#xff0c;一开始以为这个数据库docker容器是在本命名空间中&#xff0c;一直用ss -lnt查询系统的端口&#xff0c;找不到5432端口。但是harbor要能正常使用&#xff0c;所有怀疑harbor的容…

使用ffmpeg和mediamtx模拟多通道rtsp相机

首先下载ffmpeg&#xff0c;在windows系统上直接下载可执行文件&#xff0c;并配置环境变量即可在命令行当中调用执行。 下载地址&#xff1a; https://ffmpeg.org/再在github上下载mediamtx搭建rtsp服务器&#xff0c;使用ffmpeg将码流推流到rtsp服务器。 下载地址&#xff1…

大数据分库分表方案

分库分表介绍 分库分表应用场景 分库分表介绍 大数据分库分表是一种数据库架构技术&#xff0c;旨在应对大数据量场景下的数据库性能瓶颈。以下是对大数据分库分表的详细解释&#xff1a; 一、定义与背景 定义&#xff1a; 分库&#xff1a;将一个大型数据库按照一定的规则…

关于word 页眉页脚的一些小问题

去掉页眉底纹&#xff1a; 对文档的段落边框和底纹进行设置&#xff0c;也是页眉横线怎么删除的一种解决方式&#xff0c;具体操作如下&#xff1a; 选中页眉中的横线文本&#xff1b; 点击【开始】选项卡&#xff0c;在【段落】组中点击【边框】按钮的下拉箭头&#xff1b; …

爬虫-------字体反爬

目录 一、了解什么是字体加密 二. 定位字体位置 三. python处理字体 1. 工具库 2. 字体读取 3. 处理字体 案例1:起点 案例2:字符偏移: 5请求数据 - 发现偏移量 5.4 多套字体替换 套用模板 版本1 版本2 四.项目实战 1. 采集目标 2. 逆向结果 一、了解什么是…

Fortran安装(vscode+gcc+Python)

编写时间&#xff1a; 2024年11月7日 环境配置&#xff1a; gcc VScode Python 条件&#xff1a; Windows 10 x64 VMware虚拟机 前言 这是我出的第2个关于Fortran安装的教程&#xff0c;由于上一个方法&#xff08;你可以在本专栏里找到&#xff09;对储存空间的要求比较…

外包干了2年,快要废了。。。

先说一下自己的情况&#xff0c;普通本科毕业&#xff0c;在外包干了2年多的功能测试&#xff0c;这几年因为大环境不好&#xff0c;我整个人心惊胆战的&#xff0c;怕自己卷铺盖走人了&#xff0c;我感觉自己不能够在这样蹉跎下去了&#xff0c;长时间呆在一个舒适的环境真的会…

丹摩征文活动|详解 DAMODEL(丹摩智算)平台:为 AI 开发者量身打造的智算云服务

本文 什么是 DAMODEL&#xff08;丹摩智算&#xff09;&#xff1f;DAMODEL 的平台特性快速上手 DAMODEL 平台GPU 实例概览创建 GPU 云实例 储存选项技术支持与社区服务结语 在人工智能领域的飞速发展中&#xff0c;计算资源与平台的选择变得尤为重要。为了帮助 AI 开发者解决高…

canal1.1.7使用canal-adapter进行mysql同步数据

重要的事情说前面&#xff0c;canal1.1.8需要jdk11以上&#xff0c;大家自行选择&#xff0c;我这由于项目原因只能使用1.1.7兼容版的 文章参考地址&#xff1a; canal 使用详解_canal使用-CSDN博客 使用canal.deployer-1.1.7和canal.adapter-1.1.7实现mysql数据同步_mysql更…

Docker安装XXL-JOB分布式调度任务

一、持久化 1、下载 xxl-job 源码,找到持久化脚本 2、创建 xxl-job 数据库,将上述文件中的脚本在本库执行即可 create database xxl_job charset utf8mb4 collate utf8mb4_general_ci; 二、安装 1、下载 xxl-job 镜像 docker pull xuxueli/xxl-job-admin:2.4.1 2、创建挂…

线性表之链表详解

欢迎来到我的&#xff1a;世界 希望作者的文章对你有所帮助&#xff0c;有不足的地方还请指正&#xff0c;大家一起学习交流 ! 目录 前言线性表的概述链表的概述 内容链表的结构链表节点的定义 链表的基本功能单向链表的初始化链表的插入操作头插操作尾插操作 链表的删除操作头…

高校数字化校园中数据交换和共享平台的设计与实现(源码+定制+开发)校园数据整合平台、高校信息交换系统、校园数据整合平台、数字校园信息交换平台、校园数据集成管理

博主介绍&#xff1a; ✌我是阿龙&#xff0c;一名专注于Java技术领域的程序员&#xff0c;全网拥有10W粉丝。作为CSDN特邀作者、博客专家、新星计划导师&#xff0c;我在计算机毕业设计开发方面积累了丰富的经验。同时&#xff0c;我也是掘金、华为云、阿里云、InfoQ等平台…

【Linux驱动开发】timer库下的jiffies时间戳和延时驱动编写

【Linux驱动开发】timer库下的jiffies时间戳和延时驱动编写 gitee地址&#xff1a; https://gitee.com/Mike_Zhou_Admin/Linux_Driver_Timestamp_Driver/更新以gitee为准 文章目录 timer库时间戳函数延时函数驱动代码应用测试附录&#xff1a;嵌入式Linux驱动开发基本步骤开发…

python安装了numpy却用不了且报错的解决方案和numpy的简单应用于图像处理

1.报错情况如下&#xff1a;Error importing numpy: 解决方法&#xff1a;降低python和numpy的版本&#xff0c;我一开始下载安装的都是最新版的python和numpy&#xff0c;后来降低了版本后就不报错且可正常使用了&#xff0c;这里给出我使用的版本作为参考&#xff08;记得卸…

Java链表及源码解析

文章目录 创建一个ILindkedList接口创建方法(模拟实现链表方法)创建MyLinkedList来实现接口的方法创建链表节点addFirst方法&#xff08;新增头部属性&#xff09;addLast方法&#xff08;新增到末尾一个属性&#xff09;remove方法&#xff08;删除指定属性&#xff09;addInd…

潮玩宇宙方块兽系统开发:可定制UI与多种游戏内嵌助力个性化体验

潮玩宇宙方块兽系统开发正在推动潮玩与游戏的融合&#xff0c;通过个性化的UI设计和多游戏内嵌模式&#xff0c;为用户带来了独一无二的体验。本文将从可定制UI、多游戏内嵌功能以及系统实现等方面入手&#xff0c;探讨如何构建一个极具吸引力的潮玩宇宙方块兽系统。 一、可定制…

C#属性 Property

属性Property不是变量。 它们是由名为访问器方法来实现的一种方法。 实例属性表示的是实例的某个数据&#xff0c;通过这个数据反映实例当前的状态 静态属性表示的是类型的某个数据&#xff0c;通过这个数据反映类型当前的状态 意义&#xff1a; 防止恶意赋值(通过属性间接访问…

第八篇: 通过使用Google BigQuery进行数据批量和自动化处理

使用Python进行Google BigQuery数据批量和自动化处理 在大数据分析的日常工作中&#xff0c;定期更新、查询和处理数据是一项必不可少的任务。Google BigQuery结合Python脚本&#xff0c;可大幅简化这一过程。本文将介绍如何通过Python自动查询和更新BigQuery中的降水量数据&a…

AI - 人工智能;Ollama大模型工具;Java之SpringAI(三)

AI - 人工智能&#xff1b;Java之SpringAI&#xff08;一&#xff09; AI - 人工智能&#xff1b;Java之SpringAI&#xff08;二&#xff09; 一、Ollama 官网&#xff1a;https://ollama.com/ Ollama是一个大模型部署运行工具&#xff0c;在该工具里面可以部署运行各种大模型…