【WebRTC】视频采集模块中各个类的简单分析

news2024/11/28 9:34:47

目录

  • 1.视频采集模块中的类
    • 1.1 视频采集基础模块(VideoCaptureModule)
    • 1.2 视频采集工厂类(VideoCaptureFactory)
    • 1.3 设备信息的实现(DeviceInfoImpl)
    • 1.4 视频采集的实现(VideoCaptureImpl)
    • 1.5 Windows平台下设备信息的实现(DeviceInfoDS)
    • 1.6 Windows平台接受采集数据(CaptureInputPin和CaptureSinkFilter)
    • 1.7 Windows平台执行采集(VideoCaptureDS)
  • 2.数据图

参考:WebRTC源码分析-呼叫建立过程之四(中)(创建并添加本地视频轨到PeerConnection)

1.视频采集模块中的类

WebRTC主体是基于C++实现的框架,有必要分析其中各个类之间的继承关系。视频采集模块的代码在modules/video_capture中,其中有很多个文件,逐一分析。需要说明的是,采集模块与使用的平台强相关,不同的操作系统会使用不同的采集方式,在这里我记录的是Windows平台。因为RTC场景下Android平台和Windows平台使用量比较多,但是Android平台还涉及到JNI这一层,比Windows更复杂一些。

1.1 视频采集基础模块(VideoCaptureModule)

VideoCaptureModule的声明位于modules/video_capture/video_capture.h中,是VideoCapture模块中很基础的类,其中声明了大量纯虚函数,即VideoCaptureModule的子类必须实现这些虚函数。VideoCaptureModule中主要定义了设备信息、采集功能、旋转角度(与渲染有关)的相关函数

class VideoCaptureModule : public RefCountInterface {
 public:
  // Interface for receiving information about available camera devices.
  class DeviceInfo {
   public:
    virtual uint32_t NumberOfDevices() = 0; // 设备的数量,例如对于Android设备的Camera1和Camera2

    // Returns the available capture devices.
    // deviceNumber   - Index of capture device.
    // deviceNameUTF8 - Friendly name of the capture device.
    // deviceUniqueIdUTF8 - Unique name of the capture device if it exist.
    //                      Otherwise same as deviceNameUTF8.
    // productUniqueIdUTF8 - Unique product id if it exist.
    //                       Null terminated otherwise.
    // 获取可用的采集设备
    // deviceNumber: 设备的编号
    // deviceNameUTF8: 设备的昵称(例如用户易于识别的型号)
    // deviceUniqueIdUTF8: 设备的独有名称
    // productUniqueIdUTF8: product独有ID号
    virtual int32_t GetDeviceName(uint32_t deviceNumber,
                                  char* deviceNameUTF8,
                                  uint32_t deviceNameLength,
                                  char* deviceUniqueIdUTF8,
                                  uint32_t deviceUniqueIdUTF8Length,
                                  char* productUniqueIdUTF8 = 0,
                                  uint32_t productUniqueIdUTF8Length = 0) = 0;

    // Returns the number of capabilities this device.
    // 返回当前设备具有的能力
    virtual int32_t NumberOfCapabilities(const char* deviceUniqueIdUTF8) = 0;

    // Gets the capabilities of the named device.
    // 获取给定名称的设备的能力
    virtual int32_t GetCapability(const char* deviceUniqueIdUTF8,
                                  uint32_t deviceCapabilityNumber,
                                  VideoCaptureCapability& capability) = 0;

    // Gets clockwise angle the captured frames should be rotated in order
    // to be displayed correctly on a normally rotated display.
    // 获取顺时针旋转角度,以便在普通旋转的显示屏上正确显示捕获的帧
    // 这里应该面向的是渲染问题,因为渲染过程中的图像是需要倒置的,180°旋转
    virtual int32_t GetOrientation(const char* deviceUniqueIdUTF8,
                                   VideoRotation& orientation) = 0;

    // Gets the capability that best matches the requested width, height and
    // frame rate.
    // Returns the deviceCapabilityNumber on success.
    // 获取与请求的宽度、高度和帧率最为匹配的能力。
	// 成功时返回设备能力编号。
    virtual int32_t GetBestMatchedCapability(
        const char* deviceUniqueIdUTF8,
        const VideoCaptureCapability& requested,
        VideoCaptureCapability& resulting) = 0;

    // Display OS /capture device specific settings dialog
    // 显示操作系统/捕获设备特定的设置对话框。
    virtual int32_t DisplayCaptureSettingsDialogBox(
        const char* deviceUniqueIdUTF8,
        const char* dialogTitleUTF8,
        void* parentWindow,
        uint32_t positionX,
        uint32_t positionY) = 0;

    virtual ~DeviceInfo() {}
  };

  // Register capture data callback
  // 注册采集数据的回调函数
  virtual void RegisterCaptureDataCallback(
      rtc::VideoSinkInterface<VideoFrame>* dataCallback) = 0;
  virtual void RegisterCaptureDataCallback(
      RawVideoSinkInterface* dataCallback) = 0;

  //  Remove capture data callback
  // 移除采集数据的回调函数
  virtual void DeRegisterCaptureDataCallback() = 0;

  // Start capture device
  // 开始执行采集
  virtual int32_t StartCapture(const VideoCaptureCapability& capability) = 0;
  // 停止采集
  virtual int32_t StopCapture() = 0;

  // Returns the name of the device used by this module.
  // 获取当前设备的名称
  virtual const char* CurrentDeviceName() const = 0;

  // Returns true if the capture device is running
  // 检查当前设备是否已经开始采集
  virtual bool CaptureStarted() = 0;

  // Gets the current configuration.
  // 获取当前的配置信息
  virtual int32_t CaptureSettings(VideoCaptureCapability& settings) = 0;

  // Set the rotation of the captured frames.
  // If the rotation is set to the same as returned by
  // DeviceInfo::GetOrientation the captured frames are
  // displayed correctly if rendered.
  // 设置捕获帧的旋转角度。
  // 如果旋转角度设置与`DeviceInfo::GetOrientation`返回的值相同,则捕获的帧在渲染时能够正确显示。
  virtual int32_t SetCaptureRotation(VideoRotation rotation) = 0;

