【WebRTC】视频采集模块中各个类的简单分析
目录
- 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 GetDeviceNamestatic 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();
#endifprivate:
#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*/// 创建VideoCaptureModulestatic rtc::scoped_refptr<VideoCaptureModule> Create(const char* deviceUniqueIdUTF8);static rtc::scoped_refptr<VideoCaptureModule> Create(VideoCaptureOptions* options,const char* deviceUniqueIdUTF8);// 创建DeviceInfostatic 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 filterThe user of this API is responsible for releasing the filter when it notneeded.*/// 获取一个捕获设备过滤器,使用此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) overrideRTC_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;// IMemInputPinSTDMETHOD(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 onSequenceChecker 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 的指针,用于存储对象的类IDSTDMETHOD(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接口的方法// 枚举一个过滤器的所有pinSTDMETHOD(EnumPins)(IEnumPins** pins) override;// 根据引脚的标识符查找特定的pinSTDMETHOD(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 functionsint32_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)处理获取的帧
对于已经获取的帧,可以送入到渲染当中进行本地渲染显示,也可以送入到编码流程当中进行编码,随后发送到远端。大多数情况下,会同时进行本地显示和编码发送,所以需要以组播的方式发送到两个不同的模块当中。
相关文章:

【WebRTC】视频采集模块中各个类的简单分析
目录 1.视频采集模块中的类1.1 视频采集基础模块(VideoCaptureModule)1.2 视频采集工厂类(VideoCaptureFactory)1.3 设备信息的实现(DeviceInfoImpl)1.4 视频采集的实现(VideoCaptureImpl&#…...

【大模型系列】Grounded-VideoLLM(2024.10)
Paper:https://arxiv.org/pdf/2410.03290Github:https://github.com/WHB139426/Grounded-Video-LLMHuggingface:https://huggingface.co/WHB139426/Grounded-Video-LLMAuthor:Haibo Wang et al. 加州大学,复旦 动机&a…...

EV录屏好用吗?盘点2024年10款专业好用的录屏软件。
EV录屏的方式有很多种,它设置了很多模式,并且录制高清,可以免费使用。但是现在很多的录屏工具都有着与这个软件相似的功能,在这里我可以给大家列举一些。 1、福昕电脑录屏 这个软件为用户提供了多种录制模式,让视频录…...

Pandas | 理性判断数据是否存在缺失值的一种方法
理性判断 一般思路进一步思考df[B].explode() 一般思路 tcc.info()上述信息info显示没有缺失值 但是真实的情况还是要根据业务实际分析tcc.isnull().sum() # 和tcc.info()作用和tcc.info() 其实是一样的 进一步思考 在此过程中,我们需要检验是否存在采用别的值来表…...

ENSP (虚拟路由冗余协议)VRRP配置
VRRP(Virtual Router Redundancy Protocol,虚拟路由冗余协议)是一种用于提高网络可用性和可靠性的协议。它通过在多个路由器之间共享一个虚拟IP地址,确保即使一台路由器发生故障,网络依然能够正常运行,防止…...

move_base
move_base 官方介绍:http://wiki.ros.org/move_base 如果在仿真环境下, sensor source、odometry source 和 sensor transforms 都已提供好,我们只需要完成以下部分: 一、编写导航程序 ①创建 ROS 工作空间 和 pkg 包 mkdir -p …...

Android Intent 跳转常见系统设置
常量值描述android.settings.DATA_ROAMING_SETTINGS显示 2G/3G 选择的设置android.settings.SETTINGS显示系统设置android.settings.WIFI_SETTINGS显示设置以允许配置 Wi-Fiandroid.intent.action.POWER_USAGE_SUMMARY显示电池 … 更多GO 官网...

[复健计划][紫书]Chapter 7 暴力求解法
7.1 简单枚举 例7-1 Division uva725 输入正整数n,按从小到大的顺序输出所有形如abcde/fghij n的表达式,其中a~j恰好为数字0~9的一个排列(可以有前导0),2≤n≤79。枚举fghij,验证a…...

基于SpringBoot的社区讯息服务小程序【附源码】
基于SpringBoot的社区讯息服务小程序 效果如下: 系统登陆页面 管理员主页面 用户管理页面 社区活动管理页面 设施报修管理页面 缴费信息管理页面 用户主页面 用户登录页面 社区活动页面 研究背景 随着移动互联网技术的飞速发展,社区生活日益依赖于数字…...

springboot图书管理系统(一个简单的单体架构项目,适合小白)
期末作业 为了水一水期末作业,打算写一个简易的单体架构图书管理系统。以下为后端主要技术栈(后期可能更新,打算一个星期左右写完吧)。 springbootredismysqlspringcachespringsecurity … 数据库设计 第一次从0开始搭建后续可能还会多更新一些表。 -- 角…...

《CLR via C#》读书笔记--CLR的执行模型
将源代码编译成托管模块将托管模块合并成程序集加载公共语言运行时执行程序集的代码本机代码生成器:NGen.exeFramework 类库入门通用类型系统公共语言规范(CLS)与非托管代码的互操作性 将源代码编译程托管模块 公共语言运行时(Co…...

Javascript常见数据结构及其应用场景
Basic 以下是对JavaScript中常见数据结构及其应用场景的详细扩展: 数组(Array) 定义与特性:数组是由一组按顺序排列的值组成,每个值都有一个对应的索引(下标),可以通过索引访问和修…...

简单的签到程序 python笔记
简单的人脸识别签到程序 在看完以下代码后,略微修改一番,你就能够组装出自己的“简单的人脸识别签到程序”了。 请注意库的安装,否则会不可用。 你可以通过在cmd中使用:pip install来安装。 以下代码运行python 3.8 UI界面 使…...

30天如何成功转行成为AI产品经理?如果你也想转行到AI,赶紧进来抄作业!!!
前言 随着AI技术的快速发展,AI产品经理成为了备受瞩目的职业。如果您也想抓住这个机遇,不妨跟随这份30天快速入门指南,开始您的AI产品经理转型之旅。 一、学习路线 第一阶段(5天):初阶应用 该阶段让大家…...

基于Python+Vue开发的蛋糕商城管理系统
项目简介 该项目是基于PythonVue开发的蛋糕商城管理系统(前后端分离),这是一项为大学生课程设计作业而开发的项目。该系统旨在帮助大学生学习并掌握Python编程技能,同时锻炼他们的项目设计与开发能力。通过学习基于Python的蛋糕商…...

WSL开发--利用Git连接远程仓库(详细步骤)
这篇文章主要介绍了如何将本地项目推送到 GitLab 上,并且避免每次提交都需要输入用户名和密码。文中分步讲解了配置 GitLab SSH 密钥以及配置 Git 远程仓库地址的方法。以下是文章的优化和简洁版: 将本地项目推送到 GitLab 并配置 SSH 免密登录 为了方便…...

VLAN高级+以太网安全
VLAN聚合 MUX VLAN QinQ 以下是这三种VLAN技术的作用及其在项目中的应用实例: VLAN聚合 (VLAN Aggregation) VLAN聚合通常用于将多个VLAN数据聚合到一个物理链路上,以减少链路数量、提高链路利用率。这样可以在一个物理链路上同时传输不同VLAN的数据包&…...

R7:糖尿病预测模型优化探索
🍨 本文为🔗365天深度学习训练营 中的学习记录博客🍖 原作者:K同学啊 一、实验目的: 探索本案例是否还有进一步优化的空间 二、实验环境: 语言环境:python 3.8编译器:Jupyter notebo…...

Spring核心:探索IoC容器与依赖注入的奥秘
目录 一、什么是Spring? 二、什么是 Ioc ? 2.1 控制反转的实现方式 2.1.1 依赖注入 2.1.2 依赖查找 2.1.3 优点分析 2.2 理解 Ioc 一、什么是Spring? 我们通常所说的 Spring 指的是 Spring Framework(Spring 框架ÿ…...

15分钟学 Go 实践项目二:打造博客系统
打造博客系统 项目概述 在本项目中,我们将创建一个简单的博客系统,重点实现CRUD(创建、读取、更新、删除)操作和用户管理。这个博客系统将使用户能够发布文章,评论,并管理其个人账户信息。 目标 实现用…...

Follow软件的使用入门教程
开篇 看到很多兄弟还不知道怎么用这个当下爆火的浏览器!在这里简单给需要入门的小伙伴一些建议: 介绍 简单解释一下,RSS 意思是简易信息聚合,用户可以通过 RSS 阅读器或聚合工具自主订阅并浏览各个平台的内容源,不用…...

【IC验证】systemverilog的设计特性
systemverilog的设计特性 一.概述二.面向硬件的过程语句块1.说明2.always_comb2.always_latch3.always_ff 三.关系运算符1.说明2.例子 四.inside判定符1.说明2.例子 五.条件分支语句(1)说明(2)例子(case和unique case的…...

【点击劫持漏洞(附测试代码)】
漏洞描述 点击劫持(Clickjacking)是一种网络攻击技术,攻击者通过将一个恶意的页面或按钮隐藏在合法网站的页面下,诱使用户在不知情的情况下点击隐藏的内容,从而触发攻击者设计的操作。这种攻击通常会导致用户无意中执…...

【AD】3-4 在原理图中放置元件
1.打开原理图库,选中元件点击放置 2.点击工程右键,选择,,进行编译,点击Components,选中鼠标点击拖动即可...

协程2 --- 相关概念
文章目录 协程切换方案协程库的完善程度协程栈方案协程调度实现有栈协程与无栈协程对称协程与非对称协程 协程切换方案 具体使用和解析看栈切换那个博客 使用setjump、longjump c语言提供的方案 可参考:libmill 使用操作系统提供的api:ucontext、fiber …...

Hadoop-005-HDFS分布式文件存储原理
一、HDFS数据如何存储 分布式存储:每个服务器(节点)存储文件的一部分, 本文提到的part只是为方便理解, 指的文件部分数据, 并不是真实存在的概念 #mermaid-svg-qjJMG6r2bzRNcWkF {font-family:"trebuchet ms",verdana,arial,sans-s…...

【多线程入门篇】 创建线程以及线程的属性
大家好呀 我是浪前 今天给大家讲解的是创建线程以及线程的属性 祝愿所有点赞关注的人,身体健康,一夜暴富,升职加薪迎娶白富美!!! 点我领取迎娶白富美大礼包 🍓多线程编程: 前言: 我们为什么不用多进程?…...

三十四、Python基础语法(文件操作-上)
一、介绍 文件:可以储存在长期储存设备上的一段数据,在计算机储存的数据都是二进制的形式储存的,我们用软件打开文件不是看见0和1是因为软件会自动将二进制数据进行转换。 二、文件操作 1.打开文件 打开文件:文件是在硬盘中储…...

【大咖云集,院士出席 | ACM独立出版】第四届大数据、人工智能与风险管理国际学术会议 (ICBAR 2024,11月15-17日)--冬季主会场
第四届大数据、人工智能与风险管理国际学术会议 (ICBAR 2024)--冬季主会场 2024 4th International Conference on Big Data, Artificial Intelligence and Risk Management 官方信息 会议官网:www.icbar.net 2024 4th International Conference on Big Data, Art…...

03 Oracle进程秘籍:深度解析Oracle后台进程体系
文章目录 Oracle进程秘籍:深度解析Oracle后台进程体系一、Oracle后台进程概览1.1 DBWn(Database Writer Process)1.2 LGWR(Log Writer Process)1.3 SMON(System Monitor Process)1.4 PMON&#…...