第二十三章 建立P2P 连接

第二十三章 建立P2P 连接

什么是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版本的差异,接口和参数会有不同。

///

/// 创建标准管道。

///

///

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 myVideoEncoderFactory = webrtc::CreateBuiltinVideoEncoderFactory();

rtc::scoped_refptr 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 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 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 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 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设置

//默认就是DTLS的

//这个配置被移动到CryptoOptions下面

//myRTCConfig.enable_dtls_srtp = true;

webrtc::CryptoOptions myCryptoOptions;

std::vector 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加密协议中相关的加密等级等内容

其余的主要设置

//禁止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的源代码头文件

#ifndef QC_FAKE_AUDIO_CAPTURE_MODULE_H_

#define QC_FAKE_AUDIO_CAPTURE_MODULE_H_

#include

#include

#include

#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 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 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 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文件

#include "FakeADM.h"

#include

#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

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

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::Create()

{

#ifdef USE_M108_WEBRTC

rtc::scoped_refptr capture_module(new rtc::RefCountedObject());

//auto capture_module=rtc::make_ref_counted();

#elif _WIN32 ||_WIN64

//auto capture_module = rtc::make_ref_counted();

rtc::scoped_refptr capture_module(new rtc::RefCountedObject());

#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(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(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;

}

}

← 上一篇: 《艾尔登法环》BOSS及宝物位置介绍 永恒之城诺克隆恩地图攻略
下一篇: 一方红砖有多少块砖0 →

相关推荐