  // Tells the capture module whether to apply the pending rotation. By default,
  // the rotation is applied and the generated frame is up right. When set to
  // false, generated frames will carry the rotation information from
  // SetCaptureRotation. Return value indicates whether this operation succeeds.
  // 告诉捕获模块是否应用待定的旋转。默认情况下,
  // 旋转会被应用,生成的帧是正向的。当设置为false时,生成的帧将携带从
  // SetCaptureRotation设置的旋转信息。返回值指示此操作是否成功。
  virtual bool SetApplyRotation(bool enable) = 0;

  // Return whether the rotation is applied or left pending.
  // 返回旋转是否已应用或保持待定状态。
  virtual bool GetApplyRotation() = 0;

 protected:
  ~VideoCaptureModule() override {}
};

从继承关系来看,VideoCaptureModule的父类是RefCountInterface,这个类的声明位于api/ref_count.h中,是WebRTC当中一个至关重要的基类,因为涉及到了内存管理任务。在这个类当中,声明了两个const函数: Addref()和Release(),其中Addref()的作用是添加对当前对象的引用计数,Release()的作用是将当前对象的引用计数减1,如果减1之后为0,那么当前的对象会被释放掉。

对于这个类的简单理解是,程序在运行的过程中,有可能有很多地方需要使用到某一个对象,为此需要对当前对象进行一个使用计数,每多一个使用者,计数器加1,只有计数器为0时,该对象才可以被释放掉,这对于内存管理是至关重要的,因为极有可能会出现某一个对象在错误的时刻被释放掉,从而导致内存泄漏。这个概念可以类比FFmpeg当中的AVBufferRef,与FFmpeg不同的是,这里使用的是继承的方法实现,而FFmpeg中是结构体中将AVBufferRef作为一个成员。

// Refcounted objects should implement the following informal interface:
//
// void AddRef() const ;
// RefCountReleaseStatus Release() const;
//
// You may access members of a reference-counted object, including the AddRef()
// and Release() methods, only if you already own a reference to it, or if
// you're borrowing someone else's reference. (A newly created object is a
// special case: the reference count is zero on construction, and the code that
// creates the object should immediately call AddRef(), bringing the reference
// count from zero to one, e.g., by constructing an rtc::scoped_refptr).
//
// 您可以访问引用计数对象的成员,包括AddRef()和Release()方法,但前提是您已经拥有对它的引用,
// 或者您是借用别人的引用。(新创建的对象是一个特殊情况:在构造时引用计数为零,创建对象的代码
// 应立即调用AddRef(),将引用计数从零增加到一,例如,通过构造一个rtc::scoped_refptr)。

// AddRef() creates a new reference to the object.
//
// AddRef()会为当前对象创建一个新的引用

// Release() releases a reference to the object; the caller now has one less
// reference than before the call. Returns kDroppedLastRef if the number of
// references dropped to zero because of this (in which case the object destroys
// itself). Otherwise, returns kOtherRefsRemained, to signal that at the precise
// time the caller's reference was dropped, other references still remained (but
// if other threads own references, this may of course have changed by the time
// Release() returns).
//
// `Release()` 方法释放对对象的引用;调用者现在比调用前少了一个引用。如果因为这次调用,
// 引用计数降到了零(在这种情况下,对象将销毁自身),则返回 `kDroppedLastRef`。否则,
// 返回 `kOtherRefsRemained`,以表明在调用者引用被释放的确切时刻,还有其他引用存在
// (但如果其他线程拥有引用,这可能在 `Release()` 返回时已发生变化)。

// The caller of Release() must treat it in the same way as a delete operation:
// Regardless of the return value from Release(), the caller mustn't access the
// object. The object might still be alive, due to references held by other
// users of the object, but the object can go away at any time, e.g., as the
// result of another thread calling Release().
//
// 调用 `Release()` 的代码必须将其视为与删除操作相同的方式处理:无论 `Release()` 的
// 返回值如何,调用者都不能访问该对象。由于其他用户对对象持有的引用,对象可能仍然存活,
// 但对象随时可能消失,例如,作为另一个线程调用 `Release()` 的结果。

// Calling AddRef() and Release() manually is discouraged. It's recommended to
// use rtc::scoped_refptr to manage all pointers to reference counted objects.
// Note that rtc::scoped_refptr depends on compile-time duck-typing; formally
// implementing the below RefCountInterface is not required.
// 手动调用 `AddRef()` 和 `Release()` 是不被鼓励的。建议使用 `rtc::scoped_refptr` 
// 来管理所有指向引用计数对象的指针。请注意,`rtc::scoped_refptr` 依赖于编译时的鸭子
// 类型(duck-typing);正式实现下面的 `RefCountInterface` 并不是必需的。

enum class RefCountReleaseStatus { kDroppedLastRef, kOtherRefsRemained };

// Interfaces where refcounting is part of the public api should
// inherit this abstract interface. The implementation of these
// methods is usually provided by the RefCountedObject template class,
// applied as a leaf in the inheritance tree.
// 对于引用计数是公共API一部分的接口,应该继承这个抽象接口。这些方法的实现通常由 
// `RefCountedObject` 模板类提供,它被用作继承树中的叶节点。

class RefCountInterface {
 public:
  virtual void AddRef() const = 0;
  virtual RefCountReleaseStatus Release() const = 0;

  // Non-public destructor, because Release() has exclusive responsibility for
  // destroying the object.
 protected:
  virtual ~RefCountInterface() {}
};

1.2 视频采集工厂类(VideoCaptureFactory)

VideoCapture的工厂类VideoCaptureFactory,声明位于modules/video_capture/video_capture_factory.h中,包含了Create()和CreateDeviceInfo()函数,其中Create()用于创建一个视频采集模块对象,这个对象的类型是VideoCaptureModule,CreateDeviceInfo()用于创建设备信息,对象类型为DeviceInfo。另外,还可以通过VideoCaptureOptions辅助信息来创建这两个对象。

class RTC_EXPORT VideoCaptureFactory {
 public:
  // Create a video capture module object
  // id - unique identifier of this video capture module object.
  // deviceUniqueIdUTF8 - name of the device.
  //                      Available names can be found by using GetDeviceName
  static rtc::scoped_refptr<VideoCaptureModule> Create(
      const char* deviceUniqueIdUTF8);
  static rtc::scoped_refptr<VideoCaptureModule> Create(
      VideoCaptureOptions* options,
      const char* deviceUniqueIdUTF8);

