mirror of
https://github.com/DrKLO/Telegram.git
synced 2024-12-22 14:35:03 +01:00
Update to 8.6.1
This commit is contained in:
parent
d30f796d8c
commit
5d5527525f
717 changed files with 7508 additions and 19775 deletions
|
@ -300,7 +300,7 @@ android {
|
|||
}
|
||||
}
|
||||
|
||||
defaultConfig.versionCode = 2587
|
||||
defaultConfig.versionCode = 2594
|
||||
|
||||
applicationVariants.all { variant ->
|
||||
variant.outputs.all { output ->
|
||||
|
@ -319,7 +319,7 @@ android {
|
|||
defaultConfig {
|
||||
minSdkVersion 16
|
||||
targetSdkVersion 30
|
||||
versionName "8.6.0"
|
||||
versionName "8.6.1"
|
||||
|
||||
vectorDrawables.generatedDensities = ['mdpi', 'hdpi', 'xhdpi', 'xxhdpi']
|
||||
|
||||
|
|
|
@ -404,7 +404,7 @@ target_compile_definitions(sqlite PUBLIC
|
|||
#voip
|
||||
include(${CMAKE_HOME_DIRECTORY}/voip/CMakeLists.txt)
|
||||
|
||||
set(NATIVE_LIB "tmessages.41")
|
||||
set(NATIVE_LIB "tmessages.42")
|
||||
|
||||
#tmessages
|
||||
add_library(${NATIVE_LIB} SHARED
|
||||
|
|
|
@ -657,7 +657,6 @@ add_library(tgcalls STATIC
|
|||
voip/webrtc/api/video/video_adaptation_counters.cc
|
||||
voip/webrtc/api/video/video_frame_metadata.cc
|
||||
voip/webrtc/api/voip/voip_engine_factory.cc
|
||||
voip/webrtc/api/video/i444_buffer.cc
|
||||
voip/webrtc/api/video/rtp_video_frame_assembler.cc
|
||||
voip/webrtc/api/numerics/samples_stats_counter.cc
|
||||
voip/webrtc/api/wrapping_async_dns_resolver.cc
|
||||
|
@ -1181,6 +1180,7 @@ add_library(tgcalls STATIC
|
|||
voip/webrtc/modules/audio_processing/vad/vad_audio_proc.cc
|
||||
voip/webrtc/modules/audio_processing/vad/vad_circular_buffer.cc
|
||||
voip/webrtc/modules/audio_processing/vad/voice_activity_detector.cc
|
||||
voip/webrtc/modules/audio_processing/voice_detection.cc
|
||||
voip/webrtc/modules/audio_processing/optionally_built_submodule_creators.cc
|
||||
voip/webrtc/modules/audio_processing/capture_levels_adjuster/capture_levels_adjuster.cc
|
||||
voip/webrtc/modules/audio_processing/capture_levels_adjuster/audio_samples_scaler.cc
|
||||
|
@ -1327,7 +1327,7 @@ add_library(tgcalls STATIC
|
|||
voip/webrtc/modules/video_capture/video_capture_impl.cc
|
||||
voip/webrtc/modules/video_coding/codec_timer.cc
|
||||
voip/webrtc/modules/video_coding/codecs/av1/libaom_av1_decoder_absent.cc
|
||||
voip/webrtc/modules/video_coding/codecs/av1/libaom_av1_encoder_supported.cc
|
||||
voip/webrtc/modules/video_coding/codecs/av1/libaom_av1_encoder_absent.cc
|
||||
voip/webrtc/modules/video_coding/codecs/h264/h264.cc
|
||||
voip/webrtc/modules/video_coding/codecs/h264/h264_color_space.cc
|
||||
voip/webrtc/modules/video_coding/codecs/h264/h264_decoder_impl.cc
|
||||
|
@ -1409,7 +1409,6 @@ add_library(tgcalls STATIC
|
|||
voip/webrtc/modules/video_coding/codecs/av1/av1_svc_config.cc
|
||||
voip/webrtc/modules/video_coding/nack_requester.cc
|
||||
voip/webrtc/modules/video_coding/frame_buffer3.cc
|
||||
voip/webrtc/modules/video_coding/frame_helpers.cc
|
||||
voip/webrtc/modules/video_coding/h264_packet_buffer.cc
|
||||
voip/webrtc/modules/video_processing/util/denoiser_filter.cc
|
||||
voip/webrtc/modules/video_processing/util/denoiser_filter_c.cc
|
||||
|
@ -1635,12 +1634,7 @@ add_library(tgcalls STATIC
|
|||
voip/webrtc/video/receive_statistics_proxy2.cc
|
||||
voip/webrtc/video/call_stats2.cc
|
||||
voip/webrtc/video/alignment_adjuster.cc
|
||||
voip/webrtc/video/frame_buffer_proxy.cc
|
||||
voip/webrtc/video/decode_synchronizer.cc
|
||||
voip/webrtc/video/frame_cadence_adapter.cc
|
||||
voip/webrtc/video/frame_decode_timing.cc
|
||||
voip/webrtc/video/task_queue_frame_decode_scheduler.cc
|
||||
voip/webrtc/video/video_receive_stream_timeout_tracker.cc
|
||||
voip/webrtc/audio/audio_level.cc
|
||||
voip/webrtc/audio/audio_receive_stream.cc
|
||||
voip/webrtc/audio/audio_send_stream.cc
|
||||
|
@ -1786,7 +1780,7 @@ add_library(voipandroid STATIC
|
|||
voip/webrtc/sdk/android/native_api/video/wrapper.cc
|
||||
voip/webrtc/sdk/android/native_api/network_monitor/network_monitor.cc
|
||||
voip/webrtc/sdk/android/src/jni/android_histogram.cc
|
||||
voip/webrtc/sdk/android/src/jni/libaom_av1_codec.cc
|
||||
voip/webrtc/sdk/android/src/jni/av1_codec.cc
|
||||
voip/webrtc/sdk/android/src/jni/egl_base_10_impl.cc
|
||||
voip/webrtc/sdk/android/src/jni/android_metrics.cc
|
||||
voip/webrtc/sdk/android/src/jni/android_network_monitor.cc
|
||||
|
|
|
@ -209,6 +209,7 @@ struct InstanceHolder {
|
|||
std::unique_ptr<GroupInstanceCustomImpl> groupNativeInstance;
|
||||
std::shared_ptr<tgcalls::VideoCaptureInterface> _videoCapture;
|
||||
std::shared_ptr<tgcalls::VideoCaptureInterface> _screenVideoCapture;
|
||||
std::shared_ptr<rtc::VideoSinkInterface<webrtc::VideoFrame>> _sink;
|
||||
std::shared_ptr<PlatformContext> _platformContext;
|
||||
std::map<std::string, SetVideoSink> remoteGroupSinks;
|
||||
bool useScreencast = false;
|
||||
|
@ -754,7 +755,8 @@ JNIEXPORT jlong JNICALL Java_org_telegram_messenger_voip_NativeInstance_makeNati
|
|||
holder->nativeInstance = tgcalls::Meta::Create(v, std::move(descriptor));
|
||||
holder->_videoCapture = videoCapture;
|
||||
holder->_platformContext = platformContext;
|
||||
holder->nativeInstance->setIncomingVideoOutput(webrtc::JavaToNativeVideoSink(env, remoteSink));
|
||||
holder->_sink = webrtc::JavaToNativeVideoSink(env, remoteSink);
|
||||
holder->nativeInstance->setIncomingVideoOutput(holder->_sink);
|
||||
holder->nativeInstance->setNetworkType(parseNetworkType(networkType));
|
||||
holder->nativeInstance->setRequestedVideoAspect(aspectRatio);
|
||||
return reinterpret_cast<jlong>(holder);
|
||||
|
|
|
@ -139,9 +139,9 @@ void Manager::sendSignalingAsync(int delayMs, int cause) {
|
|||
}
|
||||
};
|
||||
if (delayMs) {
|
||||
_thread->PostDelayedTask(std::move(task), delayMs);
|
||||
_thread->PostDelayedTask(RTC_FROM_HERE, std::move(task), delayMs);
|
||||
} else {
|
||||
_thread->PostTask(std::move(task));
|
||||
_thread->PostTask(RTC_FROM_HERE, std::move(task));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -149,7 +149,7 @@ void Manager::start() {
|
|||
const auto weak = std::weak_ptr<Manager>(shared_from_this());
|
||||
const auto thread = _thread;
|
||||
const auto sendSignalingMessage = [=](Message &&message) {
|
||||
thread->PostTask([=, message = std::move(message)]() mutable {
|
||||
thread->PostTask(RTC_FROM_HERE, [=, message = std::move(message)]() mutable {
|
||||
const auto strong = weak.lock();
|
||||
if (!strong) {
|
||||
return;
|
||||
|
@ -167,7 +167,7 @@ void Manager::start() {
|
|||
rtcServers,
|
||||
std::move(proxy),
|
||||
[=](const NetworkManager::State &state) {
|
||||
thread->PostTask([=] {
|
||||
thread->PostTask(RTC_FROM_HERE, [=] {
|
||||
const auto strong = weak.lock();
|
||||
if (!strong) {
|
||||
return;
|
||||
|
@ -200,7 +200,7 @@ void Manager::start() {
|
|||
});
|
||||
},
|
||||
[=](DecryptedMessage &&message) {
|
||||
thread->PostTask([=, message = std::move(message)]() mutable {
|
||||
thread->PostTask(RTC_FROM_HERE, [=, message = std::move(message)]() mutable {
|
||||
if (const auto strong = weak.lock()) {
|
||||
strong->receiveMessage(std::move(message));
|
||||
}
|
||||
|
@ -216,9 +216,9 @@ void Manager::start() {
|
|||
}
|
||||
};
|
||||
if (delayMs) {
|
||||
thread->PostDelayedTask(task, delayMs);
|
||||
thread->PostDelayedTask(RTC_FROM_HERE, task, delayMs);
|
||||
} else {
|
||||
thread->PostTask(task);
|
||||
thread->PostTask(RTC_FROM_HERE, task);
|
||||
}
|
||||
});
|
||||
}));
|
||||
|
@ -232,7 +232,7 @@ void Manager::start() {
|
|||
videoCapture,
|
||||
sendSignalingMessage,
|
||||
[=](Message &&message) {
|
||||
thread->PostTask([=, message = std::move(message)]() mutable {
|
||||
thread->PostTask(RTC_FROM_HERE, [=, message = std::move(message)]() mutable {
|
||||
const auto strong = weak.lock();
|
||||
if (!strong) {
|
||||
return;
|
||||
|
@ -362,7 +362,7 @@ void Manager::getNetworkStats(std::function<void (TrafficStats, CallStats)> comp
|
|||
CallStats callStats;
|
||||
networkManager->fillCallStats(callStats);
|
||||
|
||||
thread->PostTask([weak, networkStats, completion = std::move(completion), callStats = std::move(callStats), statsLogPath = statsLogPath] {
|
||||
thread->PostTask(RTC_FROM_HERE, [weak, networkStats, completion = std::move(completion), callStats = std::move(callStats), statsLogPath = statsLogPath] {
|
||||
const auto strong = weak.lock();
|
||||
if (!strong) {
|
||||
return;
|
||||
|
|
|
@ -314,7 +314,7 @@ _platformContext(platformContext) {
|
|||
|
||||
webrtc::AudioProcessingBuilder builder;
|
||||
std::unique_ptr<AudioCapturePostProcessor> audioProcessor = std::make_unique<AudioCapturePostProcessor>([this](float level) {
|
||||
this->_thread->PostTask([this, level](){
|
||||
this->_thread->PostTask(RTC_FROM_HERE, [this, level](){
|
||||
auto strong = this;
|
||||
strong->_currentMyAudioLevel = level;
|
||||
});
|
||||
|
@ -436,7 +436,7 @@ void MediaManager::start() {
|
|||
// Here we hope that thread outlives the sink
|
||||
rtc::Thread *thread = _thread;
|
||||
std::unique_ptr<AudioTrackSinkInterfaceImpl> incomingSink(new AudioTrackSinkInterfaceImpl([weak, thread](float level) {
|
||||
thread->PostTask([weak, level] {
|
||||
thread->PostTask(RTC_FROM_HERE, [weak, level] {
|
||||
if (const auto strong = weak.lock()) {
|
||||
strong->_currentAudioLevel = level;
|
||||
}
|
||||
|
@ -545,7 +545,7 @@ void MediaManager::sendOutgoingMediaStateMessage() {
|
|||
|
||||
void MediaManager::beginStatsTimer(int timeoutMs) {
|
||||
const auto weak = std::weak_ptr<MediaManager>(shared_from_this());
|
||||
_thread->PostDelayedTask([weak]() {
|
||||
_thread->PostDelayedTask(RTC_FROM_HERE, [weak]() {
|
||||
auto strong = weak.lock();
|
||||
if (!strong) {
|
||||
return;
|
||||
|
@ -556,7 +556,7 @@ void MediaManager::beginStatsTimer(int timeoutMs) {
|
|||
|
||||
void MediaManager::beginLevelsTimer(int timeoutMs) {
|
||||
const auto weak = std::weak_ptr<MediaManager>(shared_from_this());
|
||||
_thread->PostDelayedTask([weak]() {
|
||||
_thread->PostDelayedTask(RTC_FROM_HERE, [weak]() {
|
||||
auto strong = weak.lock();
|
||||
if (!strong) {
|
||||
return;
|
||||
|
@ -653,7 +653,7 @@ void MediaManager::setSendVideo(std::shared_ptr<VideoCaptureInterface> videoCapt
|
|||
const auto object = GetVideoCaptureAssumingSameThread(_videoCapture.get());
|
||||
_isScreenCapture = object->isScreenCapture();
|
||||
object->setStateUpdated([=](VideoState state) {
|
||||
thread->PostTask([=] {
|
||||
thread->PostTask(RTC_FROM_HERE, [=] {
|
||||
if (const auto strong = weak.lock()) {
|
||||
strong->setOutgoingVideoState(state);
|
||||
}
|
||||
|
|
|
@ -274,7 +274,7 @@ void NetworkManager::logCurrentNetworkState() {
|
|||
|
||||
void NetworkManager::checkConnectionTimeout() {
|
||||
const auto weak = std::weak_ptr<NetworkManager>(shared_from_this());
|
||||
_thread->PostDelayedTask([weak]() {
|
||||
_thread->PostDelayedTask(RTC_FROM_HERE, [weak]() {
|
||||
auto strong = weak.lock();
|
||||
if (!strong) {
|
||||
return;
|
||||
|
|
|
@ -19,20 +19,20 @@ public:
|
|||
_thread(thread),
|
||||
_valueHolder(std::make_unique<ValueHolder>()) {
|
||||
assert(_thread != nullptr);
|
||||
_thread->PostTask([valueHolder = _valueHolder.get(), generator = std::forward<Generator>(generator)]() mutable {
|
||||
_thread->PostTask(RTC_FROM_HERE, [valueHolder = _valueHolder.get(), generator = std::forward<Generator>(generator)]() mutable {
|
||||
valueHolder->_value.reset(generator());
|
||||
});
|
||||
}
|
||||
|
||||
~ThreadLocalObject() {
|
||||
_thread->PostTask([valueHolder = std::move(_valueHolder)](){
|
||||
_thread->PostTask(RTC_FROM_HERE, [valueHolder = std::move(_valueHolder)](){
|
||||
valueHolder->_value.reset();
|
||||
});
|
||||
}
|
||||
|
||||
template <typename FunctorT>
|
||||
void perform(const rtc::Location& posted_from, FunctorT &&functor) {
|
||||
_thread->PostTask([valueHolder = _valueHolder.get(), f = std::forward<FunctorT>(functor)]() mutable {
|
||||
_thread->PostTask(posted_from, [valueHolder = _valueHolder.get(), f = std::forward<FunctorT>(functor)]() mutable {
|
||||
assert(valueHolder->_value != nullptr);
|
||||
f(valueHolder->_value.get());
|
||||
});
|
||||
|
|
|
@ -929,8 +929,7 @@ public:
|
|||
|
||||
std::string streamId = std::string("stream") + ssrc.name();
|
||||
|
||||
_audioChannel = _channelManager->CreateVoiceChannel(_call, cricket::MediaConfig(), std::string("audio") + uint32ToString(ssrc.networkSsrc), false, GroupNetworkManager::getDefaulCryptoOptions(), audioOptions);
|
||||
_audioChannel->SetRtpTransport(rtpTransport);
|
||||
_audioChannel = _channelManager->CreateVoiceChannel(_call, cricket::MediaConfig(), rtpTransport, _threads->getWorkerThread(), std::string("audio") + uint32ToString(ssrc.networkSsrc), false, GroupNetworkManager::getDefaulCryptoOptions(), randomIdGenerator, audioOptions);
|
||||
|
||||
const uint8_t opusPTimeMs = 120;
|
||||
|
||||
|
@ -967,9 +966,8 @@ public:
|
|||
streamParams.set_stream_ids({ streamId });
|
||||
incomingAudioDescription->AddStream(streamParams);
|
||||
|
||||
std::string error_desc;
|
||||
_audioChannel->SetLocalContent(outgoingAudioDescription.get(), webrtc::SdpType::kOffer, error_desc);
|
||||
_audioChannel->SetRemoteContent(incomingAudioDescription.get(), webrtc::SdpType::kAnswer, error_desc);
|
||||
_audioChannel->SetLocalContent(outgoingAudioDescription.get(), webrtc::SdpType::kOffer, nullptr);
|
||||
_audioChannel->SetRemoteContent(incomingAudioDescription.get(), webrtc::SdpType::kAnswer, nullptr);
|
||||
_audioChannel->SetPayloadTypeDemuxingEnabled(false);
|
||||
|
||||
outgoingAudioDescription.reset();
|
||||
|
@ -1104,11 +1102,10 @@ public:
|
|||
|
||||
incomingVideoDescription->AddStream(videoRecvStreamParams);
|
||||
|
||||
_videoChannel = _channelManager->CreateVideoChannel(_call, cricket::MediaConfig(), std::string("video") + uint32ToString(mid), false, GroupNetworkManager::getDefaulCryptoOptions(), cricket::VideoOptions(), _videoBitrateAllocatorFactory.get());
|
||||
_videoChannel->SetRtpTransport(rtpTransport);
|
||||
std::string error_desc;
|
||||
_videoChannel->SetLocalContent(outgoingVideoDescription.get(), webrtc::SdpType::kOffer, error_desc);
|
||||
_videoChannel->SetRemoteContent(incomingVideoDescription.get(), webrtc::SdpType::kAnswer, error_desc);
|
||||
_videoChannel = _channelManager->CreateVideoChannel(_call, cricket::MediaConfig(), rtpTransport, _threads->getWorkerThread(), std::string("video") + uint32ToString(mid), false, GroupNetworkManager::getDefaulCryptoOptions(), randomIdGenerator, cricket::VideoOptions(), _videoBitrateAllocatorFactory.get());
|
||||
|
||||
_videoChannel->SetLocalContent(outgoingVideoDescription.get(), webrtc::SdpType::kOffer, nullptr);
|
||||
_videoChannel->SetRemoteContent(incomingVideoDescription.get(), webrtc::SdpType::kAnswer, nullptr);
|
||||
_videoChannel->SetPayloadTypeDemuxingEnabled(false);
|
||||
_videoChannel->media_channel()->SetSink(_mainVideoSsrc, _videoSink.get());
|
||||
|
||||
|
@ -1472,7 +1469,7 @@ public:
|
|||
_networkManager.reset(new ThreadLocalObject<GroupNetworkManager>(_threads->getNetworkThread(), [weak, threads = _threads] () mutable {
|
||||
return new GroupNetworkManager(
|
||||
[=](const GroupNetworkManager::State &state) {
|
||||
threads->getMediaThread()->PostTask([=] {
|
||||
threads->getMediaThread()->PostTask(RTC_FROM_HERE, [=] {
|
||||
const auto strong = weak.lock();
|
||||
if (!strong) {
|
||||
return;
|
||||
|
@ -1484,28 +1481,28 @@ public:
|
|||
if (!isUnresolved) {
|
||||
return;
|
||||
}
|
||||
threads->getMediaThread()->PostTask([weak, message, isUnresolved]() mutable {
|
||||
threads->getMediaThread()->PostTask(RTC_FROM_HERE, [weak, message, isUnresolved]() mutable {
|
||||
if (const auto strong = weak.lock()) {
|
||||
strong->receivePacket(message, isUnresolved);
|
||||
}
|
||||
});
|
||||
},
|
||||
[=](bool isDataChannelOpen) {
|
||||
threads->getMediaThread()->PostTask([weak, isDataChannelOpen]() mutable {
|
||||
threads->getMediaThread()->PostTask(RTC_FROM_HERE, [weak, isDataChannelOpen]() mutable {
|
||||
if (const auto strong = weak.lock()) {
|
||||
strong->updateIsDataChannelOpen(isDataChannelOpen);
|
||||
}
|
||||
});
|
||||
},
|
||||
[=](std::string const &message) {
|
||||
threads->getMediaThread()->PostTask([weak, message]() {
|
||||
threads->getMediaThread()->PostTask(RTC_FROM_HERE, [weak, message]() {
|
||||
if (const auto strong = weak.lock()) {
|
||||
strong->receiveDataChannelMessage(message);
|
||||
}
|
||||
});
|
||||
},
|
||||
[=](uint32_t ssrc, uint8_t audioLevel, bool isSpeech) {
|
||||
threads->getMediaThread()->PostTask([weak, ssrc, audioLevel, isSpeech]() {
|
||||
threads->getMediaThread()->PostTask(RTC_FROM_HERE, [weak, ssrc, audioLevel, isSpeech]() {
|
||||
if (const auto strong = weak.lock()) {
|
||||
strong->updateSsrcAudioLevel(ssrc, audioLevel, isSpeech);
|
||||
}
|
||||
|
@ -1521,7 +1518,7 @@ public:
|
|||
|
||||
#if USE_RNNOISE
|
||||
audioProcessor = std::make_unique<AudioCapturePostProcessor>([weak, threads = _threads](GroupLevelValue const &level) {
|
||||
threads->getMediaThread()->PostTask([weak, level](){
|
||||
threads->getMediaThread()->PostTask(RTC_FROM_HERE, [weak, level](){
|
||||
auto strong = weak.lock();
|
||||
if (!strong) {
|
||||
return;
|
||||
|
@ -1654,8 +1651,8 @@ public:
|
|||
if (_videoContentType == VideoContentType::Screencast) {
|
||||
videoOptions.is_screencast = true;
|
||||
}
|
||||
_outgoingVideoChannel = _channelManager->CreateVideoChannel(_call.get(), cricket::MediaConfig(), "1", false, GroupNetworkManager::getDefaulCryptoOptions(), videoOptions, _videoBitrateAllocatorFactory.get());
|
||||
_outgoingVideoChannel->SetRtpTransport(_rtpTransport);
|
||||
_outgoingVideoChannel = _channelManager->CreateVideoChannel(_call.get(), cricket::MediaConfig(), _rtpTransport, _threads->getWorkerThread(), "1", false, GroupNetworkManager::getDefaulCryptoOptions(), _uniqueRandomIdGenerator.get(), videoOptions, _videoBitrateAllocatorFactory.get());
|
||||
|
||||
if (!_outgoingVideoChannel) {
|
||||
RTC_LOG(LS_ERROR) << "Could not create outgoing video channel.";
|
||||
return;
|
||||
|
@ -1715,9 +1712,8 @@ public:
|
|||
incomingVideoDescription->set_bandwidth(1300000);
|
||||
|
||||
_threads->getWorkerThread()->Invoke<void>(RTC_FROM_HERE, [&]() {
|
||||
std::string error_desc;
|
||||
_outgoingVideoChannel->SetRemoteContent(incomingVideoDescription.get(), webrtc::SdpType::kAnswer, error_desc);
|
||||
_outgoingVideoChannel->SetLocalContent(outgoingVideoDescription.get(), webrtc::SdpType::kOffer, error_desc);
|
||||
_outgoingVideoChannel->SetRemoteContent(incomingVideoDescription.get(), webrtc::SdpType::kAnswer, nullptr);
|
||||
_outgoingVideoChannel->SetLocalContent(outgoingVideoDescription.get(), webrtc::SdpType::kOffer, nullptr);
|
||||
_outgoingVideoChannel->SetPayloadTypeDemuxingEnabled(false);
|
||||
});
|
||||
|
||||
|
@ -1851,21 +1847,21 @@ public:
|
|||
audioOptions.auto_gain_control = false;
|
||||
audioOptions.highpass_filter = false;
|
||||
audioOptions.typing_detection = false;
|
||||
// audioOptions.experimental_agc = false;
|
||||
// audioOptions.experimental_ns = false;
|
||||
audioOptions.experimental_agc = false;
|
||||
audioOptions.experimental_ns = false;
|
||||
audioOptions.residual_echo_detector = false;
|
||||
} else {
|
||||
audioOptions.echo_cancellation = true;
|
||||
audioOptions.noise_suppression = true;
|
||||
// audioOptions.experimental_ns = true;
|
||||
audioOptions.experimental_ns = true;
|
||||
audioOptions.residual_echo_detector = true;
|
||||
}
|
||||
|
||||
std::vector<std::string> streamIds;
|
||||
streamIds.push_back("1");
|
||||
|
||||
_outgoingAudioChannel = _channelManager->CreateVoiceChannel(_call.get(), cricket::MediaConfig(), "0", false, GroupNetworkManager::getDefaulCryptoOptions(), audioOptions);
|
||||
_outgoingAudioChannel->SetRtpTransport(_rtpTransport);
|
||||
_outgoingAudioChannel = _channelManager->CreateVoiceChannel(_call.get(), cricket::MediaConfig(), _rtpTransport, _threads->getWorkerThread(), "0", false, GroupNetworkManager::getDefaulCryptoOptions(), _uniqueRandomIdGenerator.get(), audioOptions);
|
||||
|
||||
const uint8_t opusMinBitrateKbps = _outgoingAudioBitrateKbit;
|
||||
const uint8_t opusMaxBitrateKbps = _outgoingAudioBitrateKbit;
|
||||
const uint8_t opusStartBitrateKbps = _outgoingAudioBitrateKbit;
|
||||
|
@ -1901,9 +1897,8 @@ public:
|
|||
incomingAudioDescription->set_bandwidth(1300000);
|
||||
|
||||
_threads->getWorkerThread()->Invoke<void>(RTC_FROM_HERE, [&]() {
|
||||
std::string error_desc;
|
||||
_outgoingAudioChannel->SetLocalContent(outgoingAudioDescription.get(), webrtc::SdpType::kOffer, error_desc);
|
||||
_outgoingAudioChannel->SetRemoteContent(incomingAudioDescription.get(), webrtc::SdpType::kAnswer, error_desc);
|
||||
_outgoingAudioChannel->SetLocalContent(outgoingAudioDescription.get(), webrtc::SdpType::kOffer, nullptr);
|
||||
_outgoingAudioChannel->SetRemoteContent(incomingAudioDescription.get(), webrtc::SdpType::kAnswer, nullptr);
|
||||
_outgoingAudioChannel->SetPayloadTypeDemuxingEnabled(false);
|
||||
_outgoingAudioChannel->Enable(true);
|
||||
});
|
||||
|
@ -1949,7 +1944,7 @@ public:
|
|||
|
||||
void beginLevelsTimer(int timeoutMs) {
|
||||
const auto weak = std::weak_ptr<GroupInstanceCustomInternal>(shared_from_this());
|
||||
_threads->getMediaThread()->PostDelayedTask([weak]() {
|
||||
_threads->getMediaThread()->PostDelayedTask(RTC_FROM_HERE, [weak]() {
|
||||
auto strong = weak.lock();
|
||||
if (!strong) {
|
||||
return;
|
||||
|
@ -2010,7 +2005,7 @@ public:
|
|||
|
||||
void beginAudioChannelCleanupTimer(int delayMs) {
|
||||
const auto weak = std::weak_ptr<GroupInstanceCustomInternal>(shared_from_this());
|
||||
_threads->getMediaThread()->PostDelayedTask([weak]() {
|
||||
_threads->getMediaThread()->PostDelayedTask(RTC_FROM_HERE, [weak]() {
|
||||
auto strong = weak.lock();
|
||||
if (!strong) {
|
||||
return;
|
||||
|
@ -2039,7 +2034,7 @@ public:
|
|||
|
||||
void beginRemoteConstraintsUpdateTimer(int delayMs) {
|
||||
const auto weak = std::weak_ptr<GroupInstanceCustomInternal>(shared_from_this());
|
||||
_threads->getMediaThread()->PostDelayedTask([weak]() {
|
||||
_threads->getMediaThread()->PostDelayedTask(RTC_FROM_HERE, [weak]() {
|
||||
auto strong = weak.lock();
|
||||
if (!strong) {
|
||||
return;
|
||||
|
@ -2053,7 +2048,7 @@ public:
|
|||
|
||||
void beginNetworkStatusTimer(int delayMs) {
|
||||
const auto weak = std::weak_ptr<GroupInstanceCustomInternal>(shared_from_this());
|
||||
_threads->getMediaThread()->PostDelayedTask([weak]() {
|
||||
_threads->getMediaThread()->PostDelayedTask(RTC_FROM_HERE, [weak]() {
|
||||
auto strong = weak.lock();
|
||||
if (!strong) {
|
||||
return;
|
||||
|
@ -2362,7 +2357,7 @@ public:
|
|||
}
|
||||
|
||||
void receiveRtcpPacket(rtc::CopyOnWriteBuffer const &packet, int64_t timestamp) {
|
||||
_threads->getWorkerThread()->PostTask([this, packet, timestamp]() {
|
||||
_threads->getWorkerThread()->PostTask(RTC_FROM_HERE, [this, packet, timestamp]() {
|
||||
_call->Receiver()->DeliverPacket(webrtc::MediaType::ANY, packet, timestamp);
|
||||
});
|
||||
}
|
||||
|
@ -2393,7 +2388,7 @@ public:
|
|||
_pendingOutgoingVideoConstraintRequestId += 1;
|
||||
|
||||
const auto weak = std::weak_ptr<GroupInstanceCustomInternal>(shared_from_this());
|
||||
_threads->getMediaThread()->PostDelayedTask([weak, requestId]() {
|
||||
_threads->getMediaThread()->PostDelayedTask(RTC_FROM_HERE, [weak, requestId]() {
|
||||
auto strong = weak.lock();
|
||||
if (!strong) {
|
||||
return;
|
||||
|
@ -2493,7 +2488,7 @@ public:
|
|||
|
||||
const auto weak = std::weak_ptr<GroupInstanceCustomInternal>(shared_from_this());
|
||||
auto task = _requestMediaChannelDescriptions(requestSsrcs, [weak, threads = _threads, requestId](std::vector<MediaChannelDescription> &&descriptions) {
|
||||
threads->getWorkerThread()->PostTask([weak, requestId, descriptions = std::move(descriptions)]() mutable {
|
||||
threads->getWorkerThread()->PostTask(RTC_FROM_HERE, [weak, requestId, descriptions = std::move(descriptions)]() mutable {
|
||||
auto strong = weak.lock();
|
||||
if (!strong) {
|
||||
return;
|
||||
|
@ -3046,7 +3041,7 @@ public:
|
|||
if (ssrc.actualSsrc != ssrc.networkSsrc) {
|
||||
if (_audioLevelsUpdated) {
|
||||
onAudioSinkUpdate = [weak, ssrc = ssrc, threads = _threads](AudioSinkImpl::Update update) {
|
||||
threads->getMediaThread()->PostTask([weak, ssrc, update]() {
|
||||
threads->getMediaThread()->PostTask(RTC_FROM_HERE, [weak, ssrc, update]() {
|
||||
auto strong = weak.lock();
|
||||
if (!strong) {
|
||||
return;
|
||||
|
|
|
@ -509,7 +509,7 @@ webrtc::RtpTransport *GroupNetworkManager::getRtpTransport() {
|
|||
|
||||
void GroupNetworkManager::checkConnectionTimeout() {
|
||||
const auto weak = std::weak_ptr<GroupNetworkManager>(shared_from_this());
|
||||
_threads->getNetworkThread()->PostDelayedTask([weak]() {
|
||||
_threads->getNetworkThread()->PostDelayedTask(RTC_FROM_HERE, [weak]() {
|
||||
auto strong = weak.lock();
|
||||
if (!strong) {
|
||||
return;
|
||||
|
@ -553,7 +553,7 @@ void GroupNetworkManager::DtlsReadyToSend(bool isReadyToSend) {
|
|||
|
||||
if (isReadyToSend) {
|
||||
const auto weak = std::weak_ptr<GroupNetworkManager>(shared_from_this());
|
||||
_threads->getNetworkThread()->PostTask([weak]() {
|
||||
_threads->getNetworkThread()->PostTask(RTC_FROM_HERE, [weak]() {
|
||||
const auto strong = weak.lock();
|
||||
if (!strong) {
|
||||
return;
|
||||
|
|
|
@ -258,7 +258,7 @@ public:
|
|||
|
||||
void beginRenderTimer(int timeoutMs) {
|
||||
const auto weak = std::weak_ptr<StreamingMediaContextPrivate>(shared_from_this());
|
||||
_threads->getMediaThread()->PostDelayedTask([weak]() {
|
||||
_threads->getMediaThread()->PostDelayedTask(RTC_FROM_HERE, [weak]() {
|
||||
auto strong = weak.lock();
|
||||
if (!strong) {
|
||||
return;
|
||||
|
@ -572,7 +572,7 @@ public:
|
|||
if (!_pendingRequestTimeTask && _pendingRequestTimeDelayTaskId == 0) {
|
||||
const auto weak = std::weak_ptr<StreamingMediaContextPrivate>(shared_from_this());
|
||||
_pendingRequestTimeTask = _requestCurrentTime([weak, threads = _threads](int64_t timestamp) {
|
||||
threads->getMediaThread()->PostTask([weak, timestamp]() {
|
||||
threads->getMediaThread()->PostTask(RTC_FROM_HERE, [weak, timestamp]() {
|
||||
auto strong = weak.lock();
|
||||
if (!strong) {
|
||||
return;
|
||||
|
@ -590,7 +590,7 @@ public:
|
|||
strong->_pendingRequestTimeDelayTaskId = taskId;
|
||||
strong->_nextPendingRequestTimeDelayTaskId++;
|
||||
|
||||
strong->_threads->getMediaThread()->PostDelayedTask([weak, taskId]() {
|
||||
strong->_threads->getMediaThread()->PostDelayedTask(RTC_FROM_HERE, [weak, taskId]() {
|
||||
auto strong = weak.lock();
|
||||
if (!strong) {
|
||||
return;
|
||||
|
@ -770,7 +770,7 @@ public:
|
|||
const auto weakPart = std::weak_ptr<PendingMediaSegmentPart>(part);
|
||||
|
||||
std::function<void(BroadcastPart &&)> handleResult = [weak, weakSegment, weakPart, threads = _threads, segmentTimestamp](BroadcastPart &&part) {
|
||||
threads->getMediaThread()->PostTask([weak, weakSegment, weakPart, part = std::move(part), segmentTimestamp]() mutable {
|
||||
threads->getMediaThread()->PostTask(RTC_FROM_HERE, [weak, weakSegment, weakPart, part = std::move(part), segmentTimestamp]() mutable {
|
||||
auto strong = weak.lock();
|
||||
if (!strong) {
|
||||
return;
|
||||
|
@ -885,7 +885,7 @@ public:
|
|||
|
||||
if (minDelayedRequestTimeout < INT32_MAX) {
|
||||
const auto weak = std::weak_ptr<StreamingMediaContextPrivate>(shared_from_this());
|
||||
_threads->getMediaThread()->PostDelayedTask([weak]() {
|
||||
_threads->getMediaThread()->PostDelayedTask(RTC_FROM_HERE, [weak]() {
|
||||
auto strong = weak.lock();
|
||||
if (!strong) {
|
||||
return;
|
||||
|
@ -904,7 +904,7 @@ public:
|
|||
const auto weakPart = std::weak_ptr<PendingMediaSegmentPart>(part);
|
||||
|
||||
std::function<void(BroadcastPart &&)> handleResult = [weak, weakPart, threads = _threads, completion](BroadcastPart &&part) {
|
||||
threads->getMediaThread()->PostTask([weak, weakPart, part = std::move(part), completion]() mutable {
|
||||
threads->getMediaThread()->PostTask(RTC_FROM_HERE, [weak, weakPart, part = std::move(part), completion]() mutable {
|
||||
auto strong = weak.lock();
|
||||
if (!strong) {
|
||||
return;
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
|
||||
#include "api/audio/channel_layout.h"
|
||||
#include "api/rtp_packet_infos.h"
|
||||
#include "rtc_base/constructor_magic.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
|
@ -59,9 +60,6 @@ class AudioFrame {
|
|||
|
||||
AudioFrame();
|
||||
|
||||
AudioFrame(const AudioFrame&) = delete;
|
||||
AudioFrame& operator=(const AudioFrame&) = delete;
|
||||
|
||||
// Resets all members to their default state.
|
||||
void Reset();
|
||||
// Same as Reset(), but leaves mute state unchanged. Muting a frame requires
|
||||
|
@ -168,6 +166,8 @@ class AudioFrame {
|
|||
// capture timestamp of a received frame is found in `packet_infos_`.
|
||||
// This timestamp MUST be based on the same clock as rtc::TimeMillis().
|
||||
absl::optional<int64_t> absolute_capture_timestamp_ms_;
|
||||
|
||||
RTC_DISALLOW_COPY_AND_ASSIGN(AudioFrame);
|
||||
};
|
||||
|
||||
} // namespace webrtc
|
||||
|
|
|
@ -112,7 +112,7 @@ struct RTC_EXPORT EchoCanceller3Config {
|
|||
bool echo_can_saturate = true;
|
||||
bool bounded_erl = false;
|
||||
bool erle_onset_compensation_in_dominant_nearend = false;
|
||||
bool use_conservative_tail_frequency_response = true;
|
||||
bool use_conservative_tail_frequency_response = false;
|
||||
} ep_strength;
|
||||
|
||||
struct EchoAudibility {
|
||||
|
|
|
@ -20,6 +20,7 @@
|
|||
#include "absl/types/optional.h"
|
||||
#include "api/array_view.h"
|
||||
#include "rtc_base/buffer.h"
|
||||
#include "rtc_base/constructor_magic.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
|
@ -36,9 +37,6 @@ class AudioDecoder {
|
|||
AudioDecoder() = default;
|
||||
virtual ~AudioDecoder() = default;
|
||||
|
||||
AudioDecoder(const AudioDecoder&) = delete;
|
||||
AudioDecoder& operator=(const AudioDecoder&) = delete;
|
||||
|
||||
class EncodedAudioFrame {
|
||||
public:
|
||||
struct DecodeResult {
|
||||
|
@ -189,6 +187,9 @@ class AudioDecoder {
|
|||
int sample_rate_hz,
|
||||
int16_t* decoded,
|
||||
SpeechType* speech_type);
|
||||
|
||||
private:
|
||||
RTC_DISALLOW_COPY_AND_ASSIGN(AudioDecoder);
|
||||
};
|
||||
|
||||
} // namespace webrtc
|
||||
|
|
|
@ -55,11 +55,16 @@ void AudioOptions::SetAll(const AudioOptions& change) {
|
|||
SetFrom(&audio_jitter_buffer_enable_rtx_handling,
|
||||
change.audio_jitter_buffer_enable_rtx_handling);
|
||||
SetFrom(&typing_detection, change.typing_detection);
|
||||
SetFrom(&experimental_agc, change.experimental_agc);
|
||||
SetFrom(&experimental_ns, change.experimental_ns);
|
||||
SetFrom(&residual_echo_detector, change.residual_echo_detector);
|
||||
SetFrom(&tx_agc_target_dbov, change.tx_agc_target_dbov);
|
||||
SetFrom(&tx_agc_digital_compression_gain,
|
||||
change.tx_agc_digital_compression_gain);
|
||||
SetFrom(&tx_agc_limiter, change.tx_agc_limiter);
|
||||
SetFrom(&combined_audio_video_bwe, change.combined_audio_video_bwe);
|
||||
SetFrom(&audio_network_adaptor, change.audio_network_adaptor);
|
||||
SetFrom(&audio_network_adaptor_config, change.audio_network_adaptor_config);
|
||||
SetFrom(&init_recording_on_send, change.init_recording_on_send);
|
||||
}
|
||||
|
||||
bool AudioOptions::operator==(const AudioOptions& o) const {
|
||||
|
@ -79,11 +84,15 @@ bool AudioOptions::operator==(const AudioOptions& o) const {
|
|||
audio_jitter_buffer_enable_rtx_handling ==
|
||||
o.audio_jitter_buffer_enable_rtx_handling &&
|
||||
typing_detection == o.typing_detection &&
|
||||
experimental_agc == o.experimental_agc &&
|
||||
experimental_ns == o.experimental_ns &&
|
||||
residual_echo_detector == o.residual_echo_detector &&
|
||||
tx_agc_target_dbov == o.tx_agc_target_dbov &&
|
||||
tx_agc_digital_compression_gain == o.tx_agc_digital_compression_gain &&
|
||||
tx_agc_limiter == o.tx_agc_limiter &&
|
||||
combined_audio_video_bwe == o.combined_audio_video_bwe &&
|
||||
audio_network_adaptor == o.audio_network_adaptor &&
|
||||
audio_network_adaptor_config == o.audio_network_adaptor_config &&
|
||||
init_recording_on_send == o.init_recording_on_send;
|
||||
audio_network_adaptor_config == o.audio_network_adaptor_config;
|
||||
}
|
||||
|
||||
std::string AudioOptions::ToString() const {
|
||||
|
@ -108,10 +117,15 @@ std::string AudioOptions::ToString() const {
|
|||
ToStringIfSet(&result, "audio_jitter_buffer_enable_rtx_handling",
|
||||
audio_jitter_buffer_enable_rtx_handling);
|
||||
ToStringIfSet(&result, "typing", typing_detection);
|
||||
ToStringIfSet(&result, "experimental_agc", experimental_agc);
|
||||
ToStringIfSet(&result, "experimental_ns", experimental_ns);
|
||||
ToStringIfSet(&result, "residual_echo_detector", residual_echo_detector);
|
||||
ToStringIfSet(&result, "tx_agc_target_dbov", tx_agc_target_dbov);
|
||||
ToStringIfSet(&result, "tx_agc_digital_compression_gain",
|
||||
tx_agc_digital_compression_gain);
|
||||
ToStringIfSet(&result, "tx_agc_limiter", tx_agc_limiter);
|
||||
ToStringIfSet(&result, "combined_audio_video_bwe", combined_audio_video_bwe);
|
||||
ToStringIfSet(&result, "audio_network_adaptor", audio_network_adaptor);
|
||||
ToStringIfSet(&result, "init_recording_on_send", init_recording_on_send);
|
||||
result << "}";
|
||||
return result.str();
|
||||
}
|
||||
|
|
|
@ -60,14 +60,15 @@ struct RTC_EXPORT AudioOptions {
|
|||
absl::optional<int> audio_jitter_buffer_min_delay_ms;
|
||||
// Audio receiver jitter buffer (NetEq) should handle retransmitted packets.
|
||||
absl::optional<bool> audio_jitter_buffer_enable_rtx_handling;
|
||||
// Deprecated.
|
||||
// TODO(bugs.webrtc.org/11226): Remove.
|
||||
// Audio processing to detect typing.
|
||||
absl::optional<bool> typing_detection;
|
||||
// TODO(bugs.webrtc.org/11539): Deprecated, replaced by
|
||||
// webrtc::CreateEchoDetector() and injection when creating the audio
|
||||
// processing module.
|
||||
absl::optional<bool> experimental_agc;
|
||||
absl::optional<bool> experimental_ns;
|
||||
// Note that tx_agc_* only applies to non-experimental AGC.
|
||||
absl::optional<bool> residual_echo_detector;
|
||||
absl::optional<uint16_t> tx_agc_target_dbov;
|
||||
absl::optional<uint16_t> tx_agc_digital_compression_gain;
|
||||
absl::optional<bool> tx_agc_limiter;
|
||||
// Enable combined audio+bandwidth BWE.
|
||||
// TODO(pthatcher): This flag is set from the
|
||||
// "googCombinedAudioVideoBwe", but not used anywhere. So delete it,
|
||||
|
@ -79,10 +80,6 @@ struct RTC_EXPORT AudioOptions {
|
|||
absl::optional<bool> audio_network_adaptor;
|
||||
// Config string for audio network adaptor.
|
||||
absl::optional<std::string> audio_network_adaptor_config;
|
||||
// Pre-initialize the ADM for recording when starting to send. Default to
|
||||
// true.
|
||||
// TODO(webrtc:13566): Remove this option. See issue for details.
|
||||
absl::optional<bool> init_recording_on_send;
|
||||
};
|
||||
|
||||
} // namespace cricket
|
||||
|
|
|
@ -49,10 +49,6 @@ rtc::scoped_refptr<PeerConnectionFactoryInterface> CreatePeerConnectionFactory(
|
|||
dependencies.task_queue_factory.get());
|
||||
dependencies.trials = std::make_unique<webrtc::FieldTrialBasedConfig>();
|
||||
|
||||
if (network_thread) {
|
||||
// TODO(bugs.webrtc.org/13145): Add an rtc::SocketFactory* argument.
|
||||
dependencies.socket_factory = network_thread->socketserver();
|
||||
}
|
||||
cricket::MediaEngineDependencies media_dependencies;
|
||||
media_dependencies.task_queue_factory = dependencies.task_queue_factory.get();
|
||||
media_dependencies.adm = std::move(default_adm);
|
||||
|
|
|
@ -20,27 +20,11 @@ DtlsTransportInformation::DtlsTransportInformation(DtlsTransportState state)
|
|||
|
||||
DtlsTransportInformation::DtlsTransportInformation(
|
||||
DtlsTransportState state,
|
||||
absl::optional<DtlsTransportTlsRole> role,
|
||||
absl::optional<int> tls_version,
|
||||
absl::optional<int> ssl_cipher_suite,
|
||||
absl::optional<int> srtp_cipher_suite,
|
||||
std::unique_ptr<rtc::SSLCertChain> remote_ssl_certificates)
|
||||
: state_(state),
|
||||
role_(role),
|
||||
tls_version_(tls_version),
|
||||
ssl_cipher_suite_(ssl_cipher_suite),
|
||||
srtp_cipher_suite_(srtp_cipher_suite),
|
||||
remote_ssl_certificates_(std::move(remote_ssl_certificates)) {}
|
||||
|
||||
// Deprecated version
|
||||
DtlsTransportInformation::DtlsTransportInformation(
|
||||
DtlsTransportState state,
|
||||
absl::optional<int> tls_version,
|
||||
absl::optional<int> ssl_cipher_suite,
|
||||
absl::optional<int> srtp_cipher_suite,
|
||||
std::unique_ptr<rtc::SSLCertChain> remote_ssl_certificates)
|
||||
: state_(state),
|
||||
role_(absl::nullopt),
|
||||
tls_version_(tls_version),
|
||||
ssl_cipher_suite_(ssl_cipher_suite),
|
||||
srtp_cipher_suite_(srtp_cipher_suite),
|
||||
|
@ -49,7 +33,6 @@ DtlsTransportInformation::DtlsTransportInformation(
|
|||
DtlsTransportInformation::DtlsTransportInformation(
|
||||
const DtlsTransportInformation& c)
|
||||
: state_(c.state()),
|
||||
role_(c.role_),
|
||||
tls_version_(c.tls_version_),
|
||||
ssl_cipher_suite_(c.ssl_cipher_suite_),
|
||||
srtp_cipher_suite_(c.srtp_cipher_suite_),
|
||||
|
@ -60,7 +43,6 @@ DtlsTransportInformation::DtlsTransportInformation(
|
|||
DtlsTransportInformation& DtlsTransportInformation::operator=(
|
||||
const DtlsTransportInformation& c) {
|
||||
state_ = c.state();
|
||||
role_ = c.role_;
|
||||
tls_version_ = c.tls_version_;
|
||||
ssl_cipher_suite_ = c.ssl_cipher_suite_;
|
||||
srtp_cipher_suite_ = c.srtp_cipher_suite_;
|
||||
|
|
|
@ -36,11 +36,6 @@ enum class DtlsTransportState {
|
|||
kNumValues
|
||||
};
|
||||
|
||||
enum class DtlsTransportTlsRole {
|
||||
kServer, // Other end sends CLIENT_HELLO
|
||||
kClient // This end sends CLIENT_HELLO
|
||||
};
|
||||
|
||||
// This object gives snapshot information about the changeable state of a
|
||||
// DTLSTransport.
|
||||
class RTC_EXPORT DtlsTransportInformation {
|
||||
|
@ -49,19 +44,10 @@ class RTC_EXPORT DtlsTransportInformation {
|
|||
explicit DtlsTransportInformation(DtlsTransportState state);
|
||||
DtlsTransportInformation(
|
||||
DtlsTransportState state,
|
||||
absl::optional<DtlsTransportTlsRole> role,
|
||||
absl::optional<int> tls_version,
|
||||
absl::optional<int> ssl_cipher_suite,
|
||||
absl::optional<int> srtp_cipher_suite,
|
||||
std::unique_ptr<rtc::SSLCertChain> remote_ssl_certificates);
|
||||
ABSL_DEPRECATED("Use version with role parameter")
|
||||
DtlsTransportInformation(
|
||||
DtlsTransportState state,
|
||||
absl::optional<int> tls_version,
|
||||
absl::optional<int> ssl_cipher_suite,
|
||||
absl::optional<int> srtp_cipher_suite,
|
||||
std::unique_ptr<rtc::SSLCertChain> remote_ssl_certificates);
|
||||
|
||||
// Copy and assign
|
||||
DtlsTransportInformation(const DtlsTransportInformation& c);
|
||||
DtlsTransportInformation& operator=(const DtlsTransportInformation& c);
|
||||
|
@ -71,7 +57,6 @@ class RTC_EXPORT DtlsTransportInformation {
|
|||
default;
|
||||
|
||||
DtlsTransportState state() const { return state_; }
|
||||
absl::optional<DtlsTransportTlsRole> role() const { return role_; }
|
||||
absl::optional<int> tls_version() const { return tls_version_; }
|
||||
absl::optional<int> ssl_cipher_suite() const { return ssl_cipher_suite_; }
|
||||
absl::optional<int> srtp_cipher_suite() const { return srtp_cipher_suite_; }
|
||||
|
@ -82,7 +67,6 @@ class RTC_EXPORT DtlsTransportInformation {
|
|||
|
||||
private:
|
||||
DtlsTransportState state_;
|
||||
absl::optional<DtlsTransportTlsRole> role_;
|
||||
absl::optional<int> tls_version_;
|
||||
absl::optional<int> ssl_cipher_suite_;
|
||||
absl::optional<int> srtp_cipher_suite_;
|
||||
|
|
|
@ -22,6 +22,7 @@
|
|||
|
||||
#include "api/candidate.h"
|
||||
#include "api/jsep.h"
|
||||
#include "rtc_base/constructor_magic.h"
|
||||
#include "rtc_base/system/rtc_export.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
@ -63,10 +64,6 @@ class JsepCandidateCollection : public IceCandidateCollection {
|
|||
// Move constructor is defined so that a vector of JsepCandidateCollections
|
||||
// can be resized.
|
||||
JsepCandidateCollection(JsepCandidateCollection&& o);
|
||||
|
||||
JsepCandidateCollection(const JsepCandidateCollection&) = delete;
|
||||
JsepCandidateCollection& operator=(const JsepCandidateCollection&) = delete;
|
||||
|
||||
// Returns a copy of the candidate collection.
|
||||
JsepCandidateCollection Clone() const;
|
||||
size_t count() const override;
|
||||
|
@ -83,6 +80,8 @@ class JsepCandidateCollection : public IceCandidateCollection {
|
|||
|
||||
private:
|
||||
std::vector<std::unique_ptr<JsepIceCandidate>> candidates_;
|
||||
|
||||
RTC_DISALLOW_COPY_AND_ASSIGN(JsepCandidateCollection);
|
||||
};
|
||||
|
||||
} // namespace webrtc
|
||||
|
|
|
@ -22,6 +22,7 @@
|
|||
#include "api/candidate.h"
|
||||
#include "api/jsep.h"
|
||||
#include "api/jsep_ice_candidate.h"
|
||||
#include "rtc_base/constructor_magic.h"
|
||||
|
||||
namespace cricket {
|
||||
class SessionDescription;
|
||||
|
@ -42,9 +43,6 @@ class JsepSessionDescription : public SessionDescriptionInterface {
|
|||
absl::string_view session_version);
|
||||
virtual ~JsepSessionDescription();
|
||||
|
||||
JsepSessionDescription(const JsepSessionDescription&) = delete;
|
||||
JsepSessionDescription& operator=(const JsepSessionDescription&) = delete;
|
||||
|
||||
// Takes ownership of `description`.
|
||||
bool Initialize(std::unique_ptr<cricket::SessionDescription> description,
|
||||
const std::string& session_id,
|
||||
|
@ -84,6 +82,8 @@ class JsepSessionDescription : public SessionDescriptionInterface {
|
|||
bool GetMediasectionIndex(const IceCandidateInterface* candidate,
|
||||
size_t* index);
|
||||
int GetMediasectionIndex(const cricket::Candidate& candidate);
|
||||
|
||||
RTC_DISALLOW_COPY_AND_ASSIGN(JsepSessionDescription);
|
||||
};
|
||||
|
||||
} // namespace webrtc
|
||||
|
|
|
@ -333,8 +333,6 @@ class MediaStreamInterface : public rtc::RefCountInterface,
|
|||
virtual rtc::scoped_refptr<VideoTrackInterface> FindVideoTrack(
|
||||
const std::string& track_id) = 0;
|
||||
|
||||
// Takes ownership of added tracks.
|
||||
// TODO(hta): Should take scoped_refptr rather than raw pointer.
|
||||
virtual bool AddTrack(AudioTrackInterface* track) = 0;
|
||||
virtual bool AddTrack(VideoTrackInterface* track) = 0;
|
||||
virtual bool RemoveTrack(AudioTrackInterface* track) = 0;
|
||||
|
|
|
@ -1,65 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2022 The WebRTC project authors. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#ifndef API_METRONOME_METRONOME_H_
|
||||
#define API_METRONOME_METRONOME_H_
|
||||
|
||||
#include "api/task_queue/task_queue_base.h"
|
||||
#include "api/units/time_delta.h"
|
||||
#include "rtc_base/system/rtc_export.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
// The Metronome posts OnTick() on task queues provided by its listeners' task
|
||||
// queue periodically. The metronome can be used as an alternative to using
|
||||
// PostDelayedTask on a thread or task queue for coalescing work and reducing
|
||||
// the number of idle-wakeups.
|
||||
//
|
||||
// Listeners can be added and removed from any sequence, but it is illegal to
|
||||
// remove a listener from an OnTick invocation.
|
||||
//
|
||||
// The metronome concept is still under experimentation, and may not be availble
|
||||
// in all platforms or applications. See https://crbug.com/1253787 for more
|
||||
// details.
|
||||
//
|
||||
// Metronome implementations must be thread-safe.
|
||||
class RTC_EXPORT Metronome {
|
||||
public:
|
||||
class RTC_EXPORT TickListener {
|
||||
public:
|
||||
virtual ~TickListener() = default;
|
||||
|
||||
// OnTick is run on the task queue provided by OnTickTaskQueue each time the
|
||||
// metronome ticks.
|
||||
virtual void OnTick() = 0;
|
||||
|
||||
// The task queue that OnTick will run on. Must not be null.
|
||||
virtual TaskQueueBase* OnTickTaskQueue() = 0;
|
||||
};
|
||||
|
||||
virtual ~Metronome() = default;
|
||||
|
||||
// Adds a tick listener to the metronome. Once this method has returned
|
||||
// OnTick will be invoked on each metronome tick. A listener may
|
||||
// only be added to the metronome once.
|
||||
virtual void AddListener(TickListener* listener) = 0;
|
||||
|
||||
// Removes the tick listener from the metronome. Once this method has returned
|
||||
// OnTick will never be called again. This method must not be called from
|
||||
// within OnTick.
|
||||
virtual void RemoveListener(TickListener* listener) = 0;
|
||||
|
||||
// Returns the current tick period of the metronome.
|
||||
virtual TimeDelta TickPeriod() const = 0;
|
||||
};
|
||||
|
||||
} // namespace webrtc
|
||||
|
||||
#endif // API_METRONOME_METRONOME_H_
|
|
@ -41,6 +41,12 @@ PeerConnectionInterface::RTCConfiguration::RTCConfiguration(
|
|||
|
||||
PeerConnectionInterface::RTCConfiguration::~RTCConfiguration() = default;
|
||||
|
||||
RTCError PeerConnectionInterface::RemoveTrackNew(
|
||||
rtc::scoped_refptr<RtpSenderInterface> sender) {
|
||||
return RTCError(RemoveTrack(sender) ? RTCErrorType::NONE
|
||||
: RTCErrorType::INTERNAL_ERROR);
|
||||
}
|
||||
|
||||
RTCError PeerConnectionInterface::SetConfiguration(
|
||||
const PeerConnectionInterface::RTCConfiguration& config) {
|
||||
return RTCError();
|
||||
|
|
|
@ -95,7 +95,6 @@
|
|||
#include "api/jsep.h"
|
||||
#include "api/media_stream_interface.h"
|
||||
#include "api/media_types.h"
|
||||
#include "api/metronome/metronome.h"
|
||||
#include "api/neteq/neteq_factory.h"
|
||||
#include "api/network_state_predictor.h"
|
||||
#include "api/packet_socket_factory.h"
|
||||
|
@ -170,10 +169,9 @@ class StatsObserver : public rtc::RefCountInterface {
|
|||
};
|
||||
|
||||
enum class SdpSemantics {
|
||||
// TODO(https://crbug.com/webrtc/13528): Remove support for kPlanB.
|
||||
kPlanB_DEPRECATED,
|
||||
kPlanB [[deprecated]] = kPlanB_DEPRECATED,
|
||||
kUnifiedPlan,
|
||||
kUnifiedPlan
|
||||
};
|
||||
|
||||
class RTC_EXPORT PeerConnectionInterface : public rtc::RefCountInterface {
|
||||
|
@ -624,26 +622,27 @@ class RTC_EXPORT PeerConnectionInterface : public rtc::RefCountInterface {
|
|||
// cost.
|
||||
absl::optional<rtc::AdapterType> network_preference;
|
||||
|
||||
// Configure the SDP semantics used by this PeerConnection. By default, this
|
||||
// is Unified Plan which is compliant to the WebRTC 1.0 specification. It is
|
||||
// possible to overrwite this to the deprecated Plan B SDP format, but note
|
||||
// that kPlanB will be deleted at some future date, see
|
||||
// https://crbug.com/webrtc/13528.
|
||||
// Configure the SDP semantics used by this PeerConnection. Note that the
|
||||
// WebRTC 1.0 specification requires kUnifiedPlan semantics. The
|
||||
// RtpTransceiver API is only available with kUnifiedPlan semantics.
|
||||
//
|
||||
// kUnifiedPlan will cause the PeerConnection to create offers and answers
|
||||
// with multiple m= sections where each m= section maps to one RtpSender and
|
||||
// one RtpReceiver (an RtpTransceiver), either both audio or both video.
|
||||
// This will also cause the PeerConnection to ignore all but the first
|
||||
// a=ssrc lines that form a Plan B streams (if the PeerConnection is given
|
||||
// Plan B SDP to process).
|
||||
// kUnifiedPlan will cause PeerConnection to create offers and answers with
|
||||
// multiple m= sections where each m= section maps to one RtpSender and one
|
||||
// RtpReceiver (an RtpTransceiver), either both audio or both video. This
|
||||
// will also cause PeerConnection to ignore all but the first a=ssrc lines
|
||||
// that form a Plan B stream.
|
||||
//
|
||||
// kPlanB will cause the PeerConnection to create offers and answers with at
|
||||
// kPlanB will cause PeerConnection to create offers and answers with at
|
||||
// most one audio and one video m= section with multiple RtpSenders and
|
||||
// RtpReceivers specified as multiple a=ssrc lines within the section. This
|
||||
// will also cause PeerConnection to ignore all but the first m= section of
|
||||
// the same media type (if the PeerConnection is given Unified Plan SDP to
|
||||
// process).
|
||||
SdpSemantics sdp_semantics = SdpSemantics::kUnifiedPlan;
|
||||
// the same media type.
|
||||
//
|
||||
// For users who have to interwork with legacy WebRTC implementations,
|
||||
// it is possible to specify kPlanB until the code is finally removed.
|
||||
//
|
||||
// For all other users, specify kUnifiedPlan.
|
||||
SdpSemantics sdp_semantics = SdpSemantics::kPlanB_DEPRECATED;
|
||||
|
||||
// TODO(bugs.webrtc.org/9891) - Move to crypto_options or remove.
|
||||
// Actively reset the SRTP parameters whenever the DTLS transports
|
||||
|
@ -806,25 +805,23 @@ class RTC_EXPORT PeerConnectionInterface : public rtc::RefCountInterface {
|
|||
rtc::scoped_refptr<MediaStreamTrackInterface> track,
|
||||
const std::vector<std::string>& stream_ids) = 0;
|
||||
|
||||
// Removes the connection between a MediaStreamTrack and the PeerConnection.
|
||||
// Stops sending on the RtpSender and marks the
|
||||
// Remove an RtpSender from this PeerConnection.
|
||||
// Returns true on success.
|
||||
// TODO(steveanton): Replace with signature that returns RTCError.
|
||||
virtual bool RemoveTrack(RtpSenderInterface* sender) = 0;
|
||||
|
||||
// Plan B semantics: Removes the RtpSender from this PeerConnection.
|
||||
// Unified Plan semantics: Stop sending on the RtpSender and mark the
|
||||
// corresponding RtpTransceiver direction as no longer sending.
|
||||
// https://w3c.github.io/webrtc-pc/#dom-rtcpeerconnection-removetrack
|
||||
//
|
||||
// Errors:
|
||||
// - INVALID_PARAMETER: `sender` is null or (Plan B only) the sender is not
|
||||
// associated with this PeerConnection.
|
||||
// - INVALID_STATE: PeerConnection is closed.
|
||||
//
|
||||
// Plan B semantics: Removes the RtpSender from this PeerConnection.
|
||||
//
|
||||
// TODO(bugs.webrtc.org/9534): Rename to RemoveTrack once the other signature
|
||||
// is removed; remove default implementation once upstream is updated.
|
||||
virtual RTCError RemoveTrackOrError(
|
||||
rtc::scoped_refptr<RtpSenderInterface> sender) {
|
||||
RTC_CHECK_NOTREACHED();
|
||||
return RTCError();
|
||||
}
|
||||
// is removed.
|
||||
virtual RTCError RemoveTrackNew(
|
||||
rtc::scoped_refptr<RtpSenderInterface> sender);
|
||||
|
||||
// AddTransceiver creates a new RtpTransceiver and adds it to the set of
|
||||
// transceivers. Adding a transceiver will cause future calls to CreateOffer
|
||||
|
@ -1298,6 +1295,14 @@ class PeerConnectionObserver {
|
|||
// A new ICE candidate has been gathered.
|
||||
virtual void OnIceCandidate(const IceCandidateInterface* candidate) = 0;
|
||||
|
||||
// Gathering of an ICE candidate failed.
|
||||
// See https://w3c.github.io/webrtc-pc/#event-icecandidateerror
|
||||
// `host_candidate` is a stringified socket address.
|
||||
virtual void OnIceCandidateError(const std::string& host_candidate,
|
||||
const std::string& url,
|
||||
int error_code,
|
||||
const std::string& error_text) {}
|
||||
|
||||
// Gathering of an ICE candidate failed.
|
||||
// See https://w3c.github.io/webrtc-pc/#event-icecandidateerror
|
||||
virtual void OnIceCandidateError(const std::string& address,
|
||||
|
@ -1420,7 +1425,6 @@ struct RTC_EXPORT PeerConnectionFactoryDependencies final {
|
|||
rtc::Thread* network_thread = nullptr;
|
||||
rtc::Thread* worker_thread = nullptr;
|
||||
rtc::Thread* signaling_thread = nullptr;
|
||||
rtc::SocketFactory* socket_factory = nullptr;
|
||||
std::unique_ptr<TaskQueueFactory> task_queue_factory;
|
||||
std::unique_ptr<cricket::MediaEngineInterface> media_engine;
|
||||
std::unique_ptr<CallFactoryInterface> call_factory;
|
||||
|
@ -1438,7 +1442,6 @@ struct RTC_EXPORT PeerConnectionFactoryDependencies final {
|
|||
std::unique_ptr<WebRtcKeyValueConfig> trials;
|
||||
std::unique_ptr<RtpTransportControllerSendFactoryInterface>
|
||||
transport_controller_send_factory;
|
||||
std::unique_ptr<Metronome> metronome;
|
||||
};
|
||||
|
||||
// PeerConnectionFactoryInterface is the factory interface used for creating
|
||||
|
@ -1612,8 +1615,7 @@ inline constexpr absl::string_view PeerConnectionInterface::AsString(
|
|||
case SignalingState::kClosed:
|
||||
return "closed";
|
||||
}
|
||||
// This cannot happen.
|
||||
// Not using "RTC_CHECK_NOTREACHED()" because AsString() is constexpr.
|
||||
RTC_CHECK_NOTREACHED();
|
||||
return "";
|
||||
}
|
||||
|
||||
|
@ -1628,8 +1630,7 @@ inline constexpr absl::string_view PeerConnectionInterface::AsString(
|
|||
case IceGatheringState::kIceGatheringComplete:
|
||||
return "complete";
|
||||
}
|
||||
// This cannot happen.
|
||||
// Not using "RTC_CHECK_NOTREACHED()" because AsString() is constexpr.
|
||||
RTC_CHECK_NOTREACHED();
|
||||
return "";
|
||||
}
|
||||
|
||||
|
@ -1650,8 +1651,7 @@ inline constexpr absl::string_view PeerConnectionInterface::AsString(
|
|||
case PeerConnectionState::kClosed:
|
||||
return "closed";
|
||||
}
|
||||
// This cannot happen.
|
||||
// Not using "RTC_CHECK_NOTREACHED()" because AsString() is constexpr.
|
||||
RTC_CHECK_NOTREACHED();
|
||||
return "";
|
||||
}
|
||||
|
||||
|
@ -1673,12 +1673,10 @@ inline constexpr absl::string_view PeerConnectionInterface::AsString(
|
|||
case kIceConnectionClosed:
|
||||
return "closed";
|
||||
case kIceConnectionMax:
|
||||
// This cannot happen.
|
||||
// Not using "RTC_CHECK_NOTREACHED()" because AsString() is constexpr.
|
||||
RTC_CHECK_NOTREACHED();
|
||||
return "";
|
||||
}
|
||||
// This cannot happen.
|
||||
// Not using "RTC_CHECK_NOTREACHED()" because AsString() is constexpr.
|
||||
RTC_CHECK_NOTREACHED();
|
||||
return "";
|
||||
}
|
||||
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
|
||||
#include <type_traits>
|
||||
|
||||
#include "rtc_base/constructor_magic.h"
|
||||
#include "rtc_base/ref_counter.h"
|
||||
|
||||
namespace rtc {
|
||||
|
@ -20,9 +21,6 @@ class RefCountedBase {
|
|||
public:
|
||||
RefCountedBase() = default;
|
||||
|
||||
RefCountedBase(const RefCountedBase&) = delete;
|
||||
RefCountedBase& operator=(const RefCountedBase&) = delete;
|
||||
|
||||
void AddRef() const { ref_count_.IncRef(); }
|
||||
RefCountReleaseStatus Release() const {
|
||||
const auto status = ref_count_.DecRef();
|
||||
|
@ -41,6 +39,8 @@ class RefCountedBase {
|
|||
|
||||
private:
|
||||
mutable webrtc::webrtc_impl::RefCounter ref_count_{0};
|
||||
|
||||
RTC_DISALLOW_COPY_AND_ASSIGN(RefCountedBase);
|
||||
};
|
||||
|
||||
// Template based version of `RefCountedBase` for simple implementations that do
|
||||
|
@ -61,9 +61,6 @@ class RefCountedNonVirtual {
|
|||
public:
|
||||
RefCountedNonVirtual() = default;
|
||||
|
||||
RefCountedNonVirtual(const RefCountedNonVirtual&) = delete;
|
||||
RefCountedNonVirtual& operator=(const RefCountedNonVirtual&) = delete;
|
||||
|
||||
void AddRef() const { ref_count_.IncRef(); }
|
||||
RefCountReleaseStatus Release() const {
|
||||
// If you run into this assert, T has virtual methods. There are two
|
||||
|
@ -91,6 +88,8 @@ class RefCountedNonVirtual {
|
|||
|
||||
private:
|
||||
mutable webrtc::webrtc_impl::RefCounter ref_count_{0};
|
||||
|
||||
RTC_DISALLOW_COPY_AND_ASSIGN(RefCountedNonVirtual);
|
||||
};
|
||||
|
||||
} // namespace rtc
|
||||
|
|
|
@ -244,7 +244,7 @@ class RTCErrorOr {
|
|||
//
|
||||
// REQUIRES: !error.ok(). This requirement is DCHECKed.
|
||||
RTCErrorOr(RTCError&& error) : error_(std::move(error)) { // NOLINT
|
||||
RTC_DCHECK(!error_.ok());
|
||||
RTC_DCHECK(!error.ok());
|
||||
}
|
||||
|
||||
// Constructs a new RTCErrorOr with the given value. After calling this
|
||||
|
|
|
@ -27,7 +27,7 @@ class RtcEvent {
|
|||
// of Type. This leaks the information of existing subclasses into the
|
||||
// superclass, but the *actual* information - rtclog::StreamConfig, etc. -
|
||||
// is kept separate.
|
||||
enum class Type : uint32_t {
|
||||
enum class Type {
|
||||
AlrStateEvent,
|
||||
RouteChangeEvent,
|
||||
RemoteEstimateEvent,
|
||||
|
@ -53,9 +53,7 @@ class RtcEvent {
|
|||
GenericPacketSent,
|
||||
GenericPacketReceived,
|
||||
GenericAckReceived,
|
||||
FrameDecoded,
|
||||
BeginV3Log = 0x2501580,
|
||||
EndV3Log = 0x2501581
|
||||
FrameDecoded
|
||||
};
|
||||
|
||||
RtcEvent();
|
||||
|
@ -65,13 +63,6 @@ class RtcEvent {
|
|||
|
||||
virtual bool IsConfigEvent() const = 0;
|
||||
|
||||
// Events are grouped by Type before being encoded.
|
||||
// Optionally, `GetGroupKey` can be overloaded to group the
|
||||
// events by a secondary key (in addition to the event type.)
|
||||
// This can, in some cases, improve compression efficiency
|
||||
// e.g. by grouping events by SSRC.
|
||||
virtual uint32_t GetGroupKey() const { return 0; }
|
||||
|
||||
int64_t timestamp_ms() const { return timestamp_us_ / 1000; }
|
||||
int64_t timestamp_us() const { return timestamp_us_; }
|
||||
|
||||
|
|
|
@ -29,7 +29,7 @@ class RtcEventLog {
|
|||
|
||||
// TODO(eladalon): Get rid of the legacy encoding and this enum once all
|
||||
// clients have migrated to the new format.
|
||||
enum class EncodingType { Legacy, NewFormat, ProtoFree };
|
||||
enum class EncodingType { Legacy, NewFormat };
|
||||
|
||||
virtual ~RtcEventLog() = default;
|
||||
|
||||
|
|
|
@ -18,7 +18,6 @@
|
|||
#include "api/ref_counted_base.h"
|
||||
#include "api/rtp_packet_info.h"
|
||||
#include "api/scoped_refptr.h"
|
||||
#include "rtc_base/ref_counted_object.h"
|
||||
#include "rtc_base/system/rtc_export.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
@ -80,7 +79,7 @@ class RTC_EXPORT RtpPacketInfos {
|
|||
size_type size() const { return entries().size(); }
|
||||
|
||||
private:
|
||||
class Data final : public rtc::RefCountedNonVirtual<Data> {
|
||||
class Data : public rtc::RefCountedBase {
|
||||
public:
|
||||
static rtc::scoped_refptr<Data> Create(const vector_type& entries) {
|
||||
// Performance optimization for the empty case.
|
||||
|
@ -88,7 +87,7 @@ class RTC_EXPORT RtpPacketInfos {
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
return rtc::make_ref_counted<Data>(entries);
|
||||
return new Data(entries);
|
||||
}
|
||||
|
||||
static rtc::scoped_refptr<Data> Create(vector_type&& entries) {
|
||||
|
@ -97,16 +96,16 @@ class RTC_EXPORT RtpPacketInfos {
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
return rtc::make_ref_counted<Data>(std::move(entries));
|
||||
return new Data(std::move(entries));
|
||||
}
|
||||
|
||||
const vector_type& entries() const { return entries_; }
|
||||
|
||||
private:
|
||||
explicit Data(const vector_type& entries) : entries_(entries) {}
|
||||
explicit Data(vector_type&& entries) : entries_(std::move(entries)) {}
|
||||
~Data() = default;
|
||||
~Data() override {}
|
||||
|
||||
private:
|
||||
const vector_type entries_;
|
||||
};
|
||||
|
||||
|
|
|
@ -11,7 +11,6 @@
|
|||
|
||||
#include <algorithm>
|
||||
#include <string>
|
||||
#include <tuple>
|
||||
#include <utility>
|
||||
|
||||
#include "api/array_view.h"
|
||||
|
@ -281,14 +280,6 @@ const std::vector<RtpExtension> RtpExtension::DeduplicateHeaderExtensions(
|
|||
}
|
||||
}
|
||||
|
||||
// Sort the returned vector to make comparisons of header extensions reliable.
|
||||
// In order of priority, we sort by uri first, then encrypt and id last.
|
||||
std::sort(filtered.begin(), filtered.end(),
|
||||
[](const RtpExtension& a, const RtpExtension& b) {
|
||||
return std::tie(a.uri, a.encrypt, a.id) <
|
||||
std::tie(b.uri, b.encrypt, b.id);
|
||||
});
|
||||
|
||||
return filtered;
|
||||
}
|
||||
} // namespace webrtc
|
||||
|
|
|
@ -286,9 +286,6 @@ struct RTC_EXPORT RtpExtension {
|
|||
bool encrypt);
|
||||
|
||||
// Returns a list of extensions where any extension URI is unique.
|
||||
// The returned list will be sorted by uri first, then encrypt and id last.
|
||||
// Having the list sorted allows the caller fo compare filtered lists for
|
||||
// equality to detect when changes have been made.
|
||||
static const std::vector<RtpExtension> DeduplicateHeaderExtensions(
|
||||
const std::vector<RtpExtension>& extensions,
|
||||
Filter filter);
|
||||
|
|
|
@ -44,6 +44,33 @@ void RtpTransceiverInterface::StopInternal() {
|
|||
<< "DEBUG: RtpTransceiverInterface::StopInternal called";
|
||||
}
|
||||
|
||||
RTCError RtpTransceiverInterface::SetCodecPreferences(
|
||||
rtc::ArrayView<RtpCodecCapability>) {
|
||||
RTC_DCHECK_NOTREACHED() << "Not implemented";
|
||||
return {};
|
||||
}
|
||||
|
||||
std::vector<RtpCodecCapability> RtpTransceiverInterface::codec_preferences()
|
||||
const {
|
||||
return {};
|
||||
}
|
||||
|
||||
std::vector<RtpHeaderExtensionCapability>
|
||||
RtpTransceiverInterface::HeaderExtensionsToOffer() const {
|
||||
return {};
|
||||
}
|
||||
|
||||
webrtc::RTCError RtpTransceiverInterface::SetOfferedRtpHeaderExtensions(
|
||||
rtc::ArrayView<const RtpHeaderExtensionCapability>
|
||||
header_extensions_to_offer) {
|
||||
return webrtc::RTCError(webrtc::RTCErrorType::UNSUPPORTED_OPERATION);
|
||||
}
|
||||
|
||||
std::vector<RtpHeaderExtensionCapability>
|
||||
RtpTransceiverInterface::HeaderExtensionsNegotiated() const {
|
||||
return {};
|
||||
}
|
||||
|
||||
// TODO(bugs.webrtc.org/11839) Remove default implementations when clients
|
||||
// are updated.
|
||||
void RtpTransceiverInterface::SetDirection(
|
||||
|
|
|
@ -97,7 +97,8 @@ class RTC_EXPORT RtpTransceiverInterface : public rtc::RefCountInterface {
|
|||
// transceiver's stop() method has been called, but the negotiation with
|
||||
// the other end for shutting down the transceiver is not yet done.
|
||||
// https://w3c.github.io/webrtc-pc/#dfn-stopping-0
|
||||
virtual bool stopping() const = 0;
|
||||
// TODO(hta): Remove default implementation.
|
||||
virtual bool stopping() const;
|
||||
|
||||
// The direction attribute indicates the preferred direction of this
|
||||
// transceiver, which will be used in calls to CreateOffer and CreateAnswer.
|
||||
|
@ -146,28 +147,28 @@ class RTC_EXPORT RtpTransceiverInterface : public rtc::RefCountInterface {
|
|||
// by WebRTC for this transceiver.
|
||||
// https://w3c.github.io/webrtc-pc/#dom-rtcrtptransceiver-setcodecpreferences
|
||||
virtual RTCError SetCodecPreferences(
|
||||
rtc::ArrayView<RtpCodecCapability> codecs) = 0;
|
||||
virtual std::vector<RtpCodecCapability> codec_preferences() const = 0;
|
||||
rtc::ArrayView<RtpCodecCapability> codecs);
|
||||
virtual std::vector<RtpCodecCapability> codec_preferences() const;
|
||||
|
||||
// Readonly attribute which contains the set of header extensions that was set
|
||||
// with SetOfferedRtpHeaderExtensions, or a default set if it has not been
|
||||
// called.
|
||||
// https://w3c.github.io/webrtc-extensions/#rtcrtptransceiver-interface
|
||||
virtual std::vector<RtpHeaderExtensionCapability> HeaderExtensionsToOffer()
|
||||
const = 0;
|
||||
const;
|
||||
|
||||
// Readonly attribute which is either empty if negotation has not yet
|
||||
// happened, or a vector of the negotiated header extensions.
|
||||
// https://w3c.github.io/webrtc-extensions/#rtcrtptransceiver-interface
|
||||
virtual std::vector<RtpHeaderExtensionCapability> HeaderExtensionsNegotiated()
|
||||
const = 0;
|
||||
const;
|
||||
|
||||
// The SetOfferedRtpHeaderExtensions method modifies the next SDP negotiation
|
||||
// so that it negotiates use of header extensions which are not kStopped.
|
||||
// https://w3c.github.io/webrtc-extensions/#rtcrtptransceiver-interface
|
||||
virtual webrtc::RTCError SetOfferedRtpHeaderExtensions(
|
||||
rtc::ArrayView<const RtpHeaderExtensionCapability>
|
||||
header_extensions_to_offer) = 0;
|
||||
header_extensions_to_offer);
|
||||
|
||||
protected:
|
||||
~RtpTransceiverInterface() override = default;
|
||||
|
|
|
@ -225,6 +225,7 @@ class RTC_EXPORT RTCIceCandidateStats : public RTCStats {
|
|||
// TODO(hbos): Support enum types? "RTCStatsMember<RTCIceCandidateType>"?
|
||||
RTCStatsMember<std::string> candidate_type;
|
||||
RTCStatsMember<int32_t> priority;
|
||||
// TODO(hbos): Not collected by `RTCStatsCollector`. crbug.com/632723
|
||||
RTCStatsMember<std::string> url;
|
||||
|
||||
protected:
|
||||
|
|
|
@ -648,7 +648,6 @@ const char* StatsReport::Value::display_name() const {
|
|||
return "googTrackId";
|
||||
case kStatsValueNameTimingFrameInfo:
|
||||
return "googTimingFrameInfo";
|
||||
// TODO(bugs.webrtc.org/11226): Remove.
|
||||
case kStatsValueNameTypingNoiseState:
|
||||
return "googTypingNoiseState";
|
||||
case kStatsValueNameWritable:
|
||||
|
|
|
@ -22,6 +22,7 @@
|
|||
|
||||
#include "api/scoped_refptr.h"
|
||||
#include "api/sequence_checker.h"
|
||||
#include "rtc_base/constructor_magic.h"
|
||||
#include "rtc_base/ref_count.h"
|
||||
#include "rtc_base/system/rtc_export.h"
|
||||
|
||||
|
@ -235,7 +236,6 @@ class RTC_EXPORT StatsReport {
|
|||
kStatsValueNameTrackId,
|
||||
kStatsValueNameTransmitBitrate,
|
||||
kStatsValueNameTransportType,
|
||||
// TODO(bugs.webrtc.org/11226): Remove.
|
||||
kStatsValueNameTypingNoiseState,
|
||||
kStatsValueNameWritable,
|
||||
kStatsValueNameAudioDeviceUnderrunCounter,
|
||||
|
@ -288,9 +288,6 @@ class RTC_EXPORT StatsReport {
|
|||
|
||||
~Value();
|
||||
|
||||
Value(const Value&) = delete;
|
||||
Value& operator=(const Value&) = delete;
|
||||
|
||||
// Support ref counting. Note that for performance reasons, we
|
||||
// don't use thread safe operations. Therefore, all operations
|
||||
// affecting the ref count (in practice, creation and copying of
|
||||
|
@ -361,6 +358,8 @@ class RTC_EXPORT StatsReport {
|
|||
const char* static_string_;
|
||||
Id* id_;
|
||||
} value_;
|
||||
|
||||
RTC_DISALLOW_COPY_AND_ASSIGN(Value);
|
||||
};
|
||||
|
||||
typedef rtc::scoped_refptr<Value> ValuePtr;
|
||||
|
@ -370,9 +369,6 @@ class RTC_EXPORT StatsReport {
|
|||
explicit StatsReport(const Id& id);
|
||||
~StatsReport();
|
||||
|
||||
StatsReport(const StatsReport&) = delete;
|
||||
StatsReport& operator=(const StatsReport&) = delete;
|
||||
|
||||
// Factory functions for various types of stats IDs.
|
||||
static Id NewBandwidthEstimationId();
|
||||
static Id NewTypedId(StatsType type, const std::string& id);
|
||||
|
@ -412,6 +408,8 @@ class RTC_EXPORT StatsReport {
|
|||
const Id id_;
|
||||
double timestamp_; // Time since 1970-01-01T00:00:00Z in milliseconds.
|
||||
Values values_;
|
||||
|
||||
RTC_DISALLOW_COPY_AND_ASSIGN(StatsReport);
|
||||
};
|
||||
|
||||
// Typedef for an array of const StatsReport pointers.
|
||||
|
|
|
@ -11,7 +11,6 @@
|
|||
#define API_TASK_QUEUE_TASK_QUEUE_BASE_H_
|
||||
|
||||
#include <memory>
|
||||
#include <utility>
|
||||
|
||||
#include "api/task_queue/queued_task.h"
|
||||
#include "rtc_base/system/rtc_export.h"
|
||||
|
@ -25,16 +24,6 @@ namespace webrtc {
|
|||
// known task queue, use IsCurrent().
|
||||
class RTC_LOCKABLE RTC_EXPORT TaskQueueBase {
|
||||
public:
|
||||
enum class DelayPrecision {
|
||||
// This may include up to a 17 ms leeway in addition to OS timer precision.
|
||||
// See PostDelayedTask() for more information.
|
||||
kLow,
|
||||
// This does not have the additional delay that kLow has, but it is still
|
||||
// limited by OS timer precision. See PostDelayedHighPrecisionTask() for
|
||||
// more information.
|
||||
kHigh,
|
||||
};
|
||||
|
||||
// Starts destruction of the task queue.
|
||||
// On return ensures no task are running and no new tasks are able to start
|
||||
// on the task queue.
|
||||
|
@ -59,70 +48,14 @@ class RTC_LOCKABLE RTC_EXPORT TaskQueueBase {
|
|||
// May be called on any thread or task queue, including this task queue.
|
||||
virtual void PostTask(std::unique_ptr<QueuedTask> task) = 0;
|
||||
|
||||
// Prefer PostDelayedTask() over PostDelayedHighPrecisionTask() whenever
|
||||
// possible.
|
||||
//
|
||||
// Schedules a task to execute a specified number of milliseconds from when
|
||||
// the call is made, using "low" precision. All scheduling is affected by
|
||||
// OS-specific leeway and current workloads which means that in terms of
|
||||
// precision there are no hard guarantees, but in addition to the OS induced
|
||||
// leeway, "low" precision adds up to a 17 ms additional leeway. The purpose
|
||||
// of this leeway is to achieve more efficient CPU scheduling and reduce Idle
|
||||
// Wake Up frequency.
|
||||
//
|
||||
// The task may execute with [-1, 17 + OS induced leeway) ms additional delay.
|
||||
//
|
||||
// Avoid making assumptions about the precision of the OS scheduler. On macOS,
|
||||
// the OS induced leeway may be 10% of sleep interval. On Windows, 1 ms
|
||||
// precision timers may be used but there are cases, such as when running on
|
||||
// battery, when the timer precision can be as poor as 15 ms.
|
||||
//
|
||||
// "Low" precision is not implemented everywhere yet. Where not yet
|
||||
// implemented, PostDelayedTask() has "high" precision. See
|
||||
// https://crbug.com/webrtc/13583 for more information.
|
||||
//
|
||||
// the call is made. The precision should be considered as "best effort"
|
||||
// and in some cases, such as on Windows when all high precision timers have
|
||||
// been used up, can be off by as much as 15 millseconds.
|
||||
// May be called on any thread or task queue, including this task queue.
|
||||
virtual void PostDelayedTask(std::unique_ptr<QueuedTask> task,
|
||||
uint32_t milliseconds) = 0;
|
||||
|
||||
// Prefer PostDelayedTask() over PostDelayedHighPrecisionTask() whenever
|
||||
// possible.
|
||||
//
|
||||
// Schedules a task to execute a specified number of milliseconds from when
|
||||
// the call is made, using "high" precision. All scheduling is affected by
|
||||
// OS-specific leeway and current workloads which means that in terms of
|
||||
// precision there are no hard guarantees.
|
||||
//
|
||||
// The task may execute with [-1, OS induced leeway] ms additional delay.
|
||||
//
|
||||
// Avoid making assumptions about the precision of the OS scheduler. On macOS,
|
||||
// the OS induced leeway may be 10% of sleep interval. On Windows, 1 ms
|
||||
// precision timers may be used but there are cases, such as when running on
|
||||
// battery, when the timer precision can be as poor as 15 ms.
|
||||
//
|
||||
// May be called on any thread or task queue, including this task queue.
|
||||
virtual void PostDelayedHighPrecisionTask(std::unique_ptr<QueuedTask> task,
|
||||
uint32_t milliseconds) {
|
||||
// Remove default implementation when dependencies have implemented this
|
||||
// method.
|
||||
PostDelayedTask(std::move(task), milliseconds);
|
||||
}
|
||||
|
||||
// As specified by |precision|, calls either PostDelayedTask() or
|
||||
// PostDelayedHighPrecisionTask().
|
||||
void PostDelayedTaskWithPrecision(DelayPrecision precision,
|
||||
std::unique_ptr<QueuedTask> task,
|
||||
uint32_t milliseconds) {
|
||||
switch (precision) {
|
||||
case DelayPrecision::kLow:
|
||||
PostDelayedTask(std::move(task), milliseconds);
|
||||
break;
|
||||
case DelayPrecision::kHigh:
|
||||
PostDelayedHighPrecisionTask(std::move(task), milliseconds);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Returns the task queue that is running the current thread.
|
||||
// Returns nullptr if this thread is not associated with any task queue.
|
||||
// May be called on any thread or task queue, including this task queue.
|
||||
|
|
|
@ -45,7 +45,9 @@ class DummyPeerConnection : public PeerConnectionInterface {
|
|||
return RTCError(RTCErrorType::UNSUPPORTED_OPERATION, "Not implemented");
|
||||
}
|
||||
|
||||
RTCError RemoveTrackOrError(
|
||||
bool RemoveTrack(RtpSenderInterface* sender) override { return false; }
|
||||
|
||||
RTCError RemoveTrackNew(
|
||||
rtc::scoped_refptr<RtpSenderInterface> sender) override {
|
||||
return RTCError(RTCErrorType::UNSUPPORTED_OPERATION, "Not implemented");
|
||||
}
|
||||
|
|
|
@ -24,8 +24,8 @@ class MockAsyncDnsResolverResult : public AsyncDnsResolverResult {
|
|||
MOCK_METHOD(bool,
|
||||
GetResolvedAddress,
|
||||
(int, rtc::SocketAddress*),
|
||||
(const, override));
|
||||
MOCK_METHOD(int, GetError, (), (const, override));
|
||||
(const override));
|
||||
MOCK_METHOD(int, GetError, (), (const override));
|
||||
};
|
||||
|
||||
class MockAsyncDnsResolver : public AsyncDnsResolverInterface {
|
||||
|
@ -34,7 +34,7 @@ class MockAsyncDnsResolver : public AsyncDnsResolverInterface {
|
|||
Start,
|
||||
(const rtc::SocketAddress&, std::function<void()>),
|
||||
(override));
|
||||
MOCK_METHOD(AsyncDnsResolverResult&, result, (), (const, override));
|
||||
MOCK_METHOD(AsyncDnsResolverResult&, result, (), (const override));
|
||||
};
|
||||
|
||||
class MockAsyncDnsResolverFactory : public AsyncDnsResolverFactoryInterface {
|
||||
|
|
|
@ -22,8 +22,7 @@ class MockDataChannelInterface final
|
|||
: public rtc::RefCountedObject<webrtc::DataChannelInterface> {
|
||||
public:
|
||||
static rtc::scoped_refptr<MockDataChannelInterface> Create() {
|
||||
return rtc::scoped_refptr<MockDataChannelInterface>(
|
||||
new MockDataChannelInterface());
|
||||
return new MockDataChannelInterface();
|
||||
}
|
||||
|
||||
MOCK_METHOD(void,
|
||||
|
|
|
@ -22,7 +22,7 @@ class MockAudioSource final
|
|||
: public rtc::RefCountedObject<AudioSourceInterface> {
|
||||
public:
|
||||
static rtc::scoped_refptr<MockAudioSource> Create() {
|
||||
return rtc::scoped_refptr<MockAudioSource>(new MockAudioSource());
|
||||
return new MockAudioSource();
|
||||
}
|
||||
|
||||
MOCK_METHOD(void,
|
||||
|
@ -55,7 +55,7 @@ class MockAudioSource final
|
|||
class MockAudioTrack final : public rtc::RefCountedObject<AudioTrackInterface> {
|
||||
public:
|
||||
static rtc::scoped_refptr<MockAudioTrack> Create() {
|
||||
return rtc::scoped_refptr<MockAudioTrack>(new MockAudioTrack());
|
||||
return new MockAudioTrack();
|
||||
}
|
||||
|
||||
MOCK_METHOD(void,
|
||||
|
@ -67,7 +67,7 @@ class MockAudioTrack final : public rtc::RefCountedObject<AudioTrackInterface> {
|
|||
(ObserverInterface * observer),
|
||||
(override));
|
||||
MOCK_METHOD(std::string, kind, (), (const, override));
|
||||
MOCK_METHOD(std::string, id, (), (const, override));
|
||||
MOCK_METHOD(std::string, id, (), (const override));
|
||||
MOCK_METHOD(bool, enabled, (), (const, override));
|
||||
MOCK_METHOD(bool, set_enabled, (bool enable), (override));
|
||||
MOCK_METHOD(TrackState, state, (), (const, override));
|
||||
|
|
|
@ -23,8 +23,7 @@ class MockPeerConnectionFactoryInterface final
|
|||
: public rtc::RefCountedObject<webrtc::PeerConnectionFactoryInterface> {
|
||||
public:
|
||||
static rtc::scoped_refptr<MockPeerConnectionFactoryInterface> Create() {
|
||||
return rtc::scoped_refptr<MockPeerConnectionFactoryInterface>(
|
||||
new MockPeerConnectionFactoryInterface());
|
||||
return new MockPeerConnectionFactoryInterface();
|
||||
}
|
||||
|
||||
MOCK_METHOD(void, SetOptions, (const Options&), (override));
|
||||
|
@ -48,11 +47,11 @@ class MockPeerConnectionFactoryInterface final
|
|||
MOCK_METHOD(RtpCapabilities,
|
||||
GetRtpSenderCapabilities,
|
||||
(cricket::MediaType),
|
||||
(const, override));
|
||||
(const override));
|
||||
MOCK_METHOD(RtpCapabilities,
|
||||
GetRtpReceiverCapabilities,
|
||||
(cricket::MediaType),
|
||||
(const, override));
|
||||
(const override));
|
||||
MOCK_METHOD(rtc::scoped_refptr<MediaStreamInterface>,
|
||||
CreateLocalMediaStream,
|
||||
(const std::string&),
|
||||
|
|
|
@ -29,7 +29,7 @@ class MockPeerConnectionInterface
|
|||
: public rtc::RefCountedObject<webrtc::PeerConnectionInterface> {
|
||||
public:
|
||||
static rtc::scoped_refptr<MockPeerConnectionInterface> Create() {
|
||||
return rtc::make_ref_counted<MockPeerConnectionInterface>();
|
||||
return new MockPeerConnectionInterface();
|
||||
}
|
||||
|
||||
// PeerConnectionInterface
|
||||
|
@ -48,8 +48,9 @@ class MockPeerConnectionInterface
|
|||
(rtc::scoped_refptr<MediaStreamTrackInterface>,
|
||||
const std::vector<std::string>&),
|
||||
(override));
|
||||
MOCK_METHOD(bool, RemoveTrack, (RtpSenderInterface*), (override));
|
||||
MOCK_METHOD(RTCError,
|
||||
RemoveTrackOrError,
|
||||
RemoveTrackNew,
|
||||
(rtc::scoped_refptr<RtpSenderInterface>),
|
||||
(override));
|
||||
MOCK_METHOD(RTCErrorOr<rtc::scoped_refptr<RtpTransceiverInterface>>,
|
||||
|
@ -76,15 +77,15 @@ class MockPeerConnectionInterface
|
|||
MOCK_METHOD(std::vector<rtc::scoped_refptr<RtpSenderInterface>>,
|
||||
GetSenders,
|
||||
(),
|
||||
(const, override));
|
||||
(const override));
|
||||
MOCK_METHOD(std::vector<rtc::scoped_refptr<RtpReceiverInterface>>,
|
||||
GetReceivers,
|
||||
(),
|
||||
(const, override));
|
||||
(const override));
|
||||
MOCK_METHOD(std::vector<rtc::scoped_refptr<RtpTransceiverInterface>>,
|
||||
GetTransceivers,
|
||||
(),
|
||||
(const, override));
|
||||
(const override));
|
||||
MOCK_METHOD(bool,
|
||||
GetStats,
|
||||
(StatsObserver*, MediaStreamTrackInterface*, StatsOutputLevel),
|
||||
|
@ -104,7 +105,7 @@ class MockPeerConnectionInterface
|
|||
MOCK_METHOD(rtc::scoped_refptr<SctpTransportInterface>,
|
||||
GetSctpTransport,
|
||||
(),
|
||||
(const, override));
|
||||
(const override));
|
||||
MOCK_METHOD(RTCErrorOr<rtc::scoped_refptr<DataChannelInterface>>,
|
||||
CreateDataChannelOrError,
|
||||
(const std::string&, const DataChannelInit*),
|
||||
|
@ -112,27 +113,27 @@ class MockPeerConnectionInterface
|
|||
MOCK_METHOD(const SessionDescriptionInterface*,
|
||||
local_description,
|
||||
(),
|
||||
(const, override));
|
||||
(const override));
|
||||
MOCK_METHOD(const SessionDescriptionInterface*,
|
||||
remote_description,
|
||||
(),
|
||||
(const, override));
|
||||
(const override));
|
||||
MOCK_METHOD(const SessionDescriptionInterface*,
|
||||
current_local_description,
|
||||
(),
|
||||
(const, override));
|
||||
(const override));
|
||||
MOCK_METHOD(const SessionDescriptionInterface*,
|
||||
current_remote_description,
|
||||
(),
|
||||
(const, override));
|
||||
(const override));
|
||||
MOCK_METHOD(const SessionDescriptionInterface*,
|
||||
pending_local_description,
|
||||
(),
|
||||
(const, override));
|
||||
(const override));
|
||||
MOCK_METHOD(const SessionDescriptionInterface*,
|
||||
pending_remote_description,
|
||||
(),
|
||||
(const, override));
|
||||
(const override));
|
||||
MOCK_METHOD(void, RestartIce, (), (override));
|
||||
MOCK_METHOD(void,
|
||||
CreateOffer,
|
||||
|
|
|
@ -23,7 +23,7 @@ class MockRtpTransceiver final
|
|||
: public rtc::RefCountedObject<RtpTransceiverInterface> {
|
||||
public:
|
||||
static rtc::scoped_refptr<MockRtpTransceiver> Create() {
|
||||
return rtc::scoped_refptr<MockRtpTransceiver>(new MockRtpTransceiver());
|
||||
return new MockRtpTransceiver();
|
||||
}
|
||||
|
||||
MOCK_METHOD(cricket::MediaType, media_type, (), (const, override));
|
||||
|
@ -70,10 +70,6 @@ class MockRtpTransceiver final
|
|||
HeaderExtensionsToOffer,
|
||||
(),
|
||||
(const, override));
|
||||
MOCK_METHOD(std::vector<RtpHeaderExtensionCapability>,
|
||||
HeaderExtensionsNegotiated,
|
||||
(),
|
||||
(const, override));
|
||||
MOCK_METHOD(webrtc::RTCError,
|
||||
SetOfferedRtpHeaderExtensions,
|
||||
(rtc::ArrayView<const RtpHeaderExtensionCapability>
|
||||
|
|
|
@ -24,20 +24,20 @@ class MockRtpReceiver : public rtc::RefCountedObject<RtpReceiverInterface> {
|
|||
MOCK_METHOD(rtc::scoped_refptr<MediaStreamTrackInterface>,
|
||||
track,
|
||||
(),
|
||||
(const, override));
|
||||
(const override));
|
||||
MOCK_METHOD(std::vector<rtc::scoped_refptr<MediaStreamInterface>>,
|
||||
streams,
|
||||
(),
|
||||
(const, override));
|
||||
MOCK_METHOD(cricket::MediaType, media_type, (), (const, override));
|
||||
MOCK_METHOD(std::string, id, (), (const, override));
|
||||
MOCK_METHOD(RtpParameters, GetParameters, (), (const, override));
|
||||
(const override));
|
||||
MOCK_METHOD(cricket::MediaType, media_type, (), (const override));
|
||||
MOCK_METHOD(std::string, id, (), (const override));
|
||||
MOCK_METHOD(RtpParameters, GetParameters, (), (const override));
|
||||
MOCK_METHOD(void, SetObserver, (RtpReceiverObserverInterface*), (override));
|
||||
MOCK_METHOD(void,
|
||||
SetJitterBufferMinimumDelay,
|
||||
(absl::optional<double>),
|
||||
(override));
|
||||
MOCK_METHOD(std::vector<RtpSource>, GetSources, (), (const, override));
|
||||
MOCK_METHOD(std::vector<RtpSource>, GetSources, (), (const override));
|
||||
};
|
||||
|
||||
} // namespace webrtc
|
||||
|
|
|
@ -25,21 +25,21 @@ class MockRtpSender : public rtc::RefCountedObject<RtpSenderInterface> {
|
|||
MOCK_METHOD(rtc::scoped_refptr<MediaStreamTrackInterface>,
|
||||
track,
|
||||
(),
|
||||
(const, override));
|
||||
MOCK_METHOD(uint32_t, ssrc, (), (const, override));
|
||||
MOCK_METHOD(cricket::MediaType, media_type, (), (const, override));
|
||||
MOCK_METHOD(std::string, id, (), (const, override));
|
||||
MOCK_METHOD(std::vector<std::string>, stream_ids, (), (const, override));
|
||||
(const override));
|
||||
MOCK_METHOD(uint32_t, ssrc, (), (const override));
|
||||
MOCK_METHOD(cricket::MediaType, media_type, (), (const override));
|
||||
MOCK_METHOD(std::string, id, (), (const override));
|
||||
MOCK_METHOD(std::vector<std::string>, stream_ids, (), (const override));
|
||||
MOCK_METHOD(std::vector<RtpEncodingParameters>,
|
||||
init_send_encodings,
|
||||
(),
|
||||
(const, override));
|
||||
MOCK_METHOD(RtpParameters, GetParameters, (), (const, override));
|
||||
(const override));
|
||||
MOCK_METHOD(RtpParameters, GetParameters, (), (const override));
|
||||
MOCK_METHOD(RTCError, SetParameters, (const RtpParameters&), (override));
|
||||
MOCK_METHOD(rtc::scoped_refptr<DtmfSenderInterface>,
|
||||
GetDtmfSender,
|
||||
(),
|
||||
(const, override));
|
||||
(const override));
|
||||
};
|
||||
|
||||
} // namespace webrtc
|
||||
|
|
|
@ -21,9 +21,9 @@ namespace webrtc {
|
|||
class MockTransformableVideoFrame
|
||||
: public webrtc::TransformableVideoFrameInterface {
|
||||
public:
|
||||
MOCK_METHOD(rtc::ArrayView<const uint8_t>, GetData, (), (const, override));
|
||||
MOCK_METHOD(rtc::ArrayView<const uint8_t>, GetData, (), (const override));
|
||||
MOCK_METHOD(void, SetData, (rtc::ArrayView<const uint8_t> data), (override));
|
||||
MOCK_METHOD(uint32_t, GetTimestamp, (), (const, override));
|
||||
MOCK_METHOD(uint32_t, GetTimestamp, (), (const override));
|
||||
MOCK_METHOD(uint32_t, GetSsrc, (), (const, override));
|
||||
MOCK_METHOD(bool, IsKeyFrame, (), (const, override));
|
||||
MOCK_METHOD(std::vector<uint8_t>, GetAdditionalData, (), (const, override));
|
||||
|
|
|
@ -24,7 +24,7 @@ class MockVideoTrack final
|
|||
: public rtc::RefCountedObject<webrtc::VideoTrackInterface> {
|
||||
public:
|
||||
static rtc::scoped_refptr<MockVideoTrack> Create() {
|
||||
return rtc::scoped_refptr<MockVideoTrack>(new MockVideoTrack());
|
||||
return new MockVideoTrack();
|
||||
}
|
||||
|
||||
// NotifierInterface
|
||||
|
|
|
@ -371,11 +371,6 @@ class PeerConnectionE2EQualityTestFixture {
|
|||
std::unique_ptr<rtc::SSLCertificateVerifier> tls_cert_verifier) = 0;
|
||||
virtual PeerConfigurer* SetIceTransportFactory(
|
||||
std::unique_ptr<IceTransportFactory> factory) = 0;
|
||||
// Flags to set on `cricket::PortAllocator`. These flags will be added
|
||||
// to the default ones that are presented on the port allocator.
|
||||
// For possible values check p2p/base/port_allocator.h.
|
||||
virtual PeerConfigurer* SetPortAllocatorExtraFlags(
|
||||
uint32_t extra_flags) = 0;
|
||||
|
||||
// Add new video stream to the call that will be sent from this peer.
|
||||
// Default implementation of video frames generator will be used.
|
||||
|
@ -401,22 +396,6 @@ class PeerConnectionE2EQualityTestFixture {
|
|||
// Set the audio stream for the call from this peer. If this method won't
|
||||
// be invoked, this peer will send no audio.
|
||||
virtual PeerConfigurer* SetAudioConfig(AudioConfig config) = 0;
|
||||
|
||||
// Set if ULP FEC should be used or not. False by default.
|
||||
virtual PeerConfigurer* SetUseUlpFEC(bool value) = 0;
|
||||
// Set if Flex FEC should be used or not. False by default.
|
||||
// Client also must enable `enable_flex_fec_support` in the `RunParams` to
|
||||
// be able to use this feature.
|
||||
virtual PeerConfigurer* SetUseFlexFEC(bool value) = 0;
|
||||
// Specifies how much video encoder target bitrate should be different than
|
||||
// target bitrate, provided by WebRTC stack. Must be greater than 0. Can be
|
||||
// used to emulate overshooting of video encoders. This multiplier will
|
||||
// be applied for all video encoder on both sides for all layers. Bitrate
|
||||
// estimated by WebRTC stack will be multiplied by this multiplier and then
|
||||
// provided into VideoEncoder::SetRates(...). 1.0 by default.
|
||||
virtual PeerConfigurer* SetVideoEncoderBitrateMultiplier(
|
||||
double multiplier) = 0;
|
||||
|
||||
// If is set, an RTCEventLog will be saved in that location and it will be
|
||||
// available for further analysis.
|
||||
virtual PeerConfigurer* SetRtcEventLogPath(std::string path) = 0;
|
||||
|
@ -448,9 +427,15 @@ class PeerConnectionE2EQualityTestFixture {
|
|||
// it will be shut downed.
|
||||
TimeDelta run_duration;
|
||||
|
||||
// If set to true peers will be able to use Flex FEC, otherwise they won't
|
||||
// be able to negotiate it even if it's enabled on per peer level.
|
||||
bool enable_flex_fec_support = false;
|
||||
bool use_ulp_fec = false;
|
||||
bool use_flex_fec = false;
|
||||
// Specifies how much video encoder target bitrate should be different than
|
||||
// target bitrate, provided by WebRTC stack. Must be greater then 0. Can be
|
||||
// used to emulate overshooting of video encoders. This multiplier will
|
||||
// be applied for all video encoder on both sides for all layers. Bitrate
|
||||
// estimated by WebRTC stack will be multiplied on this multiplier and then
|
||||
// provided into VideoEncoder::SetRates(...).
|
||||
double video_encoder_bitrate_multiplier = 1.0;
|
||||
// If true will set conference mode in SDP media section for all video
|
||||
// tracks for all peers.
|
||||
bool use_conference_mode = false;
|
||||
|
|
|
@ -86,10 +86,6 @@ std::map<std::string, std::string> VideoCodecTestStats::VideoStatistics::ToMap()
|
|||
map["framerate_fps"] = std::to_string(framerate_fps);
|
||||
map["enc_speed_fps"] = std::to_string(enc_speed_fps);
|
||||
map["dec_speed_fps"] = std::to_string(dec_speed_fps);
|
||||
map["avg_encode_latency_sec"] = std::to_string(avg_encode_latency_sec);
|
||||
map["max_encode_latency_sec"] = std::to_string(max_encode_latency_sec);
|
||||
map["avg_decode_latency_sec"] = std::to_string(avg_decode_latency_sec);
|
||||
map["max_decode_latency_sec"] = std::to_string(max_decode_latency_sec);
|
||||
map["avg_delay_sec"] = std::to_string(avg_delay_sec);
|
||||
map["max_key_frame_delay_sec"] = std::to_string(max_key_frame_delay_sec);
|
||||
map["max_delta_frame_delay_sec"] = std::to_string(max_delta_frame_delay_sec);
|
||||
|
|
|
@ -101,11 +101,6 @@ class VideoCodecTestStats {
|
|||
float enc_speed_fps = 0.0f;
|
||||
float dec_speed_fps = 0.0f;
|
||||
|
||||
float avg_encode_latency_sec = 0.0f;
|
||||
float max_encode_latency_sec = 0.0f;
|
||||
float avg_decode_latency_sec = 0.0f;
|
||||
float max_decode_latency_sec = 0.0f;
|
||||
|
||||
float avg_delay_sec = 0.0f;
|
||||
float max_key_frame_delay_sec = 0.0f;
|
||||
float max_delta_frame_delay_sec = 0.0f;
|
||||
|
|
|
@ -103,4 +103,8 @@ bool PacedPacketInfo::operator==(const PacedPacketInfo& rhs) const {
|
|||
probe_cluster_min_bytes == rhs.probe_cluster_min_bytes;
|
||||
}
|
||||
|
||||
ProcessInterval::ProcessInterval() = default;
|
||||
ProcessInterval::ProcessInterval(const ProcessInterval&) = default;
|
||||
ProcessInterval::~ProcessInterval() = default;
|
||||
|
||||
} // namespace webrtc
|
||||
|
|
|
@ -241,6 +241,9 @@ struct NetworkControlUpdate {
|
|||
|
||||
// Process control
|
||||
struct ProcessInterval {
|
||||
ProcessInterval();
|
||||
ProcessInterval(const ProcessInterval&);
|
||||
~ProcessInterval();
|
||||
Timestamp at_time = Timestamp::PlusInfinity();
|
||||
absl::optional<DataSize> pacer_queue;
|
||||
};
|
||||
|
|
|
@ -13,7 +13,7 @@
|
|||
namespace webrtc {
|
||||
|
||||
bool EncodedFrame::delayed_by_retransmission() const {
|
||||
return false;
|
||||
return 0;
|
||||
}
|
||||
|
||||
} // namespace webrtc
|
||||
|
|
|
@ -154,16 +154,6 @@ class RTC_EXPORT EncodedImage {
|
|||
return encoded_data_ ? encoded_data_->data() : nullptr;
|
||||
}
|
||||
|
||||
// Returns whether the encoded image can be considered to be of target
|
||||
// quality.
|
||||
bool IsAtTargetQuality() const { return at_target_quality_; }
|
||||
|
||||
// Sets that the encoded image can be considered to be of target quality to
|
||||
// true or false.
|
||||
void SetAtTargetQuality(bool at_target_quality) {
|
||||
at_target_quality_ = at_target_quality;
|
||||
}
|
||||
|
||||
uint32_t _encodedWidth = 0;
|
||||
uint32_t _encodedHeight = 0;
|
||||
// NTP time of the capture time in local timebase in milliseconds.
|
||||
|
@ -210,8 +200,6 @@ class RTC_EXPORT EncodedImage {
|
|||
// https://w3c.github.io/webrtc-pc/#dom-rtcrtpreceiver-getcontributingsources
|
||||
RtpPacketInfos packet_infos_;
|
||||
bool retransmission_allowed_ = true;
|
||||
// True if the encoded image can be considered to be of target quality.
|
||||
bool at_target_quality_ = false;
|
||||
};
|
||||
|
||||
} // namespace webrtc
|
||||
|
|
|
@ -1,211 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2021 The WebRTC project authors. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
#include "api/video/i444_buffer.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <utility>
|
||||
|
||||
#include "api/video/i420_buffer.h"
|
||||
#include "rtc_base/checks.h"
|
||||
#include "rtc_base/ref_counted_object.h"
|
||||
#include "third_party/libyuv/include/libyuv/convert.h"
|
||||
#include "third_party/libyuv/include/libyuv/planar_functions.h"
|
||||
#include "third_party/libyuv/include/libyuv/scale.h"
|
||||
|
||||
// Aligning pointer to 64 bytes for improved performance, e.g. use SIMD.
|
||||
static const int kBufferAlignment = 64;
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
namespace {
|
||||
|
||||
int I444DataSize(int height, int stride_y, int stride_u, int stride_v) {
|
||||
return stride_y * height + stride_u * height + stride_v * height;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
I444Buffer::I444Buffer(int width, int height)
|
||||
: I444Buffer(width, height, width, (width), (width)) {}
|
||||
|
||||
I444Buffer::I444Buffer(int width,
|
||||
int height,
|
||||
int stride_y,
|
||||
int stride_u,
|
||||
int stride_v)
|
||||
: width_(width),
|
||||
height_(height),
|
||||
stride_y_(stride_y),
|
||||
stride_u_(stride_u),
|
||||
stride_v_(stride_v),
|
||||
data_(static_cast<uint8_t*>(
|
||||
AlignedMalloc(I444DataSize(height, stride_y, stride_u, stride_v),
|
||||
kBufferAlignment))) {
|
||||
RTC_DCHECK_GT(width, 0);
|
||||
RTC_DCHECK_GT(height, 0);
|
||||
RTC_DCHECK_GE(stride_y, width);
|
||||
RTC_DCHECK_GE(stride_u, (width));
|
||||
RTC_DCHECK_GE(stride_v, (width));
|
||||
}
|
||||
|
||||
I444Buffer::~I444Buffer() {}
|
||||
|
||||
// static
|
||||
rtc::scoped_refptr<I444Buffer> I444Buffer::Create(int width, int height) {
|
||||
return rtc::make_ref_counted<I444Buffer>(width, height);
|
||||
}
|
||||
|
||||
// static
|
||||
rtc::scoped_refptr<I444Buffer> I444Buffer::Create(int width,
|
||||
int height,
|
||||
int stride_y,
|
||||
int stride_u,
|
||||
int stride_v) {
|
||||
return rtc::make_ref_counted<I444Buffer>(width, height, stride_y, stride_u,
|
||||
stride_v);
|
||||
}
|
||||
|
||||
// static
|
||||
rtc::scoped_refptr<I444Buffer> I444Buffer::Copy(
|
||||
const I444BufferInterface& source) {
|
||||
return Copy(source.width(), source.height(), source.DataY(), source.StrideY(),
|
||||
source.DataU(), source.StrideU(), source.DataV(),
|
||||
source.StrideV());
|
||||
}
|
||||
|
||||
// static
|
||||
rtc::scoped_refptr<I444Buffer> I444Buffer::Copy(int width,
|
||||
int height,
|
||||
const uint8_t* data_y,
|
||||
int stride_y,
|
||||
const uint8_t* data_u,
|
||||
int stride_u,
|
||||
const uint8_t* data_v,
|
||||
int stride_v) {
|
||||
// Note: May use different strides than the input data.
|
||||
rtc::scoped_refptr<I444Buffer> buffer = Create(width, height);
|
||||
RTC_CHECK_EQ(0, libyuv::I444Copy(data_y, stride_y, data_u, stride_u, data_v,
|
||||
stride_v, buffer->MutableDataY(),
|
||||
buffer->StrideY(), buffer->MutableDataU(),
|
||||
buffer->StrideU(), buffer->MutableDataV(),
|
||||
buffer->StrideV(), width, height));
|
||||
return buffer;
|
||||
}
|
||||
|
||||
// static
|
||||
rtc::scoped_refptr<I444Buffer> I444Buffer::Rotate(
|
||||
const I444BufferInterface& src,
|
||||
VideoRotation rotation) {
|
||||
RTC_CHECK(src.DataY());
|
||||
RTC_CHECK(src.DataU());
|
||||
RTC_CHECK(src.DataV());
|
||||
|
||||
int rotated_width = src.width();
|
||||
int rotated_height = src.height();
|
||||
if (rotation == webrtc::kVideoRotation_90 ||
|
||||
rotation == webrtc::kVideoRotation_270) {
|
||||
std::swap(rotated_width, rotated_height);
|
||||
}
|
||||
|
||||
rtc::scoped_refptr<webrtc::I444Buffer> buffer =
|
||||
I444Buffer::Create(rotated_width, rotated_height);
|
||||
|
||||
RTC_CHECK_EQ(0,
|
||||
libyuv::I444Rotate(
|
||||
src.DataY(), src.StrideY(), src.DataU(), src.StrideU(),
|
||||
src.DataV(), src.StrideV(), buffer->MutableDataY(),
|
||||
buffer->StrideY(), buffer->MutableDataU(), buffer->StrideU(),
|
||||
buffer->MutableDataV(), buffer->StrideV(), src.width(),
|
||||
src.height(), static_cast<libyuv::RotationMode>(rotation)));
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
rtc::scoped_refptr<I420BufferInterface> I444Buffer::ToI420() {
|
||||
rtc::scoped_refptr<I420Buffer> i420_buffer =
|
||||
I420Buffer::Create(width(), height());
|
||||
libyuv::I444ToI420(DataY(), StrideY(), DataU(), StrideU(), DataV(), StrideV(),
|
||||
i420_buffer->MutableDataY(), i420_buffer->StrideY(),
|
||||
i420_buffer->MutableDataU(), i420_buffer->StrideU(),
|
||||
i420_buffer->MutableDataV(), i420_buffer->StrideV(),
|
||||
width(), height());
|
||||
return i420_buffer;
|
||||
}
|
||||
|
||||
void I444Buffer::InitializeData() {
|
||||
memset(data_.get(), 0,
|
||||
I444DataSize(height_, stride_y_, stride_u_, stride_v_));
|
||||
}
|
||||
|
||||
int I444Buffer::width() const {
|
||||
return width_;
|
||||
}
|
||||
|
||||
int I444Buffer::height() const {
|
||||
return height_;
|
||||
}
|
||||
|
||||
const uint8_t* I444Buffer::DataY() const {
|
||||
return data_.get();
|
||||
}
|
||||
const uint8_t* I444Buffer::DataU() const {
|
||||
return data_.get() + stride_y_ * height_;
|
||||
}
|
||||
const uint8_t* I444Buffer::DataV() const {
|
||||
return data_.get() + stride_y_ * height_ + stride_u_ * ((height_));
|
||||
}
|
||||
|
||||
int I444Buffer::StrideY() const {
|
||||
return stride_y_;
|
||||
}
|
||||
int I444Buffer::StrideU() const {
|
||||
return stride_u_;
|
||||
}
|
||||
int I444Buffer::StrideV() const {
|
||||
return stride_v_;
|
||||
}
|
||||
|
||||
uint8_t* I444Buffer::MutableDataY() {
|
||||
return const_cast<uint8_t*>(DataY());
|
||||
}
|
||||
uint8_t* I444Buffer::MutableDataU() {
|
||||
return const_cast<uint8_t*>(DataU());
|
||||
}
|
||||
uint8_t* I444Buffer::MutableDataV() {
|
||||
return const_cast<uint8_t*>(DataV());
|
||||
}
|
||||
|
||||
void I444Buffer::CropAndScaleFrom(const I444BufferInterface& src,
|
||||
int offset_x,
|
||||
int offset_y,
|
||||
int crop_width,
|
||||
int crop_height) {
|
||||
RTC_CHECK_LE(crop_width, src.width());
|
||||
RTC_CHECK_LE(crop_height, src.height());
|
||||
RTC_CHECK_LE(crop_width + offset_x, src.width());
|
||||
RTC_CHECK_LE(crop_height + offset_y, src.height());
|
||||
RTC_CHECK_GE(offset_x, 0);
|
||||
RTC_CHECK_GE(offset_y, 0);
|
||||
|
||||
const uint8_t* y_plane = src.DataY() + src.StrideY() * offset_y + offset_x;
|
||||
const uint8_t* u_plane = src.DataU() + src.StrideU() * offset_y + offset_x;
|
||||
const uint8_t* v_plane = src.DataV() + src.StrideV() * offset_y + offset_x;
|
||||
int res =
|
||||
libyuv::I444Scale(y_plane, src.StrideY(), u_plane, src.StrideU(), v_plane,
|
||||
src.StrideV(), crop_width, crop_height, MutableDataY(),
|
||||
StrideY(), MutableDataU(), StrideU(), MutableDataV(),
|
||||
StrideV(), width(), height(), libyuv::kFilterBox);
|
||||
|
||||
RTC_DCHECK_EQ(res, 0);
|
||||
}
|
||||
|
||||
} // namespace webrtc
|
|
@ -1,104 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2021 The WebRTC project authors. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#ifndef API_VIDEO_I444_BUFFER_H_
|
||||
#define API_VIDEO_I444_BUFFER_H_
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "api/scoped_refptr.h"
|
||||
#include "api/video/video_frame_buffer.h"
|
||||
#include "api/video/video_rotation.h"
|
||||
#include "rtc_base/memory/aligned_malloc.h"
|
||||
#include "rtc_base/system/rtc_export.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
// Plain I444 buffer in standard memory.
|
||||
// I444 represents an image with in YUV format withouth any chroma subsampling.
|
||||
// https://en.wikipedia.org/wiki/Chroma_subsampling#4:4:4
|
||||
class RTC_EXPORT I444Buffer : public I444BufferInterface {
|
||||
public:
|
||||
static rtc::scoped_refptr<I444Buffer> Create(int width, int height);
|
||||
static rtc::scoped_refptr<I444Buffer> Create(int width,
|
||||
int height,
|
||||
int stride_y,
|
||||
int stride_u,
|
||||
int stride_v);
|
||||
|
||||
// Create a new buffer and copy the pixel data.
|
||||
static rtc::scoped_refptr<I444Buffer> Copy(const I444BufferInterface& buffer);
|
||||
|
||||
static rtc::scoped_refptr<I444Buffer> Copy(int width,
|
||||
int height,
|
||||
const uint8_t* data_y,
|
||||
int stride_y,
|
||||
const uint8_t* data_u,
|
||||
int stride_u,
|
||||
const uint8_t* data_v,
|
||||
int stride_v);
|
||||
|
||||
// Returns a rotated copy of |src|.
|
||||
static rtc::scoped_refptr<I444Buffer> Rotate(const I444BufferInterface& src,
|
||||
VideoRotation rotation);
|
||||
|
||||
rtc::scoped_refptr<I420BufferInterface> ToI420() final;
|
||||
const I420BufferInterface* GetI420() const final { return nullptr; }
|
||||
|
||||
// Sets all three planes to all zeros. Used to work around for
|
||||
// quirks in memory checkers
|
||||
// (https://bugs.chromium.org/p/libyuv/issues/detail?id=377) and
|
||||
// ffmpeg (http://crbug.com/390941).
|
||||
// TODO(nisse): Deprecated. Should be deleted if/when those issues
|
||||
// are resolved in a better way. Or in the mean time, use SetBlack.
|
||||
void InitializeData();
|
||||
|
||||
int width() const override;
|
||||
int height() const override;
|
||||
const uint8_t* DataY() const override;
|
||||
const uint8_t* DataU() const override;
|
||||
const uint8_t* DataV() const override;
|
||||
|
||||
int StrideY() const override;
|
||||
int StrideU() const override;
|
||||
int StrideV() const override;
|
||||
|
||||
uint8_t* MutableDataY();
|
||||
uint8_t* MutableDataU();
|
||||
uint8_t* MutableDataV();
|
||||
|
||||
// Scale the cropped area of |src| to the size of |this| buffer, and
|
||||
// write the result into |this|.
|
||||
void CropAndScaleFrom(const I444BufferInterface& src,
|
||||
int offset_x,
|
||||
int offset_y,
|
||||
int crop_width,
|
||||
int crop_height);
|
||||
|
||||
protected:
|
||||
I444Buffer(int width, int height);
|
||||
I444Buffer(int width, int height, int stride_y, int stride_u, int stride_v);
|
||||
|
||||
~I444Buffer() override;
|
||||
|
||||
private:
|
||||
const int width_;
|
||||
const int height_;
|
||||
const int stride_y_;
|
||||
const int stride_u_;
|
||||
const int stride_v_;
|
||||
const std::unique_ptr<uint8_t, AlignedFreeDeleter> data_;
|
||||
};
|
||||
|
||||
} // namespace webrtc
|
||||
|
||||
#endif // API_VIDEO_I444_BUFFER_H_
|
|
@ -11,7 +11,6 @@
|
|||
#include "api/video/video_frame_buffer.h"
|
||||
|
||||
#include "api/video/i420_buffer.h"
|
||||
#include "api/video/i444_buffer.h"
|
||||
#include "api/video/nv12_buffer.h"
|
||||
#include "rtc_base/checks.h"
|
||||
|
||||
|
@ -95,7 +94,7 @@ int I420BufferInterface::ChromaHeight() const {
|
|||
}
|
||||
|
||||
rtc::scoped_refptr<I420BufferInterface> I420BufferInterface::ToI420() {
|
||||
return rtc::scoped_refptr<I420BufferInterface>(this);
|
||||
return this;
|
||||
}
|
||||
|
||||
const I420BufferInterface* I420BufferInterface::GetI420() const {
|
||||
|
@ -118,19 +117,6 @@ int I444BufferInterface::ChromaHeight() const {
|
|||
return height();
|
||||
}
|
||||
|
||||
rtc::scoped_refptr<VideoFrameBuffer> I444BufferInterface::CropAndScale(
|
||||
int offset_x,
|
||||
int offset_y,
|
||||
int crop_width,
|
||||
int crop_height,
|
||||
int scaled_width,
|
||||
int scaled_height) {
|
||||
rtc::scoped_refptr<I444Buffer> result =
|
||||
I444Buffer::Create(scaled_width, scaled_height);
|
||||
result->CropAndScaleFrom(*this, offset_x, offset_y, crop_width, crop_height);
|
||||
return result;
|
||||
}
|
||||
|
||||
VideoFrameBuffer::Type I010BufferInterface::type() const {
|
||||
return Type::kI010;
|
||||
}
|
||||
|
|
|
@ -184,13 +184,6 @@ class I444BufferInterface : public PlanarYuv8Buffer {
|
|||
int ChromaWidth() const final;
|
||||
int ChromaHeight() const final;
|
||||
|
||||
rtc::scoped_refptr<VideoFrameBuffer> CropAndScale(int offset_x,
|
||||
int offset_y,
|
||||
int crop_width,
|
||||
int crop_height,
|
||||
int scaled_width,
|
||||
int scaled_height) override;
|
||||
|
||||
protected:
|
||||
~I444BufferInterface() override {}
|
||||
};
|
||||
|
|
|
@ -97,10 +97,6 @@ class VideoSourceInterface {
|
|||
// RemoveSink must guarantee that at the time the method returns,
|
||||
// there is no current and no future calls to VideoSinkInterface::OnFrame.
|
||||
virtual void RemoveSink(VideoSinkInterface<VideoFrameT>* sink) = 0;
|
||||
|
||||
// Request underlying source to capture a new frame.
|
||||
// TODO(crbug/1255737): make pure virtual once downstream projects adapt.
|
||||
virtual void RequestRefreshFrame() {}
|
||||
};
|
||||
|
||||
} // namespace rtc
|
||||
|
|
|
@ -23,13 +23,19 @@ class EncoderSwitchRequestCallback {
|
|||
public:
|
||||
virtual ~EncoderSwitchRequestCallback() {}
|
||||
|
||||
// Requests switch to next negotiated encoder.
|
||||
struct Config {
|
||||
std::string codec_name;
|
||||
absl::optional<std::string> param;
|
||||
absl::optional<std::string> value;
|
||||
};
|
||||
|
||||
// Requests that encoder fallback is performed.
|
||||
virtual void RequestEncoderFallback() = 0;
|
||||
|
||||
// Requests switch to a specific encoder. If the encoder is not available and
|
||||
// `allow_default_fallback` is `true` the default fallback is invoked.
|
||||
virtual void RequestEncoderSwitch(const SdpVideoFormat& format,
|
||||
bool allow_default_fallback) = 0;
|
||||
// Requests that a switch to a specific encoder is performed.
|
||||
virtual void RequestEncoderSwitch(const Config& conf) = 0;
|
||||
|
||||
virtual void RequestEncoderSwitch(const SdpVideoFormat& format) = 0;
|
||||
};
|
||||
|
||||
struct VideoStreamEncoderSettings {
|
||||
|
|
|
@ -11,7 +11,6 @@
|
|||
#include "api/video/video_timing.h"
|
||||
|
||||
#include "api/array_view.h"
|
||||
#include "api/units/time_delta.h"
|
||||
#include "rtc_base/logging.h"
|
||||
#include "rtc_base/numerics/safe_conversions.h"
|
||||
#include "rtc_base/strings/string_builder.h"
|
||||
|
@ -26,14 +25,6 @@ uint16_t VideoSendTiming::GetDeltaCappedMs(int64_t base_ms, int64_t time_ms) {
|
|||
return rtc::saturated_cast<uint16_t>(time_ms - base_ms);
|
||||
}
|
||||
|
||||
uint16_t VideoSendTiming::GetDeltaCappedMs(TimeDelta delta) {
|
||||
if (delta < TimeDelta::Zero()) {
|
||||
RTC_DLOG(LS_ERROR) << "Delta " << delta.ms()
|
||||
<< "ms expected to be positive";
|
||||
}
|
||||
return rtc::saturated_cast<uint16_t>(delta.ms());
|
||||
}
|
||||
|
||||
TimingFrameInfo::TimingFrameInfo()
|
||||
: rtp_timestamp(0),
|
||||
capture_time_ms(-1),
|
||||
|
|
|
@ -16,8 +16,6 @@
|
|||
#include <limits>
|
||||
#include <string>
|
||||
|
||||
#include "api/units/time_delta.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
// Video timing timestamps in ms counted from capture_time_ms of a frame.
|
||||
|
@ -36,7 +34,6 @@ struct VideoSendTiming {
|
|||
// https://webrtc.org/experiments/rtp-hdrext/video-timing/ extension stores
|
||||
// 16-bit deltas of timestamps from packet capture time.
|
||||
static uint16_t GetDeltaCappedMs(int64_t base_ms, int64_t time_ms);
|
||||
static uint16_t GetDeltaCappedMs(TimeDelta delta);
|
||||
|
||||
uint16_t encode_start_delta_ms;
|
||||
uint16_t encode_finish_delta_ms;
|
||||
|
|
|
@ -68,8 +68,7 @@ constexpr ProfilePattern kProfilePatterns[] = {
|
|||
{0x58, BitPattern("10xx0000"), H264Profile::kProfileBaseline},
|
||||
{0x4D, BitPattern("0x0x0000"), H264Profile::kProfileMain},
|
||||
{0x64, BitPattern("00000000"), H264Profile::kProfileHigh},
|
||||
{0x64, BitPattern("00001100"), H264Profile::kProfileConstrainedHigh},
|
||||
{0xF4, BitPattern("00000000"), H264Profile::kProfilePredictiveHigh444}};
|
||||
{0x64, BitPattern("00001100"), H264Profile::kProfileConstrainedHigh}};
|
||||
|
||||
struct LevelConstraint {
|
||||
const int max_macroblocks_per_second;
|
||||
|
@ -229,9 +228,6 @@ absl::optional<std::string> H264ProfileLevelIdToString(
|
|||
case H264Profile::kProfileHigh:
|
||||
profile_idc_iop_string = "6400";
|
||||
break;
|
||||
case H264Profile::kProfilePredictiveHigh444:
|
||||
profile_idc_iop_string = "f400";
|
||||
break;
|
||||
// Unrecognized profile.
|
||||
default:
|
||||
return absl::nullopt;
|
||||
|
|
|
@ -25,7 +25,6 @@ enum class H264Profile {
|
|||
kProfileMain,
|
||||
kProfileConstrainedHigh,
|
||||
kProfileHigh,
|
||||
kProfilePredictiveHigh444,
|
||||
};
|
||||
|
||||
// All values are equal to ten times the level number, except level 1b which is
|
||||
|
|
|
@ -174,23 +174,4 @@ VideoCodecType PayloadStringToCodecType(const std::string& name) {
|
|||
return kVideoCodecGeneric;
|
||||
}
|
||||
|
||||
VideoCodecComplexity VideoCodec::GetVideoEncoderComplexity() const {
|
||||
if (complexity_.has_value()) {
|
||||
return complexity_.value();
|
||||
}
|
||||
switch (codecType) {
|
||||
case kVideoCodecVP8:
|
||||
return VP8().complexity;
|
||||
case kVideoCodecVP9:
|
||||
return VP9().complexity;
|
||||
default:
|
||||
return VideoCodecComplexity::kComplexityNormal;
|
||||
}
|
||||
}
|
||||
|
||||
void VideoCodec::SetVideoEncoderComplexity(
|
||||
VideoCodecComplexity complexity_setting) {
|
||||
complexity_ = complexity_setting;
|
||||
}
|
||||
|
||||
} // namespace webrtc
|
||||
|
|
|
@ -129,9 +129,6 @@ class RTC_EXPORT VideoCodec {
|
|||
scalability_mode_ = std::string(scalability_mode);
|
||||
}
|
||||
|
||||
VideoCodecComplexity GetVideoEncoderComplexity() const;
|
||||
void SetVideoEncoderComplexity(VideoCodecComplexity complexity_setting);
|
||||
|
||||
// Public variables. TODO(hta): Make them private with accessors.
|
||||
VideoCodecType codecType;
|
||||
|
||||
|
@ -196,9 +193,6 @@ class RTC_EXPORT VideoCodec {
|
|||
// This will allow removing the VideoCodec* types from this file.
|
||||
VideoCodecUnion codec_specific_;
|
||||
std::string scalability_mode_;
|
||||
// 'complexity_' indicates the CPU capability of the client. It's used to
|
||||
// determine encoder CPU complexity (e.g., cpu_used for VP8, VP9. and AV1).
|
||||
absl::optional<VideoCodecComplexity> complexity_;
|
||||
};
|
||||
|
||||
} // namespace webrtc
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
#include <string>
|
||||
#include <utility>
|
||||
|
||||
#include "absl/base/macros.h"
|
||||
#include "api/video/encoded_image.h"
|
||||
#include "api/video_codecs/video_decoder.h"
|
||||
#include "modules/video_coding/include/video_error_codes.h"
|
||||
|
@ -214,7 +215,7 @@ int32_t VideoDecoderSoftwareFallbackWrapper::Decode(
|
|||
}
|
||||
|
||||
// Fallback decoder initialized, fall-through.
|
||||
[[fallthrough]];
|
||||
ABSL_FALLTHROUGH_INTENDED;
|
||||
}
|
||||
case DecoderType::kFallback:
|
||||
return fallback_decoder_->Decode(input_image, missing_frames,
|
||||
|
|
|
@ -155,7 +155,7 @@ class VideoEncoderSoftwareFallbackWrapper final : public VideoEncoder {
|
|||
RTC_LOG(LS_WARNING)
|
||||
<< "Trying to access encoder in uninitialized fallback wrapper.";
|
||||
// Return main encoder to preserve previous behavior.
|
||||
[[fallthrough]];
|
||||
ABSL_FALLTHROUGH_INTENDED;
|
||||
case EncoderState::kMainEncoderUsed:
|
||||
return encoder_.get();
|
||||
case EncoderState::kFallbackDueToFailure:
|
||||
|
|
|
@ -29,7 +29,9 @@
|
|||
|
||||
namespace webrtc {
|
||||
class PacketRouter;
|
||||
class ProcessThread;
|
||||
class RtcEventLog;
|
||||
class RtpPacketReceived;
|
||||
class RtpStreamReceiverControllerInterface;
|
||||
class RtpStreamReceiverInterface;
|
||||
|
||||
|
|
|
@ -102,23 +102,6 @@ AudioTransportImpl::AudioTransportImpl(
|
|||
|
||||
AudioTransportImpl::~AudioTransportImpl() {}
|
||||
|
||||
int32_t AudioTransportImpl::RecordedDataIsAvailable(
|
||||
const void* audio_data,
|
||||
const size_t number_of_frames,
|
||||
const size_t bytes_per_sample,
|
||||
const size_t number_of_channels,
|
||||
const uint32_t sample_rate,
|
||||
const uint32_t audio_delay_milliseconds,
|
||||
const int32_t clock_drift,
|
||||
const uint32_t volume,
|
||||
const bool key_pressed,
|
||||
uint32_t& new_mic_volume) { // NOLINT: to avoid changing APIs
|
||||
return RecordedDataIsAvailable(
|
||||
audio_data, number_of_frames, bytes_per_sample, number_of_channels,
|
||||
sample_rate, audio_delay_milliseconds, clock_drift, volume, key_pressed,
|
||||
new_mic_volume, /* estimated_capture_time_ns */ 0);
|
||||
}
|
||||
|
||||
// Not used in Chromium. Process captured audio and distribute to all sending
|
||||
// streams, and try to do this at the lowest possible sample rate.
|
||||
int32_t AudioTransportImpl::RecordedDataIsAvailable(
|
||||
|
@ -131,9 +114,7 @@ int32_t AudioTransportImpl::RecordedDataIsAvailable(
|
|||
const int32_t /*clock_drift*/,
|
||||
const uint32_t /*volume*/,
|
||||
const bool key_pressed,
|
||||
uint32_t& /*new_mic_volume*/,
|
||||
const int64_t
|
||||
estimated_capture_time_ns) { // NOLINT: to avoid changing APIs
|
||||
uint32_t& /*new_mic_volume*/) { // NOLINT: to avoid changing APIs
|
||||
RTC_DCHECK(audio_data);
|
||||
RTC_DCHECK_GE(number_of_channels, 1);
|
||||
RTC_DCHECK_LE(number_of_channels, 2);
|
||||
|
@ -163,8 +144,25 @@ int32_t AudioTransportImpl::RecordedDataIsAvailable(
|
|||
ProcessCaptureFrame(audio_delay_milliseconds, key_pressed,
|
||||
swap_stereo_channels, audio_processing_,
|
||||
audio_frame.get());
|
||||
audio_frame->set_absolute_capture_timestamp_ms(estimated_capture_time_ns /
|
||||
1000000);
|
||||
|
||||
// Typing detection (utilizes the APM/VAD decision). We let the VAD determine
|
||||
// if we're using this feature or not.
|
||||
// TODO(solenberg): GetConfig() takes a lock. Work around that.
|
||||
bool typing_detected = false;
|
||||
if (audio_processing_ &&
|
||||
audio_processing_->GetConfig().voice_detection.enabled) {
|
||||
if (audio_frame->vad_activity_ != AudioFrame::kVadUnknown) {
|
||||
bool vad_active = audio_frame->vad_activity_ == AudioFrame::kVadActive;
|
||||
typing_detected = typing_detection_.Process(key_pressed, vad_active);
|
||||
}
|
||||
}
|
||||
|
||||
// Copy frame and push to each sending stream. The copy is required since an
|
||||
// encoding task will be posted internally to each stream.
|
||||
{
|
||||
MutexLock lock(&capture_lock_);
|
||||
typing_noise_detected_ = typing_detected;
|
||||
}
|
||||
|
||||
RTC_DCHECK_GT(audio_frame->samples_per_channel_, 0);
|
||||
if (async_audio_processing_)
|
||||
|
@ -272,4 +270,8 @@ void AudioTransportImpl::SetStereoChannelSwapping(bool enable) {
|
|||
swap_stereo_channels_ = enable;
|
||||
}
|
||||
|
||||
bool AudioTransportImpl::typing_noise_detected() const {
|
||||
MutexLock lock(&capture_lock_);
|
||||
return typing_noise_detected_;
|
||||
}
|
||||
} // namespace webrtc
|
||||
|
|
|
@ -41,34 +41,21 @@ class AudioTransportImpl : public AudioTransport {
|
|||
|
||||
~AudioTransportImpl() override;
|
||||
|
||||
// TODO(bugs.webrtc.org/13620) Deprecate this function
|
||||
int32_t RecordedDataIsAvailable(const void* audioSamples,
|
||||
size_t nSamples,
|
||||
size_t nBytesPerSample,
|
||||
size_t nChannels,
|
||||
uint32_t samplesPerSec,
|
||||
uint32_t totalDelayMS,
|
||||
int32_t clockDrift,
|
||||
uint32_t currentMicLevel,
|
||||
bool keyPressed,
|
||||
const size_t nSamples,
|
||||
const size_t nBytesPerSample,
|
||||
const size_t nChannels,
|
||||
const uint32_t samplesPerSec,
|
||||
const uint32_t totalDelayMS,
|
||||
const int32_t clockDrift,
|
||||
const uint32_t currentMicLevel,
|
||||
const bool keyPressed,
|
||||
uint32_t& newMicLevel) override;
|
||||
|
||||
int32_t RecordedDataIsAvailable(const void* audioSamples,
|
||||
size_t nSamples,
|
||||
size_t nBytesPerSample,
|
||||
size_t nChannels,
|
||||
uint32_t samplesPerSec,
|
||||
uint32_t totalDelayMS,
|
||||
int32_t clockDrift,
|
||||
uint32_t currentMicLevel,
|
||||
bool keyPressed,
|
||||
uint32_t& newMicLevel,
|
||||
int64_t estimated_capture_time_ns) override;
|
||||
|
||||
int32_t NeedMorePlayData(size_t nSamples,
|
||||
size_t nBytesPerSample,
|
||||
size_t nChannels,
|
||||
uint32_t samplesPerSec,
|
||||
int32_t NeedMorePlayData(const size_t nSamples,
|
||||
const size_t nBytesPerSample,
|
||||
const size_t nChannels,
|
||||
const uint32_t samplesPerSec,
|
||||
void* audioSamples,
|
||||
size_t& nSamplesOut,
|
||||
int64_t* elapsed_time_ms,
|
||||
|
@ -86,9 +73,7 @@ class AudioTransportImpl : public AudioTransport {
|
|||
int send_sample_rate_hz,
|
||||
size_t send_num_channels);
|
||||
void SetStereoChannelSwapping(bool enable);
|
||||
// Deprecated.
|
||||
// TODO(bugs.webrtc.org/11226): Remove.
|
||||
bool typing_noise_detected() const { return false; }
|
||||
bool typing_noise_detected() const;
|
||||
|
||||
private:
|
||||
void SendProcessedData(std::unique_ptr<AudioFrame> audio_frame);
|
||||
|
@ -105,6 +90,7 @@ class AudioTransportImpl : public AudioTransport {
|
|||
std::vector<AudioSender*> audio_senders_ RTC_GUARDED_BY(capture_lock_);
|
||||
int send_sample_rate_hz_ RTC_GUARDED_BY(capture_lock_) = 8000;
|
||||
size_t send_num_channels_ RTC_GUARDED_BY(capture_lock_) = 1;
|
||||
bool typing_noise_detected_ RTC_GUARDED_BY(capture_lock_) = false;
|
||||
bool swap_stereo_channels_ RTC_GUARDED_BY(capture_lock_) = false;
|
||||
PushResampler<int16_t> capture_resampler_;
|
||||
TypingDetection typing_detection_;
|
||||
|
|
|
@ -39,6 +39,7 @@
|
|||
#include "modules/rtp_rtcp/source/rtp_packet_received.h"
|
||||
#include "modules/rtp_rtcp/source/rtp_rtcp_config.h"
|
||||
#include "modules/rtp_rtcp/source/rtp_rtcp_impl2.h"
|
||||
#include "modules/utility/include/process_thread.h"
|
||||
#include "rtc_base/checks.h"
|
||||
#include "rtc_base/format_macros.h"
|
||||
#include "rtc_base/location.h"
|
||||
|
@ -846,11 +847,14 @@ CallReceiveStatistics ChannelReceive::GetRTCPStatistics() const {
|
|||
absl::optional<RtpRtcpInterface::SenderReportStats> rtcp_sr_stats =
|
||||
rtp_rtcp_->GetSenderReportStats();
|
||||
if (rtcp_sr_stats.has_value()) {
|
||||
// Number of seconds since 1900 January 1 00:00 GMT (see
|
||||
// https://tools.ietf.org/html/rfc868).
|
||||
constexpr int64_t kNtpJan1970Millisecs =
|
||||
2208988800 * rtc::kNumMillisecsPerSec;
|
||||
stats.last_sender_report_timestamp_ms =
|
||||
rtcp_sr_stats->last_arrival_timestamp.ToMs() -
|
||||
rtc::kNtpJan1970Millisecs;
|
||||
rtcp_sr_stats->last_arrival_timestamp.ToMs() - kNtpJan1970Millisecs;
|
||||
stats.last_sender_report_remote_timestamp_ms =
|
||||
rtcp_sr_stats->last_remote_timestamp.ToMs() - rtc::kNtpJan1970Millisecs;
|
||||
rtcp_sr_stats->last_remote_timestamp.ToMs() - kNtpJan1970Millisecs;
|
||||
stats.sender_reports_packets_sent = rtcp_sr_stats->packets_sent;
|
||||
stats.sender_reports_bytes_sent = rtcp_sr_stats->bytes_sent;
|
||||
stats.sender_reports_reports_count = rtcp_sr_stats->reports_count;
|
||||
|
|
|
@ -79,7 +79,7 @@ void ChannelReceiveFrameTransformerDelegate::Transform(
|
|||
|
||||
void ChannelReceiveFrameTransformerDelegate::OnTransformedFrame(
|
||||
std::unique_ptr<TransformableFrameInterface> frame) {
|
||||
rtc::scoped_refptr<ChannelReceiveFrameTransformerDelegate> delegate(this);
|
||||
rtc::scoped_refptr<ChannelReceiveFrameTransformerDelegate> delegate = this;
|
||||
channel_receive_thread_->PostTask(ToQueuedTask(
|
||||
[delegate = std::move(delegate), frame = std::move(frame)]() mutable {
|
||||
delegate->ReceiveFrame(std::move(frame));
|
||||
|
|
|
@ -31,6 +31,7 @@
|
|||
#include "modules/audio_processing/rms_level.h"
|
||||
#include "modules/pacing/packet_router.h"
|
||||
#include "modules/rtp_rtcp/source/rtp_rtcp_impl2.h"
|
||||
#include "modules/utility/include/process_thread.h"
|
||||
#include "rtc_base/checks.h"
|
||||
#include "rtc_base/event.h"
|
||||
#include "rtc_base/format_macros.h"
|
||||
|
|
|
@ -102,7 +102,7 @@ void ChannelSendFrameTransformerDelegate::OnTransformedFrame(
|
|||
MutexLock lock(&send_lock_);
|
||||
if (!send_frame_callback_)
|
||||
return;
|
||||
rtc::scoped_refptr<ChannelSendFrameTransformerDelegate> delegate(this);
|
||||
rtc::scoped_refptr<ChannelSendFrameTransformerDelegate> delegate = this;
|
||||
encoder_queue_->PostTask(
|
||||
[delegate = std::move(delegate), frame = std::move(frame)]() mutable {
|
||||
delegate->SendFrame(std::move(frame));
|
||||
|
|
|
@ -53,6 +53,9 @@ class VoipCore : public VoipEngine,
|
|||
public VoipVolumeControl {
|
||||
public:
|
||||
// Construct VoipCore with provided arguments.
|
||||
// ProcessThread implementation can be injected by `process_thread`
|
||||
// (mainly for testing purpose) and when set to nullptr, default
|
||||
// implementation will be used.
|
||||
VoipCore(rtc::scoped_refptr<AudioEncoderFactory> encoder_factory,
|
||||
rtc::scoped_refptr<AudioDecoderFactory> decoder_factory,
|
||||
std::unique_ptr<TaskQueueFactory> task_queue_factory,
|
||||
|
|
|
@ -1,295 +0,0 @@
|
|||
// Copyright 2019 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include <atomic>
|
||||
#include <vector>
|
||||
|
||||
#include "base/allocator/partition_allocator/partition_alloc.h"
|
||||
#include "base/strings/stringprintf.h"
|
||||
#include "base/threading/platform_thread.h"
|
||||
#include "base/time/time.h"
|
||||
#include "base/timer/lap_timer.h"
|
||||
#include "build/build_config.h"
|
||||
#include "testing/gtest/include/gtest/gtest.h"
|
||||
#include "testing/perf/perf_result_reporter.h"
|
||||
|
||||
namespace base {
|
||||
namespace {
|
||||
|
||||
// Change kTimeLimit to something higher if you need more time to capture a
|
||||
// trace.
|
||||
constexpr base::TimeDelta kTimeLimit = base::TimeDelta::FromSeconds(2);
|
||||
constexpr int kWarmupRuns = 5;
|
||||
constexpr int kTimeCheckInterval = 100000;
|
||||
|
||||
// Size constants are mostly arbitrary, but try to simulate something like CSS
|
||||
// parsing which consists of lots of relatively small objects.
|
||||
constexpr int kMultiBucketMinimumSize = 24;
|
||||
constexpr int kMultiBucketIncrement = 13;
|
||||
// Final size is 24 + (13 * 22) = 310 bytes.
|
||||
constexpr int kMultiBucketRounds = 22;
|
||||
|
||||
constexpr char kMetricPrefixMemoryAllocation[] = "MemoryAllocation";
|
||||
constexpr char kMetricThroughput[] = "throughput";
|
||||
constexpr char kMetricTimePerAllocation[] = "time_per_allocation";
|
||||
|
||||
perf_test::PerfResultReporter SetUpReporter(const std::string& story_name) {
|
||||
perf_test::PerfResultReporter reporter(kMetricPrefixMemoryAllocation,
|
||||
story_name);
|
||||
reporter.RegisterImportantMetric(kMetricThroughput, "runs/s");
|
||||
reporter.RegisterImportantMetric(kMetricTimePerAllocation, "ns");
|
||||
return reporter;
|
||||
}
|
||||
|
||||
enum class AllocatorType { kSystem, kPartitionAlloc };
|
||||
|
||||
class Allocator {
|
||||
public:
|
||||
Allocator() = default;
|
||||
virtual ~Allocator() = default;
|
||||
virtual void Init() {}
|
||||
virtual void* Alloc(size_t size) = 0;
|
||||
virtual void Free(void* data) = 0;
|
||||
};
|
||||
|
||||
class SystemAllocator : public Allocator {
|
||||
public:
|
||||
SystemAllocator() = default;
|
||||
~SystemAllocator() override = default;
|
||||
void* Alloc(size_t size) override { return malloc(size); }
|
||||
void Free(void* data) override { free(data); }
|
||||
};
|
||||
|
||||
class PartitionAllocator : public Allocator {
|
||||
public:
|
||||
PartitionAllocator()
|
||||
: alloc_(std::make_unique<PartitionAllocatorGeneric>()) {}
|
||||
~PartitionAllocator() override = default;
|
||||
|
||||
void Init() override { alloc_->init(); }
|
||||
void* Alloc(size_t size) override { return alloc_->root()->Alloc(size, ""); }
|
||||
void Free(void* data) override { return alloc_->root()->Free(data); }
|
||||
|
||||
private:
|
||||
std::unique_ptr<PartitionAllocatorGeneric> alloc_;
|
||||
};
|
||||
|
||||
class TestLoopThread : public PlatformThread::Delegate {
|
||||
public:
|
||||
explicit TestLoopThread(OnceCallback<float()> test_fn)
|
||||
: test_fn_(std::move(test_fn)) {
|
||||
CHECK(PlatformThread::Create(0, this, &thread_handle_));
|
||||
}
|
||||
|
||||
float Run() {
|
||||
PlatformThread::Join(thread_handle_);
|
||||
return laps_per_second_;
|
||||
}
|
||||
|
||||
void ThreadMain() override { laps_per_second_ = std::move(test_fn_).Run(); }
|
||||
|
||||
OnceCallback<float()> test_fn_;
|
||||
PlatformThreadHandle thread_handle_;
|
||||
std::atomic<float> laps_per_second_;
|
||||
};
|
||||
|
||||
void DisplayResults(const std::string& story_name,
|
||||
float iterations_per_second) {
|
||||
auto reporter = SetUpReporter(story_name);
|
||||
reporter.AddResult(kMetricThroughput, iterations_per_second);
|
||||
reporter.AddResult(kMetricTimePerAllocation,
|
||||
static_cast<size_t>(1e9 / iterations_per_second));
|
||||
}
|
||||
|
||||
class MemoryAllocationPerfNode {
|
||||
public:
|
||||
MemoryAllocationPerfNode* GetNext() const { return next_; }
|
||||
void SetNext(MemoryAllocationPerfNode* p) { next_ = p; }
|
||||
static void FreeAll(MemoryAllocationPerfNode* first, Allocator* alloc) {
|
||||
MemoryAllocationPerfNode* cur = first;
|
||||
while (cur != nullptr) {
|
||||
MemoryAllocationPerfNode* next = cur->GetNext();
|
||||
alloc->Free(cur);
|
||||
cur = next;
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
MemoryAllocationPerfNode* next_ = nullptr;
|
||||
};
|
||||
|
||||
#if !defined(OS_ANDROID)
|
||||
float SingleBucket(Allocator* allocator) {
|
||||
auto* first =
|
||||
reinterpret_cast<MemoryAllocationPerfNode*>(allocator->Alloc(40));
|
||||
|
||||
LapTimer timer(kWarmupRuns, kTimeLimit, kTimeCheckInterval);
|
||||
MemoryAllocationPerfNode* cur = first;
|
||||
do {
|
||||
auto* next =
|
||||
reinterpret_cast<MemoryAllocationPerfNode*>(allocator->Alloc(40));
|
||||
CHECK_NE(next, nullptr);
|
||||
cur->SetNext(next);
|
||||
cur = next;
|
||||
timer.NextLap();
|
||||
} while (!timer.HasTimeLimitExpired());
|
||||
// next_ = nullptr only works if the class constructor is called (it's not
|
||||
// called in this case because then we can allocate arbitrary-length
|
||||
// payloads.)
|
||||
cur->SetNext(nullptr);
|
||||
|
||||
MemoryAllocationPerfNode::FreeAll(first, allocator);
|
||||
return timer.LapsPerSecond();
|
||||
}
|
||||
#endif // defined(OS_ANDROID)
|
||||
|
||||
float SingleBucketWithFree(Allocator* allocator) {
|
||||
// Allocate an initial element to make sure the bucket stays set up.
|
||||
void* elem = allocator->Alloc(40);
|
||||
|
||||
LapTimer timer(kWarmupRuns, kTimeLimit, kTimeCheckInterval);
|
||||
do {
|
||||
void* cur = allocator->Alloc(40);
|
||||
CHECK_NE(cur, nullptr);
|
||||
allocator->Free(cur);
|
||||
timer.NextLap();
|
||||
} while (!timer.HasTimeLimitExpired());
|
||||
|
||||
allocator->Free(elem);
|
||||
return timer.LapsPerSecond();
|
||||
}
|
||||
|
||||
#if !defined(OS_ANDROID)
|
||||
float MultiBucket(Allocator* allocator) {
|
||||
auto* first =
|
||||
reinterpret_cast<MemoryAllocationPerfNode*>(allocator->Alloc(40));
|
||||
MemoryAllocationPerfNode* cur = first;
|
||||
|
||||
LapTimer timer(kWarmupRuns, kTimeLimit, kTimeCheckInterval);
|
||||
do {
|
||||
for (int i = 0; i < kMultiBucketRounds; i++) {
|
||||
auto* next = reinterpret_cast<MemoryAllocationPerfNode*>(allocator->Alloc(
|
||||
kMultiBucketMinimumSize + (i * kMultiBucketIncrement)));
|
||||
CHECK_NE(next, nullptr);
|
||||
cur->SetNext(next);
|
||||
cur = next;
|
||||
}
|
||||
timer.NextLap();
|
||||
} while (!timer.HasTimeLimitExpired());
|
||||
cur->SetNext(nullptr);
|
||||
|
||||
MemoryAllocationPerfNode::FreeAll(first, allocator);
|
||||
|
||||
return timer.LapsPerSecond() * kMultiBucketRounds;
|
||||
}
|
||||
#endif // defined(OS_ANDROID)
|
||||
|
||||
float MultiBucketWithFree(Allocator* allocator) {
|
||||
std::vector<void*> elems;
|
||||
elems.reserve(kMultiBucketRounds);
|
||||
// Do an initial round of allocation to make sure that the buckets stay in
|
||||
// use (and aren't accidentally released back to the OS).
|
||||
for (int i = 0; i < kMultiBucketRounds; i++) {
|
||||
void* cur =
|
||||
allocator->Alloc(kMultiBucketMinimumSize + (i * kMultiBucketIncrement));
|
||||
CHECK_NE(cur, nullptr);
|
||||
elems.push_back(cur);
|
||||
}
|
||||
|
||||
LapTimer timer(kWarmupRuns, kTimeLimit, kTimeCheckInterval);
|
||||
do {
|
||||
for (int i = 0; i < kMultiBucketRounds; i++) {
|
||||
void* cur = allocator->Alloc(kMultiBucketMinimumSize +
|
||||
(i * kMultiBucketIncrement));
|
||||
CHECK_NE(cur, nullptr);
|
||||
allocator->Free(cur);
|
||||
}
|
||||
timer.NextLap();
|
||||
} while (!timer.HasTimeLimitExpired());
|
||||
|
||||
for (void* ptr : elems) {
|
||||
allocator->Free(ptr);
|
||||
}
|
||||
|
||||
return timer.LapsPerSecond() * kMultiBucketRounds;
|
||||
}
|
||||
|
||||
std::unique_ptr<Allocator> CreateAllocator(AllocatorType type) {
|
||||
if (type == AllocatorType::kSystem)
|
||||
return std::make_unique<SystemAllocator>();
|
||||
return std::make_unique<PartitionAllocator>();
|
||||
}
|
||||
|
||||
void RunTest(int thread_count,
|
||||
AllocatorType alloc_type,
|
||||
float (*test_fn)(Allocator*),
|
||||
const char* story_base_name) {
|
||||
auto alloc = CreateAllocator(alloc_type);
|
||||
alloc->Init();
|
||||
|
||||
std::vector<std::unique_ptr<TestLoopThread>> threads;
|
||||
for (int i = 0; i < thread_count; ++i) {
|
||||
threads.push_back(std::make_unique<TestLoopThread>(
|
||||
BindOnce(test_fn, Unretained(alloc.get()))));
|
||||
}
|
||||
|
||||
uint64_t total_laps_per_second = 0;
|
||||
uint64_t min_laps_per_second = std::numeric_limits<uint64_t>::max();
|
||||
for (int i = 0; i < thread_count; ++i) {
|
||||
uint64_t laps_per_second = threads[i]->Run();
|
||||
min_laps_per_second = std::min(laps_per_second, min_laps_per_second);
|
||||
total_laps_per_second += laps_per_second;
|
||||
}
|
||||
|
||||
std::string name = base::StringPrintf(
|
||||
"%s.%s_%s_%d", kMetricPrefixMemoryAllocation, story_base_name,
|
||||
alloc_type == AllocatorType::kSystem ? "System" : "PartitionAlloc",
|
||||
thread_count);
|
||||
|
||||
DisplayResults(name + "_total", total_laps_per_second);
|
||||
DisplayResults(name + "_worst", min_laps_per_second);
|
||||
}
|
||||
|
||||
class MemoryAllocationPerfTest
|
||||
: public testing::TestWithParam<std::tuple<int, AllocatorType>> {};
|
||||
|
||||
INSTANTIATE_TEST_SUITE_P(
|
||||
,
|
||||
MemoryAllocationPerfTest,
|
||||
::testing::Combine(::testing::Values(1, 2, 3, 4),
|
||||
::testing::Values(AllocatorType::kSystem,
|
||||
AllocatorType::kPartitionAlloc)));
|
||||
|
||||
// This test (and the other one below) allocates a large amount of memory, which
|
||||
// can cause issues on Android.
|
||||
#if !defined(OS_ANDROID)
|
||||
TEST_P(MemoryAllocationPerfTest, SingleBucket) {
|
||||
auto params = GetParam();
|
||||
RunTest(std::get<0>(params), std::get<1>(params), SingleBucket,
|
||||
"SingleBucket");
|
||||
}
|
||||
#endif
|
||||
|
||||
TEST_P(MemoryAllocationPerfTest, SingleBucketWithFree) {
|
||||
auto params = GetParam();
|
||||
RunTest(std::get<0>(params), std::get<1>(params), SingleBucketWithFree,
|
||||
"SingleBucketWithFree");
|
||||
}
|
||||
|
||||
#if !defined(OS_ANDROID)
|
||||
TEST_P(MemoryAllocationPerfTest, MultiBucket) {
|
||||
auto params = GetParam();
|
||||
RunTest(std::get<0>(params), std::get<1>(params), MultiBucket, "MultiBucket");
|
||||
}
|
||||
#endif
|
||||
|
||||
TEST_P(MemoryAllocationPerfTest, MultiBucketWithFree) {
|
||||
auto params = GetParam();
|
||||
RunTest(std::get<0>(params), std::get<1>(params), MultiBucketWithFree,
|
||||
"MultiBucketWithFree");
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
} // namespace base
|
|
@ -1,96 +0,0 @@
|
|||
// Copyright 2019 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "base/allocator/partition_allocator/spin_lock.h"
|
||||
#include "base/threading/platform_thread.h"
|
||||
#include "base/time/time.h"
|
||||
#include "base/timer/lap_timer.h"
|
||||
#include "testing/gtest/include/gtest/gtest.h"
|
||||
#include "testing/perf/perf_result_reporter.h"
|
||||
|
||||
namespace base {
|
||||
namespace {
|
||||
|
||||
constexpr int kWarmupRuns = 1;
|
||||
constexpr TimeDelta kTimeLimit = TimeDelta::FromSeconds(1);
|
||||
constexpr int kTimeCheckInterval = 100000;
|
||||
|
||||
constexpr char kMetricPrefixSpinLock[] = "SpinLock.";
|
||||
constexpr char kMetricLockUnlockThroughput[] = "lock_unlock_throughput";
|
||||
constexpr char kStoryBaseline[] = "baseline_story";
|
||||
constexpr char kStoryWithCompetingThread[] = "with_competing_thread";
|
||||
|
||||
perf_test::PerfResultReporter SetUpReporter(const std::string& story_name) {
|
||||
perf_test::PerfResultReporter reporter(kMetricPrefixSpinLock, story_name);
|
||||
reporter.RegisterImportantMetric(kMetricLockUnlockThroughput, "runs/s");
|
||||
return reporter;
|
||||
}
|
||||
|
||||
class Spin : public PlatformThread::Delegate {
|
||||
public:
|
||||
Spin(subtle::SpinLock* lock, size_t* data)
|
||||
: lock_(lock), data_(data), should_stop_(false) {}
|
||||
~Spin() override = default;
|
||||
|
||||
void ThreadMain() override {
|
||||
while (!should_stop_.load(std::memory_order_relaxed)) {
|
||||
lock_->lock();
|
||||
(*data_)++;
|
||||
lock_->unlock();
|
||||
}
|
||||
}
|
||||
|
||||
void Stop() { should_stop_ = true; }
|
||||
|
||||
private:
|
||||
subtle::SpinLock* lock_;
|
||||
size_t* data_;
|
||||
std::atomic<bool> should_stop_;
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
TEST(SpinLockPerfTest, Simple) {
|
||||
LapTimer timer(kWarmupRuns, kTimeLimit, kTimeCheckInterval);
|
||||
size_t data = 0;
|
||||
|
||||
subtle::SpinLock lock;
|
||||
|
||||
do {
|
||||
lock.lock();
|
||||
data += 1;
|
||||
lock.unlock();
|
||||
timer.NextLap();
|
||||
} while (!timer.HasTimeLimitExpired());
|
||||
|
||||
auto reporter = SetUpReporter(kStoryBaseline);
|
||||
reporter.AddResult(kMetricLockUnlockThroughput, timer.LapsPerSecond());
|
||||
}
|
||||
|
||||
TEST(SpinLockPerfTest, WithCompetingThread) {
|
||||
LapTimer timer(kWarmupRuns, kTimeLimit, kTimeCheckInterval);
|
||||
size_t data = 0;
|
||||
|
||||
subtle::SpinLock lock;
|
||||
|
||||
// Starts a competing thread executing the same loop as this thread.
|
||||
Spin thread_main(&lock, &data);
|
||||
PlatformThreadHandle thread_handle;
|
||||
ASSERT_TRUE(PlatformThread::Create(0, &thread_main, &thread_handle));
|
||||
|
||||
do {
|
||||
lock.lock();
|
||||
data += 1;
|
||||
lock.unlock();
|
||||
timer.NextLap();
|
||||
} while (!timer.HasTimeLimitExpired());
|
||||
|
||||
thread_main.Stop();
|
||||
PlatformThread::Join(thread_handle);
|
||||
|
||||
auto reporter = SetUpReporter(kStoryWithCompetingThread);
|
||||
reporter.AddResult(kMetricLockUnlockThroughput, timer.LapsPerSecond());
|
||||
}
|
||||
|
||||
} // namespace base
|
|
@ -1,155 +0,0 @@
|
|||
// Copyright 2019 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "base/android/orderfile/orderfile_instrumentation.h"
|
||||
|
||||
#include <thread>
|
||||
|
||||
#include "base/android/library_loader/anchor_functions.h"
|
||||
#include "base/strings/stringprintf.h"
|
||||
#include "base/time/time.h"
|
||||
#include "testing/gtest/include/gtest/gtest.h"
|
||||
#include "testing/perf/perf_test.h"
|
||||
|
||||
namespace base {
|
||||
namespace android {
|
||||
namespace orderfile {
|
||||
|
||||
namespace {
|
||||
const size_t kStep = sizeof(int);
|
||||
|
||||
void CallRecordAddress(int iterations, size_t addr_count) {
|
||||
for (int i = 0; i < iterations; i++) {
|
||||
for (size_t caller_addr = kStartOfTextForTesting + kStep;
|
||||
caller_addr < addr_count; caller_addr += kStep) {
|
||||
for (size_t callee_addr = caller_addr + kStep; callee_addr < addr_count;
|
||||
callee_addr += kStep) {
|
||||
RecordAddressForTesting(callee_addr, caller_addr);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void RunBenchmark(size_t iterations, size_t addresses_count, int threads) {
|
||||
ResetForTesting();
|
||||
auto iterate = [iterations, addresses_count]() {
|
||||
CallRecordAddress(iterations, addresses_count);
|
||||
};
|
||||
if (threads != 1) {
|
||||
for (int i = 0; i < threads - 1; ++i)
|
||||
std::thread(iterate).detach();
|
||||
}
|
||||
auto tick = base::TimeTicks::Now();
|
||||
iterate();
|
||||
auto tock = base::TimeTicks::Now();
|
||||
double nanos = static_cast<double>((tock - tick).InNanoseconds());
|
||||
size_t addresses = (addresses_count - kStartOfTextForTesting - 1) / kStep;
|
||||
double calls_count = (addresses * (addresses - 1)) / 2;
|
||||
auto ns_per_call = nanos / (iterations * calls_count);
|
||||
auto modifier =
|
||||
base::StringPrintf("_%zu_%zu_%d", iterations, addresses_count, threads);
|
||||
perf_test::PrintResult("RecordAddressCostPerCall", modifier, "", ns_per_call,
|
||||
"ns", true);
|
||||
}
|
||||
|
||||
void CheckValid(size_t iterations, size_t addr_count) {
|
||||
// |reached| is expected to be ordered by callee offset
|
||||
auto reached = GetOrderedOffsetsForTesting();
|
||||
size_t buckets_per_callee = 9; // kTotalBuckets * 2 + 1.
|
||||
size_t callers_per_callee = 3;
|
||||
size_t addresses = (addr_count - kStartOfTextForTesting - 1) / kStep;
|
||||
EXPECT_EQ((addresses - 1) * buckets_per_callee, reached.size());
|
||||
size_t expected_callee = kStartOfTextForTesting + 2 * kStep;
|
||||
|
||||
for (size_t i = 0; i < reached.size(); i += buckets_per_callee) {
|
||||
EXPECT_EQ(reached[i] / 4, (expected_callee - kStartOfTextForTesting) / 4);
|
||||
size_t callee_index = i / buckets_per_callee;
|
||||
for (size_t j = 0; j < callers_per_callee; j++) {
|
||||
EXPECT_EQ(reached[i + j * 2 + 1],
|
||||
j > callee_index ? 0UL : (j + 1) * kStep);
|
||||
EXPECT_EQ(reached[i + j * 2 + 2], j > callee_index ? 0UL : iterations);
|
||||
}
|
||||
size_t misses = callee_index > 2 ? (callee_index - 2) * iterations : 0UL;
|
||||
EXPECT_EQ(reached[i + 7], 0UL);
|
||||
EXPECT_EQ(reached[i + 8], misses);
|
||||
expected_callee += kStep;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
class OrderfileInstrumentationTest : public ::testing::Test {
|
||||
// Any tests need to run ResetForTesting() when they start. Because this
|
||||
// perftest is built with instrumentation enabled, all code including
|
||||
// ::testing::Test is instrumented. If ResetForTesting() is called earlier,
|
||||
// for example in setUp(), any test harness code between setUp() and the
|
||||
// actual test will change the instrumentation offset record in unpredictable
|
||||
// ways and make these tests unreliable.
|
||||
};
|
||||
|
||||
TEST_F(OrderfileInstrumentationTest, SequentialTest_10_5000) {
|
||||
size_t iterations = 10;
|
||||
size_t addr_count = 5000;
|
||||
ResetForTesting();
|
||||
CallRecordAddress(iterations, addr_count);
|
||||
Disable();
|
||||
CheckValid(iterations, addr_count);
|
||||
}
|
||||
|
||||
TEST_F(OrderfileInstrumentationTest, SequentialTest_10_10000) {
|
||||
size_t iterations = 10;
|
||||
size_t addr_count = 10000;
|
||||
ResetForTesting();
|
||||
CallRecordAddress(iterations, addr_count);
|
||||
Disable();
|
||||
CheckValid(iterations, addr_count);
|
||||
}
|
||||
|
||||
TEST_F(OrderfileInstrumentationTest, OutOfBoundsCaller) {
|
||||
ResetForTesting();
|
||||
RecordAddressForTesting(1234, kStartOfTextForTesting);
|
||||
RecordAddressForTesting(1234, kEndOfTextForTesting + 1);
|
||||
Disable();
|
||||
auto reached = GetOrderedOffsetsForTesting();
|
||||
EXPECT_EQ(reached.size(), 9UL);
|
||||
EXPECT_EQ(reached[0] / 4, (1234 - kStartOfTextForTesting) / 4);
|
||||
for (size_t i = 1; i < 8; i++) {
|
||||
EXPECT_EQ(reached[i], 0UL);
|
||||
}
|
||||
EXPECT_EQ(reached[8], 2UL);
|
||||
}
|
||||
|
||||
TEST(OrderfileInstrumentationPerfTest, RecordAddress_10_2000) {
|
||||
RunBenchmark(10, 2000, 1);
|
||||
}
|
||||
|
||||
TEST(OrderfileInstrumentationPerfTest, RecordAddress_100_2000) {
|
||||
RunBenchmark(100, 2000, 1);
|
||||
}
|
||||
|
||||
TEST(OrderfileInstrumentationPerfTest, RecordAddress_1000_2000_2) {
|
||||
RunBenchmark(100, 2000, 2);
|
||||
}
|
||||
|
||||
TEST(OrderfileInstrumentationPerfTest, RecordAddress_1000_2000_3) {
|
||||
RunBenchmark(100, 2000, 3);
|
||||
}
|
||||
|
||||
TEST(OrderfileInstrumentationPerfTest, RecordAddress_1000_2000_4) {
|
||||
RunBenchmark(100, 2000, 4);
|
||||
}
|
||||
|
||||
TEST(OrderfileInstrumentationPerfTest, RecordAddress_1000_2000_6) {
|
||||
RunBenchmark(100, 2000, 6);
|
||||
}
|
||||
|
||||
} // namespace orderfile
|
||||
} // namespace android
|
||||
} // namespace base
|
||||
|
||||
// Custom runner implementation since base's one requires JNI on Android.
|
||||
int main(int argc, char** argv) {
|
||||
testing::InitGoogleTest(&argc, argv);
|
||||
return RUN_ALL_TESTS();
|
||||
}
|
|
@ -1,135 +0,0 @@
|
|||
// Copyright 2017 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "base/android/orderfile/orderfile_instrumentation.h"
|
||||
|
||||
#include <thread>
|
||||
|
||||
#include "base/android/library_loader/anchor_functions.h"
|
||||
#include "base/strings/stringprintf.h"
|
||||
#include "base/time/time.h"
|
||||
#include "testing/gtest/include/gtest/gtest.h"
|
||||
#include "testing/perf/perf_test.h"
|
||||
|
||||
namespace base {
|
||||
namespace android {
|
||||
namespace orderfile {
|
||||
|
||||
namespace {
|
||||
|
||||
// Records |addresses_count| distinct addresses |iterations| times, in
|
||||
// |threads|.
|
||||
void RunBenchmark(int iterations, int addresses_count, int threads) {
|
||||
ResetForTesting();
|
||||
auto iterate = [iterations, addresses_count]() {
|
||||
for (int i = 0; i < iterations; i++) {
|
||||
for (size_t addr = kStartOfTextForTesting;
|
||||
addr < static_cast<size_t>(addresses_count); addr += sizeof(int)) {
|
||||
RecordAddressForTesting(addr);
|
||||
}
|
||||
}
|
||||
};
|
||||
if (threads != 1) {
|
||||
for (int i = 0; i < threads - 1; ++i)
|
||||
std::thread(iterate).detach();
|
||||
}
|
||||
auto tick = base::TimeTicks::Now();
|
||||
iterate();
|
||||
auto tock = base::TimeTicks::Now();
|
||||
double nanos = static_cast<double>((tock - tick).InNanoseconds());
|
||||
auto ns_per_call =
|
||||
nanos / (iterations * static_cast<double>(addresses_count));
|
||||
auto modifier =
|
||||
base::StringPrintf("_%d_%d_%d", iterations, addresses_count, threads);
|
||||
perf_test::PrintResult("RecordAddressCostPerCall", modifier, "", ns_per_call,
|
||||
"ns", true);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
class OrderfileInstrumentationTest : public ::testing::Test {
|
||||
// Any tests need to run ResetForTesting() when they start. Because this
|
||||
// perftest is built with instrumentation enabled, all code including
|
||||
// ::testing::Test is instrumented. If ResetForTesting() is called earlier,
|
||||
// for example in setUp(), any test harness code between setUp() and the
|
||||
// actual test will change the instrumentation offset record in unpredictable
|
||||
// ways and make these tests unreliable.
|
||||
};
|
||||
|
||||
TEST_F(OrderfileInstrumentationTest, RecordOffset) {
|
||||
ResetForTesting();
|
||||
size_t first = 1234, second = 1456;
|
||||
RecordAddressForTesting(first);
|
||||
RecordAddressForTesting(second);
|
||||
RecordAddressForTesting(first); // No duplicates.
|
||||
RecordAddressForTesting(first + 1); // 4 bytes granularity.
|
||||
Disable();
|
||||
|
||||
auto reached = GetOrderedOffsetsForTesting();
|
||||
EXPECT_EQ(2UL, reached.size());
|
||||
EXPECT_EQ(first - kStartOfTextForTesting, reached[0]);
|
||||
EXPECT_EQ(second - kStartOfTextForTesting, reached[1]);
|
||||
}
|
||||
|
||||
TEST_F(OrderfileInstrumentationTest, RecordingStops) {
|
||||
ResetForTesting();
|
||||
size_t first = 1234, second = 1456, third = 1789;
|
||||
RecordAddressForTesting(first);
|
||||
RecordAddressForTesting(second);
|
||||
Disable();
|
||||
RecordAddressForTesting(third);
|
||||
|
||||
auto reached = GetOrderedOffsetsForTesting();
|
||||
ASSERT_EQ(2UL, reached.size());
|
||||
ASSERT_EQ(first - kStartOfTextForTesting, reached[0]);
|
||||
ASSERT_EQ(second - kStartOfTextForTesting, reached[1]);
|
||||
}
|
||||
|
||||
TEST_F(OrderfileInstrumentationTest, OutOfBounds) {
|
||||
ResetForTesting();
|
||||
EXPECT_DEATH(RecordAddressForTesting(kEndOfTextForTesting + 100), "");
|
||||
EXPECT_DEATH(RecordAddressForTesting(kStartOfTextForTesting - 100), "");
|
||||
}
|
||||
|
||||
TEST(OrderfileInstrumentationPerfTest, RecordAddress_10_10000) {
|
||||
RunBenchmark(10, 10000, 1);
|
||||
}
|
||||
|
||||
TEST(OrderfileInstrumentationPerfTest, RecordAddress_100_10000) {
|
||||
RunBenchmark(100, 10000, 1);
|
||||
}
|
||||
|
||||
TEST(OrderfileInstrumentationPerfTest, RecordAddress_10_100000) {
|
||||
RunBenchmark(10, 100000, 1);
|
||||
}
|
||||
|
||||
TEST(OrderfileInstrumentationPerfTest, RecordAddress_100_100000) {
|
||||
RunBenchmark(100, 100000, 1);
|
||||
}
|
||||
|
||||
TEST(OrderfileInstrumentationPerfTest, RecordAddress_1000_100000_2) {
|
||||
RunBenchmark(1000, 100000, 2);
|
||||
}
|
||||
|
||||
TEST(OrderfileInstrumentationPerfTest, RecordAddress_1000_100000_3) {
|
||||
RunBenchmark(1000, 100000, 3);
|
||||
}
|
||||
|
||||
TEST(OrderfileInstrumentationPerfTest, RecordAddress_1000_100000_4) {
|
||||
RunBenchmark(1000, 100000, 4);
|
||||
}
|
||||
|
||||
TEST(OrderfileInstrumentationPerfTest, RecordAddress_1000_100000_6) {
|
||||
RunBenchmark(1000, 100000, 6);
|
||||
}
|
||||
|
||||
} // namespace orderfile
|
||||
} // namespace android
|
||||
} // namespace base
|
||||
|
||||
// Custom runner implementation since base's one requires JNI on Android.
|
||||
int main(int argc, char** argv) {
|
||||
testing::InitGoogleTest(&argc, argv);
|
||||
return RUN_ALL_TESTS();
|
||||
}
|
|
@ -1,88 +0,0 @@
|
|||
// Copyright (c) 2019 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "base/hash/sha1.h"
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <algorithm>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "base/rand_util.h"
|
||||
#include "base/strings/string_number_conversions.h"
|
||||
#include "base/time/time.h"
|
||||
#include "testing/gtest/include/gtest/gtest.h"
|
||||
#include "testing/perf/perf_result_reporter.h"
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr int kBytesPerMegabyte = 1000000;
|
||||
|
||||
constexpr char kMetricPrefixSHA1[] = "SHA1.";
|
||||
constexpr char kMetricRuntime[] = "runtime";
|
||||
constexpr char kMetricThroughput[] = "throughput";
|
||||
// Histograms automatically calculate mean, min, max, and standard deviation,
|
||||
// but not median, so have a separate metric for our manually calculated median.
|
||||
constexpr char kMetricMedianThroughput[] = "median_throughput";
|
||||
|
||||
perf_test::PerfResultReporter SetUpReporter(const std::string& story_name) {
|
||||
perf_test::PerfResultReporter reporter(kMetricPrefixSHA1, story_name);
|
||||
reporter.RegisterImportantMetric(kMetricRuntime, "us");
|
||||
reporter.RegisterImportantMetric(kMetricThroughput, "bytesPerSecond");
|
||||
reporter.RegisterImportantMetric(kMetricMedianThroughput, "bytesPerSecond");
|
||||
return reporter;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
static void Timing(const size_t len) {
|
||||
std::vector<uint8_t> buf(len);
|
||||
base::RandBytes(buf.data(), len);
|
||||
|
||||
const int runs = 111;
|
||||
std::vector<base::TimeDelta> utime(runs);
|
||||
unsigned char digest[base::kSHA1Length];
|
||||
memset(digest, 0, base::kSHA1Length);
|
||||
|
||||
double total_test_time = 0.0;
|
||||
for (int i = 0; i < runs; ++i) {
|
||||
auto start = base::TimeTicks::Now();
|
||||
base::SHA1HashBytes(buf.data(), len, digest);
|
||||
auto end = base::TimeTicks::Now();
|
||||
utime[i] = end - start;
|
||||
total_test_time += utime[i].InMicroseconds();
|
||||
}
|
||||
|
||||
std::sort(utime.begin(), utime.end());
|
||||
const int med = runs / 2;
|
||||
|
||||
// Simply dividing len by utime gets us MB/s, but we need B/s.
|
||||
// MB/s = (len / (bytes/megabytes)) / (usecs / usecs/sec)
|
||||
// MB/s = (len / 1,000,000)/(usecs / 1,000,000)
|
||||
// MB/s = (len * 1,000,000)/(usecs * 1,000,000)
|
||||
// MB/s = len/utime
|
||||
double median_rate = kBytesPerMegabyte * len / utime[med].InMicroseconds();
|
||||
// Convert to a comma-separated string so we can report every data point.
|
||||
std::string rates;
|
||||
for (const auto& t : utime) {
|
||||
rates +=
|
||||
base::NumberToString(kBytesPerMegabyte * len / t.InMicroseconds()) +
|
||||
",";
|
||||
}
|
||||
// Strip off trailing comma.
|
||||
rates.pop_back();
|
||||
|
||||
auto reporter = SetUpReporter(base::NumberToString(len) + "_bytes");
|
||||
reporter.AddResult(kMetricRuntime, total_test_time);
|
||||
reporter.AddResult(kMetricMedianThroughput, median_rate);
|
||||
reporter.AddResultList(kMetricThroughput, rates);
|
||||
}
|
||||
|
||||
TEST(SHA1PerfTest, Speed) {
|
||||
Timing(1024 * 1024U >> 1);
|
||||
Timing(1024 * 1024U >> 5);
|
||||
Timing(1024 * 1024U >> 6);
|
||||
Timing(1024 * 1024U >> 7);
|
||||
}
|
|
@ -1,232 +0,0 @@
|
|||
// Copyright 2014 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
// All data that is passed through a WebSocket with type "Text" needs to be
|
||||
// validated as UTF8. Since this is done on the IO thread, it needs to be
|
||||
// reasonably fast.
|
||||
|
||||
// We are only interested in the performance on valid UTF8. Invalid UTF8 will
|
||||
// result in a connection failure, so is unlikely to become a source of
|
||||
// performance issues.
|
||||
|
||||
#include "base/i18n/streaming_utf8_validator.h"
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "base/bind.h"
|
||||
#include "base/callback.h"
|
||||
#include "base/macros.h"
|
||||
#include "base/strings/string_util.h"
|
||||
#include "base/strings/stringprintf.h"
|
||||
#include "base/test/perf_time_logger.h"
|
||||
#include "testing/gtest/include/gtest/gtest.h"
|
||||
|
||||
namespace base {
|
||||
namespace {
|
||||
|
||||
// We want to test ranges of valid UTF-8 sequences. These ranges are inclusive.
|
||||
// They are intended to be large enough that the validator needs to do
|
||||
// meaningful work while being in some sense "realistic" (eg. control characters
|
||||
// are not included).
|
||||
const char kOneByteSeqRangeStart[] = " "; // U+0020
|
||||
const char kOneByteSeqRangeEnd[] = "~"; // U+007E
|
||||
|
||||
const char kTwoByteSeqRangeStart[] = "\xc2\xa0"; // U+00A0 non-breaking space
|
||||
const char kTwoByteSeqRangeEnd[] = "\xc9\x8f"; // U+024F small y with stroke
|
||||
|
||||
const char kThreeByteSeqRangeStart[] = "\xe3\x81\x82"; // U+3042 Hiragana "a"
|
||||
const char kThreeByteSeqRangeEnd[] = "\xe9\xbf\x83"; // U+9FC3 "to blink"
|
||||
|
||||
const char kFourByteSeqRangeStart[] = "\xf0\xa0\x80\x8b"; // U+2000B
|
||||
const char kFourByteSeqRangeEnd[] = "\xf0\xaa\x9a\xb2"; // U+2A6B2
|
||||
|
||||
// The different lengths of strings to test.
|
||||
const size_t kTestLengths[] = {1, 32, 256, 32768, 1 << 20};
|
||||
|
||||
// Simplest possible byte-at-a-time validator, to provide a baseline
|
||||
// for comparison. This is only tried on 1-byte UTF-8 sequences, as
|
||||
// the results will not be meaningful with sequences containing
|
||||
// top-bit-set bytes.
|
||||
bool IsString7Bit(const std::string& s) {
|
||||
for (auto it : s) {
|
||||
if (it & 0x80)
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Assumes that |previous| is a valid UTF-8 sequence, and attempts to return
|
||||
// the next one. Is just barely smart enough to iterate through the ranges
|
||||
// defined about.
|
||||
std::string NextUtf8Sequence(const std::string& previous) {
|
||||
DCHECK(StreamingUtf8Validator::Validate(previous));
|
||||
std::string next = previous;
|
||||
for (int i = static_cast<int>(previous.length() - 1); i >= 0; --i) {
|
||||
// All bytes in a UTF-8 sequence except the first one are
|
||||
// constrained to the range 0x80 to 0xbf, inclusive. When we
|
||||
// increment past 0xbf, we carry into the previous byte.
|
||||
if (i > 0 && next[i] == '\xbf') {
|
||||
next[i] = '\x80';
|
||||
continue; // carry
|
||||
}
|
||||
++next[i];
|
||||
break; // no carry
|
||||
}
|
||||
DCHECK(StreamingUtf8Validator::Validate(next))
|
||||
<< "Result \"" << next << "\" failed validation";
|
||||
return next;
|
||||
}
|
||||
|
||||
typedef bool (*TestTargetType)(const std::string&);
|
||||
|
||||
// Run fuction |target| over |test_string| |times| times, and report the results
|
||||
// using |description|.
|
||||
bool RunTest(const std::string& description,
|
||||
TestTargetType target,
|
||||
const std::string& test_string,
|
||||
int times) {
|
||||
base::PerfTimeLogger timer(description.c_str());
|
||||
bool result = true;
|
||||
for (int i = 0; i < times; ++i) {
|
||||
result = target(test_string) && result;
|
||||
}
|
||||
timer.Done();
|
||||
return result;
|
||||
}
|
||||
|
||||
// Construct a string by repeating |input| enough times to equal or exceed
|
||||
// |length|.
|
||||
std::string ConstructRepeatedTestString(const std::string& input,
|
||||
size_t length) {
|
||||
std::string output = input;
|
||||
while (output.length() * 2 < length) {
|
||||
output += output;
|
||||
}
|
||||
if (output.length() < length) {
|
||||
output += ConstructRepeatedTestString(input, length - output.length());
|
||||
}
|
||||
return output;
|
||||
}
|
||||
|
||||
// Construct a string by expanding the range of UTF-8 sequences
|
||||
// between |input_start| and |input_end|, inclusive, and then
|
||||
// repeating the resulting string until it equals or exceeds |length|
|
||||
// bytes. |input_start| and |input_end| must be valid UTF-8
|
||||
// sequences.
|
||||
std::string ConstructRangedTestString(const std::string& input_start,
|
||||
const std::string& input_end,
|
||||
size_t length) {
|
||||
std::string output = input_start;
|
||||
std::string input = input_start;
|
||||
while (output.length() < length && input != input_end) {
|
||||
input = NextUtf8Sequence(input);
|
||||
output += input;
|
||||
}
|
||||
if (output.length() < length) {
|
||||
output = ConstructRepeatedTestString(output, length);
|
||||
}
|
||||
return output;
|
||||
}
|
||||
|
||||
struct TestFunctionDescription {
|
||||
TestTargetType function;
|
||||
const char* function_name;
|
||||
};
|
||||
|
||||
bool IsStringUTF8(const std::string& str) {
|
||||
return base::IsStringUTF8(base::StringPiece(str));
|
||||
}
|
||||
|
||||
// IsString7Bit is intentionally placed last so it can be excluded easily.
|
||||
const TestFunctionDescription kTestFunctions[] = {
|
||||
{&StreamingUtf8Validator::Validate, "StreamingUtf8Validator"},
|
||||
{&IsStringUTF8, "IsStringUTF8"}, {&IsString7Bit, "IsString7Bit"}};
|
||||
|
||||
// Construct a test string from |construct_test_string| for each of the lengths
|
||||
// in |kTestLengths| in turn. For each string, run each test in |test_functions|
|
||||
// for a number of iterations such that the total number of bytes validated
|
||||
// is around 16MB.
|
||||
void RunSomeTests(
|
||||
const char format[],
|
||||
base::RepeatingCallback<std::string(size_t length)> construct_test_string,
|
||||
const TestFunctionDescription* test_functions,
|
||||
size_t test_count) {
|
||||
for (auto length : kTestLengths) {
|
||||
const std::string test_string = construct_test_string.Run(length);
|
||||
const int real_length = static_cast<int>(test_string.length());
|
||||
const int times = (1 << 24) / real_length;
|
||||
for (size_t test_index = 0; test_index < test_count; ++test_index) {
|
||||
EXPECT_TRUE(RunTest(StringPrintf(format,
|
||||
test_functions[test_index].function_name,
|
||||
real_length,
|
||||
times),
|
||||
test_functions[test_index].function,
|
||||
test_string,
|
||||
times));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST(StreamingUtf8ValidatorPerfTest, OneByteRepeated) {
|
||||
RunSomeTests(
|
||||
"%s: bytes=1 repeated length=%d repeat=%d",
|
||||
base::BindRepeating(ConstructRepeatedTestString, kOneByteSeqRangeStart),
|
||||
kTestFunctions, 3);
|
||||
}
|
||||
|
||||
TEST(StreamingUtf8ValidatorPerfTest, OneByteRange) {
|
||||
RunSomeTests("%s: bytes=1 ranged length=%d repeat=%d",
|
||||
base::BindRepeating(ConstructRangedTestString,
|
||||
kOneByteSeqRangeStart, kOneByteSeqRangeEnd),
|
||||
kTestFunctions, 3);
|
||||
}
|
||||
|
||||
TEST(StreamingUtf8ValidatorPerfTest, TwoByteRepeated) {
|
||||
RunSomeTests(
|
||||
"%s: bytes=2 repeated length=%d repeat=%d",
|
||||
base::BindRepeating(ConstructRepeatedTestString, kTwoByteSeqRangeStart),
|
||||
kTestFunctions, 2);
|
||||
}
|
||||
|
||||
TEST(StreamingUtf8ValidatorPerfTest, TwoByteRange) {
|
||||
RunSomeTests("%s: bytes=2 ranged length=%d repeat=%d",
|
||||
base::BindRepeating(ConstructRangedTestString,
|
||||
kTwoByteSeqRangeStart, kTwoByteSeqRangeEnd),
|
||||
kTestFunctions, 2);
|
||||
}
|
||||
|
||||
TEST(StreamingUtf8ValidatorPerfTest, ThreeByteRepeated) {
|
||||
RunSomeTests(
|
||||
"%s: bytes=3 repeated length=%d repeat=%d",
|
||||
base::BindRepeating(ConstructRepeatedTestString, kThreeByteSeqRangeStart),
|
||||
kTestFunctions, 2);
|
||||
}
|
||||
|
||||
TEST(StreamingUtf8ValidatorPerfTest, ThreeByteRange) {
|
||||
RunSomeTests(
|
||||
"%s: bytes=3 ranged length=%d repeat=%d",
|
||||
base::BindRepeating(ConstructRangedTestString, kThreeByteSeqRangeStart,
|
||||
kThreeByteSeqRangeEnd),
|
||||
kTestFunctions, 2);
|
||||
}
|
||||
|
||||
TEST(StreamingUtf8ValidatorPerfTest, FourByteRepeated) {
|
||||
RunSomeTests(
|
||||
"%s: bytes=4 repeated length=%d repeat=%d",
|
||||
base::BindRepeating(ConstructRepeatedTestString, kFourByteSeqRangeStart),
|
||||
kTestFunctions, 2);
|
||||
}
|
||||
|
||||
TEST(StreamingUtf8ValidatorPerfTest, FourByteRange) {
|
||||
RunSomeTests(
|
||||
"%s: bytes=4 ranged length=%d repeat=%d",
|
||||
base::BindRepeating(ConstructRangedTestString, kFourByteSeqRangeStart,
|
||||
kFourByteSeqRangeEnd),
|
||||
kTestFunctions, 2);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace base
|
|
@ -1,102 +0,0 @@
|
|||
// Copyright 2017 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "base/json/json_reader.h"
|
||||
#include "base/json/json_writer.h"
|
||||
#include "base/memory/ptr_util.h"
|
||||
#include "base/strings/string_number_conversions.h"
|
||||
#include "base/time/time.h"
|
||||
#include "base/values.h"
|
||||
#include "build/build_config.h"
|
||||
#include "testing/gtest/include/gtest/gtest.h"
|
||||
#include "testing/perf/perf_result_reporter.h"
|
||||
|
||||
namespace base {
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr char kMetricPrefixJSON[] = "JSON.";
|
||||
constexpr char kMetricReadTime[] = "read_time";
|
||||
constexpr char kMetricWriteTime[] = "write_time";
|
||||
|
||||
perf_test::PerfResultReporter SetUpReporter(const std::string& story_name) {
|
||||
perf_test::PerfResultReporter reporter(kMetricPrefixJSON, story_name);
|
||||
reporter.RegisterImportantMetric(kMetricReadTime, "ms");
|
||||
reporter.RegisterImportantMetric(kMetricWriteTime, "ms");
|
||||
return reporter;
|
||||
}
|
||||
|
||||
// Generates a simple dictionary value with simple data types, a string and a
|
||||
// list.
|
||||
DictionaryValue GenerateDict() {
|
||||
DictionaryValue root;
|
||||
root.SetDoubleKey("Double", 3.141);
|
||||
root.SetBoolKey("Bool", true);
|
||||
root.SetIntKey("Int", 42);
|
||||
root.SetStringKey("String", "Foo");
|
||||
|
||||
ListValue list;
|
||||
list.Append(2.718);
|
||||
list.Append(false);
|
||||
list.Append(123);
|
||||
list.Append("Bar");
|
||||
root.SetKey("List", std::move(list));
|
||||
|
||||
return root;
|
||||
}
|
||||
|
||||
// Generates a tree-like dictionary value with a size of O(breadth ** depth).
|
||||
DictionaryValue GenerateLayeredDict(int breadth, int depth) {
|
||||
if (depth == 1)
|
||||
return GenerateDict();
|
||||
|
||||
DictionaryValue root = GenerateDict();
|
||||
DictionaryValue next = GenerateLayeredDict(breadth, depth - 1);
|
||||
|
||||
for (int i = 0; i < breadth; ++i) {
|
||||
root.SetKey("Dict" + base::NumberToString(i), next.Clone());
|
||||
}
|
||||
|
||||
return root;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
class JSONPerfTest : public testing::Test {
|
||||
public:
|
||||
void TestWriteAndRead(int breadth, int depth) {
|
||||
std::string description = "Breadth: " + base::NumberToString(breadth) +
|
||||
", Depth: " + base::NumberToString(depth);
|
||||
DictionaryValue dict = GenerateLayeredDict(breadth, depth);
|
||||
std::string json;
|
||||
|
||||
TimeTicks start_write = TimeTicks::Now();
|
||||
JSONWriter::Write(dict, &json);
|
||||
TimeTicks end_write = TimeTicks::Now();
|
||||
auto reporter = SetUpReporter("breadth_" + base::NumberToString(breadth) +
|
||||
"_depth_" + base::NumberToString(depth));
|
||||
reporter.AddResult(kMetricWriteTime, end_write - start_write);
|
||||
|
||||
TimeTicks start_read = TimeTicks::Now();
|
||||
JSONReader::Read(json);
|
||||
TimeTicks end_read = TimeTicks::Now();
|
||||
reporter.AddResult(kMetricReadTime, end_read - start_read);
|
||||
}
|
||||
};
|
||||
|
||||
// Times out on Android (crbug.com/906686).
|
||||
#if defined(OS_ANDROID)
|
||||
#define MAYBE_StressTest DISABLED_StressTest
|
||||
#else
|
||||
#define MAYBE_StressTest StressTest
|
||||
#endif
|
||||
TEST_F(JSONPerfTest, MAYBE_StressTest) {
|
||||
for (int i = 0; i < 4; ++i) {
|
||||
for (int j = 0; j < 12; ++j) {
|
||||
TestWriteAndRead(i + 1, j + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace base
|
|
@ -1,262 +0,0 @@
|
|||
// Copyright 2014 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "base/bind.h"
|
||||
#include "base/bind_helpers.h"
|
||||
#include "base/format_macros.h"
|
||||
#include "base/memory/ptr_util.h"
|
||||
#include "base/message_loop/message_loop_current.h"
|
||||
#include "base/message_loop/message_pump_type.h"
|
||||
#include "base/single_thread_task_runner.h"
|
||||
#include "base/strings/stringprintf.h"
|
||||
#include "base/synchronization/condition_variable.h"
|
||||
#include "base/synchronization/lock.h"
|
||||
#include "base/synchronization/waitable_event.h"
|
||||
#include "base/task/sequence_manager/sequence_manager_impl.h"
|
||||
#include "base/threading/thread.h"
|
||||
#include "base/time/time.h"
|
||||
#include "build/build_config.h"
|
||||
#include "testing/gtest/include/gtest/gtest.h"
|
||||
#include "testing/perf/perf_result_reporter.h"
|
||||
|
||||
#if defined(OS_ANDROID)
|
||||
#include "base/android/java_handler_thread.h"
|
||||
#endif
|
||||
|
||||
namespace base {
|
||||
namespace {
|
||||
|
||||
constexpr char kMetricPrefixScheduleWork[] = "ScheduleWork.";
|
||||
constexpr char kMetricMinBatchTime[] = "min_batch_time_per_task";
|
||||
constexpr char kMetricMaxBatchTime[] = "max_batch_time_per_task";
|
||||
constexpr char kMetricTotalTime[] = "total_time_per_task";
|
||||
constexpr char kMetricThreadTime[] = "thread_time_per_task";
|
||||
|
||||
perf_test::PerfResultReporter SetUpReporter(const std::string& story_name) {
|
||||
perf_test::PerfResultReporter reporter(kMetricPrefixScheduleWork, story_name);
|
||||
reporter.RegisterImportantMetric(kMetricMinBatchTime, "us");
|
||||
reporter.RegisterImportantMetric(kMetricMaxBatchTime, "us");
|
||||
reporter.RegisterImportantMetric(kMetricTotalTime, "us");
|
||||
reporter.RegisterImportantMetric(kMetricThreadTime, "us");
|
||||
return reporter;
|
||||
}
|
||||
|
||||
#if defined(OS_ANDROID)
|
||||
class JavaHandlerThreadForTest : public android::JavaHandlerThread {
|
||||
public:
|
||||
explicit JavaHandlerThreadForTest(const char* name)
|
||||
: android::JavaHandlerThread(name, base::ThreadPriority::NORMAL) {}
|
||||
|
||||
using android::JavaHandlerThread::state;
|
||||
using android::JavaHandlerThread::State;
|
||||
};
|
||||
#endif
|
||||
|
||||
} // namespace
|
||||
|
||||
class ScheduleWorkTest : public testing::Test {
|
||||
public:
|
||||
ScheduleWorkTest() : counter_(0) {}
|
||||
|
||||
void SetUp() override {
|
||||
if (base::ThreadTicks::IsSupported())
|
||||
base::ThreadTicks::WaitUntilInitialized();
|
||||
}
|
||||
|
||||
void Increment(uint64_t amount) { counter_ += amount; }
|
||||
|
||||
void Schedule(int index) {
|
||||
base::TimeTicks start = base::TimeTicks::Now();
|
||||
base::ThreadTicks thread_start;
|
||||
if (ThreadTicks::IsSupported())
|
||||
thread_start = base::ThreadTicks::Now();
|
||||
base::TimeDelta minimum = base::TimeDelta::Max();
|
||||
base::TimeDelta maximum = base::TimeDelta();
|
||||
base::TimeTicks now, lastnow = start;
|
||||
uint64_t schedule_calls = 0u;
|
||||
do {
|
||||
for (size_t i = 0; i < kBatchSize; ++i) {
|
||||
target_message_loop_base()->GetMessagePump()->ScheduleWork();
|
||||
schedule_calls++;
|
||||
}
|
||||
now = base::TimeTicks::Now();
|
||||
base::TimeDelta laptime = now - lastnow;
|
||||
lastnow = now;
|
||||
minimum = std::min(minimum, laptime);
|
||||
maximum = std::max(maximum, laptime);
|
||||
} while (now - start < base::TimeDelta::FromSeconds(kTargetTimeSec));
|
||||
|
||||
scheduling_times_[index] = now - start;
|
||||
if (ThreadTicks::IsSupported())
|
||||
scheduling_thread_times_[index] =
|
||||
base::ThreadTicks::Now() - thread_start;
|
||||
min_batch_times_[index] = minimum;
|
||||
max_batch_times_[index] = maximum;
|
||||
target_message_loop_base()->GetTaskRunner()->PostTask(
|
||||
FROM_HERE, base::BindOnce(&ScheduleWorkTest::Increment,
|
||||
base::Unretained(this), schedule_calls));
|
||||
}
|
||||
|
||||
void ScheduleWork(MessagePumpType target_type, int num_scheduling_threads) {
|
||||
#if defined(OS_ANDROID)
|
||||
if (target_type == MessagePumpType::JAVA) {
|
||||
java_thread_.reset(new JavaHandlerThreadForTest("target"));
|
||||
java_thread_->Start();
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
target_.reset(new Thread("test"));
|
||||
|
||||
Thread::Options options(target_type, 0u);
|
||||
options.message_pump_type = target_type;
|
||||
target_->StartWithOptions(options);
|
||||
|
||||
// Without this, it's possible for the scheduling threads to start and run
|
||||
// before the target thread. In this case, the scheduling threads will
|
||||
// call target_message_loop()->ScheduleWork(), which dereferences the
|
||||
// loop's message pump, which is only created after the target thread has
|
||||
// finished starting.
|
||||
target_->WaitUntilThreadStarted();
|
||||
}
|
||||
|
||||
std::vector<std::unique_ptr<Thread>> scheduling_threads;
|
||||
scheduling_times_.reset(new base::TimeDelta[num_scheduling_threads]);
|
||||
scheduling_thread_times_.reset(new base::TimeDelta[num_scheduling_threads]);
|
||||
min_batch_times_.reset(new base::TimeDelta[num_scheduling_threads]);
|
||||
max_batch_times_.reset(new base::TimeDelta[num_scheduling_threads]);
|
||||
|
||||
for (int i = 0; i < num_scheduling_threads; ++i) {
|
||||
scheduling_threads.push_back(std::make_unique<Thread>("posting thread"));
|
||||
scheduling_threads[i]->Start();
|
||||
}
|
||||
|
||||
for (int i = 0; i < num_scheduling_threads; ++i) {
|
||||
scheduling_threads[i]->task_runner()->PostTask(
|
||||
FROM_HERE, base::BindOnce(&ScheduleWorkTest::Schedule,
|
||||
base::Unretained(this), i));
|
||||
}
|
||||
|
||||
for (int i = 0; i < num_scheduling_threads; ++i) {
|
||||
scheduling_threads[i]->Stop();
|
||||
}
|
||||
#if defined(OS_ANDROID)
|
||||
if (target_type == MessagePumpType::JAVA) {
|
||||
java_thread_->Stop();
|
||||
java_thread_.reset();
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
target_->Stop();
|
||||
target_.reset();
|
||||
}
|
||||
base::TimeDelta total_time;
|
||||
base::TimeDelta total_thread_time;
|
||||
base::TimeDelta min_batch_time = base::TimeDelta::Max();
|
||||
base::TimeDelta max_batch_time = base::TimeDelta();
|
||||
for (int i = 0; i < num_scheduling_threads; ++i) {
|
||||
total_time += scheduling_times_[i];
|
||||
total_thread_time += scheduling_thread_times_[i];
|
||||
min_batch_time = std::min(min_batch_time, min_batch_times_[i]);
|
||||
max_batch_time = std::max(max_batch_time, max_batch_times_[i]);
|
||||
}
|
||||
|
||||
std::string story_name = StringPrintf(
|
||||
"%s_pump_from_%d_threads",
|
||||
target_type == MessagePumpType::IO
|
||||
? "io"
|
||||
: (target_type == MessagePumpType::UI ? "ui" : "default"),
|
||||
num_scheduling_threads);
|
||||
auto reporter = SetUpReporter(story_name);
|
||||
reporter.AddResult(kMetricMinBatchTime, total_time.InMicroseconds() /
|
||||
static_cast<double>(counter_));
|
||||
reporter.AddResult(
|
||||
kMetricMaxBatchTime,
|
||||
max_batch_time.InMicroseconds() / static_cast<double>(kBatchSize));
|
||||
reporter.AddResult(kMetricTotalTime, total_time.InMicroseconds() /
|
||||
static_cast<double>(counter_));
|
||||
if (ThreadTicks::IsSupported()) {
|
||||
reporter.AddResult(kMetricThreadTime, total_thread_time.InMicroseconds() /
|
||||
static_cast<double>(counter_));
|
||||
}
|
||||
}
|
||||
|
||||
sequence_manager::internal::SequenceManagerImpl* target_message_loop_base() {
|
||||
#if defined(OS_ANDROID)
|
||||
if (java_thread_) {
|
||||
return static_cast<sequence_manager::internal::SequenceManagerImpl*>(
|
||||
java_thread_->state()->sequence_manager.get());
|
||||
}
|
||||
#endif
|
||||
return MessageLoopCurrent::Get()->GetCurrentSequenceManagerImpl();
|
||||
}
|
||||
|
||||
private:
|
||||
std::unique_ptr<Thread> target_;
|
||||
#if defined(OS_ANDROID)
|
||||
std::unique_ptr<JavaHandlerThreadForTest> java_thread_;
|
||||
#endif
|
||||
std::unique_ptr<base::TimeDelta[]> scheduling_times_;
|
||||
std::unique_ptr<base::TimeDelta[]> scheduling_thread_times_;
|
||||
std::unique_ptr<base::TimeDelta[]> min_batch_times_;
|
||||
std::unique_ptr<base::TimeDelta[]> max_batch_times_;
|
||||
uint64_t counter_;
|
||||
|
||||
static const size_t kTargetTimeSec = 5;
|
||||
static const size_t kBatchSize = 1000;
|
||||
};
|
||||
|
||||
TEST_F(ScheduleWorkTest, ThreadTimeToIOFromOneThread) {
|
||||
ScheduleWork(MessagePumpType::IO, 1);
|
||||
}
|
||||
|
||||
TEST_F(ScheduleWorkTest, ThreadTimeToIOFromTwoThreads) {
|
||||
ScheduleWork(MessagePumpType::IO, 2);
|
||||
}
|
||||
|
||||
TEST_F(ScheduleWorkTest, ThreadTimeToIOFromFourThreads) {
|
||||
ScheduleWork(MessagePumpType::IO, 4);
|
||||
}
|
||||
|
||||
TEST_F(ScheduleWorkTest, ThreadTimeToUIFromOneThread) {
|
||||
ScheduleWork(MessagePumpType::UI, 1);
|
||||
}
|
||||
|
||||
TEST_F(ScheduleWorkTest, ThreadTimeToUIFromTwoThreads) {
|
||||
ScheduleWork(MessagePumpType::UI, 2);
|
||||
}
|
||||
|
||||
TEST_F(ScheduleWorkTest, ThreadTimeToUIFromFourThreads) {
|
||||
ScheduleWork(MessagePumpType::UI, 4);
|
||||
}
|
||||
|
||||
TEST_F(ScheduleWorkTest, ThreadTimeToDefaultFromOneThread) {
|
||||
ScheduleWork(MessagePumpType::DEFAULT, 1);
|
||||
}
|
||||
|
||||
TEST_F(ScheduleWorkTest, ThreadTimeToDefaultFromTwoThreads) {
|
||||
ScheduleWork(MessagePumpType::DEFAULT, 2);
|
||||
}
|
||||
|
||||
TEST_F(ScheduleWorkTest, ThreadTimeToDefaultFromFourThreads) {
|
||||
ScheduleWork(MessagePumpType::DEFAULT, 4);
|
||||
}
|
||||
|
||||
#if defined(OS_ANDROID)
|
||||
TEST_F(ScheduleWorkTest, ThreadTimeToJavaFromOneThread) {
|
||||
ScheduleWork(MessagePumpType::JAVA, 1);
|
||||
}
|
||||
|
||||
TEST_F(ScheduleWorkTest, ThreadTimeToJavaFromTwoThreads) {
|
||||
ScheduleWork(MessagePumpType::JAVA, 2);
|
||||
}
|
||||
|
||||
TEST_F(ScheduleWorkTest, ThreadTimeToJavaFromFourThreads) {
|
||||
ScheduleWork(MessagePumpType::JAVA, 4);
|
||||
}
|
||||
#endif
|
||||
|
||||
} // namespace base
|
|
@ -1,131 +0,0 @@
|
|||
// Copyright 2018 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "base/observer_list.h"
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "base/logging.h"
|
||||
#include "base/observer_list.h"
|
||||
#include "base/strings/stringprintf.h"
|
||||
#include "base/time/time.h"
|
||||
#include "testing/gtest/include/gtest/gtest.h"
|
||||
#include "testing/perf/perf_result_reporter.h"
|
||||
|
||||
// Ask the compiler not to use a register for this counter, in case it decides
|
||||
// to do magic optimizations like |counter += kLaps|.
|
||||
volatile int g_observer_list_perf_test_counter;
|
||||
|
||||
namespace base {
|
||||
|
||||
constexpr char kMetricPrefixObserverList[] = "ObserverList.";
|
||||
constexpr char kMetricNotifyTimePerObserver[] = "notify_time_per_observer";
|
||||
|
||||
namespace {
|
||||
|
||||
perf_test::PerfResultReporter SetUpReporter(const std::string& story_name) {
|
||||
perf_test::PerfResultReporter reporter(kMetricPrefixObserverList, story_name);
|
||||
reporter.RegisterImportantMetric(kMetricNotifyTimePerObserver, "ns");
|
||||
return reporter;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
class ObserverInterface {
|
||||
public:
|
||||
ObserverInterface() {}
|
||||
virtual ~ObserverInterface() {}
|
||||
virtual void Observe() const { ++g_observer_list_perf_test_counter; }
|
||||
|
||||
private:
|
||||
DISALLOW_COPY_AND_ASSIGN(ObserverInterface);
|
||||
};
|
||||
|
||||
class UnsafeObserver : public ObserverInterface {};
|
||||
|
||||
class TestCheckedObserver : public CheckedObserver, public ObserverInterface {};
|
||||
|
||||
template <class ObserverType>
|
||||
struct Pick {
|
||||
// The ObserverList type to use. Checked observers need to be in a checked
|
||||
// ObserverList.
|
||||
using ObserverListType = ObserverList<ObserverType>;
|
||||
static const char* GetName() { return "CheckedObserver"; }
|
||||
};
|
||||
template <>
|
||||
struct Pick<UnsafeObserver> {
|
||||
using ObserverListType = ObserverList<ObserverInterface>::Unchecked;
|
||||
static const char* GetName() { return "UnsafeObserver"; }
|
||||
};
|
||||
|
||||
template <class ObserverType>
|
||||
class ObserverListPerfTest : public ::testing::Test {
|
||||
public:
|
||||
using ObserverListType = typename Pick<ObserverType>::ObserverListType;
|
||||
|
||||
ObserverListPerfTest() {}
|
||||
|
||||
private:
|
||||
DISALLOW_COPY_AND_ASSIGN(ObserverListPerfTest);
|
||||
};
|
||||
|
||||
typedef ::testing::Types<UnsafeObserver, TestCheckedObserver> ObserverTypes;
|
||||
TYPED_TEST_SUITE(ObserverListPerfTest, ObserverTypes);
|
||||
|
||||
// Performance test for base::ObserverList and Checked Observers.
|
||||
TYPED_TEST(ObserverListPerfTest, NotifyPerformance) {
|
||||
constexpr int kMaxObservers = 128;
|
||||
#if DCHECK_IS_ON()
|
||||
// The test takes about 100x longer in debug builds, mostly due to sequence
|
||||
// checker overheads when WeakPtr gets involved.
|
||||
constexpr int kLaps = 1000000;
|
||||
#else
|
||||
constexpr int kLaps = 100000000;
|
||||
#endif
|
||||
constexpr int kWarmupLaps = 100;
|
||||
std::vector<std::unique_ptr<TypeParam>> observers;
|
||||
|
||||
for (int observer_count = 0; observer_count <= kMaxObservers;
|
||||
observer_count = observer_count ? observer_count * 2 : 1) {
|
||||
typename TestFixture::ObserverListType list;
|
||||
for (int i = 0; i < observer_count; ++i)
|
||||
observers.push_back(std::make_unique<TypeParam>());
|
||||
for (auto& o : observers)
|
||||
list.AddObserver(o.get());
|
||||
|
||||
for (int i = 0; i < kWarmupLaps; ++i) {
|
||||
for (auto& o : list)
|
||||
o.Observe();
|
||||
}
|
||||
g_observer_list_perf_test_counter = 0;
|
||||
const int weighted_laps = kLaps / (observer_count + 1);
|
||||
|
||||
TimeTicks start = TimeTicks::Now();
|
||||
for (int i = 0; i < weighted_laps; ++i) {
|
||||
for (auto& o : list)
|
||||
o.Observe();
|
||||
}
|
||||
TimeDelta duration = TimeTicks::Now() - start;
|
||||
|
||||
observers.clear();
|
||||
|
||||
EXPECT_EQ(observer_count * weighted_laps,
|
||||
g_observer_list_perf_test_counter);
|
||||
EXPECT_TRUE(observer_count == 0 || list.might_have_observers());
|
||||
|
||||
std::string story_name =
|
||||
base::StringPrintf("%s_%d", Pick<TypeParam>::GetName(), observer_count);
|
||||
|
||||
// A typical value is 3-20 nanoseconds per observe in Release, 1000-2000ns
|
||||
// in an optimized build with DCHECKs and 3000-6000ns in debug builds.
|
||||
auto reporter = SetUpReporter(story_name);
|
||||
reporter.AddResult(
|
||||
kMetricNotifyTimePerObserver,
|
||||
duration.InNanoseconds() /
|
||||
static_cast<double>(g_observer_list_perf_test_counter +
|
||||
weighted_laps));
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace base
|
|
@ -1,46 +0,0 @@
|
|||
// Copyright 2018 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "base/strings/string_util.h"
|
||||
|
||||
#include <cinttypes>
|
||||
|
||||
#include "base/time/time.h"
|
||||
#include "build/build_config.h"
|
||||
#include "testing/gtest/include/gtest/gtest.h"
|
||||
|
||||
namespace base {
|
||||
|
||||
template <typename String>
|
||||
void MeasureIsStringASCII(size_t str_length, size_t non_ascii_pos) {
|
||||
String str(str_length, 'A');
|
||||
if (non_ascii_pos < str_length)
|
||||
str[non_ascii_pos] = '\xAF';
|
||||
|
||||
TimeTicks t0 = TimeTicks::Now();
|
||||
for (size_t i = 0; i < 10000000; ++i)
|
||||
IsStringASCII(str);
|
||||
TimeDelta time = TimeTicks::Now() - t0;
|
||||
printf(
|
||||
"char-size:\t%zu\tlength:\t%zu\tnon-ascii-pos:\t%zu\ttime-ms:\t%" PRIu64
|
||||
"\n",
|
||||
sizeof(typename String::value_type), str_length, non_ascii_pos,
|
||||
time.InMilliseconds());
|
||||
}
|
||||
|
||||
TEST(StringUtilTest, DISABLED_IsStringASCIIPerf) {
|
||||
for (size_t str_length = 4; str_length <= 1024; str_length *= 2) {
|
||||
for (size_t non_ascii_loc = 0; non_ascii_loc < 3; ++non_ascii_loc) {
|
||||
size_t non_ascii_pos = str_length * non_ascii_loc / 2 + 2;
|
||||
MeasureIsStringASCII<std::string>(str_length, non_ascii_pos);
|
||||
MeasureIsStringASCII<string16>(str_length, non_ascii_pos);
|
||||
#if defined(WCHAR_T_IS_UTF32)
|
||||
MeasureIsStringASCII<std::basic_string<wchar_t>>(str_length,
|
||||
non_ascii_pos);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace base
|
|
@ -1,189 +0,0 @@
|
|||
// Copyright 2017 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "base/synchronization/waitable_event.h"
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "base/threading/simple_thread.h"
|
||||
#include "base/time/time.h"
|
||||
#include "base/timer/elapsed_timer.h"
|
||||
#include "testing/gtest/include/gtest/gtest.h"
|
||||
#include "testing/perf/perf_result_reporter.h"
|
||||
|
||||
namespace base {
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr char kMetricPrefixWaitableEvent[] = "WaitableEvent.";
|
||||
constexpr char kMetricWaitTime[] = "wait_time_per_sample";
|
||||
constexpr char kMetricSignalTime[] = "signal_time_per_sample";
|
||||
constexpr char kMetricElapsedCycles[] = "elapsed_cycles";
|
||||
constexpr char kStorySingleThread[] = "single_thread_1000_samples";
|
||||
constexpr char kStoryMultiThreadWaiter[] = "multi_thread_1000_samples_waiter";
|
||||
constexpr char kStoryMultiThreadSignaler[] =
|
||||
"multi_thread_1000_samples_signaler";
|
||||
constexpr char kStoryTimedThroughput[] = "timed_throughput";
|
||||
|
||||
perf_test::PerfResultReporter SetUpReporter(const std::string& story_name) {
|
||||
perf_test::PerfResultReporter reporter(kMetricPrefixWaitableEvent,
|
||||
story_name);
|
||||
reporter.RegisterImportantMetric(kMetricWaitTime, "ns");
|
||||
reporter.RegisterImportantMetric(kMetricSignalTime, "ns");
|
||||
reporter.RegisterImportantMetric(kMetricElapsedCycles, "count");
|
||||
return reporter;
|
||||
}
|
||||
|
||||
class TraceWaitableEvent {
|
||||
public:
|
||||
TraceWaitableEvent() = default;
|
||||
~TraceWaitableEvent() = default;
|
||||
|
||||
void Signal() {
|
||||
ElapsedTimer timer;
|
||||
event_.Signal();
|
||||
total_signal_time_ += timer.Elapsed();
|
||||
++signal_samples_;
|
||||
}
|
||||
|
||||
void Wait() {
|
||||
ElapsedTimer timer;
|
||||
event_.Wait();
|
||||
total_wait_time_ += timer.Elapsed();
|
||||
++wait_samples_;
|
||||
}
|
||||
|
||||
bool TimedWaitUntil(const TimeTicks& end_time) {
|
||||
ElapsedTimer timer;
|
||||
const bool signaled = event_.TimedWait(end_time - timer.Begin());
|
||||
total_wait_time_ += timer.Elapsed();
|
||||
++wait_samples_;
|
||||
return signaled;
|
||||
}
|
||||
|
||||
bool IsSignaled() { return event_.IsSignaled(); }
|
||||
|
||||
TimeDelta total_signal_time() const { return total_signal_time_; }
|
||||
TimeDelta total_wait_time() const { return total_wait_time_; }
|
||||
size_t signal_samples() const { return signal_samples_; }
|
||||
size_t wait_samples() const { return wait_samples_; }
|
||||
|
||||
private:
|
||||
WaitableEvent event_{WaitableEvent::ResetPolicy::AUTOMATIC};
|
||||
|
||||
TimeDelta total_signal_time_;
|
||||
TimeDelta total_wait_time_;
|
||||
|
||||
size_t signal_samples_ = 0U;
|
||||
size_t wait_samples_ = 0U;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(TraceWaitableEvent);
|
||||
};
|
||||
|
||||
class SignalerThread : public SimpleThread {
|
||||
public:
|
||||
SignalerThread(TraceWaitableEvent* waiter, TraceWaitableEvent* signaler)
|
||||
: SimpleThread("WaitableEventPerfTest signaler"),
|
||||
waiter_(waiter),
|
||||
signaler_(signaler) {}
|
||||
|
||||
~SignalerThread() override = default;
|
||||
|
||||
void Run() override {
|
||||
while (!stop_event_.IsSignaled()) {
|
||||
if (waiter_)
|
||||
waiter_->Wait();
|
||||
if (signaler_)
|
||||
signaler_->Signal();
|
||||
}
|
||||
}
|
||||
|
||||
// Signals the thread to stop on the next iteration of its loop (which
|
||||
// will happen immediately if no |waiter_| is present or is signaled.
|
||||
void RequestStop() { stop_event_.Signal(); }
|
||||
|
||||
private:
|
||||
WaitableEvent stop_event_;
|
||||
TraceWaitableEvent* waiter_;
|
||||
TraceWaitableEvent* signaler_;
|
||||
DISALLOW_COPY_AND_ASSIGN(SignalerThread);
|
||||
};
|
||||
|
||||
void PrintPerfWaitableEvent(const TraceWaitableEvent* event,
|
||||
const std::string& story_name,
|
||||
size_t* elapsed_cycles = nullptr) {
|
||||
auto reporter = SetUpReporter(story_name);
|
||||
reporter.AddResult(
|
||||
kMetricSignalTime,
|
||||
static_cast<size_t>(event->total_signal_time().InNanoseconds()) /
|
||||
event->signal_samples());
|
||||
reporter.AddResult(
|
||||
kMetricWaitTime,
|
||||
static_cast<size_t>(event->total_wait_time().InNanoseconds()) /
|
||||
event->wait_samples());
|
||||
if (elapsed_cycles) {
|
||||
reporter.AddResult(kMetricElapsedCycles, *elapsed_cycles);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
TEST(WaitableEventPerfTest, SingleThread) {
|
||||
const size_t kSamples = 1000;
|
||||
|
||||
TraceWaitableEvent event;
|
||||
|
||||
for (size_t i = 0; i < kSamples; ++i) {
|
||||
event.Signal();
|
||||
event.Wait();
|
||||
}
|
||||
|
||||
PrintPerfWaitableEvent(&event, kStorySingleThread);
|
||||
}
|
||||
|
||||
TEST(WaitableEventPerfTest, MultipleThreads) {
|
||||
const size_t kSamples = 1000;
|
||||
|
||||
TraceWaitableEvent waiter;
|
||||
TraceWaitableEvent signaler;
|
||||
|
||||
// The other thread will wait and signal on the respective opposite events.
|
||||
SignalerThread thread(&signaler, &waiter);
|
||||
thread.Start();
|
||||
|
||||
for (size_t i = 0; i < kSamples; ++i) {
|
||||
signaler.Signal();
|
||||
waiter.Wait();
|
||||
}
|
||||
|
||||
// Signal the stop event and then make sure the signaler event it is
|
||||
// waiting on is also signaled.
|
||||
thread.RequestStop();
|
||||
signaler.Signal();
|
||||
|
||||
thread.Join();
|
||||
|
||||
PrintPerfWaitableEvent(&waiter, kStoryMultiThreadWaiter);
|
||||
PrintPerfWaitableEvent(&signaler, kStoryMultiThreadSignaler);
|
||||
}
|
||||
|
||||
TEST(WaitableEventPerfTest, Throughput) {
|
||||
TraceWaitableEvent event;
|
||||
|
||||
SignalerThread thread(nullptr, &event);
|
||||
thread.Start();
|
||||
|
||||
const TimeTicks end_time = TimeTicks::Now() + TimeDelta::FromSeconds(1);
|
||||
size_t count = 0;
|
||||
while (event.TimedWaitUntil(end_time)) {
|
||||
++count;
|
||||
}
|
||||
|
||||
thread.RequestStop();
|
||||
thread.Join();
|
||||
|
||||
PrintPerfWaitableEvent(&event, kStoryTimedThroughput, &count);
|
||||
}
|
||||
|
||||
} // namespace base
|
|
@ -1,737 +0,0 @@
|
|||
// Copyright 2015 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "base/task/sequence_manager/sequence_manager.h"
|
||||
|
||||
#include <stddef.h>
|
||||
#include <memory>
|
||||
|
||||
#include "base/bind.h"
|
||||
#include "base/message_loop/message_pump_default.h"
|
||||
#include "base/message_loop/message_pump_type.h"
|
||||
#include "base/run_loop.h"
|
||||
#include "base/sequence_checker.h"
|
||||
#include "base/single_thread_task_runner.h"
|
||||
#include "base/strings/stringprintf.h"
|
||||
#include "base/synchronization/condition_variable.h"
|
||||
#include "base/task/post_task.h"
|
||||
#include "base/task/sequence_manager/task_queue_impl.h"
|
||||
#include "base/task/sequence_manager/test/mock_time_domain.h"
|
||||
#include "base/task/sequence_manager/test/sequence_manager_for_test.h"
|
||||
#include "base/task/sequence_manager/test/test_task_queue.h"
|
||||
#include "base/task/sequence_manager/test/test_task_time_observer.h"
|
||||
#include "base/task/sequence_manager/thread_controller_with_message_pump_impl.h"
|
||||
#include "base/task/task_traits.h"
|
||||
#include "base/task/thread_pool.h"
|
||||
#include "base/task/thread_pool/thread_pool_impl.h"
|
||||
#include "base/task/thread_pool/thread_pool_instance.h"
|
||||
#include "base/threading/thread.h"
|
||||
#include "base/threading/thread_task_runner_handle.h"
|
||||
#include "base/time/default_tick_clock.h"
|
||||
#include "build/build_config.h"
|
||||
#include "testing/gtest/include/gtest/gtest.h"
|
||||
#include "testing/perf/perf_result_reporter.h"
|
||||
|
||||
namespace base {
|
||||
namespace sequence_manager {
|
||||
namespace {
|
||||
const int kNumTasks = 1000000;
|
||||
|
||||
constexpr char kMetricPrefixSequenceManager[] = "SequenceManager.";
|
||||
constexpr char kMetricPostTimePerTask[] = "post_time_per_task";
|
||||
|
||||
perf_test::PerfResultReporter SetUpReporter(const std::string& story_name) {
|
||||
perf_test::PerfResultReporter reporter(kMetricPrefixSequenceManager,
|
||||
story_name);
|
||||
reporter.RegisterImportantMetric(kMetricPostTimePerTask, "us");
|
||||
return reporter;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
// To reduce noise related to the OS timer, we use a mock time domain to
|
||||
// fast forward the timers.
|
||||
class PerfTestTimeDomain : public MockTimeDomain {
|
||||
public:
|
||||
PerfTestTimeDomain() : MockTimeDomain(TimeTicks::Now()) {}
|
||||
~PerfTestTimeDomain() override = default;
|
||||
|
||||
Optional<TimeDelta> DelayTillNextTask(LazyNow* lazy_now) override {
|
||||
Optional<TimeTicks> run_time = NextScheduledRunTime();
|
||||
if (!run_time)
|
||||
return nullopt;
|
||||
SetNowTicks(*run_time);
|
||||
// Makes SequenceManager to continue immediately.
|
||||
return TimeDelta();
|
||||
}
|
||||
|
||||
void SetNextDelayedDoWork(LazyNow* lazy_now, TimeTicks run_time) override {
|
||||
// De-dupe DoWorks.
|
||||
if (NumberOfScheduledWakeUps() == 1u)
|
||||
RequestDoWork();
|
||||
}
|
||||
|
||||
private:
|
||||
DISALLOW_COPY_AND_ASSIGN(PerfTestTimeDomain);
|
||||
};
|
||||
|
||||
enum class PerfTestType {
|
||||
// A SequenceManager with a ThreadControllerWithMessagePumpImpl driving the
|
||||
// thread.
|
||||
kUseSequenceManagerWithMessagePump,
|
||||
kUseSequenceManagerWithUIMessagePump,
|
||||
kUseSequenceManagerWithIOMessagePump,
|
||||
kUseSequenceManagerWithMessagePumpAndRandomSampling,
|
||||
|
||||
// A SingleThreadTaskRunner in the thread pool.
|
||||
kUseSingleThreadInThreadPool,
|
||||
};
|
||||
|
||||
// Customization point for SequenceManagerPerfTest which allows us to test
|
||||
// various implementations.
|
||||
class PerfTestDelegate {
|
||||
public:
|
||||
virtual ~PerfTestDelegate() = default;
|
||||
|
||||
virtual const char* GetName() const = 0;
|
||||
|
||||
virtual bool VirtualTimeIsSupported() const = 0;
|
||||
|
||||
virtual bool MultipleQueuesSupported() const = 0;
|
||||
|
||||
virtual scoped_refptr<TaskRunner> CreateTaskRunner() = 0;
|
||||
|
||||
virtual void WaitUntilDone() = 0;
|
||||
|
||||
virtual void SignalDone() = 0;
|
||||
};
|
||||
|
||||
class BaseSequenceManagerPerfTestDelegate : public PerfTestDelegate {
|
||||
public:
|
||||
BaseSequenceManagerPerfTestDelegate() {}
|
||||
|
||||
~BaseSequenceManagerPerfTestDelegate() override = default;
|
||||
|
||||
bool VirtualTimeIsSupported() const override { return true; }
|
||||
|
||||
bool MultipleQueuesSupported() const override { return true; }
|
||||
|
||||
scoped_refptr<TaskRunner> CreateTaskRunner() override {
|
||||
scoped_refptr<TestTaskQueue> task_queue =
|
||||
manager_->CreateTaskQueueWithType<TestTaskQueue>(
|
||||
TaskQueue::Spec("test").SetTimeDomain(time_domain_.get()));
|
||||
owned_task_queues_.push_back(task_queue);
|
||||
return task_queue->task_runner();
|
||||
}
|
||||
|
||||
void WaitUntilDone() override {
|
||||
run_loop_.reset(new RunLoop());
|
||||
run_loop_->Run();
|
||||
}
|
||||
|
||||
void SignalDone() override { run_loop_->Quit(); }
|
||||
|
||||
SequenceManager* GetManager() const { return manager_.get(); }
|
||||
|
||||
void SetSequenceManager(std::unique_ptr<SequenceManager> manager) {
|
||||
manager_ = std::move(manager);
|
||||
time_domain_ = std::make_unique<PerfTestTimeDomain>();
|
||||
manager_->RegisterTimeDomain(time_domain_.get());
|
||||
}
|
||||
|
||||
void ShutDown() {
|
||||
owned_task_queues_.clear();
|
||||
manager_->UnregisterTimeDomain(time_domain_.get());
|
||||
manager_.reset();
|
||||
}
|
||||
|
||||
private:
|
||||
std::unique_ptr<SequenceManager> manager_;
|
||||
std::unique_ptr<TimeDomain> time_domain_;
|
||||
std::unique_ptr<RunLoop> run_loop_;
|
||||
std::vector<scoped_refptr<TestTaskQueue>> owned_task_queues_;
|
||||
};
|
||||
|
||||
class SequenceManagerWithMessagePumpPerfTestDelegate
|
||||
: public BaseSequenceManagerPerfTestDelegate {
|
||||
public:
|
||||
SequenceManagerWithMessagePumpPerfTestDelegate(
|
||||
const char* name,
|
||||
MessagePumpType type,
|
||||
bool randomised_sampling_enabled = false)
|
||||
: name_(name) {
|
||||
auto settings =
|
||||
SequenceManager::Settings::Builder()
|
||||
.SetRandomisedSamplingEnabled(randomised_sampling_enabled)
|
||||
.Build();
|
||||
SetSequenceManager(SequenceManagerForTest::Create(
|
||||
std::make_unique<internal::ThreadControllerWithMessagePumpImpl>(
|
||||
MessagePump::Create(type), settings),
|
||||
std::move(settings)));
|
||||
|
||||
// ThreadControllerWithMessagePumpImpl doesn't provide a default task
|
||||
// runner.
|
||||
scoped_refptr<TaskQueue> default_task_queue =
|
||||
GetManager()->template CreateTaskQueueWithType<TestTaskQueue>(
|
||||
TaskQueue::Spec("default"));
|
||||
GetManager()->SetDefaultTaskRunner(default_task_queue->task_runner());
|
||||
}
|
||||
|
||||
~SequenceManagerWithMessagePumpPerfTestDelegate() override { ShutDown(); }
|
||||
|
||||
const char* GetName() const override { return name_; }
|
||||
|
||||
private:
|
||||
const char* const name_;
|
||||
};
|
||||
|
||||
class SingleThreadInThreadPoolPerfTestDelegate : public PerfTestDelegate {
|
||||
public:
|
||||
SingleThreadInThreadPoolPerfTestDelegate() : done_cond_(&done_lock_) {
|
||||
ThreadPoolInstance::Set(
|
||||
std::make_unique<::base::internal::ThreadPoolImpl>("Test"));
|
||||
ThreadPoolInstance::Get()->StartWithDefaultParams();
|
||||
}
|
||||
|
||||
~SingleThreadInThreadPoolPerfTestDelegate() override {
|
||||
ThreadPoolInstance::Get()->JoinForTesting();
|
||||
ThreadPoolInstance::Set(nullptr);
|
||||
}
|
||||
|
||||
const char* GetName() const override {
|
||||
return " single thread in ThreadPool ";
|
||||
}
|
||||
|
||||
bool VirtualTimeIsSupported() const override { return false; }
|
||||
|
||||
bool MultipleQueuesSupported() const override { return false; }
|
||||
|
||||
scoped_refptr<TaskRunner> CreateTaskRunner() override {
|
||||
return ThreadPool::CreateSingleThreadTaskRunner(
|
||||
{TaskPriority::USER_BLOCKING});
|
||||
}
|
||||
|
||||
void WaitUntilDone() override {
|
||||
AutoLock auto_lock(done_lock_);
|
||||
done_cond_.Wait();
|
||||
}
|
||||
|
||||
void SignalDone() override {
|
||||
AutoLock auto_lock(done_lock_);
|
||||
done_cond_.Signal();
|
||||
}
|
||||
|
||||
private:
|
||||
Lock done_lock_;
|
||||
ConditionVariable done_cond_;
|
||||
};
|
||||
|
||||
class TestCase {
|
||||
public:
|
||||
// |delegate| is assumed to outlive TestCase.
|
||||
explicit TestCase(PerfTestDelegate* delegate) : delegate_(delegate) {}
|
||||
|
||||
virtual ~TestCase() = default;
|
||||
|
||||
virtual void Start() = 0;
|
||||
|
||||
protected:
|
||||
PerfTestDelegate* const delegate_; // NOT OWNED
|
||||
};
|
||||
|
||||
class TaskSource {
|
||||
public:
|
||||
virtual ~TaskSource() = default;
|
||||
|
||||
virtual void Start() = 0;
|
||||
};
|
||||
|
||||
class SameThreadTaskSource : public TaskSource {
|
||||
public:
|
||||
SameThreadTaskSource(std::vector<scoped_refptr<TaskRunner>> task_runners,
|
||||
size_t num_tasks)
|
||||
: num_queues_(task_runners.size()),
|
||||
num_tasks_(num_tasks),
|
||||
task_closure_(
|
||||
BindRepeating(&SameThreadTaskSource::TestTask, Unretained(this))),
|
||||
task_runners_(std::move(task_runners)) {
|
||||
DETACH_FROM_SEQUENCE(sequence_checker_);
|
||||
}
|
||||
|
||||
void Start() override {
|
||||
num_tasks_in_flight_ = 1;
|
||||
num_tasks_to_post_ = num_tasks_;
|
||||
num_tasks_to_run_ = num_tasks_;
|
||||
// Post the initial task instead of running it synchronously to ensure that
|
||||
// all invocations happen on the same sequence.
|
||||
PostTask(0);
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual void PostTask(unsigned int queue) = 0;
|
||||
|
||||
virtual void SignalDone() = 0;
|
||||
|
||||
void TestTask() {
|
||||
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
|
||||
|
||||
if (--num_tasks_to_run_ == 0) {
|
||||
SignalDone();
|
||||
return;
|
||||
}
|
||||
|
||||
num_tasks_in_flight_--;
|
||||
// NOTE there are only up to max_tasks_in_flight_ pending delayed tasks at
|
||||
// any one time. Thanks to the lower_num_tasks_to_post going to zero if
|
||||
// there are a lot of tasks in flight, the total number of task in flight at
|
||||
// any one time is very variable.
|
||||
unsigned int lower_num_tasks_to_post =
|
||||
num_tasks_in_flight_ < (max_tasks_in_flight_ / 2) ? 1 : 0;
|
||||
unsigned int max_tasks_to_post =
|
||||
num_tasks_to_post_ % 2 ? lower_num_tasks_to_post : 10;
|
||||
for (unsigned int i = 0;
|
||||
i < max_tasks_to_post && num_tasks_in_flight_ < max_tasks_in_flight_ &&
|
||||
num_tasks_to_post_ > 0;
|
||||
i++) {
|
||||
// Choose a queue weighted towards queue 0.
|
||||
unsigned int queue = num_tasks_to_post_ % (num_queues_ + 1);
|
||||
if (queue == num_queues_) {
|
||||
queue = 0;
|
||||
}
|
||||
PostTask(queue);
|
||||
num_tasks_in_flight_++;
|
||||
num_tasks_to_post_--;
|
||||
}
|
||||
}
|
||||
|
||||
const size_t num_queues_;
|
||||
const size_t num_tasks_;
|
||||
const RepeatingClosure task_closure_;
|
||||
const std::vector<scoped_refptr<TaskRunner>> task_runners_;
|
||||
const unsigned int max_tasks_in_flight_ = 200;
|
||||
unsigned int num_tasks_in_flight_;
|
||||
unsigned int num_tasks_to_post_;
|
||||
unsigned int num_tasks_to_run_;
|
||||
SEQUENCE_CHECKER(sequence_checker_);
|
||||
};
|
||||
|
||||
class CrossThreadTaskSource : public TaskSource {
|
||||
public:
|
||||
CrossThreadTaskSource(std::vector<scoped_refptr<TaskRunner>> task_runners,
|
||||
size_t num_tasks)
|
||||
: num_queues_(task_runners.size()),
|
||||
num_tasks_(num_tasks),
|
||||
task_closure_(
|
||||
BindRepeating(&CrossThreadTaskSource::TestTask, Unretained(this))),
|
||||
task_runners_(std::move(task_runners)) {}
|
||||
|
||||
void Start() override {
|
||||
num_tasks_in_flight_ = 0;
|
||||
num_tasks_to_run_ = num_tasks_;
|
||||
|
||||
for (size_t i = 0; i < num_tasks_; i++) {
|
||||
while (num_tasks_in_flight_.load(std::memory_order_acquire) >
|
||||
max_tasks_in_flight_) {
|
||||
PlatformThread::YieldCurrentThread();
|
||||
}
|
||||
// Choose a queue weighted towards queue 0.
|
||||
unsigned int queue = i % (num_queues_ + 1);
|
||||
if (queue == num_queues_) {
|
||||
queue = 0;
|
||||
}
|
||||
PostTask(queue);
|
||||
num_tasks_in_flight_++;
|
||||
}
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual void PostTask(unsigned int queue) = 0;
|
||||
|
||||
// Will be called on the main thread.
|
||||
virtual void SignalDone() = 0;
|
||||
|
||||
void TestTask() {
|
||||
if (num_tasks_to_run_.fetch_sub(1) == 1) {
|
||||
SignalDone();
|
||||
return;
|
||||
}
|
||||
num_tasks_in_flight_--;
|
||||
}
|
||||
|
||||
const size_t num_queues_;
|
||||
const size_t num_tasks_;
|
||||
const RepeatingClosure task_closure_;
|
||||
const std::vector<scoped_refptr<TaskRunner>> task_runners_;
|
||||
const unsigned int max_tasks_in_flight_ = 200;
|
||||
std::atomic<unsigned int> num_tasks_in_flight_;
|
||||
std::atomic<unsigned int> num_tasks_to_run_;
|
||||
};
|
||||
|
||||
class SingleThreadImmediateTestCase : public TestCase {
|
||||
public:
|
||||
SingleThreadImmediateTestCase(
|
||||
PerfTestDelegate* delegate,
|
||||
std::vector<scoped_refptr<TaskRunner>> task_runners)
|
||||
: TestCase(delegate),
|
||||
task_source_(std::make_unique<SingleThreadImmediateTaskSource>(
|
||||
delegate,
|
||||
std::move(task_runners),
|
||||
kNumTasks)) {}
|
||||
|
||||
void Start() override { task_source_->Start(); }
|
||||
|
||||
private:
|
||||
class SingleThreadImmediateTaskSource : public SameThreadTaskSource {
|
||||
public:
|
||||
SingleThreadImmediateTaskSource(
|
||||
PerfTestDelegate* delegate,
|
||||
std::vector<scoped_refptr<TaskRunner>> task_runners,
|
||||
size_t num_tasks)
|
||||
: SameThreadTaskSource(std::move(task_runners), num_tasks),
|
||||
delegate_(delegate) {}
|
||||
|
||||
~SingleThreadImmediateTaskSource() override = default;
|
||||
|
||||
void PostTask(unsigned int queue) override {
|
||||
task_runners_[queue]->PostTask(FROM_HERE, task_closure_);
|
||||
}
|
||||
|
||||
void SignalDone() override { delegate_->SignalDone(); }
|
||||
|
||||
PerfTestDelegate* delegate_; // NOT OWNED.
|
||||
};
|
||||
|
||||
const std::unique_ptr<TaskSource> task_source_;
|
||||
};
|
||||
|
||||
class SingleThreadDelayedTestCase : public TestCase {
|
||||
public:
|
||||
SingleThreadDelayedTestCase(
|
||||
PerfTestDelegate* delegate,
|
||||
std::vector<scoped_refptr<TaskRunner>> task_runners)
|
||||
: TestCase(delegate),
|
||||
task_source_(std::make_unique<SingleThreadDelayedTaskSource>(
|
||||
delegate,
|
||||
std::move(task_runners),
|
||||
kNumTasks)) {}
|
||||
|
||||
void Start() override { task_source_->Start(); }
|
||||
|
||||
private:
|
||||
class SingleThreadDelayedTaskSource : public SameThreadTaskSource {
|
||||
public:
|
||||
explicit SingleThreadDelayedTaskSource(
|
||||
PerfTestDelegate* delegate,
|
||||
std::vector<scoped_refptr<TaskRunner>> task_runners,
|
||||
size_t num_tasks)
|
||||
: SameThreadTaskSource(std::move(task_runners), num_tasks),
|
||||
delegate_(delegate) {}
|
||||
|
||||
~SingleThreadDelayedTaskSource() override = default;
|
||||
|
||||
void PostTask(unsigned int queue) override {
|
||||
unsigned int delay =
|
||||
num_tasks_to_post_ % 2 ? 1 : (10 + num_tasks_to_post_ % 10);
|
||||
task_runners_[queue]->PostDelayedTask(FROM_HERE, task_closure_,
|
||||
TimeDelta::FromMilliseconds(delay));
|
||||
}
|
||||
|
||||
void SignalDone() override { delegate_->SignalDone(); }
|
||||
|
||||
PerfTestDelegate* delegate_; // NOT OWNED.
|
||||
};
|
||||
|
||||
const std::unique_ptr<TaskSource> task_source_;
|
||||
};
|
||||
|
||||
class TwoThreadTestCase : public TestCase {
|
||||
public:
|
||||
TwoThreadTestCase(PerfTestDelegate* delegate,
|
||||
std::vector<scoped_refptr<TaskRunner>> task_runners)
|
||||
: TestCase(delegate),
|
||||
task_runners_(std::move(task_runners)),
|
||||
num_tasks_(kNumTasks),
|
||||
auxiliary_thread_("auxillary thread") {
|
||||
auxiliary_thread_.Start();
|
||||
}
|
||||
|
||||
~TwoThreadTestCase() override { auxiliary_thread_.Stop(); }
|
||||
|
||||
protected:
|
||||
void Start() override {
|
||||
done_count_ = 0;
|
||||
same_thread_task_source_ =
|
||||
std::make_unique<SingleThreadImmediateTaskSource>(this, task_runners_,
|
||||
num_tasks_ / 2);
|
||||
cross_thread_task_scorce_ =
|
||||
std::make_unique<CrossThreadImmediateTaskSource>(this, task_runners_,
|
||||
num_tasks_ / 2);
|
||||
|
||||
auxiliary_thread_.task_runner()->PostTask(
|
||||
FROM_HERE, base::BindOnce(&CrossThreadImmediateTaskSource::Start,
|
||||
Unretained(cross_thread_task_scorce_.get())));
|
||||
same_thread_task_source_->Start();
|
||||
}
|
||||
|
||||
class SingleThreadImmediateTaskSource : public SameThreadTaskSource {
|
||||
public:
|
||||
SingleThreadImmediateTaskSource(
|
||||
TwoThreadTestCase* two_thread_test_case,
|
||||
std::vector<scoped_refptr<TaskRunner>> task_runners,
|
||||
size_t num_tasks)
|
||||
: SameThreadTaskSource(std::move(task_runners), num_tasks),
|
||||
two_thread_test_case_(two_thread_test_case) {}
|
||||
|
||||
~SingleThreadImmediateTaskSource() override = default;
|
||||
|
||||
void PostTask(unsigned int queue) override {
|
||||
task_runners_[queue]->PostTask(FROM_HERE, task_closure_);
|
||||
}
|
||||
|
||||
// Will be called on the main thread.
|
||||
void SignalDone() override { two_thread_test_case_->SignalDone(); }
|
||||
|
||||
TwoThreadTestCase* two_thread_test_case_; // NOT OWNED.
|
||||
};
|
||||
|
||||
class CrossThreadImmediateTaskSource : public CrossThreadTaskSource {
|
||||
public:
|
||||
CrossThreadImmediateTaskSource(
|
||||
TwoThreadTestCase* two_thread_test_case,
|
||||
std::vector<scoped_refptr<TaskRunner>> task_runners,
|
||||
size_t num_tasks)
|
||||
: CrossThreadTaskSource(std::move(task_runners), num_tasks),
|
||||
two_thread_test_case_(two_thread_test_case) {}
|
||||
|
||||
~CrossThreadImmediateTaskSource() override = default;
|
||||
|
||||
void PostTask(unsigned int queue) override {
|
||||
task_runners_[queue]->PostTask(FROM_HERE, task_closure_);
|
||||
}
|
||||
|
||||
// Will be called on the main thread.
|
||||
void SignalDone() override { two_thread_test_case_->SignalDone(); }
|
||||
|
||||
TwoThreadTestCase* two_thread_test_case_; // NOT OWNED.
|
||||
};
|
||||
|
||||
void SignalDone() {
|
||||
if (++done_count_ == 2)
|
||||
delegate_->SignalDone();
|
||||
}
|
||||
|
||||
private:
|
||||
const std::vector<scoped_refptr<TaskRunner>> task_runners_;
|
||||
const size_t num_tasks_;
|
||||
Thread auxiliary_thread_;
|
||||
std::unique_ptr<SingleThreadImmediateTaskSource> same_thread_task_source_;
|
||||
std::unique_ptr<CrossThreadImmediateTaskSource> cross_thread_task_scorce_;
|
||||
int done_count_ = 0;
|
||||
};
|
||||
|
||||
class SequenceManagerPerfTest : public testing::TestWithParam<PerfTestType> {
|
||||
public:
|
||||
SequenceManagerPerfTest() = default;
|
||||
|
||||
void SetUp() override { delegate_ = CreateDelegate(); }
|
||||
|
||||
void TearDown() override { delegate_.reset(); }
|
||||
|
||||
std::unique_ptr<PerfTestDelegate> CreateDelegate() {
|
||||
switch (GetParam()) {
|
||||
case PerfTestType::kUseSequenceManagerWithMessagePump:
|
||||
return std::make_unique<SequenceManagerWithMessagePumpPerfTestDelegate>(
|
||||
" SequenceManager with MessagePumpDefault ",
|
||||
MessagePumpType::DEFAULT);
|
||||
|
||||
case PerfTestType::kUseSequenceManagerWithUIMessagePump:
|
||||
return std::make_unique<SequenceManagerWithMessagePumpPerfTestDelegate>(
|
||||
" SequenceManager with MessagePumpForUI ", MessagePumpType::UI);
|
||||
|
||||
case PerfTestType::kUseSequenceManagerWithIOMessagePump:
|
||||
return std::make_unique<SequenceManagerWithMessagePumpPerfTestDelegate>(
|
||||
" SequenceManager with MessagePumpForIO ", MessagePumpType::IO);
|
||||
|
||||
case PerfTestType::kUseSequenceManagerWithMessagePumpAndRandomSampling:
|
||||
return std::make_unique<SequenceManagerWithMessagePumpPerfTestDelegate>(
|
||||
" SequenceManager with MessagePumpDefault and random sampling ",
|
||||
MessagePumpType::DEFAULT, true);
|
||||
|
||||
case PerfTestType::kUseSingleThreadInThreadPool:
|
||||
return std::make_unique<SingleThreadInThreadPoolPerfTestDelegate>();
|
||||
|
||||
default:
|
||||
NOTREACHED();
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
bool ShouldMeasureQueueScaling() const {
|
||||
// To limit test run time, we only measure multiple queues specific sequence
|
||||
// manager configurations.
|
||||
return delegate_->MultipleQueuesSupported() &&
|
||||
GetParam() == PerfTestType::kUseSequenceManagerWithUIMessagePump;
|
||||
}
|
||||
|
||||
std::vector<scoped_refptr<TaskRunner>> CreateTaskRunners(int num) {
|
||||
std::vector<scoped_refptr<TaskRunner>> task_runners;
|
||||
for (int i = 0; i < num; i++) {
|
||||
task_runners.push_back(delegate_->CreateTaskRunner());
|
||||
}
|
||||
return task_runners;
|
||||
}
|
||||
|
||||
void Benchmark(const std::string& story_prefix, TestCase* TestCase) {
|
||||
TimeTicks start = TimeTicks::Now();
|
||||
TimeTicks now;
|
||||
TestCase->Start();
|
||||
delegate_->WaitUntilDone();
|
||||
now = TimeTicks::Now();
|
||||
|
||||
auto reporter = SetUpReporter(story_prefix + delegate_->GetName());
|
||||
reporter.AddResult(
|
||||
kMetricPostTimePerTask,
|
||||
(now - start).InMicroseconds() / static_cast<double>(kNumTasks));
|
||||
}
|
||||
|
||||
std::unique_ptr<PerfTestDelegate> delegate_;
|
||||
};
|
||||
|
||||
INSTANTIATE_TEST_SUITE_P(
|
||||
All,
|
||||
SequenceManagerPerfTest,
|
||||
testing::Values(
|
||||
PerfTestType::kUseSequenceManagerWithMessagePump,
|
||||
PerfTestType::kUseSequenceManagerWithUIMessagePump,
|
||||
PerfTestType::kUseSequenceManagerWithIOMessagePump,
|
||||
PerfTestType::kUseSingleThreadInThreadPool,
|
||||
PerfTestType::kUseSequenceManagerWithMessagePumpAndRandomSampling));
|
||||
TEST_P(SequenceManagerPerfTest, PostDelayedTasks_OneQueue) {
|
||||
if (!delegate_->VirtualTimeIsSupported()) {
|
||||
LOG(INFO) << "Unsupported";
|
||||
return;
|
||||
}
|
||||
|
||||
SingleThreadDelayedTestCase task_source(delegate_.get(),
|
||||
CreateTaskRunners(1));
|
||||
Benchmark("post delayed tasks with one queue", &task_source);
|
||||
}
|
||||
|
||||
TEST_P(SequenceManagerPerfTest, PostDelayedTasks_FourQueues) {
|
||||
if (!delegate_->VirtualTimeIsSupported() || !ShouldMeasureQueueScaling()) {
|
||||
LOG(INFO) << "Unsupported";
|
||||
return;
|
||||
}
|
||||
|
||||
SingleThreadDelayedTestCase task_source(delegate_.get(),
|
||||
CreateTaskRunners(4));
|
||||
Benchmark("post delayed tasks with four queues", &task_source);
|
||||
}
|
||||
|
||||
TEST_P(SequenceManagerPerfTest, PostDelayedTasks_EightQueues) {
|
||||
if (!delegate_->VirtualTimeIsSupported() || !ShouldMeasureQueueScaling()) {
|
||||
LOG(INFO) << "Unsupported";
|
||||
return;
|
||||
}
|
||||
|
||||
SingleThreadDelayedTestCase task_source(delegate_.get(),
|
||||
CreateTaskRunners(8));
|
||||
Benchmark("post delayed tasks with eight queues", &task_source);
|
||||
}
|
||||
|
||||
TEST_P(SequenceManagerPerfTest, PostDelayedTasks_ThirtyTwoQueues) {
|
||||
if (!delegate_->VirtualTimeIsSupported() || !ShouldMeasureQueueScaling()) {
|
||||
LOG(INFO) << "Unsupported";
|
||||
return;
|
||||
}
|
||||
|
||||
SingleThreadDelayedTestCase task_source(delegate_.get(),
|
||||
CreateTaskRunners(32));
|
||||
Benchmark("post delayed tasks with thirty two queues", &task_source);
|
||||
}
|
||||
|
||||
TEST_P(SequenceManagerPerfTest, PostImmediateTasks_OneQueue) {
|
||||
SingleThreadImmediateTestCase task_source(delegate_.get(),
|
||||
CreateTaskRunners(1));
|
||||
Benchmark("post immediate tasks with one queue", &task_source);
|
||||
}
|
||||
|
||||
TEST_P(SequenceManagerPerfTest, PostImmediateTasks_FourQueues) {
|
||||
if (!ShouldMeasureQueueScaling()) {
|
||||
LOG(INFO) << "Unsupported";
|
||||
return;
|
||||
}
|
||||
|
||||
SingleThreadImmediateTestCase task_source(delegate_.get(),
|
||||
CreateTaskRunners(4));
|
||||
Benchmark("post immediate tasks with four queues", &task_source);
|
||||
}
|
||||
|
||||
TEST_P(SequenceManagerPerfTest, PostImmediateTasks_EightQueues) {
|
||||
if (!ShouldMeasureQueueScaling()) {
|
||||
LOG(INFO) << "Unsupported";
|
||||
return;
|
||||
}
|
||||
|
||||
SingleThreadImmediateTestCase task_source(delegate_.get(),
|
||||
CreateTaskRunners(8));
|
||||
Benchmark("post immediate tasks with eight queues", &task_source);
|
||||
}
|
||||
|
||||
TEST_P(SequenceManagerPerfTest, PostImmediateTasks_ThirtyTwoQueues) {
|
||||
if (!ShouldMeasureQueueScaling()) {
|
||||
LOG(INFO) << "Unsupported";
|
||||
return;
|
||||
}
|
||||
|
||||
SingleThreadImmediateTestCase task_source(delegate_.get(),
|
||||
CreateTaskRunners(32));
|
||||
Benchmark("post immediate tasks with thirty two queues", &task_source);
|
||||
}
|
||||
|
||||
TEST_P(SequenceManagerPerfTest, PostImmediateTasksFromTwoThreads_OneQueue) {
|
||||
TwoThreadTestCase task_source(delegate_.get(), CreateTaskRunners(1));
|
||||
Benchmark("post immediate tasks with one queue from two threads",
|
||||
&task_source);
|
||||
}
|
||||
|
||||
TEST_P(SequenceManagerPerfTest, PostImmediateTasksFromTwoThreads_FourQueues) {
|
||||
if (!ShouldMeasureQueueScaling()) {
|
||||
LOG(INFO) << "Unsupported";
|
||||
return;
|
||||
}
|
||||
|
||||
TwoThreadTestCase task_source(delegate_.get(), CreateTaskRunners(4));
|
||||
Benchmark("post immediate tasks with four queues from two threads",
|
||||
&task_source);
|
||||
}
|
||||
|
||||
TEST_P(SequenceManagerPerfTest, PostImmediateTasksFromTwoThreads_EightQueues) {
|
||||
if (!ShouldMeasureQueueScaling()) {
|
||||
LOG(INFO) << "Unsupported";
|
||||
return;
|
||||
}
|
||||
|
||||
TwoThreadTestCase task_source(delegate_.get(), CreateTaskRunners(8));
|
||||
Benchmark("post immediate tasks with eight queues from two threads",
|
||||
&task_source);
|
||||
}
|
||||
|
||||
TEST_P(SequenceManagerPerfTest,
|
||||
PostImmediateTasksFromTwoThreads_ThirtyTwoQueues) {
|
||||
if (!ShouldMeasureQueueScaling()) {
|
||||
LOG(INFO) << "Unsupported";
|
||||
return;
|
||||
}
|
||||
|
||||
TwoThreadTestCase task_source(delegate_.get(), CreateTaskRunners(32));
|
||||
Benchmark("post immediate tasks with thirty two queues from two threads",
|
||||
&task_source);
|
||||
}
|
||||
|
||||
// TODO(alexclarke): Add additional tests with different mixes of non-delayed vs
|
||||
// delayed tasks.
|
||||
|
||||
} // namespace sequence_manager
|
||||
} // namespace base
|
|
@ -1,191 +0,0 @@
|
|||
// Copyright 2019 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef BASE_TASK_THREAD_POOL_CAN_RUN_POLICY_TEST_H_
|
||||
#define BASE_TASK_THREAD_POOL_CAN_RUN_POLICY_TEST_H_
|
||||
|
||||
#include "base/synchronization/atomic_flag.h"
|
||||
#include "base/task/thread_pool/task_tracker.h"
|
||||
#include "base/task/thread_pool/test_utils.h"
|
||||
#include "base/task_runner.h"
|
||||
#include "base/test/bind_test_util.h"
|
||||
#include "base/test/test_timeouts.h"
|
||||
#include "base/test/test_waitable_event.h"
|
||||
#include "base/threading/platform_thread.h"
|
||||
#include "build/build_config.h"
|
||||
|
||||
namespace base {
|
||||
namespace internal {
|
||||
namespace test {
|
||||
|
||||
// Verify that tasks only run when allowed by the CanRunPolicy. |target| is the
|
||||
// object on which DidUpdateCanRunPolicy() must be called after updating the
|
||||
// CanRunPolicy in |task_tracker|. |create_task_runner| is a function that
|
||||
// receives a TaskPriority and returns a TaskRunner. |task_tracker| is the
|
||||
// TaskTracker.
|
||||
template <typename Target, typename CreateTaskRunner>
|
||||
void TestCanRunPolicyBasic(Target* target,
|
||||
CreateTaskRunner create_task_runner,
|
||||
TaskTracker* task_tracker) {
|
||||
AtomicFlag foreground_can_run;
|
||||
TestWaitableEvent foreground_did_run;
|
||||
AtomicFlag best_effort_can_run;
|
||||
TestWaitableEvent best_effort_did_run;
|
||||
|
||||
task_tracker->SetCanRunPolicy(CanRunPolicy::kNone);
|
||||
target->DidUpdateCanRunPolicy();
|
||||
|
||||
const auto user_visible_task_runner =
|
||||
create_task_runner(TaskPriority::USER_VISIBLE);
|
||||
user_visible_task_runner->PostTask(FROM_HERE, BindLambdaForTesting([&]() {
|
||||
EXPECT_TRUE(foreground_can_run.IsSet());
|
||||
foreground_did_run.Signal();
|
||||
}));
|
||||
const auto best_effort_task_runner =
|
||||
create_task_runner(TaskPriority::BEST_EFFORT);
|
||||
best_effort_task_runner->PostTask(FROM_HERE, BindLambdaForTesting([&]() {
|
||||
EXPECT_TRUE(best_effort_can_run.IsSet());
|
||||
best_effort_did_run.Signal();
|
||||
}));
|
||||
|
||||
PlatformThread::Sleep(TestTimeouts::tiny_timeout());
|
||||
|
||||
foreground_can_run.Set();
|
||||
task_tracker->SetCanRunPolicy(CanRunPolicy::kForegroundOnly);
|
||||
target->DidUpdateCanRunPolicy();
|
||||
foreground_did_run.Wait();
|
||||
|
||||
PlatformThread::Sleep(TestTimeouts::tiny_timeout());
|
||||
|
||||
best_effort_can_run.Set();
|
||||
task_tracker->SetCanRunPolicy(CanRunPolicy::kAll);
|
||||
target->DidUpdateCanRunPolicy();
|
||||
best_effort_did_run.Wait();
|
||||
}
|
||||
|
||||
// Verify that if a task was allowed to run by the CanRunPolicy when it was
|
||||
// posted, but the CanRunPolicy is updated to disallow it from running before it
|
||||
// starts running, it doesn't run. |target| is the object on which
|
||||
// DidUpdateCanRunPolicy() must be called after updating the CanRunPolicy in
|
||||
// |task_tracker|. |create_task_runner| is a function that receives a
|
||||
// TaskPriority and returns a *Sequenced*TaskRunner. |task_tracker| is the
|
||||
// TaskTracker.
|
||||
template <typename Target, typename CreateTaskRunner>
|
||||
void TestCanRunPolicyChangedBeforeRun(Target* target,
|
||||
CreateTaskRunner create_task_runner,
|
||||
TaskTracker* task_tracker) {
|
||||
constexpr struct {
|
||||
// Descriptor for the test case.
|
||||
const char* descriptor;
|
||||
// Task priority being tested.
|
||||
TaskPriority priority;
|
||||
// Policy that disallows running tasks with |priority|.
|
||||
CanRunPolicy disallow_policy;
|
||||
// Policy that allows running tasks with |priority|.
|
||||
CanRunPolicy allow_policy;
|
||||
} kTestCases[] = {
|
||||
{"BestEffort/kNone/kAll", TaskPriority::BEST_EFFORT, CanRunPolicy::kNone,
|
||||
CanRunPolicy::kAll},
|
||||
{"BestEffort/kForegroundOnly/kAll", TaskPriority::BEST_EFFORT,
|
||||
CanRunPolicy::kForegroundOnly, CanRunPolicy::kAll},
|
||||
{"UserVisible/kNone/kForegroundOnly", TaskPriority::USER_VISIBLE,
|
||||
CanRunPolicy::kNone, CanRunPolicy::kForegroundOnly},
|
||||
{"UserVisible/kNone/kAll", TaskPriority::USER_VISIBLE,
|
||||
CanRunPolicy::kNone, CanRunPolicy::kAll}};
|
||||
|
||||
for (auto& test_case : kTestCases) {
|
||||
SCOPED_TRACE(test_case.descriptor);
|
||||
|
||||
TestWaitableEvent first_task_started;
|
||||
TestWaitableEvent first_task_blocked;
|
||||
AtomicFlag second_task_can_run;
|
||||
|
||||
task_tracker->SetCanRunPolicy(test_case.allow_policy);
|
||||
target->DidUpdateCanRunPolicy();
|
||||
|
||||
const auto task_runner = create_task_runner(test_case.priority);
|
||||
task_runner->PostTask(
|
||||
FROM_HERE, BindLambdaForTesting([&]() {
|
||||
first_task_started.Signal();
|
||||
first_task_blocked.Wait();
|
||||
}));
|
||||
task_runner->PostTask(FROM_HERE, BindLambdaForTesting([&]() {
|
||||
EXPECT_TRUE(second_task_can_run.IsSet());
|
||||
}));
|
||||
|
||||
first_task_started.Wait();
|
||||
task_tracker->SetCanRunPolicy(test_case.disallow_policy);
|
||||
target->DidUpdateCanRunPolicy();
|
||||
first_task_blocked.Signal();
|
||||
|
||||
PlatformThread::Sleep(TestTimeouts::tiny_timeout());
|
||||
|
||||
second_task_can_run.Set();
|
||||
task_tracker->SetCanRunPolicy(test_case.allow_policy);
|
||||
target->DidUpdateCanRunPolicy();
|
||||
task_tracker->FlushForTesting();
|
||||
}
|
||||
}
|
||||
|
||||
// Regression test for https://crbug.com/950383
|
||||
template <typename Target, typename CreateTaskRunner>
|
||||
void TestCanRunPolicyLoad(Target* target,
|
||||
CreateTaskRunner create_task_runner,
|
||||
TaskTracker* task_tracker) {
|
||||
constexpr struct {
|
||||
// Descriptor for the test case.
|
||||
const char* descriptor;
|
||||
// Task priority being tested.
|
||||
TaskPriority priority;
|
||||
// Policy that allows running tasks with |priority|.
|
||||
CanRunPolicy allow_policy;
|
||||
// Policy that disallows running tasks with |priority|.
|
||||
CanRunPolicy disallow_policy;
|
||||
} kTestCases[] = {
|
||||
{"BestEffort/kAll/kNone", TaskPriority::BEST_EFFORT, CanRunPolicy::kAll,
|
||||
CanRunPolicy::kNone},
|
||||
{"BestEffort/kAll/kForegroundOnly", TaskPriority::BEST_EFFORT,
|
||||
CanRunPolicy::kAll, CanRunPolicy::kForegroundOnly},
|
||||
{"UserVisible/kForegroundOnly/kNone", TaskPriority::USER_VISIBLE,
|
||||
CanRunPolicy::kForegroundOnly, CanRunPolicy::kNone},
|
||||
{"UserVisible/kAll/kNone", TaskPriority::USER_VISIBLE, CanRunPolicy::kAll,
|
||||
CanRunPolicy::kNone}};
|
||||
|
||||
for (auto& test_case : kTestCases) {
|
||||
SCOPED_TRACE(test_case.descriptor);
|
||||
|
||||
task_tracker->SetCanRunPolicy(test_case.allow_policy);
|
||||
target->DidUpdateCanRunPolicy();
|
||||
|
||||
const auto task_runner = create_task_runner(test_case.priority);
|
||||
|
||||
// Post less tasks on iOS to avoid timeouts.
|
||||
const size_t kLargeNumber =
|
||||
#if defined(OS_IOS)
|
||||
16;
|
||||
#else
|
||||
256;
|
||||
#endif
|
||||
for (size_t i = 0; i < kLargeNumber; ++i)
|
||||
task_runner->PostTask(FROM_HERE, DoNothing());
|
||||
|
||||
// Change the CanRunPolicy concurrently with running tasks.
|
||||
// This should not cause crashes.
|
||||
for (size_t i = 0; i < kLargeNumber; ++i) {
|
||||
task_tracker->SetCanRunPolicy(test_case.disallow_policy);
|
||||
target->DidUpdateCanRunPolicy();
|
||||
|
||||
task_tracker->SetCanRunPolicy(test_case.allow_policy);
|
||||
target->DidUpdateCanRunPolicy();
|
||||
}
|
||||
|
||||
task_tracker->FlushForTesting();
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace test
|
||||
} // namespace internal
|
||||
} // namespace base
|
||||
|
||||
#endif // BASE_TASK_THREAD_POOL_CAN_RUN_POLICY_TEST_H_
|
|
@ -1,269 +0,0 @@
|
|||
// Copyright 2018 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include <stddef.h>
|
||||
#include <atomic>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
#include "base/barrier_closure.h"
|
||||
#include "base/bind.h"
|
||||
#include "base/bind_helpers.h"
|
||||
#include "base/callback.h"
|
||||
#include "base/optional.h"
|
||||
#include "base/synchronization/waitable_event.h"
|
||||
#include "base/task/thread_pool.h"
|
||||
#include "base/task/thread_pool/thread_pool_instance.h"
|
||||
#include "base/threading/simple_thread.h"
|
||||
#include "base/time/time.h"
|
||||
#include "testing/gtest/include/gtest/gtest.h"
|
||||
#include "testing/perf/perf_result_reporter.h"
|
||||
|
||||
namespace base {
|
||||
namespace internal {
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr char kMetricPrefixThreadPool[] = "ThreadPool.";
|
||||
constexpr char kMetricPostTaskThroughput[] = "post_task_throughput";
|
||||
constexpr char kMetricRunTaskThroughput[] = "run_task_throughput";
|
||||
constexpr char kMetricNumTasksPosted[] = "num_tasks_posted";
|
||||
constexpr char kStoryBindPostThenRunNoOp[] = "bind_post_then_run_noop_tasks";
|
||||
constexpr char kStoryPostThenRunNoOp[] = "post_then_run_noop_tasks";
|
||||
constexpr char kStoryPostThenRunNoOpManyThreads[] =
|
||||
"post_then_run_noop_tasks_many_threads";
|
||||
constexpr char kStoryPostThenRunNoOpMoreThanRunningThreads[] =
|
||||
"post_then_run_noop_tasks_more_than_running_threads";
|
||||
constexpr char kStoryPostRunNoOp[] = "post_run_noop_tasks";
|
||||
constexpr char kStoryPostRunNoOpManyThreads[] =
|
||||
"post_run_noop_tasks_many_threads";
|
||||
constexpr char kStoryPostRunBusyManyThreads[] =
|
||||
"post_run_busy_tasks_many_threads";
|
||||
|
||||
perf_test::PerfResultReporter SetUpReporter(const std::string& story_name) {
|
||||
perf_test::PerfResultReporter reporter(kMetricPrefixThreadPool, story_name);
|
||||
reporter.RegisterImportantMetric(kMetricPostTaskThroughput, "runs/s");
|
||||
reporter.RegisterImportantMetric(kMetricRunTaskThroughput, "runs/s");
|
||||
reporter.RegisterImportantMetric(kMetricNumTasksPosted, "count");
|
||||
return reporter;
|
||||
}
|
||||
|
||||
enum class ExecutionMode {
|
||||
// Allows tasks to start running while tasks are being posted by posting
|
||||
// threads.
|
||||
kPostAndRun,
|
||||
// Uses an execution fence to wait for all posting threads to be done before
|
||||
// running tasks that were posted.
|
||||
kPostThenRun,
|
||||
};
|
||||
|
||||
// A thread that waits for the caller to signal an event before proceeding to
|
||||
// call action.Run().
|
||||
class PostingThread : public SimpleThread {
|
||||
public:
|
||||
// Creates a PostingThread that waits on |start_event| before calling
|
||||
// action.Run().
|
||||
PostingThread(WaitableEvent* start_event,
|
||||
base::OnceClosure action,
|
||||
base::OnceClosure completion)
|
||||
: SimpleThread("PostingThread"),
|
||||
start_event_(start_event),
|
||||
action_(std::move(action)),
|
||||
completion_(std::move(completion)) {
|
||||
Start();
|
||||
}
|
||||
|
||||
void Run() override {
|
||||
start_event_->Wait();
|
||||
std::move(action_).Run();
|
||||
std::move(completion_).Run();
|
||||
}
|
||||
|
||||
private:
|
||||
WaitableEvent* const start_event_;
|
||||
base::OnceClosure action_;
|
||||
base::OnceClosure completion_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(PostingThread);
|
||||
};
|
||||
|
||||
class ThreadPoolPerfTest : public testing::Test {
|
||||
public:
|
||||
// Posting actions:
|
||||
|
||||
void ContinuouslyBindAndPostNoOpTasks(size_t num_tasks) {
|
||||
scoped_refptr<TaskRunner> task_runner = ThreadPool::CreateTaskRunner({});
|
||||
for (size_t i = 0; i < num_tasks; ++i) {
|
||||
++num_tasks_pending_;
|
||||
++num_posted_tasks_;
|
||||
task_runner->PostTask(FROM_HERE,
|
||||
base::BindOnce(
|
||||
[](std::atomic_size_t* num_task_pending) {
|
||||
(*num_task_pending)--;
|
||||
},
|
||||
&num_tasks_pending_));
|
||||
}
|
||||
}
|
||||
|
||||
void ContinuouslyPostNoOpTasks(size_t num_tasks) {
|
||||
scoped_refptr<TaskRunner> task_runner = ThreadPool::CreateTaskRunner({});
|
||||
base::RepeatingClosure closure = base::BindRepeating(
|
||||
[](std::atomic_size_t* num_task_pending) { (*num_task_pending)--; },
|
||||
&num_tasks_pending_);
|
||||
for (size_t i = 0; i < num_tasks; ++i) {
|
||||
++num_tasks_pending_;
|
||||
++num_posted_tasks_;
|
||||
task_runner->PostTask(FROM_HERE, closure);
|
||||
}
|
||||
}
|
||||
|
||||
void ContinuouslyPostBusyWaitTasks(size_t num_tasks,
|
||||
base::TimeDelta duration) {
|
||||
scoped_refptr<TaskRunner> task_runner = ThreadPool::CreateTaskRunner({});
|
||||
base::RepeatingClosure closure = base::BindRepeating(
|
||||
[](std::atomic_size_t* num_task_pending, base::TimeDelta duration) {
|
||||
base::TimeTicks end_time = base::TimeTicks::Now() + duration;
|
||||
while (base::TimeTicks::Now() < end_time)
|
||||
;
|
||||
(*num_task_pending)--;
|
||||
},
|
||||
Unretained(&num_tasks_pending_), duration);
|
||||
for (size_t i = 0; i < num_tasks; ++i) {
|
||||
++num_tasks_pending_;
|
||||
++num_posted_tasks_;
|
||||
task_runner->PostTask(FROM_HERE, closure);
|
||||
}
|
||||
}
|
||||
|
||||
protected:
|
||||
ThreadPoolPerfTest() { ThreadPoolInstance::Create("PerfTest"); }
|
||||
|
||||
~ThreadPoolPerfTest() override { ThreadPoolInstance::Set(nullptr); }
|
||||
|
||||
void StartThreadPool(size_t num_running_threads,
|
||||
size_t num_posting_threads,
|
||||
base::RepeatingClosure post_action) {
|
||||
ThreadPoolInstance::Get()->Start({num_running_threads});
|
||||
|
||||
base::RepeatingClosure done = BarrierClosure(
|
||||
num_posting_threads,
|
||||
base::BindOnce(&ThreadPoolPerfTest::OnCompletePostingTasks,
|
||||
base::Unretained(this)));
|
||||
|
||||
for (size_t i = 0; i < num_posting_threads; ++i) {
|
||||
threads_.emplace_back(std::make_unique<PostingThread>(
|
||||
&start_posting_tasks_, post_action, done));
|
||||
}
|
||||
}
|
||||
|
||||
void OnCompletePostingTasks() { complete_posting_tasks_.Signal(); }
|
||||
|
||||
void Benchmark(const std::string& story_name, ExecutionMode execution_mode) {
|
||||
base::Optional<ThreadPoolInstance::ScopedExecutionFence> execution_fence;
|
||||
if (execution_mode == ExecutionMode::kPostThenRun) {
|
||||
execution_fence.emplace();
|
||||
}
|
||||
TimeTicks tasks_run_start = TimeTicks::Now();
|
||||
start_posting_tasks_.Signal();
|
||||
complete_posting_tasks_.Wait();
|
||||
post_task_duration_ = TimeTicks::Now() - tasks_run_start;
|
||||
|
||||
if (execution_mode == ExecutionMode::kPostThenRun) {
|
||||
tasks_run_start = TimeTicks::Now();
|
||||
execution_fence.reset();
|
||||
}
|
||||
|
||||
// Wait for no pending tasks.
|
||||
ThreadPoolInstance::Get()->FlushForTesting();
|
||||
tasks_run_duration_ = TimeTicks::Now() - tasks_run_start;
|
||||
ASSERT_EQ(0U, num_tasks_pending_);
|
||||
|
||||
for (auto& thread : threads_)
|
||||
thread->Join();
|
||||
ThreadPoolInstance::Get()->JoinForTesting();
|
||||
|
||||
auto reporter = SetUpReporter(story_name);
|
||||
reporter.AddResult(
|
||||
kMetricPostTaskThroughput,
|
||||
num_posted_tasks_ /
|
||||
static_cast<double>(post_task_duration_.InSecondsF()));
|
||||
reporter.AddResult(
|
||||
kMetricRunTaskThroughput,
|
||||
num_posted_tasks_ /
|
||||
static_cast<double>(tasks_run_duration_.InSecondsF()));
|
||||
reporter.AddResult(kMetricNumTasksPosted, num_posted_tasks_);
|
||||
}
|
||||
|
||||
private:
|
||||
WaitableEvent start_posting_tasks_;
|
||||
WaitableEvent complete_posting_tasks_;
|
||||
|
||||
TimeDelta post_task_duration_;
|
||||
TimeDelta tasks_run_duration_;
|
||||
|
||||
std::atomic_size_t num_tasks_pending_{0};
|
||||
std::atomic_size_t num_posted_tasks_{0};
|
||||
|
||||
std::vector<std::unique_ptr<PostingThread>> threads_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(ThreadPoolPerfTest);
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
TEST_F(ThreadPoolPerfTest, BindPostThenRunNoOpTasks) {
|
||||
StartThreadPool(
|
||||
1, 1,
|
||||
BindRepeating(&ThreadPoolPerfTest::ContinuouslyBindAndPostNoOpTasks,
|
||||
Unretained(this), 10000));
|
||||
Benchmark(kStoryBindPostThenRunNoOp, ExecutionMode::kPostThenRun);
|
||||
}
|
||||
|
||||
TEST_F(ThreadPoolPerfTest, PostThenRunNoOpTasks) {
|
||||
StartThreadPool(1, 1,
|
||||
BindRepeating(&ThreadPoolPerfTest::ContinuouslyPostNoOpTasks,
|
||||
Unretained(this), 10000));
|
||||
Benchmark(kStoryPostThenRunNoOp, ExecutionMode::kPostThenRun);
|
||||
}
|
||||
|
||||
TEST_F(ThreadPoolPerfTest, PostThenRunNoOpTasksManyThreads) {
|
||||
StartThreadPool(4, 4,
|
||||
BindRepeating(&ThreadPoolPerfTest::ContinuouslyPostNoOpTasks,
|
||||
Unretained(this), 10000));
|
||||
Benchmark(kStoryPostThenRunNoOpManyThreads, ExecutionMode::kPostThenRun);
|
||||
}
|
||||
|
||||
TEST_F(ThreadPoolPerfTest, PostThenRunNoOpTasksMorePostingThanRunningThreads) {
|
||||
StartThreadPool(1, 4,
|
||||
BindRepeating(&ThreadPoolPerfTest::ContinuouslyPostNoOpTasks,
|
||||
Unretained(this), 10000));
|
||||
Benchmark(kStoryPostThenRunNoOpMoreThanRunningThreads,
|
||||
ExecutionMode::kPostThenRun);
|
||||
}
|
||||
|
||||
TEST_F(ThreadPoolPerfTest, PostRunNoOpTasks) {
|
||||
StartThreadPool(1, 1,
|
||||
BindRepeating(&ThreadPoolPerfTest::ContinuouslyPostNoOpTasks,
|
||||
Unretained(this), 10000));
|
||||
Benchmark(kStoryPostRunNoOp, ExecutionMode::kPostAndRun);
|
||||
}
|
||||
|
||||
TEST_F(ThreadPoolPerfTest, PostRunNoOpTasksManyThreads) {
|
||||
StartThreadPool(4, 4,
|
||||
BindRepeating(&ThreadPoolPerfTest::ContinuouslyPostNoOpTasks,
|
||||
Unretained(this), 10000));
|
||||
Benchmark(kStoryPostRunNoOpManyThreads, ExecutionMode::kPostAndRun);
|
||||
}
|
||||
|
||||
TEST_F(ThreadPoolPerfTest, PostRunBusyTasksManyThreads) {
|
||||
StartThreadPool(
|
||||
4, 4,
|
||||
BindRepeating(&ThreadPoolPerfTest::ContinuouslyPostBusyWaitTasks,
|
||||
Unretained(this), 10000,
|
||||
base::TimeDelta::FromMicroseconds(200)));
|
||||
Benchmark(kStoryPostRunBusyManyThreads, ExecutionMode::kPostAndRun);
|
||||
}
|
||||
|
||||
} // namespace internal
|
||||
} // namespace base
|
|
@ -1,250 +0,0 @@
|
|||
// Copyright 2019 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include <stddef.h>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
#include "base/barrier_closure.h"
|
||||
#include "base/bind.h"
|
||||
#include "base/bind_helpers.h"
|
||||
#include "base/callback.h"
|
||||
#include "base/synchronization/waitable_event.h"
|
||||
#include "base/test/bind_test_util.h"
|
||||
#include "base/threading/simple_thread.h"
|
||||
#include "base/threading/thread_local_storage.h"
|
||||
#include "base/time/time.h"
|
||||
#include "build/build_config.h"
|
||||
#include "testing/gtest/include/gtest/gtest.h"
|
||||
#include "testing/perf/perf_result_reporter.h"
|
||||
|
||||
#if defined(OS_WIN)
|
||||
#include <windows.h>
|
||||
#include "base/win/windows_types.h"
|
||||
#elif defined(OS_POSIX) || defined(OS_FUCHSIA)
|
||||
#include <pthread.h>
|
||||
#endif
|
||||
|
||||
namespace base {
|
||||
namespace internal {
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr char kMetricPrefixThreadLocalStorage[] = "ThreadLocalStorage.";
|
||||
constexpr char kMetricBaseRead[] = "read";
|
||||
constexpr char kMetricBaseWrite[] = "write";
|
||||
constexpr char kMetricBaseReadWrite[] = "read_write";
|
||||
constexpr char kMetricSuffixThroughput[] = "_throughput";
|
||||
constexpr char kMetricSuffixOperationTime[] = "_operation_time";
|
||||
constexpr char kStoryBaseTLS[] = "thread_local_storage";
|
||||
#if defined(OS_WIN)
|
||||
constexpr char kStoryBasePlatformFLS[] = "platform_fiber_local_storage";
|
||||
#endif // defined(OS_WIN)
|
||||
constexpr char kStoryBasePlatformTLS[] = "platform_thread_local_storage";
|
||||
constexpr char kStoryBaseCPPTLS[] = "c++_platform_thread_local_storage";
|
||||
constexpr char kStorySuffixFourThreads[] = "_4_threads";
|
||||
|
||||
perf_test::PerfResultReporter SetUpReporter(const std::string& story_name) {
|
||||
perf_test::PerfResultReporter reporter(kMetricPrefixThreadLocalStorage,
|
||||
story_name);
|
||||
reporter.RegisterImportantMetric(
|
||||
std::string(kMetricBaseRead) + kMetricSuffixThroughput, "runs/s");
|
||||
reporter.RegisterImportantMetric(
|
||||
std::string(kMetricBaseRead) + kMetricSuffixOperationTime, "ns");
|
||||
reporter.RegisterImportantMetric(
|
||||
std::string(kMetricBaseWrite) + kMetricSuffixThroughput, "runs/s");
|
||||
reporter.RegisterImportantMetric(
|
||||
std::string(kMetricBaseWrite) + kMetricSuffixOperationTime, "ns");
|
||||
reporter.RegisterImportantMetric(
|
||||
std::string(kMetricBaseReadWrite) + kMetricSuffixThroughput, "runs/s");
|
||||
reporter.RegisterImportantMetric(
|
||||
std::string(kMetricBaseReadWrite) + kMetricSuffixOperationTime, "ns");
|
||||
return reporter;
|
||||
}
|
||||
|
||||
// A thread that waits for the caller to signal an event before proceeding to
|
||||
// call action.Run().
|
||||
class TLSThread : public SimpleThread {
|
||||
public:
|
||||
// Creates a PostingThread that waits on |start_event| before calling
|
||||
// action.Run().
|
||||
TLSThread(WaitableEvent* start_event,
|
||||
base::OnceClosure action,
|
||||
base::OnceClosure completion)
|
||||
: SimpleThread("TLSThread"),
|
||||
start_event_(start_event),
|
||||
action_(std::move(action)),
|
||||
completion_(std::move(completion)) {
|
||||
Start();
|
||||
}
|
||||
|
||||
void Run() override {
|
||||
start_event_->Wait();
|
||||
std::move(action_).Run();
|
||||
std::move(completion_).Run();
|
||||
}
|
||||
|
||||
private:
|
||||
WaitableEvent* const start_event_;
|
||||
base::OnceClosure action_;
|
||||
base::OnceClosure completion_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(TLSThread);
|
||||
};
|
||||
|
||||
class ThreadLocalStoragePerfTest : public testing::Test {
|
||||
protected:
|
||||
ThreadLocalStoragePerfTest() = default;
|
||||
~ThreadLocalStoragePerfTest() override = default;
|
||||
|
||||
template <class Read, class Write>
|
||||
void Benchmark(const std::string& story_name,
|
||||
Read read,
|
||||
Write write,
|
||||
size_t num_operation,
|
||||
size_t num_threads) {
|
||||
write(2);
|
||||
|
||||
BenchmarkImpl(kMetricBaseRead, story_name,
|
||||
base::BindLambdaForTesting([&]() {
|
||||
volatile intptr_t total = 0;
|
||||
for (size_t i = 0; i < num_operation; ++i)
|
||||
total += read();
|
||||
}),
|
||||
num_operation, num_threads);
|
||||
|
||||
BenchmarkImpl(kMetricBaseWrite, story_name,
|
||||
base::BindLambdaForTesting([&]() {
|
||||
for (size_t i = 0; i < num_operation; ++i)
|
||||
write(i);
|
||||
}),
|
||||
num_operation, num_threads);
|
||||
|
||||
BenchmarkImpl(kMetricBaseReadWrite, story_name,
|
||||
base::BindLambdaForTesting([&]() {
|
||||
for (size_t i = 0; i < num_operation; ++i)
|
||||
write(read() + 1);
|
||||
}),
|
||||
num_operation, num_threads);
|
||||
}
|
||||
|
||||
void BenchmarkImpl(const std::string& metric_base,
|
||||
const std::string& story_name,
|
||||
base::RepeatingClosure action,
|
||||
size_t num_operation,
|
||||
size_t num_threads) {
|
||||
WaitableEvent start_thread;
|
||||
WaitableEvent complete_thread;
|
||||
|
||||
base::RepeatingClosure done = BarrierClosure(
|
||||
num_threads,
|
||||
base::BindLambdaForTesting([&]() { complete_thread.Signal(); }));
|
||||
|
||||
std::vector<std::unique_ptr<TLSThread>> threads;
|
||||
for (size_t i = 0; i < num_threads; ++i) {
|
||||
threads.emplace_back(
|
||||
std::make_unique<TLSThread>(&start_thread, action, done));
|
||||
}
|
||||
|
||||
TimeTicks operation_start = TimeTicks::Now();
|
||||
start_thread.Signal();
|
||||
complete_thread.Wait();
|
||||
TimeDelta operation_duration = TimeTicks::Now() - operation_start;
|
||||
|
||||
for (auto& thread : threads)
|
||||
thread->Join();
|
||||
|
||||
auto reporter = SetUpReporter(story_name);
|
||||
reporter.AddResult(metric_base + kMetricSuffixThroughput,
|
||||
num_operation / operation_duration.InSecondsF());
|
||||
size_t nanos_per_operation =
|
||||
operation_duration.InNanoseconds() / num_operation;
|
||||
reporter.AddResult(metric_base + kMetricSuffixOperationTime,
|
||||
nanos_per_operation);
|
||||
}
|
||||
|
||||
private:
|
||||
DISALLOW_COPY_AND_ASSIGN(ThreadLocalStoragePerfTest);
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
TEST_F(ThreadLocalStoragePerfTest, ThreadLocalStorage) {
|
||||
ThreadLocalStorage::Slot tls;
|
||||
auto read = [&]() { return reinterpret_cast<intptr_t>(tls.Get()); };
|
||||
auto write = [&](intptr_t value) { tls.Set(reinterpret_cast<void*>(value)); };
|
||||
|
||||
Benchmark(kStoryBaseTLS, read, write, 10000000, 1);
|
||||
Benchmark(std::string(kStoryBaseTLS) + kStorySuffixFourThreads, read, write,
|
||||
10000000, 4);
|
||||
}
|
||||
|
||||
#if defined(OS_WIN)
|
||||
|
||||
void WINAPI destroy(void*) {}
|
||||
|
||||
TEST_F(ThreadLocalStoragePerfTest, PlatformFls) {
|
||||
DWORD key = FlsAlloc(destroy);
|
||||
ASSERT_NE(PlatformThreadLocalStorage::TLS_KEY_OUT_OF_INDEXES, key);
|
||||
|
||||
auto read = [&]() { return reinterpret_cast<intptr_t>(FlsGetValue(key)); };
|
||||
auto write = [&](intptr_t value) {
|
||||
FlsSetValue(key, reinterpret_cast<void*>(value));
|
||||
};
|
||||
|
||||
Benchmark(kStoryBasePlatformFLS, read, write, 10000000, 1);
|
||||
Benchmark(std::string(kStoryBasePlatformFLS) + kStorySuffixFourThreads, read,
|
||||
write, 10000000, 4);
|
||||
}
|
||||
|
||||
TEST_F(ThreadLocalStoragePerfTest, PlatformTls) {
|
||||
DWORD key = TlsAlloc();
|
||||
ASSERT_NE(PlatformThreadLocalStorage::TLS_KEY_OUT_OF_INDEXES, key);
|
||||
|
||||
auto read = [&]() { return reinterpret_cast<intptr_t>(TlsGetValue(key)); };
|
||||
auto write = [&](intptr_t value) {
|
||||
TlsSetValue(key, reinterpret_cast<void*>(value));
|
||||
};
|
||||
|
||||
Benchmark(kStoryBasePlatformTLS, read, write, 10000000, 1);
|
||||
Benchmark(std::string(kStoryBasePlatformTLS) + kStorySuffixFourThreads, read,
|
||||
write, 10000000, 4);
|
||||
}
|
||||
|
||||
#elif defined(OS_POSIX) || defined(OS_FUCHSIA)
|
||||
|
||||
TEST_F(ThreadLocalStoragePerfTest, PlatformTls) {
|
||||
pthread_key_t key;
|
||||
ASSERT_FALSE(pthread_key_create(&key, [](void*) {}));
|
||||
ASSERT_NE(PlatformThreadLocalStorage::TLS_KEY_OUT_OF_INDEXES, key);
|
||||
|
||||
auto read = [&]() {
|
||||
return reinterpret_cast<intptr_t>(pthread_getspecific(key));
|
||||
};
|
||||
auto write = [&](intptr_t value) {
|
||||
pthread_setspecific(key, reinterpret_cast<void*>(value));
|
||||
};
|
||||
|
||||
Benchmark(kStoryBasePlatformTLS, read, write, 10000000, 1);
|
||||
Benchmark(std::string(kStoryBasePlatformTLS) + kStorySuffixFourThreads, read,
|
||||
write, 10000000, 4);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
TEST_F(ThreadLocalStoragePerfTest, Cpp11Tls) {
|
||||
thread_local intptr_t thread_local_variable;
|
||||
|
||||
auto read = [&]() { return thread_local_variable; };
|
||||
auto write = [&](intptr_t value) {
|
||||
reinterpret_cast<volatile intptr_t*>(&thread_local_variable)[0] = value;
|
||||
};
|
||||
|
||||
Benchmark(kStoryBaseCPPTLS, read, write, 10000000, 1);
|
||||
Benchmark(std::string(kStoryBaseCPPTLS) + kStorySuffixFourThreads, read,
|
||||
write, 10000000, 4);
|
||||
}
|
||||
|
||||
} // namespace internal
|
||||
} // namespace base
|
|
@ -1,349 +0,0 @@
|
|||
// Copyright 2014 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
#include "base/base_switches.h"
|
||||
#include "base/bind.h"
|
||||
#include "base/command_line.h"
|
||||
#include "base/location.h"
|
||||
#include "base/memory/ptr_util.h"
|
||||
#include "base/message_loop/message_loop_current.h"
|
||||
#include "base/single_thread_task_runner.h"
|
||||
#include "base/strings/stringprintf.h"
|
||||
#include "base/synchronization/condition_variable.h"
|
||||
#include "base/synchronization/lock.h"
|
||||
#include "base/synchronization/waitable_event.h"
|
||||
#include "base/task/task_observer.h"
|
||||
#include "base/threading/thread.h"
|
||||
#include "base/time/time.h"
|
||||
#include "build/build_config.h"
|
||||
#include "testing/gtest/include/gtest/gtest.h"
|
||||
#include "testing/perf/perf_result_reporter.h"
|
||||
|
||||
#if defined(OS_POSIX)
|
||||
#include <pthread.h>
|
||||
#endif
|
||||
|
||||
namespace base {
|
||||
|
||||
namespace {
|
||||
|
||||
const int kNumRuns = 100000;
|
||||
|
||||
constexpr char kMetricPrefixThread[] = "Thread.";
|
||||
constexpr char kMetricClockTimePerHop[] = "wall_time_per_hop";
|
||||
constexpr char kMetricCpuTimePerHop[] = "cpu_time_per_hop";
|
||||
constexpr char kStoryBaseTask[] = "task";
|
||||
constexpr char kStoryBaseTaskWithObserver[] = "task_with_observer";
|
||||
constexpr char kStoryBaseWaitableEvent[] = "waitable_event";
|
||||
constexpr char kStoryBaseCondVar[] = "condition_variable";
|
||||
constexpr char kStorySuffixOneThread[] = "_1_thread";
|
||||
constexpr char kStorySuffixFourThreads[] = "_4_threads";
|
||||
|
||||
#if defined(OS_POSIX)
|
||||
constexpr char kStoryBasePthreadCondVar[] = "pthread_condition_variable";
|
||||
#endif // defined(OS_POSIX)
|
||||
|
||||
perf_test::PerfResultReporter SetUpReporter(const std::string& story_name) {
|
||||
perf_test::PerfResultReporter reporter(kMetricPrefixThread, story_name);
|
||||
reporter.RegisterImportantMetric(kMetricClockTimePerHop, "us");
|
||||
reporter.RegisterImportantMetric(kMetricCpuTimePerHop, "us");
|
||||
return reporter;
|
||||
}
|
||||
|
||||
// Base class for a threading perf-test. This sets up some threads for the
|
||||
// test and measures the clock-time in addition to time spent on each thread.
|
||||
class ThreadPerfTest : public testing::Test {
|
||||
public:
|
||||
ThreadPerfTest()
|
||||
: done_(WaitableEvent::ResetPolicy::AUTOMATIC,
|
||||
WaitableEvent::InitialState::NOT_SIGNALED) {}
|
||||
|
||||
// To be implemented by each test. Subclass must uses threads_ such that
|
||||
// their cpu-time can be measured. Test must return from PingPong() _and_
|
||||
// call FinishMeasurement from any thread to complete the test.
|
||||
virtual void Init() {
|
||||
if (ThreadTicks::IsSupported())
|
||||
ThreadTicks::WaitUntilInitialized();
|
||||
}
|
||||
virtual void PingPong(int hops) = 0;
|
||||
virtual void Reset() {}
|
||||
|
||||
void TimeOnThread(base::ThreadTicks* ticks, base::WaitableEvent* done) {
|
||||
*ticks = base::ThreadTicks::Now();
|
||||
done->Signal();
|
||||
}
|
||||
|
||||
base::ThreadTicks ThreadNow(const base::Thread& thread) {
|
||||
base::WaitableEvent done(WaitableEvent::ResetPolicy::AUTOMATIC,
|
||||
WaitableEvent::InitialState::NOT_SIGNALED);
|
||||
base::ThreadTicks ticks;
|
||||
thread.task_runner()->PostTask(
|
||||
FROM_HERE, base::BindOnce(&ThreadPerfTest::TimeOnThread,
|
||||
base::Unretained(this), &ticks, &done));
|
||||
done.Wait();
|
||||
return ticks;
|
||||
}
|
||||
|
||||
void RunPingPongTest(const std::string& story_name, unsigned num_threads) {
|
||||
// Create threads and collect starting cpu-time for each thread.
|
||||
std::vector<base::ThreadTicks> thread_starts;
|
||||
while (threads_.size() < num_threads) {
|
||||
threads_.push_back(std::make_unique<base::Thread>("PingPonger"));
|
||||
threads_.back()->Start();
|
||||
if (base::ThreadTicks::IsSupported())
|
||||
thread_starts.push_back(ThreadNow(*threads_.back()));
|
||||
}
|
||||
|
||||
Init();
|
||||
|
||||
base::TimeTicks start = base::TimeTicks::Now();
|
||||
PingPong(kNumRuns);
|
||||
done_.Wait();
|
||||
base::TimeTicks end = base::TimeTicks::Now();
|
||||
|
||||
// Gather the cpu-time spent on each thread. This does one extra tasks,
|
||||
// but that should be in the noise given enough runs.
|
||||
base::TimeDelta thread_time;
|
||||
while (threads_.size()) {
|
||||
if (base::ThreadTicks::IsSupported()) {
|
||||
thread_time += ThreadNow(*threads_.back()) - thread_starts.back();
|
||||
thread_starts.pop_back();
|
||||
}
|
||||
threads_.pop_back();
|
||||
}
|
||||
|
||||
Reset();
|
||||
|
||||
double num_runs = static_cast<double>(kNumRuns);
|
||||
double us_per_task_clock = (end - start).InMicroseconds() / num_runs;
|
||||
double us_per_task_cpu = thread_time.InMicroseconds() / num_runs;
|
||||
|
||||
auto reporter = SetUpReporter(story_name);
|
||||
// Clock time per task.
|
||||
reporter.AddResult(kMetricClockTimePerHop, us_per_task_clock);
|
||||
|
||||
// Total utilization across threads if available (likely higher).
|
||||
if (base::ThreadTicks::IsSupported()) {
|
||||
reporter.AddResult(kMetricCpuTimePerHop, us_per_task_cpu);
|
||||
}
|
||||
}
|
||||
|
||||
protected:
|
||||
void FinishMeasurement() { done_.Signal(); }
|
||||
std::vector<std::unique_ptr<base::Thread>> threads_;
|
||||
|
||||
private:
|
||||
base::WaitableEvent done_;
|
||||
};
|
||||
|
||||
// Class to test task performance by posting empty tasks back and forth.
|
||||
class TaskPerfTest : public ThreadPerfTest {
|
||||
base::Thread* NextThread(int count) {
|
||||
return threads_[count % threads_.size()].get();
|
||||
}
|
||||
|
||||
void PingPong(int hops) override {
|
||||
if (!hops) {
|
||||
FinishMeasurement();
|
||||
return;
|
||||
}
|
||||
NextThread(hops)->task_runner()->PostTask(
|
||||
FROM_HERE, base::BindOnce(&ThreadPerfTest::PingPong,
|
||||
base::Unretained(this), hops - 1));
|
||||
}
|
||||
};
|
||||
|
||||
// This tries to test the 'best-case' as well as the 'worst-case' task posting
|
||||
// performance. The best-case keeps one thread alive such that it never yeilds,
|
||||
// while the worse-case forces a context switch for every task. Four threads are
|
||||
// used to ensure the threads do yeild (with just two it might be possible for
|
||||
// both threads to stay awake if they can signal each other fast enough).
|
||||
TEST_F(TaskPerfTest, TaskPingPong) {
|
||||
RunPingPongTest(std::string(kStoryBaseTask) + kStorySuffixOneThread, 1);
|
||||
RunPingPongTest(std::string(kStoryBaseTask) + kStorySuffixFourThreads, 4);
|
||||
}
|
||||
|
||||
|
||||
// Same as above, but add observers to test their perf impact.
|
||||
class MessageLoopObserver : public base::TaskObserver {
|
||||
public:
|
||||
void WillProcessTask(const base::PendingTask& pending_task,
|
||||
bool was_blocked_or_low_priority) override {}
|
||||
void DidProcessTask(const base::PendingTask& pending_task) override {}
|
||||
};
|
||||
MessageLoopObserver message_loop_observer;
|
||||
|
||||
class TaskObserverPerfTest : public TaskPerfTest {
|
||||
public:
|
||||
void Init() override {
|
||||
TaskPerfTest::Init();
|
||||
for (auto& i : threads_) {
|
||||
i->task_runner()->PostTask(
|
||||
FROM_HERE, BindOnce(
|
||||
[](MessageLoopObserver* observer) {
|
||||
MessageLoopCurrent::Get()->AddTaskObserver(observer);
|
||||
},
|
||||
Unretained(&message_loop_observer)));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
TEST_F(TaskObserverPerfTest, TaskPingPong) {
|
||||
RunPingPongTest(
|
||||
std::string(kStoryBaseTaskWithObserver) + kStorySuffixOneThread, 1);
|
||||
RunPingPongTest(
|
||||
std::string(kStoryBaseTaskWithObserver) + kStorySuffixFourThreads, 4);
|
||||
}
|
||||
|
||||
// Class to test our WaitableEvent performance by signaling back and fort.
|
||||
// WaitableEvent is templated so we can also compare with other versions.
|
||||
template <typename WaitableEventType>
|
||||
class EventPerfTest : public ThreadPerfTest {
|
||||
public:
|
||||
void Init() override {
|
||||
for (size_t i = 0; i < threads_.size(); i++) {
|
||||
events_.push_back(std::make_unique<WaitableEventType>(
|
||||
WaitableEvent::ResetPolicy::AUTOMATIC,
|
||||
WaitableEvent::InitialState::NOT_SIGNALED));
|
||||
}
|
||||
}
|
||||
|
||||
void Reset() override { events_.clear(); }
|
||||
|
||||
void WaitAndSignalOnThread(size_t event) {
|
||||
size_t next_event = (event + 1) % events_.size();
|
||||
int my_hops = 0;
|
||||
do {
|
||||
events_[event]->Wait();
|
||||
my_hops = --remaining_hops_; // We own 'hops' between Wait and Signal.
|
||||
events_[next_event]->Signal();
|
||||
} while (my_hops > 0);
|
||||
// Once we are done, all threads will signal as hops passes zero.
|
||||
// We only signal completion once, on the thread that reaches zero.
|
||||
if (!my_hops)
|
||||
FinishMeasurement();
|
||||
}
|
||||
|
||||
void PingPong(int hops) override {
|
||||
remaining_hops_ = hops;
|
||||
for (size_t i = 0; i < threads_.size(); i++) {
|
||||
threads_[i]->task_runner()->PostTask(
|
||||
FROM_HERE, base::BindOnce(&EventPerfTest::WaitAndSignalOnThread,
|
||||
base::Unretained(this), i));
|
||||
}
|
||||
|
||||
// Kick off the Signal ping-ponging.
|
||||
events_.front()->Signal();
|
||||
}
|
||||
|
||||
int remaining_hops_;
|
||||
std::vector<std::unique_ptr<WaitableEventType>> events_;
|
||||
};
|
||||
|
||||
// Similar to the task posting test, this just tests similar functionality
|
||||
// using WaitableEvents. We only test four threads (worst-case), but we
|
||||
// might want to craft a way to test the best-case (where the thread doesn't
|
||||
// end up blocking because the event is already signalled).
|
||||
typedef EventPerfTest<base::WaitableEvent> WaitableEventThreadPerfTest;
|
||||
TEST_F(WaitableEventThreadPerfTest, EventPingPong) {
|
||||
RunPingPongTest(
|
||||
std::string(kStoryBaseWaitableEvent) + kStorySuffixFourThreads, 4);
|
||||
}
|
||||
|
||||
// Build a minimal event using ConditionVariable.
|
||||
class ConditionVariableEvent {
|
||||
public:
|
||||
ConditionVariableEvent(WaitableEvent::ResetPolicy reset_policy,
|
||||
WaitableEvent::InitialState initial_state)
|
||||
: cond_(&lock_), signaled_(false) {
|
||||
DCHECK_EQ(WaitableEvent::ResetPolicy::AUTOMATIC, reset_policy);
|
||||
DCHECK_EQ(WaitableEvent::InitialState::NOT_SIGNALED, initial_state);
|
||||
}
|
||||
|
||||
void Signal() {
|
||||
{
|
||||
base::AutoLock scoped_lock(lock_);
|
||||
signaled_ = true;
|
||||
}
|
||||
cond_.Signal();
|
||||
}
|
||||
|
||||
void Wait() {
|
||||
base::AutoLock scoped_lock(lock_);
|
||||
while (!signaled_)
|
||||
cond_.Wait();
|
||||
signaled_ = false;
|
||||
}
|
||||
|
||||
private:
|
||||
base::Lock lock_;
|
||||
base::ConditionVariable cond_;
|
||||
bool signaled_;
|
||||
};
|
||||
|
||||
// This is meant to test the absolute minimal context switching time
|
||||
// using our own base synchronization code.
|
||||
typedef EventPerfTest<ConditionVariableEvent> ConditionVariablePerfTest;
|
||||
TEST_F(ConditionVariablePerfTest, EventPingPong) {
|
||||
RunPingPongTest(std::string(kStoryBaseCondVar) + kStorySuffixFourThreads, 4);
|
||||
}
|
||||
#if defined(OS_POSIX)
|
||||
|
||||
// Absolutely 100% minimal posix waitable event. If there is a better/faster
|
||||
// way to force a context switch, we should use that instead.
|
||||
class PthreadEvent {
|
||||
public:
|
||||
PthreadEvent(WaitableEvent::ResetPolicy reset_policy,
|
||||
WaitableEvent::InitialState initial_state) {
|
||||
DCHECK_EQ(WaitableEvent::ResetPolicy::AUTOMATIC, reset_policy);
|
||||
DCHECK_EQ(WaitableEvent::InitialState::NOT_SIGNALED, initial_state);
|
||||
pthread_mutex_init(&mutex_, nullptr);
|
||||
pthread_cond_init(&cond_, nullptr);
|
||||
signaled_ = false;
|
||||
}
|
||||
|
||||
~PthreadEvent() {
|
||||
pthread_cond_destroy(&cond_);
|
||||
pthread_mutex_destroy(&mutex_);
|
||||
}
|
||||
|
||||
void Signal() {
|
||||
pthread_mutex_lock(&mutex_);
|
||||
signaled_ = true;
|
||||
pthread_mutex_unlock(&mutex_);
|
||||
pthread_cond_signal(&cond_);
|
||||
}
|
||||
|
||||
void Wait() {
|
||||
pthread_mutex_lock(&mutex_);
|
||||
while (!signaled_)
|
||||
pthread_cond_wait(&cond_, &mutex_);
|
||||
signaled_ = false;
|
||||
pthread_mutex_unlock(&mutex_);
|
||||
}
|
||||
|
||||
private:
|
||||
bool signaled_;
|
||||
pthread_mutex_t mutex_;
|
||||
pthread_cond_t cond_;
|
||||
};
|
||||
|
||||
// This is meant to test the absolute minimal context switching time.
|
||||
// If there is any faster way to do this we should substitute it in.
|
||||
typedef EventPerfTest<PthreadEvent> PthreadEventPerfTest;
|
||||
TEST_F(PthreadEventPerfTest, EventPingPong) {
|
||||
RunPingPongTest(
|
||||
std::string(kStoryBasePthreadCondVar) + kStorySuffixFourThreads, 4);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
} // namespace
|
||||
|
||||
} // namespace base
|
|
@ -32,8 +32,7 @@ class BroadcastResourceListener::AdapterResource : public Resource {
|
|||
MutexLock lock(&lock_);
|
||||
if (!listener_)
|
||||
return;
|
||||
listener_->OnResourceUsageStateMeasured(rtc::scoped_refptr<Resource>(this),
|
||||
usage_state);
|
||||
listener_->OnResourceUsageStateMeasured(this, usage_state);
|
||||
}
|
||||
|
||||
// Resource implementation.
|
||||
|
|
|
@ -27,8 +27,14 @@ namespace webrtc {
|
|||
|
||||
ResourceAdaptationProcessor::ResourceListenerDelegate::ResourceListenerDelegate(
|
||||
ResourceAdaptationProcessor* processor)
|
||||
: task_queue_(TaskQueueBase::Current()), processor_(processor) {
|
||||
RTC_DCHECK(task_queue_);
|
||||
: task_queue_(nullptr), processor_(processor) {}
|
||||
|
||||
void ResourceAdaptationProcessor::ResourceListenerDelegate::SetTaskQueue(
|
||||
TaskQueueBase* task_queue) {
|
||||
RTC_DCHECK(!task_queue_);
|
||||
RTC_DCHECK(task_queue);
|
||||
task_queue_ = task_queue;
|
||||
RTC_DCHECK_RUN_ON(task_queue_);
|
||||
}
|
||||
|
||||
void ResourceAdaptationProcessor::ResourceListenerDelegate::
|
||||
|
@ -64,15 +70,14 @@ ResourceAdaptationProcessor::MitigationResultAndLogMessage::
|
|||
|
||||
ResourceAdaptationProcessor::ResourceAdaptationProcessor(
|
||||
VideoStreamAdapter* stream_adapter)
|
||||
: task_queue_(TaskQueueBase::Current()),
|
||||
: task_queue_(nullptr),
|
||||
resource_listener_delegate_(
|
||||
rtc::make_ref_counted<ResourceListenerDelegate>(this)),
|
||||
resources_(),
|
||||
stream_adapter_(stream_adapter),
|
||||
last_reported_source_restrictions_(),
|
||||
previous_mitigation_results_() {
|
||||
RTC_DCHECK(task_queue_);
|
||||
stream_adapter_->AddRestrictionsListener(this);
|
||||
RTC_DCHECK(stream_adapter_);
|
||||
}
|
||||
|
||||
ResourceAdaptationProcessor::~ResourceAdaptationProcessor() {
|
||||
|
@ -84,6 +89,16 @@ ResourceAdaptationProcessor::~ResourceAdaptationProcessor() {
|
|||
resource_listener_delegate_->OnProcessorDestroyed();
|
||||
}
|
||||
|
||||
void ResourceAdaptationProcessor::SetTaskQueue(TaskQueueBase* task_queue) {
|
||||
RTC_DCHECK(!task_queue_);
|
||||
RTC_DCHECK(task_queue);
|
||||
task_queue_ = task_queue;
|
||||
resource_listener_delegate_->SetTaskQueue(task_queue);
|
||||
RTC_DCHECK_RUN_ON(task_queue_);
|
||||
// Now that we have the queue we can attach as adaptation listener.
|
||||
stream_adapter_->AddRestrictionsListener(this);
|
||||
}
|
||||
|
||||
void ResourceAdaptationProcessor::AddResourceLimitationsListener(
|
||||
ResourceLimitationsListener* limitations_listener) {
|
||||
RTC_DCHECK_RUN_ON(task_queue_);
|
||||
|
|
|
@ -58,6 +58,8 @@ class ResourceAdaptationProcessor : public ResourceAdaptationProcessorInterface,
|
|||
VideoStreamAdapter* video_stream_adapter);
|
||||
~ResourceAdaptationProcessor() override;
|
||||
|
||||
void SetTaskQueue(TaskQueueBase* task_queue) override;
|
||||
|
||||
// ResourceAdaptationProcessorInterface implementation.
|
||||
void AddResourceLimitationsListener(
|
||||
ResourceLimitationsListener* limitations_listener) override;
|
||||
|
@ -88,6 +90,7 @@ class ResourceAdaptationProcessor : public ResourceAdaptationProcessorInterface,
|
|||
public:
|
||||
explicit ResourceListenerDelegate(ResourceAdaptationProcessor* processor);
|
||||
|
||||
void SetTaskQueue(TaskQueueBase* task_queue);
|
||||
void OnProcessorDestroyed();
|
||||
|
||||
// ResourceListener implementation.
|
||||
|
|
|
@ -47,6 +47,8 @@ class ResourceAdaptationProcessorInterface {
|
|||
public:
|
||||
virtual ~ResourceAdaptationProcessorInterface();
|
||||
|
||||
virtual void SetTaskQueue(TaskQueueBase* task_queue) = 0;
|
||||
|
||||
virtual void AddResourceLimitationsListener(
|
||||
ResourceLimitationsListener* limitations_listener) = 0;
|
||||
virtual void RemoveResourceLimitationsListener(
|
||||
|
|
|
@ -23,6 +23,7 @@
|
|||
#include "call/adaptation/video_source_restrictions.h"
|
||||
#include "call/adaptation/video_stream_input_state.h"
|
||||
#include "rtc_base/checks.h"
|
||||
#include "rtc_base/constructor_magic.h"
|
||||
#include "rtc_base/logging.h"
|
||||
#include "rtc_base/numerics/safe_conversions.h"
|
||||
|
||||
|
@ -374,7 +375,7 @@ VideoStreamAdapter::RestrictionsOrState VideoStreamAdapter::GetAdaptationUpStep(
|
|||
return increase_frame_rate;
|
||||
}
|
||||
// else, increase resolution.
|
||||
[[fallthrough]];
|
||||
ABSL_FALLTHROUGH_INTENDED;
|
||||
}
|
||||
case DegradationPreference::MAINTAIN_FRAMERATE: {
|
||||
// Attempt to increase pixel count.
|
||||
|
@ -458,7 +459,7 @@ VideoStreamAdapter::GetAdaptationDownStep(
|
|||
return decrease_frame_rate;
|
||||
}
|
||||
// else, decrease resolution.
|
||||
[[fallthrough]];
|
||||
ABSL_FALLTHROUGH_INTENDED;
|
||||
}
|
||||
case DegradationPreference::MAINTAIN_FRAMERATE: {
|
||||
return DecreaseResolution(input_state, current_restrictions);
|
||||
|
|
|
@ -51,6 +51,7 @@
|
|||
#include "modules/utility/include/process_thread.h"
|
||||
#include "modules/video_coding/fec_controller_default.h"
|
||||
#include "rtc_base/checks.h"
|
||||
#include "rtc_base/constructor_magic.h"
|
||||
#include "rtc_base/location.h"
|
||||
#include "rtc_base/logging.h"
|
||||
#include "rtc_base/strings/string_builder.h"
|
||||
|
@ -208,9 +209,6 @@ class Call final : public webrtc::Call,
|
|||
TaskQueueFactory* task_queue_factory);
|
||||
~Call() override;
|
||||
|
||||
Call(const Call&) = delete;
|
||||
Call& operator=(const Call&) = delete;
|
||||
|
||||
// Implements webrtc::Call.
|
||||
PacketReceiver* Receiver() override;
|
||||
|
||||
|
@ -346,21 +344,12 @@ class Call final : public webrtc::Call,
|
|||
DeliveryStatus DeliverRtp(MediaType media_type,
|
||||
rtc::CopyOnWriteBuffer packet,
|
||||
int64_t packet_time_us) RTC_RUN_ON(worker_thread_);
|
||||
|
||||
AudioReceiveStream* FindAudioStreamForSyncGroup(const std::string& sync_group)
|
||||
RTC_RUN_ON(worker_thread_);
|
||||
void ConfigureSync(const std::string& sync_group) RTC_RUN_ON(worker_thread_);
|
||||
|
||||
void NotifyBweOfReceivedPacket(const RtpPacketReceived& packet,
|
||||
MediaType media_type,
|
||||
bool use_send_side_bwe)
|
||||
MediaType media_type)
|
||||
RTC_RUN_ON(worker_thread_);
|
||||
|
||||
bool IdentifyReceivedPacket(RtpPacketReceived& packet,
|
||||
bool* use_send_side_bwe = nullptr);
|
||||
bool RegisterReceiveStream(uint32_t ssrc, ReceiveStream* stream);
|
||||
bool UnregisterReceiveStream(uint32_t ssrc);
|
||||
|
||||
void UpdateAggregateNetworkState();
|
||||
|
||||
// Ensure that necessary process threads are started, and any required
|
||||
|
@ -371,7 +360,6 @@ class Call final : public webrtc::Call,
|
|||
TaskQueueFactory* const task_queue_factory_;
|
||||
TaskQueueBase* const worker_thread_;
|
||||
TaskQueueBase* const network_thread_;
|
||||
const std::unique_ptr<DecodeSynchronizer> decode_sync_;
|
||||
RTC_NO_UNIQUE_ADDRESS SequenceChecker send_transport_sequence_checker_;
|
||||
|
||||
const int num_cpu_cores_;
|
||||
|
@ -394,11 +382,14 @@ class Call final : public webrtc::Call,
|
|||
// Audio, Video, and FlexFEC receive streams are owned by the client that
|
||||
// creates them.
|
||||
// TODO(bugs.webrtc.org/11993): Move audio_receive_streams_,
|
||||
// video_receive_streams_ over to the network thread.
|
||||
// video_receive_streams_ and sync_stream_mapping_ over to the network thread.
|
||||
std::set<AudioReceiveStream*> audio_receive_streams_
|
||||
RTC_GUARDED_BY(worker_thread_);
|
||||
std::set<VideoReceiveStream2*> video_receive_streams_
|
||||
RTC_GUARDED_BY(worker_thread_);
|
||||
std::map<std::string, AudioReceiveStream*> sync_stream_mapping_
|
||||
RTC_GUARDED_BY(worker_thread_);
|
||||
|
||||
// TODO(nisse): Should eventually be injected at creation,
|
||||
// with a single object in the bundled case.
|
||||
RtpStreamReceiverController audio_receiver_controller_
|
||||
|
@ -409,12 +400,10 @@ class Call final : public webrtc::Call,
|
|||
// This extra map is used for receive processing which is
|
||||
// independent of media type.
|
||||
|
||||
RTC_NO_UNIQUE_ADDRESS SequenceChecker receive_11993_checker_;
|
||||
|
||||
// TODO(bugs.webrtc.org/11993): Move receive_rtp_config_ over to the
|
||||
// network thread.
|
||||
std::map<uint32_t, ReceiveStream*> receive_rtp_config_
|
||||
RTC_GUARDED_BY(&receive_11993_checker_);
|
||||
RTC_GUARDED_BY(worker_thread_);
|
||||
|
||||
// Audio and Video send streams are owned by the client that creates them.
|
||||
std::map<uint32_t, AudioSendStream*> audio_send_ssrcs_
|
||||
|
@ -477,11 +466,11 @@ class Call final : public webrtc::Call,
|
|||
|
||||
bool is_started_ RTC_GUARDED_BY(worker_thread_) = false;
|
||||
|
||||
// Sequence checker for outgoing network traffic. Could be the network thread.
|
||||
// Could also be a pacer owned thread or TQ such as the TaskQueuePacedSender.
|
||||
RTC_NO_UNIQUE_ADDRESS SequenceChecker sent_packet_sequence_checker_;
|
||||
absl::optional<rtc::SentPacket> last_sent_packet_
|
||||
RTC_GUARDED_BY(sent_packet_sequence_checker_);
|
||||
|
||||
RTC_DISALLOW_COPY_AND_ASSIGN(Call);
|
||||
};
|
||||
} // namespace internal
|
||||
|
||||
|
@ -602,9 +591,8 @@ SharedModuleThread::~SharedModuleThread() = default;
|
|||
rtc::scoped_refptr<SharedModuleThread> SharedModuleThread::Create(
|
||||
std::unique_ptr<ProcessThread> process_thread,
|
||||
std::function<void()> on_one_ref_remaining) {
|
||||
// Using `new` to access a non-public constructor.
|
||||
return rtc::scoped_refptr<SharedModuleThread>(new SharedModuleThread(
|
||||
std::move(process_thread), std::move(on_one_ref_remaining)));
|
||||
return new SharedModuleThread(std::move(process_thread),
|
||||
std::move(on_one_ref_remaining));
|
||||
}
|
||||
|
||||
void SharedModuleThread::EnsureStarted() {
|
||||
|
@ -801,11 +789,6 @@ Call::Call(Clock* clock,
|
|||
// must be made on `worker_thread_` (i.e. they're one and the same).
|
||||
network_thread_(config.network_task_queue_ ? config.network_task_queue_
|
||||
: worker_thread_),
|
||||
decode_sync_(config.metronome
|
||||
? std::make_unique<DecodeSynchronizer>(clock_,
|
||||
config.metronome,
|
||||
worker_thread_)
|
||||
: nullptr),
|
||||
num_cpu_cores_(CpuInfo::DetectNumberOfCores()),
|
||||
module_process_thread_(std::move(module_process_thread)),
|
||||
call_stats_(new CallStats(clock_, worker_thread_)),
|
||||
|
@ -834,7 +817,6 @@ Call::Call(Clock* clock,
|
|||
RTC_DCHECK(network_thread_);
|
||||
RTC_DCHECK(worker_thread_->IsCurrent());
|
||||
|
||||
receive_11993_checker_.Detach();
|
||||
send_transport_sequence_checker_.Detach();
|
||||
sent_packet_sequence_checker_.Detach();
|
||||
|
||||
|
@ -982,7 +964,7 @@ webrtc::AudioReceiveStream* Call::CreateAudioReceiveStream(
|
|||
// TODO(bugs.webrtc.org/11993): Update the below on the network thread.
|
||||
// We could possibly set up the audio_receiver_controller_ association up
|
||||
// as part of the async setup.
|
||||
RegisterReceiveStream(config.rtp.remote_ssrc, receive_stream);
|
||||
receive_rtp_config_.emplace(config.rtp.remote_ssrc, receive_stream);
|
||||
|
||||
ConfigureSync(config.sync_group);
|
||||
|
||||
|
@ -1015,12 +997,12 @@ void Call::DestroyAudioReceiveStream(
|
|||
|
||||
audio_receive_streams_.erase(audio_receive_stream);
|
||||
|
||||
// After calling erase(), call ConfigureSync. This will clear associated
|
||||
// video streams or associate them with a different audio stream if one exists
|
||||
// for this sync_group.
|
||||
ConfigureSync(audio_receive_stream->config().sync_group);
|
||||
|
||||
UnregisterReceiveStream(ssrc);
|
||||
const auto it = sync_stream_mapping_.find(config.sync_group);
|
||||
if (it != sync_stream_mapping_.end() && it->second == audio_receive_stream) {
|
||||
sync_stream_mapping_.erase(it);
|
||||
ConfigureSync(config.sync_group);
|
||||
}
|
||||
receive_rtp_config_.erase(ssrc);
|
||||
|
||||
UpdateAggregateNetworkState();
|
||||
// TODO(bugs.webrtc.org/11993): Consider if deleting `audio_receive_stream`
|
||||
|
@ -1097,6 +1079,10 @@ void Call::DestroyVideoSendStream(webrtc::VideoSendStream* send_stream) {
|
|||
|
||||
VideoSendStream* send_stream_impl =
|
||||
static_cast<VideoSendStream*>(send_stream);
|
||||
VideoSendStream::RtpStateMap rtp_states;
|
||||
VideoSendStream::RtpPayloadStateMap rtp_payload_states;
|
||||
send_stream_impl->StopPermanentlyAndGetRtpStates(&rtp_states,
|
||||
&rtp_payload_states);
|
||||
|
||||
auto it = video_send_ssrcs_.begin();
|
||||
while (it != video_send_ssrcs_.end()) {
|
||||
|
@ -1116,10 +1102,6 @@ void Call::DestroyVideoSendStream(webrtc::VideoSendStream* send_stream) {
|
|||
if (video_send_streams_.empty())
|
||||
video_send_streams_empty_.store(true, std::memory_order_relaxed);
|
||||
|
||||
VideoSendStream::RtpStateMap rtp_states;
|
||||
VideoSendStream::RtpPayloadStateMap rtp_payload_states;
|
||||
send_stream_impl->StopPermanentlyAndGetRtpStates(&rtp_states,
|
||||
&rtp_payload_states);
|
||||
for (const auto& kv : rtp_states) {
|
||||
suspended_video_send_ssrcs_[kv.first] = kv.second;
|
||||
}
|
||||
|
@ -1154,7 +1136,7 @@ webrtc::VideoReceiveStream* Call::CreateVideoReceiveStream(
|
|||
task_queue_factory_, this, num_cpu_cores_,
|
||||
transport_send_->packet_router(), std::move(configuration),
|
||||
call_stats_.get(), clock_, new VCMTiming(clock_),
|
||||
&nack_periodic_processor_, decode_sync_.get());
|
||||
&nack_periodic_processor_);
|
||||
// TODO(bugs.webrtc.org/11993): Set this up asynchronously on the network
|
||||
// thread.
|
||||
receive_stream->RegisterWithTransport(&video_receiver_controller_);
|
||||
|
@ -1165,9 +1147,9 @@ webrtc::VideoReceiveStream* Call::CreateVideoReceiveStream(
|
|||
// stream. Since the transport_send_cc negotiation is per payload
|
||||
// type, we may get an incorrect value for the rtx stream, but
|
||||
// that is unlikely to matter in practice.
|
||||
RegisterReceiveStream(rtp.rtx_ssrc, receive_stream);
|
||||
receive_rtp_config_.emplace(rtp.rtx_ssrc, receive_stream);
|
||||
}
|
||||
RegisterReceiveStream(rtp.remote_ssrc, receive_stream);
|
||||
receive_rtp_config_.emplace(rtp.remote_ssrc, receive_stream);
|
||||
video_receive_streams_.insert(receive_stream);
|
||||
|
||||
ConfigureSync(receive_stream->sync_group());
|
||||
|
@ -1192,9 +1174,9 @@ void Call::DestroyVideoReceiveStream(
|
|||
|
||||
// Remove all ssrcs pointing to a receive stream. As RTX retransmits on a
|
||||
// separate SSRC there can be either one or two.
|
||||
UnregisterReceiveStream(rtp.remote_ssrc);
|
||||
receive_rtp_config_.erase(rtp.remote_ssrc);
|
||||
if (rtp.rtx_ssrc) {
|
||||
UnregisterReceiveStream(rtp.rtx_ssrc);
|
||||
receive_rtp_config_.erase(rtp.rtx_ssrc);
|
||||
}
|
||||
video_receive_streams_.erase(receive_stream_impl);
|
||||
ConfigureSync(receive_stream_impl->sync_group());
|
||||
|
@ -1227,7 +1209,10 @@ FlexfecReceiveStream* Call::CreateFlexfecReceiveStream(
|
|||
// TODO(bugs.webrtc.org/11993): Set this up asynchronously on the network
|
||||
// thread.
|
||||
receive_stream->RegisterWithTransport(&video_receiver_controller_);
|
||||
RegisterReceiveStream(config.rtp.remote_ssrc, receive_stream);
|
||||
|
||||
RTC_DCHECK(receive_rtp_config_.find(config.rtp.remote_ssrc) ==
|
||||
receive_rtp_config_.end());
|
||||
receive_rtp_config_.emplace(config.rtp.remote_ssrc, receive_stream);
|
||||
|
||||
// TODO(brandtr): Store config in RtcEventLog here.
|
||||
|
||||
|
@ -1245,7 +1230,7 @@ void Call::DestroyFlexfecReceiveStream(FlexfecReceiveStream* receive_stream) {
|
|||
|
||||
RTC_DCHECK(receive_stream != nullptr);
|
||||
const FlexfecReceiveStream::RtpConfig& rtp = receive_stream->rtp_config();
|
||||
UnregisterReceiveStream(rtp.remote_ssrc);
|
||||
receive_rtp_config_.erase(rtp.remote_ssrc);
|
||||
|
||||
// Remove all SSRCs pointing to the FlexfecReceiveStreamImpl to be
|
||||
// destroyed.
|
||||
|
@ -1465,37 +1450,51 @@ void Call::OnAllocationLimitsChanged(BitrateAllocationLimits limits) {
|
|||
}
|
||||
|
||||
// RTC_RUN_ON(worker_thread_)
|
||||
AudioReceiveStream* Call::FindAudioStreamForSyncGroup(
|
||||
const std::string& sync_group) {
|
||||
RTC_DCHECK_RUN_ON(&receive_11993_checker_);
|
||||
if (!sync_group.empty()) {
|
||||
void Call::ConfigureSync(const std::string& sync_group) {
|
||||
// TODO(bugs.webrtc.org/11993): Expect to be called on the network thread.
|
||||
// Set sync only if there was no previous one.
|
||||
if (sync_group.empty())
|
||||
return;
|
||||
|
||||
AudioReceiveStream* sync_audio_stream = nullptr;
|
||||
// Find existing audio stream.
|
||||
const auto it = sync_stream_mapping_.find(sync_group);
|
||||
if (it != sync_stream_mapping_.end()) {
|
||||
sync_audio_stream = it->second;
|
||||
} else {
|
||||
// No configured audio stream, see if we can find one.
|
||||
for (AudioReceiveStream* stream : audio_receive_streams_) {
|
||||
if (stream->config().sync_group == sync_group)
|
||||
return stream;
|
||||
if (stream->config().sync_group == sync_group) {
|
||||
if (sync_audio_stream != nullptr) {
|
||||
RTC_LOG(LS_WARNING)
|
||||
<< "Attempting to sync more than one audio stream "
|
||||
"within the same sync group. This is not "
|
||||
"supported in the current implementation.";
|
||||
break;
|
||||
}
|
||||
sync_audio_stream = stream;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// TODO(bugs.webrtc.org/11993): Expect to be called on the network thread.
|
||||
// RTC_RUN_ON(worker_thread_)
|
||||
void Call::ConfigureSync(const std::string& sync_group) {
|
||||
// `audio_stream` may be nullptr when clearing the audio stream for a group.
|
||||
AudioReceiveStream* audio_stream = FindAudioStreamForSyncGroup(sync_group);
|
||||
|
||||
if (sync_audio_stream)
|
||||
sync_stream_mapping_[sync_group] = sync_audio_stream;
|
||||
size_t num_synced_streams = 0;
|
||||
for (VideoReceiveStream2* video_stream : video_receive_streams_) {
|
||||
if (video_stream->sync_group() != sync_group)
|
||||
continue;
|
||||
++num_synced_streams;
|
||||
// TODO(bugs.webrtc.org/4762): Support synchronizing more than one A/V pair.
|
||||
// Attempting to sync more than one audio/video pair within the same sync
|
||||
// group is not supported in the current implementation.
|
||||
if (num_synced_streams > 1) {
|
||||
// TODO(pbos): Support synchronizing more than one A/V pair.
|
||||
// https://code.google.com/p/webrtc/issues/detail?id=4762
|
||||
RTC_LOG(LS_WARNING)
|
||||
<< "Attempting to sync more than one audio/video pair "
|
||||
"within the same sync group. This is not supported in "
|
||||
"the current implementation.";
|
||||
}
|
||||
// Only sync the first A/V pair within this sync group.
|
||||
if (num_synced_streams == 1) {
|
||||
// sync_audio_stream may be null and that's ok.
|
||||
video_stream->SetSync(audio_stream);
|
||||
video_stream->SetSync(sync_audio_stream);
|
||||
} else {
|
||||
video_stream->SetSync(nullptr);
|
||||
}
|
||||
|
@ -1583,11 +1582,22 @@ PacketReceiver::DeliveryStatus Call::DeliverRtp(MediaType media_type,
|
|||
RTC_DCHECK(media_type == MediaType::AUDIO || media_type == MediaType::VIDEO ||
|
||||
is_keep_alive_packet);
|
||||
|
||||
bool use_send_side_bwe = false;
|
||||
if (!IdentifyReceivedPacket(parsed_packet, &use_send_side_bwe))
|
||||
auto it = receive_rtp_config_.find(parsed_packet.Ssrc());
|
||||
if (it == receive_rtp_config_.end()) {
|
||||
RTC_LOG(LS_ERROR) << "receive_rtp_config_ lookup failed for ssrc "
|
||||
<< parsed_packet.Ssrc();
|
||||
// Destruction of the receive stream, including deregistering from the
|
||||
// RtpDemuxer, is not protected by the `worker_thread_`.
|
||||
// But deregistering in the `receive_rtp_config_` map is. So by not passing
|
||||
// the packet on to demuxing in this case, we prevent incoming packets to be
|
||||
// passed on via the demuxer to a receive stream which is being torned down.
|
||||
return DELIVERY_UNKNOWN_SSRC;
|
||||
}
|
||||
|
||||
NotifyBweOfReceivedPacket(parsed_packet, media_type, use_send_side_bwe);
|
||||
parsed_packet.IdentifyExtensions(
|
||||
RtpHeaderExtensionMap(it->second->rtp_config().extensions));
|
||||
|
||||
NotifyBweOfReceivedPacket(parsed_packet, media_type);
|
||||
|
||||
// RateCounters expect input parameter as int, save it as int,
|
||||
// instead of converting each time it is passed to RateCounter::Add below.
|
||||
|
@ -1638,8 +1648,20 @@ void Call::OnRecoveredPacket(const uint8_t* packet, size_t length) {
|
|||
|
||||
parsed_packet.set_recovered(true);
|
||||
|
||||
if (!IdentifyReceivedPacket(parsed_packet))
|
||||
auto it = receive_rtp_config_.find(parsed_packet.Ssrc());
|
||||
if (it == receive_rtp_config_.end()) {
|
||||
RTC_LOG(LS_ERROR) << "receive_rtp_config_ lookup failed for ssrc "
|
||||
<< parsed_packet.Ssrc();
|
||||
// Destruction of the receive stream, including deregistering from the
|
||||
// RtpDemuxer, is not protected by the `worker_thread_`.
|
||||
// But deregistering in the `receive_rtp_config_` map is.
|
||||
// So by not passing the packet on to demuxing in this case, we prevent
|
||||
// incoming packets to be passed on via the demuxer to a receive stream
|
||||
// which is being torn down.
|
||||
return;
|
||||
}
|
||||
parsed_packet.IdentifyExtensions(
|
||||
RtpHeaderExtensionMap(it->second->rtp_config().extensions));
|
||||
|
||||
// TODO(brandtr): Update here when we support protecting audio packets too.
|
||||
parsed_packet.set_payload_type_frequency(kVideoPayloadTypeFrequency);
|
||||
|
@ -1648,8 +1670,11 @@ void Call::OnRecoveredPacket(const uint8_t* packet, size_t length) {
|
|||
|
||||
// RTC_RUN_ON(worker_thread_)
|
||||
void Call::NotifyBweOfReceivedPacket(const RtpPacketReceived& packet,
|
||||
MediaType media_type,
|
||||
bool use_send_side_bwe) {
|
||||
MediaType media_type) {
|
||||
auto it = receive_rtp_config_.find(packet.Ssrc());
|
||||
bool use_send_side_bwe = (it != receive_rtp_config_.end()) &&
|
||||
UseSendSideBwe(it->second->rtp_config());
|
||||
|
||||
RTPHeader header;
|
||||
packet.GetHeader(&header);
|
||||
|
||||
|
@ -1680,45 +1705,6 @@ void Call::NotifyBweOfReceivedPacket(const RtpPacketReceived& packet,
|
|||
}
|
||||
}
|
||||
|
||||
bool Call::IdentifyReceivedPacket(RtpPacketReceived& packet,
|
||||
bool* use_send_side_bwe /*= nullptr*/) {
|
||||
RTC_DCHECK_RUN_ON(&receive_11993_checker_);
|
||||
auto it = receive_rtp_config_.find(packet.Ssrc());
|
||||
if (it == receive_rtp_config_.end()) {
|
||||
RTC_DLOG(LS_WARNING) << "receive_rtp_config_ lookup failed for ssrc "
|
||||
<< packet.Ssrc();
|
||||
return false;
|
||||
}
|
||||
|
||||
packet.IdentifyExtensions(
|
||||
RtpHeaderExtensionMap(it->second->rtp_config().extensions));
|
||||
|
||||
if (use_send_side_bwe) {
|
||||
*use_send_side_bwe = UseSendSideBwe(it->second->rtp_config());
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Call::RegisterReceiveStream(uint32_t ssrc, ReceiveStream* stream) {
|
||||
RTC_DCHECK_RUN_ON(&receive_11993_checker_);
|
||||
RTC_DCHECK(stream);
|
||||
auto inserted = receive_rtp_config_.emplace(ssrc, stream);
|
||||
if (!inserted.second) {
|
||||
RTC_DLOG(LS_WARNING) << "ssrc already registered: " << ssrc;
|
||||
}
|
||||
return inserted.second;
|
||||
}
|
||||
|
||||
bool Call::UnregisterReceiveStream(uint32_t ssrc) {
|
||||
RTC_DCHECK_RUN_ON(&receive_11993_checker_);
|
||||
size_t erased = receive_rtp_config_.erase(ssrc);
|
||||
if (!erased) {
|
||||
RTC_DLOG(LS_WARNING) << "ssrc wasn't registered: " << ssrc;
|
||||
}
|
||||
return erased != 0u;
|
||||
}
|
||||
|
||||
} // namespace internal
|
||||
|
||||
} // namespace webrtc
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue