Make SendCodec() lock-free.

Fetching the current codec for sake of gathering stats, is frequently blocked since it's done by acquiring the same lock as is held while encoding frames.  This can mean tens of milliseconds.

To improve this, I'm taking advantage of the fact that the codec information is set on the same thread as is used to query the information.  This means that locking isn't needed for querying this information.  I'm adding checks to make sure debug builds will crash if this isn't followed.

An alternative to this approach could be to add one more lock that is specifically used for the codec information variable.  This would also decouple querying codec information from the encoder itself, but still requires a lock.

This patch depends on making ThreadChecker part of rtc_base_approved:
https://webrtc-codereview.appspot.com/40539004/

BUG=2822
R=mflodman@webrtc.org, pthatcher@webrtc.org

Review URL: https://webrtc-codereview.appspot.com/37779004

Cr-Commit-Position: refs/heads/master@{#8435}
git-svn-id: http://webrtc.googlecode.com/svn/trunk@8435 4adac7df-926f-26a2-2b94-8c16560cd09d
This commit is contained in:
tommi@webrtc.org
2015-02-19 17:43:25 +00:00
parent be29b3b4c6
commit e07710cc91
8 changed files with 140 additions and 49 deletions

View File

@ -111,12 +111,18 @@ class VideoCodingModuleImpl : public VideoCodingModule {
return sender_->RegisterSendCodec(sendCodec, numberOfCores, maxPayloadSize);
}
virtual int32_t SendCodec(VideoCodec* currentSendCodec) const OVERRIDE {
return sender_->SendCodec(currentSendCodec);
virtual const VideoCodec& GetSendCodec() const OVERRIDE {
return sender_->GetSendCodec();
}
// DEPRECATED.
virtual int32_t SendCodec(VideoCodec* currentSendCodec) const OVERRIDE {
return sender_->SendCodecBlocking(currentSendCodec);
}
// DEPRECATED.
virtual VideoCodecType SendCodec() const OVERRIDE {
return sender_->SendCodec();
return sender_->SendCodecBlocking();
}
virtual int32_t RegisterExternalEncoder(VideoEncoder* externalEncoder,
@ -351,6 +357,8 @@ class VideoCodingModuleImpl : public VideoCodingModule {
private:
EncodedImageCallbackWrapper post_encode_callback_;
// TODO(tommi): Change sender_ and receiver_ to be non pointers
// (construction is 1 alloc instead of 3).
scoped_ptr<vcm::VideoSender> sender_;
scoped_ptr<vcm::VideoReceiver> receiver_;
scoped_ptr<EventFactory> own_event_factory_;

View File

@ -16,6 +16,7 @@
#include <vector>
#include "webrtc/base/thread_annotations.h"
#include "webrtc/base/thread_checker.h"
#include "webrtc/modules/video_coding/main/source/codec_database.h"
#include "webrtc/modules/video_coding/main/source/frame_buffer.h"
#include "webrtc/modules/video_coding/main/source/generic_decoder.h"
@ -62,12 +63,25 @@ class VideoSender {
int32_t InitializeSender();
// Register the send codec to be used.
// This method must be called on the construction thread.
int32_t RegisterSendCodec(const VideoCodec* sendCodec,
uint32_t numberOfCores,
uint32_t maxPayloadSize);
// Non-blocking access to the currently active send codec configuration.
// Must be called from the same thread as the VideoSender instance was
// created on.
const VideoCodec& GetSendCodec() const;
// Get a copy of the currently configured send codec.
// This method acquires a lock to copy the current configuration out,
// so it can block and the returned information is not guaranteed to be
// accurate upon return. Consider using GetSendCodec() instead and make
// decisions on that thread with regards to the current codec.
int32_t SendCodecBlocking(VideoCodec* currentSendCodec) const;
// Same as SendCodecBlocking. Try to use GetSendCodec() instead.
VideoCodecType SendCodecBlocking() const;
int32_t SendCodec(VideoCodec* currentSendCodec) const;
VideoCodecType SendCodec() const;
int32_t RegisterExternalEncoder(VideoEncoder* externalEncoder,
uint8_t payloadType,
bool internalSource);
@ -124,6 +138,10 @@ class VideoSender {
bool frame_dropper_enabled_;
VCMProcessTimer _sendStatsTimer;
// Must be accessed on the construction thread of VideoSender.
VideoCodec current_codec_;
rtc::ThreadChecker main_thread_;
VCMQMSettingsCallback* qm_settings_callback_;
VCMProtectionCallback* protection_callback_;
};

View File

@ -12,6 +12,7 @@
#include <algorithm> // std::max
#include "webrtc/base/checks.h"
#include "webrtc/common_video/libyuv/include/webrtc_libyuv.h"
#include "webrtc/modules/video_coding/codecs/interface/video_codec_interface.h"
#include "webrtc/modules/video_coding/main/source/encoded_frame.h"
@ -72,8 +73,10 @@ VideoSender::VideoSender(Clock* clock,
_codecDataBase(),
frame_dropper_enabled_(true),
_sendStatsTimer(1000, clock_),
current_codec_(),
qm_settings_callback_(NULL),
protection_callback_(NULL) {}
protection_callback_(NULL) {
}
VideoSender::~VideoSender() {
delete _sendCritSect;
@ -86,13 +89,8 @@ int32_t VideoSender::Process() {
_sendStatsTimer.Processed();
CriticalSectionScoped cs(process_crit_sect_.get());
if (_sendStatsCallback != NULL) {
uint32_t bitRate;
uint32_t frameRate;
{
CriticalSectionScoped cs(_sendCritSect);
bitRate = _mediaOpt.SentBitRate();
frameRate = _mediaOpt.SentFrameRate();
}
uint32_t bitRate = _mediaOpt.SentBitRate();
uint32_t frameRate = _mediaOpt.SentFrameRate();
_sendStatsCallback->SendStatistics(bitRate, frameRate);
}
}
@ -102,6 +100,7 @@ int32_t VideoSender::Process() {
// Reset send side to initial state - all components
int32_t VideoSender::InitializeSender() {
DCHECK(main_thread_.CalledOnValidThread());
CriticalSectionScoped cs(_sendCritSect);
_codecDataBase.ResetSender();
_encoder = NULL;
@ -118,6 +117,7 @@ int64_t VideoSender::TimeUntilNextProcess() {
int32_t VideoSender::RegisterSendCodec(const VideoCodec* sendCodec,
uint32_t numberOfCores,
uint32_t maxPayloadSize) {
DCHECK(main_thread_.CalledOnValidThread());
CriticalSectionScoped cs(_sendCritSect);
if (sendCodec == NULL) {
return VCM_PARAMETER_ERROR;
@ -129,6 +129,9 @@ int32_t VideoSender::RegisterSendCodec(const VideoCodec* sendCodec,
// Update encoder regardless of result to make sure that we're not holding on
// to a deleted instance.
_encoder = _codecDataBase.GetEncoder();
// Cache the current codec here so they can be fetched from this thread
// without requiring the _sendCritSect lock.
current_codec_ = *sendCodec;
if (!ret) {
LOG(LS_ERROR) << "Failed to initialize the encoder with payload name "
@ -162,20 +165,21 @@ int32_t VideoSender::RegisterSendCodec(const VideoCodec* sendCodec,
return VCM_OK;
}
// Get current send codec
int32_t VideoSender::SendCodec(VideoCodec* currentSendCodec) const {
CriticalSectionScoped cs(_sendCritSect);
const VideoCodec& VideoSender::GetSendCodec() const {
DCHECK(main_thread_.CalledOnValidThread());
return current_codec_;
}
int32_t VideoSender::SendCodecBlocking(VideoCodec* currentSendCodec) const {
CriticalSectionScoped cs(_sendCritSect);
if (currentSendCodec == NULL) {
return VCM_PARAMETER_ERROR;
}
return _codecDataBase.SendCodec(currentSendCodec) ? 0 : -1;
}
// Get the current send codec type
VideoCodecType VideoSender::SendCodec() const {
VideoCodecType VideoSender::SendCodecBlocking() const {
CriticalSectionScoped cs(_sendCritSect);
return _codecDataBase.SendCodec();
}
@ -214,7 +218,6 @@ int32_t VideoSender::CodecConfigParameters(uint8_t* buffer,
// TODO(andresp): Make const once media_opt is thread-safe and this has a
// pointer to it.
int32_t VideoSender::SentFrameCount(VCMFrameCount* frameCount) {
CriticalSectionScoped cs(_sendCritSect);
*frameCount = _mediaOpt.SentFrameCount();
return VCM_OK;
}
@ -400,8 +403,6 @@ int32_t VideoSender::EnableFrameDropper(bool enable) {
}
int VideoSender::SetSenderNackMode(SenderNackMode mode) {
CriticalSectionScoped cs(_sendCritSect);
switch (mode) {
case VideoCodingModule::kNackNone:
_mediaOpt.EnableProtectionMethod(false, media_optimization::kNack);
@ -411,7 +412,6 @@ int VideoSender::SetSenderNackMode(SenderNackMode mode) {
break;
case VideoCodingModule::kNackSelective:
return VCM_NOT_IMPLEMENTED;
break;
}
return VCM_OK;
}
@ -421,7 +421,6 @@ int VideoSender::SetSenderReferenceSelection(bool enable) {
}
int VideoSender::SetSenderFEC(bool enable) {
CriticalSectionScoped cs(_sendCritSect);
_mediaOpt.EnableProtectionMethod(enable, media_optimization::kFec);
return VCM_OK;
}
@ -439,17 +438,12 @@ void VideoSender::StopDebugRecording() {
}
void VideoSender::SuspendBelowMinBitrate() {
CriticalSectionScoped cs(_sendCritSect);
VideoCodec current_send_codec;
if (SendCodec(&current_send_codec) != 0) {
assert(false); // Must set a send codec before SuspendBelowMinBitrate.
return;
}
DCHECK(main_thread_.CalledOnValidThread());
int threshold_bps;
if (current_send_codec.numberOfSimulcastStreams == 0) {
threshold_bps = current_send_codec.minBitrate * 1000;
if (current_codec_.numberOfSimulcastStreams == 0) {
threshold_bps = current_codec_.minBitrate * 1000;
} else {
threshold_bps = current_send_codec.simulcastStream[0].minBitrate * 1000;
threshold_bps = current_codec_.simulcastStream[0].minBitrate * 1000;
}
// Set the hysteresis window to be at 10% of the threshold, but at least
// 10 kbps.