  static VideoCaptureModule::DeviceInfo* CreateDeviceInfo();
  static VideoCaptureModule::DeviceInfo* CreateDeviceInfo(
      VideoCaptureOptions* options);

 private:
  ~VideoCaptureFactory();
};

上面使用到了VideoCaptureOptions这个类,它的声明位于modules/video_capture/video_capture_options.h中,定义了视频采集的一些参数,包括采集状态、是否使用Linux、是否使用PIPEWIRE等。

#if defined(WEBRTC_USE_PIPEWIRE)
namespace videocapturemodule {
class PipeWireSession;
}
#endif

// An object that stores initialization parameters for video capturers
class RTC_EXPORT VideoCaptureOptions {
 public:
  VideoCaptureOptions();
  VideoCaptureOptions(const VideoCaptureOptions& options);
  VideoCaptureOptions(VideoCaptureOptions&& options);
  ~VideoCaptureOptions();

  VideoCaptureOptions& operator=(const VideoCaptureOptions& options);
  VideoCaptureOptions& operator=(VideoCaptureOptions&& options);
  // 采集状态
  enum class Status {
    SUCCESS,
    UNINITIALIZED,
    UNAVAILABLE,
    DENIED,
    ERROR,
    MAX_VALUE = ERROR
  };
  // 回调函数
  class Callback {
   public:
    // 纯虚回调函数,表示当前的VideoCaptureOption是否已经初始化了
    virtual void OnInitialized(Status status) = 0; 

   protected:
    virtual ~Callback() = default;
  };
  // 初始化
  void Init(Callback* callback);

#if defined(WEBRTC_LINUX)
  // V4L2是Linux下的用于处理视频设备的驱动程序开发的内核框架
  bool allow_v4l2() const { return allow_v4l2_; }
  void set_allow_v4l2(bool allow) { allow_v4l2_ = allow; }
#endif

#if defined(WEBRTC_USE_PIPEWIRE)
  bool allow_pipewire() const { return allow_pipewire_; }
  void set_allow_pipewire(bool allow) { allow_pipewire_ = allow; }
  void set_pipewire_fd(int fd) { pipewire_fd_ = fd; }
  rtc::scoped_refptr<videocapturemodule::PipeWireSession> pipewire_session();
#endif

 private:
#if defined(WEBRTC_LINUX)
  bool allow_v4l2_ = false;
#endif
#if defined(WEBRTC_USE_PIPEWIRE)
  bool allow_pipewire_ = false;
  int pipewire_fd_ = kInvalidPipeWireFd;
  rtc::scoped_refptr<videocapturemodule::PipeWireSession> pipewire_session_;
#endif
};

1.3 设备信息的实现(DeviceInfoImpl)

在进行视频采集之前,需要先看看设备的初始化,DeviceInfoImpl是VideoCaptureModule中DeviceInfo的子类,实现了其中的一部分纯虚函数,其声明位于modules/video_capture/device_info_impl.h中。实现的函数包括:确定设备的能力、获取设备能力等功能。这里没有将所有父类函数实现,是因为并不确定是在哪种平台下面执行具体的代码,例如Windows、Linux和Android等,还需要其后的子类实现。

class DeviceInfoImpl : public VideoCaptureModule::DeviceInfo {
 public:
  DeviceInfoImpl();
  ~DeviceInfoImpl(void) override;
  // 获取该设备能力的数量
  int32_t NumberOfCapabilities(const char* deviceUniqueIdUTF8) override;
  // 获取能力
  int32_t GetCapability(const char* deviceUniqueIdUTF8,
                        uint32_t deviceCapabilityNumber,
                        VideoCaptureCapability& capability) override;
  // 获取最佳匹配的能力
  int32_t GetBestMatchedCapability(const char* deviceUniqueIdUTF8,
                                   const VideoCaptureCapability& requested,
                                   VideoCaptureCapability& resulting) override;
  // 获取旋转角
  int32_t GetOrientation(const char* deviceUniqueIdUTF8,
                         VideoRotation& orientation) override;

 protected:
  /* Initialize this object*/

  virtual int32_t Init() = 0;
  /*
   * Fills the member variable _captureCapabilities with capabilities for the
   * given device name.
   */
  virtual int32_t CreateCapabilityMap(const char* deviceUniqueIdUTF8)
      RTC_EXCLUSIVE_LOCKS_REQUIRED(_apiLock) = 0;

 protected:
  // Data members
  // 一系列的能力
  typedef std::vector<VideoCaptureCapability> VideoCaptureCapabilities;
  VideoCaptureCapabilities _captureCapabilities RTC_GUARDED_BY(_apiLock);
  Mutex _apiLock;
  char* _lastUsedDeviceName RTC_GUARDED_BY(_apiLock);
  uint32_t _lastUsedDeviceNameLength RTC_GUARDED_BY(_apiLock);
};

1.4 视频采集的实现(VideoCaptureImpl)

下面看看VideoCapture的实现,实现的类为VideoCaptureImpl,其声明位于modules/video_capture/video_capture_impl.h中,父类是VideoCaptureModule。这个类当中实现的功能有:创建VideoCapture、创建DeviceInfo、旋转角度、注册函数的回调、

namespace videocapturemodule {
// Class definitions
class RTC_EXPORT VideoCaptureImpl : public VideoCaptureModule {
 public:
  /*
   *   Create a video capture module object
   *
   *   id              - unique identifier of this video capture module object
   *   deviceUniqueIdUTF8 -  name of the device. Available names can be found by
   * using GetDeviceName
   */
  // 创建VideoCaptureModule
  static rtc::scoped_refptr<VideoCaptureModule> Create(
      const char* deviceUniqueIdUTF8);
  static rtc::scoped_refptr<VideoCaptureModule> Create(
      VideoCaptureOptions* options,
      const char* deviceUniqueIdUTF8);
  // 创建DeviceInfo
  static DeviceInfo* CreateDeviceInfo();
  static DeviceInfo* CreateDeviceInfo(VideoCaptureOptions* options);

