WebRTC专题开嗨鸭 !!!
一、 WebRTC 线程模型
1、WebRTC中线程模型和常见线程模型介绍
2、WebRTC网络PhysicalSocketServer之WSAEventselect模型使用
二、 WebRTC媒体协商
三、 WebRTC 音频数据采集
1、WebRTC源码之音频设备播放流程源码分析
2、WebRTC源码之音频设备的录制流程源码分析
四、 WebRTC 音频引擎(编解码和3A算法)
五、 WebRTC 视频数据采集
1、WebRTC源码之摄像头视频数据采集源码分析
六、 WebRTC 视频引擎( 编解码)
七、 WebRTC 网络传输
1、WebRTC的ICE之STUN协议
2、WebRTC的ICE之Dtls/SSL/TLSv1.x协议详解
八、 WebRTC服务质量(Qos)
1、WebRTC中RTCP协议详解
2、WebRTC中RTP协议详解
3、WebRTC之NACK、RTX 在什么时机判断丢包发送NACK请求和RTX丢包重传
4、WebRTC源码之视频质量统计数据的数据结构分析
九、 NetEQ
十、 Simulcast与SVC
WebRTC视频采集流程图
IPin::QueryDirection方法
HRESULT QueryDirection([out] PIN_DIRECTION* pPinDir);
参数1: 获取Pin的方向 PINDIR_INPUT/PINDIR_OUT
WebRTC中使用 IKsPropertySet::Get方法
HRESULT Get([in] REFGUID rguidPropSet/*要访问的属性集*/,
[in] ULONG ulId /*要访问的属性集中的某一项*/,
[out] LPVOID pInstanceData/*实例数据*/,
[out] ULONG ulInstanceLength/*实例数据长度*/,
[out] LPVOID pPropertyData/*读到的属性数据*/,
[out] ULONG ulDataLength/*存放属性数据的长度*/,
[out] PULONG pulBytesReturened/*真正读到的属性数据长度*/)
// Provides a reference count implementation for COM (IUnknown derived) classes.
// The implementation uses atomics for managing the ref count.
template <class T>
class ComRefCount : public T {
public:
ComRefCount() {}
template <class P0>
explicit ComRefCount(P0&& p0) : T(std::forward<P0>(p0)) {}
STDMETHOD_(ULONG, AddRef)() override {
ref_count_.IncRef();
return 1;
}
STDMETHOD_(ULONG, Release)() override {
const auto status = ref_count_.DecRef();
if (status == rtc::RefCountReleaseStatus::kDroppedLastRef) {
delete this;
return 0;
}
return 1;
}
protected:
~ComRefCount() {}
private:
webrtc::webrtc_impl::RefCounter ref_count_{0};
};
// Create the sink filte used for receiving Captured frames.
sink_filter_ = new ComRefCount<CaptureSinkFilter>(this);
input_pin_ = (new ComRefCount<CaptureInputPin>(this))
// 1、是一个Filter
class ComRefCount : public CaptureSinkFilter {
public:
ComRefCount() {}
explicit ComRefCount(VideoCaptureDS&& p0) : CaptureSinkFilter(std::forward<VideoCaptureDS>(p0)) {}
STDMETHOD_(ULONG, AddRef)() override {
ref_count_.IncRef();
return 1;
}
STDMETHOD_(ULONG, Release)() override {
const auto status = ref_count_.DecRef();
if (status == rtc::RefCountReleaseStatus::kDroppedLastRef) {
delete this;
return 0;
}
return 1;
}
protected:
~ComRefCount() {}
private:
webrtc::webrtc_impl::RefCounter ref_count_{0};
};
/
// 2. 是一个Pin
class ComRefCount : public CaptureInputPin {
public:
ComRefCount() {}
explicit ComRefCount(CaptureSinkFilter&& p0) : CaptureInputPin(std::forward<CaptureSinkFilter>(p0)) {}
STDMETHOD_(ULONG, AddRef)() override {
ref_count_.IncRef();
return 1;
}
STDMETHOD_(ULONG, Release)() override {
const auto status = ref_count_.DecRef();
if (status == rtc::RefCountReleaseStatus::kDroppedLastRef) {
delete this;
return 0;
}
return 1;
}
protected:
~ComRefCount() {}
private:
webrtc::webrtc_impl::RefCounter ref_count_{0};
};
typedef struct _PinInfo
{
IBaseFilter *pFilter; // Filter指针
PIN_DIRECTION dir; // Pin的方向
WCHAR achName[ 128 ]; // Pin的名子
} PIN_INFO;
Filter与FilterGraph 关系图
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-4i0NW6DK-1662190677029)(./img/filter_and_filtergraph.jpg)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-kJEEYCVw-1662190677030)(./img/filter_connect.jpg)]
WebRTC中底层CapturedFilter拿到数据, 然后应用层从SinkFilter获取数据
IFilterGraph : public IUnknown
{
public:
virtual HRESULT STDMETHODCALLTYPE AddFilter(
/* [in] */ IBaseFilter *pFilter,
/* [string][in] */ LPCWSTR pName) = 0;
virtual HRESULT STDMETHODCALLTYPE RemoveFilter(
/* [in] */ IBaseFilter *pFilter) = 0;
virtual HRESULT STDMETHODCALLTYPE EnumFilters(
/* [annotation][out] */
_Out_ IEnumFilters **ppEnum) = 0;
virtual HRESULT STDMETHODCALLTYPE FindFilterByName(
/* [string][in] */ LPCWSTR pName,
/* [annotation][out] */
_Out_ IBaseFilter **ppFilter) = 0;
// 两个Filter连接在一起的api
// TODO@chensong 20220828 将CapturePin和 sinkPin连接起来的api
// param1 : 前一个Filter的输出Pin
// param2 : 后一个Filter的输入Pin
// param3 : AM_MEDIA_TYPE等于DMO_MEDIA_TYPE
virtual HRESULT STDMETHODCALLTYPE ConnectDirect(
/* [in] */ IPin *ppinOut,
/* [in] */ IPin *ppinIn,
/* [annotation][unique][in] */
_In_opt_ const AM_MEDIA_TYPE *pmt) = 0;
virtual HRESULT STDMETHODCALLTYPE Reconnect(
/* [in] */ IPin *ppin) = 0;
virtual HRESULT STDMETHODCALLTYPE Disconnect(
/* [in] */ IPin *ppin) = 0;
virtual HRESULT STDMETHODCALLTYPE SetDefaultSyncSource( void) = 0;
};
typedef struct _AMMediaType
{
GUID majortype; // 流的主类型GUID
GUID subtype; // 流的子类型GUID
BOOL bFixedSizeSamples; // 采样算法是固定大小,音频为TRUE
BOOL bTemporalCompression; // 时域压缩
ULONG lSampleSize; // 以字节为单位的采样大小
GUID formattype; // 数据格式类型、音频为WAVEFORMATEX
IUnknown *pUnk; // 未使用
ULONG cbFormat; // 不同媒体类型格式块的大小
/* [size_is] */ BYTE *pbFormat; // 根据cbFormat决定该字段
} AM_MEDIA_TYPE;
FilterGraph调用输出Pin的Connect方法、连接输入Pin
之后调用输入Pin的ReceivConnection方法, 是否允许双方的连接
如果ReceivConnection接受连接,则两个Pin连接成功
Complete Type : 指定了具体的媒体类型 : 举例子: I420格式, rgba格式 等等
Parial Media Type : 部分类型为GUID_NULL、表示任意类型
No Media Type: 传入NULL, 表示两个Pin可以接受任意媒体类型
两个Filter交换媒体数据的机制称为transport
通常两个Filter之间通过本地内存来交换数据
有两种本地内存交换数据的方式: push和pull
push模式: 源Filter使用IMeminputPin, push数据到下游Filter
pull模式: 下游Filter向源Filter请求,通过IAsyncReader获取数据
WebRTC使用的是push模式
负责分配内存Buffer的对象称为allocator
allocator支持IMemAllocator接口
两个Pin共享同一个allocator
无论谁提供allocator,最终由输出Pin决定使用那个allocator
输出Pin还决定allocator的属性
输出Pin调用输入Pin的GetAllocatorRequirements获取buffer大小
输出Pin调用输入Pin的GetAllocator获取allocator
输入Pin调用NotifyAllocator通知输入Pin所做的选择
当流开始或停止时,输出Pin负责提交或者撤销allocator
有两种数据流: 媒体数据和控制数据
媒体数据向下游传播,控制数据向上游传播
音频、视频等都属于媒体数据
flush、流结束通知等都属于控制数据
输出Pin调用输入Pin的Receive和ReciveMultiple投递采样
如果Pin可以阻塞, ReceivecanBlock能返回S_OK
但通信情况下不应该产生阻塞
六大步骤
该接口用于报告/设置设备支持的格式和能力
CaptureFilter的CapturePin和PreviewPin支持该接口
GetNumberOfCapabilities获取Pin支持的Capa数量
GetStreamCaps获取某个具体Capability
SetFormat用于设置硬件设备的输出格式
MIDL_INTERFACE("C6E13340-30AC-11d0-A18C-00A0C9118956")
IAMStreamConfig : public IUnknown
{
public:
virtual HRESULT STDMETHODCALLTYPE SetFormat(
/* [in] */ AM_MEDIA_TYPE *pmt) = 0;
virtual HRESULT STDMETHODCALLTYPE GetFormat(
/* [annotation][out] */
_Out_ AM_MEDIA_TYPE **ppmt) = 0;
virtual HRESULT STDMETHODCALLTYPE GetNumberOfCapabilities(
/* [annotation][out] */
_Out_ int *piCount/*支持哪些Capabilities*/,
/* [annotation][out] */
_Out_ int *piSize/*每个Capabilities的大小*/) = 0;
virtual HRESULT STDMETHODCALLTYPE GetStreamCaps(
/* [in] */ int iIndex/*指定获取某个Index的Capability, 从0开始*/,
/* [annotation][out] */
_Out_ AM_MEDIA_TYPE **ppmt/*该方法分配且用媒体类型填充的*/,
/* [annotation][out] */
_Out_ BYTE *pSCC/*调用者分配 VIDEO_STREAM_CONFIG_CAPS*/) = 0;
};
该接口用于控制视频采集操作, 如枚举帧率于图像方向
GetFrameRateList用于获取采集帧率列表
MIDL_INTERFACE("6a2e0670-28e4-11d0-a18c-00a0c9118956")
IAMVideoControl : public IUnknown
{
public:
virtual HRESULT STDMETHODCALLTYPE GetCaps(
/* [in] */ IPin *pPin,
/* [annotation][out] */
_Out_ long *pCapsFlags) = 0;
virtual HRESULT STDMETHODCALLTYPE SetMode(
/* [in] */ IPin *pPin,
/* [in] */ long Mode) = 0;
virtual HRESULT STDMETHODCALLTYPE GetMode(
/* [in] */ IPin *pPin,
/* [annotation][out] */
_Out_ long *Mode) = 0;
virtual HRESULT STDMETHODCALLTYPE GetCurrentActualFrameRate(
/* [in] */ IPin *pPin,
/* [annotation][out] */
_Out_ LONGLONG *ActualFrameRate) = 0;
virtual HRESULT STDMETHODCALLTYPE GetMaxAvailableFrameRate(
/* [in] */ IPin *pPin,
/* [in] */ long iIndex,
/* [in] */ SIZE Dimensions,
/* [annotation][out] */
_Out_ LONGLONG *MaxAvailableFrameRate) = 0;
virtual HRESULT STDMETHODCALLTYPE GetFrameRateList(
/* [in] */ IPin *pPin /*查询帧率的Pin*/,
/* [in] */ long iIndex /*查询帧率格式的索引值*/,
/* [in] */ SIZE Dimensions /*以像素为单位图像的大小*/,
/* [annotation][out] */
_Out_ long *ListSize/*用于存放帧率列表元素个数*/,
/* [annotation][out] */
_Out_ LONGLONG **FrameRates/*存放帧率数组的地址指针*/) = 0;
};
WebRTC源码分析地址:https://github.com/chensongpoixs/cwebrtc