- Add task queue to Call with the intent of replacing the use of one of the process threads.

- Split VideoSendStream in two. VideoSendStreamInternal is created and used on the new task queue.

- BitrateAllocator is now created on libjingle's worker thread but always used on the new task queue instead of both encoder threads and the process thread.

- VideoEncoderConfig and VideoSendStream::Config support move semantics.

- The encoder thread is moved from VideoSendStream to ViEEncoder. Frames are forwarded directly to ViEEncoder which is responsible for timestamping ? and encoding the frames.

BUG=webrtc:5687

Review-Url: https://codereview.webrtc.org/2060403002
Cr-Commit-Position: refs/heads/master@{#13767}
This commit is contained in:
perkj 2016-08-16 00:38:44 -07:00 committed by Commit bot
parent 82dda1af5e
commit cc168360f4
37 changed files with 1684 additions and 1397 deletions

View File

@ -16,7 +16,9 @@
#include "webrtc/audio/conversion.h"
#include "webrtc/audio/scoped_voe_interface.h"
#include "webrtc/base/checks.h"
#include "webrtc/base/event.h"
#include "webrtc/base/logging.h"
#include "webrtc/base/task_queue.h"
#include "webrtc/modules/congestion_controller/include/congestion_controller.h"
#include "webrtc/modules/pacing/paced_sender.h"
#include "webrtc/modules/rtp_rtcp/include/rtp_rtcp_defines.h"
@ -59,9 +61,11 @@ namespace internal {
AudioSendStream::AudioSendStream(
const webrtc::AudioSendStream::Config& config,
const rtc::scoped_refptr<webrtc::AudioState>& audio_state,
rtc::TaskQueue* worker_queue,
CongestionController* congestion_controller,
BitrateAllocator* bitrate_allocator)
: config_(config),
: worker_queue_(worker_queue),
config_(config),
audio_state_(audio_state),
bitrate_allocator_(bitrate_allocator) {
LOG(LS_INFO) << "AudioSendStream: " << config_.ToString();
@ -109,8 +113,13 @@ void AudioSendStream::Start() {
RTC_DCHECK(thread_checker_.CalledOnValidThread());
if (config_.min_bitrate_kbps != -1 && config_.max_bitrate_kbps != -1) {
RTC_DCHECK_GE(config_.max_bitrate_kbps, config_.min_bitrate_kbps);
bitrate_allocator_->AddObserver(this, config_.min_bitrate_kbps * 1000,
config_.max_bitrate_kbps * 1000, 0, true);
rtc::Event thread_sync_event(false /* manual_reset */, false);
worker_queue_->PostTask([this, &thread_sync_event] {
bitrate_allocator_->AddObserver(this, config_.min_bitrate_kbps * 1000,
config_.max_bitrate_kbps * 1000, 0, true);
thread_sync_event.Set();
});
thread_sync_event.Wait(rtc::Event::kForever);
}
ScopedVoEInterface<VoEBase> base(voice_engine());
@ -122,7 +131,13 @@ void AudioSendStream::Start() {
void AudioSendStream::Stop() {
RTC_DCHECK(thread_checker_.CalledOnValidThread());
bitrate_allocator_->RemoveObserver(this);
rtc::Event thread_sync_event(false /* manual_reset */, false);
worker_queue_->PostTask([this, &thread_sync_event] {
bitrate_allocator_->RemoveObserver(this);
thread_sync_event.Set();
});
thread_sync_event.Wait(rtc::Event::kForever);
ScopedVoEInterface<VoEBase> base(voice_engine());
int error = base->StopSend(config_.voe_channel_id);
if (error != 0) {

View File

@ -33,6 +33,7 @@ class AudioSendStream final : public webrtc::AudioSendStream,
public:
AudioSendStream(const webrtc::AudioSendStream::Config& config,
const rtc::scoped_refptr<webrtc::AudioState>& audio_state,
rtc::TaskQueue* worker_queue,
CongestionController* congestion_controller,
BitrateAllocator* bitrate_allocator);
~AudioSendStream() override;
@ -59,6 +60,7 @@ class AudioSendStream final : public webrtc::AudioSendStream,
VoiceEngine* voice_engine() const;
rtc::ThreadChecker thread_checker_;
rtc::TaskQueue* worker_queue_;
const webrtc::AudioSendStream::Config config_;
rtc::scoped_refptr<webrtc::AudioState> audio_state_;
std::unique_ptr<voe::ChannelProxy> channel_proxy_;

View File

@ -16,6 +16,7 @@
#include "webrtc/audio/audio_send_stream.h"
#include "webrtc/audio/audio_state.h"
#include "webrtc/audio/conversion.h"
#include "webrtc/base/task_queue.h"
#include "webrtc/modules/congestion_controller/include/mock/mock_congestion_controller.h"
#include "webrtc/call/mock/mock_rtc_event_log.h"
#include "webrtc/modules/congestion_controller/include/congestion_controller.h"
@ -65,7 +66,8 @@ struct ConfigHelper {
&bitrate_observer_,
&remote_bitrate_observer_,
&event_log_),
bitrate_allocator_(&limit_observer_) {
bitrate_allocator_(&limit_observer_),
worker_queue_("ConfigHelper_worker_queue") {
using testing::Invoke;
using testing::StrEq;
@ -125,6 +127,7 @@ struct ConfigHelper {
return &congestion_controller_;
}
BitrateAllocator* bitrate_allocator() { return &bitrate_allocator_; }
rtc::TaskQueue* worker_queue() { return &worker_queue_; }
void SetupMockForSendTelephoneEvent() {
EXPECT_TRUE(channel_proxy_);
@ -181,6 +184,9 @@ struct ConfigHelper {
MockRtcEventLog event_log_;
testing::NiceMock<MockLimitObserver> limit_observer_;
BitrateAllocator bitrate_allocator_;
// |worker_queue| is defined last to ensure all pending tasks are cancelled
// and deleted before any other members.
rtc::TaskQueue worker_queue_;
};
} // namespace
@ -202,16 +208,16 @@ TEST(AudioSendStreamTest, ConfigToString) {
TEST(AudioSendStreamTest, ConstructDestruct) {
ConfigHelper helper;
internal::AudioSendStream send_stream(helper.config(), helper.audio_state(),
helper.congestion_controller(),
helper.bitrate_allocator());
internal::AudioSendStream send_stream(
helper.config(), helper.audio_state(), helper.worker_queue(),
helper.congestion_controller(), helper.bitrate_allocator());
}
TEST(AudioSendStreamTest, SendTelephoneEvent) {
ConfigHelper helper;
internal::AudioSendStream send_stream(helper.config(), helper.audio_state(),
helper.congestion_controller(),
helper.bitrate_allocator());
internal::AudioSendStream send_stream(
helper.config(), helper.audio_state(), helper.worker_queue(),
helper.congestion_controller(), helper.bitrate_allocator());
helper.SetupMockForSendTelephoneEvent();
EXPECT_TRUE(send_stream.SendTelephoneEvent(kTelephoneEventPayloadType,
kTelephoneEventCode, kTelephoneEventDuration));
@ -219,18 +225,18 @@ TEST(AudioSendStreamTest, SendTelephoneEvent) {
TEST(AudioSendStreamTest, SetMuted) {
ConfigHelper helper;
internal::AudioSendStream send_stream(helper.config(), helper.audio_state(),
helper.congestion_controller(),
helper.bitrate_allocator());
internal::AudioSendStream send_stream(
helper.config(), helper.audio_state(), helper.worker_queue(),
helper.congestion_controller(), helper.bitrate_allocator());
EXPECT_CALL(*helper.channel_proxy(), SetInputMute(true));
send_stream.SetMuted(true);
}
TEST(AudioSendStreamTest, GetStats) {
ConfigHelper helper;
internal::AudioSendStream send_stream(helper.config(), helper.audio_state(),
helper.congestion_controller(),
helper.bitrate_allocator());
internal::AudioSendStream send_stream(
helper.config(), helper.audio_state(), helper.worker_queue(),
helper.congestion_controller(), helper.bitrate_allocator());
helper.SetupMockForGetStats();
AudioSendStream::Stats stats = send_stream.GetStats();
EXPECT_EQ(kSsrc, stats.local_ssrc);
@ -257,9 +263,9 @@ TEST(AudioSendStreamTest, GetStats) {
TEST(AudioSendStreamTest, GetStatsTypingNoiseDetected) {
ConfigHelper helper;
internal::AudioSendStream send_stream(helper.config(), helper.audio_state(),
helper.congestion_controller(),
helper.bitrate_allocator());
internal::AudioSendStream send_stream(
helper.config(), helper.audio_state(), helper.worker_queue(),
helper.congestion_controller(), helper.bitrate_allocator());
helper.SetupMockForGetStats();
EXPECT_FALSE(send_stream.GetStats().typing_noise_detected);

View File

@ -113,8 +113,8 @@ class Call {
AudioReceiveStream* receive_stream) = 0;
virtual VideoSendStream* CreateVideoSendStream(
const VideoSendStream::Config& config,
const VideoEncoderConfig& encoder_config) = 0;
VideoSendStream::Config config,
VideoEncoderConfig encoder_config) = 0;
virtual void DestroyVideoSendStream(VideoSendStream* send_stream) = 0;
virtual VideoReceiveStream* CreateVideoReceiveStream(

View File

@ -29,6 +29,7 @@ source_set("call") {
"..:rtc_event_log",
"..:webrtc_common",
"../audio",
"../base:rtc_task_queue",
"../modules/congestion_controller",
"../modules/rtp_rtcp",
"../system_wrappers",
@ -51,6 +52,7 @@ if (rtc_include_tests) {
"//testing/gmock",
"//testing/gtest",
]
configs += [ "..:common_config" ]
if (is_clang) {
# Suppress warnings from the Chromium Clang plugin.
# See http://code.google.com/p/webrtc/issues/detail?id=163 for details.

View File

@ -54,7 +54,9 @@ BitrateAllocator::BitrateAllocator(LimitObserver* limit_observer)
last_rtt_(0),
num_pause_events_(0),
clock_(Clock::GetRealTimeClock()),
last_bwe_log_time_(0) {}
last_bwe_log_time_(0) {
sequenced_checker_.Detach();
}
BitrateAllocator::~BitrateAllocator() {
RTC_LOGGED_HISTOGRAM_COUNTS_100("WebRTC.Call.NumberOfPauseEvents",
@ -64,7 +66,7 @@ BitrateAllocator::~BitrateAllocator() {
void BitrateAllocator::OnNetworkChanged(uint32_t target_bitrate_bps,
uint8_t fraction_loss,
int64_t rtt) {
rtc::CritScope lock(&crit_sect_);
RTC_DCHECK_CALLED_SEQUENTIALLY(&sequenced_checker_);
last_bitrate_bps_ = target_bitrate_bps;
last_non_zero_bitrate_bps_ =
target_bitrate_bps > 0 ? target_bitrate_bps : last_non_zero_bitrate_bps_;
@ -117,7 +119,7 @@ void BitrateAllocator::AddObserver(BitrateAllocatorObserver* observer,
uint32_t max_bitrate_bps,
uint32_t pad_up_bitrate_bps,
bool enforce_min_bitrate) {
rtc::CritScope lock(&crit_sect_);
RTC_DCHECK_CALLED_SEQUENTIALLY(&sequenced_checker_);
auto it = FindObserverConfig(observer);
// Update settings if the observer already exists, create a new one otherwise.
@ -155,17 +157,15 @@ void BitrateAllocator::AddObserver(BitrateAllocatorObserver* observer,
}
void BitrateAllocator::UpdateAllocationLimits() {
RTC_DCHECK_CALLED_SEQUENTIALLY(&sequenced_checker_);
uint32_t total_requested_padding_bitrate = 0;
uint32_t total_requested_min_bitrate = 0;
{
rtc::CritScope lock(&crit_sect_);
for (const auto& config : bitrate_observer_configs_) {
if (config.enforce_min_bitrate) {
total_requested_min_bitrate += config.min_bitrate_bps;
}
total_requested_padding_bitrate += config.pad_up_bitrate_bps;
for (const auto& config : bitrate_observer_configs_) {
if (config.enforce_min_bitrate) {
total_requested_min_bitrate += config.min_bitrate_bps;
}
total_requested_padding_bitrate += config.pad_up_bitrate_bps;
}
LOG(LS_INFO) << "UpdateAllocationLimits : total_requested_min_bitrate: "
@ -177,27 +177,26 @@ void BitrateAllocator::UpdateAllocationLimits() {
}
void BitrateAllocator::RemoveObserver(BitrateAllocatorObserver* observer) {
{
rtc::CritScope lock(&crit_sect_);
auto it = FindObserverConfig(observer);
if (it != bitrate_observer_configs_.end()) {
bitrate_observer_configs_.erase(it);
}
RTC_DCHECK_CALLED_SEQUENTIALLY(&sequenced_checker_);
auto it = FindObserverConfig(observer);
if (it != bitrate_observer_configs_.end()) {
bitrate_observer_configs_.erase(it);
}
UpdateAllocationLimits();
}
int BitrateAllocator::GetStartBitrate(BitrateAllocatorObserver* observer) {
rtc::CritScope lock(&crit_sect_);
RTC_DCHECK_CALLED_SEQUENTIALLY(&sequenced_checker_);
const auto& it = FindObserverConfig(observer);
if (it == bitrate_observer_configs_.end()) {
// This observer hasn't been added yet, just give it its fair share.
return last_non_zero_bitrate_bps_ /
static_cast<int>((bitrate_observer_configs_.size() + 1));
static_cast<int>((bitrate_observer_configs_.size() + 1));
} else if (it->allocated_bitrate_bps == -1) {
// This observer hasn't received an allocation yet, so do the same.
return last_non_zero_bitrate_bps_ /
static_cast<int>(bitrate_observer_configs_.size());
static_cast<int>(bitrate_observer_configs_.size());
} else {
// This observer already has an allocation.
return it->allocated_bitrate_bps;
@ -205,8 +204,8 @@ int BitrateAllocator::GetStartBitrate(BitrateAllocatorObserver* observer) {
}
BitrateAllocator::ObserverConfigs::iterator
BitrateAllocator::FindObserverConfig(
const BitrateAllocatorObserver* observer) {
BitrateAllocator::FindObserverConfig(const BitrateAllocatorObserver* observer) {
RTC_DCHECK_CALLED_SEQUENTIALLY(&sequenced_checker_);
for (auto it = bitrate_observer_configs_.begin();
it != bitrate_observer_configs_.end(); ++it) {
if (it->observer == observer)
@ -217,6 +216,7 @@ BitrateAllocator::FindObserverConfig(
BitrateAllocator::ObserverAllocation BitrateAllocator::AllocateBitrates(
uint32_t bitrate) {
RTC_DCHECK_CALLED_SEQUENTIALLY(&sequenced_checker_);
if (bitrate_observer_configs_.empty())
return ObserverAllocation();
@ -245,6 +245,7 @@ BitrateAllocator::ObserverAllocation BitrateAllocator::AllocateBitrates(
}
BitrateAllocator::ObserverAllocation BitrateAllocator::ZeroRateAllocation() {
RTC_DCHECK_CALLED_SEQUENTIALLY(&sequenced_checker_);
ObserverAllocation allocation;
for (const auto& observer_config : bitrate_observer_configs_)
allocation[observer_config.observer] = 0;
@ -253,8 +254,8 @@ BitrateAllocator::ObserverAllocation BitrateAllocator::ZeroRateAllocation() {
BitrateAllocator::ObserverAllocation BitrateAllocator::LowRateAllocation(
uint32_t bitrate) {
RTC_DCHECK_CALLED_SEQUENTIALLY(&sequenced_checker_);
ObserverAllocation allocation;
// Start by allocating bitrate to observers enforcing a min bitrate, hence
// remaining_bitrate might turn negative.
int64_t remaining_bitrate = bitrate;
@ -308,7 +309,7 @@ BitrateAllocator::ObserverAllocation BitrateAllocator::LowRateAllocation(
BitrateAllocator::ObserverAllocation BitrateAllocator::NormalRateAllocation(
uint32_t bitrate,
uint32_t sum_min_bitrates) {
RTC_DCHECK_CALLED_SEQUENTIALLY(&sequenced_checker_);
ObserverAllocation allocation;
for (const auto& observer_config : bitrate_observer_configs_)
allocation[observer_config.observer] = observer_config.min_bitrate_bps;
@ -321,7 +322,9 @@ BitrateAllocator::ObserverAllocation BitrateAllocator::NormalRateAllocation(
}
BitrateAllocator::ObserverAllocation BitrateAllocator::MaxRateAllocation(
uint32_t bitrate, uint32_t sum_max_bitrates) {
uint32_t bitrate,
uint32_t sum_max_bitrates) {
RTC_DCHECK_CALLED_SEQUENTIALLY(&sequenced_checker_);
ObserverAllocation allocation;
for (const auto& observer_config : bitrate_observer_configs_) {
@ -335,12 +338,12 @@ BitrateAllocator::ObserverAllocation BitrateAllocator::MaxRateAllocation(
uint32_t BitrateAllocator::LastAllocatedBitrate(
const ObserverConfig& observer_config) {
// Return the configured minimum bitrate for newly added observers, to avoid
// requiring an extra high bitrate for the observer to get an allocated
// bitrate.
return observer_config.allocated_bitrate_bps == -1 ?
observer_config.min_bitrate_bps : observer_config.allocated_bitrate_bps;
return observer_config.allocated_bitrate_bps == -1
? observer_config.min_bitrate_bps
: observer_config.allocated_bitrate_bps;
}
uint32_t BitrateAllocator::MinBitrateWithHysteresis(
@ -366,6 +369,7 @@ void BitrateAllocator::DistributeBitrateEvenly(uint32_t bitrate,
bool include_zero_allocations,
int max_multiplier,
ObserverAllocation* allocation) {
RTC_DCHECK_CALLED_SEQUENTIALLY(&sequenced_checker_);
RTC_DCHECK_EQ(allocation->size(), bitrate_observer_configs_.size());
ObserverSortingMap list_max_bitrates;
@ -398,10 +402,12 @@ void BitrateAllocator::DistributeBitrateEvenly(uint32_t bitrate,
bool BitrateAllocator::EnoughBitrateForAllObservers(uint32_t bitrate,
uint32_t sum_min_bitrates) {
RTC_DCHECK_CALLED_SEQUENTIALLY(&sequenced_checker_);
if (bitrate < sum_min_bitrates)
return false;
uint32_t extra_bitrate_per_observer = (bitrate - sum_min_bitrates) /
uint32_t extra_bitrate_per_observer =
(bitrate - sum_min_bitrates) /
static_cast<uint32_t>(bitrate_observer_configs_.size());
for (const auto& observer_config : bitrate_observer_configs_) {
if (observer_config.min_bitrate_bps + extra_bitrate_per_observer <

View File

@ -17,8 +17,7 @@
#include <utility>
#include <vector>
#include "webrtc/base/criticalsection.h"
#include "webrtc/base/thread_annotations.h"
#include "webrtc/base/sequenced_task_checker.h"
namespace webrtc {
@ -121,31 +120,24 @@ class BitrateAllocator {
typedef std::vector<ObserverConfig> ObserverConfigs;
ObserverConfigs::iterator FindObserverConfig(
const BitrateAllocatorObserver* observer)
EXCLUSIVE_LOCKS_REQUIRED(crit_sect_);
const BitrateAllocatorObserver* observer);
typedef std::multimap<uint32_t, const ObserverConfig*> ObserverSortingMap;
typedef std::map<BitrateAllocatorObserver*, int> ObserverAllocation;
ObserverAllocation AllocateBitrates(uint32_t bitrate)
EXCLUSIVE_LOCKS_REQUIRED(crit_sect_);
ObserverAllocation AllocateBitrates(uint32_t bitrate);
ObserverAllocation ZeroRateAllocation() EXCLUSIVE_LOCKS_REQUIRED(crit_sect_);
ObserverAllocation LowRateAllocation(uint32_t bitrate)
EXCLUSIVE_LOCKS_REQUIRED(crit_sect_);
ObserverAllocation ZeroRateAllocation();
ObserverAllocation LowRateAllocation(uint32_t bitrate);
ObserverAllocation NormalRateAllocation(uint32_t bitrate,
uint32_t sum_min_bitrates)
EXCLUSIVE_LOCKS_REQUIRED(crit_sect_);
uint32_t sum_min_bitrates);
ObserverAllocation MaxRateAllocation(uint32_t bitrate,
uint32_t sum_max_bitrates)
EXCLUSIVE_LOCKS_REQUIRED(crit_sect_);
uint32_t sum_max_bitrates);
uint32_t LastAllocatedBitrate(const ObserverConfig& observer_config)
EXCLUSIVE_LOCKS_REQUIRED(crit_sect_);
uint32_t LastAllocatedBitrate(const ObserverConfig& observer_config);
// The minimum bitrate required by this observer, including enable-hysteresis
// if the observer is in a paused state.
uint32_t MinBitrateWithHysteresis(const ObserverConfig& observer_config)
EXCLUSIVE_LOCKS_REQUIRED(crit_sect_);
uint32_t MinBitrateWithHysteresis(const ObserverConfig& observer_config);
// Splits |bitrate| evenly to observers already in |allocation|.
// |include_zero_allocations| decides if zero allocations should be part of
// the distribution or not. The allowed max bitrate is |max_multiplier| x
@ -153,24 +145,22 @@ class BitrateAllocator {
void DistributeBitrateEvenly(uint32_t bitrate,
bool include_zero_allocations,
int max_multiplier,
ObserverAllocation* allocation)
EXCLUSIVE_LOCKS_REQUIRED(crit_sect_);
bool EnoughBitrateForAllObservers(uint32_t bitrate, uint32_t sum_min_bitrates)
EXCLUSIVE_LOCKS_REQUIRED(crit_sect_);
ObserverAllocation* allocation);
bool EnoughBitrateForAllObservers(uint32_t bitrate,
uint32_t sum_min_bitrates);
LimitObserver* const limit_observer_;
rtc::CriticalSection crit_sect_;
rtc::SequencedTaskChecker sequenced_checker_;
LimitObserver* const limit_observer_ GUARDED_BY(&sequenced_checker_);
// Stored in a list to keep track of the insertion order.
ObserverConfigs bitrate_observer_configs_ GUARDED_BY(crit_sect_);
uint32_t last_bitrate_bps_ GUARDED_BY(crit_sect_);
uint32_t last_non_zero_bitrate_bps_ GUARDED_BY(crit_sect_);
uint8_t last_fraction_loss_ GUARDED_BY(crit_sect_);
int64_t last_rtt_ GUARDED_BY(crit_sect_);
ObserverConfigs bitrate_observer_configs_ GUARDED_BY(&sequenced_checker_);
uint32_t last_bitrate_bps_ GUARDED_BY(&sequenced_checker_);
uint32_t last_non_zero_bitrate_bps_ GUARDED_BY(&sequenced_checker_);
uint8_t last_fraction_loss_ GUARDED_BY(&sequenced_checker_);
int64_t last_rtt_ GUARDED_BY(&sequenced_checker_);
// Number of mute events based on too low BWE, not network up/down.
int num_pause_events_ GUARDED_BY(crit_sect_);
Clock* const clock_;
int64_t last_bwe_log_time_;
int num_pause_events_ GUARDED_BY(&sequenced_checker_);
Clock* const clock_ GUARDED_BY(&sequenced_checker_);
int64_t last_bwe_log_time_ GUARDED_BY(&sequenced_checker_);
};
} // namespace webrtc
#endif // WEBRTC_CALL_BITRATE_ALLOCATOR_H_

View File

@ -173,7 +173,8 @@ class BitrateEstimatorTest : public test::CallTest {
test_->video_send_config_.rtp.ssrcs[0]++;
test_->video_send_config_.encoder_settings.encoder = &fake_encoder_;
send_stream_ = test_->sender_call_->CreateVideoSendStream(
test_->video_send_config_, test_->video_encoder_config_);
test_->video_send_config_.Copy(),
test_->video_encoder_config_.Copy());
RTC_DCHECK_EQ(1u, test_->video_encoder_config_.streams.size());
frame_generator_capturer_.reset(test::FrameGeneratorCapturer::Create(
send_stream_->Input(), test_->video_encoder_config_.streams[0].width,

View File

@ -9,7 +9,6 @@
*/
#include <string.h>
#include <algorithm>
#include <map>
#include <memory>
@ -22,6 +21,7 @@
#include "webrtc/base/checks.h"
#include "webrtc/base/constructormagic.h"
#include "webrtc/base/logging.h"
#include "webrtc/base/task_queue.h"
#include "webrtc/base/thread_annotations.h"
#include "webrtc/base/thread_checker.h"
#include "webrtc/base/trace_event.h"
@ -74,8 +74,8 @@ class Call : public webrtc::Call,
webrtc::AudioReceiveStream* receive_stream) override;
webrtc::VideoSendStream* CreateVideoSendStream(
const webrtc::VideoSendStream::Config& config,
const VideoEncoderConfig& encoder_config) override;
webrtc::VideoSendStream::Config config,
VideoEncoderConfig encoder_config) override;
void DestroyVideoSendStream(webrtc::VideoSendStream* send_stream) override;
webrtc::VideoReceiveStream* CreateVideoReceiveStream(
@ -198,6 +198,11 @@ class Call : public webrtc::Call,
const std::unique_ptr<CongestionController> congestion_controller_;
const std::unique_ptr<SendDelayStats> video_send_delay_stats_;
const int64_t start_ms_;
// TODO(perkj): |worker_queue_| is supposed to replace
// |module_process_thread_|.
// |worker_queue| is defined last to ensure all pending tasks are cancelled
// and deleted before any other members.
rtc::TaskQueue worker_queue_;
RTC_DISALLOW_COPY_AND_ASSIGN(Call);
};
@ -249,7 +254,8 @@ Call::Call(const Call::Config& config)
congestion_controller_(
new CongestionController(clock_, this, &remb_, event_log_.get())),
video_send_delay_stats_(new SendDelayStats(clock_)),
start_ms_(clock_->TimeInMilliseconds()) {
start_ms_(clock_->TimeInMilliseconds()),
worker_queue_("call_worker_queue") {
RTC_DCHECK(configuration_thread_checker_.CalledOnValidThread());
RTC_DCHECK_GE(config.bitrate_config.min_bitrate_bps, 0);
RTC_DCHECK_GE(config.bitrate_config.start_bitrate_bps,
@ -279,6 +285,7 @@ Call::Call(const Call::Config& config)
Call::~Call() {
RTC_DCHECK(!remb_.InUse());
RTC_DCHECK(configuration_thread_checker_.CalledOnValidThread());
RTC_CHECK(audio_send_ssrcs_.empty());
RTC_CHECK(video_send_ssrcs_.empty());
RTC_CHECK(video_send_streams_.empty());
@ -297,7 +304,10 @@ Call::~Call() {
// Only update histograms after process threads have been shut down, so that
// they won't try to concurrently update stats.
UpdateSendHistograms();
{
rtc::CritScope lock(&bitrate_crit_);
UpdateSendHistograms();
}
UpdateReceiveHistograms();
UpdateHistograms();
@ -369,7 +379,7 @@ webrtc::AudioSendStream* Call::CreateAudioSendStream(
TRACE_EVENT0("webrtc", "Call::CreateAudioSendStream");
RTC_DCHECK(configuration_thread_checker_.CalledOnValidThread());
AudioSendStream* send_stream = new AudioSendStream(
config, config_.audio_state, congestion_controller_.get(),
config, config_.audio_state, &worker_queue_, congestion_controller_.get(),
bitrate_allocator_.get());
{
WriteLockScoped write_lock(*send_crit_);
@ -445,22 +455,28 @@ void Call::DestroyAudioReceiveStream(
}
webrtc::VideoSendStream* Call::CreateVideoSendStream(
const webrtc::VideoSendStream::Config& config,
const VideoEncoderConfig& encoder_config) {
webrtc::VideoSendStream::Config config,
VideoEncoderConfig encoder_config) {
TRACE_EVENT0("webrtc", "Call::CreateVideoSendStream");
RTC_DCHECK(configuration_thread_checker_.CalledOnValidThread());
video_send_delay_stats_->AddSsrcs(config);
event_log_->LogVideoSendStreamConfig(config);
// TODO(mflodman): Base the start bitrate on a current bandwidth estimate, if
// the call has already started.
// Copy ssrcs from |config| since |config| is moved.
std::vector<uint32_t> ssrcs = config.rtp.ssrcs;
VideoSendStream* send_stream = new VideoSendStream(
num_cpu_cores_, module_process_thread_.get(), call_stats_.get(),
congestion_controller_.get(), bitrate_allocator_.get(),
video_send_delay_stats_.get(), &remb_, event_log_.get(), config,
encoder_config, suspended_video_send_ssrcs_);
num_cpu_cores_, module_process_thread_.get(), &worker_queue_,
call_stats_.get(), congestion_controller_.get(), bitrate_allocator_.get(),
video_send_delay_stats_.get(), &remb_, event_log_.get(),
std::move(config), std::move(encoder_config),
suspended_video_send_ssrcs_);
{
WriteLockScoped write_lock(*send_crit_);
for (uint32_t ssrc : config.rtp.ssrcs) {
for (uint32_t ssrc : ssrcs) {
RTC_DCHECK(video_send_ssrcs_.find(ssrc) == video_send_ssrcs_.end());
video_send_ssrcs_[ssrc] = send_stream;
}
@ -468,7 +484,7 @@ webrtc::VideoSendStream* Call::CreateVideoSendStream(
}
send_stream->SignalNetworkState(video_network_state_);
UpdateAggregateNetworkState();
event_log_->LogVideoSendStreamConfig(config);
return send_stream;
}
@ -495,11 +511,11 @@ void Call::DestroyVideoSendStream(webrtc::VideoSendStream* send_stream) {
}
RTC_CHECK(send_stream_impl != nullptr);
VideoSendStream::RtpStateMap rtp_state = send_stream_impl->GetRtpStates();
VideoSendStream::RtpStateMap rtp_state =
send_stream_impl->StopPermanentlyAndGetRtpStates();
for (VideoSendStream::RtpStateMap::iterator it = rtp_state.begin();
it != rtp_state.end();
++it) {
it != rtp_state.end(); ++it) {
suspended_video_send_ssrcs_[it->first] = it->second;
}
@ -729,6 +745,15 @@ void Call::OnSentPacket(const rtc::SentPacket& sent_packet) {
void Call::OnNetworkChanged(uint32_t target_bitrate_bps, uint8_t fraction_loss,
int64_t rtt_ms) {
// TODO(perkj): Consider making sure CongestionController operates on
// |worker_queue_|.
if (!worker_queue_.IsCurrent()) {
worker_queue_.PostTask([this, target_bitrate_bps, fraction_loss, rtt_ms] {
OnNetworkChanged(target_bitrate_bps, fraction_loss, rtt_ms);
});
return;
}
RTC_DCHECK_RUN_ON(&worker_queue_);
bitrate_allocator_->OnNetworkChanged(target_bitrate_bps, fraction_loss,
rtt_ms);

View File

@ -672,7 +672,7 @@ TEST_F(CallPerfTest, KeepsHighBitrateWhenReconfiguringSender) {
encoder_config->streams[0].target_bitrate_bps =
encoder_config->streams[0].max_bitrate_bps = 2000000;
encoder_config_ = *encoder_config;
encoder_config_ = encoder_config->Copy();
}
void OnVideoStreamsCreated(
@ -686,7 +686,7 @@ TEST_F(CallPerfTest, KeepsHighBitrateWhenReconfiguringSender) {
<< "Timed out before receiving an initial high bitrate.";
encoder_config_.streams[0].width *= 2;
encoder_config_.streams[0].height *= 2;
send_stream_->ReconfigureVideoEncoder(encoder_config_);
send_stream_->ReconfigureVideoEncoder(encoder_config_.Copy());
EXPECT_TRUE(Wait())
<< "Timed out while waiting for a couple of high bitrate estimates "
"after reconfiguring the send stream.";

View File

@ -125,12 +125,20 @@ struct VideoStream {
};
struct VideoEncoderConfig {
public:
enum class ContentType {
kRealtimeVideo,
kScreen,
};
VideoEncoderConfig& operator=(VideoEncoderConfig&&) = default;
VideoEncoderConfig& operator=(const VideoEncoderConfig&) = delete;
// Mostly used by tests. Avoid creating copies if you can.
VideoEncoderConfig Copy() const { return VideoEncoderConfig(*this); }
VideoEncoderConfig();
VideoEncoderConfig(VideoEncoderConfig&&) = default;
~VideoEncoderConfig();
std::string ToString() const;
@ -145,6 +153,11 @@ struct VideoEncoderConfig {
// unless the estimated bandwidth indicates that the link can handle it.
int min_transmit_bitrate_bps;
bool expect_encode_from_texture;
private:
// Access to the copy constructor is private to force use of the Copy()
// method for those exceptional cases where we do use it.
VideoEncoderConfig(const VideoEncoderConfig&) = default;
};
struct VideoDecoderH264Settings {

View File

@ -98,21 +98,22 @@ void FakeAudioReceiveStream::SetGain(float gain) {
}
FakeVideoSendStream::FakeVideoSendStream(
const webrtc::VideoSendStream::Config& config,
const webrtc::VideoEncoderConfig& encoder_config)
webrtc::VideoSendStream::Config config,
webrtc::VideoEncoderConfig encoder_config)
: sending_(false),
config_(config),
config_(std::move(config)),
codec_settings_set_(false),
num_swapped_frames_(0) {
RTC_DCHECK(config.encoder_settings.encoder != NULL);
ReconfigureVideoEncoder(encoder_config);
ReconfigureVideoEncoder(std::move(encoder_config));
}
webrtc::VideoSendStream::Config FakeVideoSendStream::GetConfig() const {
const webrtc::VideoSendStream::Config& FakeVideoSendStream::GetConfig() const {
return config_;
}
webrtc::VideoEncoderConfig FakeVideoSendStream::GetEncoderConfig() const {
const webrtc::VideoEncoderConfig& FakeVideoSendStream::GetEncoderConfig()
const {
return encoder_config_;
}
@ -177,8 +178,7 @@ webrtc::VideoSendStream::Stats FakeVideoSendStream::GetStats() {
}
void FakeVideoSendStream::ReconfigureVideoEncoder(
const webrtc::VideoEncoderConfig& config) {
encoder_config_ = config;
webrtc::VideoEncoderConfig config) {
if (config.encoder_specific_settings != NULL) {
if (config_.encoder_settings.payload_name == "VP8") {
vpx_settings_.vp8 = *reinterpret_cast<const webrtc::VideoCodecVP8*>(
@ -199,6 +199,7 @@ void FakeVideoSendStream::ReconfigureVideoEncoder(
<< config_.encoder_settings.payload_name;
}
}
encoder_config_ = std::move(config);
codec_settings_set_ = config.encoder_specific_settings != NULL;
++num_encoder_reconfigurations_;
}
@ -359,10 +360,10 @@ void FakeCall::DestroyAudioReceiveStream(
}
webrtc::VideoSendStream* FakeCall::CreateVideoSendStream(
const webrtc::VideoSendStream::Config& config,
const webrtc::VideoEncoderConfig& encoder_config) {
webrtc::VideoSendStream::Config config,
webrtc::VideoEncoderConfig encoder_config) {
FakeVideoSendStream* fake_stream =
new FakeVideoSendStream(config, encoder_config);
new FakeVideoSendStream(std::move(config), std::move(encoder_config));
video_send_streams_.push_back(fake_stream);
++num_created_send_streams_;
return fake_stream;

View File

@ -102,10 +102,10 @@ class FakeAudioReceiveStream final : public webrtc::AudioReceiveStream {
class FakeVideoSendStream final : public webrtc::VideoSendStream,
public webrtc::VideoCaptureInput {
public:
FakeVideoSendStream(const webrtc::VideoSendStream::Config& config,
const webrtc::VideoEncoderConfig& encoder_config);
webrtc::VideoSendStream::Config GetConfig() const;
webrtc::VideoEncoderConfig GetEncoderConfig() const;
FakeVideoSendStream(webrtc::VideoSendStream::Config config,
webrtc::VideoEncoderConfig encoder_config);
const webrtc::VideoSendStream::Config& GetConfig() const;
const webrtc::VideoEncoderConfig& GetEncoderConfig() const;
std::vector<webrtc::VideoStream> GetVideoStreams();
bool IsSending() const;
@ -128,8 +128,7 @@ class FakeVideoSendStream final : public webrtc::VideoSendStream,
void Start() override;
void Stop() override;
webrtc::VideoSendStream::Stats GetStats() override;
void ReconfigureVideoEncoder(
const webrtc::VideoEncoderConfig& config) override;
void ReconfigureVideoEncoder(webrtc::VideoEncoderConfig config) override;
webrtc::VideoCaptureInput* Input() override;
bool sending_;
@ -208,8 +207,8 @@ class FakeCall final : public webrtc::Call, public webrtc::PacketReceiver {
webrtc::AudioReceiveStream* receive_stream) override;
webrtc::VideoSendStream* CreateVideoSendStream(
const webrtc::VideoSendStream::Config& config,
const webrtc::VideoEncoderConfig& encoder_config) override;
webrtc::VideoSendStream::Config config,
webrtc::VideoEncoderConfig encoder_config) override;
void DestroyVideoSendStream(webrtc::VideoSendStream* send_stream) override;
webrtc::VideoReceiveStream* CreateVideoReceiveStream(

View File

@ -1143,8 +1143,8 @@ bool WebRtcVideoChannel2::AddSendStream(const StreamParams& sp) {
webrtc::VideoSendStream::Config config(this);
config.suspend_below_min_bitrate = video_config_.suspend_below_min_bitrate;
WebRtcVideoSendStream* stream = new WebRtcVideoSendStream(
call_, sp, config, default_send_options_, external_encoder_factory_,
video_config_.enable_cpu_overuse_detection,
call_, sp, std::move(config), default_send_options_,
external_encoder_factory_, video_config_.enable_cpu_overuse_detection,
bitrate_config_.max_bitrate_bps, send_codec_, send_rtp_extensions_,
send_params_);
@ -1533,11 +1533,11 @@ bool WebRtcVideoChannel2::SendRtcp(const uint8_t* data, size_t len) {
WebRtcVideoChannel2::WebRtcVideoSendStream::VideoSendStreamParameters::
VideoSendStreamParameters(
const webrtc::VideoSendStream::Config& config,
webrtc::VideoSendStream::Config config,
const VideoOptions& options,
int max_bitrate_bps,
const rtc::Optional<VideoCodecSettings>& codec_settings)
: config(config),
: config(std::move(config)),
options(options),
max_bitrate_bps(max_bitrate_bps),
codec_settings(codec_settings) {}
@ -1560,7 +1560,7 @@ WebRtcVideoChannel2::WebRtcVideoSendStream::AllocatedEncoder::AllocatedEncoder(
WebRtcVideoChannel2::WebRtcVideoSendStream::WebRtcVideoSendStream(
webrtc::Call* call,
const StreamParams& sp,
const webrtc::VideoSendStream::Config& config,
webrtc::VideoSendStream::Config config,
const VideoOptions& options,
WebRtcVideoEncoderFactory* external_encoder_factory,
bool enable_cpu_overuse_detection,
@ -1579,7 +1579,7 @@ WebRtcVideoChannel2::WebRtcVideoSendStream::WebRtcVideoSendStream(
source_(nullptr),
external_encoder_factory_(external_encoder_factory),
stream_(nullptr),
parameters_(config, options, max_bitrate_bps, codec_settings),
parameters_(std::move(config), options, max_bitrate_bps, codec_settings),
rtp_parameters_(CreateRtpParametersWithOneEncoding()),
pending_encoder_reconfiguration_(false),
allocated_encoder_(nullptr, webrtc::kVideoCodecUnknown, false),
@ -2035,11 +2035,11 @@ void WebRtcVideoChannel2::WebRtcVideoSendStream::ReconfigureEncoder() {
encoder_config.encoder_specific_settings = ConfigureVideoEncoderSettings(
codec_settings.codec);
stream_->ReconfigureVideoEncoder(encoder_config);
stream_->ReconfigureVideoEncoder(encoder_config.Copy());
encoder_config.encoder_specific_settings = NULL;
parameters_.encoder_config = encoder_config;
parameters_.encoder_config = std::move(encoder_config);
}
void WebRtcVideoChannel2::WebRtcVideoSendStream::SetSend(bool send) {
@ -2232,13 +2232,14 @@ void WebRtcVideoChannel2::WebRtcVideoSendStream::RecreateWebRtcStream() {
parameters_.encoder_config.encoder_specific_settings =
ConfigureVideoEncoderSettings(parameters_.codec_settings->codec);
webrtc::VideoSendStream::Config config = parameters_.config;
webrtc::VideoSendStream::Config config = parameters_.config.Copy();
if (!config.rtp.rtx.ssrcs.empty() && config.rtp.rtx.payload_type == -1) {
LOG(LS_WARNING) << "RTX SSRCs configured but there's no configured RTX "
"payload type the set codec. Ignoring RTX.";
config.rtp.rtx.ssrcs.clear();
}
stream_ = call_->CreateVideoSendStream(config, parameters_.encoder_config);
stream_ = call_->CreateVideoSendStream(std::move(config),
parameters_.encoder_config.Copy());
parameters_.encoder_config.encoder_specific_settings = NULL;
pending_encoder_reconfiguration_ = false;

View File

@ -248,7 +248,7 @@ class WebRtcVideoChannel2 : public VideoMediaChannel, public webrtc::Transport {
WebRtcVideoSendStream(
webrtc::Call* call,
const StreamParams& sp,
const webrtc::VideoSendStream::Config& config,
webrtc::VideoSendStream::Config config,
const VideoOptions& options,
WebRtcVideoEncoderFactory* external_encoder_factory,
bool enable_cpu_overuse_detection,
@ -284,7 +284,7 @@ class WebRtcVideoChannel2 : public VideoMediaChannel, public webrtc::Transport {
// similar parameters depending on which options changed etc.
struct VideoSendStreamParameters {
VideoSendStreamParameters(
const webrtc::VideoSendStream::Config& config,
webrtc::VideoSendStream::Config config,
const VideoOptions& options,
int max_bitrate_bps,
const rtc::Optional<VideoCodecSettings>& codec_settings);

View File

@ -1153,7 +1153,8 @@ class WebRtcVideoChannel2Test : public WebRtcVideoEngine2Test {
EXPECT_TRUE(streams.size() > 0);
FakeVideoSendStream* stream = streams[streams.size() - 1];
webrtc::VideoEncoderConfig encoder_config = stream->GetEncoderConfig();
webrtc::VideoEncoderConfig encoder_config =
stream->GetEncoderConfig().Copy();
EXPECT_EQ(1, encoder_config.streams.size());
return encoder_config.streams[0].max_bitrate_bps;
}
@ -1645,7 +1646,8 @@ TEST_F(WebRtcVideoChannel2Test, UsesCorrectSettingsForScreencast) {
EXPECT_EQ(1, send_stream->GetNumberOfSwappedFrames());
// Verify non-screencast settings.
webrtc::VideoEncoderConfig encoder_config = send_stream->GetEncoderConfig();
webrtc::VideoEncoderConfig encoder_config =
send_stream->GetEncoderConfig().Copy();
EXPECT_EQ(webrtc::VideoEncoderConfig::ContentType::kRealtimeVideo,
encoder_config.content_type);
EXPECT_EQ(codec.width, encoder_config.streams.front().width);
@ -1666,7 +1668,7 @@ TEST_F(WebRtcVideoChannel2Test, UsesCorrectSettingsForScreencast) {
EXPECT_EQ(3, send_stream->GetNumberOfSwappedFrames());
// Verify screencast settings.
encoder_config = send_stream->GetEncoderConfig();
encoder_config = send_stream->GetEncoderConfig().Copy();
EXPECT_EQ(webrtc::VideoEncoderConfig::ContentType::kScreen,
encoder_config.content_type);
EXPECT_EQ(kScreenshareMinBitrateKbps * 1000,
@ -1693,7 +1695,7 @@ TEST_F(WebRtcVideoChannel2Test, NoRecreateStreamForScreencast) {
ASSERT_EQ(1, fake_call_->GetNumCreatedSendStreams());
FakeVideoSendStream* stream = fake_call_->GetVideoSendStreams().front();
webrtc::VideoEncoderConfig encoder_config = stream->GetEncoderConfig();
webrtc::VideoEncoderConfig encoder_config = stream->GetEncoderConfig().Copy();
EXPECT_EQ(webrtc::VideoEncoderConfig::ContentType::kRealtimeVideo,
encoder_config.content_type);
@ -1710,7 +1712,7 @@ TEST_F(WebRtcVideoChannel2Test, NoRecreateStreamForScreencast) {
ASSERT_EQ(stream, fake_call_->GetVideoSendStreams().front());
EXPECT_EQ(2, stream->GetNumberOfSwappedFrames());
encoder_config = stream->GetEncoderConfig();
encoder_config = stream->GetEncoderConfig().Copy();
EXPECT_EQ(webrtc::VideoEncoderConfig::ContentType::kScreen,
encoder_config.content_type);
@ -1723,7 +1725,7 @@ TEST_F(WebRtcVideoChannel2Test, NoRecreateStreamForScreencast) {
ASSERT_EQ(stream, fake_call_->GetVideoSendStreams().front());
EXPECT_EQ(3, stream->GetNumberOfSwappedFrames());
encoder_config = stream->GetEncoderConfig();
encoder_config = stream->GetEncoderConfig().Copy();
EXPECT_EQ(webrtc::VideoEncoderConfig::ContentType::kRealtimeVideo,
encoder_config.content_type);
@ -1752,10 +1754,11 @@ TEST_F(WebRtcVideoChannel2Test,
ASSERT_EQ(1u, fake_call_->GetVideoSendStreams().size());
FakeVideoSendStream* send_stream = fake_call_->GetVideoSendStreams().front();
webrtc::VideoEncoderConfig encoder_config = send_stream->GetEncoderConfig();
webrtc::VideoEncoderConfig encoder_config =
send_stream->GetEncoderConfig().Copy();
// Verify screencast settings.
encoder_config = send_stream->GetEncoderConfig();
encoder_config = send_stream->GetEncoderConfig().Copy();
EXPECT_EQ(webrtc::VideoEncoderConfig::ContentType::kScreen,
encoder_config.content_type);
ASSERT_EQ(1u, encoder_config.streams.size());
@ -2310,7 +2313,7 @@ TEST_F(WebRtcVideoChannel2Test, SetDefaultSendCodecs) {
const std::vector<uint32_t> rtx_ssrcs = MAKE_VECTOR(kRtxSsrcs1);
FakeVideoSendStream* stream = AddSendStream(
cricket::CreateSimWithRtxStreamParams("cname", ssrcs, rtx_ssrcs));
webrtc::VideoSendStream::Config config = stream->GetConfig();
webrtc::VideoSendStream::Config config = stream->GetConfig().Copy();
// Make sure NACK and FEC are enabled on the correct payload types.
EXPECT_EQ(1000, config.rtp.nack.rtp_history_ms);
@ -2329,7 +2332,7 @@ TEST_F(WebRtcVideoChannel2Test, SetSendCodecsWithoutFec) {
ASSERT_TRUE(channel_->SetSendParameters(parameters));
FakeVideoSendStream* stream = AddSendStream();
webrtc::VideoSendStream::Config config = stream->GetConfig();
webrtc::VideoSendStream::Config config = stream->GetConfig().Copy();
EXPECT_EQ(-1, config.rtp.fec.ulpfec_payload_type);
EXPECT_EQ(-1, config.rtp.fec.red_payload_type);
@ -2368,7 +2371,7 @@ TEST_F(WebRtcVideoChannel2Test, SetSendCodecsWithoutFecDisablesFec) {
ASSERT_TRUE(channel_->SetSendParameters(parameters));
FakeVideoSendStream* stream = AddSendStream();
webrtc::VideoSendStream::Config config = stream->GetConfig();
webrtc::VideoSendStream::Config config = stream->GetConfig().Copy();
EXPECT_EQ(kUlpfecCodec.id, config.rtp.fec.ulpfec_payload_type);
@ -2376,7 +2379,7 @@ TEST_F(WebRtcVideoChannel2Test, SetSendCodecsWithoutFecDisablesFec) {
ASSERT_TRUE(channel_->SetSendParameters(parameters));
stream = fake_call_->GetVideoSendStreams()[0];
ASSERT_TRUE(stream != NULL);
config = stream->GetConfig();
config = stream->GetConfig().Copy();
EXPECT_EQ(-1, config.rtp.fec.ulpfec_payload_type)
<< "SetSendCodec without FEC should disable current FEC.";
}

View File

@ -267,7 +267,7 @@ void CallTest::CreateVideoStreams() {
RTC_DCHECK(audio_receive_streams_.empty());
video_send_stream_ = sender_call_->CreateVideoSendStream(
video_send_config_, video_encoder_config_);
video_send_config_.Copy(), video_encoder_config_.Copy());
for (size_t i = 0; i < video_receive_configs_.size(); ++i) {
video_receive_streams_.push_back(receiver_call_->CreateVideoReceiveStream(
video_receive_configs_[i].Copy()));

View File

@ -12,6 +12,7 @@
#include "testing/gtest/include/gtest/gtest.h"
#include "webrtc/base/checks.h"
#include "webrtc/modules/video_coding/include/video_codec_interface.h"
#include "webrtc/system_wrappers/include/sleep.h"
@ -33,7 +34,7 @@ FakeEncoder::FakeEncoder(Clock* clock)
FakeEncoder::~FakeEncoder() {}
void FakeEncoder::SetMaxBitrate(int max_kbps) {
assert(max_kbps >= -1); // max_kbps == -1 disables it.
RTC_DCHECK_GE(max_kbps, -1); // max_kbps == -1 disables it.
max_target_bitrate_kbps_ = max_kbps;
}
@ -48,7 +49,7 @@ int32_t FakeEncoder::InitEncode(const VideoCodec* config,
int32_t FakeEncoder::Encode(const VideoFrame& input_image,
const CodecSpecificInfo* codec_specific_info,
const std::vector<FrameType>* frame_types) {
assert(config_.maxFramerate > 0);
RTC_DCHECK_GT(config_.maxFramerate, 0);
int64_t time_since_last_encode_ms = 1000 / config_.maxFramerate;
int64_t time_now_ms = clock_->TimeInMilliseconds();
const bool first_encode = last_encode_time_ms_ == 0;
@ -75,7 +76,7 @@ int32_t FakeEncoder::Encode(const VideoFrame& input_image,
bits_available = max_bits;
last_encode_time_ms_ = time_now_ms;
assert(config_.numberOfSimulcastStreams > 0);
RTC_DCHECK_GT(config_.numberOfSimulcastStreams, 0);
for (unsigned char i = 0; i < config_.numberOfSimulcastStreams; ++i) {
CodecSpecificInfo specifics;
memset(&specifics, 0, sizeof(specifics));
@ -97,6 +98,9 @@ int32_t FakeEncoder::Encode(const VideoFrame& input_image,
if (stream_bytes > sizeof(encoded_buffer_))
stream_bytes = sizeof(encoded_buffer_);
// Always encode something on the first frame.
if (min_stream_bits > bits_available && i > 0)
continue;
EncodedImage encoded(
encoded_buffer_, stream_bytes, sizeof(encoded_buffer_));
encoded._timeStamp = input_image.timestamp();
@ -104,10 +108,7 @@ int32_t FakeEncoder::Encode(const VideoFrame& input_image,
encoded._frameType = (*frame_types)[i];
encoded._encodedWidth = config_.simulcastStream[i].width;
encoded._encodedHeight = config_.simulcastStream[i].height;
// Always encode something on the first frame.
if (min_stream_bits > bits_available && i > 0)
continue;
assert(callback_ != NULL);
RTC_DCHECK(callback_ != NULL);
if (callback_->Encoded(encoded, &specifics, NULL) != 0)
return -1;
bits_available -= std::min(encoded._length * 8, bits_available);

View File

@ -35,8 +35,6 @@ source_set("video") {
"stats_counter.h",
"stream_synchronization.cc",
"stream_synchronization.h",
"video_capture_input.cc",
"video_capture_input.h",
"video_decoder.cc",
"video_encoder.cc",
"video_receive_stream.cc",
@ -64,6 +62,7 @@ source_set("video") {
"..:rtc_event_log",
"..:webrtc_common",
"../base:rtc_base_approved",
"../base:rtc_task_queue",
"../common_video",
"../modules/bitrate_controller",
"../modules/congestion_controller",
@ -94,10 +93,10 @@ if (rtc_include_tests) {
"send_statistics_proxy_unittest.cc",
"stats_counter_unittest.cc",
"stream_synchronization_unittest.cc",
"video_capture_input_unittest.cc",
"video_decoder_unittest.cc",
"video_encoder_unittest.cc",
"video_send_stream_tests.cc",
"vie_encoder_unittest.cc",
"vie_remb_unittest.cc",
]
configs += [ "..:common_config" ]

View File

@ -21,9 +21,14 @@ namespace webrtc {
class MockVieEncoder : public ViEEncoder {
public:
explicit MockVieEncoder(ProcessThread* process_thread)
: ViEEncoder(1, process_thread, nullptr, nullptr, nullptr) {}
~MockVieEncoder() {}
MockVieEncoder()
: ViEEncoder(1,
nullptr,
VideoSendStream::Config::EncoderSettings("fake", 0, nullptr),
nullptr,
nullptr,
nullptr) {}
~MockVieEncoder() { Stop(); }
MOCK_METHOD1(OnReceivedIntraFrameRequest, void(size_t));
MOCK_METHOD1(OnReceivedSLI, void(uint8_t picture_id));
@ -33,8 +38,7 @@ class MockVieEncoder : public ViEEncoder {
class VieKeyRequestTest : public ::testing::Test {
public:
VieKeyRequestTest()
: encoder_(&process_thread_),
simulated_clock_(123456789),
: simulated_clock_(123456789),
encoder_state_feedback_(
&simulated_clock_,
std::vector<uint32_t>(1, VieKeyRequestTest::kSsrc),
@ -42,7 +46,6 @@ class VieKeyRequestTest : public ::testing::Test {
protected:
const uint32_t kSsrc = 1234;
NiceMock<MockProcessThread> process_thread_;
MockVieEncoder encoder_;
SimulatedClock simulated_clock_;
EncoderStateFeedback encoder_state_feedback_;

View File

@ -1281,8 +1281,8 @@ class MultiStreamTest {
UpdateSendConfig(i, &send_config, &encoder_config, &frame_generators[i]);
send_streams[i] =
sender_call->CreateVideoSendStream(send_config, encoder_config);
send_streams[i] = sender_call->CreateVideoSendStream(
send_config.Copy(), encoder_config.Copy());
send_streams[i]->Start();
VideoReceiveStream::Config receive_config(receiver_transport.get());
@ -2486,7 +2486,7 @@ void EndToEndTest::TestSendsSetSsrcs(size_t num_ssrcs,
}
}
video_encoder_config_all_streams_ = *encoder_config;
video_encoder_config_all_streams_ = encoder_config->Copy();
if (send_single_ssrc_first_)
encoder_config->streams.resize(1);
}
@ -2505,7 +2505,7 @@ void EndToEndTest::TestSendsSetSsrcs(size_t num_ssrcs,
if (send_single_ssrc_first_) {
// Set full simulcast and continue with the rest of the SSRCs.
send_stream_->ReconfigureVideoEncoder(
video_encoder_config_all_streams_);
std::move(video_encoder_config_all_streams_));
EXPECT_TRUE(Wait()) << "Timed out while waiting on additional SSRCs.";
}
}
@ -3200,7 +3200,7 @@ void EndToEndTest::TestRtpStatePreservation(bool use_rtx,
// Use the same total bitrates when sending a single stream to avoid lowering
// the bitrate estimate and requiring a subsequent rampup.
VideoEncoderConfig one_stream = video_encoder_config_;
VideoEncoderConfig one_stream = video_encoder_config_.Copy();
one_stream.streams.resize(1);
for (size_t i = 1; i < video_encoder_config_.streams.size(); ++i) {
one_stream.streams.front().min_bitrate_bps +=
@ -3227,8 +3227,8 @@ void EndToEndTest::TestRtpStatePreservation(bool use_rtx,
sender_call_->DestroyVideoSendStream(video_send_stream_);
// Re-create VideoSendStream with only one stream.
video_send_stream_ =
sender_call_->CreateVideoSendStream(video_send_config_, one_stream);
video_send_stream_ = sender_call_->CreateVideoSendStream(
video_send_config_.Copy(), one_stream.Copy());
video_send_stream_->Start();
if (provoke_rtcpsr_before_rtp) {
// Rapid Resync Request forces sending RTCP Sender Report back.
@ -3246,18 +3246,18 @@ void EndToEndTest::TestRtpStatePreservation(bool use_rtx,
EXPECT_TRUE(observer.Wait()) << "Timed out waiting for single RTP packet.";
// Reconfigure back to use all streams.
video_send_stream_->ReconfigureVideoEncoder(video_encoder_config_);
video_send_stream_->ReconfigureVideoEncoder(video_encoder_config_.Copy());
observer.ResetExpectedSsrcs(kNumSsrcs);
EXPECT_TRUE(observer.Wait())
<< "Timed out waiting for all SSRCs to send packets.";
// Reconfigure down to one stream.
video_send_stream_->ReconfigureVideoEncoder(one_stream);
video_send_stream_->ReconfigureVideoEncoder(one_stream.Copy());
observer.ResetExpectedSsrcs(1);
EXPECT_TRUE(observer.Wait()) << "Timed out waiting for single RTP packet.";
// Reconfigure back to use all streams.
video_send_stream_->ReconfigureVideoEncoder(video_encoder_config_);
video_send_stream_->ReconfigureVideoEncoder(video_encoder_config_.Copy());
observer.ResetExpectedSsrcs(kNumSsrcs);
EXPECT_TRUE(observer.Wait())
<< "Timed out waiting for all SSRCs to send packets.";

View File

@ -193,7 +193,6 @@ OveruseFrameDetector::OveruseFrameDetector(
in_quick_rampup_(false),
current_rampup_delay_ms_(kStandardRampUpDelayMs),
usage_(new SendProcessingUsage(options)) {
RTC_DCHECK(metrics_observer);
processing_thread_.DetachFromThread();
}

View File

@ -75,7 +75,8 @@ SendStatisticsProxy::SendStatisticsProxy(
const VideoSendStream::Config& config,
VideoEncoderConfig::ContentType content_type)
: clock_(clock),
config_(config),
payload_name_(config.encoder_settings.payload_name),
rtp_config_(config.rtp),
content_type_(content_type),
start_ms_(clock->TimeInMilliseconds()),
last_sent_frame_timestamp_(0),
@ -86,14 +87,14 @@ SendStatisticsProxy::SendStatisticsProxy(
SendStatisticsProxy::~SendStatisticsProxy() {
rtc::CritScope lock(&crit_);
uma_container_->UpdateHistograms(config_, stats_);
uma_container_->UpdateHistograms(rtp_config_, stats_);
int64_t elapsed_sec = (clock_->TimeInMilliseconds() - start_ms_) / 1000;
RTC_LOGGED_HISTOGRAM_COUNTS_100000("WebRTC.Video.SendStreamLifetimeInSeconds",
elapsed_sec);
if (elapsed_sec >= metrics::kMinRunTimeInSeconds)
UpdateCodecTypeHistogram(config_.encoder_settings.payload_name);
UpdateCodecTypeHistogram(payload_name_);
}
SendStatisticsProxy::UmaSamplesContainer::UmaSamplesContainer(
@ -112,12 +113,11 @@ SendStatisticsProxy::UmaSamplesContainer::UmaSamplesContainer(
SendStatisticsProxy::UmaSamplesContainer::~UmaSamplesContainer() {}
void AccumulateRtpStats(const VideoSendStream::Stats& stats,
const VideoSendStream::Config& config,
void AccumulateRtxStats(const VideoSendStream::Stats& stats,
const std::vector<uint32_t>& rtx_ssrcs,
StreamDataCounters* total_rtp_stats,
StreamDataCounters* rtx_stats) {
for (auto it : stats.substreams) {
const std::vector<uint32_t> rtx_ssrcs = config.rtp.rtx.ssrcs;
if (std::find(rtx_ssrcs.begin(), rtx_ssrcs.end(), it.first) !=
rtx_ssrcs.end()) {
rtx_stats->Add(it.second.rtp_stats);
@ -128,7 +128,7 @@ void AccumulateRtpStats(const VideoSendStream::Stats& stats,
}
void SendStatisticsProxy::UmaSamplesContainer::UpdateHistograms(
const VideoSendStream::Config& config,
const VideoSendStream::Config::Rtp& rtp_config,
const VideoSendStream::Stats& current_stats) {
RTC_DCHECK(uma_prefix_ == kRealtimePrefix || uma_prefix_ == kScreenPrefix);
const int kIndex = uma_prefix_ == kScreenPrefix ? 1 : 0;
@ -262,7 +262,7 @@ void SendStatisticsProxy::UmaSamplesContainer::UpdateHistograms(
// UmaSamplesContainer, we save the initial state of the counters, so that
// we can calculate the delta here and aggregate over all ssrcs.
RtcpPacketTypeCounter counters;
for (uint32_t ssrc : config.rtp.ssrcs) {
for (uint32_t ssrc : rtp_config.ssrcs) {
auto kv = current_stats.substreams.find(ssrc);
if (kv == current_stats.substreams.end())
continue;
@ -298,10 +298,11 @@ void SendStatisticsProxy::UmaSamplesContainer::UpdateHistograms(
if (elapsed_sec >= metrics::kMinRunTimeInSeconds) {
StreamDataCounters rtp;
StreamDataCounters rtx;
AccumulateRtpStats(current_stats, config, &rtp, &rtx);
AccumulateRtxStats(current_stats, rtp_config.rtx.ssrcs, &rtp, &rtx);
StreamDataCounters start_rtp;
StreamDataCounters start_rtx;
AccumulateRtpStats(start_stats_, config, &start_rtp, &start_rtx);
AccumulateRtxStats(start_stats_, rtp_config.rtx.ssrcs, &start_rtp,
&start_rtx);
rtp.Subtract(start_rtp);
rtx.Subtract(start_rtx);
StreamDataCounters rtp_rtx = rtp;
@ -322,13 +323,13 @@ void SendStatisticsProxy::UmaSamplesContainer::UpdateHistograms(
kIndex, uma_prefix_ + "RetransmittedBitrateSentInKbps",
static_cast<int>(rtp_rtx.retransmitted.TotalBytes() * 8 /
elapsed_sec / 1000));
if (!config.rtp.rtx.ssrcs.empty()) {
if (!rtp_config.rtx.ssrcs.empty()) {
RTC_LOGGED_HISTOGRAMS_COUNTS_10000(
kIndex, uma_prefix_ + "RtxBitrateSentInKbps",
static_cast<int>(rtx.transmitted.TotalBytes() * 8 / elapsed_sec /
1000));
}
if (config.rtp.fec.red_payload_type != -1) {
if (rtp_config.fec.red_payload_type != -1) {
RTC_LOGGED_HISTOGRAMS_COUNTS_10000(
kIndex, uma_prefix_ + "FecBitrateSentInKbps",
static_cast<int>(rtp_rtx.fec.TotalBytes() * 8 / elapsed_sec /
@ -342,7 +343,7 @@ void SendStatisticsProxy::SetContentType(
VideoEncoderConfig::ContentType content_type) {
rtc::CritScope lock(&crit_);
if (content_type_ != content_type) {
uma_container_->UpdateHistograms(config_, stats_);
uma_container_->UpdateHistograms(rtp_config_, stats_);
uma_container_.reset(
new UmaSamplesContainer(GetUmaPrefix(content_type), stats_, clock_));
content_type_ = content_type;
@ -403,10 +404,10 @@ VideoSendStream::StreamStats* SendStatisticsProxy::GetStatsEntry(
return &it->second;
bool is_rtx = false;
if (std::find(config_.rtp.ssrcs.begin(), config_.rtp.ssrcs.end(), ssrc) ==
config_.rtp.ssrcs.end()) {
if (std::find(config_.rtp.rtx.ssrcs.begin(), config_.rtp.rtx.ssrcs.end(),
ssrc) == config_.rtp.rtx.ssrcs.end()) {
if (std::find(rtp_config_.ssrcs.begin(), rtp_config_.ssrcs.end(), ssrc) ==
rtp_config_.ssrcs.end()) {
if (std::find(rtp_config_.rtx.ssrcs.begin(), rtp_config_.rtx.ssrcs.end(),
ssrc) == rtp_config_.rtx.ssrcs.end()) {
return nullptr;
}
is_rtx = true;
@ -449,12 +450,12 @@ void SendStatisticsProxy::OnSendEncodedImage(
}
}
if (simulcast_idx >= config_.rtp.ssrcs.size()) {
if (simulcast_idx >= rtp_config_.ssrcs.size()) {
LOG(LS_ERROR) << "Encoded image outside simulcast range (" << simulcast_idx
<< " >= " << config_.rtp.ssrcs.size() << ").";
<< " >= " << rtp_config_.ssrcs.size() << ").";
return;
}
uint32_t ssrc = config_.rtp.ssrcs[simulcast_idx];
uint32_t ssrc = rtp_config_.ssrcs[simulcast_idx];
rtc::CritScope lock(&crit_);
VideoSendStream::StreamStats* stats = GetStatsEntry(ssrc);
@ -492,7 +493,7 @@ void SendStatisticsProxy::OnSendEncodedImage(
if (encoded_image.qp_ != -1 && codec_info) {
if (codec_info->codecType == kVideoCodecVP8) {
int spatial_idx = (config_.rtp.ssrcs.size() == 1)
int spatial_idx = (rtp_config_.ssrcs.size() == 1)
? -1
: static_cast<int>(simulcast_idx);
uma_container_->qp_counters_[spatial_idx].vp8.Add(encoded_image.qp_);

View File

@ -136,7 +136,8 @@ class SendStatisticsProxy : public CpuOveruseMetricsObserver,
EXCLUSIVE_LOCKS_REQUIRED(crit_);
Clock* const clock_;
const VideoSendStream::Config config_;
const std::string payload_name_;
const VideoSendStream::Config::Rtp rtp_config_;
rtc::CriticalSection crit_;
VideoEncoderConfig::ContentType content_type_ GUARDED_BY(crit_);
const int64_t start_ms_;
@ -154,7 +155,7 @@ class SendStatisticsProxy : public CpuOveruseMetricsObserver,
Clock* clock);
~UmaSamplesContainer();
void UpdateHistograms(const VideoSendStream::Config& config,
void UpdateHistograms(const VideoSendStream::Config::Rtp& rtp_config,
const VideoSendStream::Stats& current_stats);
const std::string uma_prefix_;

View File

@ -1,109 +0,0 @@
/*
* Copyright (c) 2012 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 "webrtc/video/video_capture_input.h"
#include "webrtc/base/checks.h"
#include "webrtc/base/logging.h"
#include "webrtc/base/trace_event.h"
#include "webrtc/modules/include/module_common_types.h"
#include "webrtc/modules/video_capture/video_capture_factory.h"
#include "webrtc/modules/video_processing/include/video_processing.h"
#include "webrtc/video/overuse_frame_detector.h"
#include "webrtc/video/send_statistics_proxy.h"
#include "webrtc/video/vie_encoder.h"
namespace webrtc {
namespace internal {
VideoCaptureInput::VideoCaptureInput(
rtc::Event* capture_event,
rtc::VideoSinkInterface<VideoFrame>* local_renderer,
SendStatisticsProxy* stats_proxy,
OveruseFrameDetector* overuse_detector)
: local_renderer_(local_renderer),
stats_proxy_(stats_proxy),
capture_event_(capture_event),
// TODO(danilchap): Pass clock from outside to ensure it is same clock
// rtcp module use to calculate offset since last frame captured
// to estimate rtp timestamp for SenderReport.
clock_(Clock::GetRealTimeClock()),
last_captured_timestamp_(0),
delta_ntp_internal_ms_(clock_->CurrentNtpInMilliseconds() -
clock_->TimeInMilliseconds()),
overuse_detector_(overuse_detector) {}
VideoCaptureInput::~VideoCaptureInput() {
}
void VideoCaptureInput::IncomingCapturedFrame(const VideoFrame& video_frame) {
// TODO(pbos): Remove local rendering, it should be handled by the client code
// if required.
if (local_renderer_)
local_renderer_->OnFrame(video_frame);
stats_proxy_->OnIncomingFrame(video_frame.width(), video_frame.height());
VideoFrame incoming_frame = video_frame;
// Local time in webrtc time base.
int64_t current_time = clock_->TimeInMilliseconds();
incoming_frame.set_render_time_ms(current_time);
// Capture time may come from clock with an offset and drift from clock_.
int64_t capture_ntp_time_ms;
if (video_frame.ntp_time_ms() != 0) {
capture_ntp_time_ms = video_frame.ntp_time_ms();
} else if (video_frame.render_time_ms() != 0) {
capture_ntp_time_ms = video_frame.render_time_ms() + delta_ntp_internal_ms_;
} else {
capture_ntp_time_ms = current_time + delta_ntp_internal_ms_;
}
incoming_frame.set_ntp_time_ms(capture_ntp_time_ms);
// Convert NTP time, in ms, to RTP timestamp.
const int kMsToRtpTimestamp = 90;
incoming_frame.set_timestamp(
kMsToRtpTimestamp * static_cast<uint32_t>(incoming_frame.ntp_time_ms()));
rtc::CritScope lock(&crit_);
if (incoming_frame.ntp_time_ms() <= last_captured_timestamp_) {
// We don't allow the same capture time for two frames, drop this one.
LOG(LS_WARNING) << "Same/old NTP timestamp ("
<< incoming_frame.ntp_time_ms()
<< " <= " << last_captured_timestamp_
<< ") for incoming frame. Dropping.";
return;
}
captured_frame_.reset(new VideoFrame);
captured_frame_->ShallowCopy(incoming_frame);
last_captured_timestamp_ = incoming_frame.ntp_time_ms();
overuse_detector_->FrameCaptured(*captured_frame_);
TRACE_EVENT_ASYNC_BEGIN1("webrtc", "Video", video_frame.render_time_ms(),
"render_time", video_frame.render_time_ms());
capture_event_->Set();
}
bool VideoCaptureInput::GetVideoFrame(VideoFrame* video_frame) {
rtc::CritScope lock(&crit_);
if (!captured_frame_)
return false;
*video_frame = *captured_frame_;
captured_frame_.reset();
return true;
}
} // namespace internal
} // namespace webrtc

View File

@ -1,70 +0,0 @@
/*
* Copyright (c) 2012 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 WEBRTC_VIDEO_VIDEO_CAPTURE_INPUT_H_
#define WEBRTC_VIDEO_VIDEO_CAPTURE_INPUT_H_
#include <memory>
#include <vector>
#include "webrtc/base/criticalsection.h"
#include "webrtc/base/event.h"
#include "webrtc/base/platform_thread.h"
#include "webrtc/base/thread_annotations.h"
#include "webrtc/common_types.h"
#include "webrtc/engine_configurations.h"
#include "webrtc/modules/video_capture/video_capture.h"
#include "webrtc/modules/video_coding/include/video_codec_interface.h"
#include "webrtc/modules/video_coding/include/video_coding.h"
#include "webrtc/modules/video_processing/include/video_processing.h"
#include "webrtc/system_wrappers/include/clock.h"
#include "webrtc/typedefs.h"
#include "webrtc/video_send_stream.h"
namespace webrtc {
class Config;
class OveruseFrameDetector;
class SendStatisticsProxy;
namespace internal {
class VideoCaptureInput : public webrtc::VideoCaptureInput {
public:
VideoCaptureInput(rtc::Event* capture_event,
rtc::VideoSinkInterface<VideoFrame>* local_renderer,
SendStatisticsProxy* send_stats_proxy,
OveruseFrameDetector* overuse_detector);
~VideoCaptureInput();
void IncomingCapturedFrame(const VideoFrame& video_frame) override;
bool GetVideoFrame(VideoFrame* frame);
private:
rtc::CriticalSection crit_;
rtc::VideoSinkInterface<VideoFrame>* const local_renderer_;
SendStatisticsProxy* const stats_proxy_;
rtc::Event* const capture_event_;
std::unique_ptr<VideoFrame> captured_frame_ GUARDED_BY(crit_);
Clock* const clock_;
// Used to make sure incoming time stamp is increasing for every frame.
int64_t last_captured_timestamp_;
// Delta used for translating between NTP and internal timestamps.
const int64_t delta_ntp_internal_ms_;
OveruseFrameDetector* const overuse_detector_;
};
} // namespace internal
} // namespace webrtc
#endif // WEBRTC_VIDEO_VIDEO_CAPTURE_INPUT_H_

View File

@ -1,255 +0,0 @@
/*
* Copyright (c) 2014 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 "webrtc/video/video_capture_input.h"
#include <memory>
#include <vector>
#include "testing/gtest/include/gtest/gtest.h"
#include "webrtc/base/event.h"
#include "webrtc/base/refcount.h"
#include "webrtc/test/fake_texture_frame.h"
#include "webrtc/test/frame_utils.h"
#include "webrtc/video/send_statistics_proxy.h"
// If an output frame does not arrive in 500ms, the test will fail.
#define FRAME_TIMEOUT_MS 500
namespace webrtc {
bool EqualFramesVector(const std::vector<std::unique_ptr<VideoFrame>>& frames1,
const std::vector<std::unique_ptr<VideoFrame>>& frames2);
std::unique_ptr<VideoFrame> CreateVideoFrame(uint8_t length);
class VideoCaptureInputTest : public ::testing::Test {
protected:
VideoCaptureInputTest()
: stats_proxy_(Clock::GetRealTimeClock(),
webrtc::VideoSendStream::Config(nullptr),
webrtc::VideoEncoderConfig::ContentType::kRealtimeVideo),
capture_event_(false, false) {}
virtual void SetUp() {
overuse_detector_.reset(
new OveruseFrameDetector(Clock::GetRealTimeClock(), CpuOveruseOptions(),
nullptr, nullptr, &stats_proxy_));
input_.reset(new internal::VideoCaptureInput(
&capture_event_, nullptr, &stats_proxy_, overuse_detector_.get()));
}
void AddInputFrame(VideoFrame* frame) {
input_->IncomingCapturedFrame(*frame);
}
void WaitOutputFrame() {
EXPECT_TRUE(capture_event_.Wait(FRAME_TIMEOUT_MS));
VideoFrame frame;
EXPECT_TRUE(input_->GetVideoFrame(&frame));
ASSERT_TRUE(frame.video_frame_buffer());
if (!frame.video_frame_buffer()->native_handle()) {
output_frame_ybuffers_.push_back(frame.video_frame_buffer()->DataY());
}
output_frames_.push_back(
std::unique_ptr<VideoFrame>(new VideoFrame(frame)));
}
SendStatisticsProxy stats_proxy_;
rtc::Event capture_event_;
std::unique_ptr<OveruseFrameDetector> overuse_detector_;
// Used to send input capture frames to VideoCaptureInput.
std::unique_ptr<internal::VideoCaptureInput> input_;
// Input capture frames of VideoCaptureInput.
std::vector<std::unique_ptr<VideoFrame>> input_frames_;
// Output delivered frames of VideoCaptureInput.
std::vector<std::unique_ptr<VideoFrame>> output_frames_;
// The pointers of Y plane buffers of output frames. This is used to verify
// the frame are swapped and not copied.
std::vector<const uint8_t*> output_frame_ybuffers_;
};
TEST_F(VideoCaptureInputTest, DoesNotRetainHandleNorCopyBuffer) {
// Indicate an output frame has arrived.
rtc::Event frame_destroyed_event(false, false);
class TestBuffer : public webrtc::I420Buffer {
public:
explicit TestBuffer(rtc::Event* event) : I420Buffer(5, 5), event_(event) {}
private:
friend class rtc::RefCountedObject<TestBuffer>;
~TestBuffer() override { event_->Set(); }
rtc::Event* const event_;
};
{
VideoFrame frame(
new rtc::RefCountedObject<TestBuffer>(&frame_destroyed_event), 1, 1,
kVideoRotation_0);
AddInputFrame(&frame);
WaitOutputFrame();
EXPECT_EQ(output_frames_[0]->video_frame_buffer().get(),
frame.video_frame_buffer().get());
output_frames_.clear();
}
EXPECT_TRUE(frame_destroyed_event.Wait(FRAME_TIMEOUT_MS));
}
TEST_F(VideoCaptureInputTest, TestNtpTimeStampSetIfRenderTimeSet) {
input_frames_.push_back(CreateVideoFrame(0));
input_frames_[0]->set_render_time_ms(5);
input_frames_[0]->set_ntp_time_ms(0);
AddInputFrame(input_frames_[0].get());
WaitOutputFrame();
EXPECT_GT(output_frames_[0]->ntp_time_ms(),
input_frames_[0]->render_time_ms());
}
TEST_F(VideoCaptureInputTest, TestRtpTimeStampSet) {
input_frames_.push_back(CreateVideoFrame(0));
input_frames_[0]->set_render_time_ms(0);
input_frames_[0]->set_ntp_time_ms(1);
input_frames_[0]->set_timestamp(0);
AddInputFrame(input_frames_[0].get());
WaitOutputFrame();
EXPECT_EQ(output_frames_[0]->timestamp(),
input_frames_[0]->ntp_time_ms() * 90);
}
TEST_F(VideoCaptureInputTest, DropsFramesWithSameOrOldNtpTimestamp) {
input_frames_.push_back(CreateVideoFrame(0));
input_frames_[0]->set_ntp_time_ms(17);
AddInputFrame(input_frames_[0].get());
WaitOutputFrame();
EXPECT_EQ(output_frames_[0]->timestamp(),
input_frames_[0]->ntp_time_ms() * 90);
// Repeat frame with the same NTP timestamp should drop.
AddInputFrame(input_frames_[0].get());
EXPECT_FALSE(capture_event_.Wait(FRAME_TIMEOUT_MS));
// As should frames with a decreased NTP timestamp.
input_frames_[0]->set_ntp_time_ms(input_frames_[0]->ntp_time_ms() - 1);
AddInputFrame(input_frames_[0].get());
EXPECT_FALSE(capture_event_.Wait(FRAME_TIMEOUT_MS));
// But delivering with an increased NTP timestamp should succeed.
input_frames_[0]->set_ntp_time_ms(4711);
AddInputFrame(input_frames_[0].get());
WaitOutputFrame();
EXPECT_EQ(output_frames_[1]->timestamp(),
input_frames_[0]->ntp_time_ms() * 90);
}
TEST_F(VideoCaptureInputTest, TestTextureFrames) {
const int kNumFrame = 3;
for (int i = 0 ; i < kNumFrame; ++i) {
test::FakeNativeHandle* dummy_handle = new test::FakeNativeHandle();
// Add one to |i| so that width/height > 0.
input_frames_.push_back(std::unique_ptr<VideoFrame>(new VideoFrame(
test::FakeNativeHandle::CreateFrame(dummy_handle, i + 1, i + 1, i + 1,
i + 1, webrtc::kVideoRotation_0))));
AddInputFrame(input_frames_[i].get());
WaitOutputFrame();
ASSERT_TRUE(output_frames_[i]->video_frame_buffer());
EXPECT_EQ(dummy_handle,
output_frames_[i]->video_frame_buffer()->native_handle());
}
EXPECT_TRUE(EqualFramesVector(input_frames_, output_frames_));
}
TEST_F(VideoCaptureInputTest, TestI420Frames) {
const int kNumFrame = 4;
std::vector<const uint8_t*> ybuffer_pointers;
for (int i = 0; i < kNumFrame; ++i) {
input_frames_.push_back(CreateVideoFrame(static_cast<uint8_t>(i + 1)));
ybuffer_pointers.push_back(input_frames_[i]->video_frame_buffer()->DataY());
AddInputFrame(input_frames_[i].get());
WaitOutputFrame();
}
EXPECT_TRUE(EqualFramesVector(input_frames_, output_frames_));
// Make sure the buffer is not copied.
for (int i = 0; i < kNumFrame; ++i)
EXPECT_EQ(ybuffer_pointers[i], output_frame_ybuffers_[i]);
}
TEST_F(VideoCaptureInputTest, TestI420FrameAfterTextureFrame) {
test::FakeNativeHandle* dummy_handle = new test::FakeNativeHandle();
input_frames_.push_back(std::unique_ptr<VideoFrame>(
new VideoFrame(test::FakeNativeHandle::CreateFrame(
dummy_handle, 1, 1, 1, 1, webrtc::kVideoRotation_0))));
AddInputFrame(input_frames_[0].get());
WaitOutputFrame();
ASSERT_TRUE(output_frames_[0]->video_frame_buffer());
EXPECT_EQ(dummy_handle,
output_frames_[0]->video_frame_buffer()->native_handle());
input_frames_.push_back(CreateVideoFrame(2));
AddInputFrame(input_frames_[1].get());
WaitOutputFrame();
EXPECT_TRUE(EqualFramesVector(input_frames_, output_frames_));
}
TEST_F(VideoCaptureInputTest, TestTextureFrameAfterI420Frame) {
input_frames_.push_back(CreateVideoFrame(1));
AddInputFrame(input_frames_[0].get());
WaitOutputFrame();
test::FakeNativeHandle* dummy_handle = new test::FakeNativeHandle();
input_frames_.push_back(std::unique_ptr<VideoFrame>(
new VideoFrame(test::FakeNativeHandle::CreateFrame(
dummy_handle, 1, 1, 2, 2, webrtc::kVideoRotation_0))));
AddInputFrame(input_frames_[1].get());
WaitOutputFrame();
EXPECT_TRUE(EqualFramesVector(input_frames_, output_frames_));
}
bool EqualFramesVector(
const std::vector<std::unique_ptr<VideoFrame>>& frames1,
const std::vector<std::unique_ptr<VideoFrame>>& frames2) {
if (frames1.size() != frames2.size())
return false;
for (size_t i = 0; i < frames1.size(); ++i) {
// Compare frame buffers, since we don't care about differing timestamps.
if (!test::FrameBufsEqual(frames1[i]->video_frame_buffer(),
frames2[i]->video_frame_buffer())) {
return false;
}
}
return true;
}
std::unique_ptr<VideoFrame> CreateVideoFrame(uint8_t data) {
std::unique_ptr<VideoFrame> frame(new VideoFrame());
const int width = 36;
const int height = 24;
const int kSizeY = width * height * 2;
uint8_t buffer[kSizeY];
memset(buffer, data, kSizeY);
frame->CreateFrame(buffer, buffer, buffer, width, height, width, width / 2,
width / 2, kVideoRotation_0);
frame->set_render_time_ms(data);
return frame;
}
} // namespace webrtc

View File

@ -1135,7 +1135,7 @@ void VideoQualityTest::RunWithVideoRenderer(const Params& params) {
SetupCommon(&transport, &transport);
video_send_config_.local_renderer = local_preview.get();
video_send_config_.pre_encode_callback = local_preview.get();
video_receive_configs_[stream_id].renderer = loopback_video.get();
video_send_config_.suspend_below_min_bitrate =
@ -1153,8 +1153,8 @@ void VideoQualityTest::RunWithVideoRenderer(const Params& params) {
if (params_.screenshare.enabled)
SetupScreenshare();
video_send_stream_ =
call->CreateVideoSendStream(video_send_config_, video_encoder_config_);
video_send_stream_ = call->CreateVideoSendStream(
video_send_config_.Copy(), video_encoder_config_.Copy());
VideoReceiveStream* receive_stream =
call->CreateVideoReceiveStream(video_receive_configs_[stream_id].Copy());
CreateCapturer(video_send_stream_->Input());

File diff suppressed because it is too large Load Diff

View File

@ -17,6 +17,8 @@
#include "webrtc/call/bitrate_allocator.h"
#include "webrtc/base/criticalsection.h"
#include "webrtc/base/event.h"
#include "webrtc/base/task_queue.h"
#include "webrtc/call.h"
#include "webrtc/common_video/libyuv/include/webrtc_libyuv.h"
#include "webrtc/modules/video_coding/protection_bitrate_calculator.h"
@ -24,7 +26,6 @@
#include "webrtc/video/payload_router.h"
#include "webrtc/video/send_delay_stats.h"
#include "webrtc/video/send_statistics_proxy.h"
#include "webrtc/video/video_capture_input.h"
#include "webrtc/video/vie_encoder.h"
#include "webrtc/video_receive_stream.h"
#include "webrtc/video_send_stream.h"
@ -37,32 +38,29 @@ class CongestionController;
class IvfFileWriter;
class ProcessThread;
class RtpRtcp;
class ViEEncoder;
class VieRemb;
class RtcEventLog;
namespace vcm {
class VideoSender;
} // namespace vcm
namespace internal {
class VideoSendStream : public webrtc::VideoSendStream,
public webrtc::CpuOveruseObserver,
public webrtc::BitrateAllocatorObserver,
public webrtc::VCMProtectionCallback,
public EncodedImageCallback {
class VideoSendStreamImpl;
// VideoSendStream implements webrtc::VideoSendStream.
// Internally, it delegates all public methods to VideoSendStreamImpl and / or
// VieEncoder. VideoSendStreamInternal is created and deleted on |worker_queue|.
class VideoSendStream : public webrtc::VideoSendStream {
public:
VideoSendStream(int num_cpu_cores,
ProcessThread* module_process_thread,
rtc::TaskQueue* worker_queue,
CallStats* call_stats,
CongestionController* congestion_controller,
BitrateAllocator* bitrate_allocator,
SendDelayStats* send_delay_stats,
VieRemb* remb,
RtcEventLog* event_log,
const VideoSendStream::Config& config,
const VideoEncoderConfig& encoder_config,
VideoSendStream::Config config,
VideoEncoderConfig encoder_config,
const std::map<uint32_t, RtpState>& suspended_ssrcs);
~VideoSendStream() override;
@ -74,101 +72,26 @@ class VideoSendStream : public webrtc::VideoSendStream,
void Start() override;
void Stop() override;
VideoCaptureInput* Input() override;
void ReconfigureVideoEncoder(const VideoEncoderConfig& config) override;
void ReconfigureVideoEncoder(VideoEncoderConfig) override;
Stats GetStats() override;
// webrtc::CpuOveruseObserver implementation.
void OveruseDetected() override;
void NormalUsage() override;
typedef std::map<uint32_t, RtpState> RtpStateMap;
RtpStateMap GetRtpStates() const;
int GetPaddingNeededBps() const;
// Implements BitrateAllocatorObserver.
uint32_t OnBitrateUpdated(uint32_t bitrate_bps,
uint8_t fraction_loss,
int64_t rtt) override;
protected:
// Implements webrtc::VCMProtectionCallback.
int ProtectionRequest(const FecProtectionParams* delta_params,
const FecProtectionParams* key_params,
uint32_t* sent_video_rate_bps,
uint32_t* sent_nack_rate_bps,
uint32_t* sent_fec_rate_bps) override;
RtpStateMap StopPermanentlyAndGetRtpStates();
private:
struct EncoderSettings {
VideoCodec video_codec;
VideoEncoderConfig config;
};
class ConstructionTask;
class DestructAndGetRtpStateTask;
// Implements EncodedImageCallback. The implementation routes encoded frames
// to the |payload_router_| and |config.pre_encode_callback| if set.
// Called on an arbitrary encoder callback thread.
EncodedImageCallback::Result OnEncodedImage(
const EncodedImage& encoded_image,
const CodecSpecificInfo* codec_specific_info,
const RTPFragmentationHeader* fragmentation) override;
static bool EncoderThreadFunction(void* obj);
void EncoderProcess();
void ConfigureProtection();
void ConfigureSsrcs();
rtc::ThreadChecker thread_checker_;
rtc::TaskQueue* const worker_queue_;
rtc::Event thread_sync_event_;
SendStatisticsProxy stats_proxy_;
const VideoSendStream::Config config_;
std::map<uint32_t, RtpState> suspended_ssrcs_;
ProcessThread* const module_process_thread_;
CallStats* const call_stats_;
CongestionController* const congestion_controller_;
BitrateAllocator* const bitrate_allocator_;
VieRemb* const remb_;
static const bool kEnableFrameRecording = false;
static const int kMaxLayers = 3;
std::unique_ptr<IvfFileWriter> file_writers_[kMaxLayers];
rtc::PlatformThread encoder_thread_;
rtc::Event encoder_wakeup_event_;
volatile int stop_encoder_thread_;
rtc::CriticalSection encoder_settings_crit_;
std::unique_ptr<EncoderSettings> pending_encoder_settings_
GUARDED_BY(encoder_settings_crit_);
uint32_t encoder_max_bitrate_bps_ GUARDED_BY(encoder_settings_crit_);
uint32_t encoder_target_rate_bps_ GUARDED_BY(encoder_settings_crit_);
enum class State {
kStopped, // VideoSendStream::Start has not yet been called.
kStarted, // VideoSendStream::Start has been called.
// VideoSendStream::Start has been called but the encoder have timed out.
kEncoderTimedOut,
};
rtc::Optional<State> pending_state_change_ GUARDED_BY(encoder_settings_crit_);
// Only used on the encoder thread.
rtc::ThreadChecker encoder_thread_checker_;
State state_ ACCESS_ON(&encoder_thread_checker_);
std::unique_ptr<EncoderSettings> current_encoder_settings_
ACCESS_ON(&encoder_thread_checker_);
OveruseFrameDetector overuse_detector_;
ViEEncoder vie_encoder_;
EncoderStateFeedback encoder_feedback_;
ProtectionBitrateCalculator protection_bitrate_calculator_;
vcm::VideoSender* const video_sender_;
const std::unique_ptr<RtcpBandwidthObserver> bandwidth_observer_;
// RtpRtcp modules, declared here as they use other members on construction.
const std::vector<RtpRtcp*> rtp_rtcp_modules_;
PayloadRouter payload_router_;
VideoCaptureInput input_;
std::unique_ptr<VideoSendStreamImpl> send_stream_;
std::unique_ptr<ViEEncoder> vie_encoder_;
};
} // namespace internal
} // namespace webrtc

View File

@ -866,7 +866,8 @@ TEST_F(VideoSendStreamTest, SuspendBelowMinBitrate) {
return SEND_PACKET;
}
// This method implements the rtc::VideoSinkInterface
// This method implements the rtc::VideoSinkInterface. This is called when
// a frame is provided to the VideoSendStream.
void OnFrame(const VideoFrame& video_frame) override {
rtc::CritScope lock(&crit_);
if (test_state_ == kDuringSuspend &&
@ -1205,7 +1206,7 @@ class MaxPaddingSetTest : public test::SendTest {
encoder_config->min_transmit_bitrate_bps = kMinTransmitBitrateBps;
encoder_config->content_type = VideoEncoderConfig::ContentType::kScreen;
}
encoder_config_ = *encoder_config;
encoder_config_ = encoder_config->Copy();
}
void OnCallsCreated(Call* sender_call, Call* receiver_call) override {
@ -1229,7 +1230,7 @@ class MaxPaddingSetTest : public test::SendTest {
packets_sent_ = 0;
encoder_config_.min_transmit_bitrate_bps = kMinTransmitBitrateBps;
encoder_config_.content_type = VideoEncoderConfig::ContentType::kScreen;
send_stream_->ReconfigureVideoEncoder(encoder_config_);
send_stream_->ReconfigureVideoEncoder(encoder_config_.Copy());
running_without_padding_ = false;
return SEND_PACKET;
}
@ -1324,7 +1325,7 @@ TEST_F(VideoSendStreamTest, CanReconfigureToUseStartBitrateAbovePreviousMax) {
video_encoder_config_.streams[0].max_bitrate_bps =
2 * bitrate_config.start_bitrate_bps;
video_send_stream_->ReconfigureVideoEncoder(video_encoder_config_);
video_send_stream_->ReconfigureVideoEncoder(video_encoder_config_.Copy());
// New bitrate should be reconfigured above the previous max. As there's no
// network connection this shouldn't be flaky, as no bitrate should've been
@ -1589,13 +1590,13 @@ TEST_F(VideoSendStreamTest, EncoderIsProperlyInitializedAndDestroyed) {
std::vector<VideoReceiveStream::Config>* receive_configs,
VideoEncoderConfig* encoder_config) override {
send_config->encoder_settings.encoder = this;
encoder_config_ = *encoder_config;
encoder_config_ = encoder_config->Copy();
}
void PerformTest() override {
EXPECT_TRUE(Wait()) << "Timed out while waiting for Encode.";
EXPECT_EQ(0u, num_releases());
stream_->ReconfigureVideoEncoder(encoder_config_);
stream_->ReconfigureVideoEncoder(std::move(encoder_config_));
EXPECT_EQ(0u, num_releases());
stream_->Stop();
// Encoder should not be released before destroying the VideoSendStream.
@ -1638,7 +1639,7 @@ TEST_F(VideoSendStreamTest, EncoderSetupPropagatesCommonEncoderConfigValues) {
std::vector<VideoReceiveStream::Config>* receive_configs,
VideoEncoderConfig* encoder_config) override {
send_config->encoder_settings.encoder = this;
encoder_config_ = *encoder_config;
encoder_config_ = encoder_config->Copy();
}
void OnVideoStreamsCreated(
@ -1667,7 +1668,7 @@ TEST_F(VideoSendStreamTest, EncoderSetupPropagatesCommonEncoderConfigValues) {
EXPECT_EQ(1u, num_initializations_) << "VideoEncoder not initialized.";
encoder_config_.content_type = VideoEncoderConfig::ContentType::kScreen;
stream_->ReconfigureVideoEncoder(encoder_config_);
stream_->ReconfigureVideoEncoder(std::move(encoder_config_));
EXPECT_TRUE(init_encode_event_.Wait(kDefaultTimeoutMs));
EXPECT_EQ(2u, num_initializations_)
<< "ReconfigureVideoEncoder did not reinitialize the encoder with "
@ -1714,7 +1715,7 @@ class VideoCodecConfigObserver : public test::SendTest,
}
encoder_config->encoder_specific_settings = &encoder_settings_;
encoder_config_ = *encoder_config;
encoder_config_ = encoder_config->Copy();
}
void OnVideoStreamsCreated(
@ -1741,7 +1742,7 @@ class VideoCodecConfigObserver : public test::SendTest,
ASSERT_EQ(1u, num_initializations_) << "VideoEncoder not initialized.";
encoder_settings_.frameDroppingOn = true;
stream_->ReconfigureVideoEncoder(encoder_config_);
stream_->ReconfigureVideoEncoder(std::move(encoder_config_));
ASSERT_TRUE(
init_encode_event_.Wait(VideoSendStreamTest::kDefaultTimeoutMs));
EXPECT_EQ(2u, num_initializations_)
@ -1938,6 +1939,8 @@ TEST_F(VideoSendStreamTest, ReconfigureBitratesSetsEncoderBitratesCorrectly) {
: SendTest(kDefaultTimeoutMs),
FakeEncoder(Clock::GetRealTimeClock()),
init_encode_event_(false, false),
bitrate_changed_event_(false, false),
target_bitrate_(0),
num_initializations_(0),
call_(nullptr),
send_stream_(nullptr) {}
@ -1946,6 +1949,8 @@ TEST_F(VideoSendStreamTest, ReconfigureBitratesSetsEncoderBitratesCorrectly) {
int32_t InitEncode(const VideoCodec* codecSettings,
int32_t numberOfCores,
size_t maxPayloadSize) override {
EXPECT_GE(codecSettings->startBitrate, codecSettings->minBitrate);
EXPECT_LE(codecSettings->startBitrate, codecSettings->maxBitrate);
if (num_initializations_ == 0) {
EXPECT_EQ(static_cast<unsigned int>(kMinBitrateKbps),
codecSettings->minBitrate);
@ -1964,8 +1969,9 @@ TEST_F(VideoSendStreamTest, ReconfigureBitratesSetsEncoderBitratesCorrectly) {
} else if (num_initializations_ == 2) {
EXPECT_EQ(static_cast<unsigned int>(kIncreasedMaxBitrateKbps),
codecSettings->maxBitrate);
EXPECT_EQ(static_cast<unsigned int>(kIncreasedStartBitrateKbps),
codecSettings->startBitrate);
// The start bitrate will be whatever the rate BitRateController
// has currently configured but in the span of the set max and min
// bitrate.
}
++num_initializations_;
init_encode_event_.Set();
@ -1973,6 +1979,23 @@ TEST_F(VideoSendStreamTest, ReconfigureBitratesSetsEncoderBitratesCorrectly) {
maxPayloadSize);
}
int32_t SetRates(uint32_t newBitRate, uint32_t frameRate) override {
{
rtc::CritScope lock(&crit_);
target_bitrate_ = newBitRate;
}
bitrate_changed_event_.Set();
return FakeEncoder::SetRates(newBitRate, frameRate);
}
void WaitForSetRates(uint32_t expected_bitrate) {
EXPECT_TRUE(
bitrate_changed_event_.Wait(VideoSendStreamTest::kDefaultTimeoutMs))
<< "Timed out while waiting encoder rate to be set.";
rtc::CritScope lock(&crit_);
EXPECT_EQ(expected_bitrate, target_bitrate_);
}
Call::Config GetSenderCallConfig() override {
Call::Config config;
config.bitrate_config.min_bitrate_bps = kMinBitrateKbps * 1000;
@ -1990,7 +2013,7 @@ TEST_F(VideoSendStreamTest, ReconfigureBitratesSetsEncoderBitratesCorrectly) {
// capped.
encoder_config->streams.front().min_bitrate_bps = kMinBitrateKbps * 1000;
encoder_config->streams.front().max_bitrate_bps = kMaxBitrateKbps * 1000;
encoder_config_ = *encoder_config;
encoder_config_ = encoder_config->Copy();
}
void OnCallsCreated(Call* sender_call, Call* receiver_call) override {
@ -2006,32 +2029,42 @@ TEST_F(VideoSendStreamTest, ReconfigureBitratesSetsEncoderBitratesCorrectly) {
void PerformTest() override {
ASSERT_TRUE(
init_encode_event_.Wait(VideoSendStreamTest::kDefaultTimeoutMs))
<< "Timed out while waiting encoder to be configured.";
<< "Timed out while waiting for encoder to be configured.";
WaitForSetRates(kStartBitrateKbps);
Call::Config::BitrateConfig bitrate_config;
bitrate_config.start_bitrate_bps = kIncreasedStartBitrateKbps * 1000;
bitrate_config.max_bitrate_bps = kIncreasedMaxBitrateKbps * 1000;
call_->SetBitrateConfig(bitrate_config);
EXPECT_TRUE(Wait())
<< "Timed out while waiting encoder to be configured.";
// Encoder rate is capped by EncoderConfig max_bitrate_bps.
WaitForSetRates(kMaxBitrateKbps);
encoder_config_.streams[0].min_bitrate_bps = 0;
encoder_config_.streams[0].max_bitrate_bps = kLowerMaxBitrateKbps * 1000;
send_stream_->ReconfigureVideoEncoder(encoder_config_);
send_stream_->ReconfigureVideoEncoder(encoder_config_.Copy());
ASSERT_TRUE(
init_encode_event_.Wait(VideoSendStreamTest::kDefaultTimeoutMs));
EXPECT_EQ(2, num_initializations_)
<< "Encoder should have been reconfigured with the new value.";
WaitForSetRates(kLowerMaxBitrateKbps);
encoder_config_.streams[0].target_bitrate_bps =
encoder_config_.streams[0].min_bitrate_bps;
encoder_config_.streams[0].max_bitrate_bps =
kIncreasedMaxBitrateKbps * 1000;
send_stream_->ReconfigureVideoEncoder(encoder_config_);
send_stream_->ReconfigureVideoEncoder(encoder_config_.Copy());
ASSERT_TRUE(
init_encode_event_.Wait(VideoSendStreamTest::kDefaultTimeoutMs));
EXPECT_EQ(3, num_initializations_)
<< "Encoder should have been reconfigured with the new value.";
// Expected target bitrate is the start bitrate set in the call to
// call_->SetBitrateConfig.
WaitForSetRates(kIncreasedStartBitrateKbps);
}
rtc::Event init_encode_event_;
rtc::Event bitrate_changed_event_;
rtc::CriticalSection crit_;
uint32_t target_bitrate_ GUARDED_BY(&crit_);
int num_initializations_;
webrtc::Call* call_;
webrtc::VideoSendStream* send_stream_;
@ -2153,7 +2186,7 @@ class Vp9HeaderObserver : public test::SendTest {
EXPECT_EQ(1u, encoder_config->streams.size());
encoder_config->streams[0].temporal_layer_thresholds_bps.resize(
vp9_settings_.numberOfTemporalLayers - 1);
encoder_config_ = *encoder_config;
encoder_config_ = encoder_config->Copy();
}
void PerformTest() override {

View File

@ -27,64 +27,315 @@
namespace webrtc {
namespace {
VideoCodecType PayloadNameToCodecType(const std::string& payload_name) {
if (payload_name == "VP8")
return kVideoCodecVP8;
if (payload_name == "VP9")
return kVideoCodecVP9;
if (payload_name == "H264")
return kVideoCodecH264;
return kVideoCodecGeneric;
}
VideoCodec VideoEncoderConfigToVideoCodec(const VideoEncoderConfig& config,
const std::string& payload_name,
int payload_type) {
const std::vector<VideoStream>& streams = config.streams;
static const int kEncoderMinBitrateKbps = 30;
RTC_DCHECK(!streams.empty());
RTC_DCHECK_GE(config.min_transmit_bitrate_bps, 0);
VideoCodec video_codec;
memset(&video_codec, 0, sizeof(video_codec));
video_codec.codecType = PayloadNameToCodecType(payload_name);
switch (config.content_type) {
case VideoEncoderConfig::ContentType::kRealtimeVideo:
video_codec.mode = kRealtimeVideo;
break;
case VideoEncoderConfig::ContentType::kScreen:
video_codec.mode = kScreensharing;
if (config.streams.size() == 1 &&
config.streams[0].temporal_layer_thresholds_bps.size() == 1) {
video_codec.targetBitrate =
config.streams[0].temporal_layer_thresholds_bps[0] / 1000;
}
break;
}
switch (video_codec.codecType) {
case kVideoCodecVP8: {
if (config.encoder_specific_settings) {
video_codec.codecSpecific.VP8 = *reinterpret_cast<const VideoCodecVP8*>(
config.encoder_specific_settings);
} else {
video_codec.codecSpecific.VP8 = VideoEncoder::GetDefaultVp8Settings();
}
video_codec.codecSpecific.VP8.numberOfTemporalLayers =
static_cast<unsigned char>(
streams.back().temporal_layer_thresholds_bps.size() + 1);
break;
}
case kVideoCodecVP9: {
if (config.encoder_specific_settings) {
video_codec.codecSpecific.VP9 = *reinterpret_cast<const VideoCodecVP9*>(
config.encoder_specific_settings);
if (video_codec.mode == kScreensharing) {
video_codec.codecSpecific.VP9.flexibleMode = true;
// For now VP9 screensharing use 1 temporal and 2 spatial layers.
RTC_DCHECK_EQ(video_codec.codecSpecific.VP9.numberOfTemporalLayers,
1);
RTC_DCHECK_EQ(video_codec.codecSpecific.VP9.numberOfSpatialLayers, 2);
}
} else {
video_codec.codecSpecific.VP9 = VideoEncoder::GetDefaultVp9Settings();
}
video_codec.codecSpecific.VP9.numberOfTemporalLayers =
static_cast<unsigned char>(
streams.back().temporal_layer_thresholds_bps.size() + 1);
break;
}
case kVideoCodecH264: {
if (config.encoder_specific_settings) {
video_codec.codecSpecific.H264 =
*reinterpret_cast<const VideoCodecH264*>(
config.encoder_specific_settings);
} else {
video_codec.codecSpecific.H264 = VideoEncoder::GetDefaultH264Settings();
}
break;
}
default:
// TODO(pbos): Support encoder_settings codec-agnostically.
RTC_DCHECK(!config.encoder_specific_settings)
<< "Encoder-specific settings for codec type not wired up.";
break;
}
strncpy(video_codec.plName, payload_name.c_str(), kPayloadNameSize - 1);
video_codec.plName[kPayloadNameSize - 1] = '\0';
video_codec.plType = payload_type;
video_codec.numberOfSimulcastStreams =
static_cast<unsigned char>(streams.size());
video_codec.minBitrate = streams[0].min_bitrate_bps / 1000;
if (video_codec.minBitrate < kEncoderMinBitrateKbps)
video_codec.minBitrate = kEncoderMinBitrateKbps;
RTC_DCHECK_LE(streams.size(), static_cast<size_t>(kMaxSimulcastStreams));
if (video_codec.codecType == kVideoCodecVP9) {
// If the vector is empty, bitrates will be configured automatically.
RTC_DCHECK(config.spatial_layers.empty() ||
config.spatial_layers.size() ==
video_codec.codecSpecific.VP9.numberOfSpatialLayers);
RTC_DCHECK_LE(video_codec.codecSpecific.VP9.numberOfSpatialLayers,
kMaxSimulcastStreams);
for (size_t i = 0; i < config.spatial_layers.size(); ++i)
video_codec.spatialLayers[i] = config.spatial_layers[i];
}
for (size_t i = 0; i < streams.size(); ++i) {
SimulcastStream* sim_stream = &video_codec.simulcastStream[i];
RTC_DCHECK_GT(streams[i].width, 0u);
RTC_DCHECK_GT(streams[i].height, 0u);
RTC_DCHECK_GT(streams[i].max_framerate, 0);
// Different framerates not supported per stream at the moment.
RTC_DCHECK_EQ(streams[i].max_framerate, streams[0].max_framerate);
RTC_DCHECK_GE(streams[i].min_bitrate_bps, 0);
RTC_DCHECK_GE(streams[i].target_bitrate_bps, streams[i].min_bitrate_bps);
RTC_DCHECK_GE(streams[i].max_bitrate_bps, streams[i].target_bitrate_bps);
RTC_DCHECK_GE(streams[i].max_qp, 0);
sim_stream->width = static_cast<uint16_t>(streams[i].width);
sim_stream->height = static_cast<uint16_t>(streams[i].height);
sim_stream->minBitrate = streams[i].min_bitrate_bps / 1000;
sim_stream->targetBitrate = streams[i].target_bitrate_bps / 1000;
sim_stream->maxBitrate = streams[i].max_bitrate_bps / 1000;
sim_stream->qpMax = streams[i].max_qp;
sim_stream->numberOfTemporalLayers = static_cast<unsigned char>(
streams[i].temporal_layer_thresholds_bps.size() + 1);
video_codec.width =
std::max(video_codec.width, static_cast<uint16_t>(streams[i].width));
video_codec.height =
std::max(video_codec.height, static_cast<uint16_t>(streams[i].height));
video_codec.minBitrate =
std::min(static_cast<uint16_t>(video_codec.minBitrate),
static_cast<uint16_t>(streams[i].min_bitrate_bps / 1000));
video_codec.maxBitrate += streams[i].max_bitrate_bps / 1000;
video_codec.qpMax = std::max(video_codec.qpMax,
static_cast<unsigned int>(streams[i].max_qp));
}
if (video_codec.maxBitrate == 0) {
// Unset max bitrate -> cap to one bit per pixel.
video_codec.maxBitrate =
(video_codec.width * video_codec.height * video_codec.maxFramerate) /
1000;
}
if (video_codec.maxBitrate < kEncoderMinBitrateKbps)
video_codec.maxBitrate = kEncoderMinBitrateKbps;
RTC_DCHECK_GT(streams[0].max_framerate, 0);
video_codec.maxFramerate = streams[0].max_framerate;
video_codec.expect_encode_from_texture = config.expect_encode_from_texture;
return video_codec;
}
// TODO(pbos): Lower these thresholds (to closer to 100%) when we handle
// pipelining encoders better (multiple input frames before something comes
// out). This should effectively turn off CPU adaptations for systems that
// remotely cope with the load right now.
CpuOveruseOptions GetCpuOveruseOptions(bool full_overuse_time) {
CpuOveruseOptions options;
if (full_overuse_time) {
options.low_encode_usage_threshold_percent = 150;
options.high_encode_usage_threshold_percent = 200;
}
return options;
}
} // namespace
class ViEEncoder::EncodeTask : public rtc::QueuedTask {
public:
EncodeTask(const VideoFrame& frame, ViEEncoder* vie_encoder)
: vie_encoder_(vie_encoder) {
frame_.ShallowCopy(frame);
++vie_encoder_->posted_frames_waiting_for_encode_;
}
private:
bool Run() override {
RTC_DCHECK_GT(vie_encoder_->posted_frames_waiting_for_encode_.Value(), 0);
if (--vie_encoder_->posted_frames_waiting_for_encode_ == 0) {
vie_encoder_->EncodeVideoFrame(frame_);
} else {
// There is a newer frame in flight. Do not encode this frame.
LOG(LS_VERBOSE)
<< "Incoming frame dropped due to that the encoder is blocked.";
}
return true;
}
VideoFrame frame_;
ViEEncoder* vie_encoder_;
};
ViEEncoder::ViEEncoder(uint32_t number_of_cores,
ProcessThread* module_process_thread,
SendStatisticsProxy* stats_proxy,
OveruseFrameDetector* overuse_detector,
EncodedImageCallback* sink)
: number_of_cores_(number_of_cores),
sink_(sink),
const VideoSendStream::Config::EncoderSettings& settings,
rtc::VideoSinkInterface<VideoFrame>* pre_encode_callback,
LoadObserver* overuse_callback,
EncodedFrameObserver* encoder_timing)
: shutdown_event_(true /* manual_reset */, false),
number_of_cores_(number_of_cores),
settings_(settings),
vp_(VideoProcessing::Create()),
video_sender_(Clock::GetRealTimeClock(), this, this),
overuse_detector_(Clock::GetRealTimeClock(),
GetCpuOveruseOptions(settings.full_overuse_time),
this,
encoder_timing,
stats_proxy),
load_observer_(overuse_callback),
stats_proxy_(stats_proxy),
overuse_detector_(overuse_detector),
time_of_last_frame_activity_ms_(std::numeric_limits<int64_t>::max()),
pre_encode_callback_(pre_encode_callback),
module_process_thread_(nullptr),
encoder_config_(),
encoder_start_bitrate_bps_(0),
last_observed_bitrate_bps_(0),
encoder_paused_and_dropped_frame_(false),
module_process_thread_(module_process_thread),
has_received_sli_(false),
picture_id_sli_(0),
has_received_rpsi_(false),
picture_id_rpsi_(0),
video_suspended_(false) {
module_process_thread_->RegisterModule(&video_sender_);
vp_->EnableTemporalDecimation(true);
}
clock_(Clock::GetRealTimeClock()),
last_captured_timestamp_(0),
delta_ntp_internal_ms_(clock_->CurrentNtpInMilliseconds() -
clock_->TimeInMilliseconds()),
encoder_queue_("EncoderQueue") {
vp_->EnableTemporalDecimation(false);
vcm::VideoSender* ViEEncoder::video_sender() {
return &video_sender_;
encoder_queue_.PostTask([this] {
RTC_DCHECK_RUN_ON(&encoder_queue_);
video_sender_.RegisterExternalEncoder(
settings_.encoder, settings_.payload_type, settings_.internal_source);
});
}
ViEEncoder::~ViEEncoder() {
RTC_DCHECK(shutdown_event_.Wait(0))
<< "Must call ::Stop() before destruction.";
}
void ViEEncoder::Stop() {
if (!encoder_queue_.IsCurrent()) {
encoder_queue_.PostTask([this] { Stop(); });
shutdown_event_.Wait(rtc::Event::kForever);
return;
}
RTC_DCHECK_RUN_ON(&encoder_queue_);
video_sender_.RegisterExternalEncoder(nullptr, settings_.payload_type, false);
shutdown_event_.Set();
}
void ViEEncoder::RegisterProcessThread(ProcessThread* module_process_thread) {
RTC_DCHECK(!module_process_thread_);
module_process_thread_ = module_process_thread;
module_process_thread_->RegisterModule(&overuse_detector_);
module_process_thread_->RegisterModule(&video_sender_);
module_process_thread_checker_.DetachFromThread();
}
void ViEEncoder::DeRegisterProcessThread() {
module_process_thread_->DeRegisterModule(&overuse_detector_);
module_process_thread_->DeRegisterModule(&video_sender_);
}
int32_t ViEEncoder::RegisterExternalEncoder(webrtc::VideoEncoder* encoder,
uint8_t pl_type,
bool internal_source) {
video_sender_.RegisterExternalEncoder(encoder, pl_type, internal_source);
return 0;
void ViEEncoder::SetSink(EncodedImageCallback* sink) {
encoder_queue_.PostTask([this, sink] {
RTC_DCHECK_RUN_ON(&encoder_queue_);
sink_ = sink;
});
}
int32_t ViEEncoder::DeRegisterExternalEncoder(uint8_t pl_type) {
video_sender_.RegisterExternalEncoder(nullptr, pl_type, false);
return 0;
void ViEEncoder::SetStartBitrate(int start_bitrate_bps) {
encoder_queue_.PostTask([this, start_bitrate_bps] {
RTC_DCHECK_RUN_ON(&encoder_queue_);
encoder_start_bitrate_bps_ = start_bitrate_bps;
});
}
void ViEEncoder::SetEncoder(const webrtc::VideoCodec& video_codec,
size_t max_data_payload_length) {
void ViEEncoder::ConfigureEncoder(const VideoEncoderConfig& config,
size_t max_data_payload_length) {
VideoCodec video_codec = VideoEncoderConfigToVideoCodec(
config, settings_.payload_name, settings_.payload_type);
encoder_queue_.PostTask([this, video_codec, max_data_payload_length] {
ConfigureEncoderInternal(video_codec, max_data_payload_length);
});
return;
}
void ViEEncoder::ConfigureEncoderInternal(const VideoCodec& video_codec,
size_t max_data_payload_length) {
RTC_DCHECK_RUN_ON(&encoder_queue_);
RTC_DCHECK_GE(encoder_start_bitrate_bps_, 0);
RTC_DCHECK(sink_);
// Setting target width and height for VPM.
RTC_CHECK_EQ(VPM_OK,
vp_->SetTargetResolution(video_codec.width, video_codec.height,
video_codec.maxFramerate));
{
rtc::CritScope lock(&data_cs_);
encoder_config_ = video_codec;
}
encoder_config_ = video_codec;
encoder_config_.startBitrate = encoder_start_bitrate_bps_ / 1000;
encoder_config_.startBitrate =
std::max(encoder_config_.startBitrate, video_codec.minBitrate);
encoder_config_.startBitrate =
std::min(encoder_config_.startBitrate, video_codec.maxBitrate);
bool success = video_sender_.RegisterSendCodec(
&video_codec, number_of_cores_,
&encoder_config_, number_of_cores_,
static_cast<uint32_t>(max_data_payload_length)) == VCM_OK;
if (!success) {
@ -110,15 +361,58 @@ void ViEEncoder::SetEncoder(const webrtc::VideoCodec& video_codec,
}
}
void ViEEncoder::IncomingCapturedFrame(const VideoFrame& video_frame) {
RTC_DCHECK_RUNS_SERIALIZED(&incoming_frame_race_checker_);
stats_proxy_->OnIncomingFrame(video_frame.width(), video_frame.height());
VideoFrame incoming_frame = video_frame;
// Local time in webrtc time base.
int64_t current_time = clock_->TimeInMilliseconds();
incoming_frame.set_render_time_ms(current_time);
// Capture time may come from clock with an offset and drift from clock_.
int64_t capture_ntp_time_ms;
if (video_frame.ntp_time_ms() != 0) {
capture_ntp_time_ms = video_frame.ntp_time_ms();
} else if (video_frame.render_time_ms() != 0) {
capture_ntp_time_ms = video_frame.render_time_ms() + delta_ntp_internal_ms_;
} else {
capture_ntp_time_ms = current_time + delta_ntp_internal_ms_;
}
incoming_frame.set_ntp_time_ms(capture_ntp_time_ms);
// Convert NTP time, in ms, to RTP timestamp.
const int kMsToRtpTimestamp = 90;
incoming_frame.set_timestamp(
kMsToRtpTimestamp * static_cast<uint32_t>(incoming_frame.ntp_time_ms()));
if (incoming_frame.ntp_time_ms() <= last_captured_timestamp_) {
// We don't allow the same capture time for two frames, drop this one.
LOG(LS_WARNING) << "Same/old NTP timestamp ("
<< incoming_frame.ntp_time_ms()
<< " <= " << last_captured_timestamp_
<< ") for incoming frame. Dropping.";
return;
}
last_captured_timestamp_ = incoming_frame.ntp_time_ms();
overuse_detector_.FrameCaptured(incoming_frame);
encoder_queue_.PostTask(
std::unique_ptr<rtc::QueuedTask>(new EncodeTask(incoming_frame, this)));
}
bool ViEEncoder::EncoderPaused() const {
RTC_DCHECK_RUN_ON(&encoder_queue_);
// Pause video if paused by caller or as long as the network is down or the
// pacer queue has grown too large in buffered mode.
// If the pacer queue has grown too large or the network is down,
// last_observed_bitrate_bps_ will be 0.
return video_suspended_ || last_observed_bitrate_bps_ == 0;
return last_observed_bitrate_bps_ == 0;
}
void ViEEncoder::TraceFrameDropStart() {
RTC_DCHECK_RUN_ON(&encoder_queue_);
// Start trace event only on the first frame after encoder is paused.
if (!encoder_paused_and_dropped_frame_) {
TRACE_EVENT_ASYNC_BEGIN0("webrtc", "EncoderPaused", this);
@ -128,6 +422,7 @@ void ViEEncoder::TraceFrameDropStart() {
}
void ViEEncoder::TraceFrameDropEnd() {
RTC_DCHECK_RUN_ON(&encoder_queue_);
// End trace event on first frame after encoder resumes, if frame was dropped.
if (encoder_paused_and_dropped_frame_) {
TRACE_EVENT_ASYNC_END0("webrtc", "EncoderPaused", this);
@ -136,17 +431,15 @@ void ViEEncoder::TraceFrameDropEnd() {
}
void ViEEncoder::EncodeVideoFrame(const VideoFrame& video_frame) {
VideoCodecType codec_type;
{
rtc::CritScope lock(&data_cs_);
time_of_last_frame_activity_ms_ = rtc::TimeMillis();
if (EncoderPaused()) {
TraceFrameDropStart();
return;
}
TraceFrameDropEnd();
codec_type = encoder_config_.codecType;
RTC_DCHECK_RUN_ON(&encoder_queue_);
if (pre_encode_callback_)
pre_encode_callback_->OnFrame(video_frame);
if (EncoderPaused()) {
TraceFrameDropStart();
return;
}
TraceFrameDropEnd();
TRACE_EVENT_ASYNC_STEP0("webrtc", "Video", video_frame.render_time_ms(),
"Encode");
@ -161,11 +454,10 @@ void ViEEncoder::EncodeVideoFrame(const VideoFrame& video_frame) {
}
}
if (codec_type == webrtc::kVideoCodecVP8) {
if (encoder_config_.codecType == webrtc::kVideoCodecVP8) {
webrtc::CodecSpecificInfo codec_specific_info;
codec_specific_info.codecType = webrtc::kVideoCodecVP8;
{
rtc::CritScope lock(&data_cs_);
codec_specific_info.codecSpecific.VP8.hasReceivedRPSI =
has_received_rpsi_;
codec_specific_info.codecSpecific.VP8.hasReceivedSLI =
@ -176,7 +468,6 @@ void ViEEncoder::EncodeVideoFrame(const VideoFrame& video_frame) {
picture_id_sli_;
has_received_sli_ = false;
has_received_rpsi_ = false;
}
video_sender_.AddVideoFrame(*frame_to_send, &codec_specific_info);
return;
@ -185,22 +476,21 @@ void ViEEncoder::EncodeVideoFrame(const VideoFrame& video_frame) {
}
void ViEEncoder::SendKeyFrame() {
if (!encoder_queue_.IsCurrent()) {
encoder_queue_.PostTask([this] { SendKeyFrame(); });
return;
}
RTC_DCHECK_RUN_ON(&encoder_queue_);
video_sender_.IntraFrameRequest(0);
}
int64_t ViEEncoder::time_of_last_frame_activity_ms() {
rtc::CritScope lock(&data_cs_);
return time_of_last_frame_activity_ms_;
}
EncodedImageCallback::Result ViEEncoder::OnEncodedImage(
const EncodedImage& encoded_image,
const CodecSpecificInfo* codec_specific_info,
const RTPFragmentationHeader* fragmentation) {
{
rtc::CritScope lock(&data_cs_);
time_of_last_frame_activity_ms_ = rtc::TimeMillis();
}
// Encoded is called on whatever thread the real encoder implementation run
// on. In the case of hardware encoders, there might be several encoders
// running in parallel on different threads.
if (stats_proxy_) {
stats_proxy_->OnSendEncodedImage(encoded_image, codec_specific_info);
}
@ -208,30 +498,45 @@ EncodedImageCallback::Result ViEEncoder::OnEncodedImage(
EncodedImageCallback::Result result =
sink_->OnEncodedImage(encoded_image, codec_specific_info, fragmentation);
overuse_detector_->FrameSent(encoded_image._timeStamp);
overuse_detector_.FrameSent(encoded_image._timeStamp);
return result;
}
void ViEEncoder::SendStatistics(uint32_t bit_rate,
uint32_t frame_rate,
const std::string& encoder_name) {
RTC_DCHECK(module_process_thread_checker_.CalledOnValidThread());
if (stats_proxy_)
stats_proxy_->OnEncoderStatsUpdate(frame_rate, bit_rate, encoder_name);
}
void ViEEncoder::OnReceivedSLI(uint8_t picture_id) {
rtc::CritScope lock(&data_cs_);
if (!encoder_queue_.IsCurrent()) {
encoder_queue_.PostTask([this, picture_id] { OnReceivedSLI(picture_id); });
return;
}
RTC_DCHECK_RUN_ON(&encoder_queue_);
picture_id_sli_ = picture_id;
has_received_sli_ = true;
}
void ViEEncoder::OnReceivedRPSI(uint64_t picture_id) {
rtc::CritScope lock(&data_cs_);
if (!encoder_queue_.IsCurrent()) {
encoder_queue_.PostTask([this, picture_id] { OnReceivedRPSI(picture_id); });
return;
}
RTC_DCHECK_RUN_ON(&encoder_queue_);
picture_id_rpsi_ = picture_id;
has_received_rpsi_ = true;
}
void ViEEncoder::OnReceivedIntraFrameRequest(size_t stream_index) {
if (!encoder_queue_.IsCurrent()) {
encoder_queue_.PostTask(
[this, stream_index] { OnReceivedIntraFrameRequest(stream_index); });
return;
}
RTC_DCHECK_RUN_ON(&encoder_queue_);
// Key frame request from remote side, signal to VCM.
TRACE_EVENT0("webrtc", "OnKeyFrameRequest");
video_sender_.IntraFrameRequest(stream_index);
@ -240,29 +545,29 @@ void ViEEncoder::OnReceivedIntraFrameRequest(size_t stream_index) {
void ViEEncoder::OnBitrateUpdated(uint32_t bitrate_bps,
uint8_t fraction_lost,
int64_t round_trip_time_ms) {
if (!encoder_queue_.IsCurrent()) {
encoder_queue_.PostTask(
[this, bitrate_bps, fraction_lost, round_trip_time_ms] {
OnBitrateUpdated(bitrate_bps, fraction_lost, round_trip_time_ms);
});
return;
}
RTC_DCHECK_RUN_ON(&encoder_queue_);
RTC_DCHECK(sink_) << "sink_ must be set before the encoder is active.";
LOG(LS_VERBOSE) << "OnBitrateUpdated, bitrate " << bitrate_bps
<< " packet loss " << static_cast<int>(fraction_lost)
<< " rtt " << round_trip_time_ms;
video_sender_.SetChannelParameters(bitrate_bps, fraction_lost,
round_trip_time_ms);
bool video_suspension_changed;
encoder_start_bitrate_bps_ =
bitrate_bps != 0 ? bitrate_bps : encoder_start_bitrate_bps_;
bool video_is_suspended = bitrate_bps == 0;
{
rtc::CritScope lock(&data_cs_);
last_observed_bitrate_bps_ = bitrate_bps;
video_suspension_changed = video_suspended_ != video_is_suspended;
video_suspended_ = video_is_suspended;
// Set |time_of_last_frame_activity_ms_| to now if this is the first time
// the encoder is supposed to produce encoded frames.
// TODO(perkj): Remove this hack. It is here to avoid a race that the
// encoder report that it has timed out before it has processed the first
// frame.
if (last_observed_bitrate_bps_ != 0 &&
time_of_last_frame_activity_ms_ ==
std::numeric_limits<int64_t>::max()) {
time_of_last_frame_activity_ms_ = rtc::TimeMillis();
}
}
bool video_suspension_changed =
video_is_suspended != (last_observed_bitrate_bps_ == 0);
last_observed_bitrate_bps_ = bitrate_bps;
if (stats_proxy_ && video_suspension_changed) {
LOG(LS_INFO) << "Video suspend state changed to: "
@ -271,4 +576,19 @@ void ViEEncoder::OnBitrateUpdated(uint32_t bitrate_bps,
}
}
void ViEEncoder::OveruseDetected() {
RTC_DCHECK_RUN_ON(&module_process_thread_checker_);
// TODO(perkj): When ViEEncoder inherit rtc::VideoSink instead of
// VideoCaptureInput |load_observer_| should be removed and overuse be
// expressed as rtc::VideoSinkWants instead.
if (load_observer_)
load_observer_->OnLoadUpdate(LoadObserver::kOveruse);
}
void ViEEncoder::NormalUsage() {
RTC_DCHECK_RUN_ON(&module_process_thread_checker_);
if (load_observer_)
load_observer_->OnLoadUpdate(LoadObserver::kUnderuse);
}
} // namespace webrtc

View File

@ -16,82 +16,72 @@
#include <vector>
#include "webrtc/base/criticalsection.h"
#include "webrtc/base/scoped_ref_ptr.h"
#include "webrtc/base/thread_annotations.h"
#include "webrtc/base/event.h"
#include "webrtc/base/sequenced_task_checker.h"
#include "webrtc/base/task_queue.h"
#include "webrtc/call.h"
#include "webrtc/common_types.h"
#include "webrtc/video_encoder.h"
#include "webrtc/media/base/videosinkinterface.h"
#include "webrtc/modules/rtp_rtcp/include/rtp_rtcp_defines.h"
#include "webrtc/modules/video_coding/include/video_coding_defines.h"
#include "webrtc/modules/video_coding/video_coding_impl.h"
#include "webrtc/modules/video_processing/include/video_processing.h"
#include "webrtc/system_wrappers/include/atomic32.h"
#include "webrtc/video/overuse_frame_detector.h"
#include "webrtc/video_encoder.h"
#include "webrtc/video_send_stream.h"
#include "webrtc/typedefs.h"
namespace webrtc {
class Config;
class EncodedImageCallback;
class OveruseFrameDetector;
class PacedSender;
class ProcessThread;
class SendStatisticsProxy;
class ViEBitrateObserver;
class ViEEffectFilter;
class VideoEncoder;
// VieEncoder represent a video encoder that accepts raw video frames as input
// and produces an encoded bit stream.
// Usage:
// 1. Instantiate
// 2. Call Init
// 3. Call RegisterExternalEncoder if available.
// 4. Call SetEncoder with the codec settings and the object that shall receive
// the encoded bit stream.
// 5. For each available raw video frame call EncodeVideoFrame.
class ViEEncoder : public EncodedImageCallback,
public VCMSendStatisticsCallback {
// Instantiate.
// Call SetStartRate and SetSink.
// Call ConfigureEncoder with the codec settings.
// Provide frames to encode by calling IncomingCapturedFrame.
// Call Stop() when done.
class ViEEncoder : public VideoCaptureInput,
public EncodedImageCallback,
public VCMSendStatisticsCallback,
public CpuOveruseObserver {
public:
friend class ViEBitrateObserver;
ViEEncoder(uint32_t number_of_cores,
ProcessThread* module_process_thread,
SendStatisticsProxy* stats_proxy,
OveruseFrameDetector* overuse_detector,
EncodedImageCallback* sink);
const webrtc::VideoSendStream::Config::EncoderSettings& settings,
rtc::VideoSinkInterface<VideoFrame>* pre_encode_callback,
LoadObserver* overuse_callback,
EncodedFrameObserver* encoder_timing);
~ViEEncoder();
// RegisterProcessThread register |module_process_thread| with those objects
// that use it. Registration has to happen on the thread where
// |module_process_thread| was created (libjingle's worker thread).
// TODO(perkj): Replace the use of |module_process_thread| with a TaskQueue.
void RegisterProcessThread(ProcessThread* module_process_thread);
void DeRegisterProcessThread();
vcm::VideoSender* video_sender();
void SetSink(EncodedImageCallback* sink);
// Returns the id of the owning channel.
int Owner() const;
// TODO(perkj): Can we remove VideoCodec.startBitrate ?
void SetStartBitrate(int start_bitrate_bps);
// Codec settings.
int32_t RegisterExternalEncoder(VideoEncoder* encoder,
uint8_t pl_type,
bool internal_source);
int32_t DeRegisterExternalEncoder(uint8_t pl_type);
void SetEncoder(const VideoCodec& video_codec,
size_t max_data_payload_length);
void ConfigureEncoder(const VideoEncoderConfig& config,
size_t max_data_payload_length);
// Permanently stop encoding. After this method has returned, it is
// guaranteed that no encoded frames will be delivered to the sink.
void Stop();
// Implements VideoCaptureInput.
// TODO(perkj): Refactor ViEEncoder to inherit rtc::VideoSink instead of
// VideoCaptureInput.
void IncomingCapturedFrame(const VideoFrame& video_frame) override;
void EncodeVideoFrame(const VideoFrame& video_frame);
void SendKeyFrame();
// Returns the time when the encoder last received an input frame or produced
// an encoded frame.
int64_t time_of_last_frame_activity_ms();
// Implements EncodedImageCallback.
EncodedImageCallback::Result OnEncodedImage(
const EncodedImage& encoded_image,
const CodecSpecificInfo* codec_specific_info,
const RTPFragmentationHeader* fragmentation) override;
// Implements VideoSendStatisticsCallback.
void SendStatistics(uint32_t bit_rate,
uint32_t frame_rate,
const std::string& encoder_name) override;
// virtual to test EncoderStateFeedback with mocks.
virtual void OnReceivedIntraFrameRequest(size_t stream_index);
virtual void OnReceivedSLI(uint8_t picture_id);
@ -102,37 +92,69 @@ class ViEEncoder : public EncodedImageCallback,
int64_t round_trip_time_ms);
private:
bool EncoderPaused() const EXCLUSIVE_LOCKS_REQUIRED(data_cs_);
void TraceFrameDropStart() EXCLUSIVE_LOCKS_REQUIRED(data_cs_);
void TraceFrameDropEnd() EXCLUSIVE_LOCKS_REQUIRED(data_cs_);
class EncodeTask;
void ConfigureEncoderInternal(const VideoCodec& video_codec,
size_t max_data_payload_length);
// Implements VideoSendStatisticsCallback.
void SendStatistics(uint32_t bit_rate,
uint32_t frame_rate,
const std::string& encoder_name) override;
void EncodeVideoFrame(const VideoFrame& frame);
// Implements EncodedImageCallback.
EncodedImageCallback::Result OnEncodedImage(
const EncodedImage& encoded_image,
const CodecSpecificInfo* codec_specific_info,
const RTPFragmentationHeader* fragmentation) override;
// webrtc::CpuOveruseObserver implementation.
void OveruseDetected() override;
void NormalUsage() override;
bool EncoderPaused() const;
void TraceFrameDropStart();
void TraceFrameDropEnd();
rtc::Event shutdown_event_;
const uint32_t number_of_cores_;
EncodedImageCallback* const sink_;
EncodedImageCallback* sink_;
const VideoSendStream::Config::EncoderSettings settings_;
const std::unique_ptr<VideoProcessing> vp_;
vcm::VideoSender video_sender_;
rtc::CriticalSection data_cs_;
vcm::VideoSender video_sender_ ACCESS_ON(&encoder_queue_);
OveruseFrameDetector overuse_detector_;
LoadObserver* const load_observer_ ACCESS_ON(&module_process_thread_checker_);
SendStatisticsProxy* const stats_proxy_;
OveruseFrameDetector* const overuse_detector_;
// The time we last received an input frame or encoded frame. This is used to
// track when video is stopped long enough that we also want to stop sending
// padding.
int64_t time_of_last_frame_activity_ms_ GUARDED_BY(data_cs_);
VideoCodec encoder_config_ GUARDED_BY(data_cs_);
uint32_t last_observed_bitrate_bps_ GUARDED_BY(data_cs_);
bool encoder_paused_and_dropped_frame_ GUARDED_BY(data_cs_);
rtc::VideoSinkInterface<VideoFrame>* const pre_encode_callback_;
ProcessThread* module_process_thread_;
rtc::ThreadChecker module_process_thread_checker_;
bool has_received_sli_ GUARDED_BY(data_cs_);
uint8_t picture_id_sli_ GUARDED_BY(data_cs_);
bool has_received_rpsi_ GUARDED_BY(data_cs_);
uint64_t picture_id_rpsi_ GUARDED_BY(data_cs_);
VideoCodec encoder_config_ ACCESS_ON(&encoder_queue_);
bool video_suspended_ GUARDED_BY(data_cs_);
int encoder_start_bitrate_bps_ ACCESS_ON(&encoder_queue_);
uint32_t last_observed_bitrate_bps_ ACCESS_ON(&encoder_queue_);
bool encoder_paused_and_dropped_frame_ ACCESS_ON(&encoder_queue_);
bool has_received_sli_ ACCESS_ON(&encoder_queue_);
uint8_t picture_id_sli_ ACCESS_ON(&encoder_queue_);
bool has_received_rpsi_ ACCESS_ON(&encoder_queue_);
uint64_t picture_id_rpsi_ ACCESS_ON(&encoder_queue_);
Clock* const clock_;
rtc::RaceChecker incoming_frame_race_checker_;
Atomic32 posted_frames_waiting_for_encode_;
// Used to make sure incoming time stamp is increasing for every frame.
int64_t last_captured_timestamp_ GUARDED_BY(incoming_frame_race_checker_);
// Delta used for translating between NTP and internal timestamps.
const int64_t delta_ntp_internal_ms_;
// All public methods are proxied to |encoder_queue_|. It must must be
// destroyed first to make sure no tasks are run that use other members.
rtc::TaskQueue encoder_queue_;
};
} // namespace webrtc

View File

@ -0,0 +1,255 @@
/*
* Copyright (c) 2016 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 "testing/gtest/include/gtest/gtest.h"
#include "webrtc/base/logging.h"
#include "webrtc/test/encoder_settings.h"
#include "webrtc/test/fake_encoder.h"
#include "webrtc/video/send_statistics_proxy.h"
#include "webrtc/video/vie_encoder.h"
namespace webrtc {
class ViEEncoderTest : public ::testing::Test {
public:
static const int kDefaultTimeoutMs = 30 * 1000;
ViEEncoderTest()
: video_send_config_(VideoSendStream::Config(nullptr)),
fake_encoder_(),
stats_proxy_(Clock::GetRealTimeClock(),
video_send_config_,
webrtc::VideoEncoderConfig::ContentType::kRealtimeVideo),
sink_(&fake_encoder_) {}
void SetUp() override {
video_send_config_ = VideoSendStream::Config(nullptr);
video_send_config_.encoder_settings.encoder = &fake_encoder_;
video_send_config_.encoder_settings.payload_name = "FAKE";
video_send_config_.encoder_settings.payload_type = 125;
video_encoder_config_.streams = test::CreateVideoStreams(1);
vie_encoder_.reset(new ViEEncoder(
1 /* number_of_cores */, &stats_proxy_,
video_send_config_.encoder_settings, nullptr /* pre_encode_callback */,
nullptr /* overuse_callback */, nullptr /* encoder_timing */));
vie_encoder_->SetSink(&sink_);
vie_encoder_->SetStartBitrate(10000);
vie_encoder_->ConfigureEncoder(video_encoder_config_, 1440);
}
VideoFrame CreateFrame(int64_t ntp_ts, rtc::Event* destruction_event) const {
class TestBuffer : public webrtc::I420Buffer {
public:
TestBuffer(rtc::Event* event, int width, int height)
: I420Buffer(width, height), event_(event) {}
private:
friend class rtc::RefCountedObject<TestBuffer>;
~TestBuffer() override {
if (event_)
event_->Set();
}
rtc::Event* const event_;
};
VideoFrame frame(
new rtc::RefCountedObject<TestBuffer>(
destruction_event,
static_cast<int>(video_encoder_config_.streams[0].width),
static_cast<int>(video_encoder_config_.streams[0].height)),
99, 99, kVideoRotation_0);
frame.set_ntp_time_ms(ntp_ts);
return frame;
}
class TestEncoder : public test::FakeEncoder {
public:
TestEncoder()
: FakeEncoder(Clock::GetRealTimeClock()),
continue_encode_event_(false, false) {}
int32_t Encode(const VideoFrame& input_image,
const CodecSpecificInfo* codec_specific_info,
const std::vector<FrameType>* frame_types) override {
bool block_encode;
{
rtc::CritScope lock(&crit_);
EXPECT_GT(input_image.timestamp(), timestamp_);
EXPECT_GT(input_image.ntp_time_ms(), ntp_time_ms_);
EXPECT_EQ(input_image.timestamp(), input_image.ntp_time_ms() * 90);
timestamp_ = input_image.timestamp();
ntp_time_ms_ = input_image.ntp_time_ms();
block_encode = block_next_encode_;
block_next_encode_ = false;
}
int32_t result =
FakeEncoder::Encode(input_image, codec_specific_info, frame_types);
if (block_encode)
continue_encode_event_.Wait(kDefaultTimeoutMs);
return result;
}
void BlockNextEncode() {
rtc::CritScope lock(&crit_);
block_next_encode_ = true;
}
void ContinueEncode() { continue_encode_event_.Set(); }
void CheckLastTimeStampsMatch(int64_t ntp_time_ms,
uint32_t timestamp) const {
rtc::CritScope lock(&crit_);
EXPECT_EQ(timestamp_, timestamp);
EXPECT_EQ(ntp_time_ms_, ntp_time_ms);
}
private:
rtc::CriticalSection crit_;
bool block_next_encode_ = false;
rtc::Event continue_encode_event_;
uint32_t timestamp_ = 0;
int64_t ntp_time_ms_ = 0;
};
class TestSink : public EncodedImageCallback {
public:
explicit TestSink(TestEncoder* test_encoder)
: test_encoder_(test_encoder), encoded_frame_event_(false, false) {}
int32_t Encoded(const EncodedImage& encoded_image,
const CodecSpecificInfo* codec_specific_info,
const RTPFragmentationHeader* fragmentation) override {
rtc::CritScope lock(&crit_);
EXPECT_TRUE(expect_frames_);
timestamp_ = encoded_image._timeStamp;
encoded_frame_event_.Set();
return 0;
}
void WaitForEncodedFrame(int64_t expected_ntp_time) {
uint32_t timestamp = 0;
encoded_frame_event_.Wait(kDefaultTimeoutMs);
{
rtc::CritScope lock(&crit_);
timestamp = timestamp_;
}
test_encoder_->CheckLastTimeStampsMatch(expected_ntp_time, timestamp);
}
void SetExpectNoFrames() {
rtc::CritScope lock(&crit_);
expect_frames_ = false;
}
private:
rtc::CriticalSection crit_;
TestEncoder* test_encoder_;
rtc::Event encoded_frame_event_;
uint32_t timestamp_ = 0;
bool expect_frames_ = true;
};
VideoSendStream::Config video_send_config_;
VideoEncoderConfig video_encoder_config_;
TestEncoder fake_encoder_;
SendStatisticsProxy stats_proxy_;
TestSink sink_;
std::unique_ptr<ViEEncoder> vie_encoder_;
};
TEST_F(ViEEncoderTest, EncodeOneFrame) {
const int kTargetBitrateBps = 100000;
vie_encoder_->OnBitrateUpdated(kTargetBitrateBps, 0, 0);
rtc::Event frame_destroyed_event(false, false);
vie_encoder_->IncomingCapturedFrame(CreateFrame(1, &frame_destroyed_event));
sink_.WaitForEncodedFrame(1);
frame_destroyed_event.Wait(kDefaultTimeoutMs);
vie_encoder_->Stop();
}
TEST_F(ViEEncoderTest, DropsFramesBeforeFirstOnBitrateUpdated) {
// Dropped since no target bitrate has been set.
rtc::Event frame_destroyed_event(false, false);
vie_encoder_->IncomingCapturedFrame(CreateFrame(1, &frame_destroyed_event));
frame_destroyed_event.Wait(kDefaultTimeoutMs);
const int kTargetBitrateBps = 100000;
vie_encoder_->OnBitrateUpdated(kTargetBitrateBps, 0, 0);
vie_encoder_->IncomingCapturedFrame(CreateFrame(2, nullptr));
sink_.WaitForEncodedFrame(2);
vie_encoder_->Stop();
}
TEST_F(ViEEncoderTest, DropsFramesWhenRateSetToZero) {
const int kTargetBitrateBps = 100000;
vie_encoder_->OnBitrateUpdated(kTargetBitrateBps, 0, 0);
vie_encoder_->IncomingCapturedFrame(CreateFrame(1, nullptr));
sink_.WaitForEncodedFrame(1);
vie_encoder_->OnBitrateUpdated(0, 0, 0);
// Dropped since bitrate is zero.
vie_encoder_->IncomingCapturedFrame(CreateFrame(2, nullptr));
vie_encoder_->OnBitrateUpdated(kTargetBitrateBps, 0, 0);
vie_encoder_->IncomingCapturedFrame(CreateFrame(3, nullptr));
sink_.WaitForEncodedFrame(3);
vie_encoder_->Stop();
}
TEST_F(ViEEncoderTest, DropsFramesWithSameOrOldNtpTimestamp) {
const int kTargetBitrateBps = 100000;
vie_encoder_->OnBitrateUpdated(kTargetBitrateBps, 0, 0);
vie_encoder_->IncomingCapturedFrame(CreateFrame(1, nullptr));
sink_.WaitForEncodedFrame(1);
// This frame will be dropped since it has the same ntp timestamp.
vie_encoder_->IncomingCapturedFrame(CreateFrame(1, nullptr));
vie_encoder_->IncomingCapturedFrame(CreateFrame(2, nullptr));
sink_.WaitForEncodedFrame(2);
vie_encoder_->Stop();
}
TEST_F(ViEEncoderTest, DropsFrameAfterStop) {
const int kTargetBitrateBps = 100000;
vie_encoder_->OnBitrateUpdated(kTargetBitrateBps, 0, 0);
vie_encoder_->IncomingCapturedFrame(CreateFrame(1, nullptr));
sink_.WaitForEncodedFrame(1);
vie_encoder_->Stop();
sink_.SetExpectNoFrames();
rtc::Event frame_destroyed_event(false, false);
vie_encoder_->IncomingCapturedFrame(CreateFrame(2, &frame_destroyed_event));
frame_destroyed_event.Wait(kDefaultTimeoutMs);
}
TEST_F(ViEEncoderTest, DropsPendingFramesOnSlowEncode) {
const int kTargetBitrateBps = 100000;
vie_encoder_->OnBitrateUpdated(kTargetBitrateBps, 0, 0);
fake_encoder_.BlockNextEncode();
vie_encoder_->IncomingCapturedFrame(CreateFrame(1, nullptr));
sink_.WaitForEncodedFrame(1);
// Here, the encoder thread will be blocked in the TestEncoder waiting for a
// call to ContinueEncode.
vie_encoder_->IncomingCapturedFrame(CreateFrame(2, nullptr));
vie_encoder_->IncomingCapturedFrame(CreateFrame(3, nullptr));
fake_encoder_.ContinueEncode();
sink_.WaitForEncodedFrame(3);
vie_encoder_->Stop();
}
} // namespace webrtc

View File

@ -48,8 +48,6 @@
'video/stats_counter.h',
'video/stream_synchronization.cc',
'video/stream_synchronization.h',
'video/video_capture_input.cc',
'video/video_capture_input.h',
'video/video_decoder.cc',
'video/video_encoder.cc',
'video/video_receive_stream.cc',

View File

@ -13,13 +13,13 @@
#include <map>
#include <string>
#include <vector>
#include "webrtc/common_types.h"
#include "webrtc/common_video/include/frame_callback.h"
#include "webrtc/config.h"
#include "webrtc/media/base/videosinkinterface.h"
#include "webrtc/transport.h"
#include "webrtc/media/base/videosinkinterface.h"
namespace webrtc {
@ -72,13 +72,28 @@ class VideoSendStream {
};
struct Config {
public:
Config() = delete;
Config(Config&&) = default;
explicit Config(Transport* send_transport)
: send_transport(send_transport) {}
Config& operator=(Config&&) = default;
Config& operator=(const Config&) = delete;
// Mostly used by tests. Avoid creating copies if you can.
Config Copy() const { return Config(*this); }
std::string ToString() const;
struct EncoderSettings {
EncoderSettings() = default;
EncoderSettings(std::string payload_name,
int payload_type,
VideoEncoder* encoder)
: payload_name(std::move(payload_name)),
payload_type(payload_type),
encoder(encoder) {}
std::string ToString() const;
std::string payload_name;
@ -151,10 +166,6 @@ class VideoSendStream {
// than the measuring window, since the sample data will have been dropped.
EncodedFrameObserver* post_encode_callback = nullptr;
// Renderer for local preview. The local renderer will be called even if
// sending hasn't started. 'nullptr' disables local rendering.
rtc::VideoSinkInterface<VideoFrame>* local_renderer = nullptr;
// Expected delay needed by the renderer, i.e. the frame will be delivered
// this many milliseconds, if possible, earlier than expected render time.
// Only valid if |local_renderer| is set.
@ -168,6 +179,11 @@ class VideoSendStream {
// below the minimum configured bitrate. If this variable is false, the
// stream may send at a rate higher than the estimated available bitrate.
bool suspend_below_min_bitrate = false;
private:
// Access to the copy constructor is private to force use of the Copy()
// method for those exceptional cases where we do use it.
Config(const Config&) = default;
};
// Starts stream activity.
@ -184,7 +200,7 @@ class VideoSendStream {
// Set which streams to send. Must have at least as many SSRCs as configured
// in the config. Encoder settings are passed on to the encoder instance along
// with the VideoStream settings.
virtual void ReconfigureVideoEncoder(const VideoEncoderConfig& config) = 0;
virtual void ReconfigureVideoEncoder(VideoEncoderConfig config) = 0;
virtual Stats GetStats() = 0;

View File

@ -378,10 +378,10 @@
'video/send_statistics_proxy_unittest.cc',
'video/stats_counter_unittest.cc',
'video/stream_synchronization_unittest.cc',
'video/video_capture_input_unittest.cc',
'video/video_decoder_unittest.cc',
'video/video_encoder_unittest.cc',
'video/video_send_stream_tests.cc',
'video/vie_encoder_unittest.cc',
'video/vie_remb_unittest.cc',
],
'dependencies': [