  // Helpers for converting between (integral) degrees and
  // VideoRotation values.  Return 0 on success.
  // 用于在(整数)度和VideoRotation值之间相互转换的助手。成功时返回0。
  static int32_t RotationFromDegrees(int degrees, VideoRotation* rotation);
  static int32_t RotationInDegrees(VideoRotation rotation, int* degrees);

  // Call backs
  // 注册采集数据的回调函数
  // 支持VideoSinkInterface和RawVideoSinkInterface两种类型
  void RegisterCaptureDataCallback(
      rtc::VideoSinkInterface<VideoFrame>* dataCallback) override;
  virtual void RegisterCaptureDataCallback(
      RawVideoSinkInterface* dataCallback) override;
  // 取消采集数据的回调函数
  void DeRegisterCaptureDataCallback() override;
  // 设置采集的角度
  int32_t SetCaptureRotation(VideoRotation rotation) override;
  bool SetApplyRotation(bool enable) override;
  bool GetApplyRotation() override;
  // 获取当前设备名称
  const char* CurrentDeviceName() const override;

  // `capture_time` must be specified in NTP time format in milliseconds.
  // 处理摄像头获取的帧(核心函数)
  int32_t IncomingFrame(uint8_t* videoFrame,
                        size_t videoFrameLength,
                        const VideoCaptureCapability& frameInfo,
                        int64_t captureTime = 0);

  // Platform dependent
  // 与平台相关的采集函数
  int32_t StartCapture(const VideoCaptureCapability& capability) override;
  int32_t StopCapture() override;
  bool CaptureStarted() override;
  int32_t CaptureSettings(VideoCaptureCapability& /*settings*/) override;

 protected:
  VideoCaptureImpl();
  ~VideoCaptureImpl() override;

  // Calls to the public API must happen on a single thread.
  // 这是一个SequenceChecker对象,用于确保公共API的调用都在同一个线程上执行。
  // 它用来检查对API的调用是否违反了单线程的约定,从而帮助防止多线程环境下的竞态条件。
  SequenceChecker api_checker_;
  // RaceChecker for members that can be accessed on the API thread while
  // capture is not happening, and on a callback thread otherwise.
  // 这是一个rtc::RaceChecker对象,用于检查在特定条件下,成员变量是否被多个线程访问。
  // 当捕获没有发生时,成员变量可以在API线程上被访问,而在捕获发生时,则可能在回调线程上被访问。
  // RaceChecker用于避免数据竞争,确保线程安全
  rtc::RaceChecker capture_checker_;
  // current Device unique name;
  // 当前设备独有名称,这里的RTC_GUARDED_BY意思是只能在api_checker_的线程中进行访问
  char* _deviceUniqueId RTC_GUARDED_BY(api_checker_);
  Mutex api_lock_;
  // Should be set by platform dependent code in StartCapture.
  // 应当在StartCapture中被设置,基于不同的平台
  VideoCaptureCapability _requestedCapability RTC_GUARDED_BY(api_checker_);

 private:
  void UpdateFrameCount();
  uint32_t CalculateFrameRate(int64_t now_ns);
  int32_t DeliverCapturedFrame(VideoFrame& captureFrame)
      RTC_EXCLUSIVE_LOCKS_REQUIRED(api_lock_);
  void DeliverRawFrame(uint8_t* videoFrame,
                       size_t videoFrameLength,
                       const VideoCaptureCapability& frameInfo,
                       int64_t captureTime)
      RTC_EXCLUSIVE_LOCKS_REQUIRED(api_lock_);

  // last time the module process function was called.
  // 上一次模块过程函数被调用的时间
  int64_t _lastProcessTimeNanos RTC_GUARDED_BY(capture_checker_);
  // last time the frame rate callback function was called.
  // 上一次帧率回调函数被调用的时间
  int64_t _lastFrameRateCallbackTimeNanos RTC_GUARDED_BY(capture_checker_);

  rtc::VideoSinkInterface<VideoFrame>* _dataCallBack RTC_GUARDED_BY(api_lock_);
  RawVideoSinkInterface* _rawDataCallBack RTC_GUARDED_BY(api_lock_);

  int64_t _lastProcessFrameTimeNanos RTC_GUARDED_BY(capture_checker_);
  // timestamp for local captured frames
  // 本地捕获帧的时间戳
  int64_t _incomingFrameTimesNanos[kFrameRateCountHistorySize] RTC_GUARDED_BY(
      capture_checker_);
  // Set if the frame should be rotated by the capture module.
  // 如果需要旋转,则由采集模块配置
  VideoRotation _rotateFrame RTC_GUARDED_BY(api_lock_);

  // Indicate whether rotation should be applied before delivered externally.
  bool apply_rotation_ RTC_GUARDED_BY(api_lock_);
};

在上面的声明中,涉及到了VideoSinkInterface和RawVideoSinkInterface两个类,这两个类都能够用于处理获取的视频帧。VideoSinkInterface的声明位于api/video/video_sink_interface.h中,RawVideoSinkInterface的声明位于modules/video_capture/raw_video_sink_interface.h中。从声明中可以看出,其中定义的核心函数分别是OnRawFrame()和OnFrame(),均为回调函数(一般带有On前缀的是回调函数)。

// 位于modules/video_capture/raw_video_sink_interface.h
class RawVideoSinkInterface {
 public:
  virtual ~RawVideoSinkInterface() = default;
  // 处理原始视频帧
  virtual int32_t OnRawFrame(uint8_t* videoFrame,
                             size_t videoFrameLength,
                             const webrtc::VideoCaptureCapability& frameInfo,
                             VideoRotation rotation,
                             int64_t captureTime) = 0;
};

// 位于api/video/video_sink_interface.h
template <typename VideoFrameT>
class VideoSinkInterface {
 public:
  virtual ~VideoSinkInterface() = default;
  // 处理视频帧
  virtual void OnFrame(const VideoFrameT& frame) = 0;

  // Should be called by the source when it discards the frame due to rate
  // limiting.
  // 当源由于速率限制丢弃帧时,应该被源调用。
  virtual void OnDiscardedFrame() {}

