访问 2img.ai 官网以获取更多AI/AIGC信息 访问 个人技术博客: fuqifai.github.io
本文大部分配图使用微信小程序【字形绘梦】免费生成。
AIGC技术讨论群
什么是P2P连接
PeerConnection 是 WebRTC 连接流程中无法绕过的一个类,甚至可以认为这个类是整个连接流程的 Controller。当2个用户分别在2端试图连接对方的时候,首先由信令服务器服务,使得相互可以认识到对方的存在,之后,就需要建立一个稳定的长连接,此时并没有另外一个中央服务器处理这2个客户端的共有信息,我们认为是2个Endpoint到Endpoint的连接,也称为Peer2Peer的连接,简称P2P .
WebRTC中重要的类PeerConnection负责建立这个连接。 在这个连接的过程中,程序初始化了很多很多重要的内容,包括编码器,解码器,视频音频的内容,工作流线程等。所以可以说最重要的一个类都不为过。
时序图
假定读者已经阅读过「基础知识」中的内容,对信令(signaling)服务器、ICE 服务器等概念都有所了解,那么没有什么比一张时序图更加简洁清晰的了:
这里针对时序图中的一些情况做具体说明:
- 上图不完全是 API 的调用流程,读者在编程时仍需参考 WebRTC 的文档或源码注释。
- 先进入房间的用户是发起方(Indicator),后进入房间的用户是参与者(Participant)。如果参与者进房时信令服务器已经有 offerSdp 甚至(发起方的)ICE candidate 信息了,则信令服务器可以将它们与 ICE server addr 一起返回给参与者。
- add audio & video tracks 不是连接流程中的关键步骤,也可以在 ICE 流程之后再执行。
- 在 SetLocalDescription 执行成功后,协商 SDP 和 ICE candidate 的流程便会同时开始。
- 通话双方均与选定的 ICE 服务器连接成功后,即可开始相互推流。
- 在 多人会议服务端架构 中,一般由 SFU 服务器同时充当 ICE 服务器的角色。
完整的建立P2P函数源代码
这里的函数源代码包含了很多我们产品的业务逻辑和配置,你需要自行吸收,并且可能由于WebRTC版本的差异,接口和参数会有不同。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
/// <summary>
/// 创建标准管道。
/// </summary>
/// <returns></returns>
bool QLSignalConnection::CreateP2PConnection()
{
if (mPeerConnectionFactory)
{
QL_ERROR("Already created P2P Connection Factory,will return.");
return false;
}
//global threads
//if networkThread is nullptr seems no difference
//If we create socket by ourself, it will cause except when we exit app,
//The reason might be we have to clean thread info by ourselves. so just use default
//networkThread = rtc::Thread::CreateWithSocketServer();
//networkThread->Start();
//networkThread = nullptr;
//Those are ok
globalWorkerThread = rtc::Thread::Create();
globalWorkerThread->Start();
globalSignalingThread = rtc::Thread::Create();
globalSignalingThread->Start();
//init video and audio factory
std::unique_ptr<webrtc::VideoEncoderFactory> myVideoEncoderFactory = webrtc::CreateBuiltinVideoEncoderFactory();
rtc::scoped_refptr<webrtc::AudioEncoderFactory> myAudioEncoderFactory = webrtc::CreateBuiltinAudioEncoderFactory();
//try to add some constraint but failed
//webrtc::CreatePeerConnectionFactory(rtc::Thread::Current(), rtc::Thread::Current(), rtc::Thread::Current(),nullptr,nu)
webrtc::PeerConnectionFactoryDependencies deps;
webrtc::PeerConnectionFactoryInterface::Options myOptions;
//WE want to control not adjust birrate regarding CPU usage
webrtc::PeerConnectionInterface::RTCConfiguration configuration;
//Important ,Use NVENC
if (QL::QLGlobalConfig::Get().IsUseNVENC())
{
//myVideoEncoderFactory = UniEncoderFactory::CreateUniEncoderFactory();
myVideoEncoderFactory = QL::CreateBuiltinExternalVideoEncoderFactory();
QL_LOG("Create My NVENC encoder");
}
rtc::scoped_refptr<webrtc::AudioDeviceModule> myADM = nullptr;
if (QL::QLGlobalConfig::Get().IsUseFakeADM())
{
myADM = FakeADM::Create();
if (!myADM)
{
QL_ERROR("Failed to create dummy ADM");
}
else
{
QL_LOG("Create faked ADM successfully.");
}
}
else
{
//myADM = webrtc::AudioDeviceModule::Create(webrtc::AudioDeviceModule::kPlatformDefaultAudio, webrtc::CreateDefaultTaskQueueFactory().get());
}
//detect supported encoders,should place before create p2p
std::vector<webrtc::SdpVideoFormat> video_formats = myVideoEncoderFactory->GetSupportedFormats();
for (const webrtc::SdpVideoFormat& myFormat : video_formats)
{
QL_LOG(std::string("Supported video encoders:" + myFormat.name).c_str());
}
//detect current audio codecs,should place before create p2p
std::vector<webrtc::AudioCodecSpec> audioCodecs = myAudioEncoderFactory->GetSupportedEncoders();
for (const webrtc::AudioCodecSpec& myAudioCodes : audioCodecs)
{
QL_LOG(std::string("Supported audio encoders:" + myAudioCodes.format.name).c_str());
}
//注意创建的时候globalSignalingThread和globalWorkerThread是必须的,否则信令不会收到
mPeerConnectionFactory = webrtc::CreatePeerConnectionFactory(
networkThread.get() /* network_thread */,
globalWorkerThread.get()/* worker_thread */,
globalSignalingThread.get() /* signaling_thread */,
myADM /* default_adm */,
std::move(myAudioEncoderFactory),
webrtc::CreateBuiltinAudioDecoderFactory(),
std::move(myVideoEncoderFactory),
webrtc::CreateBuiltinVideoDecoderFactory(),
nullptr /* audio_mixer */,
nullptr /* audio_processing */);
if (!mPeerConnectionFactory)
{
QL_ERROR("Create P2P Connection Factory error.");
DeletePeerConnection();
return false;
}
//Config 更多的是针对于ICE等网络配置
webrtc::PeerConnectionInterface::RTCConfiguration myRTCConfig;
//允许动态切换编码器根据网络带宽情况
myRTCConfig.allow_codec_switching = true;
//CPU自适应编码或负载情况
myRTCConfig.set_cpu_adaptation(true);
myRTCConfig.set_dscp(true);
myRTCConfig.set_prerenderer_smoothing(true);
//只有这个配置,其余都废弃了
myRTCConfig.sdp_semantics = webrtc::SdpSemantics::kUnifiedPlan;
//默认就是DTLS的
//这个配置被移动到CryptoOptions下面
//myRTCConfig.enable_dtls_srtp = true;
webrtc::CryptoOptions myCryptoOptions;
std::vector<int> myWay= myCryptoOptions.GetSupportedDtlsSrtpCryptoSuites();
myCryptoOptions.sframe.require_frame_encryption = true;
myCryptoOptions.srtp.enable_encrypted_rtp_header_extensions = true;
myCryptoOptions.srtp.enable_aes128_sha1_80_crypto_cipher = true;
myRTCConfig.crypto_options = myCryptoOptions;
//myRTCConfig.screencast_min_bitrate
//
//禁止TCP方式传输(不使用TCP进行媒体传输)
myRTCConfig.tcp_candidate_policy = webrtc::PeerConnectionInterface::kTcpCandidatePolicyDisabled;
//优先选用WIFI
myRTCConfig.candidate_network_policy = webrtc::PeerConnectionInterface::CandidateNetworkPolicy::kCandidateNetworkPolicyLowCost;
//持续寻找ICe潜在对象信息
//允许WebRTC在通讯过程中持续收集candidates,一旦网络状况发生改变,将使用当前有效的candidate pair取代失效的candidate pair进行数据传输。
//本地环境下实测,禁用一个网卡时,WebRTC的数据传输会迅速调整到另一块网卡,视频流几乎没有卡顿出现
myRTCConfig.continual_gathering_policy = webrtc::PeerConnectionInterface::ContinualGatheringPolicy::GATHER_CONTINUALLY;
//设置ice通路方式
myRTCConfig.type = webrtc::PeerConnectionInterface::IceTransportsType::kAll;
if (QL::QLGlobalConfig::Get().IsUseTurnServer())
{
//Please notice ,the turn server content should like as: "turn:10.2.3.4:3478" [turn:] as prefix
//
////add turn server
webrtc::PeerConnectionInterface::IceServer turn_server;
turn_server.urls.push_back(QL::QLGlobalConfig::Get().TurnServer());
turn_server.username = QL::QLGlobalConfig::Get().TurnServerUsername();
turn_server.password = QL::QLGlobalConfig::Get().TurnServerPwd();
myRTCConfig.servers.push_back(turn_server);
}
if (QL::QLGlobalConfig::Get().IsUseStunServer())
{
////add stun server
webrtc::PeerConnectionInterface::IceServer stunServer;
stunServer.urls.push_back(QL::QLGlobalConfig::Get().StunServer());
stunServer.username = QL::QLGlobalConfig::Get().StunServerUsername();
stunServer.password = QL::QLGlobalConfig::Get().StunServerPwd();
myRTCConfig.servers.push_back(stunServer);
QL_INFO("Add Turn server");
}
//设置端口,如果配置中>0的设置。
webrtc::PeerConnectionInterface::PortAllocatorConfig myPortConfig;
if (QLGlobalConfig::Get().MinPort() > 0)
{
myPortConfig.min_port = QLGlobalConfig::Get().MinPort();
}
if (QLGlobalConfig::Get().MaxPort() > 0)
{
myPortConfig.max_port = QLGlobalConfig::Get().MaxPort();
}
myRTCConfig.port_allocator_config = myPortConfig;
//使用配置真实创建对象
mPeerConnection = mPeerConnectionFactory->CreatePeerConnection(myRTCConfig, nullptr, nullptr, this);
if (!mPeerConnection)
{
QL_ERROR("Create P2P Connection error.");
DeletePeerConnection();
return false;
}
//add to global session to maintain p2p object
QLRoomSession::Get().CurrentP2PConnection(mPeerConnection);
//Try to set bitrate if we want to
//so far these codes are not affect logic since the CreateOffer was called by web client
//If native client CreateOffer then these codes will be useful.
if (!QL::QLGlobalConfig::Get().IsAutoBitrate())
{
webrtc::BitrateSettings mySettings;
mySettings.max_bitrate_bps = QL::QLGlobalConfig::Get().MaxBitrate();
mySettings.min_bitrate_bps = QL::QLGlobalConfig::Get().MinBitrate();
mySettings.start_bitrate_bps = QL::QLGlobalConfig::Get().StartBitrate();
mPeerConnection->SetBitrate(mySettings);
}
//webrtc::RtpParameters parameters=mPeerConnection->GetSenders()[0].get()->GetParameters();
//parameters.encodings[0].ss
//
//
//mPeerConnection->AddIceCandidate();
return mPeerConnection != nullptr;
}
我们针对上述的源代码进行重要内容的解释。
重要的步骤有:
- 首先需要创建几个线程对象,Global Worker 线程和 Global Signaling线程network线程。否则webrtc的消息循环不能正常工作。导致ICE消息或者SDP讯息,回调函数等不能被正常触发。具体的关系比较复杂,可能需要看webrtc的源代码才能梳理清楚
- 创建P2P的时候,需要我们创建好2个工厂对象,一个是音频的,一个是视频的。如果采用webrtc内置的,就直接嗲用CreateBuiltinVideoEncoderFactory和CreateBuiltinAudioEncoderFactory方法,编码器对应的解码器这块,分别是CreateBuiltinAudioDecoderFactory和CreateBuiltinVideoDecoderFactory
- 第四个参数ADM很有意思,管理声卡相关的对象,我们有一次在Linux平台上的一台机器没有安装声卡,这个对象出错。解决的方法是我们使用一个Fake的ADM对象代替。成功解决。
- webrtc::PeerConnectionInterface::RTCConfiguration这个配置很重要。更多的是针对于ICE等网络配置。allow_codec_switching,可以允许动态切换编码器根据网络带宽情况。set_cpu_adaptation,CPU自适应编码或负载情况。
- 默认相关的DTLS设置
1
2
3
4
5
6
7
8
9
//默认就是DTLS的
//这个配置被移动到CryptoOptions下面
//myRTCConfig.enable_dtls_srtp = true;
webrtc::CryptoOptions myCryptoOptions;
std::vector<int> myWay= myCryptoOptions.GetSupportedDtlsSrtpCryptoSuites();
myCryptoOptions.sframe.require_frame_encryption = true;
myCryptoOptions.srtp.enable_encrypted_rtp_header_extensions = true;
myCryptoOptions.srtp.enable_aes128_sha1_80_crypto_cipher = true;
myRTCConfig.crypto_options = myCryptoOptions;
设定DTLS加密协议中相关的加密等级等内容
- 其余的主要设置
1
2
3
4
5
6
7
8
9
10
//禁止TCP方式传输(不使用TCP进行媒体传输)
myRTCConfig.tcp_candidate_policy = webrtc::PeerConnectionInterface::kTcpCandidatePolicyDisabled;
//优先选用WIFI
myRTCConfig.candidate_network_policy = webrtc::PeerConnectionInterface::CandidateNetworkPolicy::kCandidateNetworkPolicyLowCost;
//持续寻找ICe潜在对象信息
//允许WebRTC在通讯过程中持续收集candidates,一旦网络状况发生改变,将使用当前有效的candidate pair取代失效的candidate pair进行数据传输。
//本地环境下实测,禁用一个网卡时,WebRTC的数据传输会迅速调整到另一块网卡,视频流几乎没有卡顿出现
myRTCConfig.continual_gathering_policy = webrtc::PeerConnectionInterface::ContinualGatheringPolicy::GATHER_CONTINUALLY;
//设置ice通路方式
myRTCConfig.type = webrtc::PeerConnectionInterface::IceTransportsType::kAll;
- 在整体的P2P连接建立后,继续建立几个重要的对象,分别是AddTrack ,增加视频,音频轨道。 CreateDataChannel, 创建数据通道管道。 SetREmoteDescription, 设置远端描述,告诉远端,我们已经配置好了我们的信息,远端可以连接了。 CreateAnswer, 发送回复消息给到远端。
这些步骤完成后,我们底层的ClientSink对象,会在WebSocket层面传递网络流出去。整个机制就流转起来了。
以下附上Fake ADM的源代码头文件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
#ifndef QC_FAKE_AUDIO_CAPTURE_MODULE_H_
#define QC_FAKE_AUDIO_CAPTURE_MODULE_H_
#include <stddef.h>
#include <stdint.h>
#include <memory>
#include "api/scoped_refptr.h"
#include "api/sequence_checker.h"
#include "modules/audio_device/include/audio_device.h"
#include "modules/audio_device/include/audio_device_defines.h"
#include "rtc_base/synchronization/mutex.h"
#include "rtc_base/thread_annotations.h"
#include "rtc_base/ref_counted_object.h"
namespace rtc
{
class Thread;
} // namespace rtc
namespace QL
{
class FakeADM : public webrtc::AudioDeviceModule
{
public:
typedef uint16_t Sample;
// The value for the following constants have been derived by running VoE
// using a real ADM. The constants correspond to 10ms of mono audio at 44kHz.
static const size_t kNumberSamples = 440;
static const size_t kNumberBytesPerSample = sizeof(Sample);
virtual void AddRef() const override;
virtual rtc::RefCountReleaseStatus Release() const override;
// Creates a FakeAudioCaptureModule or returns NULL on failure.
static rtc::scoped_refptr<FakeADM> Create();
// Returns the number of frames that have been successfully pulled by the
// instance. Note that correctly detecting success can only be done if the
// pulled frame was generated/pushed from a FakeAudioCaptureModule.
int frames_received() const RTC_LOCKS_EXCLUDED(mutex_);
int32_t ActiveAudioLayer(AudioLayer* audio_layer) const override;
// Note: Calling this method from a callback may result in deadlock.
int32_t RegisterAudioCallback(webrtc::AudioTransport* audio_callback) override
RTC_LOCKS_EXCLUDED(mutex_);
int32_t Init() override;
int32_t Terminate() override;
bool Initialized() const override;
int16_t PlayoutDevices() override;
int16_t RecordingDevices() override;
int32_t PlayoutDeviceName(uint16_t index,
char name[webrtc::kAdmMaxDeviceNameSize],
char guid[webrtc::kAdmMaxGuidSize]) override;
int32_t RecordingDeviceName(uint16_t index,
char name[webrtc::kAdmMaxDeviceNameSize],
char guid[webrtc::kAdmMaxGuidSize]) override;
int32_t SetPlayoutDevice(uint16_t index) override;
int32_t SetPlayoutDevice(WindowsDeviceType device) override;
int32_t SetRecordingDevice(uint16_t index) override;
int32_t SetRecordingDevice(WindowsDeviceType device) override;
int32_t PlayoutIsAvailable(bool* available) override;
int32_t InitPlayout() override;
bool PlayoutIsInitialized() const override;
int32_t RecordingIsAvailable(bool* available) override;
int32_t InitRecording() override;
bool RecordingIsInitialized() const override;
int32_t StartPlayout() RTC_LOCKS_EXCLUDED(mutex_) override;
int32_t StopPlayout() RTC_LOCKS_EXCLUDED(mutex_) override;
bool Playing() const RTC_LOCKS_EXCLUDED(mutex_) override;
int32_t StartRecording() RTC_LOCKS_EXCLUDED(mutex_) override;
int32_t StopRecording() RTC_LOCKS_EXCLUDED(mutex_) override;
bool Recording() const RTC_LOCKS_EXCLUDED(mutex_) override;
int32_t InitSpeaker() override;
bool SpeakerIsInitialized() const override;
int32_t InitMicrophone() override;
bool MicrophoneIsInitialized() const override;
int32_t SpeakerVolumeIsAvailable(bool* available) override;
int32_t SetSpeakerVolume(uint32_t volume) override;
int32_t SpeakerVolume(uint32_t* volume) const override;
int32_t MaxSpeakerVolume(uint32_t* max_volume) const override;
int32_t MinSpeakerVolume(uint32_t* min_volume) const override;
int32_t MicrophoneVolumeIsAvailable(bool* available) override;
int32_t SetMicrophoneVolume(uint32_t volume)
RTC_LOCKS_EXCLUDED(mutex_) override;
int32_t MicrophoneVolume(uint32_t* volume) const
RTC_LOCKS_EXCLUDED(mutex_) override;
int32_t MaxMicrophoneVolume(uint32_t* max_volume) const override;
int32_t MinMicrophoneVolume(uint32_t* min_volume) const override;
int32_t SpeakerMuteIsAvailable(bool* available) override;
int32_t SetSpeakerMute(bool enable) override;
int32_t SpeakerMute(bool* enabled) const override;
int32_t MicrophoneMuteIsAvailable(bool* available) override;
int32_t SetMicrophoneMute(bool enable) override;
int32_t MicrophoneMute(bool* enabled) const override;
int32_t StereoPlayoutIsAvailable(bool* available) const override;
int32_t SetStereoPlayout(bool enable) override;
int32_t StereoPlayout(bool* enabled) const override;
int32_t StereoRecordingIsAvailable(bool* available) const override;
int32_t SetStereoRecording(bool enable) override;
int32_t StereoRecording(bool* enabled) const override;
int32_t PlayoutDelay(uint16_t* delay_ms) const override;
bool BuiltInAECIsAvailable() const override { return false; }
int32_t EnableBuiltInAEC(bool enable) override { return -1; }
bool BuiltInAGCIsAvailable() const override { return false; }
int32_t EnableBuiltInAGC(bool enable) override { return -1; }
bool BuiltInNSIsAvailable() const override { return false; }
int32_t EnableBuiltInNS(bool enable) override { return -1; }
int32_t GetPlayoutUnderrunCount() const override { return -1; }
//absl::optional<webrtc::AudioDeviceModule::Stats> GetStats() const override
//{
// return webrtc::AudioDeviceModule::Stats();
//}
#if defined(WEBRTC_IOS)
int GetPlayoutAudioParameters(
webrtc::AudioParameters* params) const override {
return -1;
}
int GetRecordAudioParameters(webrtc::AudioParameters* params) const override {
return -1;
}
#endif // WEBRTC_IOS
// End of functions inherited from webrtc::AudioDeviceModule.
protected:
// The constructor is protected because the class needs to be created as a
// reference counted object (for memory managment reasons). It could be
// exposed in which case the burden of proper instantiation would be put on
// the creator of a FakeAudioCaptureModule instance. To create an instance of
// this class use the Create(..) API.
FakeADM();
// The destructor is protected because it is reference counted and should not
// be deleted directly.
virtual ~FakeADM();
private:
mutable webrtc::webrtc_impl::RefCounter mRefCount;
// Initializes the state of the FakeAudioCaptureModule. This API is called on
// creation by the Create() API.
bool Initialize();
// SetBuffer() sets all samples in send_buffer_ to `value`.
void SetSendBuffer(int value);
// Resets rec_buffer_. I.e., sets all rec_buffer_ samples to 0.
void ResetRecBuffer();
// Returns true if rec_buffer_ contains one or more sample greater than or
// equal to `value`.
bool CheckRecBuffer(int value);
// Returns true/false depending on if recording or playback has been
// enabled/started.
bool ShouldStartProcessing() RTC_EXCLUSIVE_LOCKS_REQUIRED(mutex_);
// Starts or stops the pushing and pulling of audio frames.
void UpdateProcessing(bool start) RTC_LOCKS_EXCLUDED(mutex_);
// Starts the periodic calling of ProcessFrame() in a thread safe way.
void StartProcessP();
// Periodcally called function that ensures that frames are pulled and pushed
// periodically if enabled/started.
void ProcessFrameP() RTC_LOCKS_EXCLUDED(mutex_);
// Pulls frames from the registered webrtc::AudioTransport.
void ReceiveFrameP() RTC_EXCLUSIVE_LOCKS_REQUIRED(mutex_);
// Pushes frames to the registered webrtc::AudioTransport.
void SendFrameP() RTC_EXCLUSIVE_LOCKS_REQUIRED(mutex_);
// Callback for playout and recording.
webrtc::AudioTransport* audio_callback_ RTC_GUARDED_BY(mutex_);
bool recording_ RTC_GUARDED_BY(
mutex_); // True when audio is being pushed from the instance.
bool playing_ RTC_GUARDED_BY(
mutex_); // True when audio is being pulled by the instance.
bool play_is_initialized_; // True when the instance is ready to pull audio.
bool rec_is_initialized_; // True when the instance is ready to push audio.
// Input to and output from RecordedDataIsAvailable(..) makes it possible to
// modify the current mic level. The implementation does not care about the
// mic level so it just feeds back what it receives.
uint32_t current_mic_level_ RTC_GUARDED_BY(mutex_);
// next_frame_time_ is updated in a non-drifting manner to indicate the next
// wall clock time the next frame should be generated and received. started_
// ensures that next_frame_time_ can be initialized properly on first call.
bool started_ RTC_GUARDED_BY(mutex_);
int64_t next_frame_time_ RTC_GUARDED_BY(process_thread_checker_);
std::unique_ptr<rtc::Thread> process_thread_;
// Buffer for storing samples received from the webrtc::AudioTransport.
char rec_buffer_[kNumberSamples * kNumberBytesPerSample];
// Buffer for samples to send to the webrtc::AudioTransport.
char send_buffer_[kNumberSamples * kNumberBytesPerSample];
// Counter of frames received that have samples of high enough amplitude to
// indicate that the frames are not faked somewhere in the audio pipeline
// (e.g. by a jitter buffer).
int frames_received_;
// Protects variables that are accessed from process_thread_ and
// the main thread.
mutable webrtc::Mutex mutex_;
webrtc::SequenceChecker process_thread_checker_;
};
}
#endif // QC_FAKE_AUDIO_CAPTURE_MODULE_H_
Cpp文件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
#include "FakeADM.h"
#include <string.h>
#ifdef USE_M108_WEBRTC
#include "api/make_ref_counted.h"
#endif
#if _WIN32||_WIN64
#include "api/make_ref_counted.h"
#else
#endif
#include "api/units/time_delta.h"
#include "rtc_base/checks.h"
#include "rtc_base/thread.h"
#include "rtc_base/time_utils.h"
#include <boost/bind.hpp>
using ::webrtc::TimeDelta;
// Audio sample value that is high enough that it doesn't occur naturally when
// frames are being faked. E.g. NetEq will not generate this large sample value
// unless it has received an audio frame containing a sample of this value.
// Even simpler buffers would likely just contain audio sample values of 0.
static const int kHighSampleValue = 10000;
// Constants here are derived by running VoE using a real ADM.
// The constants correspond to 10ms of mono audio at 44kHz.
static const int kTimePerFrameMs = 10;
static const uint8_t kNumberOfChannels = 1;
static const int kSamplesPerSecond = 44000;
static const int kTotalDelayMs = 0;
static const int kClockDriftMs = 0;
static const uint32_t kMaxVolume = 14392;
namespace QL
{
FakeADM::FakeADM()
: audio_callback_(nullptr),
recording_(false),
playing_(false),
play_is_initialized_(false),
rec_is_initialized_(false),
current_mic_level_(kMaxVolume),
started_(false),
next_frame_time_(0),
frames_received_(0),
mRefCount(0)
{
process_thread_checker_.Detach();
}
//#include <rtc_base/atomic_ops.h>
void FakeADM::AddRef() const
{
mRefCount.IncRef();
}
rtc::RefCountReleaseStatus FakeADM::Release() const
{
rtc::RefCountReleaseStatus status = mRefCount.DecRef();
return status;
}
FakeADM::~FakeADM()
{
if (process_thread_) {
process_thread_->Stop();
}
}
rtc::scoped_refptr<FakeADM> FakeADM::Create()
{
#ifdef USE_M108_WEBRTC
rtc::scoped_refptr<FakeADM> capture_module(new rtc::RefCountedObject<FakeADM>());
//auto capture_module=rtc::make_ref_counted<FakeAudioCaptureModule>();
#elif _WIN32 ||_WIN64
//auto capture_module = rtc::make_ref_counted<FakeAudioCaptureModule>();
rtc::scoped_refptr<FakeADM> capture_module(new rtc::RefCountedObject<FakeADM>());
#else
auto capture_module = new FakeAudioCaptureModule();
#endif
if (!capture_module->Initialize()) {
return nullptr;
}
return capture_module;
}
int FakeADM::frames_received() const {
webrtc::MutexLock lock(&mutex_);
return frames_received_;
}
int32_t FakeADM::ActiveAudioLayer(
AudioLayer* /*audio_layer*/) const {
//RTC_DCHECK_NOTREACHED();
return 0;
}
int32_t FakeADM::RegisterAudioCallback(
webrtc::AudioTransport* audio_callback) {
webrtc::MutexLock lock(&mutex_);
audio_callback_ = audio_callback;
return 0;
}
int32_t FakeADM::Init() {
// Initialize is called by the factory method. Safe to ignore this Init call.
return 0;
}
int32_t FakeADM::Terminate() {
// Clean up in the destructor. No action here, just success.
return 0;
}
bool FakeADM::Initialized() const {
//RTC_DCHECK_NOTREACHED();
return 0;
}
int16_t FakeADM::PlayoutDevices() {
//RTC_DCHECK_NOTREACHED();
return 0;
}
int16_t FakeADM::RecordingDevices() {
//RTC_DCHECK_NOTREACHED();
return 0;
}
int32_t FakeADM::PlayoutDeviceName(
uint16_t /*index*/,
char /*name*/[webrtc::kAdmMaxDeviceNameSize],
char /*guid*/[webrtc::kAdmMaxGuidSize]) {
//RTC_DCHECK_NOTREACHED();
return 0;
}
int32_t FakeADM::RecordingDeviceName(
uint16_t /*index*/,
char /*name*/[webrtc::kAdmMaxDeviceNameSize],
char /*guid*/[webrtc::kAdmMaxGuidSize]) {
//RTC_DCHECK_NOTREACHED();
return 0;
}
int32_t FakeADM::SetPlayoutDevice(uint16_t /*index*/) {
// No playout device, just playing from file. Return success.
return 0;
}
int32_t FakeADM::SetPlayoutDevice(WindowsDeviceType /*device*/) {
if (play_is_initialized_) {
return -1;
}
return 0;
}
int32_t FakeADM::SetRecordingDevice(uint16_t /*index*/) {
// No recording device, just dropping audio. Return success.
return 0;
}
int32_t FakeADM::SetRecordingDevice(
WindowsDeviceType /*device*/) {
if (rec_is_initialized_) {
return -1;
}
return 0;
}
int32_t FakeADM::PlayoutIsAvailable(bool* /*available*/) {
//RTC_DCHECK_NOTREACHED();
return 0;
}
int32_t FakeADM::InitPlayout() {
play_is_initialized_ = true;
return 0;
}
bool FakeADM::PlayoutIsInitialized() const {
return play_is_initialized_;
}
int32_t FakeADM::RecordingIsAvailable(bool* /*available*/) {
//RTC_DCHECK_NOTREACHED();
return 0;
}
int32_t FakeADM::InitRecording() {
rec_is_initialized_ = true;
return 0;
}
bool FakeADM::RecordingIsInitialized() const {
return rec_is_initialized_;
}
int32_t FakeADM::StartPlayout() {
if (!play_is_initialized_) {
return -1;
}
{
webrtc::MutexLock lock(&mutex_);
playing_ = true;
}
bool start = true;
UpdateProcessing(start);
return 0;
}
int32_t FakeADM::StopPlayout() {
bool start = false;
{
webrtc::MutexLock lock(&mutex_);
playing_ = false;
start = ShouldStartProcessing();
}
UpdateProcessing(start);
return 0;
}
bool FakeADM::Playing() const {
webrtc::MutexLock lock(&mutex_);
return playing_;
}
int32_t FakeADM::StartRecording() {
if (!rec_is_initialized_) {
return -1;
}
{
webrtc::MutexLock lock(&mutex_);
recording_ = true;
}
bool start = true;
UpdateProcessing(start);
return 0;
}
int32_t FakeADM::StopRecording() {
bool start = false;
{
webrtc::MutexLock lock(&mutex_);
recording_ = false;
start = ShouldStartProcessing();
}
UpdateProcessing(start);
return 0;
}
bool FakeADM::Recording() const {
webrtc::MutexLock lock(&mutex_);
return recording_;
}
int32_t FakeADM::InitSpeaker() {
// No speaker, just playing from file. Return success.
return 0;
}
bool FakeADM::SpeakerIsInitialized() const {
//RTC_DCHECK_NOTREACHED();
return 0;
}
int32_t FakeADM::InitMicrophone() {
// No microphone, just playing from file. Return success.
return 0;
}
bool FakeADM::MicrophoneIsInitialized() const {
//RTC_DCHECK_NOTREACHED();
return 0;
}
int32_t FakeADM::SpeakerVolumeIsAvailable(bool* /*available*/) {
//RTC_DCHECK_NOTREACHED();
return 0;
}
int32_t FakeADM::SetSpeakerVolume(uint32_t /*volume*/) {
//RTC_DCHECK_NOTREACHED();
return 0;
}
int32_t FakeADM::SpeakerVolume(uint32_t* /*volume*/) const {
//RTC_DCHECK_NOTREACHED();
return 0;
}
int32_t FakeADM::MaxSpeakerVolume(
uint32_t* /*max_volume*/) const {
//RTC_DCHECK_NOTREACHED();
return 0;
}
int32_t FakeADM::MinSpeakerVolume(
uint32_t* /*min_volume*/) const {
//RTC_DCHECK_NOTREACHED();
return 0;
}
int32_t FakeADM::MicrophoneVolumeIsAvailable(
bool* /*available*/) {
//RTC_DCHECK_NOTREACHED();
return 0;
}
int32_t FakeADM::SetMicrophoneVolume(uint32_t volume) {
webrtc::MutexLock lock(&mutex_);
current_mic_level_ = volume;
return 0;
}
int32_t FakeADM::MicrophoneVolume(uint32_t* volume) const {
webrtc::MutexLock lock(&mutex_);
*volume = current_mic_level_;
return 0;
}
int32_t FakeADM::MaxMicrophoneVolume(
uint32_t* max_volume) const {
*max_volume = kMaxVolume;
return 0;
}
int32_t FakeADM::MinMicrophoneVolume(
uint32_t* /*min_volume*/) const {
//RTC_DCHECK_NOTREACHED();
return 0;
}
int32_t FakeADM::SpeakerMuteIsAvailable(bool* /*available*/) {
//RTC_DCHECK_NOTREACHED();
return 0;
}
int32_t FakeADM::SetSpeakerMute(bool /*enable*/) {
//RTC_DCHECK_NOTREACHED();
return 0;
}
int32_t FakeADM::SpeakerMute(bool* /*enabled*/) const {
//RTC_DCHECK_NOTREACHED();
return 0;
}
int32_t FakeADM::MicrophoneMuteIsAvailable(bool* /*available*/) {
//RTC_DCHECK_NOTREACHED();
return 0;
}
int32_t FakeADM::SetMicrophoneMute(bool /*enable*/) {
//RTC_DCHECK_NOTREACHED();
return 0;
}
int32_t FakeADM::MicrophoneMute(bool* /*enabled*/) const {
//RTC_DCHECK_NOTREACHED();
return 0;
}
int32_t FakeADM::StereoPlayoutIsAvailable(
bool* available) const {
// No recording device, just dropping audio. Stereo can be dropped just
// as easily as mono.
*available = true;
return 0;
}
int32_t FakeADM::SetStereoPlayout(bool /*enable*/) {
// No recording device, just dropping audio. Stereo can be dropped just
// as easily as mono.
return 0;
}
int32_t FakeADM::StereoPlayout(bool* /*enabled*/) const {
RTC_DCHECK_NOTREACHED();
return 0;
}
int32_t FakeADM::StereoRecordingIsAvailable(
bool* available) const {
// Keep thing simple. No stereo recording.
*available = false;
return 0;
}
int32_t FakeADM::SetStereoRecording(bool enable) {
if (!enable) {
return 0;
}
return -1;
}
int32_t FakeADM::StereoRecording(bool* /*enabled*/) const {
//RTC_DCHECK_NOTREACHED();
return 0;
}
int32_t FakeADM::PlayoutDelay(uint16_t* delay_ms) const {
// No delay since audio frames are dropped.
*delay_ms = 0;
return 0;
}
bool FakeADM::Initialize() {
// Set the send buffer samples high enough that it would not occur on the
// remote side unless a packet containing a sample of that magnitude has been
// sent to it. Note that the audio processing pipeline will likely distort the
// original signal.
SetSendBuffer(kHighSampleValue);
return true;
}
void FakeADM::SetSendBuffer(int value) {
Sample* buffer_ptr = reinterpret_cast<Sample*>(send_buffer_);
const size_t buffer_size_in_samples =
sizeof(send_buffer_) / kNumberBytesPerSample;
for (size_t i = 0; i < buffer_size_in_samples; ++i) {
buffer_ptr[i] = value;
}
}
void FakeADM::ResetRecBuffer() {
memset(rec_buffer_, 0, sizeof(rec_buffer_));
}
bool FakeADM::CheckRecBuffer(int value) {
const Sample* buffer_ptr = reinterpret_cast<const Sample*>(rec_buffer_);
const size_t buffer_size_in_samples =
sizeof(rec_buffer_) / kNumberBytesPerSample;
for (size_t i = 0; i < buffer_size_in_samples; ++i) {
if (buffer_ptr[i] >= value)
return true;
}
return false;
}
bool FakeADM::ShouldStartProcessing() {
return recording_ || playing_;
}
void FakeADM::UpdateProcessing(bool start) {
if (start) {
if (!process_thread_) {
process_thread_ = rtc::Thread::Create();
process_thread_->Start();
}
#if _WIN32||_WIN64
process_thread_->PostTask([this] { StartProcessP(); });
#else
//process_thread_->PostTask(boost::bind(&FakeAudioCaptureModule::UpdateProcessing, this, start));
#endif
}
else {
if (process_thread_) {
process_thread_->Stop();
process_thread_.reset(nullptr);
process_thread_checker_.Detach();
}
webrtc::MutexLock lock(&mutex_);
started_ = false;
}
}
void FakeADM::StartProcessP() {
RTC_DCHECK_RUN_ON(&process_thread_checker_);
{
webrtc::MutexLock lock(&mutex_);
if (started_)
{
// Already started.
return;
}
}
ProcessFrameP();
}
void FakeADM::ProcessFrameP() {
RTC_DCHECK_RUN_ON(&process_thread_checker_);
{
webrtc::MutexLock lock(&mutex_);
if (!started_) {
next_frame_time_ = rtc::TimeMillis();
started_ = true;
}
// Receive and send frames every kTimePerFrameMs.
if (playing_) {
ReceiveFrameP();
}
if (recording_) {
SendFrameP();
}
}
next_frame_time_ += kTimePerFrameMs;
const int64_t current_time = rtc::TimeMillis();
const int64_t wait_time =
(next_frame_time_ > current_time) ? next_frame_time_ - current_time : 0;
#if _WIN32||_WIN64
process_thread_->PostDelayedTask([this] { ProcessFrameP(); },TimeDelta::Millis(wait_time));
#else
//process_thread_->PostDelayedTask(boost::bind(&FakeAudioCaptureModule::ProcessFrameP, this), webrtc::TimeDelta::Millis(wait_time));
#endif
}
void FakeADM::ReceiveFrameP() {
RTC_DCHECK_RUN_ON(&process_thread_checker_);
if (!audio_callback_) {
return;
}
ResetRecBuffer();
size_t nSamplesOut = 0;
int64_t elapsed_time_ms = 0;
int64_t ntp_time_ms = 0;
if (audio_callback_->NeedMorePlayData(kNumberSamples, kNumberBytesPerSample,
kNumberOfChannels, kSamplesPerSecond,
rec_buffer_, nSamplesOut,
&elapsed_time_ms, &ntp_time_ms) != 0) {
//RTC_DCHECK_NOTREACHED();
}
RTC_CHECK(nSamplesOut == kNumberSamples);
// The SetBuffer() function ensures that after decoding, the audio buffer
// should contain samples of similar magnitude (there is likely to be some
// distortion due to the audio pipeline). If one sample is detected to
// have the same or greater magnitude somewhere in the frame, an actual frame
// has been received from the remote side (i.e. faked frames are not being
// pulled).
if (CheckRecBuffer(kHighSampleValue)) {
++frames_received_;
}
}
void FakeADM::SendFrameP() {
RTC_DCHECK_RUN_ON(&process_thread_checker_);
if (!audio_callback_) {
return;
}
bool key_pressed = false;
uint32_t current_mic_level = current_mic_level_;
if (audio_callback_->RecordedDataIsAvailable(
send_buffer_, kNumberSamples, kNumberBytesPerSample,
kNumberOfChannels, kSamplesPerSecond, kTotalDelayMs, kClockDriftMs,
current_mic_level, key_pressed, current_mic_level) != 0) {
//RTC_DCHECK_NOTREACHED();
}
current_mic_level_ = current_mic_level;
}
}