  // Called on the network thread when video constraints change.
  // TODO(crbug/1255737): make pure virtual once downstream project adapts.
  // 在网络线程上调用,当视频约束发生变化时。
  // TODO(crbug/1255737):一旦下游项目适应,就变为纯虚函数。
  virtual void OnConstraintsChanged(
      const webrtc::VideoTrackSourceConstraints& /* constraints */) {}
};

1.5 Windows平台下设备信息的实现(DeviceInfoDS)

老版本的Windows设备使用的是DirectShow(DS)多媒体框架,新版本的Windows设备普遍使用的是Microsoft Media Foundation(IMF)多媒体框架,新版本Windows兼容DirectShow。DeviceInfoDS描述了在Windows平台下,处理视频采集设备类的实现,其声明位于modules/video_capture/windows/device_info_ds.h

class DeviceInfoDS : public DeviceInfoImpl {
 public:
  // Factory function.
  static DeviceInfoDS* Create();

  DeviceInfoDS();
  ~DeviceInfoDS() override;
  // 设备的初始化
  int32_t Init() override;
  // 设备的数量
  uint32_t NumberOfDevices() override;

  /*
   * Returns the available capture devices.
   */
  // 获取可用设备
  int32_t GetDeviceName(uint32_t deviceNumber,
                        char* deviceNameUTF8,
                        uint32_t deviceNameLength,
                        char* deviceUniqueIdUTF8,
                        uint32_t deviceUniqueIdUTF8Length,
                        char* productUniqueIdUTF8,
                        uint32_t productUniqueIdUTF8Length) override;

  /*
   * Display OS /capture device specific settings dialog
   */
  // 显示操作系统/捕获设备特定的设置对话框。
  int32_t DisplayCaptureSettingsDialogBox(const char* deviceUniqueIdUTF8,
                                          const char* dialogTitleUTF8,
                                          void* parentWindow,
                                          uint32_t positionX,
                                          uint32_t positionY) override;

  // Windows specific

  /* Gets a capture device filter
   The user of this API is responsible for releasing the filter when it not
   needed.
   */
  // 获取一个捕获设备过滤器,使用此API的用户在不再需要该过滤器时负责释放它。
  IBaseFilter* GetDeviceFilter(const char* deviceUniqueIdUTF8,
                               char* productUniqueIdUTF8 = NULL,
                               uint32_t productUniqueIdUTF8Length = 0);
  // 获取Windows平台下设备的能力
  int32_t GetWindowsCapability(
      int32_t capabilityIndex,
      VideoCaptureCapabilityWindows& windowsCapability);
  // 获取product的ID号
  static void GetProductId(const char* devicePath,
                           char* productUniqueIdUTF8,
                           uint32_t productUniqueIdUTF8Length);

 protected:
  // 获取设备信息
  int32_t GetDeviceInfo(uint32_t deviceNumber,
                        char* deviceNameUTF8,
                        uint32_t deviceNameLength,
                        char* deviceUniqueIdUTF8,
                        uint32_t deviceUniqueIdUTF8Length,
                        char* productUniqueIdUTF8,
                        uint32_t productUniqueIdUTF8Length);
  // 创建能力图
  int32_t CreateCapabilityMap(const char* deviceUniqueIdUTF8) override
      RTC_EXCLUSIVE_LOCKS_REQUIRED(_apiLock);

 private:
  // ICreateDevEnum 接口用于创建一个设备枚举器,该枚举器可以列出系统中的所有视频捕获设备。
  // 这个变量用于访问和管理视频捕获设备。
  ICreateDevEnum* _dsDevEnum;
  // IEnumMoniker 接口用于枚举设备,它提供了迭代设备的能力。
  // 这个变量用于遍历和管理设备列表。
  IEnumMoniker* _dsMonikerDevEnum;
  // CoUninitialize 是Windows API中的一个函数,用于在应用程序结束之前清理COM库。
  // 这个变量通常用于确保在应用程序退出时正确清理COM资源。
  bool _CoUninitializeIsRequired;
  // VideoCaptureCapabilityWindows 是一个类,它封装了Windows平台上的视频捕获能力,如分辨率、帧率和格式等。
  // 这个变量用于存储和管理Windows平台上所有可用的视频捕获能力。
  std::vector<VideoCaptureCapabilityWindows> _captureCapabilitiesWindows;
};

其中使用的VideoCaptureCapabilityWindows的声明如下

struct VideoCaptureCapabilityWindows : public VideoCaptureCapability {
  uint32_t directShowCapabilityIndex;
  bool supportFrameRateControl;
  VideoCaptureCapabilityWindows() {
    directShowCapabilityIndex = 0;
    supportFrameRateControl = false;
  }
};

1.6 Windows平台接受采集数据(CaptureInputPin和CaptureSinkFilter)

从Windows平台上接受采集数据的类有两个,CaptureInputPin和CaptureSinkFilter,其中CaptureInputPin直接与Windows端口连接,更加底层,而CaptureSinkFilter会处理从Windows端口获取的帧数据,还会对连接进行操作,相对而言上层一点。CaptureInputPin声明位于modules/video_capture/windows/sink_filter_ds.h中,实现了一系列与Windows平台对接的函数,如连接、问询、接收数据等功能

// Input pin for camera input
// Implements IMemInputPin, IPin.
class CaptureInputPin : public IMemInputPin, public IPin {
 public:
  CaptureInputPin(CaptureSinkFilter* filter);
  // 根据要求设置能力
  HRESULT SetRequestedCapability(const VideoCaptureCapability& capability);

  // Notifications from the filter.
  // Filter使用的回调函数
  void OnFilterActivated();
  void OnFilterDeactivated();

 protected:
  virtual ~CaptureInputPin();

 private:
  CaptureSinkFilter* Filter() const;
  // 尝试连接
  HRESULT AttemptConnection(IPin* receive_pin, const AM_MEDIA_TYPE* media_type);
  std::vector<AM_MEDIA_TYPE*> DetermineCandidateFormats(
      IPin* receive_pin,
      const AM_MEDIA_TYPE* media_type);
  void ClearAllocator(bool decommit);
  HRESULT CheckDirection(IPin* pin) const;

  // IUnknown
  /* 
    STDMETHOD宏,它通常定义为virtual HRESULT STDMETHODCALLTYPE,其中STDMETHODCALLTYPE
    是一个调用约定宏,确保函数使用stdcall调用约定,这是COM接口的标准调用约定。
  */ 
  // 用来查询视频捕获设备是否支持特定的接口, 例如IMediaEvent或IMediaControl
  // REFIID是IID(Interface Identifier)的引用类型
  STDMETHOD(QueryInterface)(REFIID riid, void** ppv) override;

  // clang-format off
  // clang isn't sure what to do with the longer STDMETHOD() function
  // declarations.

  // IPin
  // 建立两个DirectShow过滤器之间的连接,receive_pin参数是指向接收端过滤器的IPin
  // 接口的指针,这个过滤器是数据流的接收端
  STDMETHOD(Connect)(IPin* receive_pin,
                     const AM_MEDIA_TYPE* media_type) override;
  // 
  STDMETHOD(ReceiveConnection)(IPin* connector,
                               const AM_MEDIA_TYPE* media_type) override;
  STDMETHOD(Disconnect)() override;
  STDMETHOD(ConnectedTo)(IPin** pin) override;
  STDMETHOD(ConnectionMediaType)(AM_MEDIA_TYPE* media_type) override;
  STDMETHOD(QueryPinInfo)(PIN_INFO* info) override;
  STDMETHOD(QueryDirection)(PIN_DIRECTION* pin_dir) override;
  STDMETHOD(QueryId)(LPWSTR* id) override;
  STDMETHOD(QueryAccept)(const AM_MEDIA_TYPE* media_type) override;
  STDMETHOD(EnumMediaTypes)(IEnumMediaTypes** types) override;
  STDMETHOD(QueryInternalConnections)(IPin** pins, ULONG* count) override;
  STDMETHOD(EndOfStream)() override;
  STDMETHOD(BeginFlush)() override;
  STDMETHOD(EndFlush)() override;
  STDMETHOD(NewSegment)(REFERENCE_TIME start, REFERENCE_TIME stop,
                        double rate) override;

  // IMemInputPin
  STDMETHOD(GetAllocator)(IMemAllocator** allocator) override;
  STDMETHOD(NotifyAllocator)(IMemAllocator* allocator, BOOL read_only) override;
  STDMETHOD(GetAllocatorRequirements)(ALLOCATOR_PROPERTIES* props) override;
  STDMETHOD(Receive)(IMediaSample* sample) override;
  STDMETHOD(ReceiveMultiple)(IMediaSample** samples, long count,
                             long* processed) override;
  STDMETHOD(ReceiveCanBlock)() override;
  // clang-format on

  SequenceChecker main_checker_;
  SequenceChecker capture_checker_;

  VideoCaptureCapability requested_capability_ RTC_GUARDED_BY(main_checker_);
  // Accessed on the main thread when Filter()->IsStopped() (capture thread not
  // running), otherwise accessed on the capture thread.
  VideoCaptureCapability resulting_capability_;
  DWORD capture_thread_id_ = 0;
  rtc::scoped_refptr<IMemAllocator> allocator_ RTC_GUARDED_BY(main_checker_);
  rtc::scoped_refptr<IPin> receive_pin_ RTC_GUARDED_BY(main_checker_);
  std::atomic_bool flushing_{false};
  std::atomic_bool runtime_error_{false};
  // Holds a referenceless pointer to the owning filter, the name and
  // direction of the pin. The filter pointer can be considered const.
  PIN_INFO info_ = {};
  AM_MEDIA_TYPE media_type_ RTC_GUARDED_BY(main_checker_) = {};
};

CaptureSinkFilter的定义位于modules/video_capture/windows/sink_filter_ds.h中

// Implement IBaseFilter (including IPersist and IMediaFilter).
class CaptureSinkFilter : public IBaseFilter {
 public:
  CaptureSinkFilter(VideoCaptureImpl* capture_observer);
  // 根据要求设置能力
  HRESULT SetRequestedCapability(const VideoCaptureCapability& capability);

  // Called on the capture thread.
  // 处理从摄像头获取的帧,在capture县城中执行
  void ProcessCapturedFrame(unsigned char* buffer,
                            size_t length,
                            const VideoCaptureCapability& frame_info);

  void NotifyEvent(long code, LONG_PTR param1, LONG_PTR param2);
  bool IsStopped() const;

  //  IUnknown
  // 用于接口查询。它允许客户端请求对象的其他接口。REFIID 是一个接口ID的引用
  STDMETHOD(QueryInterface)(REFIID riid, void** ppv) override;

  // IPersist
  // IPersist 接口的方法,用于获取对象的类ID。类ID是一个全局唯一标识符(GUID),
  // 用于标识对象的类。CLSID 是 GUID 类型的别名,clsid 参数是一个指向 GUID 的指针,用于存储对象的类ID
  STDMETHOD(GetClassID)(CLSID* clsid) override;

  // IMediaFilter.
  // 下面都是IMediaFilter接口的方法
  // 获取媒体过滤器的当前状态,FILTER_STATE 是一个枚举类型,表示过滤器的状态(如停止、暂停、运行等)
  STDMETHOD(GetState)(DWORD msecs, FILTER_STATE* state) override;
  // 设置同步源。IReferenceClock 是一个接口,提供时间戳和同步功能。
  STDMETHOD(SetSyncSource)(IReferenceClock* clock) override;
  // 获取当前的同步源
  STDMETHOD(GetSyncSource)(IReferenceClock** clock) override;
  // 暂停媒体流的处理
  STDMETHOD(Pause)() override;
  // 开始处理媒体流
  STDMETHOD(Run)(REFERENCE_TIME start) override;
  // 停止媒体流的处理
  STDMETHOD(Stop)() override;

  // IBaseFilter
  // 下面都是IBaseFilter接口的方法
  // 枚举一个过滤器的所有pin
  STDMETHOD(EnumPins)(IEnumPins** pins) override;
  // 根据引脚的标识符查找特定的pin
  STDMETHOD(FindPin)(LPCWSTR id, IPin** pin) override;
  // 查询过滤器的信息
  STDMETHOD(QueryFilterInfo)(FILTER_INFO* info) override;
  // 将过滤器加入到一个过滤器图(filter graph)中
  STDMETHOD(JoinFilterGraph)(IFilterGraph* graph, LPCWSTR name) override;
  // 查询过滤器的供应商信息
  STDMETHOD(QueryVendorInfo)(LPWSTR* vendor_info) override;

 protected:
  virtual ~CaptureSinkFilter();

 private:
  /*
    关于SequenceChecker的解释,WebRTC是这么说的:
    SequenceChecker 是一个辅助类,用于帮助验证类的某些方法是否在相同的任务队列或线程上被调用。
    如果一个 SequenceChecker 对象是在任务队列上创建的,那么它就与该任务队列绑定;否则,它与一个线程绑定。
  */
  SequenceChecker main_checker_; 
  const rtc::scoped_refptr<ComRefCount<CaptureInputPin>> input_pin_;
  VideoCaptureImpl* const capture_observer_;
  FILTER_INFO info_ RTC_GUARDED_BY(main_checker_) = {};
  // Set/cleared in JoinFilterGraph. The filter must be stopped (no capture)
  // at that time, so no lock is required. While the state is not stopped,
  // the sink will be used from the capture thread.
  IMediaEventSink* sink_ = nullptr;
  FILTER_STATE state_ RTC_GUARDED_BY(main_checker_) = State_Stopped;
};

1.7 Windows平台执行采集(VideoCaptureDS)

Windows平台执行采集的类为VideoCaptureDS,其父类为VideoCaptureImpl。在VideoCaptureDS中,主要实现了采集的开始/结束两个函数,即StartCapture()和StopCapture()。VideoCaptureDS

class VideoCaptureDS : public VideoCaptureImpl {
 public:
  VideoCaptureDS();

  virtual int32_t Init(const char* deviceUniqueIdUTF8);

  /*************************************************************************
   *
   *   Start/Stop
   *
   *************************************************************************/
  int32_t StartCapture(const VideoCaptureCapability& capability) override;
  int32_t StopCapture() override;

  /**************************************************************************
   *
   *   Properties of the set device
   *
   **************************************************************************/

  bool CaptureStarted() override;
  int32_t CaptureSettings(VideoCaptureCapability& settings) override;

 protected:
  ~VideoCaptureDS() override;

  // Help functions

  int32_t SetCameraOutput(const VideoCaptureCapability& requestedCapability);
  int32_t DisconnectGraph();
  HRESULT ConnectDVCamera();

  DeviceInfoDS _dsInfo RTC_GUARDED_BY(api_checker_);

  IBaseFilter* _captureFilter RTC_GUARDED_BY(api_checker_);
  IGraphBuilder* _graphBuilder RTC_GUARDED_BY(api_checker_);
  IMediaControl* _mediaControl RTC_GUARDED_BY(api_checker_);
  rtc::scoped_refptr<CaptureSinkFilter> sink_filter_
      RTC_GUARDED_BY(api_checker_);
  IPin* _inputSendPin RTC_GUARDED_BY(api_checker_);
  IPin* _outputCapturePin RTC_GUARDED_BY(api_checker_);

  // Microsoft DV interface (external DV cameras)
  IBaseFilter* _dvFilter RTC_GUARDED_BY(api_checker_);
  IPin* _inputDvPin RTC_GUARDED_BY(api_checker_);
  IPin* _outputDvPin RTC_GUARDED_BY(api_checker_);
};

2.数据图

面向Windows平台的视频帧采集流程,结合UML图,做了一张数据流图,大体分为四个步骤:
(1)从摄像头获取视频帧数据(底层实现)
(2)处理从摄像头中获取到的视频帧
(3)处理从Windows层获取到的视频帧,并且送入帧处理器中
从底层获取的帧,可以分为RawFrame和Frame,RawFrame通常是未转换格式的帧,如MJPG,而Frame通常是转换格式之后的帧,如I420格式。
(4)处理获取的帧
对于已经获取的帧,可以送入到渲染当中进行本地渲染显示,也可以送入到编码流程当中进行编码,随后发送到远端。大多数情况下,会同时进行本地显示和编码发送,所以需要以组播的方式发送到两个不同的模块当中。
在这里插入图片描述

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

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

相关文章

江协科技STM32学习- P40 硬件SPI读写W25Q64

&#x1f680;write in front&#x1f680; &#x1f50e;大家好&#xff0c;我是黄桃罐头&#xff0c;希望你看完之后&#xff0c;能对你有所帮助&#xff0c;不足请指正&#xff01;共同学习交流 &#x1f381;欢迎各位→点赞&#x1f44d; 收藏⭐️ 留言&#x1f4dd;​…

智慧场馆:安全、节能与智能化管理的未来

在当今社会&#xff0c;智慧场馆已经成为了现代场馆建设的一种重要模式。通过整合先进技术和智能系统&#xff0c;智慧场馆致力于提供全方位的解决方案&#xff0c;以实现场馆的安全性、节能性和智能化管理。本文将深入探讨智慧场馆如何实现安全、节能和全面智能化&#xff0c;…

Facebook与人工智能:推动社交媒体发展的新动力

在数字化时代的浪潮中&#xff0c;社交媒体已成为人们日常生活不可或缺的一部分。作为全球最大的社交平台之一&#xff0c;Facebook凭借其庞大的用户基础和先进的技术&#xff0c;正积极探索与人工智能&#xff08;AI&#xff09;的结合&#xff0c;以推动社交媒体的不断发展。…

【论文复现】自动化细胞核分割与特征分析

本文所涉及所有资源均在这里可获取。 作者主页&#xff1a; 七七的个人主页 文章收录专栏&#xff1a; 论文复现 欢迎大家点赞 &#x1f44d; 收藏 ⭐ 加关注哦&#xff01;&#x1f496;&#x1f496; 自动化细胞核分割与特征分析 引言效果展示HoverNet概述HoverNet原理分析整…

阿里云文本内容安全处理

1、什么是内容安全 内容安全是一款基于AI算法和云计算技术&#xff0c;对多媒体内容的不宜或违规内容提供识别和标注的产品。该产品&#xff0c;支持对各行业及业务场景下的图片、视频、文本、语音等对象进行检测&#xff0c;可以帮助您提高内容审核效率、提高平台内容质量和用…

双指针算法习题解答

1.移动零 题目链接&#xff1a;283. 移动零 - 力扣&#xff08;LeetCode&#xff09; 题目解析&#xff1a;该题要求将数组中为0的元素全部转移到数组的末尾&#xff0c;同时不能改变非零元素的相对位置。 解题思路&#xff1a;我们可以用变量dest和cur将该数组分为三个区域。…

idea、pycharm等软件的文件名红色怎么变绿色

1.问题 有时候在写完代码打算提交的时候&#xff0c;会遇到某个资源文件不是绿色的&#xff0c;不能提交 2.解决方法 右键该文件——git——添加即可 3.不同颜色含义 3.1 蓝色&#xff08;Blue&#xff09; 含义&#xff1a;文件被修改了但尚未保存。蓝色通常表示文件自上…

Python进阶之IO操作

文章目录 一、文件的读取二、文件内容的写入三、之操作文件夹四、StringIO与BytesIO 一、文件的读取 在python里面&#xff0c;可以使用open函数来打开文件&#xff0c;具体语法如下&#xff1a; open(filename, mode)filename&#xff1a;文件名&#xff0c;一般包括该文件所…

ECharts折线图背景渐变设置

目录 引入 1.在一个HTML文件中编写两个图表 2.渐变背景 引入 如何在一个HTML文件中编写两个图表&#xff1a;&#xff08;这个例子基于这个篇文章的基础&#xff09;一篇搞懂前端获取数据-CSDN博客 一个例子&#xff1a; 1.在一个HTML文件中编写两个图表 重点在于名字的不重…

基于SpringBoot的“乐校园二手书交易管理系统”的设计与实现(源码+数据库+文档+PPT)

基于SpringBoot的“乐校园二手书交易管理系统”的设计与实现&#xff08;源码数据库文档PPT) 开发语言&#xff1a;Java 数据库&#xff1a;MySQL 技术&#xff1a;SpringBoot 工具&#xff1a;IDEA/Ecilpse、Navicat、Maven 系统展示 系统首页界面图 用户注册界面图 二手…

CSS弹性布局:灵活布局的终极指南

在网页设计中&#xff0c;CSS 弹性布局&#xff08;Flexbox&#xff09;是一个不可或缺的工具。它能帮助你轻松地排列和对齐元素&#xff0c;尤其是在响应式设计中表现出色。今天&#xff0c;我们就来深入探讨一下 Flexbox 的各个属性&#xff0c;让你彻底掌握这个强大的布局工…

OpenJDK Vendor下载选择

首先JDK可以通过idea进行安装 File➡️Project Structure➡️SDK➡️Add SDK➡️Download JDK 然后在JDK版本选择时&#xff0c;Idea提供了很多版本&#xff0c;让我茫然了 OpenJDK国外厂商 供应商 说明 Amazon Corretto 亚马逊云基于OpenJDK构建&#xff0c;收费 Eclipse…

SAP-ABAP开发-ONLINE 程序、DIALOG屏幕开发

目录 一、Online 程序概览 1、程序类型 2、Online程序的主要对象 二、界面 1、SAP的屏幕开发 2、屏幕功能实现 3、界面中的事件块&#xff08;Event Block&#xff09; 4、界面的创建 三、简单界面元素 1、文本/输入框控件 2、数据检查 3、一些常用的关键字 四、复…

基于vue框架的的留守儿童帮扶系统143b5(程序+源码+数据库+调试部署+开发环境)系统界面在最后面。

系统程序文件列表 项目功能&#xff1a;留守儿童,帮扶活动,申请记录,帮扶机构,帮扶进度,帮扶人,申请加入记录,参与帮扶记录 开题报告内容 基于Vue框架的留守儿童帮扶系统开题报告 一、研究背景与意义 随着城乡经济差异的不断扩大&#xff0c;大量农村劳动力涌向城市寻求更好…

xftp连接中不成功 + sudo vim 修改sshd_config不成功的解决方法

我们使用sudo vim不成功&#xff0c;但是我们使用sudo su就可以 了&#xff01; root用户权利更大&#xff01; 喵的&#xff0c;终于成功了&#xff0c;一个xftp连接半天不成功。&#xff08;添加上面的内容就可以连接成功了↑&#xff09;

这款Chrome 插件,使浏览器页面快速滑动到最底部和最顶部,并且还能...

前言 前几日我在使用谷歌浏览器&#xff0c;也就是chrome的时候&#xff0c;浏览一个内容很长的页面&#xff0c;由于页面上的内容有前后关联&#xff0c;所以我必须不停地切换到上面和下面。这非常不方便。使我非常抓狂。后来&#xff0c;我灵机一动&#xff0c;去谷歌浏览器…

嵌入式linux中设备树控制硬件的方法

大家好,今天主要给大家分享一下,如何使用linux系统下的设备树进行硬件控制方法。 第一:linux系统中设备树驱动LED原理 在linux系统中可以使用设备树向Linux内核传递相关的寄存器地址,linux驱动中使用OF函数从设备树中获取所需的属性值,然后使用获取到的属性值来初始化相关…

2024年网鼎杯青龙组|MISC全解

转载或摘抄时请标明出处 MISC01 wdbflag{22226aba1d98c4302a6f508cad7da5d8} MISC02 一把梭工具没有任何结果&#xff0c;估计缺少符号表&#xff0c;直接strings flag > out.txt导出后慢慢找线索 在桌面上发现了png和txt文件&#xff0c;用文件名做一次筛选 第一行发现bas…

AI驱动无人驾驶:安全与效率能否兼得?

内容概要 如今&#xff0c;人工智能正以其神奇的魔力驱动着无人驾驶的浪潮&#xff0c;带来了无数令人兴奋的可能性。这一领域的最新动态显示&#xff0c;AI技术在车辆的决策过程和实时数据分析中发挥着重要作用&#xff0c;帮助车辆更聪明地应对复杂的交通环境。通过实时监测…

华为手机卸载系统应用的方法

摘要&#xff1a; 1.手机环境&#xff1a;手机需要开启开发者模式并使用usb连接电脑&#xff0c;并选择文件传输模式 2.电脑环境&#xff1a;使用鸿蒙工具箱进行傻瓜操作或安装adb工具进行命令卸载 3.鸿蒙工具箱和adb工具本质都是使用adb shell pm uninstall -k --user 0 xx…