Update stable to r5195.
git-svn-id: http://webrtc.googlecode.com/svn/stable/webrtc@5196 4adac7df-926f-26a2-2b94-8c16560cd09d
diff --git a/build/common.gypi b/build/common.gypi
index 73608a4..0c47315 100644
--- a/build/common.gypi
+++ b/build/common.gypi
@@ -183,6 +183,7 @@
'-Wno-missing-field-initializers',
],
'cflags_cc': [
+ '-Wnon-virtual-dtor',
# This is enabled for clang; enable for gcc as well.
'-Woverloaded-virtual',
],
diff --git a/call.cc b/call.cc
index 45f06eb..9e23b24 100644
--- a/call.cc
+++ b/call.cc
@@ -38,18 +38,18 @@
virtual VideoSendStream::Config GetDefaultSendConfig() OVERRIDE;
- virtual VideoSendStream* CreateSendStream(
+ virtual VideoSendStream* CreateVideoSendStream(
const VideoSendStream::Config& config) OVERRIDE;
- virtual SendStreamState* DestroySendStream(
- webrtc::VideoSendStream* send_stream) OVERRIDE;
+ virtual void DestroyVideoSendStream(webrtc::VideoSendStream* send_stream)
+ OVERRIDE;
virtual VideoReceiveStream::Config GetDefaultReceiveConfig() OVERRIDE;
- virtual VideoReceiveStream* CreateReceiveStream(
+ virtual VideoReceiveStream* CreateVideoReceiveStream(
const VideoReceiveStream::Config& config) OVERRIDE;
- virtual void DestroyReceiveStream(
+ virtual void DestroyVideoReceiveStream(
webrtc::VideoReceiveStream* receive_stream) OVERRIDE;
virtual uint32_t SendBitrateEstimate() OVERRIDE;
@@ -212,10 +212,10 @@
return config;
}
-VideoSendStream* Call::CreateSendStream(const VideoSendStream::Config& config) {
+VideoSendStream* Call::CreateVideoSendStream(
+ const VideoSendStream::Config& config) {
assert(config.rtp.ssrcs.size() > 0);
- assert(config.codec.numberOfSimulcastStreams == 0 ||
- config.codec.numberOfSimulcastStreams == config.rtp.ssrcs.size());
+ assert(config.rtp.ssrcs.size() >= config.codec.numberOfSimulcastStreams);
VideoSendStream* send_stream = new VideoSendStream(
config_.send_transport, config_.overuse_detection, video_engine_, config);
@@ -228,7 +228,7 @@
return send_stream;
}
-SendStreamState* Call::DestroySendStream(webrtc::VideoSendStream* send_stream) {
+void Call::DestroyVideoSendStream(webrtc::VideoSendStream* send_stream) {
assert(send_stream != NULL);
VideoSendStream* send_stream_impl = NULL;
@@ -248,19 +248,16 @@
assert(send_stream_impl != NULL);
delete send_stream_impl;
-
- // TODO(pbos): Return its previous state
- return NULL;
}
VideoReceiveStream::Config Call::GetDefaultReceiveConfig() {
return VideoReceiveStream::Config();
}
-VideoReceiveStream* Call::CreateReceiveStream(
+VideoReceiveStream* Call::CreateVideoReceiveStream(
const VideoReceiveStream::Config& config) {
- VideoReceiveStream* receive_stream =
- new VideoReceiveStream(video_engine_, config, config_.send_transport);
+ VideoReceiveStream* receive_stream = new VideoReceiveStream(
+ video_engine_, config, config_.send_transport, config_.voice_engine);
WriteLockScoped write_lock(*receive_lock_);
assert(receive_ssrcs_.find(config.rtp.ssrc) == receive_ssrcs_.end());
@@ -268,7 +265,8 @@
return receive_stream;
}
-void Call::DestroyReceiveStream(webrtc::VideoReceiveStream* receive_stream) {
+void Call::DestroyVideoReceiveStream(
+ webrtc::VideoReceiveStream* receive_stream) {
assert(receive_stream != NULL);
VideoReceiveStream* receive_stream_impl = NULL;
diff --git a/call.h b/call.h
index 5239f5a..186672d 100644
--- a/call.h
+++ b/call.h
@@ -60,19 +60,20 @@
virtual VideoSendStream::Config GetDefaultSendConfig() = 0;
- virtual VideoSendStream* CreateSendStream(
+ virtual VideoSendStream* CreateVideoSendStream(
const VideoSendStream::Config& config) = 0;
// Returns the internal state of the send stream, for resume sending with a
// new stream with different settings.
// Note: Only the last returned send-stream state is valid.
- virtual SendStreamState* DestroySendStream(VideoSendStream* send_stream) = 0;
+ virtual void DestroyVideoSendStream(VideoSendStream* send_stream) = 0;
virtual VideoReceiveStream::Config GetDefaultReceiveConfig() = 0;
- virtual VideoReceiveStream* CreateReceiveStream(
+ virtual VideoReceiveStream* CreateVideoReceiveStream(
const VideoReceiveStream::Config& config) = 0;
- virtual void DestroyReceiveStream(VideoReceiveStream* receive_stream) = 0;
+ virtual void DestroyVideoReceiveStream(
+ VideoReceiveStream* receive_stream) = 0;
// All received RTP and RTCP packets for the call should be inserted to this
// PacketReceiver. The PacketReceiver pointer is valid as long as the
diff --git a/call_tests.cc b/call_tests.cc
index 6a8f34f..5393286 100644
--- a/call_tests.cc
+++ b/call_tests.cc
@@ -9,28 +9,43 @@
*/
#include <assert.h>
+#include <algorithm>
#include <map>
+#include <sstream>
+#include <string>
#include "testing/gtest/include/gtest/gtest.h"
#include "webrtc/call.h"
#include "webrtc/common_video/test/frame_generator.h"
+#include "webrtc/frame_callback.h"
+#include "webrtc/modules/remote_bitrate_estimator/include/rtp_to_ntp.h"
#include "webrtc/modules/rtp_rtcp/interface/rtp_header_parser.h"
#include "webrtc/modules/rtp_rtcp/source/rtcp_utility.h"
#include "webrtc/system_wrappers/interface/critical_section_wrapper.h"
#include "webrtc/system_wrappers/interface/event_wrapper.h"
#include "webrtc/system_wrappers/interface/scoped_ptr.h"
+#include "webrtc/video/transport_adapter.h"
+#include "webrtc/voice_engine/include/voe_base.h"
+#include "webrtc/voice_engine/include/voe_codec.h"
+#include "webrtc/voice_engine/include/voe_network.h"
+#include "webrtc/voice_engine/include/voe_rtp_rtcp.h"
+#include "webrtc/voice_engine/include/voe_video_sync.h"
+#include "webrtc/voice_engine/test/auto_test/resource_manager.h"
#include "webrtc/test/direct_transport.h"
+#include "webrtc/test/fake_audio_device.h"
#include "webrtc/test/fake_decoder.h"
#include "webrtc/test/fake_encoder.h"
#include "webrtc/test/frame_generator_capturer.h"
#include "webrtc/test/generate_ssrcs.h"
#include "webrtc/test/rtp_rtcp_observer.h"
+#include "webrtc/test/testsupport/perf_test.h"
namespace webrtc {
static unsigned int kDefaultTimeoutMs = 30 * 1000;
static unsigned int kLongTimeoutMs = 120 * 1000;
+static const uint8_t kSendPayloadType = 125;
class CallTest : public ::testing::Test {
public:
@@ -59,6 +74,7 @@
send_config_.encoder = &fake_encoder_;
send_config_.internal_source = false;
test::FakeEncoder::SetCodecSettings(&send_config_.codec, 1);
+ send_config_.codec.plType = kSendPayloadType;
receive_config_.codecs.clear();
receive_config_.codecs.push_back(send_config_.codec);
@@ -73,8 +89,8 @@
assert(send_stream_ == NULL);
assert(receive_stream_ == NULL);
- send_stream_ = sender_call_->CreateSendStream(send_config_);
- receive_stream_ = receiver_call_->CreateReceiveStream(receive_config_);
+ send_stream_ = sender_call_->CreateVideoSendStream(send_config_);
+ receive_stream_ = receiver_call_->CreateVideoReceiveStream(receive_config_);
}
void CreateFrameGenerator() {
@@ -87,8 +103,8 @@
}
void StartSending() {
- receive_stream_->StartReceive();
- send_stream_->StartSend();
+ receive_stream_->StartReceiving();
+ send_stream_->StartSending();
if (frame_generator_capturer_.get() != NULL)
frame_generator_capturer_->Start();
}
@@ -97,22 +113,23 @@
if (frame_generator_capturer_.get() != NULL)
frame_generator_capturer_->Stop();
if (send_stream_ != NULL)
- send_stream_->StopSend();
+ send_stream_->StopSending();
if (receive_stream_ != NULL)
- receive_stream_->StopReceive();
+ receive_stream_->StopReceiving();
}
void DestroyStreams() {
if (send_stream_ != NULL)
- sender_call_->DestroySendStream(send_stream_);
+ sender_call_->DestroyVideoSendStream(send_stream_);
if (receive_stream_ != NULL)
- receiver_call_->DestroyReceiveStream(receive_stream_);
+ receiver_call_->DestroyVideoReceiveStream(receive_stream_);
send_stream_ = NULL;
receive_stream_ = NULL;
}
void ReceivesPliAndRecovers(int rtp_history_ms);
void RespectsRtcpMode(newapi::RtcpMode rtcp_mode);
+ void PlaysOutAudioAndVideoInSync();
scoped_ptr<Call> sender_call_;
scoped_ptr<Call> receiver_call_;
@@ -607,7 +624,7 @@
CreateFrameGenerator();
StartSending();
- receiver_call_->DestroyReceiveStream(receive_stream_);
+ receiver_call_->DestroyVideoReceiveStream(receive_stream_);
receive_stream_ = NULL;
// Wait() waits for a received packet.
@@ -771,15 +788,16 @@
receiver_call->GetDefaultReceiveConfig();
receive_config.renderer = observers[i];
receive_config.rtp.ssrc = ssrc;
- receive_streams[i] = receiver_call->CreateReceiveStream(receive_config);
- receive_streams[i]->StartReceive();
+ receive_streams[i] =
+ receiver_call->CreateVideoReceiveStream(receive_config);
+ receive_streams[i]->StartReceiving();
VideoSendStream::Config send_config = sender_call->GetDefaultSendConfig();
send_config.rtp.ssrcs.push_back(ssrc);
send_config.codec.width = width;
send_config.codec.height = height;
- send_streams[i] = sender_call->CreateSendStream(send_config);
- send_streams[i]->StartSend();
+ send_streams[i] = sender_call->CreateVideoSendStream(send_config);
+ send_streams[i]->StartSending();
frame_generators[i] = test::FrameGeneratorCapturer::Create(
send_streams[i]->Input(), width, height, 30, Clock::GetRealTimeClock());
@@ -793,12 +811,307 @@
for (size_t i = 0; i < kNumStreams; ++i) {
frame_generators[i]->Stop();
delete frame_generators[i];
- sender_call->DestroySendStream(send_streams[i]);
- receiver_call->DestroyReceiveStream(receive_streams[i]);
+ sender_call->DestroyVideoSendStream(send_streams[i]);
+ receiver_call->DestroyVideoReceiveStream(receive_streams[i]);
delete observers[i];
}
sender_transport.StopSending();
receiver_transport.StopSending();
}
+
+class SyncRtcpObserver : public test::RtpRtcpObserver {
+ public:
+ SyncRtcpObserver(int delay_ms)
+ : test::RtpRtcpObserver(kLongTimeoutMs, delay_ms),
+ critical_section_(CriticalSectionWrapper::CreateCriticalSection()) {}
+
+ virtual Action OnSendRtcp(const uint8_t* packet, size_t length) OVERRIDE {
+ RTCPUtility::RTCPParserV2 parser(packet, length, true);
+ EXPECT_TRUE(parser.IsValid());
+
+ for (RTCPUtility::RTCPPacketTypes packet_type = parser.Begin();
+ packet_type != RTCPUtility::kRtcpNotValidCode;
+ packet_type = parser.Iterate()) {
+ if (packet_type == RTCPUtility::kRtcpSrCode) {
+ const RTCPUtility::RTCPPacket& packet = parser.Packet();
+ synchronization::RtcpMeasurement ntp_rtp_pair(
+ packet.SR.NTPMostSignificant,
+ packet.SR.NTPLeastSignificant,
+ packet.SR.RTPTimestamp);
+ StoreNtpRtpPair(ntp_rtp_pair);
+ }
+ }
+ return SEND_PACKET;
+ }
+
+ int64_t RtpTimestampToNtp(uint32_t timestamp) const {
+ CriticalSectionScoped cs(critical_section_.get());
+ int64_t timestamp_in_ms = -1;
+ if (ntp_rtp_pairs_.size() == 2) {
+ // TODO(stefan): We can't EXPECT_TRUE on this call due to a bug in the
+ // RTCP sender where it sends RTCP SR before any RTP packets, which leads
+ // to a bogus NTP/RTP mapping.
+ synchronization::RtpToNtpMs(timestamp, ntp_rtp_pairs_, ×tamp_in_ms);
+ return timestamp_in_ms;
+ }
+ return -1;
+ }
+
+ private:
+ void StoreNtpRtpPair(synchronization::RtcpMeasurement ntp_rtp_pair) {
+ CriticalSectionScoped cs(critical_section_.get());
+ for (synchronization::RtcpList::iterator it = ntp_rtp_pairs_.begin();
+ it != ntp_rtp_pairs_.end();
+ ++it) {
+ if (ntp_rtp_pair.ntp_secs == it->ntp_secs &&
+ ntp_rtp_pair.ntp_frac == it->ntp_frac) {
+ // This RTCP has already been added to the list.
+ return;
+ }
+ }
+ // We need two RTCP SR reports to map between RTP and NTP. More than two
+ // will not improve the mapping.
+ if (ntp_rtp_pairs_.size() == 2) {
+ ntp_rtp_pairs_.pop_back();
+ }
+ ntp_rtp_pairs_.push_front(ntp_rtp_pair);
+ }
+
+ scoped_ptr<CriticalSectionWrapper> critical_section_;
+ synchronization::RtcpList ntp_rtp_pairs_;
+};
+
+class VideoRtcpAndSyncObserver : public SyncRtcpObserver, public VideoRenderer {
+ static const int kInSyncThresholdMs = 50;
+ static const int kStartupTimeMs = 2000;
+ static const int kMinRunTimeMs = 30000;
+
+ public:
+ VideoRtcpAndSyncObserver(Clock* clock,
+ int voe_channel,
+ VoEVideoSync* voe_sync,
+ SyncRtcpObserver* audio_observer)
+ : SyncRtcpObserver(0),
+ clock_(clock),
+ voe_channel_(voe_channel),
+ voe_sync_(voe_sync),
+ audio_observer_(audio_observer),
+ creation_time_ms_(clock_->TimeInMilliseconds()),
+ first_time_in_sync_(-1) {}
+
+ virtual void RenderFrame(const I420VideoFrame& video_frame,
+ int time_to_render_ms) OVERRIDE {
+ int64_t now_ms = clock_->TimeInMilliseconds();
+ uint32_t playout_timestamp = 0;
+ if (voe_sync_->GetPlayoutTimestamp(voe_channel_, playout_timestamp) != 0)
+ return;
+ int64_t latest_audio_ntp =
+ audio_observer_->RtpTimestampToNtp(playout_timestamp);
+ int64_t latest_video_ntp = RtpTimestampToNtp(video_frame.timestamp());
+ if (latest_audio_ntp < 0 || latest_video_ntp < 0)
+ return;
+ int time_until_render_ms =
+ std::max(0, static_cast<int>(video_frame.render_time_ms() - now_ms));
+ latest_video_ntp += time_until_render_ms;
+ int64_t stream_offset = latest_audio_ntp - latest_video_ntp;
+ std::stringstream ss;
+ ss << stream_offset;
+ webrtc::test::PrintResult(
+ "stream_offset", "", "synchronization", ss.str(), "ms", false);
+ int64_t time_since_creation = now_ms - creation_time_ms_;
+ // During the first couple of seconds audio and video can falsely be
+ // estimated as being synchronized. We don't want to trigger on those.
+ if (time_since_creation < kStartupTimeMs)
+ return;
+ if (abs(latest_audio_ntp - latest_video_ntp) < kInSyncThresholdMs) {
+ if (first_time_in_sync_ == -1) {
+ first_time_in_sync_ = now_ms;
+ webrtc::test::PrintResult("sync_convergence_time",
+ "",
+ "synchronization",
+ time_since_creation,
+ "ms",
+ false);
+ }
+ if (time_since_creation > kMinRunTimeMs)
+ observation_complete_->Set();
+ }
+ }
+
+ private:
+ Clock* clock_;
+ int voe_channel_;
+ VoEVideoSync* voe_sync_;
+ SyncRtcpObserver* audio_observer_;
+ int64_t creation_time_ms_;
+ int64_t first_time_in_sync_;
+};
+
+TEST_F(CallTest, PlaysOutAudioAndVideoInSync) {
+ VoiceEngine* voice_engine = VoiceEngine::Create();
+ VoEBase* voe_base = VoEBase::GetInterface(voice_engine);
+ VoECodec* voe_codec = VoECodec::GetInterface(voice_engine);
+ VoENetwork* voe_network = VoENetwork::GetInterface(voice_engine);
+ VoEVideoSync* voe_sync = VoEVideoSync::GetInterface(voice_engine);
+ ResourceManager resource_manager;
+ const std::string audio_filename = resource_manager.long_audio_file_path();
+ ASSERT_STRNE("", audio_filename.c_str());
+ test::FakeAudioDevice fake_audio_device(Clock::GetRealTimeClock(),
+ audio_filename);
+ EXPECT_EQ(0, voe_base->Init(&fake_audio_device, NULL));
+ int channel = voe_base->CreateChannel();
+
+ const int kVoiceDelayMs = 500;
+ SyncRtcpObserver audio_observer(kVoiceDelayMs);
+ VideoRtcpAndSyncObserver observer(
+ Clock::GetRealTimeClock(), channel, voe_sync, &audio_observer);
+
+ Call::Config receiver_config(observer.ReceiveTransport());
+ receiver_config.voice_engine = voice_engine;
+ CreateCalls(Call::Config(observer.SendTransport()), receiver_config);
+ CodecInst isac = {103, "ISAC", 16000, 480, 1, 32000};
+ EXPECT_EQ(0, voe_codec->SetSendCodec(channel, isac));
+
+ class VoicePacketReceiver : public PacketReceiver {
+ public:
+ VoicePacketReceiver(int channel, VoENetwork* voe_network)
+ : channel_(channel),
+ voe_network_(voe_network),
+ parser_(RtpHeaderParser::Create()) {}
+ virtual bool DeliverPacket(const uint8_t* packet, size_t length) {
+ int ret;
+ if (parser_->IsRtcp(packet, static_cast<int>(length))) {
+ ret = voe_network_->ReceivedRTCPPacket(
+ channel_, packet, static_cast<unsigned int>(length));
+ } else {
+ ret = voe_network_->ReceivedRTPPacket(
+ channel_, packet, static_cast<unsigned int>(length));
+ }
+ return ret == 0;
+ }
+
+ private:
+ int channel_;
+ VoENetwork* voe_network_;
+ scoped_ptr<RtpHeaderParser> parser_;
+ } voe_packet_receiver(channel, voe_network);
+
+ audio_observer.SetReceivers(&voe_packet_receiver, &voe_packet_receiver);
+
+ internal::TransportAdapter transport_adapter(audio_observer.SendTransport());
+ EXPECT_EQ(0,
+ voe_network->RegisterExternalTransport(channel, transport_adapter));
+
+ observer.SetReceivers(receiver_call_->Receiver(), sender_call_->Receiver());
+
+ CreateTestConfigs();
+ send_config_.rtp.nack.rtp_history_ms = 1000;
+ receive_config_.rtp.nack.rtp_history_ms = 1000;
+ receive_config_.renderer = &observer;
+ receive_config_.audio_channel_id = channel;
+
+ CreateStreams();
+ CreateFrameGenerator();
+ StartSending();
+
+ fake_audio_device.Start();
+ EXPECT_EQ(0, voe_base->StartPlayout(channel));
+ EXPECT_EQ(0, voe_base->StartReceive(channel));
+ EXPECT_EQ(0, voe_base->StartSend(channel));
+
+ EXPECT_EQ(kEventSignaled, observer.Wait())
+ << "Timed out while waiting for audio and video to be synchronized.";
+
+ EXPECT_EQ(0, voe_base->StopSend(channel));
+ EXPECT_EQ(0, voe_base->StopReceive(channel));
+ EXPECT_EQ(0, voe_base->StopPlayout(channel));
+ fake_audio_device.Stop();
+
+ StopSending();
+ observer.StopSending();
+ audio_observer.StopSending();
+
+ voe_base->DeleteChannel(channel);
+ voe_base->Release();
+ voe_codec->Release();
+ voe_network->Release();
+ voe_sync->Release();
+ DestroyStreams();
+ VoiceEngine::Delete(voice_engine);
+}
+
+TEST_F(CallTest, ObserversEncodedFrames) {
+ class EncodedFrameTestObserver : public EncodedFrameObserver {
+ public:
+ EncodedFrameTestObserver() : length_(0),
+ frame_type_(kFrameEmpty),
+ called_(EventWrapper::Create()) {}
+ virtual ~EncodedFrameTestObserver() {}
+
+ virtual void EncodedFrameCallback(const EncodedFrame& encoded_frame) {
+ frame_type_ = encoded_frame.frame_type_;
+ length_ = encoded_frame.length_;
+ buffer_.reset(new uint8_t[length_]);
+ memcpy(buffer_.get(), encoded_frame.data_, length_);
+ called_->Set();
+ }
+
+ EventTypeWrapper Wait() {
+ return called_->Wait(kDefaultTimeoutMs);
+ }
+
+ void ExpectEqualFrames(const EncodedFrameTestObserver& observer) {
+ ASSERT_EQ(length_, observer.length_)
+ << "Observed frames are of different lengths.";
+ EXPECT_EQ(frame_type_, observer.frame_type_)
+ << "Observed frames have different frame types.";
+ EXPECT_EQ(0, memcmp(buffer_.get(), observer.buffer_.get(), length_))
+ << "Observed encoded frames have different content.";
+ }
+
+ private:
+ scoped_ptr<uint8_t[]> buffer_;
+ size_t length_;
+ FrameType frame_type_;
+ scoped_ptr<EventWrapper> called_;
+ };
+
+ EncodedFrameTestObserver post_encode_observer;
+ EncodedFrameTestObserver pre_decode_observer;
+
+ test::DirectTransport sender_transport, receiver_transport;
+
+ CreateCalls(Call::Config(&sender_transport),
+ Call::Config(&receiver_transport));
+
+ sender_transport.SetReceiver(receiver_call_->Receiver());
+ receiver_transport.SetReceiver(sender_call_->Receiver());
+
+ CreateTestConfigs();
+ send_config_.post_encode_callback = &post_encode_observer;
+ receive_config_.pre_decode_callback = &pre_decode_observer;
+
+ CreateStreams();
+ StartSending();
+
+ scoped_ptr<test::FrameGenerator> frame_generator(test::FrameGenerator::Create(
+ send_config_.codec.width, send_config_.codec.height));
+ send_stream_->Input()->PutFrame(frame_generator->NextFrame(), 0);
+
+ EXPECT_EQ(kEventSignaled, post_encode_observer.Wait())
+ << "Timed out while waiting for send-side encoded-frame callback.";
+
+ EXPECT_EQ(kEventSignaled, pre_decode_observer.Wait())
+ << "Timed out while waiting for pre-decode encoded-frame callback.";
+
+ post_encode_observer.ExpectEqualFrames(pre_decode_observer);
+
+ StopSending();
+
+ sender_transport.StopSending();
+ receiver_transport.StopSending();
+
+ DestroyStreams();
+}
} // namespace webrtc
diff --git a/common_types.h b/common_types.h
index b736a2f..7138b05 100644
--- a/common_types.h
+++ b/common_types.h
@@ -225,6 +225,7 @@
Transport() {}
};
+// Statistics for an RTCP channel
struct RtcpStatistics {
public:
RtcpStatistics()
@@ -241,6 +242,71 @@
uint32_t max_jitter;
};
+// Callback, called whenever a new rtcp report block is transmitted.
+class RtcpStatisticsCallback {
+ public:
+ virtual ~RtcpStatisticsCallback() {}
+
+ virtual void StatisticsUpdated(const RtcpStatistics& statistics,
+ uint32_t ssrc) = 0;
+};
+
+// Data usage statistics for a (rtp) stream
+struct StreamDataCounters {
+ public:
+ StreamDataCounters()
+ : bytes(0),
+ padding_bytes(0),
+ packets(0),
+ retransmitted_packets(0),
+ fec_packets(0) {}
+
+ uint32_t bytes;
+ uint32_t padding_bytes;
+ uint32_t packets;
+ uint32_t retransmitted_packets;
+ uint32_t fec_packets;
+};
+
+// Callback, called whenever byte/packet counts have been updated.
+class StreamDataCountersCallback {
+ public:
+ virtual ~StreamDataCountersCallback() {}
+
+ virtual void DataCountersUpdated(const StreamDataCounters& counters,
+ uint32_t ssrc) = 0;
+};
+
+// Rate statistics for a stream
+struct BitrateStatistics {
+ public:
+ BitrateStatistics()
+ : bitrate_(0),
+ packet_rate(0),
+ now(0) {}
+
+ uint32_t bitrate_;
+ uint32_t packet_rate;
+ uint64_t now;
+};
+
+// Callback, used to notify an observer whenever new rates have been estimated.
+class BitrateStatisticsObserver {
+ public:
+ virtual ~BitrateStatisticsObserver() {}
+
+ virtual void Notify(const BitrateStatistics& stats, uint32_t ssrc) = 0;
+};
+
+// Callback, used to notify an observer whenever frame counts have been updated
+class FrameCountObserver {
+ public:
+ virtual ~FrameCountObserver() {}
+ virtual void Notify(const unsigned int key_frames,
+ const unsigned int delta_frames,
+ const unsigned int ssrc) = 0;
+};
+
// ==================================================================
// Voice specific types
// ==================================================================
diff --git a/config.cc b/config.cc
new file mode 100644
index 0000000..021bbbf
--- /dev/null
+++ b/config.cc
@@ -0,0 +1,17 @@
+/*
+ * Copyright (c) 2013 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/config.h"
+
+namespace webrtc {
+const char* RtpExtension::kTOffset = "urn:ietf:params:rtp-hdrext:toffset";
+const char* RtpExtension::kAbsSendTime =
+ "http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time";
+} // namespace webrtc
diff --git a/config.h b/config.h
index a00a8f9..3ff3bb8 100644
--- a/config.h
+++ b/config.h
@@ -16,6 +16,8 @@
#include <string>
#include <vector>
+#include "webrtc/typedefs.h"
+
namespace webrtc {
struct RtpStatistics {
@@ -67,6 +69,8 @@
// RTP header extension to use for the video stream, see RFC 5285.
struct RtpExtension {
+ static const char* kTOffset;
+ static const char* kAbsSendTime;
RtpExtension(const char* name, int id) : name(name), id(id) {}
// TODO(mflodman) Add API to query supported extensions.
std::string name;
diff --git a/frame_callback.h b/frame_callback.h
index 7f54245..cfb07d8 100644
--- a/frame_callback.h
+++ b/frame_callback.h
@@ -11,11 +11,22 @@
#ifndef WEBRTC_VIDEO_ENGINE_NEW_INCLUDE_FRAME_CALLBACK_H_
#define WEBRTC_VIDEO_ENGINE_NEW_INCLUDE_FRAME_CALLBACK_H_
+#include "webrtc/common_types.h"
+
namespace webrtc {
class I420VideoFrame;
-struct EncodedFrame;
+struct EncodedFrame {
+ public:
+ EncodedFrame() : data_(NULL), length_(0), frame_type_(kFrameEmpty) {}
+ EncodedFrame(const uint8_t* data, size_t length, FrameType frame_type)
+ : data_(data), length_(length), frame_type_(frame_type) {}
+
+ const uint8_t* data_;
+ const size_t length_;
+ const FrameType frame_type_;
+};
class I420FrameCallback {
public:
@@ -34,6 +45,7 @@
protected:
virtual ~EncodedFrameObserver() {}
};
+
} // namespace webrtc
#endif // WEBRTC_VIDEO_ENGINE_NEW_INCLUDE_FRAME_CALLBACK_H_
diff --git a/loopback.cc b/loopback.cc
index 5c5dbce..e07138a 100644
--- a/loopback.cc
+++ b/loopback.cc
@@ -63,7 +63,7 @@
send_config.codec.maxBitrate =
static_cast<unsigned int>(test::flags::MaxBitrate());
- VideoSendStream* send_stream = call->CreateSendStream(send_config);
+ VideoSendStream* send_stream = call->CreateVideoSendStream(send_config);
Clock* test_clock = Clock::GetRealTimeClock();
@@ -79,20 +79,20 @@
receive_config.renderer = loopback_video.get();
VideoReceiveStream* receive_stream =
- call->CreateReceiveStream(receive_config);
+ call->CreateVideoReceiveStream(receive_config);
- receive_stream->StartReceive();
- send_stream->StartSend();
+ receive_stream->StartReceiving();
+ send_stream->StartSending();
camera->Start();
test::PressEnterToContinue();
camera->Stop();
- send_stream->StopSend();
- receive_stream->StopReceive();
+ send_stream->StopSending();
+ receive_stream->StopReceiving();
- call->DestroyReceiveStream(receive_stream);
- call->DestroySendStream(send_stream);
+ call->DestroyVideoReceiveStream(receive_stream);
+ call->DestroyVideoSendStream(send_stream);
transport.StopSending();
}
diff --git a/modules/audio_coding/main/acm2/audio_coding_module.cc b/modules/audio_coding/main/acm2/audio_coding_module.cc
index c7fe9fa..60ed69c 100644
--- a/modules/audio_coding/main/acm2/audio_coding_module.cc
+++ b/modules/audio_coding/main/acm2/audio_coding_module.cc
@@ -19,6 +19,9 @@
namespace webrtc {
+const char kLegacyAcmVersion[] = "acm1";
+const char kExperimentalAcmVersion[] = "acm2";
+
// Create module
AudioCodingModule* AudioCodingModule::Create(int id) {
return new acm1::AudioCodingModuleImpl(id, Clock::GetRealTimeClock());
diff --git a/modules/audio_coding/main/acm2/audio_coding_module_impl.cc b/modules/audio_coding/main/acm2/audio_coding_module_impl.cc
index 59d5727..ce05218 100644
--- a/modules/audio_coding/main/acm2/audio_coding_module_impl.cc
+++ b/modules/audio_coding/main/acm2/audio_coding_module_impl.cc
@@ -1975,6 +1975,10 @@
return receiver_.LeastRequiredDelayMs();
}
+const char* AudioCodingModuleImpl::Version() const {
+ return kExperimentalAcmVersion;
+}
+
} // namespace acm2
} // namespace webrtc
diff --git a/modules/audio_coding/main/acm2/audio_coding_module_impl.h b/modules/audio_coding/main/acm2/audio_coding_module_impl.h
index 6bf90cf..17fab39 100644
--- a/modules/audio_coding/main/acm2/audio_coding_module_impl.h
+++ b/modules/audio_coding/main/acm2/audio_coding_module_impl.h
@@ -32,12 +32,11 @@
class AudioCodingModuleImpl : public AudioCodingModule {
public:
- // Constructor
explicit AudioCodingModuleImpl(int id);
-
- // Destructor
~AudioCodingModuleImpl();
+ virtual const char* Version() const;
+
// Change the unique identifier of this object.
virtual int32_t ChangeUniqueId(const int32_t id);
diff --git a/modules/audio_coding/main/interface/audio_coding_module.h b/modules/audio_coding/main/interface/audio_coding_module.h
index 2f4776d..f8b9690 100644
--- a/modules/audio_coding/main/interface/audio_coding_module.h
+++ b/modules/audio_coding/main/interface/audio_coding_module.h
@@ -73,6 +73,10 @@
const uint16_t delayMS) = 0; // average delay in ms
};
+// Version string for testing, to distinguish instances of ACM1 from ACM2.
+extern const char kLegacyAcmVersion[];
+extern const char kExperimentalAcmVersion[];
+
class AudioCodingModule: public Module {
protected:
AudioCodingModule() {}
@@ -174,6 +178,11 @@
//
static bool IsCodecValid(const CodecInst& codec);
+ // Returns the version of ACM. This facilitates distinguishing instances of
+ // ACM1 from ACM2 while testing. This API will be removed when ACM1 is
+ // completely removed.
+ virtual const char* Version() const = 0;
+
///////////////////////////////////////////////////////////////////////////
// Sender
//
diff --git a/modules/audio_coding/main/source/audio_coding_module.gypi b/modules/audio_coding/main/source/audio_coding_module.gypi
index c6c10f6..a0389b0 100644
--- a/modules/audio_coding/main/source/audio_coding_module.gypi
+++ b/modules/audio_coding/main/source/audio_coding_module.gypi
@@ -117,7 +117,7 @@
'dependencies': [
'audio_coding_module',
'<(DEPTH)/testing/gtest.gyp:gtest',
- '<(webrtc_root)/test/test.gyp:test_support_main',
+ '<(webrtc_root)/test/test.gyp:test_support',
'<(webrtc_root)/system_wrappers/source/system_wrappers.gyp:system_wrappers',
'<(DEPTH)/third_party/gflags/gflags.gyp:gflags',
],
@@ -125,6 +125,7 @@
'../test/delay_test.cc',
'../test/Channel.cc',
'../test/PCMFile.cc',
+ '../test/utility.cc',
],
}, # delay_test
{
@@ -133,7 +134,7 @@
'dependencies': [
'audio_coding_module',
'<(DEPTH)/testing/gtest.gyp:gtest',
- '<(webrtc_root)/test/test.gyp:test_support_main',
+ '<(webrtc_root)/test/test.gyp:test_support',
'<(webrtc_root)/system_wrappers/source/system_wrappers.gyp:system_wrappers',
'<(DEPTH)/third_party/gflags/gflags.gyp:gflags',
],
diff --git a/modules/audio_coding/main/source/audio_coding_module_impl.cc b/modules/audio_coding/main/source/audio_coding_module_impl.cc
index 3802733..1e71a04 100644
--- a/modules/audio_coding/main/source/audio_coding_module_impl.cc
+++ b/modules/audio_coding/main/source/audio_coding_module_impl.cc
@@ -3026,6 +3026,10 @@
nack_enabled_ = false;
}
+const char* AudioCodingModuleImpl::Version() const {
+ return kLegacyAcmVersion;
+}
+
} // namespace acm1
} // namespace webrtc
diff --git a/modules/audio_coding/main/source/audio_coding_module_impl.h b/modules/audio_coding/main/source/audio_coding_module_impl.h
index 7d58f37..7acde17 100644
--- a/modules/audio_coding/main/source/audio_coding_module_impl.h
+++ b/modules/audio_coding/main/source/audio_coding_module_impl.h
@@ -40,12 +40,11 @@
class AudioCodingModuleImpl : public AudioCodingModule {
public:
- // Constructor
AudioCodingModuleImpl(const int32_t id, Clock* clock);
-
- // Destructor
~AudioCodingModuleImpl();
+ virtual const char* Version() const;
+
// Change the unique identifier of this object.
virtual int32_t ChangeUniqueId(const int32_t id);
diff --git a/modules/audio_coding/main/test/delay_test.cc b/modules/audio_coding/main/test/delay_test.cc
index 1a0f8f8..63bfe2b 100644
--- a/modules/audio_coding/main/test/delay_test.cc
+++ b/modules/audio_coding/main/test/delay_test.cc
@@ -8,8 +8,6 @@
* be found in the AUTHORS file in the root of the source tree.
*/
-#include "webrtc/modules/audio_coding/main/interface/audio_coding_module.h"
-
#include <assert.h>
#include <math.h>
@@ -17,8 +15,10 @@
#include "gflags/gflags.h"
#include "testing/gtest/include/gtest/gtest.h"
+#include "webrtc/common.h"
#include "webrtc/common_types.h"
#include "webrtc/engine_configurations.h"
+#include "webrtc/modules/audio_coding/main/interface/audio_coding_module.h"
#include "webrtc/modules/audio_coding/main/interface/audio_coding_module_typedefs.h"
#include "webrtc/modules/audio_coding/main/acm2/acm_common_defs.h"
#include "webrtc/modules/audio_coding/main/test/Channel.h"
@@ -35,68 +35,76 @@
DEFINE_int32(delay, 0, "Delay in millisecond.");
DEFINE_int32(init_delay, 0, "Initial delay in millisecond.");
DEFINE_bool(dtx, false, "Enable DTX at the sender side.");
+DEFINE_bool(acm2, false, "Run the test with ACM2.");
+DEFINE_bool(packet_loss, false, "Apply packet loss, c.f. Channel{.cc, .h}.");
+DEFINE_bool(fec, false, "Use Forward Error Correction (FEC).");
namespace webrtc {
+
namespace {
-struct CodecConfig {
+struct CodecSettings {
char name[50];
int sample_rate_hz;
int num_channels;
};
-struct AcmConfig {
+struct AcmSettings {
bool dtx;
bool fec;
};
-struct Config {
- CodecConfig codec;
- AcmConfig acm;
+struct TestSettings {
+ CodecSettings codec;
+ AcmSettings acm;
bool packet_loss;
};
+} // namespace
class DelayTest {
public:
-
- DelayTest()
- : acm_a_(AudioCodingModule::Create(0)),
- acm_b_(AudioCodingModule::Create(1)),
- channel_a2b_(NULL),
+ explicit DelayTest(const Config& config)
+ : acm_a_(config.Get<AudioCodingModuleFactory>().Create(0)),
+ acm_b_(config.Get<AudioCodingModuleFactory>().Create(1)),
+ channel_a2b_(new Channel),
test_cntr_(0),
encoding_sample_rate_hz_(8000) {}
- ~DelayTest() {}
-
- void TearDown() {
+ ~DelayTest() {
if (channel_a2b_ != NULL) {
delete channel_a2b_;
channel_a2b_ = NULL;
}
+ in_file_a_.Close();
}
- void SetUp() {
+ void Initialize() {
test_cntr_ = 0;
std::string file_name = webrtc::test::ResourcePath(
"audio_coding/testfile32kHz", "pcm");
if (FLAGS_input_file.size() > 0)
file_name = FLAGS_input_file;
in_file_a_.Open(file_name, 32000, "rb");
- acm_a_->InitializeReceiver();
- acm_b_->InitializeReceiver();
+ ASSERT_EQ(0, acm_a_->InitializeReceiver()) <<
+ "Couldn't initialize receiver.\n";
+ ASSERT_EQ(0, acm_b_->InitializeReceiver()) <<
+ "Couldn't initialize receiver.\n";
if (FLAGS_init_delay > 0) {
- ASSERT_EQ(0, acm_b_->SetInitialPlayoutDelay(FLAGS_init_delay));
+ ASSERT_EQ(0, acm_b_->SetInitialPlayoutDelay(FLAGS_init_delay)) <<
+ "Failed to set initial delay.\n";
}
if (FLAGS_delay > 0) {
- ASSERT_EQ(0, acm_b_->SetMinimumPlayoutDelay(FLAGS_delay));
+ ASSERT_EQ(0, acm_b_->SetMinimumPlayoutDelay(FLAGS_delay)) <<
+ "Failed to set minimum delay.\n";
}
- uint8_t num_encoders = acm_a_->NumberOfCodecs();
+ int num_encoders = acm_a_->NumberOfCodecs();
CodecInst my_codec_param;
for (int n = 0; n < num_encoders; n++) {
- acm_b_->Codec(n, &my_codec_param);
+ EXPECT_EQ(0, acm_b_->Codec(n, &my_codec_param)) <<
+ "Failed to get codec.";
if (STR_CASE_CMP(my_codec_param.plname, "opus") == 0)
my_codec_param.channels = 1;
else if (my_codec_param.channels > 1)
@@ -106,16 +114,17 @@
continue;
if (STR_CASE_CMP(my_codec_param.plname, "telephone-event") == 0)
continue;
- acm_b_->RegisterReceiveCodec(my_codec_param);
+ ASSERT_EQ(0, acm_b_->RegisterReceiveCodec(my_codec_param)) <<
+ "Couldn't register receive codec.\n";
}
// Create and connect the channel
- channel_a2b_ = new Channel;
- acm_a_->RegisterTransportCallback(channel_a2b_);
+ ASSERT_EQ(0, acm_a_->RegisterTransportCallback(channel_a2b_)) <<
+ "Couldn't register Transport callback.\n";
channel_a2b_->RegisterReceiverACM(acm_b_.get());
}
- void Perform(const Config* config, size_t num_tests, int duration_sec,
+ void Perform(const TestSettings* config, size_t num_tests, int duration_sec,
const char* output_prefix) {
for (size_t n = 0; n < num_tests; ++n) {
ApplyConfig(config[n]);
@@ -124,8 +133,7 @@
}
private:
-
- void ApplyConfig(const Config& config) {
+ void ApplyConfig(const TestSettings& config) {
printf("====================================\n");
printf("Test %d \n"
"Codec: %s, %d kHz, %d channel(s)\n"
@@ -140,19 +148,22 @@
ConfigChannel(config.packet_loss);
}
- void SendCodec(const CodecConfig& config) {
+ void SendCodec(const CodecSettings& config) {
CodecInst my_codec_param;
- ASSERT_EQ(
- 0,
- AudioCodingModule::Codec(config.name, &my_codec_param,
- config.sample_rate_hz, config.num_channels));
+ ASSERT_EQ(0, AudioCodingModule::Codec(
+ config.name, &my_codec_param, config.sample_rate_hz,
+ config.num_channels)) << "Specified codec is not supported.\n";
+
encoding_sample_rate_hz_ = my_codec_param.plfreq;
- ASSERT_EQ(0, acm_a_->RegisterSendCodec(my_codec_param));
+ ASSERT_EQ(0, acm_a_->RegisterSendCodec(my_codec_param)) <<
+ "Failed to register send-codec.\n";
}
- void ConfigAcm(const AcmConfig& config) {
- ASSERT_EQ(0, acm_a_->SetVAD(config.dtx, config.dtx, VADAggr));
- ASSERT_EQ(0, acm_a_->SetFECStatus(config.fec));
+ void ConfigAcm(const AcmSettings& config) {
+ ASSERT_EQ(0, acm_a_->SetVAD(config.dtx, config.dtx, VADAggr)) <<
+ "Failed to set VAD.\n";
+ ASSERT_EQ(0, acm_a_->SetFECStatus(config.fec)) <<
+ "Failed to set FEC.\n";
}
void ConfigChannel(bool packet_loss) {
@@ -230,19 +241,39 @@
int encoding_sample_rate_hz_;
};
-void RunTest() {
- Config config;
- strcpy(config.codec.name, FLAGS_codec.c_str());
- config.codec.sample_rate_hz = FLAGS_sample_rate_hz;
- config.codec.num_channels = FLAGS_num_channels;
- config.acm.dtx = FLAGS_dtx;
- config.acm.fec = false;
- config.packet_loss = false;
-
- DelayTest delay_test;
- delay_test.SetUp();
- delay_test.Perform(&config, 1, 240, "delay_test");
- delay_test.TearDown();
-}
-} // namespace
} // namespace webrtc
+
+int main(int argc, char* argv[]) {
+ google::ParseCommandLineFlags(&argc, &argv, true);
+ webrtc::Config config;
+ webrtc::TestSettings test_setting;
+ strcpy(test_setting.codec.name, FLAGS_codec.c_str());
+
+ if (FLAGS_sample_rate_hz != 8000 &&
+ FLAGS_sample_rate_hz != 16000 &&
+ FLAGS_sample_rate_hz != 32000 &&
+ FLAGS_sample_rate_hz != 48000) {
+ std::cout << "Invalid sampling rate.\n";
+ return 1;
+ }
+ test_setting.codec.sample_rate_hz = FLAGS_sample_rate_hz;
+ if (FLAGS_num_channels < 1 || FLAGS_num_channels > 2) {
+ std::cout << "Only mono and stereo are supported.\n";
+ return 1;
+ }
+ test_setting.codec.num_channels = FLAGS_num_channels;
+ test_setting.acm.dtx = FLAGS_dtx;
+ test_setting.acm.fec = FLAGS_fec;
+ test_setting.packet_loss = FLAGS_packet_loss;
+
+ if (FLAGS_acm2) {
+ webrtc::UseNewAcm(&config);
+ } else {
+ webrtc::UseLegacyAcm(&config);
+ }
+
+ webrtc::DelayTest delay_test(config);
+ delay_test.Initialize();
+ delay_test.Perform(&test_setting, 1, 240, "delay_test");
+ return 0;
+}
diff --git a/modules/audio_coding/neteq/automode.c b/modules/audio_coding/neteq/automode.c
index a922448..4dbd81e 100644
--- a/modules/audio_coding/neteq/automode.c
+++ b/modules/audio_coding/neteq/automode.c
@@ -28,6 +28,17 @@
extern FILE *delay_fid2; /* file pointer to delay log file */
#endif /* NETEQ_DELAY_LOGGING */
+// These two functions are copied from module_common_types.h, but adapted for C.
+int WebRtcNetEQ_IsNewerSequenceNumber(uint16_t sequence_number,
+ uint16_t prev_sequence_number) {
+ return sequence_number != prev_sequence_number &&
+ ((uint16_t) (sequence_number - prev_sequence_number)) < 0x8000;
+}
+
+int WebRtcNetEQ_IsNewerTimestamp(uint32_t timestamp, uint32_t prev_timestamp) {
+ return timestamp != prev_timestamp &&
+ ((uint32_t) (timestamp - prev_timestamp)) < 0x80000000;
+}
int WebRtcNetEQ_UpdateIatStatistics(AutomodeInst_t *inst, int maxBufLen,
uint16_t seqNumber, uint32_t timeStamp,
@@ -55,7 +66,8 @@
/****************************/
/* Try calculating packet length from current and previous timestamps */
- if ((timeStamp <= inst->lastTimeStamp) || (seqNumber <= inst->lastSeqNo))
+ if (!WebRtcNetEQ_IsNewerTimestamp(timeStamp, inst->lastTimeStamp) ||
+ !WebRtcNetEQ_IsNewerSequenceNumber(seqNumber, inst->lastSeqNo))
{
/* Wrong timestamp or sequence order; revert to backup plan */
packetLenSamp = inst->packetSpeechLenSamp; /* use stored value */
@@ -68,7 +80,7 @@
}
/* Check that the packet size is positive; if not, the statistics cannot be updated. */
- if (packetLenSamp > 0)
+ if (inst->firstPacketReceived && packetLenSamp > 0)
{ /* packet size ok */
/* calculate inter-arrival time in integer packets (rounding down) */
@@ -113,19 +125,19 @@
} /* end of streaming mode */
/* check for discontinuous packet sequence and re-ordering */
- if (seqNumber > inst->lastSeqNo + 1)
+ if (WebRtcNetEQ_IsNewerSequenceNumber(seqNumber, inst->lastSeqNo + 1))
{
/* Compensate for gap in the sequence numbers.
* Reduce IAT with expected extra time due to lost packets, but ensure that
* the IAT is not negative.
*/
timeIat -= WEBRTC_SPL_MIN(timeIat,
- (uint32_t) (seqNumber - inst->lastSeqNo - 1));
+ (uint16_t) (seqNumber - (uint16_t) (inst->lastSeqNo + 1)));
}
- else if (seqNumber < inst->lastSeqNo)
+ else if (!WebRtcNetEQ_IsNewerSequenceNumber(seqNumber, inst->lastSeqNo))
{
/* compensate for re-ordering */
- timeIat += (uint32_t) (inst->lastSeqNo + 1 - seqNumber);
+ timeIat += (uint16_t) (inst->lastSeqNo + 1 - seqNumber);
}
/* saturate IAT at maximum value */
@@ -316,6 +328,8 @@
inst->lastTimeStamp = timeStamp; /* remember current timestamp */
+ inst->firstPacketReceived = 1;
+
return retval;
}
diff --git a/modules/audio_coding/neteq/automode.h b/modules/audio_coding/neteq/automode.h
index 16d72e8..c5dd829 100644
--- a/modules/audio_coding/neteq/automode.h
+++ b/modules/audio_coding/neteq/automode.h
@@ -80,6 +80,8 @@
contained special information */
uint16_t lastSeqNo; /* sequence number for last packet received */
uint32_t lastTimeStamp; /* timestamp for the last packet received */
+ int firstPacketReceived; /* set to zero implicitly when the instance is
+ filled with zeros */
int32_t sampleMemory; /* memory position for keeping track of how many
samples we cut during expand */
int16_t prevTimeScale; /* indicates that the last mode was an accelerate
diff --git a/modules/audio_coding/neteq/webrtc_neteq_unittest.cc b/modules/audio_coding/neteq/webrtc_neteq_unittest.cc
index 8f1c6ba..c37f899 100644
--- a/modules/audio_coding/neteq/webrtc_neteq_unittest.cc
+++ b/modules/audio_coding/neteq/webrtc_neteq_unittest.cc
@@ -17,6 +17,7 @@
#include <stdlib.h>
#include <string.h> // memset
+#include <set>
#include <sstream>
#include <string>
#include <vector>
@@ -193,6 +194,8 @@
WebRtcNetEQ_RTPInfo* rtp_info,
uint8_t* payload,
int* payload_len);
+ void WrapTest(uint16_t start_seq_no, uint32_t start_timestamp,
+ const std::set<uint16_t>& drop_seq_numbers);
NETEQTEST_NetEQClass* neteq_inst_;
std::vector<NETEQTEST_Decoder*> dec_;
@@ -505,7 +508,7 @@
WebRtcNetEQ_NetworkStatistics network_stats;
ASSERT_EQ(0, WebRtcNetEQ_GetNetworkStatistics(neteq_inst_->instance(),
&network_stats));
- EXPECT_EQ(-106911, network_stats.clockDriftPPM);
+ EXPECT_EQ(-103196, network_stats.clockDriftPPM);
}
TEST_F(NetEqDecodingTest, TestAverageInterArrivalTimePositive) {
@@ -536,7 +539,7 @@
WebRtcNetEQ_NetworkStatistics network_stats;
ASSERT_EQ(0, WebRtcNetEQ_GetNetworkStatistics(neteq_inst_->instance(),
&network_stats));
- EXPECT_EQ(108352, network_stats.clockDriftPPM);
+ EXPECT_EQ(110946, network_stats.clockDriftPPM);
}
TEST_F(NetEqDecodingTest, LongCngWithClockDrift) {
@@ -700,4 +703,76 @@
}
}
+void NetEqDecodingTest::WrapTest(uint16_t start_seq_no,
+ uint32_t start_timestamp,
+ const std::set<uint16_t>& drop_seq_numbers) {
+ uint16_t seq_no = start_seq_no;
+ uint32_t timestamp = start_timestamp;
+ const int kFrameSizeMs = 30;
+ const int kSamples = kFrameSizeMs * 16;
+ const int kPayloadBytes = kSamples * 2;
+ double next_input_time_ms = 0.0;
+
+ // Insert speech for 1 second.
+ const int kSpeechDurationMs = 1000;
+ for (double t_ms = 0; t_ms < kSpeechDurationMs; t_ms += 10) {
+ // Each turn in this for loop is 10 ms.
+ while (next_input_time_ms <= t_ms) {
+ // Insert one 30 ms speech frame.
+ uint8_t payload[kPayloadBytes] = {0};
+ WebRtcNetEQ_RTPInfo rtp_info;
+ PopulateRtpInfo(seq_no, timestamp, &rtp_info);
+ if (drop_seq_numbers.find(seq_no) == drop_seq_numbers.end()) {
+ // This sequence number was not in the set to drop. Insert it.
+ ASSERT_EQ(0,
+ WebRtcNetEQ_RecInRTPStruct(neteq_inst_->instance(),
+ &rtp_info,
+ payload,
+ kPayloadBytes, 0));
+ }
+ ++seq_no;
+ timestamp += kSamples;
+ next_input_time_ms += static_cast<double>(kFrameSizeMs);
+ WebRtcNetEQ_NetworkStatistics network_stats;
+ ASSERT_EQ(0, WebRtcNetEQ_GetNetworkStatistics(neteq_inst_->instance(),
+ &network_stats));
+ // Expect preferred and actual buffer size to be no more than 2 frames.
+ EXPECT_LE(network_stats.preferredBufferSize, kFrameSizeMs * 2);
+ EXPECT_LE(network_stats.currentBufferSize, kFrameSizeMs * 2);
+ }
+ // Pull out data once.
+ ASSERT_TRUE(kBlockSize16kHz == neteq_inst_->recOut(out_data_));
+ // Expect delay (in samples) to be less than 2 packets.
+ EXPECT_LE(timestamp - neteq_inst_->getSpeechTimeStamp(),
+ static_cast<uint32_t>(kSamples * 2));
+ }
+}
+
+TEST_F(NetEqDecodingTest, SequenceNumberWrap) {
+ // Start with a sequence number that will soon wrap.
+ std::set<uint16_t> drop_seq_numbers; // Don't drop any packets.
+ WrapTest(0xFFFF - 5, 0, drop_seq_numbers);
+}
+
+TEST_F(NetEqDecodingTest, SequenceNumberWrapAndDrop) {
+ // Start with a sequence number that will soon wrap.
+ std::set<uint16_t> drop_seq_numbers;
+ drop_seq_numbers.insert(0xFFFF);
+ drop_seq_numbers.insert(0x0);
+ WrapTest(0xFFFF - 5, 0, drop_seq_numbers);
+}
+
+TEST_F(NetEqDecodingTest, TimestampWrap) {
+ // Start with a timestamp that will soon wrap.
+ std::set<uint16_t> drop_seq_numbers;
+ WrapTest(0, 0xFFFFFFFF - 1000, drop_seq_numbers);
+}
+
+TEST_F(NetEqDecodingTest, TimestampAndSequenceNumberWrap) {
+ // Start with a timestamp and a sequence number that will wrap at the same
+ // time.
+ std::set<uint16_t> drop_seq_numbers;
+ WrapTest(0xFFFF - 2, 0xFFFFFFFF - 1000, drop_seq_numbers);
+}
+
} // namespace
diff --git a/modules/audio_coding/neteq4/audio_decoder_unittest.cc b/modules/audio_coding/neteq4/audio_decoder_unittest.cc
index 0ed13e2..dbd9d12 100644
--- a/modules/audio_coding/neteq4/audio_decoder_unittest.cc
+++ b/modules/audio_coding/neteq4/audio_decoder_unittest.cc
@@ -119,14 +119,16 @@
encoded_bytes_ += enc_len;
processed_samples += frame_size_;
}
- // This test fails on Win x64, see issue webrtc:1459
-#if !(defined(_WIN32) && defined(WEBRTC_ARCH_64_BITS))
- EXPECT_EQ(expected_bytes, encoded_bytes_);
+ // For some codecs it doesn't make sense to check expected number of bytes,
+ // since the number can vary for different platforms. Opus and iSAC are
+ // such codecs. In this case expected_bytes is set to 0.
+ if (expected_bytes) {
+ EXPECT_EQ(expected_bytes, encoded_bytes_);
+ }
CompareInputOutput(processed_samples, tolerance, delay);
if (channels_ == 2)
CompareTwoChannels(processed_samples, channel_diff_tolerance);
EXPECT_LE(MseInputOutput(processed_samples, delay), mse);
-#endif
}
// The absolute difference between the input and output (the first channel) is
@@ -733,7 +735,7 @@
double mse = 434951.0;
int delay = 48; // Delay from input to output.
EXPECT_TRUE(AudioDecoder::CodecSupported(kDecoderISAC));
- EncodeDecodeTest(883, tolerance, mse, delay);
+ EncodeDecodeTest(0, tolerance, mse, delay);
ReInitTest();
EXPECT_TRUE(decoder_->HasDecodePlc());
DecodePlcTest();
@@ -744,7 +746,7 @@
double mse = 8.18e6;
int delay = 160; // Delay from input to output.
EXPECT_TRUE(AudioDecoder::CodecSupported(kDecoderISACswb));
- EncodeDecodeTest(853, tolerance, mse, delay);
+ EncodeDecodeTest(0, tolerance, mse, delay);
ReInitTest();
EXPECT_TRUE(decoder_->HasDecodePlc());
DecodePlcTest();
@@ -755,7 +757,7 @@
double mse = 8.18e6;
int delay = 160; // Delay from input to output.
EXPECT_TRUE(AudioDecoder::CodecSupported(kDecoderISACswb));
- EncodeDecodeTest(853, tolerance, mse, delay);
+ EncodeDecodeTest(0, tolerance, mse, delay);
ReInitTest();
EXPECT_TRUE(decoder_->HasDecodePlc());
DecodePlcTest();
@@ -801,7 +803,7 @@
double mse = 238630.0;
int delay = 22; // Delay from input to output.
EXPECT_TRUE(AudioDecoder::CodecSupported(kDecoderOpus));
- EncodeDecodeTest(731, tolerance, mse, delay);
+ EncodeDecodeTest(0, tolerance, mse, delay);
ReInitTest();
EXPECT_FALSE(decoder_->HasDecodePlc());
}
@@ -812,7 +814,7 @@
double mse = 238630.0;
int delay = 22; // Delay from input to output.
EXPECT_TRUE(AudioDecoder::CodecSupported(kDecoderOpus_2ch));
- EncodeDecodeTest(1383, tolerance, mse, delay, channel_diff_tolerance);
+ EncodeDecodeTest(0, tolerance, mse, delay, channel_diff_tolerance);
ReInitTest();
EXPECT_FALSE(decoder_->HasDecodePlc());
}
diff --git a/modules/audio_coding/neteq4/delay_manager.cc b/modules/audio_coding/neteq4/delay_manager.cc
index 63ed525..e80b9de 100644
--- a/modules/audio_coding/neteq4/delay_manager.cc
+++ b/modules/audio_coding/neteq4/delay_manager.cc
@@ -17,6 +17,7 @@
#include "webrtc/common_audio/signal_processing/include/signal_processing_library.h"
#include "webrtc/modules/audio_coding/neteq4/delay_peak_detector.h"
+#include "webrtc/modules/interface/module_common_types.h"
#include "webrtc/system_wrappers/interface/logging.h"
namespace webrtc {
@@ -85,10 +86,9 @@
}
// Try calculating packet length from current and previous timestamps.
- // TODO(hlundin): Take care of wrap-around. Not done yet due to legacy
- // bit-exactness.
int packet_len_ms;
- if ((timestamp <= last_timestamp_) || (sequence_number <= last_seq_no_)) {
+ if (!IsNewerTimestamp(timestamp, last_timestamp_) ||
+ !IsNewerSequenceNumber(sequence_number, last_seq_no_)) {
// Wrong timestamp or sequence order; use stored value.
packet_len_ms = packet_len_ms_;
} else {
@@ -111,18 +111,14 @@
}
// Check for discontinuous packet sequence and re-ordering.
- if (sequence_number > last_seq_no_ + 1) {
- // TODO(hlundin): Take care of wrap-around. Not done yet due to legacy
- // bit-exactness.
+ if (IsNewerSequenceNumber(sequence_number, last_seq_no_ + 1)) {
// Compensate for gap in the sequence numbers. Reduce IAT with the
// expected extra time due to lost packets, but ensure that the IAT is
// not negative.
- iat_packets -= sequence_number - last_seq_no_ - 1;
+ iat_packets -= static_cast<uint16_t>(sequence_number - last_seq_no_ - 1);
iat_packets = std::max(iat_packets, 0);
- } else if (sequence_number < last_seq_no_) {
- // TODO(hlundin): Take care of wrap-around.
- // Compensate for re-ordering.
- iat_packets += last_seq_no_ + 1 - sequence_number;
+ } else if (!IsNewerSequenceNumber(sequence_number, last_seq_no_)) {
+ iat_packets += static_cast<uint16_t>(last_seq_no_ + 1 - sequence_number);
}
// Saturate IAT at maximum value.
diff --git a/modules/audio_coding/neteq4/neteq_unittest.cc b/modules/audio_coding/neteq4/neteq_unittest.cc
index a8cff5e..965f75f 100644
--- a/modules/audio_coding/neteq4/neteq_unittest.cc
+++ b/modules/audio_coding/neteq4/neteq_unittest.cc
@@ -18,6 +18,7 @@
#include <string.h> // memset
#include <cmath>
+#include <set>
#include <string>
#include <vector>
@@ -214,6 +215,10 @@
void CheckBgnOff(int sampling_rate, NetEqBackgroundNoiseMode bgn_mode);
+ void WrapTest(uint16_t start_seq_no, uint32_t start_timestamp,
+ const std::set<uint16_t>& drop_seq_numbers,
+ bool expect_seq_no_wrap, bool expect_timestamp_wrap);
+
NetEq* neteq_;
FILE* rtp_fp_;
unsigned int sim_clock_;
@@ -260,10 +265,12 @@
#endif // WEBRTC_ANDROID
// Load iSAC.
ASSERT_EQ(0, neteq_->RegisterPayloadType(kDecoderISAC, 103));
+#ifndef WEBRTC_ANDROID
// Load iSAC SWB.
ASSERT_EQ(0, neteq_->RegisterPayloadType(kDecoderISACswb, 104));
// Load iSAC FB.
ASSERT_EQ(0, neteq_->RegisterPayloadType(kDecoderISACfb, 105));
+#endif // WEBRTC_ANDROID
// Load PCM16B nb.
ASSERT_EQ(0, neteq_->RegisterPayloadType(kDecoderPCM16B, 93));
// Load PCM16B wb.
@@ -1104,4 +1111,109 @@
}
}
+void NetEqDecodingTest::WrapTest(uint16_t start_seq_no,
+ uint32_t start_timestamp,
+ const std::set<uint16_t>& drop_seq_numbers,
+ bool expect_seq_no_wrap,
+ bool expect_timestamp_wrap) {
+ uint16_t seq_no = start_seq_no;
+ uint32_t timestamp = start_timestamp;
+ const int kBlocksPerFrame = 3; // Number of 10 ms blocks per frame.
+ const int kFrameSizeMs = kBlocksPerFrame * kTimeStepMs;
+ const int kSamples = kBlockSize16kHz * kBlocksPerFrame;
+ const int kPayloadBytes = kSamples * sizeof(int16_t);
+ double next_input_time_ms = 0.0;
+ int16_t decoded[kBlockSize16kHz];
+ int num_channels;
+ int samples_per_channel;
+ NetEqOutputType output_type;
+ uint32_t receive_timestamp = 0;
+
+ // Insert speech for 1 second.
+ const int kSpeechDurationMs = 2000;
+ int packets_inserted = 0;
+ uint16_t last_seq_no;
+ uint32_t last_timestamp;
+ bool timestamp_wrapped = false;
+ bool seq_no_wrapped = false;
+ for (double t_ms = 0; t_ms < kSpeechDurationMs; t_ms += 10) {
+ // Each turn in this for loop is 10 ms.
+ while (next_input_time_ms <= t_ms) {
+ // Insert one 30 ms speech frame.
+ uint8_t payload[kPayloadBytes] = {0};
+ WebRtcRTPHeader rtp_info;
+ PopulateRtpInfo(seq_no, timestamp, &rtp_info);
+ if (drop_seq_numbers.find(seq_no) == drop_seq_numbers.end()) {
+ // This sequence number was not in the set to drop. Insert it.
+ ASSERT_EQ(0,
+ neteq_->InsertPacket(rtp_info, payload, kPayloadBytes,
+ receive_timestamp));
+ ++packets_inserted;
+ }
+ NetEqNetworkStatistics network_stats;
+ ASSERT_EQ(0, neteq_->NetworkStatistics(&network_stats));
+
+ // Due to internal NetEq logic, preferred buffer-size is about 4 times the
+ // packet size for first few packets. Therefore we refrain from checking
+ // the criteria.
+ if (packets_inserted > 4) {
+ // Expect preferred and actual buffer size to be no more than 2 frames.
+ EXPECT_LE(network_stats.preferred_buffer_size_ms, kFrameSizeMs * 2);
+ EXPECT_LE(network_stats.current_buffer_size_ms, kFrameSizeMs * 2);
+ }
+ last_seq_no = seq_no;
+ last_timestamp = timestamp;
+
+ ++seq_no;
+ timestamp += kSamples;
+ receive_timestamp += kSamples;
+ next_input_time_ms += static_cast<double>(kFrameSizeMs);
+
+ seq_no_wrapped |= seq_no < last_seq_no;
+ timestamp_wrapped |= timestamp < last_timestamp;
+ }
+ // Pull out data once.
+ ASSERT_EQ(0, neteq_->GetAudio(kBlockSize16kHz, decoded,
+ &samples_per_channel, &num_channels,
+ &output_type));
+ ASSERT_EQ(kBlockSize16kHz, samples_per_channel);
+ ASSERT_EQ(1, num_channels);
+
+ // Expect delay (in samples) to be less than 2 packets.
+ EXPECT_LE(timestamp - neteq_->PlayoutTimestamp(),
+ static_cast<uint32_t>(kSamples * 2));
+
+ }
+ // Make sure we have actually tested wrap-around.
+ ASSERT_EQ(expect_seq_no_wrap, seq_no_wrapped);
+ ASSERT_EQ(expect_timestamp_wrap, timestamp_wrapped);
+}
+
+TEST_F(NetEqDecodingTest, SequenceNumberWrap) {
+ // Start with a sequence number that will soon wrap.
+ std::set<uint16_t> drop_seq_numbers; // Don't drop any packets.
+ WrapTest(0xFFFF - 10, 0, drop_seq_numbers, true, false);
+}
+
+TEST_F(NetEqDecodingTest, SequenceNumberWrapAndDrop) {
+ // Start with a sequence number that will soon wrap.
+ std::set<uint16_t> drop_seq_numbers;
+ drop_seq_numbers.insert(0xFFFF);
+ drop_seq_numbers.insert(0x0);
+ WrapTest(0xFFFF - 10, 0, drop_seq_numbers, true, false);
+}
+
+TEST_F(NetEqDecodingTest, TimestampWrap) {
+ // Start with a timestamp that will soon wrap.
+ std::set<uint16_t> drop_seq_numbers;
+ WrapTest(0, 0xFFFFFFFF - 3000, drop_seq_numbers, false, true);
+}
+
+TEST_F(NetEqDecodingTest, TimestampAndSequenceNumberWrap) {
+ // Start with a timestamp and a sequence number that will wrap at the same
+ // time.
+ std::set<uint16_t> drop_seq_numbers;
+ WrapTest(0xFFFF - 10, 0xFFFFFFFF - 5000, drop_seq_numbers, true, true);
+}
+
} // namespace
diff --git a/modules/audio_device/android/opensles_input.cc b/modules/audio_device/android/opensles_input.cc
index 9f58205..5bce539 100644
--- a/modules/audio_device/android/opensles_input.cc
+++ b/modules/audio_device/android/opensles_input.cc
@@ -126,7 +126,6 @@
int32_t OpenSlesInput::InitRecording() {
assert(initialized_);
- assert(!rec_initialized_);
rec_initialized_ = true;
return 0;
}
@@ -165,6 +164,7 @@
int32_t OpenSlesInput::StopRecording() {
StopCbThreads();
DestroyAudioRecorder();
+ recording_ = false;
return 0;
}
diff --git a/modules/audio_device/android/opensles_output.cc b/modules/audio_device/android/opensles_output.cc
index 882440e..f264f21 100644
--- a/modules/audio_device/android/opensles_output.cc
+++ b/modules/audio_device/android/opensles_output.cc
@@ -139,7 +139,6 @@
int32_t OpenSlesOutput::InitPlayout() {
assert(initialized_);
- assert(!play_initialized_);
play_initialized_ = true;
return 0;
}
@@ -176,6 +175,7 @@
int32_t OpenSlesOutput::StopPlayout() {
StopCbThreads();
DestroyAudioPlayer();
+ playing_ = false;
return 0;
}
diff --git a/modules/audio_device/include/fake_audio_device.h b/modules/audio_device/include/fake_audio_device.h
index 7966716..0248317 100644
--- a/modules/audio_device/include/fake_audio_device.h
+++ b/modules/audio_device/include/fake_audio_device.h
@@ -15,7 +15,7 @@
class FakeAudioDeviceModule : public AudioDeviceModule {
public:
FakeAudioDeviceModule() {}
- ~FakeAudioDeviceModule() {}
+ virtual ~FakeAudioDeviceModule() {}
virtual int32_t AddRef() { return 0; }
virtual int32_t Release() { return 0; }
virtual int32_t RegisterEventObserver(AudioDeviceObserver* eventCallback) {
@@ -48,283 +48,112 @@
virtual int32_t Process() { return 0; }
virtual int32_t Terminate() { return 0; }
- virtual int32_t ActiveAudioLayer(AudioLayer* audioLayer) const {
- assert(false);
- return 0;
- }
- virtual ErrorCode LastError() const {
- assert(false);
- return kAdmErrNone;
- }
- virtual bool Initialized() const {
- assert(false);
- return true;
- }
- virtual int16_t PlayoutDevices() {
- assert(false);
- return 0;
- }
- virtual int16_t RecordingDevices() {
- assert(false);
- return 0;
- }
+ virtual int32_t ActiveAudioLayer(AudioLayer* audioLayer) const { return 0; }
+ virtual ErrorCode LastError() const { return kAdmErrNone; }
+ virtual bool Initialized() const { return true; }
+ virtual int16_t PlayoutDevices() { return 0; }
+ virtual int16_t RecordingDevices() { return 0; }
virtual int32_t PlayoutDeviceName(uint16_t index,
char name[kAdmMaxDeviceNameSize],
char guid[kAdmMaxGuidSize]) {
- assert(false);
return 0;
}
virtual int32_t RecordingDeviceName(uint16_t index,
char name[kAdmMaxDeviceNameSize],
char guid[kAdmMaxGuidSize]) {
- assert(false);
return 0;
}
- virtual int32_t PlayoutIsAvailable(bool* available) {
- assert(false);
- return 0;
- }
- virtual int32_t InitPlayout() {
- assert(false);
- return 0;
- }
- virtual bool PlayoutIsInitialized() const {
- assert(false);
- return true;
- }
- virtual int32_t RecordingIsAvailable(bool* available) {
- assert(false);
- return 0;
- }
- virtual int32_t InitRecording() {
- assert(false);
- return 0;
- }
- virtual bool RecordingIsInitialized() const {
- assert(false);
- return true;
- }
- virtual int32_t StartPlayout() {
- assert(false);
- return 0;
- }
- virtual bool Playing() const {
- assert(false);
- return false;
- }
- virtual int32_t StartRecording() {
- assert(false);
- return 0;
- }
- virtual bool Recording() const {
- assert(false);
- return false;
- }
- virtual bool AGC() const {
- assert(false);
- return true;
- }
+ virtual int32_t PlayoutIsAvailable(bool* available) { return 0; }
+ virtual int32_t InitPlayout() { return 0; }
+ virtual bool PlayoutIsInitialized() const { return true; }
+ virtual int32_t RecordingIsAvailable(bool* available) { return 0; }
+ virtual int32_t InitRecording() { return 0; }
+ virtual bool RecordingIsInitialized() const { return true; }
+ virtual int32_t StartPlayout() { return 0; }
+ virtual bool Playing() const { return false; }
+ virtual int32_t StartRecording() { return 0; }
+ virtual bool Recording() const { return false; }
+ virtual bool AGC() const { return true; }
virtual int32_t SetWaveOutVolume(uint16_t volumeLeft,
uint16_t volumeRight) {
- assert(false);
return 0;
}
virtual int32_t WaveOutVolume(uint16_t* volumeLeft,
uint16_t* volumeRight) const {
- assert(false);
return 0;
}
- virtual bool SpeakerIsInitialized() const {
- assert(false);
- return true;
- }
- virtual bool MicrophoneIsInitialized() const {
- assert(false);
- return true;
- }
- virtual int32_t SpeakerVolumeIsAvailable(bool* available) {
- assert(false);
- return 0;
- }
- virtual int32_t SetSpeakerVolume(uint32_t volume) {
- assert(false);
- return 0;
- }
- virtual int32_t SpeakerVolume(uint32_t* volume) const {
- assert(false);
- return 0;
- }
- virtual int32_t MaxSpeakerVolume(uint32_t* maxVolume) const {
- assert(false);
- return 0;
- }
- virtual int32_t MinSpeakerVolume(uint32_t* minVolume) const {
- assert(false);
- return 0;
- }
- virtual int32_t SpeakerVolumeStepSize(uint16_t* stepSize) const {
- assert(false);
- return 0;
- }
- virtual int32_t MicrophoneVolumeIsAvailable(bool* available) {
- assert(false);
- return 0;
- }
- virtual int32_t SetMicrophoneVolume(uint32_t volume) {
- assert(false);
- return 0;
- }
- virtual int32_t MicrophoneVolume(uint32_t* volume) const {
- assert(false);
- return 0;
- }
- virtual int32_t MaxMicrophoneVolume(uint32_t* maxVolume) const {
- assert(false);
- return 0;
- }
- virtual int32_t MinMicrophoneVolume(uint32_t* minVolume) const {
- assert(false);
- return 0;
- }
+ virtual bool SpeakerIsInitialized() const { return true; }
+ virtual bool MicrophoneIsInitialized() const { return true; }
+ virtual int32_t SpeakerVolumeIsAvailable(bool* available) { return 0; }
+ virtual int32_t SetSpeakerVolume(uint32_t volume) { return 0; }
+ virtual int32_t SpeakerVolume(uint32_t* volume) const { return 0; }
+ virtual int32_t MaxSpeakerVolume(uint32_t* maxVolume) const { return 0; }
+ virtual int32_t MinSpeakerVolume(uint32_t* minVolume) const { return 0; }
+ virtual int32_t SpeakerVolumeStepSize(uint16_t* stepSize) const { return 0; }
+ virtual int32_t MicrophoneVolumeIsAvailable(bool* available) { return 0; }
+ virtual int32_t SetMicrophoneVolume(uint32_t volume) { return 0; }
+ virtual int32_t MicrophoneVolume(uint32_t* volume) const { return 0; }
+ virtual int32_t MaxMicrophoneVolume(uint32_t* maxVolume) const { return 0; }
+ virtual int32_t MinMicrophoneVolume(uint32_t* minVolume) const { return 0; }
virtual int32_t MicrophoneVolumeStepSize(uint16_t* stepSize) const {
- assert(false);
return 0;
}
- virtual int32_t SpeakerMuteIsAvailable(bool* available) {
- assert(false);
- return 0;
- }
- virtual int32_t SetSpeakerMute(bool enable) {
- assert(false);
- return 0;
- }
- virtual int32_t SpeakerMute(bool* enabled) const {
- assert(false);
- return 0;
- }
- virtual int32_t MicrophoneMuteIsAvailable(bool* available) {
- assert(false);
- return 0;
- }
- virtual int32_t SetMicrophoneMute(bool enable) {
- assert(false);
- return 0;
- }
- virtual int32_t MicrophoneMute(bool* enabled) const {
- assert(false);
- return 0;
- }
- virtual int32_t MicrophoneBoostIsAvailable(bool* available) {
- assert(false);
- return 0;
- }
- virtual int32_t SetMicrophoneBoost(bool enable) {
- assert(false);
- return 0;
- }
- virtual int32_t MicrophoneBoost(bool* enabled) const {
- assert(false);
- return 0;
- }
+ virtual int32_t SpeakerMuteIsAvailable(bool* available) { return 0; }
+ virtual int32_t SetSpeakerMute(bool enable) { return 0; }
+ virtual int32_t SpeakerMute(bool* enabled) const { return 0; }
+ virtual int32_t MicrophoneMuteIsAvailable(bool* available) { return 0; }
+ virtual int32_t SetMicrophoneMute(bool enable) { return 0; }
+ virtual int32_t MicrophoneMute(bool* enabled) const { return 0; }
+ virtual int32_t MicrophoneBoostIsAvailable(bool* available) { return 0; }
+ virtual int32_t SetMicrophoneBoost(bool enable) { return 0; }
+ virtual int32_t MicrophoneBoost(bool* enabled) const { return 0; }
virtual int32_t StereoPlayoutIsAvailable(bool* available) const {
*available = false;
return 0;
}
- virtual int32_t StereoPlayout(bool* enabled) const {
- assert(false);
- return 0;
- }
+ virtual int32_t StereoPlayout(bool* enabled) const { return 0; }
virtual int32_t StereoRecordingIsAvailable(bool* available) const {
*available = false;
return 0;
}
- virtual int32_t StereoRecording(bool* enabled) const {
- assert(false);
- return 0;
- }
- virtual int32_t SetRecordingChannel(const ChannelType channel) {
- assert(false);
- return 0;
- }
- virtual int32_t RecordingChannel(ChannelType* channel) const {
- assert(false);
- return 0;
- }
+ virtual int32_t StereoRecording(bool* enabled) const { return 0; }
+ virtual int32_t SetRecordingChannel(const ChannelType channel) { return 0; }
+ virtual int32_t RecordingChannel(ChannelType* channel) const { return 0; }
virtual int32_t SetPlayoutBuffer(const BufferType type,
uint16_t sizeMS = 0) {
- assert(false);
return 0;
}
virtual int32_t PlayoutBuffer(BufferType* type, uint16_t* sizeMS) const {
- assert(false);
return 0;
}
- virtual int32_t PlayoutDelay(uint16_t* delayMS) const {
- assert(false);
- return 0;
- }
- virtual int32_t RecordingDelay(uint16_t* delayMS) const {
- assert(false);
- return 0;
- }
- virtual int32_t CPULoad(uint16_t* load) const {
- assert(false);
- return 0;
- }
+ virtual int32_t PlayoutDelay(uint16_t* delayMS) const { return 0; }
+ virtual int32_t RecordingDelay(uint16_t* delayMS) const { return 0; }
+ virtual int32_t CPULoad(uint16_t* load) const { return 0; }
virtual int32_t StartRawOutputFileRecording(
const char pcmFileNameUTF8[kAdmMaxFileNameSize]) {
- assert(false);
return 0;
}
- virtual int32_t StopRawOutputFileRecording() {
- assert(false);
- return 0;
- }
+ virtual int32_t StopRawOutputFileRecording() { return 0; }
virtual int32_t StartRawInputFileRecording(
const char pcmFileNameUTF8[kAdmMaxFileNameSize]) {
- assert(false);
return 0;
}
- virtual int32_t StopRawInputFileRecording() {
- assert(false);
- return 0;
- }
+ virtual int32_t StopRawInputFileRecording() { return 0; }
virtual int32_t SetRecordingSampleRate(const uint32_t samplesPerSec) {
- assert(false);
return 0;
}
virtual int32_t RecordingSampleRate(uint32_t* samplesPerSec) const {
- assert(false);
return 0;
}
virtual int32_t SetPlayoutSampleRate(const uint32_t samplesPerSec) {
- assert(false);
return 0;
}
- virtual int32_t PlayoutSampleRate(uint32_t* samplesPerSec) const {
- assert(false);
- return 0;
- }
- virtual int32_t ResetAudioDevice() {
- assert(false);
- return 0;
- }
- virtual int32_t SetLoudspeakerStatus(bool enable) {
- assert(false);
- return 0;
- }
- virtual int32_t GetLoudspeakerStatus(bool* enabled) const {
- assert(false);
- return 0;
- }
- virtual int32_t EnableBuiltInAEC(bool enable) {
- assert(false);
- return -1;
- }
- virtual bool BuiltInAECIsEnabled() const {
- assert(false);
- return false;
- }
+ virtual int32_t PlayoutSampleRate(uint32_t* samplesPerSec) const { return 0; }
+ virtual int32_t ResetAudioDevice() { return 0; }
+ virtual int32_t SetLoudspeakerStatus(bool enable) { return 0; }
+ virtual int32_t GetLoudspeakerStatus(bool* enabled) const { return 0; }
+ virtual int32_t EnableBuiltInAEC(bool enable) { return -1; }
+ virtual bool BuiltInAECIsEnabled() const { return false; }
};
} // namespace webrtc
diff --git a/modules/audio_processing/aec/aec_core.c b/modules/audio_processing/aec/aec_core.c
index 9227f10..bfa087c 100644
--- a/modules/audio_processing/aec/aec_core.c
+++ b/modules/audio_processing/aec/aec_core.c
@@ -1156,9 +1156,11 @@
memcpy(efw, dfw, sizeof(efw));
}
- // Reset if error is significantly larger than nearend (13 dB).
- if (seSum > (19.95f * sdSum)) {
- memset(aec->wfBuf, 0, sizeof(aec->wfBuf));
+ if (!aec->extended_filter_enabled) {
+ // Reset if error is significantly larger than nearend (13 dB).
+ if (seSum > (19.95f * sdSum)) {
+ memset(aec->wfBuf, 0, sizeof(aec->wfBuf));
+ }
}
// Subband coherence
diff --git a/modules/audio_processing/audio_processing_impl.cc b/modules/audio_processing/audio_processing_impl.cc
index b33049e..2eb97bf 100644
--- a/modules/audio_processing/audio_processing_impl.cc
+++ b/modules/audio_processing/audio_processing_impl.cc
@@ -186,6 +186,10 @@
(*it)->SetExtraOptions(config);
}
+int AudioProcessingImpl::EnableExperimentalNs(bool enable) {
+ return kNoError;
+}
+
int AudioProcessingImpl::set_sample_rate_hz(int rate) {
CriticalSectionScoped crit_scoped(crit_);
if (rate == sample_rate_hz_) {
diff --git a/modules/audio_processing/audio_processing_impl.h b/modules/audio_processing/audio_processing_impl.h
index b0afd6d..6c22878 100644
--- a/modules/audio_processing/audio_processing_impl.h
+++ b/modules/audio_processing/audio_processing_impl.h
@@ -59,6 +59,10 @@
virtual int Initialize() OVERRIDE;
virtual int InitializeLocked();
virtual void SetExtraOptions(const Config& config) OVERRIDE;
+ virtual int EnableExperimentalNs(bool enable) OVERRIDE;
+ virtual bool experimental_ns_enabled() const OVERRIDE {
+ return false;
+ }
virtual int set_sample_rate_hz(int rate) OVERRIDE;
virtual int sample_rate_hz() const OVERRIDE;
virtual int set_num_channels(int input_channels,
diff --git a/modules/audio_processing/include/audio_processing.h b/modules/audio_processing/include/audio_processing.h
index 1babe43..8297649 100644
--- a/modules/audio_processing/include/audio_processing.h
+++ b/modules/audio_processing/include/audio_processing.h
@@ -155,6 +155,9 @@
// ensures the options are applied immediately.
virtual void SetExtraOptions(const Config& config) = 0;
+ virtual int EnableExperimentalNs(bool enable) = 0;
+ virtual bool experimental_ns_enabled() const = 0;
+
// Sets the sample |rate| in Hz for both the primary and reverse audio
// streams. 8000, 16000 or 32000 Hz are permitted.
virtual int set_sample_rate_hz(int rate) = 0;
diff --git a/modules/audio_processing/include/mock_audio_processing.h b/modules/audio_processing/include/mock_audio_processing.h
index 9a36fe8..c4600bd 100644
--- a/modules/audio_processing/include/mock_audio_processing.h
+++ b/modules/audio_processing/include/mock_audio_processing.h
@@ -183,6 +183,10 @@
int());
MOCK_METHOD1(SetExtraOptions,
void(const Config& config));
+ MOCK_METHOD1(EnableExperimentalNs,
+ int(bool enable));
+ MOCK_CONST_METHOD0(experimental_ns_enabled,
+ bool());
MOCK_METHOD1(set_sample_rate_hz,
int(int rate));
MOCK_CONST_METHOD0(sample_rate_hz,
diff --git a/modules/audio_processing/utility/delay_estimator.c b/modules/audio_processing/utility/delay_estimator.c
index 91c1e9b..062874d 100644
--- a/modules/audio_processing/utility/delay_estimator.c
+++ b/modules/audio_processing/utility/delay_estimator.c
@@ -196,15 +196,19 @@
// Default return value if we're unable to estimate. -1 is used for errors.
self->last_delay = -2;
+
+ self->robust_validation_enabled = 0; // Disabled by default.
}
int WebRtc_ProcessBinarySpectrum(BinaryDelayEstimator* self,
uint32_t binary_near_spectrum) {
int i = 0;
int candidate_delay = -1;
+ int valid_candidate = 0;
int32_t value_best_candidate = 32 << 9; // 32 in Q9, (max |mean_bit_counts|).
int32_t value_worst_candidate = 0;
+ int32_t valley_depth = 0;
assert(self != NULL);
if (self->near_history_size > 1) {
@@ -249,6 +253,7 @@
value_worst_candidate = self->mean_bit_counts[i];
}
}
+ valley_depth = value_worst_candidate - value_best_candidate;
// The |value_best_candidate| is a good indicator on the probability of
// |candidate_delay| being an accurate delay (a small |value_best_candidate|
@@ -265,7 +270,7 @@
// Update |minimum_probability|.
if ((self->minimum_probability > kProbabilityLowerLimit) &&
- (value_worst_candidate - value_best_candidate > kProbabilityMinSpread)) {
+ (valley_depth > kProbabilityMinSpread)) {
// The "hard" threshold can't be lower than 17 (in Q9).
// The valley in the curve also has to be distinct, i.e., the
// difference between |value_worst_candidate| and |value_best_candidate| has
@@ -281,14 +286,21 @@
// Update |last_delay_probability|.
// We use a Markov type model, i.e., a slowly increasing level over time.
self->last_delay_probability++;
- if (value_worst_candidate > value_best_candidate + kProbabilityOffset) {
- // Reliable delay value for usage.
- if (value_best_candidate < self->minimum_probability) {
- self->last_delay = candidate_delay;
- }
+ // Validate |candidate_delay|. We have a reliable instantaneous delay
+ // estimate if
+ // 1) The valley is distinct enough (|valley_depth| > |kProbabilityOffset|)
+ // and
+ // 2) The depth of the valley is deep enough
+ // (|value_best_candidate| < |minimum_probability|)
+ // and deeper than the best estimate so far
+ // (|value_best_candidate| < |last_delay_probability|)
+ valid_candidate = ((valley_depth > kProbabilityOffset) &&
+ ((value_best_candidate < self->minimum_probability) ||
+ (value_best_candidate < self->last_delay_probability)));
+
+ if (valid_candidate) {
+ self->last_delay = candidate_delay;
if (value_best_candidate < self->last_delay_probability) {
- self->last_delay = candidate_delay;
- // Reset |last_delay_probability|.
self->last_delay_probability = value_best_candidate;
}
}
diff --git a/modules/audio_processing/utility/delay_estimator.h b/modules/audio_processing/utility/delay_estimator.h
index bf2b08a..561514b 100644
--- a/modules/audio_processing/utility/delay_estimator.h
+++ b/modules/audio_processing/utility/delay_estimator.h
@@ -42,6 +42,9 @@
// Delay memory.
int last_delay;
+ // Robust validation
+ int robust_validation_enabled;
+
// Far-end binary spectrum history buffer etc.
BinaryDelayEstimatorFarend* farend;
} BinaryDelayEstimator;
diff --git a/modules/audio_processing/utility/delay_estimator_unittest.cc b/modules/audio_processing/utility/delay_estimator_unittest.cc
index f4b4711..bdc199c 100644
--- a/modules/audio_processing/utility/delay_estimator_unittest.cc
+++ b/modules/audio_processing/utility/delay_estimator_unittest.cc
@@ -246,6 +246,17 @@
EXPECT_EQ(-1, WebRtc_AddFarSpectrumFix(farend_handle_, far_u16_,
spectrum_size_, 16));
+ // WebRtc_enable_robust_validation() should return -1 if we have:
+ // 1) NULL pointer as |handle|.
+ // 2) Incorrect |enable| value (not 0 or 1).
+ EXPECT_EQ(-1, WebRtc_enable_robust_validation(NULL, 0));
+ EXPECT_EQ(-1, WebRtc_enable_robust_validation(handle_, -1));
+ EXPECT_EQ(-1, WebRtc_enable_robust_validation(handle_, 2));
+
+ // WebRtc_is_robust_validation_enabled() should return -1 if we have NULL
+ // pointer as |handle|.
+ EXPECT_EQ(-1, WebRtc_is_robust_validation_enabled(NULL));
+
// WebRtc_DelayEstimatorProcessFloat() should return -1 if we have:
// 1) NULL pointer as |handle|.
// 2) NULL pointer as near-end spectrum.
@@ -283,6 +294,16 @@
WebRtc_FreeDelayEstimator(handle);
}
+TEST_F(DelayEstimatorTest, VerifyEnableRobustValidation) {
+ Init();
+ // Disabled by default.
+ EXPECT_EQ(0, WebRtc_is_robust_validation_enabled(handle_));
+ for (int i = 1; i >= 0; i--) {
+ EXPECT_EQ(0, WebRtc_enable_robust_validation(handle_, i));
+ EXPECT_EQ(i, WebRtc_is_robust_validation_enabled(handle_));
+ }
+}
+
TEST_F(DelayEstimatorTest, InitializedSpectrumAfterProcess) {
// In this test we verify that the mean spectra are initialized after first
// time we call WebRtc_AddFarSpectrum() and Process() respectively.
diff --git a/modules/audio_processing/utility/delay_estimator_wrapper.c b/modules/audio_processing/utility/delay_estimator_wrapper.c
index b72b8ff..ce44318 100644
--- a/modules/audio_processing/utility/delay_estimator_wrapper.c
+++ b/modules/audio_processing/utility/delay_estimator_wrapper.c
@@ -312,6 +312,30 @@
return 0;
}
+int WebRtc_enable_robust_validation(void* handle, int enable) {
+ DelayEstimator* self = (DelayEstimator*) handle;
+
+ if (self == NULL) {
+ return -1;
+ }
+ if ((enable < 0) || (enable > 1)) {
+ return -1;
+ }
+ assert(self->binary_handle != NULL);
+ self->binary_handle->robust_validation_enabled = enable;
+ return 0;
+}
+
+int WebRtc_is_robust_validation_enabled(void* handle) {
+ DelayEstimator* self = (DelayEstimator*) handle;
+
+ if (self == NULL) {
+ return -1;
+ }
+ assert(self->binary_handle != NULL);
+ return self->binary_handle->robust_validation_enabled;
+}
+
int WebRtc_DelayEstimatorProcessFix(void* handle,
uint16_t* near_spectrum,
int spectrum_size,
diff --git a/modules/audio_processing/utility/delay_estimator_wrapper.h b/modules/audio_processing/utility/delay_estimator_wrapper.h
index 51b9a0a..50bcdde 100644
--- a/modules/audio_processing/utility/delay_estimator_wrapper.h
+++ b/modules/audio_processing/utility/delay_estimator_wrapper.h
@@ -123,6 +123,18 @@
//
int WebRtc_InitDelayEstimator(void* handle);
+// TODO(bjornv): Implement this functionality. Currently, enabling it has no
+// impact, hence this is an empty API.
+// Enables/Disables a robust validation functionality in the delay estimation.
+// This is by default disabled upon initialization.
+// Inputs:
+// - handle : Pointer to the delay estimation instance.
+// - enable : Enable (1) or disable (0) this feature.
+int WebRtc_enable_robust_validation(void* handle, int enable);
+
+// Returns 1 if robust validation is enabled and 0 if disabled.
+int WebRtc_is_robust_validation_enabled(void* handle);
+
// Estimates and returns the delay between the far-end and near-end blocks. The
// value will be offset by the lookahead (i.e. the lookahead should be
// subtracted from the returned value).
diff --git a/modules/desktop_capture/desktop_and_cursor_composer.cc b/modules/desktop_capture/desktop_and_cursor_composer.cc
index 3141fee..6ed3ae8 100644
--- a/modules/desktop_capture/desktop_and_cursor_composer.cc
+++ b/modules/desktop_capture/desktop_and_cursor_composer.cc
@@ -51,6 +51,73 @@
}
}
+// DesktopFrame wrapper that draws mouse on a frame and restores original
+// content before releasing the underlying frame.
+class DesktopFrameWithCursor : public DesktopFrame {
+ public:
+ // Takes ownership of |frame|.
+ DesktopFrameWithCursor(DesktopFrame* frame,
+ const MouseCursor& cursor,
+ const DesktopVector& position);
+ virtual ~DesktopFrameWithCursor();
+
+ private:
+ scoped_ptr<DesktopFrame> original_frame_;
+
+ DesktopVector restore_position_;
+ scoped_ptr<DesktopFrame> restore_frame_;
+
+ DISALLOW_COPY_AND_ASSIGN(DesktopFrameWithCursor);
+};
+
+DesktopFrameWithCursor::DesktopFrameWithCursor(DesktopFrame* frame,
+ const MouseCursor& cursor,
+ const DesktopVector& position)
+ : DesktopFrame(frame->size(), frame->stride(),
+ frame->data(), frame->shared_memory()),
+ original_frame_(frame) {
+ set_dpi(frame->dpi());
+ set_capture_time_ms(frame->capture_time_ms());
+ mutable_updated_region()->Swap(frame->mutable_updated_region());
+
+ DesktopVector image_pos = position.subtract(cursor.hotspot());
+ DesktopRect target_rect = DesktopRect::MakeSize(cursor.image().size());
+ target_rect.Translate(image_pos);
+ DesktopVector target_origin = target_rect.top_left();
+ target_rect.IntersectWith(DesktopRect::MakeSize(size()));
+
+ if (target_rect.is_empty())
+ return;
+
+ // Copy original screen content under cursor to |restore_frame_|.
+ restore_position_ = target_rect.top_left();
+ restore_frame_.reset(new BasicDesktopFrame(target_rect.size()));
+ restore_frame_->CopyPixelsFrom(*this, target_rect.top_left(),
+ DesktopRect::MakeSize(restore_frame_->size()));
+
+ // Blit the cursor.
+ uint8_t* target_rect_data = reinterpret_cast<uint8_t*>(data()) +
+ target_rect.top() * stride() +
+ target_rect.left() * DesktopFrame::kBytesPerPixel;
+ DesktopVector origin_shift = target_rect.top_left().subtract(target_origin);
+ AlphaBlend(target_rect_data, stride(),
+ cursor.image().data() +
+ origin_shift.y() * cursor.image().stride() +
+ origin_shift.x() * DesktopFrame::kBytesPerPixel,
+ cursor.image().stride(),
+ target_rect.size());
+}
+
+DesktopFrameWithCursor::~DesktopFrameWithCursor() {
+ // Restore original content of the frame.
+ if (restore_frame_.get()) {
+ DesktopRect target_rect = DesktopRect::MakeSize(restore_frame_->size());
+ target_rect.Translate(restore_position_);
+ CopyPixelsFrom(restore_frame_->data(), restore_frame_->stride(),
+ target_rect);
+ }
+}
+
} // namespace
DesktopAndCursorComposer::DesktopAndCursorComposer(
@@ -81,22 +148,9 @@
void DesktopAndCursorComposer::OnCaptureCompleted(DesktopFrame* frame) {
if (cursor_.get() && cursor_state_ == MouseCursorMonitor::INSIDE) {
- DesktopVector image_pos = cursor_position_.subtract(cursor_->hotspot());
- DesktopRect target_rect = DesktopRect::MakeSize(cursor_->image().size());
- target_rect.Translate(image_pos);
- DesktopVector target_origin = target_rect.top_left();
- target_rect.IntersectWith(DesktopRect::MakeSize(frame->size()));
- DesktopVector origin_shift = target_rect.top_left().subtract(target_origin);
- int cursor_width = cursor_->image().size().width();
- AlphaBlend(reinterpret_cast<uint8_t*>(frame->data()) +
- target_rect.top() * frame->stride() +
- target_rect.left() * DesktopFrame::kBytesPerPixel,
- frame->stride(),
- cursor_->image().data() +
- (origin_shift.y() * cursor_width + origin_shift.x()) *
- DesktopFrame::kBytesPerPixel,
- cursor_width * DesktopFrame::kBytesPerPixel,
- target_rect.size());
+ DesktopFrameWithCursor* frame_with_cursor =
+ new DesktopFrameWithCursor(frame, *cursor_, cursor_position_);
+ frame = frame_with_cursor;
}
callback_->OnCaptureCompleted(frame);
diff --git a/modules/desktop_capture/desktop_and_cursor_composer_unittest.cc b/modules/desktop_capture/desktop_and_cursor_composer_unittest.cc
index 0bb7237..15d6f54 100644
--- a/modules/desktop_capture/desktop_and_cursor_composer_unittest.cc
+++ b/modules/desktop_capture/desktop_and_cursor_composer_unittest.cc
@@ -14,6 +14,7 @@
#include "webrtc/modules/desktop_capture/desktop_capture_options.h"
#include "webrtc/modules/desktop_capture/desktop_frame.h"
#include "webrtc/modules/desktop_capture/mouse_cursor.h"
+#include "webrtc/modules/desktop_capture/shared_desktop_frame.h"
#include "webrtc/modules/desktop_capture/window_capturer.h"
#include "webrtc/system_wrappers/interface/logging.h"
#include "webrtc/system_wrappers/interface/scoped_ptr.h"
@@ -74,11 +75,19 @@
*(data++) = GetFakeFramePixelValue(DesktopVector(x, y));
}
}
- callback_->OnCaptureCompleted(frame);
+
+ last_frame_.reset(SharedDesktopFrame::Wrap(frame));
+
+ callback_->OnCaptureCompleted(last_frame_->Share());
}
+ // Returns last fake captured frame.
+ SharedDesktopFrame* last_frame() { return last_frame_.get(); }
+
private:
Callback* callback_;
+
+ scoped_ptr<SharedDesktopFrame> last_frame_;
};
class FakeMouseMonitor : public MouseCursorMonitor {
@@ -155,8 +164,9 @@
public DesktopCapturer::Callback {
public:
DesktopAndCursorComposerTest()
- : fake_cursor_(new FakeMouseMonitor()),
- blender_(new FakeScreenCapturer(), fake_cursor_) {
+ : fake_screen_(new FakeScreenCapturer()),
+ fake_cursor_(new FakeMouseMonitor()),
+ blender_(fake_screen_, fake_cursor_) {
}
// DesktopCapturer::Callback interface
@@ -170,7 +180,9 @@
protected:
// Owned by |blender_|.
+ FakeScreenCapturer* fake_screen_;
FakeMouseMonitor* fake_cursor_;
+
DesktopAndCursorComposer blender_;
scoped_ptr<DesktopFrame> frame_;
};
@@ -213,6 +225,13 @@
blender_.Capture(DesktopRegion());
VerifyFrame(*frame_, state, pos);
+
+ // Verify that the cursor is erased before the frame buffer is returned to
+ // the screen capturer.
+ frame_.reset();
+ VerifyFrame(*fake_screen_->last_frame(),
+ MouseCursorMonitor::OUTSIDE,
+ DesktopVector());
}
}
diff --git a/modules/desktop_capture/desktop_frame.cc b/modules/desktop_capture/desktop_frame.cc
index f293baf..f26dc93 100644
--- a/modules/desktop_capture/desktop_frame.cc
+++ b/modules/desktop_capture/desktop_frame.cc
@@ -10,6 +10,7 @@
#include "webrtc/modules/desktop_capture/desktop_frame.h"
+#include <assert.h>
#include <string.h>
namespace webrtc {
@@ -27,6 +28,30 @@
DesktopFrame::~DesktopFrame() {}
+void DesktopFrame::CopyPixelsFrom(uint8_t* src_buffer, int src_stride,
+ const DesktopRect& dest_rect) {
+ assert(DesktopRect::MakeSize(size()).ContainsRect(dest_rect));
+
+ uint8_t* dest = data() + stride() * dest_rect.top() +
+ DesktopFrame::kBytesPerPixel * dest_rect.left();
+ for (int y = 0; y < dest_rect.height(); ++y) {
+ memcpy(dest, src_buffer, DesktopFrame::kBytesPerPixel * dest_rect.width());
+ src_buffer += src_stride;
+ dest += stride();
+ }
+}
+
+void DesktopFrame::CopyPixelsFrom(const DesktopFrame& src_frame,
+ const DesktopVector& src_pos,
+ const DesktopRect& dest_rect) {
+ assert(DesktopRect::MakeSize(src_frame.size()).ContainsRect(
+ DesktopRect::MakeOriginSize(src_pos, dest_rect.size())));
+
+ CopyPixelsFrom(src_frame.data() + src_frame.stride() * src_pos.y() +
+ DesktopFrame::kBytesPerPixel * src_pos.x(),
+ src_frame.stride(), dest_rect);
+}
+
BasicDesktopFrame::BasicDesktopFrame(DesktopSize size)
: DesktopFrame(size, kBytesPerPixel * size.width(),
new uint8_t[kBytesPerPixel * size.width() * size.height()],
diff --git a/modules/desktop_capture/desktop_frame.h b/modules/desktop_capture/desktop_frame.h
index b420a3c..a38359a 100644
--- a/modules/desktop_capture/desktop_frame.h
+++ b/modules/desktop_capture/desktop_frame.h
@@ -53,6 +53,14 @@
int32_t capture_time_ms() const { return capture_time_ms_; }
void set_capture_time_ms(int32_t time_ms) { capture_time_ms_ = time_ms; }
+ // Copies pixels from a buffer or another frame. |dest_rect| rect must lay
+ // within bounds of this frame.
+ void CopyPixelsFrom(uint8_t* src_buffer, int src_stride,
+ const DesktopRect& dest_rect);
+ void CopyPixelsFrom(const DesktopFrame& src_frame,
+ const DesktopVector& src_pos,
+ const DesktopRect& dest_rect);
+
protected:
DesktopFrame(DesktopSize size,
int stride,
diff --git a/modules/desktop_capture/desktop_geometry.cc b/modules/desktop_capture/desktop_geometry.cc
index cffaefe..1ff7c68 100644
--- a/modules/desktop_capture/desktop_geometry.cc
+++ b/modules/desktop_capture/desktop_geometry.cc
@@ -19,6 +19,11 @@
point.y() >= top() && point.y() < bottom();
}
+bool DesktopRect::ContainsRect(const DesktopRect& rect) const {
+ return rect.left() >= left() && rect.right() <= right() &&
+ rect.top() >= top() && rect.bottom() <= bottom();
+}
+
void DesktopRect::IntersectWith(const DesktopRect& rect) {
left_ = std::max(left(), rect.left());
top_ = std::max(top(), rect.top());
diff --git a/modules/desktop_capture/desktop_geometry.h b/modules/desktop_capture/desktop_geometry.h
index 580e0bf..e51273d 100644
--- a/modules/desktop_capture/desktop_geometry.h
+++ b/modules/desktop_capture/desktop_geometry.h
@@ -91,6 +91,10 @@
int32_t right, int32_t bottom) {
return DesktopRect(left, top, right, bottom);
}
+ static DesktopRect MakeOriginSize(const DesktopVector& origin,
+ const DesktopSize& size) {
+ return MakeXYWH(origin.x(), origin.y(), size.width(), size.height());
+ }
DesktopRect() : left_(0), top_(0), right_(0), bottom_(0) {}
@@ -114,6 +118,9 @@
// Returns true if |point| lies within the rectangle boundaries.
bool Contains(const DesktopVector& point) const;
+ // Returns true if |rect| lies within the boundaries of this rectangle.
+ bool ContainsRect(const DesktopRect& rect) const;
+
// Finds intersection with |rect|.
void IntersectWith(const DesktopRect& rect);
diff --git a/modules/desktop_capture/screen_capturer_win.cc b/modules/desktop_capture/screen_capturer_win.cc
index 42a7192..a9bcd48 100644
--- a/modules/desktop_capture/screen_capturer_win.cc
+++ b/modules/desktop_capture/screen_capturer_win.cc
@@ -337,10 +337,13 @@
scoped_ptr<MouseCursorShape> cursor(new MouseCursorShape);
cursor->hotspot = cursor_image->hotspot();
cursor->size = cursor_image->image().size();
- cursor->data.assign(
- cursor_image->image().data(),
- cursor_image->image().data() +
- cursor_image->image().stride() * DesktopFrame::kBytesPerPixel);
+ uint8_t* current_row = cursor_image->image().data();
+ for (int y = 0; y < cursor_image->image().size().height(); ++y) {
+ cursor->data.append(current_row,
+ current_row + cursor_image->image().size().width() *
+ DesktopFrame::kBytesPerPixel);
+ current_row += cursor_image->image().stride();
+ }
// Compare the current cursor with the last one we sent to the client. If
// they're the same, then don't bother sending the cursor again.
diff --git a/modules/desktop_capture/screen_capturer_x11.cc b/modules/desktop_capture/screen_capturer_x11.cc
index 97bdb65..c5a4c8c 100644
--- a/modules/desktop_capture/screen_capturer_x11.cc
+++ b/modules/desktop_capture/screen_capturer_x11.cc
@@ -462,14 +462,7 @@
DCHECK(current != last);
for (DesktopRegion::Iterator it(last_invalid_region_);
!it.IsAtEnd(); it.Advance()) {
- const DesktopRect& r = it.rect();
- int offset = r.top() * current->stride() +
- r.left() * DesktopFrame::kBytesPerPixel;
- for (int i = 0; i < r.height(); ++i) {
- memcpy(current->data() + offset, last->data() + offset,
- r.width() * DesktopFrame::kBytesPerPixel);
- offset += current->size().width() * DesktopFrame::kBytesPerPixel;
- }
+ current->CopyPixelsFrom(*last, it.rect().top_left(), it.rect());
}
}
diff --git a/modules/media_file/source/media_file.gypi b/modules/media_file/source/media_file.gypi
index 0d1b15f..3add36c 100644
--- a/modules/media_file/source/media_file.gypi
+++ b/modules/media_file/source/media_file.gypi
@@ -26,6 +26,9 @@
'../interface',
'../../interface',
],
+ 'defines': [
+ 'WEBRTC_MODULE_UTILITY_VIDEO',
+ ],
},
'sources': [
'../interface/media_file.h',
diff --git a/modules/media_file/source/media_file_utility.cc b/modules/media_file/source/media_file_utility.cc
index 04022ad..85df0b3 100644
--- a/modules/media_file/source/media_file_utility.cc
+++ b/modules/media_file/source/media_file_utility.cc
@@ -8,6 +8,8 @@
* be found in the AUTHORS file in the root of the source tree.
*/
+#include "webrtc/modules/media_file/source/media_file_utility.h"
+
#include <assert.h>
#include <sys/stat.h>
#include <sys/types.h>
@@ -15,7 +17,6 @@
#include "webrtc/common_types.h"
#include "webrtc/engine_configurations.h"
#include "webrtc/modules/interface/module_common_types.h"
-#include "webrtc/modules/media_file/source/media_file_utility.h"
#include "webrtc/system_wrappers/interface/file_wrapper.h"
#include "webrtc/system_wrappers/interface/trace.h"
diff --git a/modules/modules.gyp b/modules/modules.gyp
index 0888263..65c4827 100644
--- a/modules/modules.gyp
+++ b/modules/modules.gyp
@@ -184,6 +184,8 @@
'remote_bitrate_estimator/test/bwe_test_framework_unittest.cc',
'remote_bitrate_estimator/test/bwe_test_logging.cc',
'remote_bitrate_estimator/test/bwe_test_logging.h',
+ 'remote_bitrate_estimator/test/bwe_test.cc',
+ 'remote_bitrate_estimator/test/bwe_test.h',
'rtp_rtcp/source/mock/mock_rtp_payload_strategy.h',
'rtp_rtcp/source/fec_receiver_unittest.cc',
'rtp_rtcp/source/fec_test_helper.cc',
diff --git a/modules/pacing/include/paced_sender.h b/modules/pacing/include/paced_sender.h
index a0c1f1d..44e8144 100644
--- a/modules/pacing/include/paced_sender.h
+++ b/modules/pacing/include/paced_sender.h
@@ -52,6 +52,9 @@
protected:
virtual ~Callback() {}
};
+
+ static const int kDefaultMaxQueueLengthMs = 2000;
+
PacedSender(Callback* callback, int target_bitrate_kbps,
float pace_multiplier);
@@ -85,6 +88,10 @@
int bytes,
bool retransmission);
+ // Sets the max length of the pacer queue in milliseconds.
+ // A negative queue size is interpreted as infinite.
+ virtual void set_max_queue_length_ms(int max_queue_length_ms);
+
// Returns the time since the oldest queued packet was captured.
virtual int QueueInMs() const;
@@ -105,6 +112,8 @@
uint32_t* ssrc, uint16_t* sequence_number, int64_t* capture_time_ms,
bool* retransmission);
+ bool SendPacketFromList(paced_sender::PacketList* packet_list);
+
// Updates the number of bytes that can be sent for the next time interval.
void UpdateBytesPerInterval(uint32_t delta_time_in_ms);
@@ -115,6 +124,7 @@
const float pace_multiplier_;
bool enabled_;
bool paused_;
+ int max_queue_length_ms_;
scoped_ptr<CriticalSectionWrapper> critsect_;
// This is the media budget, keeping track of how many bits of media
// we can pace out during the current interval.
diff --git a/modules/pacing/paced_sender.cc b/modules/pacing/paced_sender.cc
index ed9d29a..7d4e81b 100644
--- a/modules/pacing/paced_sender.cc
+++ b/modules/pacing/paced_sender.cc
@@ -107,7 +107,7 @@
void UseBudget(int bytes) {
bytes_remaining_ = std::max(bytes_remaining_ - bytes,
- -100 * target_rate_kbps_ / 8);
+ -500 * target_rate_kbps_ / 8);
}
int bytes_remaining() const { return bytes_remaining_; }
@@ -124,6 +124,7 @@
pace_multiplier_(pace_multiplier),
enabled_(false),
paused_(false),
+ max_queue_length_ms_(kDefaultMaxQueueLengthMs),
critsect_(CriticalSectionWrapper::CreateCriticalSection()),
media_budget_(new paced_sender::IntervalBudget(
pace_multiplier_ * target_bitrate_kbps)),
@@ -206,6 +207,11 @@
return false;
}
+void PacedSender::set_max_queue_length_ms(int max_queue_length_ms) {
+ CriticalSectionScoped cs(critsect_.get());
+ max_queue_length_ms_ = max_queue_length_ms;
+}
+
int PacedSender::QueueInMs() const {
CriticalSectionScoped cs(critsect_.get());
int64_t now_ms = TickTime::MillisecondTimestamp();
@@ -254,36 +260,10 @@
uint32_t delta_time_ms = std::min(kMaxIntervalTimeMs, elapsed_time_ms);
UpdateBytesPerInterval(delta_time_ms);
}
- uint32_t ssrc;
- uint16_t sequence_number;
- int64_t capture_time_ms;
- bool retransmission;
paced_sender::PacketList* packet_list;
while (ShouldSendNextPacket(&packet_list)) {
- GetNextPacketFromList(packet_list, &ssrc, &sequence_number,
- &capture_time_ms, &retransmission);
- critsect_->Leave();
-
- const bool success = callback_->TimeToSendPacket(ssrc, sequence_number,
- capture_time_ms,
- retransmission);
- critsect_->Enter();
- // If packet cannot be sent then keep it in packet list and exit early.
- // There's no need to send more packets.
- if (!success) {
+ if (!SendPacketFromList(packet_list))
return 0;
- }
- packet_list->pop_front();
- const bool last_packet = packet_list->empty() ||
- packet_list->front().capture_time_ms_ > capture_time_ms;
- if (packet_list != high_priority_packets_.get()) {
- if (capture_time_ms > capture_time_ms_last_sent_) {
- capture_time_ms_last_sent_ = capture_time_ms;
- } else if (capture_time_ms == capture_time_ms_last_sent_ &&
- last_packet) {
- TRACE_EVENT_ASYNC_END0("webrtc_rtp", "PacedSend", capture_time_ms);
- }
- }
}
if (high_priority_packets_->empty() &&
normal_priority_packets_->empty() &&
@@ -305,6 +285,39 @@
}
// MUST have critsect_ when calling.
+bool PacedSender::SendPacketFromList(paced_sender::PacketList* packet_list) {
+ uint32_t ssrc;
+ uint16_t sequence_number;
+ int64_t capture_time_ms;
+ bool retransmission;
+ GetNextPacketFromList(packet_list, &ssrc, &sequence_number,
+ &capture_time_ms, &retransmission);
+ critsect_->Leave();
+
+ const bool success = callback_->TimeToSendPacket(ssrc, sequence_number,
+ capture_time_ms,
+ retransmission);
+ critsect_->Enter();
+ // If packet cannot be sent then keep it in packet list and exit early.
+ // There's no need to send more packets.
+ if (!success) {
+ return false;
+ }
+ packet_list->pop_front();
+ const bool last_packet = packet_list->empty() ||
+ packet_list->front().capture_time_ms_ > capture_time_ms;
+ if (packet_list != high_priority_packets_.get()) {
+ if (capture_time_ms > capture_time_ms_last_sent_) {
+ capture_time_ms_last_sent_ = capture_time_ms;
+ } else if (capture_time_ms == capture_time_ms_last_sent_ &&
+ last_packet) {
+ TRACE_EVENT_ASYNC_END0("webrtc_rtp", "PacedSend", capture_time_ms);
+ }
+ }
+ return true;
+}
+
+// MUST have critsect_ when calling.
void PacedSender::UpdateBytesPerInterval(uint32_t delta_time_ms) {
media_budget_->IncreaseBudget(delta_time_ms);
padding_budget_->IncreaseBudget(delta_time_ms);
@@ -313,6 +326,7 @@
// MUST have critsect_ when calling.
bool PacedSender::ShouldSendNextPacket(paced_sender::PacketList** packet_list) {
+ *packet_list = NULL;
if (media_budget_->bytes_remaining() <= 0) {
// All bytes consumed for this interval.
// Check if we have not sent in a too long time.
@@ -327,6 +341,22 @@
return true;
}
}
+ // Send any old packets to avoid queuing for too long.
+ if (max_queue_length_ms_ >= 0 && QueueInMs() > max_queue_length_ms_) {
+ int64_t high_priority_capture_time = -1;
+ if (!high_priority_packets_->empty()) {
+ high_priority_capture_time =
+ high_priority_packets_->front().capture_time_ms_;
+ *packet_list = high_priority_packets_.get();
+ }
+ if (!normal_priority_packets_->empty() &&
+ (high_priority_capture_time == -1 || high_priority_capture_time >
+ normal_priority_packets_->front().capture_time_ms_)) {
+ *packet_list = normal_priority_packets_.get();
+ }
+ if (*packet_list)
+ return true;
+ }
return false;
}
if (!high_priority_packets_->empty()) {
diff --git a/modules/pacing/paced_sender_unittest.cc b/modules/pacing/paced_sender_unittest.cc
index 66a3383..655c03d 100644
--- a/modules/pacing/paced_sender_unittest.cc
+++ b/modules/pacing/paced_sender_unittest.cc
@@ -83,53 +83,50 @@
TEST_F(PacedSenderTest, QueuePacket) {
uint32_t ssrc = 12345;
uint16_t sequence_number = 1234;
- int64_t capture_time_ms = 56789;
// Due to the multiplicative factor we can send 3 packets not 2 packets.
SendAndExpectPacket(PacedSender::kNormalPriority, ssrc, sequence_number++,
- capture_time_ms, 250, false);
+ TickTime::MillisecondTimestamp(), 250, false);
SendAndExpectPacket(PacedSender::kNormalPriority, ssrc, sequence_number++,
- capture_time_ms, 250, false);
+ TickTime::MillisecondTimestamp(), 250, false);
SendAndExpectPacket(PacedSender::kNormalPriority, ssrc, sequence_number++,
- capture_time_ms, 250, false);
+ TickTime::MillisecondTimestamp(), 250, false);
+ int64_t queued_packet_timestamp = TickTime::MillisecondTimestamp();
EXPECT_FALSE(send_bucket_->SendPacket(PacedSender::kNormalPriority, ssrc,
- sequence_number, capture_time_ms, 250, false));
+ sequence_number, queued_packet_timestamp, 250, false));
send_bucket_->Process();
EXPECT_EQ(5, send_bucket_->TimeUntilNextProcess());
EXPECT_CALL(callback_, TimeToSendPadding(_)).Times(0);
- EXPECT_CALL(callback_,
- TimeToSendPacket(ssrc, sequence_number, capture_time_ms, false)).Times(0);
TickTime::AdvanceFakeClock(4);
EXPECT_EQ(1, send_bucket_->TimeUntilNextProcess());
TickTime::AdvanceFakeClock(1);
EXPECT_EQ(0, send_bucket_->TimeUntilNextProcess());
EXPECT_CALL(callback_, TimeToSendPacket(
- ssrc, sequence_number++, capture_time_ms, false))
+ ssrc, sequence_number++, queued_packet_timestamp, false))
.Times(1)
.WillRepeatedly(Return(true));
send_bucket_->Process();
sequence_number++;
SendAndExpectPacket(PacedSender::kNormalPriority, ssrc, sequence_number++,
- capture_time_ms, 250, false);
+ TickTime::MillisecondTimestamp(), 250, false);
SendAndExpectPacket(PacedSender::kNormalPriority, ssrc, sequence_number++,
- capture_time_ms, 250, false);
+ TickTime::MillisecondTimestamp(), 250, false);
EXPECT_FALSE(send_bucket_->SendPacket(PacedSender::kNormalPriority, ssrc,
- sequence_number++, capture_time_ms, 250, false));
+ sequence_number++, TickTime::MillisecondTimestamp(), 250, false));
send_bucket_->Process();
}
TEST_F(PacedSenderTest, PaceQueuedPackets) {
uint32_t ssrc = 12345;
uint16_t sequence_number = 1234;
- int64_t capture_time_ms = 56789;
// Due to the multiplicative factor we can send 3 packets not 2 packets.
for (int i = 0; i < 3; ++i) {
SendAndExpectPacket(PacedSender::kNormalPriority, ssrc, sequence_number++,
- capture_time_ms, 250, false);
+ TickTime::MillisecondTimestamp(), 250, false);
}
for (int j = 0; j < 30; ++j) {
EXPECT_FALSE(send_bucket_->SendPacket(PacedSender::kNormalPriority, ssrc,
- sequence_number++, capture_time_ms, 250, false));
+ sequence_number++, TickTime::MillisecondTimestamp(), 250, false));
}
send_bucket_->Process();
EXPECT_CALL(callback_, TimeToSendPadding(_)).Times(0);
@@ -137,7 +134,7 @@
EXPECT_EQ(5, send_bucket_->TimeUntilNextProcess());
TickTime::AdvanceFakeClock(5);
EXPECT_CALL(callback_,
- TimeToSendPacket(ssrc, _, capture_time_ms, false))
+ TimeToSendPacket(ssrc, _, _, false))
.Times(3)
.WillRepeatedly(Return(true));
EXPECT_EQ(0, send_bucket_->TimeUntilNextProcess());
@@ -148,35 +145,34 @@
EXPECT_EQ(0, send_bucket_->TimeUntilNextProcess());
EXPECT_EQ(0, send_bucket_->Process());
SendAndExpectPacket(PacedSender::kNormalPriority, ssrc, sequence_number++,
- capture_time_ms, 250, false);
+ TickTime::MillisecondTimestamp(), 250, false);
SendAndExpectPacket(PacedSender::kNormalPriority, ssrc, sequence_number++,
- capture_time_ms, 250, false);
+ TickTime::MillisecondTimestamp(), 250, false);
SendAndExpectPacket(PacedSender::kNormalPriority, ssrc, sequence_number++,
- capture_time_ms, 250, false);
+ TickTime::MillisecondTimestamp(), 250, false);
EXPECT_FALSE(send_bucket_->SendPacket(PacedSender::kNormalPriority, ssrc,
- sequence_number, capture_time_ms, 250, false));
+ sequence_number, TickTime::MillisecondTimestamp(), 250, false));
send_bucket_->Process();
}
TEST_F(PacedSenderTest, PaceQueuedPacketsWithDuplicates) {
uint32_t ssrc = 12345;
uint16_t sequence_number = 1234;
- int64_t capture_time_ms = 56789;
uint16_t queued_sequence_number;
// Due to the multiplicative factor we can send 3 packets not 2 packets.
for (int i = 0; i < 3; ++i) {
SendAndExpectPacket(PacedSender::kNormalPriority, ssrc, sequence_number++,
- capture_time_ms, 250, false);
+ TickTime::MillisecondTimestamp(), 250, false);
}
queued_sequence_number = sequence_number;
for (int j = 0; j < 30; ++j) {
// Send in duplicate packets.
EXPECT_FALSE(send_bucket_->SendPacket(PacedSender::kNormalPriority, ssrc,
- sequence_number, capture_time_ms, 250, false));
+ sequence_number, TickTime::MillisecondTimestamp(), 250, false));
EXPECT_FALSE(send_bucket_->SendPacket(PacedSender::kNormalPriority, ssrc,
- sequence_number++, capture_time_ms, 250, false));
+ sequence_number++, TickTime::MillisecondTimestamp(), 250, false));
}
EXPECT_CALL(callback_, TimeToSendPadding(_)).Times(0);
send_bucket_->Process();
@@ -186,7 +182,8 @@
for (int i = 0; i < 3; ++i) {
EXPECT_CALL(callback_, TimeToSendPacket(ssrc, queued_sequence_number++,
- capture_time_ms, false))
+ _,
+ false))
.Times(1)
.WillRepeatedly(Return(true));
}
@@ -198,29 +195,28 @@
EXPECT_EQ(0, send_bucket_->TimeUntilNextProcess());
EXPECT_EQ(0, send_bucket_->Process());
SendAndExpectPacket(PacedSender::kNormalPriority, ssrc, sequence_number++,
- capture_time_ms, 250, false);
+ TickTime::MillisecondTimestamp(), 250, false);
SendAndExpectPacket(PacedSender::kNormalPriority, ssrc, sequence_number++,
- capture_time_ms, 250, false);
+ TickTime::MillisecondTimestamp(), 250, false);
SendAndExpectPacket(PacedSender::kNormalPriority, ssrc, sequence_number++,
- capture_time_ms, 250, false);
+ TickTime::MillisecondTimestamp(), 250, false);
EXPECT_FALSE(send_bucket_->SendPacket(PacedSender::kNormalPriority, ssrc,
- sequence_number++, capture_time_ms, 250, false));
+ sequence_number++, TickTime::MillisecondTimestamp(), 250, false));
send_bucket_->Process();
}
TEST_F(PacedSenderTest, Padding) {
uint32_t ssrc = 12345;
uint16_t sequence_number = 1234;
- int64_t capture_time_ms = 56789;
send_bucket_->UpdateBitrate(kTargetBitrate, kTargetBitrate, kTargetBitrate);
// Due to the multiplicative factor we can send 3 packets not 2 packets.
SendAndExpectPacket(PacedSender::kNormalPriority, ssrc, sequence_number++,
- capture_time_ms, 250, false);
+ TickTime::MillisecondTimestamp(), 250, false);
SendAndExpectPacket(PacedSender::kNormalPriority, ssrc, sequence_number++,
- capture_time_ms, 250, false);
+ TickTime::MillisecondTimestamp(), 250, false);
SendAndExpectPacket(PacedSender::kNormalPriority, ssrc, sequence_number++,
- capture_time_ms, 250, false);
+ TickTime::MillisecondTimestamp(), 250, false);
// No padding is expected since we have sent too much already.
EXPECT_CALL(callback_, TimeToSendPadding(_)).Times(0);
EXPECT_EQ(5, send_bucket_->TimeUntilNextProcess());
@@ -492,5 +488,33 @@
EXPECT_EQ(0, send_bucket_->QueueInMs());
}
+TEST_F(PacedSenderTest, MaxQueueLength) {
+ uint32_t ssrc = 12346;
+ uint16_t sequence_number = 1234;
+ EXPECT_EQ(0, send_bucket_->QueueInMs());
+
+ send_bucket_->UpdateBitrate(30, 0, 0);
+ for (int i = 0; i < 30; ++i) {
+ SendAndExpectPacket(PacedSender::kNormalPriority,
+ ssrc,
+ sequence_number++,
+ TickTime::MillisecondTimestamp(),
+ 1200,
+ false);
+ }
+
+ TickTime::AdvanceFakeClock(2001);
+ SendAndExpectPacket(PacedSender::kNormalPriority,
+ ssrc,
+ sequence_number++,
+ TickTime::MillisecondTimestamp(),
+ 1200,
+ false);
+ EXPECT_EQ(2001, send_bucket_->QueueInMs());
+ send_bucket_->Process();
+ EXPECT_EQ(0, send_bucket_->QueueInMs());
+ TickTime::AdvanceFakeClock(31);
+ send_bucket_->Process();
+}
} // namespace test
} // namespace webrtc
diff --git a/modules/remote_bitrate_estimator/bitrate_estimator.cc b/modules/remote_bitrate_estimator/bitrate_estimator.cc
index 316297f..8934d43 100644
--- a/modules/remote_bitrate_estimator/bitrate_estimator.cc
+++ b/modules/remote_bitrate_estimator/bitrate_estimator.cc
@@ -12,52 +12,73 @@
namespace webrtc {
-const float kBitrateAverageWindowMs = 500.0f;
+const int kBitrateAverageWindowMs = 500;
BitRateStats::BitRateStats()
- : data_samples_(),
- accumulated_bytes_(0) {
+ : num_buckets_(kBitrateAverageWindowMs + 1), // N ms in (N+1) buckets.
+ buckets_(new uint32_t[num_buckets_]()),
+ accumulated_bytes_(0),
+ oldest_time_(0),
+ oldest_index_(0),
+ bps_coefficient_(8.f * 1000.f / (num_buckets_ - 1)) {
}
BitRateStats::~BitRateStats() {
- Init();
}
void BitRateStats::Init() {
accumulated_bytes_ = 0;
- while (data_samples_.size() > 0) {
- delete data_samples_.front();
- data_samples_.pop_front();
+ oldest_time_ = 0;
+ oldest_index_ = 0;
+ for (int i = 0; i < num_buckets_; i++) {
+ buckets_[i] = 0;
}
}
void BitRateStats::Update(uint32_t packet_size_bytes, int64_t now_ms) {
- // Find an empty slot for storing the new sample and at the same time
- // accumulate the history.
- data_samples_.push_back(new DataTimeSizeTuple(packet_size_bytes, now_ms));
- accumulated_bytes_ += packet_size_bytes;
- EraseOld(now_ms);
-}
-
-void BitRateStats::EraseOld(int64_t now_ms) {
- while (data_samples_.size() > 0) {
- if (now_ms - data_samples_.front()->time_complete_ms >
- kBitrateAverageWindowMs) {
- // Delete old sample
- accumulated_bytes_ -= data_samples_.front()->size_bytes;
- delete data_samples_.front();
- data_samples_.pop_front();
- } else {
- break;
- }
+ if (now_ms < oldest_time_) {
+ // Too old data is ignored.
+ return;
}
+
+ EraseOld(now_ms);
+
+ int now_offset = static_cast<int>(now_ms - oldest_time_);
+ assert(now_offset < num_buckets_);
+ int index = oldest_index_ + now_offset;
+ if (index >= num_buckets_) {
+ index -= num_buckets_;
+ }
+ buckets_[index] += packet_size_bytes;
+ accumulated_bytes_ += packet_size_bytes;
}
uint32_t BitRateStats::BitRate(int64_t now_ms) {
- // Calculate the average bit rate the past BITRATE_AVERAGE_WINDOW ms.
- // Removes any old samples from the list.
EraseOld(now_ms);
- return static_cast<uint32_t>(accumulated_bytes_ * 8.0f * 1000.0f /
- kBitrateAverageWindowMs + 0.5f);
+ return static_cast<uint32_t>(accumulated_bytes_ * bps_coefficient_ + 0.5f);
+}
+
+void BitRateStats::EraseOld(int64_t now_ms) {
+ int64_t new_oldest_time = now_ms - num_buckets_ + 1;
+ if (new_oldest_time <= oldest_time_) {
+ return;
+ }
+
+ while (oldest_time_ < new_oldest_time) {
+ uint32_t num_bytes_in_oldest_bucket = buckets_[oldest_index_];
+ assert(accumulated_bytes_ >= num_bytes_in_oldest_bucket);
+ accumulated_bytes_ -= num_bytes_in_oldest_bucket;
+ buckets_[oldest_index_] = 0;
+ if (++oldest_index_ >= num_buckets_) {
+ oldest_index_ = 0;
+ }
+ ++oldest_time_;
+ if (accumulated_bytes_ == 0) {
+ // This guarantees we go through all the buckets at most once, even if
+ // |new_oldest_time| is far greater than |oldest_time_|.
+ break;
+ }
+ }
+ oldest_time_ = new_oldest_time;
}
} // namespace webrtc
diff --git a/modules/remote_bitrate_estimator/bitrate_estimator.h b/modules/remote_bitrate_estimator/bitrate_estimator.h
index 3d5d51b..9ee31eb 100644
--- a/modules/remote_bitrate_estimator/bitrate_estimator.h
+++ b/modules/remote_bitrate_estimator/bitrate_estimator.h
@@ -11,8 +11,7 @@
#ifndef WEBRTC_MODULES_REMOTE_BITRATE_ESTIMATOR_BITRATE_ESTIMATOR_H_
#define WEBRTC_MODULES_REMOTE_BITRATE_ESTIMATOR_BITRATE_ESTIMATOR_H_
-#include <list>
-
+#include "webrtc/system_wrappers/interface/scoped_ptr.h"
#include "webrtc/typedefs.h"
namespace webrtc {
@@ -27,20 +26,24 @@
uint32_t BitRate(int64_t now_ms);
private:
- struct DataTimeSizeTuple {
- DataTimeSizeTuple(uint32_t size_bytes_in, int64_t time_complete_ms_in)
- : size_bytes(size_bytes_in),
- time_complete_ms(time_complete_ms_in) {
- }
-
- uint32_t size_bytes;
- int64_t time_complete_ms;
- };
-
void EraseOld(int64_t now_ms);
- std::list<DataTimeSizeTuple*> data_samples_;
+ // Numbers of bytes are kept in buckets (circular buffer), with one bucket
+ // per millisecond.
+ const int num_buckets_;
+ scoped_array<uint32_t> buckets_;
+
+ // Total number of bytes recorded in buckets.
uint32_t accumulated_bytes_;
+
+ // Oldest time recorded in buckets.
+ int64_t oldest_time_;
+
+ // Bucket index of oldest bytes recorded in buckets.
+ int oldest_index_;
+
+ // To convert number of bytes in bits/second.
+ const float bps_coefficient_;
};
} // namespace webrtc
diff --git a/modules/remote_bitrate_estimator/bitrate_estimator_unittest.cc b/modules/remote_bitrate_estimator/bitrate_estimator_unittest.cc
index ca9da99..3d49ca4 100644
--- a/modules/remote_bitrate_estimator/bitrate_estimator_unittest.cc
+++ b/modules/remote_bitrate_estimator/bitrate_estimator_unittest.cc
@@ -17,7 +17,7 @@
class BitRateStatsTest : public ::testing::Test {
protected:
- BitRateStatsTest() {};
+ BitRateStatsTest() {}
BitRateStats stats_;
};
@@ -47,4 +47,51 @@
// the estimate should be 0.
EXPECT_EQ(0u, stats_.BitRate(now_ms));
}
+
+TEST_F(BitRateStatsTest, IncreasingThenDecreasingBitrate) {
+ int64_t now_ms = 0;
+ stats_.Init();
+ // Expecting 0 after init.
+ uint32_t bitrate = stats_.BitRate(now_ms);
+ EXPECT_EQ(0u, bitrate);
+ // 1000 bytes per millisecond until plateau is reached.
+ while (++now_ms < 10000) {
+ stats_.Update(1000, now_ms);
+ uint32_t new_bitrate = stats_.BitRate(now_ms);
+ if (new_bitrate != bitrate) {
+ // New bitrate must be higher than previous one.
+ EXPECT_GT(new_bitrate, bitrate);
+ } else {
+ // Plateau reached, 8000 kbps expected.
+ EXPECT_NEAR(8000000u, bitrate, 80000u);
+ break;
+ }
+ bitrate = new_bitrate;
+ }
+ // 1000 bytes per millisecond until 10-second mark, 8000 kbps expected.
+ while (++now_ms < 10000) {
+ stats_.Update(1000, now_ms);
+ bitrate = stats_.BitRate(now_ms);
+ EXPECT_NEAR(8000000u, bitrate, 80000u);
+ }
+ // Zero bytes per millisecond until 0 is reached.
+ while (++now_ms < 20000) {
+ stats_.Update(0, now_ms);
+ uint32_t new_bitrate = stats_.BitRate(now_ms);
+ if (new_bitrate != bitrate) {
+ // New bitrate must be lower than previous one.
+ EXPECT_LT(new_bitrate, bitrate);
+ } else {
+ // 0 kbps expected.
+ EXPECT_EQ(0u, bitrate);
+ break;
+ }
+ bitrate = new_bitrate;
+ }
+ // Zero bytes per millisecond until 20-second mark, 0 kbps expected.
+ while (++now_ms < 20000) {
+ stats_.Update(0, now_ms);
+ EXPECT_EQ(0u, stats_.BitRate(now_ms));
+ }
+}
} // namespace
diff --git a/modules/remote_bitrate_estimator/remote_bitrate_estimators_test.cc b/modules/remote_bitrate_estimator/remote_bitrate_estimators_test.cc
index deeeb90..ed8e5c5 100644
--- a/modules/remote_bitrate_estimator/remote_bitrate_estimators_test.cc
+++ b/modules/remote_bitrate_estimator/remote_bitrate_estimators_test.cc
@@ -8,444 +8,225 @@
* be found in the AUTHORS file in the root of the source tree.
*/
-#include "gtest/gtest.h"
-#include "webrtc/modules/remote_bitrate_estimator/test/bwe_test_framework.h"
#include "webrtc/modules/remote_bitrate_estimator/include/remote_bitrate_estimator.h"
-#include "webrtc/system_wrappers/interface/clock.h"
-#include "webrtc/system_wrappers/interface/constructor_magic.h"
-#include "webrtc/system_wrappers/interface/scoped_ptr.h"
-
-#define ENABLE_1_SENDER 1
-#define ENABLE_3_SENDERS 1
-#define ENABLE_10_SENDERS 1
-#define ENABLE_BASIC_TESTS 1
-#define ENABLE_LOSS_TESTS 0
-#define ENABLE_DELAY_TESTS 0
-#define ENABLE_JITTER_TESTS 0
-#define ENABLE_REORDER_TESTS 0
-#define ENABLE_CHOKE_TESTS 0
-#define ENABLE_MULTI_TESTS 0
-
-#define ENABLE_TOF_ESTIMATOR 1
-#define ENABLE_AST_ESTIMATOR 1
-
-using std::vector;
+#include "webrtc/modules/remote_bitrate_estimator/test/bwe_test.h"
namespace webrtc {
namespace testing {
namespace bwe {
-const int64_t kSimulationIntervalMs = 1000;
+std::vector<const PacketSenderFactory*> VideoSenderFactories(uint32_t count) {
+ class VideoPacketSenderFactory : public PacketSenderFactory {
+ public:
+ VideoPacketSenderFactory(float fps, uint32_t kbps, uint32_t ssrc,
+ float frame_offset)
+ : fps_(fps),
+ kbps_(kbps),
+ ssrc_(ssrc),
+ frame_offset_(frame_offset) {
+ }
+ virtual ~VideoPacketSenderFactory() {}
+ virtual PacketSender* Create() const {
+ return new VideoSender(NULL, fps_, kbps_, ssrc_, frame_offset_);
+ }
+ private:
+ float fps_;
+ uint32_t kbps_;
+ uint32_t ssrc_;
+ float frame_offset_;
+ };
-namespace stl_helpers {
-template<typename T> void DeleteElements(T* container) {
- if (!container) return;
- for (typename T::iterator it = container->begin(); it != container->end();
- ++it) {
- delete *it;
+ static const VideoPacketSenderFactory factories[] = {
+ VideoPacketSenderFactory(30.00f, 150, 0x1234, 0.13f),
+ VideoPacketSenderFactory(15.00f, 500, 0x2345, 0.16f),
+ VideoPacketSenderFactory(30.00f, 1200, 0x3456, 0.26f),
+ VideoPacketSenderFactory(7.49f, 150, 0x4567, 0.05f),
+ VideoPacketSenderFactory(7.50f, 150, 0x5678, 0.15f),
+ VideoPacketSenderFactory(7.51f, 150, 0x6789, 0.25f),
+ VideoPacketSenderFactory(15.02f, 150, 0x7890, 0.27f),
+ VideoPacketSenderFactory(15.03f, 150, 0x8901, 0.38f),
+ VideoPacketSenderFactory(30.02f, 150, 0x9012, 0.39f),
+ VideoPacketSenderFactory(30.03f, 150, 0x0123, 0.52f)
+ };
+ assert(count <= sizeof(factories) / sizeof(factories[0]));
+
+ std::vector<const PacketSenderFactory*> result;
+ for (uint32_t i = 0; i < count; ++i) {
+ result.push_back(&factories[i]);
}
- container->clear();
+ return result;
}
-} // namespace stl_helpers
-class TestedEstimator : public RemoteBitrateObserver {
- public:
- TestedEstimator(const std::string& debug_name,
- const RemoteBitrateEstimatorFactory& factory)
- : debug_name_(debug_name),
- clock_(0),
- stats_(),
- relative_estimator_stats_(),
- latest_estimate_kbps_(-1.0),
- estimator_(factory.Create(this, &clock_)),
- relative_estimator_(NULL) {
- assert(estimator_.get());
- // Default RTT in RemoteRateControl is 200 ms ; 50 ms is more realistic.
- estimator_->OnRttUpdate(50);
+std::vector<BweTestConfig::EstimatorConfig> EstimatorConfigs() {
+ static const RemoteBitrateEstimatorFactory factories[] = {
+ RemoteBitrateEstimatorFactory(),
+ AbsoluteSendTimeRemoteBitrateEstimatorFactory()
+ };
+
+ std::vector<BweTestConfig::EstimatorConfig> result;
+ result.push_back(BweTestConfig::EstimatorConfig("TOF", &factories[0]));
+ result.push_back(BweTestConfig::EstimatorConfig("AST", &factories[1]));
+ return result;
+}
+
+BweTestConfig MakeBweTestConfig(uint32_t sender_count) {
+ BweTestConfig result = {
+ VideoSenderFactories(sender_count), EstimatorConfigs()
+ };
+ return result;
+}
+
+INSTANTIATE_TEST_CASE_P(VideoSendersTest, BweTest,
+ ::testing::Values(MakeBweTestConfig(1),
+ MakeBweTestConfig(3)));
+
+TEST_P(BweTest, UnlimitedSpeed) {
+ VerboseLogging(false);
+ RunFor(10 * 60 * 1000);
+}
+
+TEST_P(BweTest, SteadyLoss) {
+ LossFilter loss(this);
+ loss.SetLoss(20.0);
+ RunFor(10 * 60 * 1000);
+}
+
+TEST_P(BweTest, IncreasingLoss1) {
+ LossFilter loss(this);
+ for (int i = 0; i < 76; ++i) {
+ loss.SetLoss(i);
+ RunFor(5000);
}
+}
- void SetRelativeEstimator(TestedEstimator* relative_estimator) {
- relative_estimator_ = relative_estimator;
+TEST_P(BweTest, SteadyDelay) {
+ DelayFilter delay(this);
+ delay.SetDelay(1000);
+ RunFor(10 * 60 * 1000);
+}
+
+TEST_P(BweTest, IncreasingDelay1) {
+ DelayFilter delay(this);
+ RunFor(10 * 60 * 1000);
+ for (int i = 0; i < 30 * 2; ++i) {
+ delay.SetDelay(i);
+ RunFor(10 * 1000);
}
+ RunFor(10 * 60 * 1000);
+}
- void EatPacket(const BwePacket& packet) {
- BWE_TEST_LOGGING_CONTEXT(debug_name_);
-
- latest_estimate_kbps_ = -1.0;
-
- // We're treating the send time (from previous filter) as the arrival
- // time once packet reaches the estimator.
- int64_t packet_time_ms = (packet.send_time_us() + 500) / 1000;
- BWE_TEST_LOGGING_TIME(packet_time_ms);
-
- int64_t step_ms = estimator_->TimeUntilNextProcess();
- while ((clock_.TimeInMilliseconds() + step_ms) < packet_time_ms) {
- clock_.AdvanceTimeMilliseconds(step_ms);
- estimator_->Process();
- step_ms = estimator_->TimeUntilNextProcess();
- }
-
- estimator_->IncomingPacket(packet_time_ms, packet.payload_size(),
- packet.header());
- clock_.AdvanceTimeMilliseconds(packet_time_ms -
- clock_.TimeInMilliseconds());
- ASSERT_TRUE(packet_time_ms == clock_.TimeInMilliseconds());
+TEST_P(BweTest, IncreasingDelay2) {
+ DelayFilter delay(this);
+ RateCounterFilter counter(this);
+ RunFor(1 * 60 * 1000);
+ for (int i = 1; i < 51; ++i) {
+ delay.SetDelay(10.0f * i);
+ RunFor(10 * 1000);
}
+ delay.SetDelay(0.0f);
+ RunFor(10 * 60 * 1000);
+}
- void CheckEstimate() {
- BWE_TEST_LOGGING_CONTEXT(debug_name_);
- double estimated_kbps = 0.0;
- if (LatestEstimate(&estimated_kbps)) {
- stats_.Push(estimated_kbps);
- BWE_TEST_LOGGING_PLOT("Estimate", clock_.TimeInMilliseconds(),
- estimated_kbps / 100);
- double relative_estimate_kbps = 0.0;
- if (relative_estimator_ &&
- relative_estimator_->LatestEstimate(&relative_estimate_kbps)) {
- relative_estimator_stats_.Push(estimated_kbps - relative_estimate_kbps);
- }
- }
+TEST_P(BweTest, JumpyDelay1) {
+ DelayFilter delay(this);
+ RunFor(10 * 60 * 1000);
+ for (int i = 1; i < 200; ++i) {
+ delay.SetDelay((10 * i) % 500);
+ RunFor(1000);
+ delay.SetDelay(1.0f);
+ RunFor(1000);
}
+ delay.SetDelay(0.0f);
+ RunFor(10 * 60 * 1000);
+}
- void LogStats() {
- BWE_TEST_LOGGING_CONTEXT(debug_name_);
- BWE_TEST_LOGGING_CONTEXT("Mean");
- stats_.Log("kbps");
- if (relative_estimator_) {
- BWE_TEST_LOGGING_CONTEXT("Diff");
- relative_estimator_stats_.Log("kbps");
- }
+TEST_P(BweTest, SteadyJitter) {
+ JitterFilter jitter(this);
+ RateCounterFilter counter(this);
+ jitter.SetJitter(20);
+ RunFor(2 * 60 * 1000);
+}
+
+TEST_P(BweTest, IncreasingJitter1) {
+ JitterFilter jitter(this);
+ for (int i = 0; i < 2 * 60 * 2; ++i) {
+ jitter.SetJitter(i);
+ RunFor(10 * 1000);
}
+ RunFor(10 * 60 * 1000);
+}
- virtual void OnReceiveBitrateChanged(const vector<unsigned int>& ssrcs,
- unsigned int bitrate) {
+TEST_P(BweTest, IncreasingJitter2) {
+ JitterFilter jitter(this);
+ RunFor(30 * 1000);
+ for (int i = 1; i < 51; ++i) {
+ jitter.SetJitter(10.0f * i);
+ RunFor(10 * 1000);
}
+ jitter.SetJitter(0.0f);
+ RunFor(10 * 60 * 1000);
+}
- private:
- bool LatestEstimate(double* estimate_kbps) {
- if (latest_estimate_kbps_ < 0.0) {
- vector<unsigned int> ssrcs;
- unsigned int bps = 0;
- if (!estimator_->LatestEstimate(&ssrcs, &bps)) {
- return false;
- }
- latest_estimate_kbps_ = bps / 1000.0;
- }
- *estimate_kbps = latest_estimate_kbps_;
- return true;
+TEST_P(BweTest, SteadyReorder) {
+ ReorderFilter reorder(this);
+ reorder.SetReorder(20.0);
+ RunFor(10 * 60 * 1000);
+}
+
+TEST_P(BweTest, IncreasingReorder1) {
+ ReorderFilter reorder(this);
+ for (int i = 0; i < 76; ++i) {
+ reorder.SetReorder(i);
+ RunFor(5000);
}
+}
- std::string debug_name_;
- bool log_estimates_;
- SimulatedClock clock_;
- Stats<double> stats_;
- Stats<double> relative_estimator_stats_;
- double latest_estimate_kbps_;
- scoped_ptr<RemoteBitrateEstimator> estimator_;
- TestedEstimator* relative_estimator_;
+TEST_P(BweTest, SteadyChoke) {
+ ChokeFilter choke(this);
+ choke.SetCapacity(140);
+ RunFor(10 * 60 * 1000);
+}
- DISALLOW_IMPLICIT_CONSTRUCTORS(TestedEstimator);
-};
-
-class RemoteBitrateEstimatorsTest : public ::testing::Test {
- public:
- RemoteBitrateEstimatorsTest()
- : run_time_ms_(0),
- estimators_(),
- previous_packets_(),
- processors_(),
- video_senders_() {
+TEST_P(BweTest, IncreasingChoke1) {
+ ChokeFilter choke(this);
+ for (int i = 1200; i >= 100; i -= 100) {
+ choke.SetCapacity(i);
+ RunFor(5000);
}
- virtual ~RemoteBitrateEstimatorsTest() {
- stl_helpers::DeleteElements(&estimators_);
- stl_helpers::DeleteElements(&video_senders_);
- }
+}
- virtual void SetUp() {
-#if ENABLE_TOF_ESTIMATOR
- estimators_.push_back(new TestedEstimator("TOF",
- RemoteBitrateEstimatorFactory()));
-#endif
-#if ENABLE_AST_ESTIMATOR
- estimators_.push_back(new TestedEstimator("AST",
- AbsoluteSendTimeRemoteBitrateEstimatorFactory()));
-#endif
- // Set all estimators as relative to the first one.
- for (uint32_t i = 1; i < estimators_.size(); ++i) {
- estimators_[i]->SetRelativeEstimator(estimators_[0]);
- }
+TEST_P(BweTest, IncreasingChoke2) {
+ ChokeFilter choke(this);
+ RunFor(60 * 1000);
+ for (int i = 1200; i >= 100; i -= 20) {
+ choke.SetCapacity(i);
+ RunFor(1000);
}
+}
- protected:
- void RunFor(int64_t time_ms) {
- for (run_time_ms_ += time_ms; run_time_ms_ >= kSimulationIntervalMs;
- run_time_ms_ -= kSimulationIntervalMs) {
- Packets packets;
- for (vector<PacketProcessorInterface*>::const_iterator it =
- processors_.begin(); it != processors_.end(); ++it) {
- (*it)->RunFor(kSimulationIntervalMs, &packets);
- }
+TEST_P(BweTest, Multi1) {
+ DelayFilter delay(this);
+ ChokeFilter choke(this);
+ RateCounterFilter counter(this);
+ choke.SetCapacity(1000);
+ RunFor(1 * 60 * 1000);
+ for (int i = 1; i < 51; ++i) {
+ delay.SetDelay(100.0f * i);
+ RunFor(10 * 1000);
+ }
+ RunFor(500 * 1000);
+ delay.SetDelay(0.0f);
+ RunFor(5 * 60 * 1000);
+}
- // Verify packets are in order between batches.
- if (!packets.empty() && !previous_packets_.empty()) {
- packets.splice(packets.begin(), previous_packets_,
- --previous_packets_.end());
- ASSERT_TRUE(IsTimeSorted(packets));
- packets.erase(packets.begin());
- } else {
- ASSERT_TRUE(IsTimeSorted(packets));
- }
-
- for (PacketsConstIt pit = packets.begin(); pit != packets.end(); ++pit) {
- for (vector<TestedEstimator*>::iterator eit = estimators_.begin();
- eit != estimators_.end(); ++eit) {
- (*eit)->EatPacket(*pit);
- }
- }
-
- previous_packets_.swap(packets);
-
- for (vector<TestedEstimator*>::iterator eit = estimators_.begin();
- eit != estimators_.end(); ++eit) {
- (*eit)->CheckEstimate();
- }
- }
- }
-
- void AddVideoSenders(uint32_t count) {
- struct { float fps; uint32_t kbps; uint32_t ssrc; float frame_offset; }
- configs[] = {
- { 30.00f, 150, 0x1234, 0.13f },
- { 15.00f, 500, 0x2345, 0.16f },
- { 30.00f, 1200, 0x3456, 0.26f },
- { 7.49f, 150, 0x4567, 0.05f },
- { 7.50f, 150, 0x5678, 0.15f },
- { 7.51f, 150, 0x6789, 0.25f },
- { 15.02f, 150, 0x7890, 0.27f },
- { 15.03f, 150, 0x8901, 0.38f },
- { 30.02f, 150, 0x9012, 0.39f },
- { 30.03f, 150, 0x0123, 0.52f }
- };
- assert(count <= sizeof(configs) / sizeof(configs[0]));
- uint32_t total_capacity = 0;
- for (uint32_t i = 0; i < count; ++i) {
- video_senders_.push_back(new VideoSender(configs[i].fps, configs[i].kbps,
- configs[i].ssrc, configs[i].frame_offset));
- processors_.push_back(video_senders_.back());
- total_capacity += configs[i].kbps;
- }
- BWE_TEST_LOGGING_LOG1("RequiredLinkCapacity", "%d kbps", total_capacity)
- }
-
- void LogStats() {
- for (vector<TestedEstimator*>::iterator eit = estimators_.begin();
- eit != estimators_.end(); ++eit) {
- (*eit)->LogStats();
- }
- }
-
- void UnlimitedSpeedTest() {
- RunFor(10 * 60 * 1000);
- }
-
- void SteadyLossTest() {
- LossFilter loss;
- processors_.push_back(&loss);
- loss.SetLoss(20.0);
- RunFor(10 * 60 * 1000);
- }
- void IncreasingLoss1Test() {
- LossFilter loss;
- processors_.push_back(&loss);
- for (int i = 0; i < 76; ++i) {
- loss.SetLoss(i);
- RunFor(5000);
- }
- }
-
- void SteadyDelayTest() {
- DelayFilter delay;
- processors_.push_back(&delay);
- delay.SetDelay(1000);
- RunFor(10 * 60 * 1000);
- }
- void IncreasingDelay1Test() {
- DelayFilter delay;
- processors_.push_back(&delay);
- RunFor(10 * 60 * 1000);
- for (int i = 0; i < 30 * 2; ++i) {
- delay.SetDelay(i);
- RunFor(10 * 1000);
- }
- RunFor(10 * 60 * 1000);
- }
- void IncreasingDelay2Test() {
- DelayFilter delay;
- RateCounterFilter counter;
- processors_.push_back(&delay);
- processors_.push_back(&counter);
- RunFor(1 * 60 * 1000);
- for (int i = 1; i < 51; ++i) {
- delay.SetDelay(10.0f * i);
- RunFor(10 * 1000);
- }
- delay.SetDelay(0.0f);
- RunFor(10 * 60 * 1000);
- }
- void JumpyDelay1Test() {
- DelayFilter delay;
- processors_.push_back(&delay);
- RunFor(10 * 60 * 1000);
- for (int i = 1; i < 200; ++i) {
- delay.SetDelay((10 * i) % 500);
- RunFor(1000);
- delay.SetDelay(1.0f);
- RunFor(1000);
- }
- delay.SetDelay(0.0f);
- RunFor(10 * 60 * 1000);
- }
-
- void SteadyJitterTest() {
- JitterFilter jitter;
- RateCounterFilter counter;
- processors_.push_back(&jitter);
- processors_.push_back(&counter);
- jitter.SetJitter(20);
- RunFor(2 * 60 * 1000);
- }
- void IncreasingJitter1Test() {
- JitterFilter jitter;
- processors_.push_back(&jitter);
- for (int i = 0; i < 2 * 60 * 2; ++i) {
- jitter.SetJitter(i);
- RunFor(10 * 1000);
- }
- RunFor(10 * 60 * 1000);
- }
- void IncreasingJitter2Test() {
- JitterFilter jitter;
- processors_.push_back(&jitter);
- RunFor(30 * 1000);
- for (int i = 1; i < 51; ++i) {
- jitter.SetJitter(10.0f * i);
- RunFor(10 * 1000);
- }
- jitter.SetJitter(0.0f);
- RunFor(10 * 60 * 1000);
- }
-
- void SteadyReorderTest() {
- ReorderFilter reorder;
- processors_.push_back(&reorder);
- reorder.SetReorder(20.0);
- RunFor(10 * 60 * 1000);
- }
- void IncreasingReorder1Test() {
- ReorderFilter reorder;
- processors_.push_back(&reorder);
- for (int i = 0; i < 76; ++i) {
- reorder.SetReorder(i);
- RunFor(5000);
- }
- }
-
- void SteadyChokeTest() {
- ChokeFilter choke;
- processors_.push_back(&choke);
- choke.SetCapacity(140);
- RunFor(10 * 60 * 1000);
- }
- void IncreasingChoke1Test() {
- ChokeFilter choke;
- processors_.push_back(&choke);
- for (int i = 1200; i >= 100; i -= 100) {
- choke.SetCapacity(i);
- RunFor(5000);
- }
- }
- void IncreasingChoke2Test() {
- ChokeFilter choke;
- processors_.push_back(&choke);
- RunFor(60 * 1000);
- for (int i = 1200; i >= 100; i -= 20) {
- choke.SetCapacity(i);
- RunFor(1000);
- }
- }
-
- void Multi1Test() {
- DelayFilter delay;
- ChokeFilter choke;
- RateCounterFilter counter;
- processors_.push_back(&delay);
- processors_.push_back(&choke);
- processors_.push_back(&counter);
- choke.SetCapacity(1000);
- RunFor(1 * 60 * 1000);
- for (int i = 1; i < 51; ++i) {
- delay.SetDelay(100.0f * i);
- RunFor(10 * 1000);
- }
- delay.SetDelay(0.0f);
- RunFor(5 * 60 * 1000);
- }
- void Multi2Test() {
- ChokeFilter choke;
- JitterFilter jitter;
- RateCounterFilter counter;
- processors_.push_back(&choke);
- processors_.push_back(&jitter);
- processors_.push_back(&counter);
- choke.SetCapacity(2000);
- jitter.SetJitter(120);
- RunFor(5 * 60 * 1000);
- }
-
- private:
- int64_t run_time_ms_;
- vector<TestedEstimator*> estimators_;
- Packets previous_packets_;
- vector<PacketProcessorInterface*> processors_;
- vector<VideoSender*> video_senders_;
-
- DISALLOW_COPY_AND_ASSIGN(RemoteBitrateEstimatorsTest);
-};
-
-#define SINGLE_TEST(enabled, test_name, video_senders, log)\
- TEST_F(RemoteBitrateEstimatorsTest, test_name##_##video_senders##Sender) {\
- BWE_TEST_LOGGING_ENABLE(log);\
- if (enabled) {\
- BWE_TEST_LOGGING_CONTEXT(#test_name);\
- AddVideoSenders(video_senders);\
- test_name##Test();\
- LogStats();\
- }\
- }
-
-#define MULTI_TEST(enabled, test_name, log)\
- SINGLE_TEST((enabled) && ENABLE_1_SENDER, test_name, 1, log)\
- SINGLE_TEST((enabled) && ENABLE_3_SENDERS, test_name, 3, log)\
- SINGLE_TEST((enabled) && ENABLE_10_SENDERS, test_name, 10, log)
-
-MULTI_TEST(ENABLE_BASIC_TESTS, UnlimitedSpeed, true)
-MULTI_TEST(ENABLE_LOSS_TESTS, SteadyLoss, true)
-MULTI_TEST(ENABLE_LOSS_TESTS, IncreasingLoss1, true)
-MULTI_TEST(ENABLE_DELAY_TESTS, SteadyDelay, true)
-MULTI_TEST(ENABLE_DELAY_TESTS, IncreasingDelay1, true)
-MULTI_TEST(ENABLE_DELAY_TESTS, IncreasingDelay2, true)
-MULTI_TEST(ENABLE_DELAY_TESTS, JumpyDelay1, true)
-MULTI_TEST(ENABLE_JITTER_TESTS, SteadyJitter, true)
-MULTI_TEST(ENABLE_JITTER_TESTS, IncreasingJitter1, true)
-MULTI_TEST(ENABLE_JITTER_TESTS, IncreasingJitter2, true)
-MULTI_TEST(ENABLE_REORDER_TESTS, SteadyReorder, true)
-MULTI_TEST(ENABLE_REORDER_TESTS, IncreasingReorder1, true)
-MULTI_TEST(ENABLE_CHOKE_TESTS, SteadyChoke, true)
-MULTI_TEST(ENABLE_CHOKE_TESTS, IncreasingChoke1, true)
-MULTI_TEST(ENABLE_CHOKE_TESTS, IncreasingChoke2, true)
-MULTI_TEST(ENABLE_MULTI_TESTS, Multi1, true)
-MULTI_TEST(ENABLE_MULTI_TESTS, Multi2, true)
-
+TEST_P(BweTest, Multi2) {
+ ChokeFilter choke(this);
+ JitterFilter jitter(this);
+ RateCounterFilter counter(this);
+ choke.SetCapacity(2000);
+ jitter.SetJitter(120);
+ RunFor(5 * 60 * 1000);
+}
} // namespace bwe
} // namespace testing
} // namespace webrtc
diff --git a/modules/remote_bitrate_estimator/test/bwe_test.cc b/modules/remote_bitrate_estimator/test/bwe_test.cc
new file mode 100644
index 0000000..3046a5b
--- /dev/null
+++ b/modules/remote_bitrate_estimator/test/bwe_test.cc
@@ -0,0 +1,263 @@
+/*
+ * Copyright (c) 2013 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/modules/remote_bitrate_estimator/test/bwe_test.h"
+
+#include "webrtc/modules/remote_bitrate_estimator/test/bwe_test_framework.h"
+#include "webrtc/modules/remote_bitrate_estimator/include/remote_bitrate_estimator.h"
+#include "webrtc/system_wrappers/interface/clock.h"
+#include "webrtc/system_wrappers/interface/scoped_ptr.h"
+
+using std::string;
+using std::vector;
+
+namespace webrtc {
+namespace testing {
+namespace bwe {
+
+namespace stl_helpers {
+template<typename T> void DeleteElements(T* container) {
+ if (!container) return;
+ for (typename T::iterator it = container->begin(); it != container->end();
+ ++it) {
+ delete *it;
+ }
+ container->clear();
+}
+} // namespace stl_helpers
+
+class BweTest::TestedEstimator : public RemoteBitrateObserver {
+ public:
+ explicit TestedEstimator(const BweTestConfig::EstimatorConfig& config)
+ : debug_name_(config.debug_name),
+ clock_(0),
+ stats_(),
+ relative_estimator_stats_(),
+ latest_estimate_kbps_(-1.0),
+ estimator_(config.estimator_factory->Create(this, &clock_)),
+ relative_estimator_(NULL) {
+ assert(estimator_.get());
+ // Default RTT in RemoteRateControl is 200 ms ; 50 ms is more realistic.
+ estimator_->OnRttUpdate(50);
+ }
+
+ void SetRelativeEstimator(TestedEstimator* relative_estimator) {
+ relative_estimator_ = relative_estimator;
+ }
+
+ void EatPacket(const Packet& packet) {
+ BWE_TEST_LOGGING_CONTEXT(debug_name_);
+
+ latest_estimate_kbps_ = -1.0;
+
+ // We're treating the send time (from previous filter) as the arrival
+ // time once packet reaches the estimator.
+ int64_t packet_time_ms = (packet.send_time_us() + 500) / 1000;
+ BWE_TEST_LOGGING_TIME(packet_time_ms);
+
+ int64_t step_ms = estimator_->TimeUntilNextProcess();
+ while ((clock_.TimeInMilliseconds() + step_ms) < packet_time_ms) {
+ clock_.AdvanceTimeMilliseconds(step_ms);
+ estimator_->Process();
+ step_ms = estimator_->TimeUntilNextProcess();
+ }
+
+ estimator_->IncomingPacket(packet_time_ms, packet.payload_size(),
+ packet.header());
+ clock_.AdvanceTimeMilliseconds(packet_time_ms -
+ clock_.TimeInMilliseconds());
+ ASSERT_TRUE(packet_time_ms == clock_.TimeInMilliseconds());
+ }
+
+ bool CheckEstimate(PacketSender::Feedback* feedback) {
+ assert(feedback);
+ BWE_TEST_LOGGING_CONTEXT(debug_name_);
+ double estimated_kbps = 0.0;
+ if (LatestEstimate(&estimated_kbps)) {
+ stats_.Push(estimated_kbps);
+ BWE_TEST_LOGGING_PLOT("Estimate", clock_.TimeInMilliseconds(),
+ estimated_kbps / 100);
+ double relative_estimate_kbps = 0.0;
+ if (relative_estimator_ &&
+ relative_estimator_->LatestEstimate(&relative_estimate_kbps)) {
+ relative_estimator_stats_.Push(estimated_kbps - relative_estimate_kbps);
+ }
+ feedback->estimated_kbps = estimated_kbps;
+ return true;
+ }
+ return false;
+ }
+
+ void LogStats() {
+ BWE_TEST_LOGGING_CONTEXT(debug_name_);
+ BWE_TEST_LOGGING_CONTEXT("Mean");
+ stats_.Log("kbps");
+ if (relative_estimator_) {
+ BWE_TEST_LOGGING_CONTEXT("Diff");
+ relative_estimator_stats_.Log("kbps");
+ }
+ }
+
+ virtual void OnReceiveBitrateChanged(const vector<unsigned int>& ssrcs,
+ unsigned int bitrate) {
+ }
+
+ private:
+ bool LatestEstimate(double* estimate_kbps) {
+ if (latest_estimate_kbps_ < 0.0) {
+ vector<unsigned int> ssrcs;
+ unsigned int bps = 0;
+ if (!estimator_->LatestEstimate(&ssrcs, &bps)) {
+ return false;
+ }
+ latest_estimate_kbps_ = bps / 1000.0;
+ }
+ *estimate_kbps = latest_estimate_kbps_;
+ return true;
+ }
+
+ string debug_name_;
+ bool log_estimates_;
+ SimulatedClock clock_;
+ Stats<double> stats_;
+ Stats<double> relative_estimator_stats_;
+ double latest_estimate_kbps_;
+ scoped_ptr<RemoteBitrateEstimator> estimator_;
+ TestedEstimator* relative_estimator_;
+
+ DISALLOW_IMPLICIT_CONSTRUCTORS(TestedEstimator);
+};
+
+BweTest::BweTest()
+ : run_time_ms_(0),
+ simulation_interval_ms_(-1),
+ previous_packets_(),
+ packet_senders_(),
+ estimators_(),
+ processors_() {
+}
+
+BweTest::~BweTest() {
+ stl_helpers::DeleteElements(&estimators_);
+ stl_helpers::DeleteElements(&packet_senders_);
+}
+
+void BweTest::SetUp() {
+ BWE_TEST_LOGGING_GLOBAL_CONTEXT(::testing::UnitTest::GetInstance()->
+ current_test_info()->test_case_name());
+
+ const BweTestConfig& config = GetParam();
+
+ uint32_t total_capacity = 0;
+ for (vector<const PacketSenderFactory*>::const_iterator it =
+ config.sender_factories.begin(); it != config.sender_factories.end();
+ ++it) {
+ PacketSender* sender = (*it)->Create();
+ assert(sender);
+ total_capacity += sender->GetCapacityKbps();
+ packet_senders_.push_back(sender);
+ processors_.push_back(sender);
+ }
+ BWE_TEST_LOGGING_LOG1("RequiredLinkCapacity", "%d kbps", total_capacity)
+
+ // Set simulation interval from first packet sender.
+ if (packet_senders_.size() > 0) {
+ simulation_interval_ms_ = packet_senders_[0]->GetFeedbackIntervalMs();
+ }
+
+ for (vector<BweTestConfig::EstimatorConfig>:: const_iterator it =
+ config.estimator_configs.begin(); it != config.estimator_configs.end();
+ ++it) {
+ estimators_.push_back(new TestedEstimator(*it));
+ }
+ if (estimators_.size() > 1) {
+ // Set all estimators as relative to the first one.
+ for (uint32_t i = 1; i < estimators_.size(); ++i) {
+ estimators_[i]->SetRelativeEstimator(estimators_[0]);
+ }
+ }
+
+ BWE_TEST_LOGGING_GLOBAL_ENABLE(false);
+}
+
+void BweTest::TearDown() {
+ BWE_TEST_LOGGING_GLOBAL_ENABLE(true);
+ LogStats();
+ BWE_TEST_LOGGING_GLOBAL_CONTEXT("");
+}
+
+void BweTest::AddPacketProcessor(
+ PacketProcessor* processor) {
+ assert(processor);
+ processors_.push_back(processor);
+}
+
+void BweTest::RemovePacketProcessor(
+ PacketProcessor* processor) {
+ vector<PacketProcessor*>::iterator it =
+ std::find(processors_.begin(), processors_.end(), processor);
+ assert(it != processors_.end());
+ processors_.erase(it);
+}
+
+void BweTest::VerboseLogging(bool enable) {
+ BWE_TEST_LOGGING_GLOBAL_ENABLE(enable);
+}
+
+void BweTest::RunFor(int64_t time_ms) {
+ for (run_time_ms_ += time_ms; run_time_ms_ >= simulation_interval_ms_;
+ run_time_ms_ -= simulation_interval_ms_) {
+ Packets packets;
+ for (vector<PacketProcessor*>::const_iterator it =
+ processors_.begin(); it != processors_.end(); ++it) {
+ (*it)->RunFor(simulation_interval_ms_, &packets);
+ }
+
+ // Verify packets are in order between batches.
+ if (!packets.empty() && !previous_packets_.empty()) {
+ packets.splice(packets.begin(), previous_packets_,
+ --previous_packets_.end());
+ ASSERT_TRUE(IsTimeSorted(packets));
+ packets.erase(packets.begin());
+ } else {
+ ASSERT_TRUE(IsTimeSorted(packets));
+ }
+
+ for (PacketsConstIt pit = packets.begin(); pit != packets.end(); ++pit) {
+ for (vector<TestedEstimator*>::iterator eit = estimators_.begin();
+ eit != estimators_.end(); ++eit) {
+ (*eit)->EatPacket(*pit);
+ }
+ }
+
+ previous_packets_.swap(packets);
+
+ for (vector<TestedEstimator*>::iterator eit = estimators_.begin();
+ eit != estimators_.end(); ++eit) {
+ PacketSender::Feedback feedback = {0};
+ if ((*eit)->CheckEstimate(&feedback)) {
+ for (vector<PacketSender*>::iterator psit = packet_senders_.begin();
+ psit != packet_senders_.end(); ++psit) {
+ (*psit)->GiveFeedback(feedback);
+ }
+ }
+ }
+ }
+}
+
+void BweTest::LogStats() {
+ for (vector<TestedEstimator*>::iterator eit = estimators_.begin();
+ eit != estimators_.end(); ++eit) {
+ (*eit)->LogStats();
+ }
+}
+} // namespace bwe
+} // namespace testing
+} // namespace webrtc
diff --git a/modules/remote_bitrate_estimator/test/bwe_test.h b/modules/remote_bitrate_estimator/test/bwe_test.h
new file mode 100644
index 0000000..0945ea6
--- /dev/null
+++ b/modules/remote_bitrate_estimator/test/bwe_test.h
@@ -0,0 +1,70 @@
+/*
+ * Copyright (c) 2013 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 <string>
+#include <vector>
+#include "gtest/gtest.h"
+#include "webrtc/modules/remote_bitrate_estimator/test/bwe_test_framework.h"
+#include "webrtc/system_wrappers/interface/constructor_magic.h"
+
+namespace webrtc {
+
+struct RemoteBitrateEstimatorFactory;
+
+namespace testing {
+namespace bwe {
+
+struct BweTestConfig {
+ struct EstimatorConfig {
+ EstimatorConfig() : debug_name(), estimator_factory(NULL) {}
+ EstimatorConfig(std::string debug_name,
+ const RemoteBitrateEstimatorFactory* estimator_factory)
+ : debug_name(debug_name),
+ estimator_factory(estimator_factory) {
+ }
+ std::string debug_name;
+ const RemoteBitrateEstimatorFactory* estimator_factory;
+ };
+
+ std::vector<const PacketSenderFactory*> sender_factories;
+ std::vector<EstimatorConfig> estimator_configs;
+};
+
+class BweTest : public ::testing::TestWithParam<BweTestConfig>,
+ public PacketProcessorListener {
+ public:
+ BweTest();
+ virtual ~BweTest();
+
+ virtual void SetUp();
+ virtual void TearDown();
+ virtual void AddPacketProcessor(PacketProcessor* processor);
+ virtual void RemovePacketProcessor(PacketProcessor* processor);
+
+ protected:
+ void VerboseLogging(bool enable);
+ void RunFor(int64_t time_ms);
+ void LogStats();
+
+ private:
+ class TestedEstimator;
+
+ int64_t run_time_ms_;
+ int64_t simulation_interval_ms_;
+ Packets previous_packets_;
+ std::vector<PacketSender*> packet_senders_;
+ std::vector<TestedEstimator*> estimators_;
+ std::vector<PacketProcessor*> processors_;
+
+ DISALLOW_COPY_AND_ASSIGN(BweTest);
+};
+} // namespace bwe
+} // namespace testing
+} // namespace webrtc
diff --git a/modules/remote_bitrate_estimator/test/bwe_test_framework.cc b/modules/remote_bitrate_estimator/test/bwe_test_framework.cc
index b90a7f6..9b64eaf 100644
--- a/modules/remote_bitrate_estimator/test/bwe_test_framework.cc
+++ b/modules/remote_bitrate_estimator/test/bwe_test_framework.cc
@@ -14,6 +14,62 @@
namespace testing {
namespace bwe {
+Random::Random(uint32_t seed)
+ : a_(0x531FDB97 ^ seed),
+ b_(0x6420ECA8 + seed) {
+}
+
+float Random::Rand() {
+ const float kScale = 1.0f / 0xffffffff;
+ float result = kScale * b_;
+ a_ ^= b_;
+ b_ += a_;
+ return result;
+}
+
+int Random::Gaussian(int mean, int standard_deviation) {
+ // Creating a Normal distribution variable from two independent uniform
+ // variables based on the Box-Muller transform, which is defined on the
+ // interval (0, 1], hence the mask+add below.
+ const double kPi = 3.14159265358979323846;
+ const double kScale = 1.0 / 0x80000000ul;
+ double u1 = kScale * ((a_ & 0x7ffffffful) + 1);
+ double u2 = kScale * ((b_ & 0x7ffffffful) + 1);
+ a_ ^= b_;
+ b_ += a_;
+ return static_cast<int>(mean + standard_deviation *
+ std::sqrt(-2 * std::log(u1)) * std::cos(2 * kPi * u2));
+}
+
+Packet::Packet()
+ : send_time_us_(0),
+ payload_size_(0) {
+ memset(&header_, 0, sizeof(header_));
+}
+
+Packet::Packet(int64_t send_time_us, uint32_t payload_size,
+ const RTPHeader& header)
+ : send_time_us_(send_time_us),
+ payload_size_(payload_size),
+ header_(header) {
+}
+
+Packet::Packet(int64_t send_time_us, uint32_t sequence_number)
+ : send_time_us_(send_time_us),
+ payload_size_(0) {
+ memset(&header_, 0, sizeof(header_));
+ header_.sequenceNumber = sequence_number;
+}
+
+bool Packet::operator<(const Packet& rhs) const {
+ return send_time_us_ < rhs.send_time_us_;
+}
+
+void Packet::set_send_time_us(int64_t send_time_us) {
+ assert(send_time_us >= 0);
+ send_time_us_ = send_time_us;
+}
+
bool IsTimeSorted(const Packets& packets) {
PacketsConstIt last_it = packets.begin();
for (PacketsConstIt it = last_it; it != packets.end(); ++it) {
@@ -24,6 +80,261 @@
}
return true;
}
+
+PacketProcessor::PacketProcessor(PacketProcessorListener* listener)
+ : listener_(listener) {
+ if (listener_) {
+ listener_->AddPacketProcessor(this);
+ }
+}
+
+PacketProcessor::~PacketProcessor() {
+ if (listener_) {
+ listener_->RemovePacketProcessor(this);
+ }
+}
+
+RateCounterFilter::RateCounterFilter(PacketProcessorListener* listener)
+ : PacketProcessor(listener),
+ kWindowSizeUs(1000000),
+ packets_per_second_(0),
+ bytes_per_second_(0),
+ last_accumulated_us_(0),
+ window_(),
+ pps_stats_(),
+ kbps_stats_() {
+}
+
+RateCounterFilter::~RateCounterFilter() {
+ LogStats();
+}
+
+void RateCounterFilter::LogStats() {
+ BWE_TEST_LOGGING_CONTEXT("RateCounterFilter");
+ pps_stats_.Log("pps");
+ kbps_stats_.Log("kbps");
+}
+
+void RateCounterFilter::RunFor(int64_t /*time_ms*/, Packets* in_out) {
+ assert(in_out);
+ for (PacketsConstIt it = in_out->begin(); it != in_out->end(); ++it) {
+ packets_per_second_++;
+ bytes_per_second_ += it->payload_size();
+ last_accumulated_us_ = it->send_time_us();
+ }
+ window_.insert(window_.end(), in_out->begin(), in_out->end());
+ while (!window_.empty()) {
+ const Packet& packet = window_.front();
+ if (packet.send_time_us() > (last_accumulated_us_ - kWindowSizeUs)) {
+ break;
+ }
+ assert(packets_per_second_ >= 1);
+ assert(bytes_per_second_ >= packet.payload_size());
+ packets_per_second_--;
+ bytes_per_second_ -= packet.payload_size();
+ window_.pop_front();
+ }
+ pps_stats_.Push(packets_per_second_);
+ kbps_stats_.Push((bytes_per_second_ * 8) / 1000.0);
+}
+
+LossFilter::LossFilter(PacketProcessorListener* listener)
+ : PacketProcessor(listener),
+ random_(0x12345678),
+ loss_fraction_(0.0f) {
+}
+
+void LossFilter::SetLoss(float loss_percent) {
+ BWE_TEST_LOGGING_ENABLE(false);
+ BWE_TEST_LOGGING_LOG1("Loss", "%f%%", loss_percent);
+ assert(loss_percent >= 0.0f);
+ assert(loss_percent <= 100.0f);
+ loss_fraction_ = loss_percent * 0.01f;
+}
+
+void LossFilter::RunFor(int64_t /*time_ms*/, Packets* in_out) {
+ assert(in_out);
+ for (PacketsIt it = in_out->begin(); it != in_out->end(); ) {
+ if (random_.Rand() < loss_fraction_) {
+ it = in_out->erase(it);
+ } else {
+ ++it;
+ }
+ }
+}
+
+DelayFilter::DelayFilter(PacketProcessorListener* listener)
+ : PacketProcessor(listener),
+ delay_us_(0),
+ last_send_time_us_(0) {
+}
+
+void DelayFilter::SetDelay(int64_t delay_ms) {
+ BWE_TEST_LOGGING_ENABLE(false);
+ BWE_TEST_LOGGING_LOG1("Delay", "%d ms", static_cast<int>(delay_ms));
+ assert(delay_ms >= 0);
+ delay_us_ = delay_ms * 1000;
+}
+
+void DelayFilter::RunFor(int64_t /*time_ms*/, Packets* in_out) {
+ assert(in_out);
+ for (PacketsIt it = in_out->begin(); it != in_out->end(); ++it) {
+ int64_t new_send_time_us = it->send_time_us() + delay_us_;
+ last_send_time_us_ = std::max(last_send_time_us_, new_send_time_us);
+ it->set_send_time_us(last_send_time_us_);
+ }
+}
+
+JitterFilter::JitterFilter(PacketProcessorListener* listener)
+ : PacketProcessor(listener),
+ random_(0x89674523),
+ stddev_jitter_us_(0),
+ last_send_time_us_(0) {
+}
+
+void JitterFilter::SetJitter(int64_t stddev_jitter_ms) {
+ BWE_TEST_LOGGING_ENABLE(false);
+ BWE_TEST_LOGGING_LOG1("Jitter", "%d ms",
+ static_cast<int>(stddev_jitter_ms));
+ assert(stddev_jitter_ms >= 0);
+ stddev_jitter_us_ = stddev_jitter_ms * 1000;
+}
+
+void JitterFilter::RunFor(int64_t /*time_ms*/, Packets* in_out) {
+ assert(in_out);
+ for (PacketsIt it = in_out->begin(); it != in_out->end(); ++it) {
+ int64_t new_send_time_us = it->send_time_us();
+ new_send_time_us += random_.Gaussian(0, stddev_jitter_us_);
+ last_send_time_us_ = std::max(last_send_time_us_, new_send_time_us);
+ it->set_send_time_us(last_send_time_us_);
+ }
+}
+
+ReorderFilter::ReorderFilter(PacketProcessorListener* listener)
+ : PacketProcessor(listener),
+ random_(0x27452389),
+ reorder_fraction_(0.0f) {
+}
+
+void ReorderFilter::SetReorder(float reorder_percent) {
+ BWE_TEST_LOGGING_ENABLE(false);
+ BWE_TEST_LOGGING_LOG1("Reordering", "%f%%", reorder_percent);
+ assert(reorder_percent >= 0.0f);
+ assert(reorder_percent <= 100.0f);
+ reorder_fraction_ = reorder_percent * 0.01f;
+}
+
+void ReorderFilter::RunFor(int64_t /*time_ms*/, Packets* in_out) {
+ assert(in_out);
+ if (in_out->size() >= 2) {
+ PacketsIt last_it = in_out->begin();
+ PacketsIt it = last_it;
+ while (++it != in_out->end()) {
+ if (random_.Rand() < reorder_fraction_) {
+ int64_t t1 = last_it->send_time_us();
+ int64_t t2 = it->send_time_us();
+ std::swap(*last_it, *it);
+ last_it->set_send_time_us(t1);
+ it->set_send_time_us(t2);
+ }
+ last_it = it;
+ }
+ }
+}
+
+ChokeFilter::ChokeFilter(PacketProcessorListener* listener)
+ : PacketProcessor(listener),
+ kbps_(1200),
+ max_delay_us_(0),
+ last_send_time_us_(0) {
+}
+
+void ChokeFilter::SetCapacity(uint32_t kbps) {
+ BWE_TEST_LOGGING_ENABLE(false);
+ BWE_TEST_LOGGING_LOG1("BitrateChoke", "%d kbps", kbps);
+ kbps_ = kbps;
+}
+
+void ChokeFilter::SetMaxDelay(int64_t max_delay_ms) {
+ BWE_TEST_LOGGING_ENABLE(false);
+ BWE_TEST_LOGGING_LOG1("Max Delay", "%d ms", static_cast<int>(max_delay_ms));
+ assert(max_delay_ms >= 0);
+ max_delay_us_ = max_delay_ms * 1000;
+}
+
+void ChokeFilter::RunFor(int64_t /*time_ms*/, Packets* in_out) {
+ assert(in_out);
+ for (PacketsIt it = in_out->begin(); it != in_out->end(); ) {
+ int64_t earliest_send_time_us = last_send_time_us_ +
+ (it->payload_size() * 8 * 1000 + kbps_ / 2) / kbps_;
+ int64_t new_send_time_us = std::max(it->send_time_us(),
+ earliest_send_time_us);
+ if (max_delay_us_ == 0 ||
+ max_delay_us_ >= (new_send_time_us - it->send_time_us())) {
+ it->set_send_time_us(new_send_time_us);
+ last_send_time_us_ = new_send_time_us;
+ ++it;
+ } else {
+ it = in_out->erase(it);
+ }
+ }
+}
+
+PacketSender::PacketSender(PacketProcessorListener* listener)
+ : PacketProcessor(listener) {
+}
+
+VideoSender::VideoSender(PacketProcessorListener* listener, float fps,
+ uint32_t kbps, uint32_t ssrc, float first_frame_offset)
+ : PacketSender(listener),
+ kMaxPayloadSizeBytes(1000),
+ kTimestampBase(0xff80ff00ul),
+ frame_period_ms_(1000.0 / fps),
+ next_frame_ms_(frame_period_ms_ * first_frame_offset),
+ now_ms_(0.0),
+ bytes_per_second_((1000 * kbps) / 8),
+ frame_size_bytes_(bytes_per_second_ / fps),
+ prototype_header_() {
+ assert(first_frame_offset >= 0.0f);
+ assert(first_frame_offset < 1.0f);
+ memset(&prototype_header_, 0, sizeof(prototype_header_));
+ prototype_header_.ssrc = ssrc;
+ prototype_header_.sequenceNumber = 0xf000u;
+}
+
+uint32_t VideoSender::GetCapacityKbps() const {
+ return (bytes_per_second_ * 8) / 1000;
+}
+
+void VideoSender::RunFor(int64_t time_ms, Packets* in_out) {
+ assert(in_out);
+ now_ms_ += time_ms;
+ Packets newPackets;
+ while (now_ms_ >= next_frame_ms_) {
+ prototype_header_.sequenceNumber++;
+ prototype_header_.timestamp = kTimestampBase +
+ static_cast<uint32_t>(next_frame_ms_ * 90.0);
+ prototype_header_.extension.absoluteSendTime = (kTimestampBase +
+ ((static_cast<int64_t>(next_frame_ms_ * (1 << 18)) + 500) / 1000)) &
+ 0x00fffffful;
+ prototype_header_.extension.transmissionTimeOffset = 0;
+
+ // Generate new packets for this frame, all with the same timestamp,
+ // but the payload size is capped, so if the whole frame doesn't fit in
+ // one packet, we will see a number of equally sized packets followed by
+ // one smaller at the tail.
+ int64_t send_time_us = next_frame_ms_ * 1000.0;
+ uint32_t payload_size = frame_size_bytes_;
+ while (payload_size > 0) {
+ uint32_t size = std::min(kMaxPayloadSizeBytes, payload_size);
+ newPackets.push_back(Packet(send_time_us, size, prototype_header_));
+ payload_size -= size;
+ }
+
+ next_frame_ms_ += frame_period_ms_;
+ }
+ in_out->merge(newPackets);
+}
} // namespace bwe
} // namespace testing
} // namespace webrtc
diff --git a/modules/remote_bitrate_estimator/test/bwe_test_framework.h b/modules/remote_bitrate_estimator/test/bwe_test_framework.h
index 3b340bc..c19cb2b 100644
--- a/modules/remote_bitrate_estimator/test/bwe_test_framework.h
+++ b/modules/remote_bitrate_estimator/test/bwe_test_framework.h
@@ -26,44 +26,6 @@
namespace testing {
namespace bwe {
-class Random {
- public:
- explicit Random(uint32_t seed)
- : a_(0x531FDB97 ^ seed),
- b_(0x6420ECA8 + seed) {
- }
-
- // Return semi-random number in the interval [0.0, 1.0].
- float Rand() {
- const float kScale = 1.0f / 0xffffffff;
- float result = kScale * b_;
- a_ ^= b_;
- b_ += a_;
- return result;
- }
-
- // Normal Distribution.
- int Gaussian(int mean, int standard_deviation) {
- // Creating a Normal distribution variable from two independent uniform
- // variables based on the Box-Muller transform, which is defined on the
- // interval (0, 1], hence the mask+add below.
- const double kPi = 3.14159265358979323846;
- const double kScale = 1.0 / 0x80000000ul;
- double u1 = kScale * ((a_ & 0x7ffffffful) + 1);
- double u2 = kScale * ((b_ & 0x7ffffffful) + 1);
- a_ ^= b_;
- b_ += a_;
- return static_cast<int>(mean + standard_deviation *
- std::sqrt(-2 * std::log(u1)) * std::cos(2 * kPi * u2));
- }
-
- private:
- uint32_t a_;
- uint32_t b_;
-
- DISALLOW_IMPLICIT_CONSTRUCTORS(Random);
-};
-
template<typename T> class Stats {
public:
Stats()
@@ -149,36 +111,36 @@
T max_;
};
-class BwePacket {
+class Random {
public:
- BwePacket()
- : send_time_us_(0),
- payload_size_(0) {
- memset(&header_, 0, sizeof(header_));
- }
+ explicit Random(uint32_t seed);
- BwePacket(int64_t send_time_us, uint32_t payload_size,
- const RTPHeader& header)
- : send_time_us_(send_time_us),
- payload_size_(payload_size),
- header_(header) {
- }
+ // Return pseudo random number in the interval [0.0, 1.0].
+ float Rand();
- BwePacket(int64_t send_time_us, uint32_t sequence_number)
- : send_time_us_(send_time_us),
- payload_size_(0) {
- memset(&header_, 0, sizeof(header_));
- header_.sequenceNumber = sequence_number;
- }
+ // Normal Distribution.
+ int Gaussian(int mean, int standard_deviation);
- bool operator<(const BwePacket& rhs) const {
- return send_time_us_ < rhs.send_time_us_;
- }
+ // TODO(solenberg): Random from histogram.
+ // template<typename T> int Distribution(const std::vector<T> histogram) {
- void set_send_time_us(int64_t send_time_us) {
- assert(send_time_us >= 0);
- send_time_us_ = send_time_us;
- }
+ private:
+ uint32_t a_;
+ uint32_t b_;
+
+ DISALLOW_IMPLICIT_CONSTRUCTORS(Random);
+};
+
+class Packet {
+ public:
+ Packet();
+ Packet(int64_t send_time_us, uint32_t payload_size,
+ const RTPHeader& header);
+ Packet(int64_t send_time_us, uint32_t sequence_number);
+
+ bool operator<(const Packet& rhs) const;
+
+ void set_send_time_us(int64_t send_time_us);
int64_t send_time_us() const { return send_time_us_; }
uint32_t payload_size() const { return payload_size_; }
const RTPHeader& header() const { return header_; }
@@ -189,73 +151,180 @@
RTPHeader header_; // Actual contents.
};
-typedef std::list<BwePacket> Packets;
-typedef std::list<BwePacket>::iterator PacketsIt;
-typedef std::list<BwePacket>::const_iterator PacketsConstIt;
+typedef std::list<Packet> Packets;
+typedef std::list<Packet>::iterator PacketsIt;
+typedef std::list<Packet>::const_iterator PacketsConstIt;
bool IsTimeSorted(const Packets& packets);
-class PacketProcessorInterface {
+class PacketProcessor;
+
+class PacketProcessorListener {
public:
- virtual ~PacketProcessorInterface() {}
+ virtual ~PacketProcessorListener() {}
+
+ virtual void AddPacketProcessor(PacketProcessor* processor) = 0;
+ virtual void RemovePacketProcessor(PacketProcessor* processor) = 0;
+};
+
+class PacketProcessor {
+ public:
+ explicit PacketProcessor(PacketProcessorListener* listener);
+ virtual ~PacketProcessor();
// Run simulation for |time_ms| micro seconds, consuming packets from, and
// producing packets into in_out. The outgoing packet list must be sorted on
// |send_time_us_|. The simulation time |time_ms| is optional to use.
virtual void RunFor(int64_t time_ms, Packets* in_out) = 0;
+
+ private:
+ PacketProcessorListener* listener_;
+
+ DISALLOW_COPY_AND_ASSIGN(PacketProcessor);
};
-class VideoSender : public PacketProcessorInterface {
+class RateCounterFilter : public PacketProcessor {
public:
- VideoSender(float fps, uint32_t kbps, uint32_t ssrc, float first_frame_offset)
- : kMaxPayloadSizeBytes(1000),
- kTimestampBase(0xff80ff00ul),
- frame_period_ms_(1000.0 / fps),
- next_frame_ms_(frame_period_ms_ * first_frame_offset),
- now_ms_(0.0),
- bytes_per_second_(1000 * kbps / 8),
- frame_size_bytes_(bytes_per_second_ / fps),
- prototype_header_() {
- assert(first_frame_offset >= 0.0f);
- assert(first_frame_offset < 1.0f);
- memset(&prototype_header_, 0, sizeof(prototype_header_));
- prototype_header_.ssrc = ssrc;
- prototype_header_.sequenceNumber = 0xf000u;
- }
+ explicit RateCounterFilter(PacketProcessorListener* listener);
+ virtual ~RateCounterFilter();
+
+ uint32_t packets_per_second() const { return packets_per_second_; }
+ uint32_t bits_per_second() const { return bytes_per_second_ * 8; }
+
+ void LogStats();
+ virtual void RunFor(int64_t time_ms, Packets* in_out);
+
+ private:
+ const int64_t kWindowSizeUs;
+ uint32_t packets_per_second_;
+ uint32_t bytes_per_second_;
+ int64_t last_accumulated_us_;
+ Packets window_;
+ Stats<double> pps_stats_;
+ Stats<double> kbps_stats_;
+
+ DISALLOW_IMPLICIT_CONSTRUCTORS(RateCounterFilter);
+};
+
+class LossFilter : public PacketProcessor {
+ public:
+ explicit LossFilter(PacketProcessorListener* listener);
+ virtual ~LossFilter() {}
+
+ void SetLoss(float loss_percent);
+ virtual void RunFor(int64_t time_ms, Packets* in_out);
+
+ private:
+ Random random_;
+ float loss_fraction_;
+
+ DISALLOW_IMPLICIT_CONSTRUCTORS(LossFilter);
+};
+
+class DelayFilter : public PacketProcessor {
+ public:
+ explicit DelayFilter(PacketProcessorListener* listener);
+ virtual ~DelayFilter() {}
+
+ void SetDelay(int64_t delay_ms);
+ virtual void RunFor(int64_t time_ms, Packets* in_out);
+
+ private:
+ int64_t delay_us_;
+ int64_t last_send_time_us_;
+
+ DISALLOW_IMPLICIT_CONSTRUCTORS(DelayFilter);
+};
+
+class JitterFilter : public PacketProcessor {
+ public:
+ explicit JitterFilter(PacketProcessorListener* listener);
+ virtual ~JitterFilter() {}
+
+ void SetJitter(int64_t stddev_jitter_ms);
+ virtual void RunFor(int64_t time_ms, Packets* in_out);
+
+ private:
+ Random random_;
+ int64_t stddev_jitter_us_;
+ int64_t last_send_time_us_;
+
+ DISALLOW_IMPLICIT_CONSTRUCTORS(JitterFilter);
+};
+
+class ReorderFilter : public PacketProcessor {
+ public:
+ explicit ReorderFilter(PacketProcessorListener* listener);
+ virtual ~ReorderFilter() {}
+
+ void SetReorder(float reorder_percent);
+ virtual void RunFor(int64_t time_ms, Packets* in_out);
+
+ private:
+ Random random_;
+ float reorder_fraction_;
+
+ DISALLOW_IMPLICIT_CONSTRUCTORS(ReorderFilter);
+};
+
+// Apply a bitrate choke with an infinite queue on the packet stream.
+class ChokeFilter : public PacketProcessor {
+ public:
+ explicit ChokeFilter(PacketProcessorListener* listener);
+ virtual ~ChokeFilter() {}
+
+ void SetCapacity(uint32_t kbps);
+ void SetMaxDelay(int64_t max_delay_ms);
+ virtual void RunFor(int64_t time_ms, Packets* in_out);
+
+ private:
+ uint32_t kbps_;
+ int64_t max_delay_us_;
+ int64_t last_send_time_us_;
+
+ DISALLOW_IMPLICIT_CONSTRUCTORS(ChokeFilter);
+};
+
+class PacketSender : public PacketProcessor {
+ public:
+ struct Feedback {
+ double estimated_kbps;
+ };
+
+ explicit PacketSender(PacketProcessorListener* listener);
+ virtual ~PacketSender() {}
+
+ virtual uint32_t GetCapacityKbps() const { return 0; }
+
+ // Call GiveFeedback() with the returned interval in milliseconds, provided
+ // there is a new estimate available.
+ virtual int64_t GetFeedbackIntervalMs() const { return 1000; }
+ virtual void GiveFeedback(const Feedback& feedback) {}
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(PacketSender);
+};
+
+struct PacketSenderFactory {
+ PacketSenderFactory() {}
+ virtual ~PacketSenderFactory() {}
+ virtual PacketSender* Create() const = 0;
+};
+
+class VideoSender : public PacketSender {
+ public:
+ VideoSender(PacketProcessorListener* listener, float fps, uint32_t kbps,
+ uint32_t ssrc, float first_frame_offset);
virtual ~VideoSender() {}
uint32_t max_payload_size_bytes() const { return kMaxPayloadSizeBytes; }
uint32_t bytes_per_second() const { return bytes_per_second_; }
- virtual void RunFor(int64_t time_ms, Packets* in_out) {
- assert(in_out);
- now_ms_ += time_ms;
- Packets newPackets;
- while (now_ms_ >= next_frame_ms_) {
- prototype_header_.sequenceNumber++;
- prototype_header_.timestamp = kTimestampBase +
- static_cast<uint32_t>(next_frame_ms_ * 90.0);
- prototype_header_.extension.absoluteSendTime = (kTimestampBase +
- ((static_cast<int64_t>(next_frame_ms_ * (1 << 18)) + 500) / 1000)) &
- 0x00fffffful;
- prototype_header_.extension.transmissionTimeOffset = 0;
+ virtual uint32_t GetCapacityKbps() const;
- // Generate new packets for this frame, all with the same timestamp,
- // but the payload size is capped, so if the whole frame doesn't fit in
- // one packet, we will see a number of equally sized packets followed by
- // one smaller at the tail.
- int64_t send_time_us = next_frame_ms_ * 1000.0;
- uint32_t payload_size = frame_size_bytes_;
- while (payload_size > 0) {
- uint32_t size = std::min(kMaxPayloadSizeBytes, payload_size);
- newPackets.push_back(BwePacket(send_time_us, size, prototype_header_));
- payload_size -= size;
- }
-
- next_frame_ms_ += frame_period_ms_;
- }
- in_out->merge(newPackets);
- }
+ // TODO(solenberg): void SetFrameRate(float fps);
+ // TODO(solenberg): void SetRate(uint32_t kbps);
+ virtual void RunFor(int64_t time_ms, Packets* in_out);
private:
const uint32_t kMaxPayloadSizeBytes;
@@ -269,242 +338,6 @@
DISALLOW_IMPLICIT_CONSTRUCTORS(VideoSender);
};
-
-class RateCounterFilter : public PacketProcessorInterface {
- public:
- RateCounterFilter()
- : kWindowSizeUs(1000000),
- packets_per_second_(0),
- bytes_per_second_(0),
- last_accumulated_us_(0),
- window_(),
- pps_stats_(),
- kbps_stats_() {
- }
- virtual ~RateCounterFilter() {
- LogStats();
- }
-
- uint32_t packets_per_second() const { return packets_per_second_; }
- uint32_t bits_per_second() const { return bytes_per_second_ * 8; }
-
- void LogStats() {
- BWE_TEST_LOGGING_CONTEXT("RateCounterFilter");
- pps_stats_.Log("pps");
- kbps_stats_.Log("kbps");
- }
-
- virtual void RunFor(int64_t /*time_ms*/, Packets* in_out) {
- assert(in_out);
- for (PacketsConstIt it = in_out->begin(); it != in_out->end(); ++it) {
- packets_per_second_++;
- bytes_per_second_ += it->payload_size();
- last_accumulated_us_ = it->send_time_us();
- }
- window_.insert(window_.end(), in_out->begin(), in_out->end());
- while (!window_.empty()) {
- const BwePacket& packet = window_.front();
- if (packet.send_time_us() > (last_accumulated_us_ - kWindowSizeUs)) {
- break;
- }
- assert(packets_per_second_ >= 1);
- assert(bytes_per_second_ >= packet.payload_size());
- packets_per_second_--;
- bytes_per_second_ -= packet.payload_size();
- window_.pop_front();
- }
- pps_stats_.Push(packets_per_second_);
- kbps_stats_.Push((bytes_per_second_ * 8) / 1000.0);
- }
-
- private:
- const int64_t kWindowSizeUs;
- uint32_t packets_per_second_;
- uint32_t bytes_per_second_;
- int64_t last_accumulated_us_;
- Packets window_;
- Stats<double> pps_stats_;
- Stats<double> kbps_stats_;
-
- DISALLOW_COPY_AND_ASSIGN(RateCounterFilter);
-};
-
-class LossFilter : public PacketProcessorInterface {
- public:
- LossFilter() : random_(0x12345678), loss_fraction_(0.0f) {}
- virtual ~LossFilter() {}
-
- void SetLoss(float loss_percent) {
- BWE_TEST_LOGGING_ENABLE(false);
- BWE_TEST_LOGGING_LOG1("Loss", "%f%%", loss_percent);
- assert(loss_percent >= 0.0f);
- assert(loss_percent <= 100.0f);
- loss_fraction_ = loss_percent * 0.01f;
- }
-
- virtual void RunFor(int64_t /*time_ms*/, Packets* in_out) {
- assert(in_out);
- for (PacketsIt it = in_out->begin(); it != in_out->end(); ) {
- if (random_.Rand() < loss_fraction_) {
- it = in_out->erase(it);
- } else {
- ++it;
- }
- }
- }
-
- private:
- Random random_;
- float loss_fraction_;
-
- DISALLOW_COPY_AND_ASSIGN(LossFilter);
-};
-
-class DelayFilter : public PacketProcessorInterface {
- public:
- DelayFilter() : delay_us_(0), last_send_time_us_(0) {}
- virtual ~DelayFilter() {}
-
- void SetDelay(int64_t delay_ms) {
- BWE_TEST_LOGGING_ENABLE(false);
- BWE_TEST_LOGGING_LOG1("Delay", "%d ms", static_cast<int>(delay_ms));
- assert(delay_ms >= 0);
- delay_us_ = delay_ms * 1000;
- }
-
- virtual void RunFor(int64_t /*time_ms*/, Packets* in_out) {
- assert(in_out);
- for (PacketsIt it = in_out->begin(); it != in_out->end(); ++it) {
- int64_t new_send_time_us = it->send_time_us() + delay_us_;
- last_send_time_us_ = std::max(last_send_time_us_, new_send_time_us);
- it->set_send_time_us(last_send_time_us_);
- }
- }
-
- private:
- int64_t delay_us_;
- int64_t last_send_time_us_;
-
- DISALLOW_COPY_AND_ASSIGN(DelayFilter);
-};
-
-class JitterFilter : public PacketProcessorInterface {
- public:
- JitterFilter()
- : random_(0x89674523),
- stddev_jitter_us_(0),
- last_send_time_us_(0) {
- }
- virtual ~JitterFilter() {}
-
- void SetJitter(int64_t stddev_jitter_ms) {
- BWE_TEST_LOGGING_ENABLE(false);
- BWE_TEST_LOGGING_LOG1("Jitter", "%d ms",
- static_cast<int>(stddev_jitter_ms));
- assert(stddev_jitter_ms >= 0);
- stddev_jitter_us_ = stddev_jitter_ms * 1000;
- }
-
- virtual void RunFor(int64_t /*time_ms*/, Packets* in_out) {
- assert(in_out);
- for (PacketsIt it = in_out->begin(); it != in_out->end(); ++it) {
- int64_t new_send_time_us = it->send_time_us();
- new_send_time_us += random_.Gaussian(0, stddev_jitter_us_);
- last_send_time_us_ = std::max(last_send_time_us_, new_send_time_us);
- it->set_send_time_us(last_send_time_us_);
- }
- }
-
- private:
- Random random_;
- int64_t stddev_jitter_us_;
- int64_t last_send_time_us_;
-
- DISALLOW_COPY_AND_ASSIGN(JitterFilter);
-};
-
-class ReorderFilter : public PacketProcessorInterface {
- public:
- ReorderFilter() : random_(0x27452389), reorder_fraction_(0.0f) {}
- virtual ~ReorderFilter() {}
-
- void SetReorder(float reorder_percent) {
- BWE_TEST_LOGGING_ENABLE(false);
- BWE_TEST_LOGGING_LOG1("Reordering", "%f%%", reorder_percent);
- assert(reorder_percent >= 0.0f);
- assert(reorder_percent <= 100.0f);
- reorder_fraction_ = reorder_percent * 0.01f;
- }
-
- virtual void RunFor(int64_t /*time_ms*/, Packets* in_out) {
- assert(in_out);
- if (in_out->size() >= 2) {
- PacketsIt last_it = in_out->begin();
- PacketsIt it = last_it;
- while (++it != in_out->end()) {
- if (random_.Rand() < reorder_fraction_) {
- int64_t t1 = last_it->send_time_us();
- int64_t t2 = it->send_time_us();
- std::swap(*last_it, *it);
- last_it->set_send_time_us(t1);
- it->set_send_time_us(t2);
- }
- last_it = it;
- }
- }
- }
-
- private:
- Random random_;
- float reorder_fraction_;
-
- DISALLOW_COPY_AND_ASSIGN(ReorderFilter);
-};
-
-// Apply a bitrate choke with an infinite queue on the packet stream.
-class ChokeFilter : public PacketProcessorInterface {
- public:
- ChokeFilter() : kbps_(1200), max_delay_us_(0), last_send_time_us_(0) {}
- virtual ~ChokeFilter() {}
-
- void SetCapacity(uint32_t kbps) {
- BWE_TEST_LOGGING_ENABLE(false);
- BWE_TEST_LOGGING_LOG1("BitrateChoke", "%d kbps", kbps);
- kbps_ = kbps;
- }
-
- void SetMaxDelay(int64_t max_delay_ms) {
- BWE_TEST_LOGGING_ENABLE(false);
- BWE_TEST_LOGGING_LOG1("Max Delay", "%d ms", static_cast<int>(max_delay_ms));
- assert(max_delay_ms >= 0);
- max_delay_us_ = max_delay_ms * 1000;
- }
-
- virtual void RunFor(int64_t /*time_ms*/, Packets* in_out) {
- assert(in_out);
- for (PacketsIt it = in_out->begin(); it != in_out->end(); ) {
- int64_t earliest_send_time_us = last_send_time_us_ +
- (it->payload_size() * 8 * 1000 + kbps_ / 2) / kbps_;
- int64_t new_send_time_us = std::max(it->send_time_us(),
- earliest_send_time_us);
- if (max_delay_us_ == 0 ||
- max_delay_us_ >= (new_send_time_us - it->send_time_us())) {
- it->set_send_time_us(new_send_time_us);
- last_send_time_us_ = new_send_time_us;
- ++it;
- } else {
- it = in_out->erase(it);
- }
- }
- }
-
- private:
- uint32_t kbps_;
- int64_t max_delay_us_;
- int64_t last_send_time_us_;
-
- DISALLOW_COPY_AND_ASSIGN(ChokeFilter);
-};
} // namespace bwe
} // namespace testing
} // namespace webrtc
diff --git a/modules/remote_bitrate_estimator/test/bwe_test_framework_unittest.cc b/modules/remote_bitrate_estimator/test/bwe_test_framework_unittest.cc
index b6a7123..440044a 100644
--- a/modules/remote_bitrate_estimator/test/bwe_test_framework_unittest.cc
+++ b/modules/remote_bitrate_estimator/test/bwe_test_framework_unittest.cc
@@ -66,45 +66,45 @@
return true;
}
-TEST(BweTestFramework_BwePacketTest, IsTimeSorted) {
+TEST(BweTestFramework_PacketTest, IsTimeSorted) {
Packets packets;
// Insert some packets in order...
EXPECT_TRUE(IsTimeSorted(packets));
- packets.push_back(BwePacket(100, 0));
+ packets.push_back(Packet(100, 0));
EXPECT_TRUE(IsTimeSorted(packets));
- packets.push_back(BwePacket(110, 0));
+ packets.push_back(Packet(110, 0));
EXPECT_TRUE(IsTimeSorted(packets));
// ...and one out-of-order...
- packets.push_back(BwePacket(100, 0));
+ packets.push_back(Packet(100, 0));
EXPECT_FALSE(IsTimeSorted(packets));
// ...remove the out-of-order packet, insert another in-order packet.
packets.pop_back();
- packets.push_back(BwePacket(120, 0));
+ packets.push_back(Packet(120, 0));
EXPECT_TRUE(IsTimeSorted(packets));
}
-TEST(BweTestFramework_BwePacketTest, IsSequenceNumberSorted) {
+TEST(BweTestFramework_PacketTest, IsSequenceNumberSorted) {
Packets packets;
// Insert some packets in order...
EXPECT_TRUE(IsSequenceNumberSorted(packets));
- packets.push_back(BwePacket(0, 100));
+ packets.push_back(Packet(0, 100));
EXPECT_TRUE(IsSequenceNumberSorted(packets));
- packets.push_back(BwePacket(0, 110));
+ packets.push_back(Packet(0, 110));
EXPECT_TRUE(IsSequenceNumberSorted(packets));
// ...and one out-of-order...
- packets.push_back(BwePacket(0, 100));
+ packets.push_back(Packet(0, 100));
EXPECT_FALSE(IsSequenceNumberSorted(packets));
// ...remove the out-of-order packet, insert another in-order packet.
packets.pop_back();
- packets.push_back(BwePacket(0, 120));
+ packets.push_back(Packet(0, 120));
EXPECT_TRUE(IsSequenceNumberSorted(packets));
}
@@ -168,167 +168,10 @@
EXPECT_EQ(3, stats.GetMax());
}
-void TestVideoSender(VideoSender* sender, int64_t run_for_ms,
- uint32_t expected_packets,
- uint32_t expected_payload_size,
- uint32_t expected_total_payload_size) {
- assert(sender);
- Packets packets;
- sender->RunFor(run_for_ms, &packets);
- ASSERT_TRUE(IsTimeSorted(packets));
- ASSERT_TRUE(IsSequenceNumberSorted(packets));
- EXPECT_EQ(expected_packets, packets.size());
- int64_t send_time_us = -1;
- uint32_t total_payload_size = 0;
- uint32_t absolute_send_time = 0;
- uint32_t absolute_send_time_wraps = 0;
- uint32_t rtp_timestamp = 0;
- uint32_t rtp_timestamp_wraps = 0;
- for (PacketsIt it = packets.begin(); it != packets.end(); ++it) {
- EXPECT_LE(send_time_us, it->send_time_us());
- send_time_us = it->send_time_us();
- if (sender->max_payload_size_bytes() != it->payload_size()) {
- EXPECT_EQ(expected_payload_size, it->payload_size());
- }
- total_payload_size += it->payload_size();
- if (absolute_send_time > it->header().extension.absoluteSendTime) {
- absolute_send_time_wraps++;
- }
- absolute_send_time = it->header().extension.absoluteSendTime;
- if (rtp_timestamp > it->header().timestamp) {
- rtp_timestamp_wraps++;
- }
- rtp_timestamp = it->header().timestamp;
- }
- EXPECT_EQ(expected_total_payload_size, total_payload_size);
- EXPECT_GE(1u, absolute_send_time_wraps);
- EXPECT_GE(1u, rtp_timestamp_wraps);
-}
-
-TEST(BweTestFramework_VideoSenderTest, Fps1Kpbs80_1s) {
- // 1 fps, 80 kbps
- VideoSender sender(1.0f, 80, 0x1234, 0);
- EXPECT_EQ(10000u, sender.bytes_per_second());
- // We're at 1 fps, so all packets should be generated on first call, giving 10
- // packets of each 1000 bytes, total 10000 bytes.
- TestVideoSender(&sender, 1, 10, 1000, 10000);
- // 999ms, should see no output here.
- TestVideoSender(&sender, 998, 0, 0, 0);
- // 1999ms, should get data for one more frame.
- TestVideoSender(&sender, 1000, 10, 1000, 10000);
- // 2000ms, one more frame.
- TestVideoSender(&sender, 1, 10, 1000, 10000);
- // 2999ms, should see nothing.
- TestVideoSender(&sender, 999, 0, 0, 0);
-}
-
-TEST(BweTestFramework_VideoSenderTest, Fps1Kpbs80_1s_Offset) {
- // 1 fps, 80 kbps, offset 0.5 of a frame period, ==0.5s in this case.
- VideoSender sender(1.0f, 80, 0x1234, 0.5f);
- EXPECT_EQ(10000u, sender.bytes_per_second());
- // 499ms, no output.
- TestVideoSender(&sender, 499, 0, 0, 0);
- // 500ms, first frame (this is the offset we set), 10 packets of 1000 bytes.
- TestVideoSender(&sender, 1, 10, 1000, 10000);
- // 1499ms, nothing.
- TestVideoSender(&sender, 999, 0, 0, 0);
- // 1999ms, second frame.
- TestVideoSender(&sender, 500, 10, 1000, 10000);
- // 2499ms, nothing.
- TestVideoSender(&sender, 500, 0, 0, 0);
- // 2500ms, third frame.
- TestVideoSender(&sender, 1, 10, 1000, 10000);
- // 3499ms, nothing.
- TestVideoSender(&sender, 999, 0, 0, 0);
-}
-
-TEST(BweTestFramework_VideoSenderTest, Fps50Kpbs80_11s) {
- // 50 fps, 80 kbps.
- VideoSender sender(50.0f, 80, 0x1234, 0);
- EXPECT_EQ(10000u, sender.bytes_per_second());
- // 9998ms, should see 500 frames, 200 byte payloads, total 100000 bytes.
- TestVideoSender(&sender, 9998, 500, 200, 100000);
- // 9999ms, nothing.
- TestVideoSender(&sender, 1, 0, 0, 0);
- // 10000ms, 501st frame as a single packet.
- TestVideoSender(&sender, 1, 1, 200, 200);
- // 10998ms, 49 more frames.
- TestVideoSender(&sender, 998, 49, 200, 9800);
- // 10999ms, nothing.
- TestVideoSender(&sender, 1, 0, 0, 0);
-}
-
-TEST(BweTestFramework_VideoSenderTest, Fps10Kpbs120_1s) {
- // 20 fps, 120 kbps.
- VideoSender sender(20.0f, 120, 0x1234, 0);
- EXPECT_EQ(15000u, sender.bytes_per_second());
- // 498ms, 10 frames with 750 byte payloads, total 7500 bytes.
- TestVideoSender(&sender, 498, 10, 750, 7500);
- // 499ms, nothing.
- TestVideoSender(&sender, 1, 0, 0, 0);
- // 500ms, one more frame.
- TestVideoSender(&sender, 1, 1, 750, 750);
- // 998ms, 9 more frames.
- TestVideoSender(&sender, 498, 9, 750, 6750);
- // 999ms, nothing.
- TestVideoSender(&sender, 1, 0, 0, 0);
-}
-
-TEST(BweTestFramework_VideoSenderTest, Fps30Kpbs800_20s) {
- // 20 fps, 820 kbps.
- VideoSender sender(25.0f, 820, 0x1234, 0);
- EXPECT_EQ(102500u, sender.bytes_per_second());
- // 9998ms, 250 frames. 820 kbps = 102500 bytes/s, so total should be 1025000.
- // Each frame is 102500/25=4100 bytes, or 5 packets (4 @1000 bytes, 1 @100),
- // so packet count should be 5*250=1250 and last packet of each frame has
- // 100 bytes of payload.
- TestVideoSender(&sender, 9998, 1250, 100, 1025000);
- // 9999ms, nothing.
- TestVideoSender(&sender, 1, 0, 0, 0);
- // 19998ms, 250 more frames.
- TestVideoSender(&sender, 9999, 1250, 100, 1025000);
- // 19999ms, nothing.
- TestVideoSender(&sender, 1, 0, 0, 0);
- // 20038ms, one more frame, as described above (25fps == 40ms/frame).
- TestVideoSender(&sender, 39, 5, 100, 4100);
- // 20039ms, nothing.
- TestVideoSender(&sender, 1, 0, 0, 0);
-}
-
-TEST(BweTestFramework_VideoSenderTest, TestAppendInOrder) {
- // 1 fps, 80 kbps, 250ms offset.
- VideoSender sender1(1.0f, 80, 0x1234, 0.25f);
- EXPECT_EQ(10000u, sender1.bytes_per_second());
- Packets packets;
- // Generate some packets, verify they are sorted.
- sender1.RunFor(999, &packets);
- ASSERT_TRUE(IsTimeSorted(packets));
- ASSERT_TRUE(IsSequenceNumberSorted(packets));
- EXPECT_EQ(10u, packets.size());
- // Generate some more packets and verify they are appended to end of list.
- sender1.RunFor(1000, &packets);
- ASSERT_TRUE(IsTimeSorted(packets));
- ASSERT_TRUE(IsSequenceNumberSorted(packets));
- EXPECT_EQ(20u, packets.size());
-
- // Another sender, 2 fps, 160 kpbs, 150ms offset
- VideoSender sender2(2.0f, 160, 0x2234, 0.30f);
- EXPECT_EQ(20000u, sender2.bytes_per_second());
- // Generate some packets, verify that they are merged with the packets already
- // on the list.
- sender2.RunFor(999, &packets);
- ASSERT_TRUE(IsTimeSorted(packets));
- EXPECT_EQ(40u, packets.size());
- // Generate some more.
- sender2.RunFor(1000, &packets);
- ASSERT_TRUE(IsTimeSorted(packets));
- EXPECT_EQ(60u, packets.size());
-}
-
class BweTestFramework_RateCounterFilterTest : public ::testing::Test {
public:
BweTestFramework_RateCounterFilterTest()
- : filter_(),
+ : filter_(NULL),
now_ms_(0) {
}
virtual ~BweTestFramework_RateCounterFilterTest() {}
@@ -340,7 +183,7 @@
RTPHeader header = {0};
// "Send" a packet every 10 ms.
for (int64_t i = 0; i < run_for_ms; i += 10, now_ms_ += 10) {
- packets.push_back(BwePacket(now_ms_ * 1000, payload_bits / 8, header));
+ packets.push_back(Packet(now_ms_ * 1000, payload_bits / 8, header));
}
filter_.RunFor(run_for_ms, &packets);
ASSERT_TRUE(IsTimeSorted(packets));
@@ -386,7 +229,7 @@
}
static void TestLossFilter(float loss_percent, bool zero_tolerance) {
- LossFilter filter;
+ LossFilter filter(NULL);
filter.SetLoss(loss_percent);
Packets::size_type sent_packets = 0;
Packets::size_type remaining_packets = 0;
@@ -406,7 +249,7 @@
// Generate and process 10000 packets in different batch sizes (some empty)
for (int i = 0; i < 2225; ++i) {
Packets packets;
- packets.insert(packets.end(), i % 10, BwePacket());
+ packets.insert(packets.end(), i % 10, Packet());
sent_packets += packets.size();
filter.RunFor(0, &packets);
ASSERT_TRUE(IsTimeSorted(packets));
@@ -445,7 +288,7 @@
class BweTestFramework_DelayFilterTest : public ::testing::Test {
public:
BweTestFramework_DelayFilterTest()
- : filter_(),
+ : filter_(NULL),
now_ms_(0),
sequence_number_(0) {
}
@@ -456,7 +299,7 @@
uint32_t out_packets) {
Packets packets;
for (uint32_t i = 0; i < in_packets; ++i) {
- packets.push_back(BwePacket(now_ms_ * 1000 + (sequence_number_ >> 4),
+ packets.push_back(Packet(now_ms_ * 1000 + (sequence_number_ >> 4),
sequence_number_));
sequence_number_++;
}
@@ -550,14 +393,14 @@
}
TEST_F(BweTestFramework_DelayFilterTest, JumpToZeroDelay) {
- DelayFilter delay;
+ DelayFilter delay(NULL);
Packets acc;
Packets packets;
// Delay a bunch of packets, accumulate them to the 'acc' list.
delay.SetDelay(100.0f);
for (uint32_t i = 0; i < 10; ++i) {
- packets.push_back(BwePacket(i * 100, i));
+ packets.push_back(Packet(i * 100, i));
}
delay.RunFor(1000, &packets);
acc.splice(acc.end(), packets);
@@ -568,7 +411,7 @@
// to the 'acc' list and verify that it is all sorted.
delay.SetDelay(0.0f);
for (uint32_t i = 10; i < 50; ++i) {
- packets.push_back(BwePacket(i * 100, i));
+ packets.push_back(Packet(i * 100, i));
}
delay.RunFor(1000, &packets);
acc.splice(acc.end(), packets);
@@ -595,7 +438,7 @@
}
static void TestJitterFilter(int64_t stddev_jitter_ms) {
- JitterFilter filter;
+ JitterFilter filter(NULL);
filter.SetJitter(stddev_jitter_ms);
int64_t now_ms = 0;
@@ -607,7 +450,7 @@
for (uint32_t i = 0; i < 1000; ++i) {
Packets packets;
for (uint32_t j = 0; j < i % 100; ++j) {
- packets.push_back(BwePacket(now_ms * 1000, sequence_number++));
+ packets.push_back(Packet(now_ms * 1000, sequence_number++));
now_ms += 5 * stddev_jitter_ms;
}
original.insert(original.end(), packets.begin(), packets.end());
@@ -664,13 +507,13 @@
int64_t now_ms = 0;
uint32_t sequence_number = 1;
for (uint32_t i = 0; i < kPacketCount; ++i, now_ms += 10) {
- packets.push_back(BwePacket(now_ms * 1000, sequence_number++));
+ packets.push_back(Packet(now_ms * 1000, sequence_number++));
}
ASSERT_TRUE(IsTimeSorted(packets));
ASSERT_TRUE(IsSequenceNumberSorted(packets));
// Reorder packets, verify that send times are still in order.
- ReorderFilter filter;
+ ReorderFilter filter(NULL);
filter.SetReorder(reorder_percent);
filter.RunFor(now_ms, &packets);
ASSERT_TRUE(IsTimeSorted(packets));
@@ -724,7 +567,7 @@
class BweTestFramework_ChokeFilterTest : public ::testing::Test {
public:
BweTestFramework_ChokeFilterTest()
- : filter_(),
+ : filter_(NULL),
now_ms_(0),
sequence_number_(0),
output_packets_(),
@@ -744,7 +587,7 @@
int64_t send_time_ms = now_ms_ + (i * run_for_ms) / packets_to_generate;
header.sequenceNumber = sequence_number_++;
// Payload is 1000 bits.
- packets.push_back(BwePacket(send_time_ms * 1000, 125, header));
+ packets.push_back(Packet(send_time_ms * 1000, 125, header));
send_times_us_.push_back(send_time_ms * 1000);
}
ASSERT_TRUE(IsTimeSorted(packets));
@@ -757,7 +600,7 @@
// Sum up the transmitted bytes up until the current time.
uint32_t bytes_transmitted = 0;
while (!output_packets_.empty()) {
- const BwePacket& packet = output_packets_.front();
+ const Packet& packet = output_packets_.front();
if (packet.send_time_us() > now_ms_ * 1000) {
break;
}
@@ -770,7 +613,7 @@
void CheckMaxDelay(int64_t max_delay_ms) {
for (PacketsIt it = output_packets_.begin(); it != output_packets_.end();
++it) {
- const BwePacket& packet = *it;
+ const Packet& packet = *it;
int64_t delay_us = packet.send_time_us() -
send_times_us_[packet.header().sequenceNumber];
EXPECT_GE(max_delay_ms * 1000, delay_us);
@@ -853,6 +696,163 @@
TestChoke(100, 100, 2);
TestChoke(9900, 0, 98);
}
+
+void TestVideoSender(VideoSender* sender, int64_t run_for_ms,
+ uint32_t expected_packets,
+ uint32_t expected_payload_size,
+ uint32_t expected_total_payload_size) {
+ assert(sender);
+ Packets packets;
+ sender->RunFor(run_for_ms, &packets);
+ ASSERT_TRUE(IsTimeSorted(packets));
+ ASSERT_TRUE(IsSequenceNumberSorted(packets));
+ EXPECT_EQ(expected_packets, packets.size());
+ int64_t send_time_us = -1;
+ uint32_t total_payload_size = 0;
+ uint32_t absolute_send_time = 0;
+ uint32_t absolute_send_time_wraps = 0;
+ uint32_t rtp_timestamp = 0;
+ uint32_t rtp_timestamp_wraps = 0;
+ for (PacketsIt it = packets.begin(); it != packets.end(); ++it) {
+ EXPECT_LE(send_time_us, it->send_time_us());
+ send_time_us = it->send_time_us();
+ if (sender->max_payload_size_bytes() != it->payload_size()) {
+ EXPECT_EQ(expected_payload_size, it->payload_size());
+ }
+ total_payload_size += it->payload_size();
+ if (absolute_send_time > it->header().extension.absoluteSendTime) {
+ absolute_send_time_wraps++;
+ }
+ absolute_send_time = it->header().extension.absoluteSendTime;
+ if (rtp_timestamp > it->header().timestamp) {
+ rtp_timestamp_wraps++;
+ }
+ rtp_timestamp = it->header().timestamp;
+ }
+ EXPECT_EQ(expected_total_payload_size, total_payload_size);
+ EXPECT_GE(1u, absolute_send_time_wraps);
+ EXPECT_GE(1u, rtp_timestamp_wraps);
+}
+
+TEST(BweTestFramework_VideoSenderTest, Fps1Kpbs80_1s) {
+ // 1 fps, 80 kbps
+ VideoSender sender(NULL, 1.0f, 80, 0x1234, 0);
+ EXPECT_EQ(10000u, sender.bytes_per_second());
+ // We're at 1 fps, so all packets should be generated on first call, giving 10
+ // packets of each 1000 bytes, total 10000 bytes.
+ TestVideoSender(&sender, 1, 10, 1000, 10000);
+ // 999ms, should see no output here.
+ TestVideoSender(&sender, 998, 0, 0, 0);
+ // 1999ms, should get data for one more frame.
+ TestVideoSender(&sender, 1000, 10, 1000, 10000);
+ // 2000ms, one more frame.
+ TestVideoSender(&sender, 1, 10, 1000, 10000);
+ // 2999ms, should see nothing.
+ TestVideoSender(&sender, 999, 0, 0, 0);
+}
+
+TEST(BweTestFramework_VideoSenderTest, Fps1Kpbs80_1s_Offset) {
+ // 1 fps, 80 kbps, offset 0.5 of a frame period, ==0.5s in this case.
+ VideoSender sender(NULL, 1.0f, 80, 0x1234, 0.5f);
+ EXPECT_EQ(10000u, sender.bytes_per_second());
+ // 499ms, no output.
+ TestVideoSender(&sender, 499, 0, 0, 0);
+ // 500ms, first frame (this is the offset we set), 10 packets of 1000 bytes.
+ TestVideoSender(&sender, 1, 10, 1000, 10000);
+ // 1499ms, nothing.
+ TestVideoSender(&sender, 999, 0, 0, 0);
+ // 1999ms, second frame.
+ TestVideoSender(&sender, 500, 10, 1000, 10000);
+ // 2499ms, nothing.
+ TestVideoSender(&sender, 500, 0, 0, 0);
+ // 2500ms, third frame.
+ TestVideoSender(&sender, 1, 10, 1000, 10000);
+ // 3499ms, nothing.
+ TestVideoSender(&sender, 999, 0, 0, 0);
+}
+
+TEST(BweTestFramework_VideoSenderTest, Fps50Kpbs80_11s) {
+ // 50 fps, 80 kbps.
+ VideoSender sender(NULL, 50.0f, 80, 0x1234, 0);
+ EXPECT_EQ(10000u, sender.bytes_per_second());
+ // 9998ms, should see 500 frames, 200 byte payloads, total 100000 bytes.
+ TestVideoSender(&sender, 9998, 500, 200, 100000);
+ // 9999ms, nothing.
+ TestVideoSender(&sender, 1, 0, 0, 0);
+ // 10000ms, 501st frame as a single packet.
+ TestVideoSender(&sender, 1, 1, 200, 200);
+ // 10998ms, 49 more frames.
+ TestVideoSender(&sender, 998, 49, 200, 9800);
+ // 10999ms, nothing.
+ TestVideoSender(&sender, 1, 0, 0, 0);
+}
+
+TEST(BweTestFramework_VideoSenderTest, Fps10Kpbs120_1s) {
+ // 20 fps, 120 kbps.
+ VideoSender sender(NULL, 20.0f, 120, 0x1234, 0);
+ EXPECT_EQ(15000u, sender.bytes_per_second());
+ // 498ms, 10 frames with 750 byte payloads, total 7500 bytes.
+ TestVideoSender(&sender, 498, 10, 750, 7500);
+ // 499ms, nothing.
+ TestVideoSender(&sender, 1, 0, 0, 0);
+ // 500ms, one more frame.
+ TestVideoSender(&sender, 1, 1, 750, 750);
+ // 998ms, 9 more frames.
+ TestVideoSender(&sender, 498, 9, 750, 6750);
+ // 999ms, nothing.
+ TestVideoSender(&sender, 1, 0, 0, 0);
+}
+
+TEST(BweTestFramework_VideoSenderTest, Fps30Kpbs800_20s) {
+ // 20 fps, 820 kbps.
+ VideoSender sender(NULL, 25.0f, 820, 0x1234, 0);
+ EXPECT_EQ(102500u, sender.bytes_per_second());
+ // 9998ms, 250 frames. 820 kbps = 102500 bytes/s, so total should be 1025000.
+ // Each frame is 102500/25=4100 bytes, or 5 packets (4 @1000 bytes, 1 @100),
+ // so packet count should be 5*250=1250 and last packet of each frame has
+ // 100 bytes of payload.
+ TestVideoSender(&sender, 9998, 1250, 100, 1025000);
+ // 9999ms, nothing.
+ TestVideoSender(&sender, 1, 0, 0, 0);
+ // 19998ms, 250 more frames.
+ TestVideoSender(&sender, 9999, 1250, 100, 1025000);
+ // 19999ms, nothing.
+ TestVideoSender(&sender, 1, 0, 0, 0);
+ // 20038ms, one more frame, as described above (25fps == 40ms/frame).
+ TestVideoSender(&sender, 39, 5, 100, 4100);
+ // 20039ms, nothing.
+ TestVideoSender(&sender, 1, 0, 0, 0);
+}
+
+TEST(BweTestFramework_VideoSenderTest, TestAppendInOrder) {
+ // 1 fps, 80 kbps, 250ms offset.
+ VideoSender sender1(NULL, 1.0f, 80, 0x1234, 0.25f);
+ EXPECT_EQ(10000u, sender1.bytes_per_second());
+ Packets packets;
+ // Generate some packets, verify they are sorted.
+ sender1.RunFor(999, &packets);
+ ASSERT_TRUE(IsTimeSorted(packets));
+ ASSERT_TRUE(IsSequenceNumberSorted(packets));
+ EXPECT_EQ(10u, packets.size());
+ // Generate some more packets and verify they are appended to end of list.
+ sender1.RunFor(1000, &packets);
+ ASSERT_TRUE(IsTimeSorted(packets));
+ ASSERT_TRUE(IsSequenceNumberSorted(packets));
+ EXPECT_EQ(20u, packets.size());
+
+ // Another sender, 2 fps, 160 kpbs, 150ms offset
+ VideoSender sender2(NULL, 2.0f, 160, 0x2234, 0.30f);
+ EXPECT_EQ(20000u, sender2.bytes_per_second());
+ // Generate some packets, verify that they are merged with the packets already
+ // on the list.
+ sender2.RunFor(999, &packets);
+ ASSERT_TRUE(IsTimeSorted(packets));
+ EXPECT_EQ(40u, packets.size());
+ // Generate some more.
+ sender2.RunFor(1000, &packets);
+ ASSERT_TRUE(IsTimeSorted(packets));
+ EXPECT_EQ(60u, packets.size());
+}
} // namespace bwe
} // namespace testing
} // namespace webrtc
diff --git a/modules/remote_bitrate_estimator/test/bwe_test_logging.cc b/modules/remote_bitrate_estimator/test/bwe_test_logging.cc
index ae1d42b..1a43f09 100644
--- a/modules/remote_bitrate_estimator/test/bwe_test_logging.cc
+++ b/modules/remote_bitrate_estimator/test/bwe_test_logging.cc
@@ -25,20 +25,24 @@
Logging Logging::g_Logging;
-Logging::Context::Context(uint32_t name, int64_t timestamp_ms, bool enabled) {
+static std::string ToString(uint32_t v) {
const size_t kBufferSize = 16;
char string_buffer[kBufferSize] = {0};
#if defined(_MSC_VER) && defined(_WIN32)
- _snprintf(string_buffer, kBufferSize - 1, "%08x", name);
+ _snprintf(string_buffer, kBufferSize - 1, "%08x", v);
#else
- snprintf(string_buffer, kBufferSize, "%08x", name);
+ snprintf(string_buffer, kBufferSize, "%08x", v);
#endif
- Logging::GetInstance()->PushState(string_buffer, timestamp_ms, enabled);
+ return string_buffer;
+}
+
+Logging::Context::Context(uint32_t name, int64_t timestamp_ms, bool enabled) {
+ Logging::GetInstance()->PushState(ToString(name), timestamp_ms, enabled);
}
Logging::Context::Context(const std::string& name, int64_t timestamp_ms,
bool enabled) {
- Logging::GetInstance()->PushState(name.c_str(), timestamp_ms, enabled);
+ Logging::GetInstance()->PushState(name, timestamp_ms, enabled);
}
Logging::Context::Context(const char* name, int64_t timestamp_ms,
@@ -54,11 +58,31 @@
return &g_Logging;
}
+void Logging::SetGlobalContext(uint32_t name) {
+ CriticalSectionScoped cs(crit_sect_.get());
+ thread_map_[ThreadWrapper::GetThreadId()].global_state.tag = ToString(name);
+}
+
+void Logging::SetGlobalContext(const std::string& name) {
+ CriticalSectionScoped cs(crit_sect_.get());
+ thread_map_[ThreadWrapper::GetThreadId()].global_state.tag = name;
+}
+
+void Logging::SetGlobalContext(const char* name) {
+ CriticalSectionScoped cs(crit_sect_.get());
+ thread_map_[ThreadWrapper::GetThreadId()].global_state.tag = name;
+}
+
+void Logging::SetGlobalEnable(bool enabled) {
+ CriticalSectionScoped cs(crit_sect_.get());
+ thread_map_[ThreadWrapper::GetThreadId()].global_state.enabled = enabled;
+}
+
void Logging::Log(const char format[], ...) {
CriticalSectionScoped cs(crit_sect_.get());
ThreadMap::iterator it = thread_map_.find(ThreadWrapper::GetThreadId());
assert(it != thread_map_.end());
- const State& state = it->second.top();
+ const State& state = it->second.stack.top();
if (state.enabled) {
printf("%s\t", state.tag.c_str());
va_list args;
@@ -73,7 +97,7 @@
CriticalSectionScoped cs(crit_sect_.get());
ThreadMap::iterator it = thread_map_.find(ThreadWrapper::GetThreadId());
assert(it != thread_map_.end());
- const State& state = it->second.top();
+ const State& state = it->second.stack.top();
if (state.enabled) {
printf("PLOT\t%s\t%f\t%f\n", state.tag.c_str(), state.timestamp_ms * 0.001,
value);
@@ -85,37 +109,48 @@
thread_map_() {
}
-void Logging::PushState(const char append_to_tag[], int64_t timestamp_ms,
- bool enabled) {
- assert(append_to_tag);
- CriticalSectionScoped cs(crit_sect_.get());
- std::stack<State>* stack = &thread_map_[ThreadWrapper::GetThreadId()];
- if (stack->empty()) {
- State new_state(append_to_tag, std::max(static_cast<int64_t>(0),
- timestamp_ms), enabled);
- stack->push(new_state);
- } else {
- stack->push(stack->top());
- State* state = &stack->top();
- if (state->tag != "" && std::string(append_to_tag) != "") {
- state->tag.append("_");
- }
- state->tag.append(append_to_tag);
- state->timestamp_ms = std::max(timestamp_ms, state->timestamp_ms);
- state->enabled = enabled && state->enabled;
+Logging::State::State() : tag(""), timestamp_ms(0), enabled(true) {}
+
+Logging::State::State(const std::string& tag, int64_t timestamp_ms,
+ bool enabled)
+ : tag(tag),
+ timestamp_ms(timestamp_ms),
+ enabled(enabled) {
+}
+
+void Logging::State::MergePrevious(const State& previous) {
+ if (tag == "") {
+ tag = previous.tag;
+ } else if (previous.tag != "") {
+ tag = previous.tag + "_" + tag;
}
+ timestamp_ms = std::max(previous.timestamp_ms, timestamp_ms);
+ enabled = previous.enabled && enabled;
+}
+
+void Logging::PushState(const std::string& append_to_tag, int64_t timestamp_ms,
+ bool enabled) {
+ CriticalSectionScoped cs(crit_sect_.get());
+ State new_state(append_to_tag, timestamp_ms, enabled);
+ ThreadState* thread_state = &thread_map_[ThreadWrapper::GetThreadId()];
+ std::stack<State>* stack = &thread_state->stack;
+ if (stack->empty()) {
+ new_state.MergePrevious(thread_state->global_state);
+ } else {
+ new_state.MergePrevious(stack->top());
+ }
+ stack->push(new_state);
}
void Logging::PopState() {
CriticalSectionScoped cs(crit_sect_.get());
ThreadMap::iterator it = thread_map_.find(ThreadWrapper::GetThreadId());
assert(it != thread_map_.end());
- int64_t newest_timestamp_ms = it->second.top().timestamp_ms;
- it->second.pop();
- if (it->second.empty()) {
- thread_map_.erase(it);
- } else {
- State* state = &it->second.top();
+ std::stack<State>* stack = &it->second.stack;
+ int64_t newest_timestamp_ms = stack->top().timestamp_ms;
+ stack->pop();
+ if (!stack->empty()) {
+ State* state = &stack->top();
// Update time so that next log/plot will use the latest time seen so far
// in this call tree.
state->timestamp_ms = std::max(state->timestamp_ms, newest_timestamp_ms);
diff --git a/modules/remote_bitrate_estimator/test/bwe_test_logging.h b/modules/remote_bitrate_estimator/test/bwe_test_logging.h
index 0ff5825..c949763 100644
--- a/modules/remote_bitrate_estimator/test/bwe_test_logging.h
+++ b/modules/remote_bitrate_estimator/test/bwe_test_logging.h
@@ -51,6 +51,15 @@
#if !(BWE_TEST_LOGGING_COMPILE_TIME_ENABLE)
+// Set a thread-global base logging context. This name will be prepended to all
+// hierarchical contexts.
+// |name| is a char*, std::string or uint32_t to name the context.
+#define BWE_TEST_LOGGING_GLOBAL_CONTEXT(name)
+
+// Thread-globally allow/disallow logging.
+// |enable| is expected to be a bool.
+#define BWE_TEST_LOGGING_GLOBAL_ENABLE(enabled)
+
// Insert a (hierarchical) logging context.
// |name| is a char*, std::string or uint32_t to name the context.
#define BWE_TEST_LOGGING_CONTEXT(name)
@@ -93,6 +102,16 @@
#include "webrtc/system_wrappers/interface/constructor_magic.h"
#include "webrtc/system_wrappers/interface/scoped_ptr.h"
+#define BWE_TEST_LOGGING_GLOBAL_CONTEXT(name) \
+ do { \
+ webrtc::testing::bwe::Logging::GetInstance()->SetGlobalContext(name); \
+ } while (0);
+
+#define BWE_TEST_LOGGING_GLOBAL_ENABLE(enabled) \
+ do { \
+ webrtc::testing::bwe::Logging::GetInstance()->SetGlobalEnable(enabled); \
+ } while (0);
+
#define __BWE_TEST_LOGGING_CONTEXT_NAME(ctx, line) ctx ## line
#define __BWE_TEST_LOGGING_CONTEXT_DECLARE(ctx, line, name, time, enabled) \
webrtc::testing::bwe::Logging::Context \
@@ -162,24 +181,33 @@
};
static Logging* GetInstance();
+
+ void SetGlobalContext(uint32_t name);
+ void SetGlobalContext(const std::string& name);
+ void SetGlobalContext(const char* name);
+ void SetGlobalEnable(bool enabled);
+
void Log(const char format[], ...);
void Plot(double value);
private:
struct State {
- State(const char new_tag[], int64_t timestamp_ms, bool enabled)
- : tag(new_tag),
- timestamp_ms(timestamp_ms),
- enabled(enabled) {
- }
+ State();
+ State(const std::string& new_tag, int64_t timestamp_ms, bool enabled);
+ void MergePrevious(const State& previous);
+
std::string tag;
int64_t timestamp_ms;
bool enabled;
};
- typedef std::map<uint32_t, std::stack<State> > ThreadMap;
+ struct ThreadState {
+ State global_state;
+ std::stack<State> stack;
+ };
+ typedef std::map<uint32_t, ThreadState> ThreadMap;
Logging();
- void PushState(const char append_to_tag[], int64_t timestamp_ms,
+ void PushState(const std::string& append_to_tag, int64_t timestamp_ms,
bool enabled);
void PopState();
diff --git a/modules/rtp_rtcp/interface/rtp_rtcp.h b/modules/rtp_rtcp/interface/rtp_rtcp.h
index c0e6183..54aadf5 100644
--- a/modules/rtp_rtcp/interface/rtp_rtcp.h
+++ b/modules/rtp_rtcp/interface/rtp_rtcp.h
@@ -63,7 +63,7 @@
RtcpFeedback* rtcp_feedback;
RtcpIntraFrameObserver* intra_frame_callback;
RtcpBandwidthObserver* bandwidth_callback;
- RtcpRttObserver* rtt_observer;
+ RtcpRttStats* rtt_stats;
RtpAudioFeedback* audio_messages;
RemoteBitrateEstimator* remote_bitrate_estimator;
PacedSender* paced_sender;
@@ -510,6 +510,8 @@
*/
virtual void SetRtcpXrRrtrStatus(bool enable) = 0;
+ virtual bool RtcpXrRrtrStatus() const = 0;
+
/*
* (REMB) Receiver Estimated Max Bitrate
*/
diff --git a/modules/rtp_rtcp/interface/rtp_rtcp_defines.h b/modules/rtp_rtcp/interface/rtp_rtcp_defines.h
index 21dcc9f..29a1bbc 100644
--- a/modules/rtp_rtcp/interface/rtp_rtcp_defines.h
+++ b/modules/rtp_rtcp/interface/rtp_rtcp_defines.h
@@ -288,11 +288,13 @@
virtual ~RtcpBandwidthObserver() {}
};
-class RtcpRttObserver {
+class RtcpRttStats {
public:
virtual void OnRttUpdate(uint32_t rtt) = 0;
- virtual ~RtcpRttObserver() {};
+ virtual uint32_t LastProcessedRtt() const = 0;
+
+ virtual ~RtcpRttStats() {};
};
// Null object version of RtpFeedback.
diff --git a/modules/rtp_rtcp/mocks/mock_rtp_rtcp.h b/modules/rtp_rtcp/mocks/mock_rtp_rtcp.h
index 09c3ca6..90ea4f8 100644
--- a/modules/rtp_rtcp/mocks/mock_rtp_rtcp.h
+++ b/modules/rtp_rtcp/mocks/mock_rtp_rtcp.h
@@ -170,6 +170,8 @@
int32_t(const RTCPVoIPMetric* VoIPMetric));
MOCK_METHOD1(SetRtcpXrRrtrStatus,
void(bool enable));
+ MOCK_CONST_METHOD0(RtcpXrRrtrStatus,
+ bool());
MOCK_CONST_METHOD0(REMB,
bool());
MOCK_METHOD1(SetREMBStatus,
diff --git a/modules/rtp_rtcp/source/forward_error_correction.cc b/modules/rtp_rtcp/source/forward_error_correction.cc
index a892386..189e1b0 100644
--- a/modules/rtp_rtcp/source/forward_error_correction.cc
+++ b/modules/rtp_rtcp/source/forward_error_correction.cc
@@ -11,6 +11,7 @@
#include "webrtc/modules/rtp_rtcp/source/forward_error_correction.h"
#include <assert.h>
+#include <stdlib.h>
#include <string.h>
#include <algorithm>
diff --git a/modules/rtp_rtcp/source/rtcp_sender.cc b/modules/rtp_rtcp/source/rtcp_sender.cc
index 7962e97..a407a42 100644
--- a/modules/rtp_rtcp/source/rtcp_sender.cc
+++ b/modules/rtp_rtcp/source/rtcp_sender.cc
@@ -2182,6 +2182,11 @@
xrSendReceiverReferenceTimeEnabled_ = enable;
}
+bool RTCPSender::RtcpXrReceiverReferenceTime() const {
+ CriticalSectionScoped lock(_criticalSectionRTCPSender);
+ return xrSendReceiverReferenceTimeEnabled_;
+}
+
// called under critsect _criticalSectionRTCPSender
int32_t RTCPSender::WriteAllReportBlocksToBuffer(
uint8_t* rtcpbuffer,
diff --git a/modules/rtp_rtcp/source/rtcp_sender.h b/modules/rtp_rtcp/source/rtcp_sender.h
index 106a78d..9ed5824 100644
--- a/modules/rtp_rtcp/source/rtcp_sender.h
+++ b/modules/rtp_rtcp/source/rtcp_sender.h
@@ -171,6 +171,8 @@
void SendRtcpXrReceiverReferenceTime(bool enable);
+ bool RtcpXrReceiverReferenceTime() const;
+
int32_t SetCSRCs(const uint32_t arrOfCSRC[kRtpCsrcSize],
const uint8_t arrLength);
diff --git a/modules/rtp_rtcp/source/rtp_rtcp_impl.cc b/modules/rtp_rtcp/source/rtp_rtcp_impl.cc
index f910a18..44b33a8 100644
--- a/modules/rtp_rtcp/source/rtp_rtcp_impl.cc
+++ b/modules/rtp_rtcp/source/rtp_rtcp_impl.cc
@@ -39,7 +39,7 @@
rtcp_feedback(NULL),
intra_frame_callback(NULL),
bandwidth_callback(NULL),
- rtt_observer(NULL),
+ rtt_stats(NULL),
audio_messages(NullObjectRtpAudioFeedback()),
remote_bitrate_estimator(NULL),
paced_sender(NULL) {
@@ -92,7 +92,9 @@
#ifdef MATLAB
, plot1_(NULL),
#endif
- rtt_observer_(configuration.rtt_observer) {
+ rtt_stats_(configuration.rtt_stats),
+ critical_section_rtt_(CriticalSectionWrapper::CreateCriticalSection()),
+ rtt_ms_(0) {
send_video_codec_.codecType = kVideoCodecUnknown;
if (default_module_) {
@@ -212,8 +214,8 @@
max_rtt = (rtt > max_rtt) ? rtt : max_rtt;
}
// Report the rtt.
- if (rtt_observer_ && max_rtt != 0)
- rtt_observer_->OnRttUpdate(max_rtt);
+ if (rtt_stats_ && max_rtt != 0)
+ rtt_stats_->OnRttUpdate(max_rtt);
}
// Verify receiver reports are delivered and the reported sequence number
@@ -240,14 +242,18 @@
// Report rtt from receiver.
if (process_rtt) {
uint16_t rtt_ms;
- if (rtt_observer_ && rtcp_receiver_.GetAndResetXrRrRtt(&rtt_ms)) {
- rtt_observer_->OnRttUpdate(rtt_ms);
+ if (rtt_stats_ && rtcp_receiver_.GetAndResetXrRrRtt(&rtt_ms)) {
+ rtt_stats_->OnRttUpdate(rtt_ms);
}
}
}
+ // Get processed rtt.
if (process_rtt) {
last_rtt_process_time_ = now;
+ if (rtt_stats_) {
+ set_rtt_ms(rtt_stats_->LastProcessedRtt());
+ }
}
if (rtcp_sender_.TimeToSendRTCPReport()) {
@@ -346,7 +352,12 @@
video_codec.plType);
send_video_codec_ = video_codec;
- simulcast_ = (video_codec.numberOfSimulcastStreams > 1) ? true : false;
+ {
+ // simulcast_ is accessed when accessing child_modules_, so this write needs
+ // to be protected by the same lock.
+ CriticalSectionScoped lock(critical_section_module_ptrs_.get());
+ simulcast_ = video_codec.numberOfSimulcastStreams > 1;
+ }
return rtp_sender_.RegisterPayload(video_codec.plName,
video_codec.plType,
90000,
@@ -600,12 +611,12 @@
&(rtp_video_hdr->codecHeader));
}
int32_t ret_val = -1;
+ CriticalSectionScoped lock(critical_section_module_ptrs_.get());
if (simulcast_) {
if (rtp_video_hdr == NULL) {
return -1;
}
int idx = 0;
- CriticalSectionScoped lock(critical_section_module_ptrs_.get());
std::list<ModuleRtpRtcpImpl*>::iterator it = child_modules_.begin();
for (; idx < rtp_video_hdr->simulcastIdx; ++it) {
if (it == child_modules_.end()) {
@@ -638,7 +649,6 @@
fragmentation,
rtp_video_hdr);
} else {
- CriticalSectionScoped lock(critical_section_module_ptrs_.get());
std::list<ModuleRtpRtcpImpl*>::iterator it = child_modules_.begin();
// Send to all "child" modules
while (it != child_modules_.end()) {
@@ -957,6 +967,10 @@
return rtcp_sender_.SendRtcpXrReceiverReferenceTime(enable);
}
+bool ModuleRtpRtcpImpl::RtcpXrRrtrStatus() const {
+ return rtcp_sender_.RtcpXrReceiverReferenceTime();
+}
+
int32_t ModuleRtpRtcpImpl::DataCountersRTP(
uint32_t* bytes_sent,
uint32_t* packets_sent) const {
@@ -1623,4 +1637,14 @@
rtcp_receiver_.SetSsrcs(main_ssrc, ssrcs);
}
+void ModuleRtpRtcpImpl::set_rtt_ms(uint32_t rtt_ms) {
+ CriticalSectionScoped cs(critical_section_rtt_.get());
+ rtt_ms_ = rtt_ms;
+}
+
+uint32_t ModuleRtpRtcpImpl::rtt_ms() const {
+ CriticalSectionScoped cs(critical_section_rtt_.get());
+ return rtt_ms_;
+}
+
} // Namespace webrtc
diff --git a/modules/rtp_rtcp/source/rtp_rtcp_impl.h b/modules/rtp_rtcp/source/rtp_rtcp_impl.h
index b29ec57..940a656 100644
--- a/modules/rtp_rtcp/source/rtp_rtcp_impl.h
+++ b/modules/rtp_rtcp/source/rtp_rtcp_impl.h
@@ -19,6 +19,7 @@
#include "webrtc/modules/rtp_rtcp/source/rtcp_sender.h"
#include "webrtc/modules/rtp_rtcp/source/rtp_sender.h"
#include "webrtc/system_wrappers/interface/scoped_ptr.h"
+#include "webrtc/test/testsupport/gtest_prod_util.h"
#ifdef MATLAB
class MatlabPlot;
@@ -258,6 +259,8 @@
// (XR) Receiver reference time report.
virtual void SetRtcpXrRrtrStatus(bool enable) OVERRIDE;
+ virtual bool RtcpXrRrtrStatus() const OVERRIDE;
+
// Audio part.
// Set audio packet size, used to determine when it's time to send a DTMF
@@ -383,9 +386,13 @@
Clock* clock_;
private:
+ FRIEND_TEST_ALL_PREFIXES(RtpRtcpImplTest, RttForReceiverOnly);
int64_t RtcpReportInterval();
void SetRtcpReceiverSsrcs(uint32_t main_ssrc);
+ void set_rtt_ms(uint32_t rtt_ms);
+ uint32_t rtt_ms() const;
+
int32_t id_;
const bool audio_;
bool collision_detected_;
@@ -414,7 +421,11 @@
MatlabPlot* plot1_;
#endif
- RtcpRttObserver* rtt_observer_;
+ RtcpRttStats* rtt_stats_;
+
+ // The processed RTT from RtcpRttStats.
+ scoped_ptr<CriticalSectionWrapper> critical_section_rtt_;
+ uint32_t rtt_ms_;
};
} // namespace webrtc
diff --git a/modules/rtp_rtcp/source/rtp_rtcp_impl_unittest.cc b/modules/rtp_rtcp/source/rtp_rtcp_impl_unittest.cc
index ff3c32e..6248f49 100644
--- a/modules/rtp_rtcp/source/rtp_rtcp_impl_unittest.cc
+++ b/modules/rtp_rtcp/source/rtp_rtcp_impl_unittest.cc
@@ -18,7 +18,7 @@
namespace webrtc {
namespace {
-class RtcpRttStatsTestImpl : public RtcpRttObserver {
+class RtcpRttStatsTestImpl : public RtcpRttStats {
public:
RtcpRttStatsTestImpl() : rtt_ms_(0) {}
virtual ~RtcpRttStatsTestImpl() {}
@@ -26,6 +26,9 @@
virtual void OnRttUpdate(uint32_t rtt_ms) {
rtt_ms_ = rtt_ms;
}
+ virtual uint32_t LastProcessedRtt() const {
+ return rtt_ms_;
+ }
uint32_t rtt_ms_;
};
@@ -70,7 +73,7 @@
configuration.clock = &clock_;
configuration.outgoing_transport = &transport_;
configuration.receive_statistics = receive_statistics_.get();
- configuration.rtt_observer = &rtt_stats_;
+ configuration.rtt_stats = &rtt_stats_;
rtp_rtcp_impl_.reset(new ModuleRtpRtcpImpl(configuration));
transport_.SetRtpRtcpModule(rtp_rtcp_impl_.get());
@@ -121,6 +124,12 @@
rtp_rtcp_impl_->RTT(kSsrc + 1, &rtt, &avg_rtt, &min_rtt, &max_rtt));
}
+TEST_F(RtpRtcpImplTest, SetRtcpXrRrtrStatus) {
+ EXPECT_FALSE(rtp_rtcp_impl_->RtcpXrRrtrStatus());
+ rtp_rtcp_impl_->SetRtcpXrRrtrStatus(true);
+ EXPECT_TRUE(rtp_rtcp_impl_->RtcpXrRrtrStatus());
+}
+
TEST_F(RtpRtcpImplTest, RttForReceiverOnly) {
rtp_rtcp_impl_->SetRtcpXrRrtrStatus(true);
EXPECT_EQ(0, rtp_rtcp_impl_->SetSendingStatus(false));
@@ -136,10 +145,12 @@
EXPECT_EQ(0, rtp_rtcp_impl_->SendRTCP(kRtcpReport));
// Verify RTT.
- EXPECT_EQ(0U, rtt_stats_.rtt_ms_);
+ EXPECT_EQ(0U, rtt_stats_.LastProcessedRtt());
+ EXPECT_EQ(0U, rtp_rtcp_impl_->rtt_ms());
rtp_rtcp_impl_->Process();
- EXPECT_EQ(100U, rtt_stats_.rtt_ms_);
+ EXPECT_EQ(100U, rtt_stats_.LastProcessedRtt());
+ EXPECT_EQ(100U, rtp_rtcp_impl_->rtt_ms());
}
} // namespace webrtc
diff --git a/modules/rtp_rtcp/source/rtp_sender.cc b/modules/rtp_rtcp/source/rtp_sender.cc
index 5c63b58..0031a0e 100644
--- a/modules/rtp_rtcp/source/rtp_sender.cc
+++ b/modules/rtp_rtcp/source/rtp_sender.cc
@@ -60,9 +60,9 @@
packets_sent_(0), payload_bytes_sent_(0), start_time_stamp_forced_(false),
start_time_stamp_(0), ssrc_db_(*SSRCDatabase::GetSSRCDatabase()),
remote_ssrc_(0), sequence_number_forced_(false), ssrc_forced_(false),
- timestamp_(0), capture_time_ms_(0), last_packet_marker_bit_(false),
- num_csrcs_(0), csrcs_(), include_csrcs_(true),
- rtx_(kRtxOff), payload_type_rtx_(-1) {
+ timestamp_(0), capture_time_ms_(0), last_timestamp_time_ms_(0),
+ last_packet_marker_bit_(false), num_csrcs_(0), csrcs_(),
+ include_csrcs_(true), rtx_(kRtxOff), payload_type_rtx_(-1) {
memset(nack_byte_count_times_, 0, sizeof(nack_byte_count_times_));
memset(nack_byte_count_, 0, sizeof(nack_byte_count_));
memset(csrcs_, 0, sizeof(csrcs_));
@@ -419,6 +419,7 @@
timestamp = start_time_stamp_ + capture_timestamp;
timestamp_ = timestamp;
capture_time_ms_ = capture_time_ms;
+ last_timestamp_time_ms_ = clock_->TimeInMilliseconds();
}
int bytes_sent = SendPadData(payload_type, timestamp, capture_time_ms,
bytes, kDontRetransmit, false, false);
@@ -771,6 +772,12 @@
payload_type = (rtx_ == kRtxOff) ? payload_type_ : payload_type_rtx_;
timestamp = timestamp_;
capture_time_ms = capture_time_ms_;
+ if (last_timestamp_time_ms_ > 0) {
+ timestamp +=
+ (clock_->TimeInMilliseconds() - last_timestamp_time_ms_) * 90;
+ capture_time_ms +=
+ (clock_->TimeInMilliseconds() - last_timestamp_time_ms_);
+ }
}
return SendPadData(payload_type, timestamp, capture_time_ms, bytes,
kDontStore, true, true);
@@ -941,6 +948,7 @@
// timing.
timestamp_++;
}
+ last_timestamp_time_ms_ = clock_->TimeInMilliseconds();
uint32_t sequence_number = sequence_number_++;
capture_time_ms_ = capture_time_ms;
last_packet_marker_bit_ = marker_bit;
diff --git a/modules/rtp_rtcp/source/rtp_sender.h b/modules/rtp_rtcp/source/rtp_sender.h
index afa57f1..6a3bacc 100644
--- a/modules/rtp_rtcp/source/rtp_sender.h
+++ b/modules/rtp_rtcp/source/rtp_sender.h
@@ -331,6 +331,7 @@
uint32_t ssrc_;
uint32_t timestamp_;
int64_t capture_time_ms_;
+ int64_t last_timestamp_time_ms_;
bool last_packet_marker_bit_;
uint8_t num_csrcs_;
uint32_t csrcs_[kRtpCsrcSize];
diff --git a/modules/rtp_rtcp/source/rtp_sender_unittest.cc b/modules/rtp_rtcp/source/rtp_sender_unittest.cc
index b1f1b50..4320a0d 100644
--- a/modules/rtp_rtcp/source/rtp_sender_unittest.cc
+++ b/modules/rtp_rtcp/source/rtp_sender_unittest.cc
@@ -15,6 +15,7 @@
#include "testing/gtest/include/gtest/gtest.h"
#include "webrtc/modules/pacing/include/mock/mock_paced_sender.h"
+#include "webrtc/modules/rtp_rtcp/interface/rtp_header_parser.h"
#include "webrtc/modules/rtp_rtcp/interface/rtp_rtcp_defines.h"
#include "webrtc/modules/rtp_rtcp/source/rtp_format_video_generic.h"
#include "webrtc/modules/rtp_rtcp/source/rtp_header_extension.h"
@@ -52,6 +53,10 @@
return static_cast<uint16_t>(length);
}
+uint64_t ConvertMsToAbsSendTime(int64_t time_ms) {
+ return 0x00fffffful & ((time_ms << 18) / 1000);
+}
+
class LoopbackTransportTest : public webrtc::Transport {
public:
LoopbackTransportTest()
@@ -392,7 +397,7 @@
// Verify transmission time offset.
EXPECT_EQ(kStoredTimeInMs * 90, rtp_header.extension.transmissionTimeOffset);
uint64_t expected_send_time =
- 0x00fffffful & ((fake_clock_.TimeInMilliseconds() << 18) / 1000);
+ ConvertMsToAbsSendTime(fake_clock_.TimeInMilliseconds());
EXPECT_EQ(expected_send_time, rtp_header.extension.absoluteSendTime);
}
@@ -454,7 +459,126 @@
// Verify transmission time offset.
EXPECT_EQ(kStoredTimeInMs * 90, rtp_header.extension.transmissionTimeOffset);
uint64_t expected_send_time =
- 0x00fffffful & ((fake_clock_.TimeInMilliseconds() << 18) / 1000);
+ ConvertMsToAbsSendTime(fake_clock_.TimeInMilliseconds());
+ EXPECT_EQ(expected_send_time, rtp_header.extension.absoluteSendTime);
+}
+
+// This test sends 1 regular video packet, then 4 padding packets, and then
+// 1 more regular packet.
+TEST_F(RtpSenderTest, SendPadding) {
+ // Make all (non-padding) packets go to send queue.
+ EXPECT_CALL(mock_paced_sender_,
+ SendPacket(PacedSender::kNormalPriority, _, _, _, _, _)).
+ WillRepeatedly(testing::Return(false));
+
+ uint16_t seq_num = kSeqNum;
+ uint32_t timestamp = kTimestamp;
+ rtp_sender_->SetStorePacketsStatus(true, 10);
+ int rtp_header_len = 12;
+ EXPECT_EQ(0, rtp_sender_->RegisterRtpHeaderExtension(
+ kRtpExtensionTransmissionTimeOffset, kTransmissionTimeOffsetExtensionId));
+ rtp_header_len += 4; // 4 bytes extension.
+ EXPECT_EQ(0, rtp_sender_->RegisterRtpHeaderExtension(
+ kRtpExtensionAbsoluteSendTime, kAbsoluteSendTimeExtensionId));
+ rtp_header_len += 4; // 4 bytes extension.
+ rtp_header_len += 4; // 4 extra bytes common to all extension headers.
+
+ // Create and set up parser.
+ scoped_ptr<webrtc::RtpHeaderParser> rtp_parser(
+ webrtc::RtpHeaderParser::Create());
+ ASSERT_TRUE(rtp_parser.get() != NULL);
+ rtp_parser->RegisterRtpHeaderExtension(kRtpExtensionTransmissionTimeOffset,
+ kTransmissionTimeOffsetExtensionId);
+ rtp_parser->RegisterRtpHeaderExtension(kRtpExtensionAbsoluteSendTime,
+ kAbsoluteSendTimeExtensionId);
+ webrtc::RTPHeader rtp_header;
+
+ rtp_sender_->SetTargetSendBitrate(300000);
+ int64_t capture_time_ms = fake_clock_.TimeInMilliseconds();
+ int32_t rtp_length = rtp_sender_->BuildRTPheader(packet_,
+ kPayload,
+ kMarkerBit,
+ timestamp,
+ capture_time_ms);
+
+ // Packet should be stored in a send bucket.
+ EXPECT_EQ(0, rtp_sender_->SendToNetwork(packet_,
+ 0,
+ rtp_length,
+ capture_time_ms,
+ kAllowRetransmission,
+ PacedSender::kNormalPriority));
+
+ int total_packets_sent = 0;
+ EXPECT_EQ(total_packets_sent, transport_.packets_sent_);
+
+ const int kStoredTimeInMs = 100;
+ fake_clock_.AdvanceTimeMilliseconds(kStoredTimeInMs);
+ rtp_sender_->TimeToSendPacket(seq_num++, capture_time_ms, false);
+ // Packet should now be sent. This test doesn't verify the regular video
+ // packet, since it is tested in another test.
+ EXPECT_EQ(++total_packets_sent, transport_.packets_sent_);
+ timestamp += 90 * kStoredTimeInMs;
+
+ // Send padding 4 times, waiting 50 ms between each.
+ for (int i = 0; i < 4; ++i) {
+ const int kPaddingPeriodMs = 50;
+ const int kPaddingBytes = 100;
+ const int kMaxPaddingLength = 224; // Value taken from rtp_sender.cc.
+ // Padding will be forced to full packets.
+ EXPECT_EQ(kMaxPaddingLength, rtp_sender_->TimeToSendPadding(kPaddingBytes));
+
+ // Process send bucket. Padding should now be sent.
+ EXPECT_EQ(++total_packets_sent, transport_.packets_sent_);
+ EXPECT_EQ(kMaxPaddingLength + rtp_header_len,
+ transport_.last_sent_packet_len_);
+ // Parse sent packet.
+ ASSERT_TRUE(rtp_parser->Parse(transport_.last_sent_packet_, kPaddingBytes,
+ &rtp_header));
+
+ // Verify sequence number and timestamp.
+ EXPECT_EQ(seq_num++, rtp_header.sequenceNumber);
+ EXPECT_EQ(timestamp, rtp_header.timestamp);
+ // Verify transmission time offset.
+ EXPECT_EQ(0, rtp_header.extension.transmissionTimeOffset);
+ uint64_t expected_send_time =
+ ConvertMsToAbsSendTime(fake_clock_.TimeInMilliseconds());
+ EXPECT_EQ(expected_send_time, rtp_header.extension.absoluteSendTime);
+ fake_clock_.AdvanceTimeMilliseconds(kPaddingPeriodMs);
+ timestamp += 90 * kPaddingPeriodMs;
+ }
+
+ // Send a regular video packet again.
+ capture_time_ms = fake_clock_.TimeInMilliseconds();
+ rtp_length = rtp_sender_->BuildRTPheader(packet_,
+ kPayload,
+ kMarkerBit,
+ timestamp,
+ capture_time_ms);
+
+ // Packet should be stored in a send bucket.
+ EXPECT_EQ(0, rtp_sender_->SendToNetwork(packet_,
+ 0,
+ rtp_length,
+ capture_time_ms,
+ kAllowRetransmission,
+ PacedSender::kNormalPriority));
+
+ rtp_sender_->TimeToSendPacket(seq_num, capture_time_ms, false);
+ // Process send bucket.
+ EXPECT_EQ(++total_packets_sent, transport_.packets_sent_);
+ EXPECT_EQ(rtp_length, transport_.last_sent_packet_len_);
+ // Parse sent packet.
+ ASSERT_TRUE(rtp_parser->Parse(transport_.last_sent_packet_, rtp_length,
+ &rtp_header));
+
+ // Verify sequence number and timestamp.
+ EXPECT_EQ(seq_num, rtp_header.sequenceNumber);
+ EXPECT_EQ(timestamp, rtp_header.timestamp);
+ // Verify transmission time offset. This packet is sent without delay.
+ EXPECT_EQ(0, rtp_header.extension.transmissionTimeOffset);
+ uint64_t expected_send_time =
+ ConvertMsToAbsSendTime(fake_clock_.TimeInMilliseconds());
EXPECT_EQ(expected_send_time, rtp_header.extension.absoluteSendTime);
}
diff --git a/modules/video_capture/include/video_capture_defines.h b/modules/video_capture/include/video_capture_defines.h
index c592633..330bfc7 100644
--- a/modules/video_capture/include/video_capture_defines.h
+++ b/modules/video_capture/include/video_capture_defines.h
@@ -84,33 +84,6 @@
Cleared = 1
};
-// VideoFrameI420 doesn't take the ownership of the buffer.
-// It's mostly used to group the parameters for external capture.
-struct VideoFrameI420
-{
- VideoFrameI420() {
- y_plane = NULL;
- u_plane = NULL;
- v_plane = NULL;
- y_pitch = 0;
- u_pitch = 0;
- v_pitch = 0;
- width = 0;
- height = 0;
- }
-
- unsigned char* y_plane;
- unsigned char* u_plane;
- unsigned char* v_plane;
-
- int y_pitch;
- int u_pitch;
- int v_pitch;
-
- unsigned short width;
- unsigned short height;
-};
-
/* External Capture interface. Returned by Create
and implemented by the capture module.
*/
@@ -122,8 +95,9 @@
int32_t videoFrameLength,
const VideoCaptureCapability& frameInfo,
int64_t captureTime = 0) = 0;
- virtual int32_t IncomingFrameI420(const VideoFrameI420& video_frame,
- int64_t captureTime = 0) = 0;
+ virtual int32_t IncomingI420VideoFrame(I420VideoFrame* video_frame,
+ int64_t captureTime = 0) = 0;
+
protected:
~VideoCaptureExternal() {}
};
diff --git a/modules/video_capture/test/video_capture_unittest.cc b/modules/video_capture/test/video_capture_unittest.cc
index b047bee..0f8052e 100644
--- a/modules/video_capture/test/video_capture_unittest.cc
+++ b/modules/video_capture/test/video_capture_unittest.cc
@@ -84,60 +84,6 @@
return true;
}
-// Compares the content of a I420 frame in planar form and the new video frame.
-static bool CompareFrames(const webrtc::VideoFrameI420& frame1,
- const webrtc::I420VideoFrame& frame2) {
- if (frame1.width != frame2.width() ||
- frame1.height != frame2.height()) {
- return false;
- }
-
- // Compare Y
- const unsigned char* y_plane = frame1.y_plane;
- const unsigned char* y_plane2 = frame2.buffer(webrtc::kYPlane);
- for (int i = 0; i < frame2.height(); ++i) {
- for (int j = 0; j < frame2.width(); ++j) {
- if (*y_plane != *y_plane2)
- return false;
- ++y_plane;
- ++y_plane2;
- }
- y_plane += frame1.y_pitch - frame1.width;
- y_plane2 += frame2.stride(webrtc::kYPlane) - frame2.width();
- }
-
- // Compare U
- const unsigned char* u_plane = frame1.u_plane;
- const unsigned char* u_plane2 = frame2.buffer(webrtc::kUPlane);
- for (int i = 0; i < (frame2.height() + 1) / 2; ++i) {
- for (int j = 0; j < (frame2.width() + 1) / 2; ++j) {
- if (*u_plane != *u_plane2)
- return false;
- ++u_plane;
- ++u_plane2;
- }
- u_plane += frame1.u_pitch - (frame1.width + 1) / 2;
- u_plane2+= frame2.stride(webrtc::kUPlane) - (frame2.width() + 1) / 2;
- }
-
- // Compare V
- unsigned char* v_plane = frame1.v_plane;
- const unsigned char* v_plane2 = frame2.buffer(webrtc::kVPlane);
- for (int i = 0; i < frame2.height() /2; ++i) {
- for (int j = 0; j < frame2.width() /2; ++j) {
- if (*u_plane != *u_plane2) {
- return false;
- }
- ++v_plane;
- ++v_plane2;
- }
- v_plane += frame1.v_pitch - (frame1.width + 1) / 2;
- u_plane2+= frame2.stride(webrtc::kVPlane) - (frame2.width() + 1) / 2;
- }
- return true;
-}
-
-
class TestVideoCaptureCallback : public VideoCaptureDataCallback {
public:
TestVideoCaptureCallback()
@@ -229,11 +175,6 @@
return CompareFrames(last_frame_, frame);
}
- bool CompareLastFrame(const webrtc::VideoFrameI420& frame) {
- CriticalSectionScoped cs(capture_cs_.get());
- return CompareFrames(frame, last_frame_);
- }
-
void SetExpectedCaptureRotation(webrtc::VideoCaptureRotation rotation) {
CriticalSectionScoped cs(capture_cs_.get());
rotate_frame_ = rotation;
@@ -503,16 +444,11 @@
// NOTE: flaky, sometimes fails on the last CompareLastFrame.
// http://code.google.com/p/webrtc/issues/detail?id=777
TEST_F(VideoCaptureExternalTest, DISABLED_TestExternalCaptureI420) {
- webrtc::VideoFrameI420 frame_i420;
- frame_i420.width = kTestWidth;
- frame_i420.height = kTestHeight;
- frame_i420.y_plane = test_frame_.buffer(webrtc::kYPlane);
- frame_i420.u_plane = frame_i420.y_plane + (kTestWidth * kTestHeight);
- frame_i420.v_plane = frame_i420.u_plane + ((kTestWidth * kTestHeight) >> 2);
- frame_i420.y_pitch = kTestWidth;
- frame_i420.u_pitch = kTestWidth / 2;
- frame_i420.v_pitch = kTestWidth / 2;
- EXPECT_EQ(0, capture_input_interface_->IncomingFrameI420(frame_i420, 0));
+ webrtc::I420VideoFrame frame_i420;
+ frame_i420.CopyFrame(test_frame_);
+
+ EXPECT_EQ(0,
+ capture_input_interface_->IncomingI420VideoFrame(&frame_i420, 0));
EXPECT_TRUE(capture_callback_.CompareLastFrame(frame_i420));
// Test with a frame with pitch not equal to width
@@ -566,16 +502,10 @@
current_pointer += v_pitch;
v_plane += uv_width;
}
- frame_i420.width = kTestWidth;
- frame_i420.height = kTestHeight;
- frame_i420.y_plane = aligned_test_frame.buffer(webrtc::kYPlane);
- frame_i420.u_plane = aligned_test_frame.buffer(webrtc::kYPlane);
- frame_i420.v_plane = aligned_test_frame.buffer(webrtc::kVPlane);
- frame_i420.y_pitch = y_pitch;
- frame_i420.u_pitch = u_pitch;
- frame_i420.v_pitch = v_pitch;
+ frame_i420.CopyFrame(aligned_test_frame);
- EXPECT_EQ(0, capture_input_interface_->IncomingFrameI420(frame_i420, 0));
+ EXPECT_EQ(0,
+ capture_input_interface_->IncomingI420VideoFrame(&frame_i420, 0));
EXPECT_TRUE(capture_callback_.CompareLastFrame(test_frame_));
}
diff --git a/modules/video_capture/video_capture_impl.cc b/modules/video_capture/video_capture_impl.cc
index a23d22b..6689fd1 100644
--- a/modules/video_capture/video_capture_impl.cc
+++ b/modules/video_capture/video_capture_impl.cc
@@ -358,27 +358,11 @@
return 0;
}
-int32_t VideoCaptureImpl::IncomingFrameI420(
- const VideoFrameI420& video_frame, int64_t captureTime) {
+int32_t VideoCaptureImpl::IncomingI420VideoFrame(I420VideoFrame* video_frame,
+ int64_t captureTime) {
CriticalSectionScoped cs(&_callBackCs);
- int size_y = video_frame.height * video_frame.y_pitch;
- int size_u = video_frame.u_pitch * ((video_frame.height + 1) / 2);
- int size_v = video_frame.v_pitch * ((video_frame.height + 1) / 2);
- // TODO(mikhal): Can we use Swap here? This will do a memcpy.
- int ret = _captureFrame.CreateFrame(size_y, video_frame.y_plane,
- size_u, video_frame.u_plane,
- size_v, video_frame.v_plane,
- video_frame.width, video_frame.height,
- video_frame.y_pitch, video_frame.u_pitch,
- video_frame.v_pitch);
- if (ret < 0) {
- WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, _id,
- "Failed to create I420VideoFrame");
- return -1;
- }
-
- DeliverCapturedFrame(_captureFrame, captureTime);
+ DeliverCapturedFrame(*video_frame, captureTime);
return 0;
}
diff --git a/modules/video_capture/video_capture_impl.h b/modules/video_capture/video_capture_impl.h
index 41f555c..80d5e67 100644
--- a/modules/video_capture/video_capture_impl.h
+++ b/modules/video_capture/video_capture_impl.h
@@ -86,9 +86,9 @@
int32_t videoFrameLength,
const VideoCaptureCapability& frameInfo,
int64_t captureTime = 0);
- virtual int32_t IncomingFrameI420(
- const VideoFrameI420& video_frame,
- int64_t captureTime = 0);
+
+ virtual int32_t IncomingI420VideoFrame(I420VideoFrame* video_frame,
+ int64_t captureTime = 0);
// Platform dependent
virtual int32_t StartCapture(const VideoCaptureCapability& capability)
diff --git a/modules/video_coding/main/interface/video_coding.h b/modules/video_coding/main/interface/video_coding.h
index 5a206e9..c016676 100644
--- a/modules/video_coding/main/interface/video_coding.h
+++ b/modules/video_coding/main/interface/video_coding.h
@@ -21,6 +21,7 @@
{
class Clock;
+class EncodedImageCallback;
class VideoEncoder;
class VideoDecoder;
struct CodecSpecificInfo;
@@ -592,17 +593,19 @@
// Disables recording of debugging information.
virtual int StopDebugRecording() = 0;
- // Enables AutoMuter to turn off video when the rate drops below
+ // Lets the sender suspend video when the rate drops below
// |threshold_bps|, and turns back on when the rate goes back up above
// |threshold_bps| + |window_bps|.
- virtual void EnableAutoMuting() = 0;
+ virtual void SuspendBelowMinBitrate() = 0;
- // Disables AutoMuter.
- virtual void DisableAutoMuting() = 0;
+ // Returns true if SuspendBelowMinBitrate is engaged and the video has been
+ // suspended due to bandwidth limitations; otherwise false.
+ virtual bool VideoSuspended() const = 0;
- // Returns true if AutoMuter is engaged and the video has been muted due to
- // bandwidth limitations; otherwise false.
- virtual bool VideoMuted() const = 0;
+ virtual void RegisterPreDecodeImageCallback(
+ EncodedImageCallback* observer) = 0;
+ virtual void RegisterPostEncodeImageCallback(
+ EncodedImageCallback* post_encode_callback) = 0;
};
} // namespace webrtc
diff --git a/modules/video_coding/main/source/generic_encoder.cc b/modules/video_coding/main/source/generic_encoder.cc
index c6ab9fb..064470b 100644
--- a/modules/video_coding/main/source/generic_encoder.cc
+++ b/modules/video_coding/main/source/generic_encoder.cc
@@ -12,6 +12,7 @@
#include "webrtc/modules/video_coding/main/source/encoded_frame.h"
#include "webrtc/modules/video_coding/main/source/generic_encoder.h"
#include "webrtc/modules/video_coding/main/source/media_optimization.h"
+#include "webrtc/system_wrappers/interface/critical_section_wrapper.h"
namespace webrtc {
@@ -147,7 +148,9 @@
_encodedBytes(0),
_payloadType(0),
_codecType(kVideoCodecUnknown),
-_internalSource(false)
+_internalSource(false),
+post_encode_callback_lock_(CriticalSectionWrapper::CreateCriticalSection()),
+post_encode_callback_(NULL)
#ifdef DEBUG_ENCODER_BIT_STREAM
, _bitStreamAfterEncoder(NULL)
#endif
@@ -177,6 +180,12 @@
const CodecSpecificInfo* codecSpecificInfo,
const RTPFragmentationHeader* fragmentationHeader)
{
+ {
+ CriticalSectionScoped cs(post_encode_callback_lock_.get());
+ if (post_encode_callback_) {
+ post_encode_callback_->Encoded(encodedImage);
+ }
+ }
FrameType frameType = VCMEncodedFrame::ConvertFrameType(encodedImage._frameType);
uint32_t encodedBytes = 0;
@@ -271,4 +280,10 @@
return;
}
}
+
+void VCMEncodedFrameCallback::RegisterPostEncodeImageCallback(
+ EncodedImageCallback* callback) {
+ CriticalSectionScoped cs(post_encode_callback_lock_.get());
+ post_encode_callback_ = callback;
+}
} // namespace webrtc
diff --git a/modules/video_coding/main/source/generic_encoder.h b/modules/video_coding/main/source/generic_encoder.h
index 0c2d287..c5cfeab 100644
--- a/modules/video_coding/main/source/generic_encoder.h
+++ b/modules/video_coding/main/source/generic_encoder.h
@@ -15,9 +15,13 @@
#include <stdio.h>
+#include "webrtc/system_wrappers/interface/scoped_ptr.h"
+
namespace webrtc
{
+class CriticalSectionWrapper;
+
namespace media_optimization {
class MediaOptimization;
} // namespace media_optimization
@@ -55,6 +59,8 @@
void SetCodecType(VideoCodecType codecType) {_codecType = codecType;};
void SetInternalSource(bool internalSource) { _internalSource = internalSource; };
+ void RegisterPostEncodeImageCallback(EncodedImageCallback* callback);
+
private:
/*
* Map information from info into rtp. If no relevant information is found
@@ -69,6 +75,10 @@
uint8_t _payloadType;
VideoCodecType _codecType;
bool _internalSource;
+
+ scoped_ptr<CriticalSectionWrapper> post_encode_callback_lock_;
+ EncodedImageCallback* post_encode_callback_;
+
#ifdef DEBUG_ENCODER_BIT_STREAM
FILE* _bitStreamAfterEncoder;
#endif
diff --git a/modules/video_coding/main/source/media_optimization.cc b/modules/video_coding/main/source/media_optimization.cc
index 815be36..37dff6c 100644
--- a/modules/video_coding/main/source/media_optimization.cc
+++ b/modules/video_coding/main/source/media_optimization.cc
@@ -47,10 +47,10 @@
last_qm_update_time_(0),
last_change_time_(0),
num_layers_(0),
- muting_enabled_(false),
- video_muted_(false),
- muter_threshold_bps_(0),
- muter_window_bps_(0) {
+ suspension_enabled_(false),
+ video_suspended_(false),
+ suspension_threshold_bps_(0),
+ suspension_window_bps_(0) {
memset(send_statistics_, 0, sizeof(send_statistics_));
memset(incoming_frame_times_, -1, sizeof(incoming_frame_times_));
}
@@ -193,7 +193,7 @@
content_->ResetShortTermAvgData();
}
- CheckAutoMuteConditions();
+ CheckSuspendConditions();
return target_bit_rate_;
}
@@ -351,7 +351,7 @@
bool MediaOptimization::DropFrame() {
// Leak appropriate number of bytes.
frame_dropper_->Leak((uint32_t)(InputFrameRate() + 0.5f));
- if (video_muted_) {
+ if (video_suspended_) {
return true; // Drop all frames when muted.
}
return frame_dropper_->DropFrame();
@@ -418,17 +418,13 @@
return VCM_OK;
}
-void MediaOptimization::EnableAutoMuting(int threshold_bps, int window_bps) {
+void MediaOptimization::SuspendBelowMinBitrate(int threshold_bps,
+ int window_bps) {
assert(threshold_bps > 0 && window_bps >= 0);
- muter_threshold_bps_ = threshold_bps;
- muter_window_bps_ = window_bps;
- muting_enabled_ = true;
- video_muted_ = false;
-}
-
-void MediaOptimization::DisableAutoMuting() {
- muting_enabled_ = false;
- video_muted_ = false;
+ suspension_threshold_bps_ = threshold_bps;
+ suspension_window_bps_ = window_bps;
+ suspension_enabled_ = true;
+ video_suspended_ = false;
}
// Private methods below this line.
@@ -605,19 +601,20 @@
}
}
-void MediaOptimization::CheckAutoMuteConditions() {
- // Check conditions for AutoMute. |target_bit_rate_| is in bps.
- if (muting_enabled_) {
- if (!video_muted_) {
+void MediaOptimization::CheckSuspendConditions() {
+ // Check conditions for SuspendBelowMinBitrate. |target_bit_rate_| is in bps.
+ if (suspension_enabled_) {
+ if (!video_suspended_) {
// Check if we just went below the threshold.
- if (target_bit_rate_ < muter_threshold_bps_) {
- video_muted_ = true;
+ if (target_bit_rate_ < suspension_threshold_bps_) {
+ video_suspended_ = true;
}
} else {
- // Video is already muted. Check if we just went over the threshold
+ // Video is already suspended. Check if we just went over the threshold
// with a margin.
- if (target_bit_rate_ > muter_threshold_bps_ + muter_window_bps_) {
- video_muted_ = false;
+ if (target_bit_rate_ >
+ suspension_threshold_bps_ + suspension_window_bps_) {
+ video_suspended_ = false;
}
}
}
diff --git a/modules/video_coding/main/source/media_optimization.h b/modules/video_coding/main/source/media_optimization.h
index ca383bf..cde28d2 100644
--- a/modules/video_coding/main/source/media_optimization.h
+++ b/modules/video_coding/main/source/media_optimization.h
@@ -120,18 +120,15 @@
// Computes new Quality Mode.
int32_t SelectQuality();
- // Enables AutoMuter to turn off video when the rate drops below
+ // Lets the sender suspend video when the rate drops below
// |threshold_bps|, and turns back on when the rate goes back up above
// |threshold_bps| + |window_bps|.
- void EnableAutoMuting(int threshold_bps, int window_bps);
-
- // Disables AutoMuter.
- void DisableAutoMuting();
+ void SuspendBelowMinBitrate(int threshold_bps, int window_bps);
// Accessors and mutators.
int32_t max_bit_rate() const { return max_bit_rate_; }
void set_max_payload_size(int32_t mtu) { max_payload_size_ = mtu; }
- bool video_muted() const { return video_muted_; }
+ bool video_suspended() const { return video_suspended_; }
private:
typedef std::list<EncodedFrameSample> FrameSampleList;
@@ -161,10 +158,10 @@
void ProcessIncomingFrameRate(int64_t now);
- // Checks conditions for AutoMute. The method compares |target_bit_rate_|
- // with the threshold values for AutoMute, and changes the state of
- // |video_muted_| accordingly.
- void CheckAutoMuteConditions();
+ // Checks conditions for suspending the video. The method compares
+ // |target_bit_rate_| with the threshold values for suspension, and changes
+ // the state of |video_suspended_| accordingly.
+ void CheckSuspendConditions();
int32_t id_;
Clock* clock_;
@@ -195,10 +192,10 @@
int64_t last_qm_update_time_;
int64_t last_change_time_; // Content/user triggered.
int num_layers_;
- bool muting_enabled_;
- bool video_muted_;
- int muter_threshold_bps_;
- int muter_window_bps_;
+ bool suspension_enabled_;
+ bool video_suspended_;
+ int suspension_threshold_bps_;
+ int suspension_window_bps_;
}; // End of MediaOptimization class declaration.
} // namespace media_optimization
diff --git a/modules/video_coding/main/source/media_optimization_unittest.cc b/modules/video_coding/main/source/media_optimization_unittest.cc
index fc4fd75..1425dad 100644
--- a/modules/video_coding/main/source/media_optimization_unittest.cc
+++ b/modules/video_coding/main/source/media_optimization_unittest.cc
@@ -55,15 +55,15 @@
TEST_F(TestMediaOptimization, VerifyMuting) {
- // Enable video muter with these limits.
- // Mute the video when the rate is below 50 kbps and unmute when it gets above
- // 50 + 10 kbps again.
+ // Enable video suspension with these limits.
+ // Suspend the video when the rate is below 50 kbps and resume when it gets
+ // above 50 + 10 kbps again.
const int kThresholdBps = 50000;
const int kWindowBps = 10000;
- media_opt_.EnableAutoMuting(kThresholdBps, kWindowBps);
+ media_opt_.SuspendBelowMinBitrate(kThresholdBps, kWindowBps);
- // The video should not be muted from the start.
- EXPECT_FALSE(media_opt_.video_muted());
+ // The video should not be suspended from the start.
+ EXPECT_FALSE(media_opt_.video_suspended());
int target_bitrate_kbps = 100;
media_opt_.SetTargetRates(target_bitrate_kbps * 1000,
@@ -81,7 +81,7 @@
// Expect the muter to engage immediately and stay muted.
// Test during 2 seconds.
for (int time = 0; time < 2000; time += frame_time_ms_) {
- EXPECT_TRUE(media_opt_.video_muted());
+ EXPECT_TRUE(media_opt_.video_suspended());
ASSERT_NO_FATAL_FAILURE(AddFrameAndAdvanceTime(target_bitrate_kbps, true));
}
@@ -93,7 +93,7 @@
// Expect the muter to stay muted.
// Test during 2 seconds.
for (int time = 0; time < 2000; time += frame_time_ms_) {
- EXPECT_TRUE(media_opt_.video_muted());
+ EXPECT_TRUE(media_opt_.video_suspended());
ASSERT_NO_FATAL_FAILURE(AddFrameAndAdvanceTime(target_bitrate_kbps, true));
}
@@ -104,7 +104,7 @@
// Expect the muter to disengage immediately.
// Test during 2 seconds.
for (int time = 0; time < 2000; time += frame_time_ms_) {
- EXPECT_FALSE(media_opt_.video_muted());
+ EXPECT_FALSE(media_opt_.video_suspended());
ASSERT_NO_FATAL_FAILURE(
AddFrameAndAdvanceTime((kThresholdBps + kWindowBps) / 1000, false));
}
diff --git a/modules/video_coding/main/source/video_coding_impl.cc b/modules/video_coding/main/source/video_coding_impl.cc
index 9524984..1decc2f 100644
--- a/modules/video_coding/main/source/video_coding_impl.cc
+++ b/modules/video_coding/main/source/video_coding_impl.cc
@@ -197,16 +197,12 @@
return sender_->StopDebugRecording();
}
- virtual void EnableAutoMuting() {
- return sender_->EnableAutoMuting();
+ virtual void SuspendBelowMinBitrate() {
+ return sender_->SuspendBelowMinBitrate();
}
- virtual void DisableAutoMuting() {
- return sender_->DisableAutoMuting();
- }
-
- virtual bool VideoMuted() const {
- return sender_->VideoMuted();
+ virtual bool VideoSuspended() const {
+ return sender_->VideoSuspended();
}
virtual int32_t InitializeReceiver() OVERRIDE {
@@ -323,6 +319,16 @@
return receiver_->SetReceiveChannelParameters(rtt);
}
+ virtual void RegisterPreDecodeImageCallback(
+ EncodedImageCallback* observer) OVERRIDE {
+ receiver_->RegisterPreDecodeImageCallback(observer);
+ }
+
+ virtual void RegisterPostEncodeImageCallback(
+ EncodedImageCallback* observer) OVERRIDE {
+ sender_->RegisterPostEncodeImageCallback(observer);
+ }
+
private:
scoped_ptr<vcm::VideoSender> sender_;
scoped_ptr<vcm::VideoReceiver> receiver_;
diff --git a/modules/video_coding/main/source/video_coding_impl.h b/modules/video_coding/main/source/video_coding_impl.h
index f9a7973..d9564c0 100644
--- a/modules/video_coding/main/source/video_coding_impl.h
+++ b/modules/video_coding/main/source/video_coding_impl.h
@@ -27,6 +27,9 @@
#include "webrtc/system_wrappers/interface/critical_section_wrapper.h"
namespace webrtc {
+
+class EncodedFrameObserver;
+
namespace vcm {
class VCMProcessTimer {
@@ -95,9 +98,11 @@
int StartDebugRecording(const char* file_name_utf8);
int StopDebugRecording();
- void EnableAutoMuting();
- void DisableAutoMuting();
- bool VideoMuted() const;
+ void SuspendBelowMinBitrate();
+ bool VideoSuspended() const;
+
+ void RegisterPostEncodeImageCallback(
+ EncodedImageCallback* post_encode_callback);
int32_t TimeUntilNextProcess();
int32_t Process();
@@ -174,6 +179,8 @@
int32_t TimeUntilNextProcess();
int32_t Process();
+ void RegisterPreDecodeImageCallback(EncodedImageCallback* observer);
+
protected:
int32_t Decode(const webrtc::VCMEncodedFrame& frame);
int32_t RequestKeyFrame();
@@ -214,6 +221,7 @@
VCMKeyRequestMode _keyRequestMode;
bool _scheduleKeyRequest;
size_t max_nack_list_size_;
+ EncodedImageCallback* pre_decode_image_callback_;
VCMCodecDataBase _codecDataBase;
VCMProcessTimer _receiveStatsTimer;
diff --git a/modules/video_coding/main/source/video_receiver.cc b/modules/video_coding/main/source/video_receiver.cc
index cea8b44..68668ea 100644
--- a/modules/video_coding/main/source/video_receiver.cc
+++ b/modules/video_coding/main/source/video_receiver.cc
@@ -52,6 +52,7 @@
_keyRequestMode(kKeyOnError),
_scheduleKeyRequest(false),
max_nack_list_size_(0),
+ pre_decode_image_callback_(NULL),
_codecDataBase(id),
_receiveStatsTimer(1000, clock_),
_retransmissionTimer(10, clock_),
@@ -400,6 +401,11 @@
_timing.UpdateCurrentDelay(frame->RenderTimeMs(),
clock_->TimeInMilliseconds());
+ if (pre_decode_image_callback_) {
+ EncodedImage encoded_image(frame->EncodedImage());
+ pre_decode_image_callback_->Encoded(encoded_image);
+ }
+
#ifdef DEBUG_DECODER_BIT_STREAM
if (_bitStreamBeforeDecoder != NULL) {
// Write bit stream to file for debugging purposes
@@ -815,5 +821,11 @@
return _receiver.SetMinReceiverDelay(desired_delay_ms);
}
+void VideoReceiver::RegisterPreDecodeImageCallback(
+ EncodedImageCallback* observer) {
+ CriticalSectionScoped cs(_receiveCritSect);
+ pre_decode_image_callback_ = observer;
+}
+
} // namespace vcm
} // namespace webrtc
diff --git a/modules/video_coding/main/source/video_sender.cc b/modules/video_coding/main/source/video_sender.cc
index 4cbcc89..948218b 100644
--- a/modules/video_coding/main/source/video_sender.cc
+++ b/modules/video_coding/main/source/video_sender.cc
@@ -422,11 +422,11 @@
return VCM_OK;
}
-void VideoSender::EnableAutoMuting() {
+void VideoSender::SuspendBelowMinBitrate() {
CriticalSectionScoped cs(_sendCritSect);
VideoCodec current_send_codec;
if (SendCodec(¤t_send_codec) != 0) {
- assert(false); // Must set a send codec before enabling auto-mute.
+ assert(false); // Must set a send codec before SuspendBelowMinBitrate.
return;
}
int threshold_bps;
@@ -438,17 +438,18 @@
// Set the hysteresis window to be at 10% of the threshold, but at least
// 10 kbps.
int window_bps = std::max(threshold_bps / 10, 10000);
- _mediaOpt.EnableAutoMuting(threshold_bps, window_bps);
+ _mediaOpt.SuspendBelowMinBitrate(threshold_bps, window_bps);
}
-void VideoSender::DisableAutoMuting() {
+bool VideoSender::VideoSuspended() const {
CriticalSectionScoped cs(_sendCritSect);
- _mediaOpt.DisableAutoMuting();
+ return _mediaOpt.video_suspended();
}
-bool VideoSender::VideoMuted() const {
+void VideoSender::RegisterPostEncodeImageCallback(
+ EncodedImageCallback* observer) {
CriticalSectionScoped cs(_sendCritSect);
- return _mediaOpt.video_muted();
+ _encodedFrameCallback.RegisterPostEncodeImageCallback(observer);
}
} // namespace vcm
diff --git a/modules/video_processing/main/test/unit_test/video_processing_unittest.cc b/modules/video_processing/main/test/unit_test/video_processing_unittest.cc
index 89c59ec..6e54923 100644
--- a/modules/video_processing/main/test/unit_test/video_processing_unittest.cc
+++ b/modules/video_processing/main/test/unit_test/video_processing_unittest.cc
@@ -45,7 +45,10 @@
ASSERT_EQ(0, video_frame_.CreateEmptyFrame(width_, height_, width_,
half_width_, half_width_));
-
+ // Clear video frame so DrMemory/Valgrind will allow reads of the buffer.
+ memset(video_frame_.buffer(kYPlane), 0, video_frame_.allocated_size(kYPlane));
+ memset(video_frame_.buffer(kUPlane), 0, video_frame_.allocated_size(kUPlane));
+ memset(video_frame_.buffer(kVPlane), 0, video_frame_.allocated_size(kVPlane));
const std::string video_file =
webrtc::test::ResourcePath("foreman_cif", "yuv");
source_file_ = fopen(video_file.c_str(),"rb");
diff --git a/test/direct_transport.cc b/test/direct_transport.cc
index 8c7e351..7b3ff22 100644
--- a/test/direct_transport.cc
+++ b/test/direct_transport.cc
@@ -12,6 +12,7 @@
#include "testing/gtest/include/gtest/gtest.h"
#include "webrtc/call.h"
+#include "webrtc/system_wrappers/interface/clock.h"
namespace webrtc {
namespace test {
@@ -20,8 +21,22 @@
: lock_(CriticalSectionWrapper::CreateCriticalSection()),
packet_event_(EventWrapper::Create()),
thread_(ThreadWrapper::CreateThread(NetworkProcess, this)),
+ clock_(Clock::GetRealTimeClock()),
shutting_down_(false),
- receiver_(NULL) {
+ receiver_(NULL),
+ delay_ms_(0) {
+ unsigned int thread_id;
+ EXPECT_TRUE(thread_->Start(thread_id));
+}
+
+DirectTransport::DirectTransport(int delay_ms)
+ : lock_(CriticalSectionWrapper::CreateCriticalSection()),
+ packet_event_(EventWrapper::Create()),
+ thread_(ThreadWrapper::CreateThread(NetworkProcess, this)),
+ clock_(Clock::GetRealTimeClock()),
+ shutting_down_(false),
+ receiver_(NULL),
+ delay_ms_(delay_ms) {
unsigned int thread_id;
EXPECT_TRUE(thread_->Start(thread_id));
}
@@ -42,28 +57,33 @@
receiver_ = receiver;
}
-bool DirectTransport::SendRTP(const uint8_t* data, size_t length) {
- QueuePacket(data, length);
+bool DirectTransport::SendRtp(const uint8_t* data, size_t length) {
+ QueuePacket(data, length, clock_->TimeInMilliseconds() + delay_ms_);
return true;
}
-bool DirectTransport::SendRTCP(const uint8_t* data, size_t length) {
- QueuePacket(data, length);
+bool DirectTransport::SendRtcp(const uint8_t* data, size_t length) {
+ QueuePacket(data, length, clock_->TimeInMilliseconds() + delay_ms_);
return true;
}
-DirectTransport::Packet::Packet() : length(0) {}
+DirectTransport::Packet::Packet() : length(0), delivery_time_ms(0) {}
-DirectTransport::Packet::Packet(const uint8_t* data, size_t length)
- : length(length) {
+DirectTransport::Packet::Packet(const uint8_t* data,
+ size_t length,
+ int64_t delivery_time_ms)
+ : length(length), delivery_time_ms(delivery_time_ms) {
EXPECT_LE(length, sizeof(this->data));
memcpy(this->data, data, length);
}
-void DirectTransport::QueuePacket(const uint8_t* data, size_t length) {
+void DirectTransport::QueuePacket(const uint8_t* data,
+ size_t length,
+ int64_t delivery_time_ms) {
CriticalSectionScoped crit(lock_.get());
- EXPECT_TRUE(receiver_ != NULL);
- packet_queue_.push_back(Packet(data, length));
+ if (receiver_ == NULL)
+ return;
+ packet_queue_.push_back(Packet(data, length, delivery_time_ms));
packet_event_->Set();
}
@@ -79,12 +99,27 @@
if (packet_queue_.empty())
break;
p = packet_queue_.front();
+ if (p.delivery_time_ms > clock_->TimeInMilliseconds())
+ break;
packet_queue_.pop_front();
}
receiver_->DeliverPacket(p.data, p.length);
}
+ uint32_t time_until_next_delivery = WEBRTC_EVENT_INFINITE;
+ {
+ CriticalSectionScoped crit(lock_.get());
+ if (!packet_queue_.empty()) {
+ int64_t now_ms = clock_->TimeInMilliseconds();
+ const int64_t delivery_time_ms = packet_queue_.front().delivery_time_ms;
+ if (delivery_time_ms > now_ms) {
+ time_until_next_delivery = delivery_time_ms - now_ms;
+ } else {
+ time_until_next_delivery = 0;
+ }
+ }
+ }
- switch (packet_event_->Wait(WEBRTC_EVENT_INFINITE)) {
+ switch (packet_event_->Wait(time_until_next_delivery)) {
case kEventSignaled:
packet_event_->Reset();
break;
diff --git a/test/direct_transport.h b/test/direct_transport.h
index d4cb45a..b6021cb 100644
--- a/test/direct_transport.h
+++ b/test/direct_transport.h
@@ -22,6 +22,7 @@
namespace webrtc {
+class Clock;
class PacketReceiver;
namespace test {
@@ -29,24 +30,28 @@
class DirectTransport : public newapi::Transport {
public:
DirectTransport();
+ explicit DirectTransport(int delay_ms);
~DirectTransport();
virtual void StopSending();
virtual void SetReceiver(PacketReceiver* receiver);
- virtual bool SendRTP(const uint8_t* data, size_t length) OVERRIDE;
- virtual bool SendRTCP(const uint8_t* data, size_t length) OVERRIDE;
+ virtual bool SendRtp(const uint8_t* data, size_t length) OVERRIDE;
+ virtual bool SendRtcp(const uint8_t* data, size_t length) OVERRIDE;
private:
struct Packet {
Packet();
- Packet(const uint8_t* data, size_t length);
+ Packet(const uint8_t* data, size_t length, int64_t delivery_time_ms);
uint8_t data[1500];
size_t length;
+ int64_t delivery_time_ms;
};
- void QueuePacket(const uint8_t* data, size_t length);
+ void QueuePacket(const uint8_t* data,
+ size_t length,
+ int64_t delivery_time_ms);
static bool NetworkProcess(void* transport);
bool SendPackets();
@@ -54,11 +59,14 @@
scoped_ptr<CriticalSectionWrapper> lock_;
scoped_ptr<EventWrapper> packet_event_;
scoped_ptr<ThreadWrapper> thread_;
+ Clock* clock_;
bool shutting_down_;
std::deque<Packet> packet_queue_;
PacketReceiver* receiver_;
+ // TODO(stefan): Replace this with FakeNetworkPipe.
+ const int delay_ms_;
};
} // namespace test
} // namespace webrtc
diff --git a/test/fake_audio_device.cc b/test/fake_audio_device.cc
new file mode 100644
index 0000000..a6fe165
--- /dev/null
+++ b/test/fake_audio_device.cc
@@ -0,0 +1,146 @@
+/*
+ * Copyright (c) 2013 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/test/fake_audio_device.h"
+
+#include <algorithm>
+
+#include "testing/gtest/include/gtest/gtest.h"
+#include "webrtc/modules/media_file/source/media_file_utility.h"
+#include "webrtc/system_wrappers/interface/clock.h"
+#include "webrtc/system_wrappers/interface/critical_section_wrapper.h"
+#include "webrtc/system_wrappers/interface/event_wrapper.h"
+#include "webrtc/system_wrappers/interface/file_wrapper.h"
+#include "webrtc/system_wrappers/interface/thread_wrapper.h"
+
+namespace webrtc {
+namespace test {
+
+FakeAudioDevice::FakeAudioDevice(Clock* clock, const std::string& filename)
+ : audio_callback_(NULL),
+ capturing_(false),
+ captured_audio_(),
+ playout_buffer_(),
+ last_playout_ms_(-1),
+ clock_(clock),
+ tick_(EventWrapper::Create()),
+ lock_(CriticalSectionWrapper::CreateCriticalSection()),
+ file_utility_(new ModuleFileUtility(0)),
+ input_stream_(FileWrapper::Create()) {
+ memset(captured_audio_, 0, sizeof(captured_audio_));
+ memset(playout_buffer_, 0, sizeof(playout_buffer_));
+ // Open audio input file as read-only and looping.
+ EXPECT_EQ(0, input_stream_->OpenFile(filename.c_str(), true, true))
+ << filename;
+}
+
+FakeAudioDevice::~FakeAudioDevice() {
+ Stop();
+
+ if (thread_.get() != NULL)
+ thread_->Stop();
+}
+
+int32_t FakeAudioDevice::Init() {
+ CriticalSectionScoped cs(lock_.get());
+ if (file_utility_->InitPCMReading(*input_stream_.get()) != 0)
+ return -1;
+
+ if (!tick_->StartTimer(true, 10))
+ return -1;
+ thread_.reset(ThreadWrapper::CreateThread(
+ FakeAudioDevice::Run, this, webrtc::kHighPriority, "FakeAudioDevice"));
+ if (thread_.get() == NULL)
+ return -1;
+ unsigned int thread_id;
+ if (!thread_->Start(thread_id)) {
+ thread_.reset();
+ return -1;
+ }
+ return 0;
+}
+
+int32_t FakeAudioDevice::RegisterAudioCallback(AudioTransport* callback) {
+ CriticalSectionScoped cs(lock_.get());
+ audio_callback_ = callback;
+ return 0;
+}
+
+bool FakeAudioDevice::Playing() const {
+ CriticalSectionScoped cs(lock_.get());
+ return capturing_;
+}
+
+int32_t FakeAudioDevice::PlayoutDelay(uint16_t* delay_ms) const {
+ *delay_ms = 0;
+ return 0;
+}
+
+bool FakeAudioDevice::Recording() const {
+ CriticalSectionScoped cs(lock_.get());
+ return capturing_;
+}
+
+bool FakeAudioDevice::Run(void* obj) {
+ static_cast<FakeAudioDevice*>(obj)->CaptureAudio();
+ return true;
+}
+
+void FakeAudioDevice::CaptureAudio() {
+ {
+ CriticalSectionScoped cs(lock_.get());
+ if (capturing_) {
+ int bytes_read = file_utility_->ReadPCMData(
+ *input_stream_.get(), captured_audio_, kBufferSizeBytes);
+ if (bytes_read <= 0)
+ return;
+ int num_samples = bytes_read / 2; // 2 bytes per sample.
+ uint32_t new_mic_level;
+ EXPECT_EQ(0,
+ audio_callback_->RecordedDataIsAvailable(captured_audio_,
+ num_samples,
+ 2,
+ 1,
+ kFrequencyHz,
+ 0,
+ 0,
+ 0,
+ false,
+ new_mic_level));
+ uint32_t samples_needed = kFrequencyHz / 100;
+ int64_t now_ms = clock_->TimeInMilliseconds();
+ uint32_t time_since_last_playout_ms = now_ms - last_playout_ms_;
+ if (last_playout_ms_ > 0 && time_since_last_playout_ms > 0)
+ samples_needed = std::min(kFrequencyHz / time_since_last_playout_ms,
+ kBufferSizeBytes / 2);
+ uint32_t samples_out = 0;
+ EXPECT_EQ(0,
+ audio_callback_->NeedMorePlayData(samples_needed,
+ 2,
+ 1,
+ kFrequencyHz,
+ playout_buffer_,
+ samples_out));
+ }
+ }
+ tick_->Wait(WEBRTC_EVENT_INFINITE);
+}
+
+void FakeAudioDevice::Start() {
+ CriticalSectionScoped cs(lock_.get());
+ capturing_ = true;
+}
+
+void FakeAudioDevice::Stop() {
+ CriticalSectionScoped cs(lock_.get());
+ capturing_ = false;
+}
+} // namespace test
+} // namespace webrtc
diff --git a/test/fake_audio_device.h b/test/fake_audio_device.h
new file mode 100644
index 0000000..40a7547
--- /dev/null
+++ b/test/fake_audio_device.h
@@ -0,0 +1,69 @@
+/*
+ * Copyright (c) 2013 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_TEST_FAKE_AUDIO_DEVICE_H_
+#define WEBRTC_TEST_FAKE_AUDIO_DEVICE_H_
+
+#include <string>
+
+#include "webrtc/modules/audio_device/include/fake_audio_device.h"
+#include "webrtc/system_wrappers/interface/scoped_ptr.h"
+#include "webrtc/typedefs.h"
+
+namespace webrtc {
+
+class Clock;
+class CriticalSectionWrapper;
+class EventWrapper;
+class FileWrapper;
+class ModuleFileUtility;
+class ThreadWrapper;
+
+namespace test {
+
+class FakeAudioDevice : public FakeAudioDeviceModule {
+ public:
+ FakeAudioDevice(Clock* clock, const std::string& filename);
+
+ virtual ~FakeAudioDevice();
+
+ virtual int32_t Init() OVERRIDE;
+ virtual int32_t RegisterAudioCallback(AudioTransport* callback) OVERRIDE;
+
+ virtual bool Playing() const OVERRIDE;
+ virtual int32_t PlayoutDelay(uint16_t* delay_ms) const OVERRIDE;
+ virtual bool Recording() const OVERRIDE;
+
+ void Start();
+ void Stop();
+
+ private:
+ static bool Run(void* obj);
+ void CaptureAudio();
+
+ static const uint32_t kFrequencyHz = 16000;
+ static const uint32_t kBufferSizeBytes = 2 * kFrequencyHz;
+
+ AudioTransport* audio_callback_;
+ bool capturing_;
+ int8_t captured_audio_[kBufferSizeBytes];
+ int8_t playout_buffer_[kBufferSizeBytes];
+ int64_t last_playout_ms_;
+
+ Clock* clock_;
+ scoped_ptr<EventWrapper> tick_;
+ scoped_ptr<CriticalSectionWrapper> lock_;
+ scoped_ptr<ThreadWrapper> thread_;
+ scoped_ptr<ModuleFileUtility> file_utility_;
+ scoped_ptr<FileWrapper> input_stream_;
+};
+} // namespace test
+} // namespace webrtc
+
+#endif // WEBRTC_TEST_FAKE_AUDIO_DEVICE_H_
diff --git a/test/fake_encoder.cc b/test/fake_encoder.cc
index 454be16..f4e5227 100644
--- a/test/fake_encoder.cc
+++ b/test/fake_encoder.cc
@@ -20,13 +20,16 @@
callback_(NULL),
target_bitrate_kbps_(0),
last_encode_time_ms_(0) {
- memset(encoded_buffer_, 0, sizeof(encoded_buffer_));
+ // Generate some arbitrary not-all-zero data
+ for (size_t i = 0; i < sizeof(encoded_buffer_); ++i) {
+ encoded_buffer_[i] = static_cast<uint8_t>(i);
+ }
}
FakeEncoder::~FakeEncoder() {}
void FakeEncoder::SetCodecSettings(VideoCodec* codec,
- size_t num_streams) {
+ size_t num_streams) {
assert(num_streams > 0);
assert(num_streams <= kMaxSimulcastStreams);
@@ -57,7 +60,6 @@
codec->codecType = kVideoCodecGeneric;
strcpy(codec->plName, "FAKE");
- codec->plType = 125;
}
int32_t FakeEncoder::InitEncode(const VideoCodec* config,
diff --git a/test/null_transport.cc b/test/null_transport.cc
index 1baeedf..3cba638 100644
--- a/test/null_transport.cc
+++ b/test/null_transport.cc
@@ -12,11 +12,11 @@
namespace webrtc {
namespace test {
-bool NullTransport::SendRTP(const uint8_t* packet, size_t length) {
+bool NullTransport::SendRtp(const uint8_t* packet, size_t length) {
return true;
}
-bool NullTransport::SendRTCP(const uint8_t* packet, size_t length) {
+bool NullTransport::SendRtcp(const uint8_t* packet, size_t length) {
return true;
}
diff --git a/test/null_transport.h b/test/null_transport.h
index f9839b9..e8d4d10 100644
--- a/test/null_transport.h
+++ b/test/null_transport.h
@@ -19,8 +19,8 @@
namespace test {
class NullTransport : public newapi::Transport {
public:
- virtual bool SendRTP(const uint8_t* packet, size_t length) OVERRIDE;
- virtual bool SendRTCP(const uint8_t* packet, size_t length) OVERRIDE;
+ virtual bool SendRtp(const uint8_t* packet, size_t length) OVERRIDE;
+ virtual bool SendRtcp(const uint8_t* packet, size_t length) OVERRIDE;
};
} // namespace test
} // namespace webrtc
diff --git a/test/rtp_rtcp_observer.h b/test/rtp_rtcp_observer.h
index ee06e2a..56c96fa 100644
--- a/test/rtp_rtcp_observer.h
+++ b/test/rtp_rtcp_observer.h
@@ -13,6 +13,7 @@
#include <map>
#include <vector>
+#include "webrtc/modules/rtp_rtcp/interface/rtp_header_parser.h"
#include "webrtc/typedefs.h"
#include "webrtc/video_send_stream.h"
@@ -21,6 +22,7 @@
class RtpRtcpObserver {
public:
+ virtual ~RtpRtcpObserver() {}
newapi::Transport* SendTransport() {
return &send_transport_;
}
@@ -40,20 +42,43 @@
receive_transport_.StopSending();
}
- EventTypeWrapper Wait() { return observation_complete_->Wait(timeout_ms_); }
+ virtual EventTypeWrapper Wait() {
+ EventTypeWrapper result = observation_complete_->Wait(timeout_ms_);
+ observation_complete_->Reset();
+ return result;
+ }
protected:
- RtpRtcpObserver(unsigned int event_timeout_ms)
+ RtpRtcpObserver(unsigned int event_timeout_ms, int delay_ms)
: lock_(CriticalSectionWrapper::CreateCriticalSection()),
observation_complete_(EventWrapper::Create()),
+ parser_(RtpHeaderParser::Create()),
send_transport_(lock_.get(),
this,
&RtpRtcpObserver::OnSendRtp,
- &RtpRtcpObserver::OnSendRtcp),
+ &RtpRtcpObserver::OnSendRtcp,
+ delay_ms),
receive_transport_(lock_.get(),
this,
&RtpRtcpObserver::OnReceiveRtp,
- &RtpRtcpObserver::OnReceiveRtcp),
+ &RtpRtcpObserver::OnReceiveRtcp,
+ delay_ms),
+ timeout_ms_(event_timeout_ms) {}
+
+ explicit RtpRtcpObserver(unsigned int event_timeout_ms)
+ : lock_(CriticalSectionWrapper::CreateCriticalSection()),
+ observation_complete_(EventWrapper::Create()),
+ parser_(RtpHeaderParser::Create()),
+ send_transport_(lock_.get(),
+ this,
+ &RtpRtcpObserver::OnSendRtp,
+ &RtpRtcpObserver::OnSendRtcp,
+ 0),
+ receive_transport_(lock_.get(),
+ this,
+ &RtpRtcpObserver::OnReceiveRtp,
+ &RtpRtcpObserver::OnReceiveRtcp,
+ 0),
timeout_ms_(event_timeout_ms) {}
enum Action {
@@ -83,17 +108,20 @@
public:
typedef Action (RtpRtcpObserver::*PacketTransportAction)(const uint8_t*,
size_t);
+
PacketTransport(CriticalSectionWrapper* lock,
RtpRtcpObserver* observer,
PacketTransportAction on_rtp,
- PacketTransportAction on_rtcp)
- : lock_(lock),
+ PacketTransportAction on_rtcp,
+ int delay_ms)
+ : test::DirectTransport(delay_ms),
+ lock_(lock),
observer_(observer),
on_rtp_(on_rtp),
on_rtcp_(on_rtcp) {}
private:
- virtual bool SendRTP(const uint8_t* packet, size_t length) OVERRIDE {
+ virtual bool SendRtp(const uint8_t* packet, size_t length) OVERRIDE {
Action action;
{
CriticalSectionScoped crit_(lock_);
@@ -104,12 +132,12 @@
// Drop packet silently.
return true;
case SEND_PACKET:
- return test::DirectTransport::SendRTP(packet, length);
+ return test::DirectTransport::SendRtp(packet, length);
}
return true; // Will never happen, makes compiler happy.
}
- virtual bool SendRTCP(const uint8_t* packet, size_t length) OVERRIDE {
+ virtual bool SendRtcp(const uint8_t* packet, size_t length) OVERRIDE {
Action action;
{
CriticalSectionScoped crit_(lock_);
@@ -120,7 +148,7 @@
// Drop packet silently.
return true;
case SEND_PACKET:
- return test::DirectTransport::SendRTCP(packet, length);
+ return test::DirectTransport::SendRtcp(packet, length);
}
return true; // Will never happen, makes compiler happy.
}
@@ -135,6 +163,7 @@
protected:
scoped_ptr<CriticalSectionWrapper> lock_;
scoped_ptr<EventWrapper> observation_complete_;
+ scoped_ptr<RtpHeaderParser> parser_;
private:
PacketTransport send_transport_, receive_transport_;
diff --git a/test/webrtc_test_common.gyp b/test/webrtc_test_common.gyp
index 1a7e579..5b546c7 100644
--- a/test/webrtc_test_common.gyp
+++ b/test/webrtc_test_common.gyp
@@ -16,6 +16,8 @@
'sources': [
'direct_transport.cc',
'direct_transport.h',
+ 'fake_audio_device.cc',
+ 'fake_audio_device.h',
'fake_decoder.cc',
'fake_decoder.h',
'fake_encoder.cc',
@@ -115,6 +117,7 @@
'<(DEPTH)/testing/gtest.gyp:gtest',
'<(DEPTH)/third_party/gflags/gflags.gyp:gflags',
'<(webrtc_root)/modules/modules.gyp:video_capture_module',
+ '<(webrtc_root)/modules/modules.gyp:media_file',
'<(webrtc_root)/test/test.gyp:test_support',
'<(webrtc_root)/common_video/common_video.gyp:frame_generator',
],
diff --git a/tools/barcode_tools/barcode_decoder.py b/tools/barcode_tools/barcode_decoder.py
index 0bf8b5b..7a36668 100755
--- a/tools/barcode_tools/barcode_decoder.py
+++ b/tools/barcode_tools/barcode_decoder.py
@@ -11,15 +11,18 @@
import os
import sys
-import helper_functions
+if __name__ == '__main__':
+ # Make sure we always can import helper_functions.
+ sys.path.append(os.path.dirname(__file__))
+import helper_functions
# Chrome browsertests will throw away stderr; avoid that output gets lost.
sys.stderr = sys.stdout
def convert_yuv_to_png_files(yuv_file_name, yuv_frame_width, yuv_frame_height,
- output_directory, ffmpeg_dir=None):
+ output_directory, ffmpeg_path):
"""Converts a YUV video file into PNG frames.
The function uses ffmpeg to convert the YUV file. The output of ffmpeg is in
@@ -31,18 +34,17 @@
yuv_frame_height(int): The height of one YUV frame.
output_directory(string): The output directory where the PNG frames will be
stored.
- ffmpeg_dir(string): The directory containing the ffmpeg executable. If
- omitted, the PATH will be searched for it.
+ ffmpeg_path(string): The path to the ffmpeg executable. If None, the PATH
+ will be searched for it.
Return:
(bool): True if the conversion was OK.
"""
size_string = str(yuv_frame_width) + 'x' + str(yuv_frame_height)
output_files_pattern = os.path.join(output_directory, 'frame_%04d.png')
- ffmpeg_executable = 'ffmpeg.exe' if sys.platform == 'win32' else 'ffmpeg'
- if ffmpeg_dir:
- ffmpeg_executable = os.path.join(ffmpeg_dir, ffmpeg_executable)
- command = [ffmpeg_executable, '-s', '%s' % size_string, '-i', '%s'
+ if not ffmpeg_path:
+ ffmpeg_path = 'ffmpeg.exe' if sys.platform == 'win32' else 'ffmpeg'
+ command = [ffmpeg_path, '-s', '%s' % size_string, '-i', '%s'
% yuv_file_name, '-f', 'image2', '-vcodec', 'png',
'%s' % output_files_pattern]
try:
@@ -54,12 +56,12 @@
print 'Error executing command: %s. Error: %s' % (command, err)
return False
except OSError:
- print ('Did not find %s. Have you installed it?' % ffmpeg_executable)
+ print ('Did not find %s. Have you installed it?' % ffmpeg_path)
return False
return True
-def decode_frames(input_directory, zxing_dir=None):
+def decode_frames(input_directory, zxing_path):
"""Decodes the barcodes overlaid in each frame.
The function uses the Zxing command-line tool from the Zxing C++ distribution
@@ -73,19 +75,18 @@
Args:
input_directory(string): The input directory from where the PNG frames are
read.
- zxing_dir(string): The directory containing the zxing executable. If
- omitted, the PATH will be searched for it.
+ zxing_path(string): The path to the zxing binary. If specified as None,
+ the PATH will be searched for it.
Return:
- (bool): True if the decoding went without errors.
+ (bool): True if the decoding succeeded.
"""
- zxing_executable = 'zxing.exe' if sys.platform == 'win32' else 'zxing'
- if zxing_dir:
- zxing_executable = os.path.join(zxing_dir, zxing_executable)
- print 'Decoding barcodes from PNG files with %s...' % zxing_executable
+ if not zxing_path:
+ zxing_path = 'zxing.exe' if sys.platform == 'win32' else 'zxing'
+ print 'Decoding barcodes from PNG files with %s...' % zxing_path
return helper_functions.perform_action_on_all_files(
directory=input_directory, file_pattern='frame_',
file_extension='png', start_number=1, action=_decode_barcode_in_file,
- command_line_decoder=zxing_executable)
+ command_line_decoder=zxing_path)
def _decode_barcode_in_file(file_name, command_line_decoder):
@@ -230,14 +231,14 @@
usage = "usage: %prog [options]"
parser = optparse.OptionParser(usage=usage)
- parser.add_option('--zxing_dir', type='string',
- help=('The path to the directory where the zxing executable'
- 'is located. If omitted, it will be assumed to be '
- 'present in the PATH.'))
- parser.add_option('--ffmpeg_dir', type='string', default=None,
- help=('The path to the directory where the ffmpeg '
- 'executable is located. If omitted, it will be '
- 'assumed to be present in the PATH.'))
+ parser.add_option('--zxing_path', type='string',
+ help=('The path to where the zxing executable is located. '
+ 'If omitted, it will be assumed to be present in the '
+ 'PATH with the name zxing[.exe].'))
+ parser.add_option('--ffmpeg_path', type='string',
+ help=('The path to where the ffmpeg executable is located. '
+ 'If omitted, it will be assumed to be present in the '
+ 'PATH with the name ffmpeg[.exe].'))
parser.add_option('--yuv_frame_width', type='int', default=640,
help='Width of the YUV file\'s frames. Default: %default')
parser.add_option('--yuv_frame_height', type='int', default=480,
@@ -271,13 +272,13 @@
if not convert_yuv_to_png_files(options.yuv_file, options.yuv_frame_width,
options.yuv_frame_height,
output_directory=options.png_working_dir,
- ffmpeg_dir=options.ffmpeg_dir):
+ ffmpeg_path=options.ffmpeg_path):
print 'An error occurred converting from YUV to PNG frames.'
return -1
# Decode the barcodes from the PNG frames.
if not decode_frames(input_directory=options.png_working_dir,
- zxing_dir=options.zxing_dir):
+ zxing_path=options.zxing_path):
print 'An error occurred decoding barcodes from PNG frames.'
return -2
diff --git a/tools/compare_videos.py b/tools/compare_videos.py
index ee8e6d5..f6275a6 100755
--- a/tools/compare_videos.py
+++ b/tools/compare_videos.py
@@ -9,8 +9,10 @@
import optparse
import os
+import shutil
import subprocess
import sys
+import tempfile
SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__))
@@ -24,7 +26,7 @@
usage = 'usage: %prog [options]'
parser = optparse.OptionParser(usage=usage)
- parser.add_option('--label', type='string', default="MY_TEST",
+ parser.add_option('--label', type='string', default='MY_TEST',
help=('Label of the test, used to identify different '
'tests. Default: %default'))
parser.add_option('--ref_video', type='string',
@@ -34,6 +36,18 @@
'video (YUV).'))
parser.add_option('--frame_analyzer', type='string',
help='Path to the frame analyzer executable.')
+ parser.add_option('--barcode_decoder', type='string',
+ help=('Path to the barcode decoder script. By default, we '
+ 'will assume we can find it in barcode_tools/'
+ 'relative to this directory.'))
+ parser.add_option('--ffmpeg_path', type='string',
+ help=('The path to where the ffmpeg executable is located. '
+ 'If omitted, it will be assumed to be present in the '
+ 'PATH with the name ffmpeg[.exe].'))
+ parser.add_option('--zxing_path', type='string',
+ help=('The path to where the zxing executable is located. '
+ 'If omitted, it will be assumed to be present in the '
+ 'PATH with the name zxing[.exe].'))
parser.add_option('--stats_file', type='string', default='stats.txt',
help=('Path to the temporary stats file to be created and '
'used. Default: %default'))
@@ -77,14 +91,18 @@
"""
options = _ParseArgs()
- # Run barcode decoder on the test video to identify frame numbers.
- path_to_decoder = os.path.join(SCRIPT_DIR, 'barcode_tools',
- 'barcode_decoder.py')
+ if options.barcode_decoder:
+ path_to_decoder = options.barcode_decoder
+ else:
+ path_to_decoder = os.path.join(SCRIPT_DIR, 'barcode_tools',
+ 'barcode_decoder.py')
# On Windows, sometimes the inherited stdin handle from the parent process
# fails. Work around this by passing null to stdin to the subprocesses.
null_filehandle = open(os.devnull, 'r')
+ # Run barcode decoder on the test video to identify frame numbers.
+ png_working_directory = tempfile.mkdtemp()
cmd = [
sys.executable,
path_to_decoder,
@@ -92,10 +110,17 @@
'--yuv_frame_width=%d' % options.yuv_frame_width,
'--yuv_frame_height=%d' % options.yuv_frame_height,
'--stats_file=%s' % options.stats_file,
+ '--png_working_dir=%s' % png_working_directory,
]
+ if options.zxing_path:
+ cmd.append('--zxing_path=%s' % options.zxing_path)
+ if options.ffmpeg_path:
+ cmd.append('--ffmpeg_path=%s' % options.ffmpeg_path)
barcode_decoder = subprocess.Popen(cmd, stdin=null_filehandle,
stdout=sys.stdout, stderr=sys.stderr)
barcode_decoder.wait()
+
+ shutil.rmtree(png_working_directory)
if barcode_decoder.returncode != 0:
print 'Failed to run barcode decoder script.'
return 1
diff --git a/transport.h b/transport.h
index f83e1e7..c44c5b2 100644
--- a/transport.h
+++ b/transport.h
@@ -20,8 +20,8 @@
class Transport {
public:
- virtual bool SendRTP(const uint8_t* packet, size_t length) = 0;
- virtual bool SendRTCP(const uint8_t* packet, size_t length) = 0;
+ virtual bool SendRtp(const uint8_t* packet, size_t length) = 0;
+ virtual bool SendRtcp(const uint8_t* packet, size_t length) = 0;
protected:
virtual ~Transport() {}
diff --git a/video/encoded_frame_callback_adapter.cc b/video/encoded_frame_callback_adapter.cc
new file mode 100644
index 0000000..f5eca7c
--- /dev/null
+++ b/video/encoded_frame_callback_adapter.cc
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2013 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/encoded_frame_callback_adapter.h"
+
+#include "webrtc/modules/video_coding/main/source/encoded_frame.h"
+
+namespace webrtc {
+namespace internal {
+
+EncodedFrameCallbackAdapter::EncodedFrameCallbackAdapter(
+ EncodedFrameObserver* observer) : observer_(observer) {
+}
+
+EncodedFrameCallbackAdapter::~EncodedFrameCallbackAdapter() {}
+
+int32_t EncodedFrameCallbackAdapter::Encoded(
+ EncodedImage& encodedImage,
+ const CodecSpecificInfo* codecSpecificInfo,
+ const RTPFragmentationHeader* fragmentation) {
+ assert(observer_ != NULL);
+ FrameType frame_type =
+ VCMEncodedFrame::ConvertFrameType(encodedImage._frameType);
+ const EncodedFrame frame(encodedImage._buffer,
+ encodedImage._length,
+ frame_type);
+ observer_->EncodedFrameCallback(frame);
+ return 0;
+}
+
+} // namespace internal
+} // namespace webrtc
diff --git a/video/encoded_frame_callback_adapter.h b/video/encoded_frame_callback_adapter.h
new file mode 100644
index 0000000..d381479
--- /dev/null
+++ b/video/encoded_frame_callback_adapter.h
@@ -0,0 +1,36 @@
+/*
+ * Copyright (c) 2013 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_ENCODED_FRAME_CALLBACK_ADAPTER_H_
+#define WEBRTC_VIDEO_ENCODED_FRAME_CALLBACK_ADAPTER_H_
+
+#include "webrtc/modules/video_coding/codecs/interface/video_codec_interface.h"
+#include "webrtc/frame_callback.h"
+
+namespace webrtc {
+namespace internal {
+
+class EncodedFrameCallbackAdapter : public EncodedImageCallback {
+ public:
+ explicit EncodedFrameCallbackAdapter(EncodedFrameObserver* observer);
+ virtual ~EncodedFrameCallbackAdapter();
+
+ virtual int32_t Encoded(EncodedImage& encodedImage,
+ const CodecSpecificInfo* codecSpecificInfo,
+ const RTPFragmentationHeader* fragmentation);
+
+ private:
+ EncodedFrameObserver* observer_;
+};
+
+} // namespace internal
+} // namespace webrtc
+
+#endif // WEBRTC_VIDEO_ENCODED_FRAME_CALLBACK_ADAPTER_H_
diff --git a/video/full_stack.cc b/video/full_stack.cc
index b154df3..17e7686 100644
--- a/video/full_stack.cc
+++ b/video/full_stack.cc
@@ -149,7 +149,7 @@
input_->PutFrame(video_frame, delta_capture_ms);
}
- virtual bool SendRTP(const uint8_t* packet, size_t length) OVERRIDE {
+ virtual bool SendRtp(const uint8_t* packet, size_t length) OVERRIDE {
scoped_ptr<RtpHeaderParser> parser(RtpHeaderParser::Create());
RTPHeader header;
parser->Parse(packet, static_cast<int>(length), &header);
@@ -165,11 +165,11 @@
Clock::GetRealTimeClock()->CurrentNtpInMilliseconds();
}
- return transport_->SendRTP(packet, length);
+ return transport_->SendRtp(packet, length);
}
- virtual bool SendRTCP(const uint8_t* packet, size_t length) OVERRIDE {
- return transport_->SendRTCP(packet, length);
+ virtual bool SendRtcp(const uint8_t* packet, size_t length) OVERRIDE {
+ return transport_->SendRtcp(packet, length);
}
virtual void RenderFrame(const I420VideoFrame& video_frame,
@@ -402,7 +402,7 @@
send_config.codec.startBitrate = params.bitrate;
send_config.codec.maxBitrate = params.bitrate;
- VideoSendStream* send_stream = call->CreateSendStream(send_config);
+ VideoSendStream* send_stream = call->CreateVideoSendStream(send_config);
analyzer.input_ = send_stream->Input();
scoped_ptr<test::FrameGeneratorCapturer> file_capturer(
@@ -422,21 +422,21 @@
receive_config.renderer = &analyzer;
VideoReceiveStream* receive_stream =
- call->CreateReceiveStream(receive_config);
+ call->CreateVideoReceiveStream(receive_config);
- receive_stream->StartReceive();
- send_stream->StartSend();
+ receive_stream->StartReceiving();
+ send_stream->StartSending();
file_capturer->Start();
analyzer.Wait();
file_capturer->Stop();
- send_stream->StopSend();
- receive_stream->StopReceive();
+ send_stream->StopSending();
+ receive_stream->StopReceiving();
- call->DestroyReceiveStream(receive_stream);
- call->DestroySendStream(send_stream);
+ call->DestroyVideoReceiveStream(receive_stream);
+ call->DestroyVideoSendStream(send_stream);
transport.StopSending();
}
diff --git a/video/rampup_tests.cc b/video/rampup_tests.cc
index c668458..b294c70 100644
--- a/video/rampup_tests.cc
+++ b/video/rampup_tests.cc
@@ -27,6 +27,7 @@
#include "webrtc/test/fake_encoder.h"
#include "webrtc/test/frame_generator_capturer.h"
#include "webrtc/test/generate_ssrcs.h"
+#include "webrtc/video/transport_adapter.h"
namespace webrtc {
@@ -43,7 +44,7 @@
: critical_section_(CriticalSectionWrapper::CreateCriticalSection()),
all_ssrcs_sent_(EventWrapper::Create()),
rtp_parser_(RtpHeaderParser::Create()),
- feedback_transport_(new TransportWrapper(feedback_transport)),
+ feedback_transport_(feedback_transport),
receive_stats_(ReceiveStatistics::Create(clock)),
clock_(clock),
num_expected_ssrcs_(num_expected_ssrcs) {
@@ -53,7 +54,7 @@
// be able to produce an RTCP with REMB.
RtpRtcp::Configuration config;
config.receive_statistics = receive_stats_.get();
- config.outgoing_transport = feedback_transport_.get();
+ config.outgoing_transport = &feedback_transport_;
rtp_rtcp_.reset(RtpRtcp::CreateRtpRtcp(config));
rtp_rtcp_->SetREMBStatus(true);
rtp_rtcp_->SetRTCPStatus(kRtcpNonCompound);
@@ -73,7 +74,7 @@
rtp_rtcp_->Process();
}
- virtual bool SendRTP(const uint8_t* packet, size_t length) OVERRIDE {
+ virtual bool SendRtp(const uint8_t* packet, size_t length) OVERRIDE {
CriticalSectionScoped lock(critical_section_.get());
RTPHeader header;
EXPECT_TRUE(rtp_parser_->Parse(packet, static_cast<int>(length), &header));
@@ -87,43 +88,20 @@
return true;
}
- virtual bool SendRTCP(const uint8_t* packet, size_t length) OVERRIDE {
+ virtual bool SendRtcp(const uint8_t* packet, size_t length) OVERRIDE {
return true;
}
EventTypeWrapper Wait() { return all_ssrcs_sent_->Wait(120 * 1000); }
private:
- class TransportWrapper : public webrtc::Transport {
- public:
- explicit TransportWrapper(newapi::Transport* new_transport)
- : new_transport_(new_transport) {}
-
- virtual int SendPacket(int channel, const void* data, int len) OVERRIDE {
- return new_transport_->SendRTP(static_cast<const uint8_t*>(data), len)
- ? len
- : -1;
- }
-
- virtual int SendRTCPPacket(int channel,
- const void* data,
- int len) OVERRIDE {
- return new_transport_->SendRTCP(static_cast<const uint8_t*>(data), len)
- ? len
- : -1;
- }
-
- private:
- newapi::Transport* new_transport_;
- };
-
static const unsigned int kExpectedBitrateBps = 1200000;
scoped_ptr<CriticalSectionWrapper> critical_section_;
scoped_ptr<EventWrapper> all_ssrcs_sent_;
scoped_ptr<RtpHeaderParser> rtp_parser_;
scoped_ptr<RtpRtcp> rtp_rtcp_;
- scoped_ptr<TransportWrapper> feedback_transport_;
+ internal::TransportAdapter feedback_transport_;
scoped_ptr<ReceiveStatistics> receive_stats_;
scoped_ptr<RemoteBitrateEstimator> remote_bitrate_estimator_;
Clock* clock_;
@@ -152,19 +130,20 @@
send_config.encoder = &encoder;
send_config.internal_source = false;
test::FakeEncoder::SetCodecSettings(&send_config.codec, 3);
+ send_config.codec.plType = 125;
send_config.pacing = GetParam();
send_config.rtp.extensions.push_back(
- RtpExtension("toffset", kTOffsetExtensionId));
+ RtpExtension(RtpExtension::kTOffset, kTOffsetExtensionId));
test::GenerateRandomSsrcs(&send_config, &reserved_ssrcs_);
- VideoSendStream* send_stream = call->CreateSendStream(send_config);
+ VideoSendStream* send_stream = call->CreateVideoSendStream(send_config);
VideoReceiveStream::Config receive_config;
receive_config.rtp.ssrc = send_config.rtp.ssrcs[0];
receive_config.rtp.nack.rtp_history_ms = send_config.rtp.nack.rtp_history_ms;
VideoReceiveStream* receive_stream =
- call->CreateReceiveStream(receive_config);
+ call->CreateVideoReceiveStream(receive_config);
scoped_ptr<test::FrameGeneratorCapturer> frame_generator_capturer(
test::FrameGeneratorCapturer::Create(send_stream->Input(),
@@ -173,18 +152,18 @@
30,
Clock::GetRealTimeClock()));
- receive_stream->StartReceive();
- send_stream->StartSend();
+ receive_stream->StartReceiving();
+ send_stream->StartSending();
frame_generator_capturer->Start();
EXPECT_EQ(kEventSignaled, stream_observer.Wait());
frame_generator_capturer->Stop();
- send_stream->StopSend();
- receive_stream->StopReceive();
+ send_stream->StopSending();
+ receive_stream->StopReceiving();
- call->DestroyReceiveStream(receive_stream);
- call->DestroySendStream(send_stream);
+ call->DestroyVideoReceiveStream(receive_stream);
+ call->DestroyVideoSendStream(send_stream);
}
INSTANTIATE_TEST_CASE_P(RampUpTest, RampUpTest, ::testing::Bool());
diff --git a/video/transport_adapter.cc b/video/transport_adapter.cc
index 1d32504..7cc6a0a 100644
--- a/video/transport_adapter.cc
+++ b/video/transport_adapter.cc
@@ -19,7 +19,7 @@
int TransportAdapter::SendPacket(int /*channel*/,
const void* packet,
int length) {
- bool success = transport_->SendRTP(static_cast<const uint8_t*>(packet),
+ bool success = transport_->SendRtp(static_cast<const uint8_t*>(packet),
static_cast<size_t>(length));
return success ? length : -1;
}
@@ -27,7 +27,7 @@
int TransportAdapter::SendRTCPPacket(int /*channel*/,
const void* packet,
int length) {
- bool success = transport_->SendRTCP(static_cast<const uint8_t*>(packet),
+ bool success = transport_->SendRtcp(static_cast<const uint8_t*>(packet),
static_cast<size_t>(length));
return success ? length : -1;
}
diff --git a/video/video_receive_stream.cc b/video/video_receive_stream.cc
index a84c6d2..5157237 100644
--- a/video/video_receive_stream.cc
+++ b/video/video_receive_stream.cc
@@ -30,8 +30,12 @@
VideoReceiveStream::VideoReceiveStream(webrtc::VideoEngine* video_engine,
const VideoReceiveStream::Config& config,
- newapi::Transport* transport)
- : transport_adapter_(transport), config_(config), channel_(-1) {
+ newapi::Transport* transport,
+ webrtc::VoiceEngine* voice_engine)
+ : transport_adapter_(transport),
+ encoded_frame_proxy_(config.pre_decode_callback),
+ config_(config),
+ channel_(-1) {
video_engine_base_ = ViEBase::GetInterface(video_engine);
// TODO(mflodman): Use the other CreateChannel method.
video_engine_base_->CreateChannel(channel_);
@@ -84,12 +88,21 @@
}
}
- render_ = webrtc::ViERender::GetInterface(video_engine);
+ render_ = ViERender::GetInterface(video_engine);
assert(render_ != NULL);
- render_->AddRenderer(channel_, kVideoI420, this);
+ render_->AddRenderCallback(channel_, this);
+
+ if (voice_engine) {
+ video_engine_base_->SetVoiceEngine(voice_engine);
+ video_engine_base_->ConnectAudioChannel(channel_, config_.audio_channel_id);
+ }
image_process_ = ViEImageProcess::GetInterface(video_engine);
+ if (config.pre_decode_callback) {
+ image_process_->RegisterPreDecodeImageCallback(channel_,
+ &encoded_frame_proxy_);
+ }
image_process_->RegisterPreRenderCallback(channel_,
config_.pre_render_callback);
@@ -97,7 +110,8 @@
}
VideoReceiveStream::~VideoReceiveStream() {
- image_process_->DeRegisterPreEncodeCallback(channel_);
+ image_process_->DeRegisterPreRenderCallback(channel_);
+ image_process_->DeRegisterPreDecodeCallback(channel_);
render_->RemoveRenderer(channel_);
@@ -108,6 +122,7 @@
network_->DeregisterSendTransport(channel_);
+ video_engine_base_->SetVoiceEngine(NULL);
image_process_->Release();
video_engine_base_->Release();
external_codec_->Release();
@@ -117,22 +132,18 @@
rtp_rtcp_->Release();
}
-void VideoReceiveStream::StartReceive() {
- if (render_->StartRender(channel_)) {
+void VideoReceiveStream::StartReceiving() {
+ if (render_->StartRender(channel_) != 0)
abort();
- }
- if (video_engine_base_->StartReceive(channel_) != 0) {
+ if (video_engine_base_->StartReceive(channel_) != 0)
abort();
- }
}
-void VideoReceiveStream::StopReceive() {
- if (render_->StopRender(channel_)) {
+void VideoReceiveStream::StopReceiving() {
+ if (render_->StopRender(channel_) != 0)
abort();
- }
- if (video_engine_base_->StopReceive(channel_) != 0) {
+ if (video_engine_base_->StopReceive(channel_) != 0)
abort();
- }
}
void VideoReceiveStream::GetCurrentReceiveCodec(VideoCodec* receive_codec) {
@@ -149,44 +160,14 @@
channel_, packet, static_cast<int>(length)) == 0;
}
-int VideoReceiveStream::FrameSizeChange(unsigned int width,
- unsigned int height,
- unsigned int /*number_of_streams*/) {
- width_ = width;
- height_ = height;
- return 0;
-}
-
-int VideoReceiveStream::DeliverFrame(uint8_t* frame,
- int buffer_size,
- uint32_t timestamp,
- int64_t render_time,
- void* /*handle*/) {
- if (config_.renderer == NULL) {
+int32_t VideoReceiveStream::RenderFrame(const uint32_t stream_id,
+ I420VideoFrame& video_frame) {
+ if (config_.renderer == NULL)
return 0;
- }
- I420VideoFrame video_frame;
- video_frame.CreateEmptyFrame(width_, height_, width_, height_, height_);
- ConvertToI420(kI420,
- frame,
- 0,
- 0,
- width_,
- height_,
- buffer_size,
- webrtc::kRotateNone,
- &video_frame);
- video_frame.set_timestamp(timestamp);
- video_frame.set_render_time_ms(render_time);
-
- config_.renderer->RenderFrame(video_frame,
- render_time - clock_->TimeInMilliseconds());
-
+ config_.renderer->RenderFrame(
+ video_frame, video_frame.render_time_ms() - clock_->TimeInMilliseconds());
return 0;
}
-
-bool VideoReceiveStream::IsTextureSupported() { return false; }
-
} // internal
} // webrtc
diff --git a/video/video_receive_stream.h b/video/video_receive_stream.h
index c2352f4..8a23a10 100644
--- a/video/video_receive_stream.h
+++ b/video/video_receive_stream.h
@@ -14,7 +14,9 @@
#include <vector>
#include "webrtc/common_video/libyuv/include/webrtc_libyuv.h"
+#include "webrtc/modules/video_render/include/video_render_defines.h"
#include "webrtc/system_wrappers/interface/clock.h"
+#include "webrtc/video/encoded_frame_callback_adapter.h"
#include "webrtc/video/transport_adapter.h"
#include "webrtc/video_engine/include/vie_render.h"
#include "webrtc/video_receive_stream.h"
@@ -29,28 +31,26 @@
class ViENetwork;
class ViERender;
class ViERTP_RTCP;
+class VoiceEngine;
namespace internal {
class VideoReceiveStream : public webrtc::VideoReceiveStream,
- public webrtc::ExternalRenderer {
+ public VideoRenderCallback {
public:
VideoReceiveStream(webrtc::VideoEngine* video_engine,
const VideoReceiveStream::Config& config,
- newapi::Transport* transport);
+ newapi::Transport* transport,
+ webrtc::VoiceEngine* voice_engine);
virtual ~VideoReceiveStream();
- virtual void StartReceive() OVERRIDE;
- virtual void StopReceive() OVERRIDE;
+ virtual void StartReceiving() OVERRIDE;
+ virtual void StopReceiving() OVERRIDE;
virtual void GetCurrentReceiveCodec(VideoCodec* receive_codec) OVERRIDE;
- virtual int FrameSizeChange(unsigned int width, unsigned int height,
- unsigned int /*number_of_streams*/) OVERRIDE;
- virtual int DeliverFrame(uint8_t* frame, int buffer_size, uint32_t timestamp,
- int64_t render_time, void* /*handle*/) OVERRIDE;
-
- virtual bool IsTextureSupported() OVERRIDE;
+ virtual int32_t RenderFrame(const uint32_t stream_id,
+ I420VideoFrame& video_frame) OVERRIDE;
public:
virtual bool DeliverRtcp(const uint8_t* packet, size_t length);
@@ -58,6 +58,7 @@
private:
TransportAdapter transport_adapter_;
+ EncodedFrameCallbackAdapter encoded_frame_proxy_;
VideoReceiveStream::Config config_;
Clock* clock_;
@@ -70,10 +71,6 @@
ViEImageProcess* image_process_;
int channel_;
-
- // TODO(pbos): Remove VideoReceiveStream can operate on I420 frames directly.
- unsigned int height_;
- unsigned int width_;
};
} // internal
} // webrtc
diff --git a/video/video_send_stream.cc b/video/video_send_stream.cc
index 8814c35..d90c733 100644
--- a/video/video_send_stream.cc
+++ b/video/video_send_stream.cc
@@ -82,14 +82,11 @@
bool overuse_detection,
webrtc::VideoEngine* video_engine,
const VideoSendStream::Config& config)
- : transport_adapter_(transport), config_(config), external_codec_(NULL) {
-
- if (config_.codec.numberOfSimulcastStreams > 0) {
- assert(config_.rtp.ssrcs.size() == config_.codec.numberOfSimulcastStreams);
- } else {
- assert(config_.rtp.ssrcs.size() == 1);
- }
-
+ : transport_adapter_(transport),
+ encoded_frame_proxy_(config.post_encode_callback),
+ codec_lock_(CriticalSectionWrapper::CreateCriticalSection()),
+ config_(config),
+ external_codec_(NULL) {
video_engine_base_ = ViEBase::GetInterface(video_engine);
video_engine_base_->CreateChannel(channel_);
assert(channel_ != -1);
@@ -97,39 +94,18 @@
rtp_rtcp_ = ViERTP_RTCP::GetInterface(video_engine);
assert(rtp_rtcp_ != NULL);
- if (config_.rtp.ssrcs.size() == 1) {
- rtp_rtcp_->SetLocalSSRC(channel_, config_.rtp.ssrcs[0]);
- } else {
- for (size_t i = 0; i < config_.rtp.ssrcs.size(); ++i) {
- rtp_rtcp_->SetLocalSSRC(channel_,
- config_.rtp.ssrcs[i],
- kViEStreamTypeNormal,
- static_cast<unsigned char>(i));
- }
- }
+ assert(config_.rtp.ssrcs.size() > 0);
+ if (config_.suspend_below_min_bitrate)
+ config_.pacing = true;
rtp_rtcp_->SetTransmissionSmoothingStatus(channel_, config_.pacing);
- if (!config_.rtp.rtx.ssrcs.empty()) {
- assert(config_.rtp.rtx.ssrcs.size() == config_.rtp.ssrcs.size());
- for (size_t i = 0; i < config_.rtp.rtx.ssrcs.size(); ++i) {
- rtp_rtcp_->SetLocalSSRC(channel_,
- config_.rtp.rtx.ssrcs[i],
- kViEStreamTypeRtx,
- static_cast<unsigned char>(i));
- }
-
- if (config_.rtp.rtx.rtx_payload_type != 0) {
- rtp_rtcp_->SetRtxSendPayloadType(channel_,
- config_.rtp.rtx.rtx_payload_type);
- }
- }
for (size_t i = 0; i < config_.rtp.extensions.size(); ++i) {
const std::string& extension = config_.rtp.extensions[i].name;
int id = config_.rtp.extensions[i].id;
- if (extension == "toffset") {
+ if (extension == RtpExtension::kTOffset) {
if (rtp_rtcp_->SetSendTimestampOffsetStatus(channel_, true, id) != 0)
abort();
- } else if (extension == "abs-send-time") {
+ } else if (extension == RtpExtension::kAbsSendTime) {
if (rtp_rtcp_->SetSendAbsoluteSendTimeStatus(channel_, true, id) != 0)
abort();
} else {
@@ -186,9 +162,8 @@
}
codec_ = ViECodec::GetInterface(video_engine);
- if (codec_->SetSendCodec(channel_, config_.codec) != 0) {
+ if (!SetCodec(config_.codec))
abort();
- }
if (overuse_detection) {
overuse_observer_.reset(
@@ -201,9 +176,13 @@
image_process_ = ViEImageProcess::GetInterface(video_engine);
image_process_->RegisterPreEncodeCallback(channel_,
config_.pre_encode_callback);
+ if (config_.post_encode_callback) {
+ image_process_->RegisterPostEncodeImageCallback(channel_,
+ &encoded_frame_proxy_);
+ }
- if (config.auto_mute) {
- codec_->EnableAutoMuting(channel_);
+ if (config.suspend_below_min_bitrate) {
+ codec_->SuspendBelowMinBitrate(channel_);
}
}
@@ -261,35 +240,63 @@
VideoSendStreamInput* VideoSendStream::Input() { return this; }
-void VideoSendStream::StartSend() {
+void VideoSendStream::StartSending() {
if (video_engine_base_->StartSend(channel_) != 0)
abort();
if (video_engine_base_->StartReceive(channel_) != 0)
abort();
}
-void VideoSendStream::StopSend() {
+void VideoSendStream::StopSending() {
if (video_engine_base_->StopSend(channel_) != 0)
abort();
if (video_engine_base_->StopReceive(channel_) != 0)
abort();
}
-bool VideoSendStream::SetTargetBitrate(
- int min_bitrate,
- int max_bitrate,
- const std::vector<SimulcastStream>& streams) {
- return false;
+bool VideoSendStream::SetCodec(const VideoCodec& codec) {
+ assert(config_.rtp.ssrcs.size() >= codec.numberOfSimulcastStreams);
+
+ CriticalSectionScoped crit(codec_lock_.get());
+ if (codec_->SetSendCodec(channel_, codec) != 0)
+ return false;
+
+ for (size_t i = 0; i < config_.rtp.ssrcs.size(); ++i) {
+ rtp_rtcp_->SetLocalSSRC(channel_,
+ config_.rtp.ssrcs[i],
+ kViEStreamTypeNormal,
+ static_cast<unsigned char>(i));
+ }
+
+ config_.codec = codec;
+ if (config_.rtp.rtx.ssrcs.empty())
+ return true;
+
+ // Set up RTX.
+ assert(config_.rtp.rtx.ssrcs.size() == config_.rtp.ssrcs.size());
+ for (size_t i = 0; i < config_.rtp.ssrcs.size(); ++i) {
+ rtp_rtcp_->SetLocalSSRC(channel_,
+ config_.rtp.rtx.ssrcs[i],
+ kViEStreamTypeRtx,
+ static_cast<unsigned char>(i));
+ }
+
+ if (config_.rtp.rtx.rtx_payload_type != 0) {
+ rtp_rtcp_->SetRtxSendPayloadType(channel_,
+ config_.rtp.rtx.rtx_payload_type);
+ }
+
+ return true;
}
-void VideoSendStream::GetSendCodec(VideoCodec* send_codec) {
- *send_codec = config_.codec;
+VideoCodec VideoSendStream::GetCodec() {
+ CriticalSectionScoped crit(codec_lock_.get());
+ return config_.codec;
}
bool VideoSendStream::DeliverRtcp(const uint8_t* packet, size_t length) {
return network_->ReceivedRTCPPacket(
channel_, packet, static_cast<int>(length)) == 0;
}
-
} // namespace internal
} // namespace webrtc
diff --git a/video/video_send_stream.h b/video/video_send_stream.h
index 304d825..0881d91 100644
--- a/video/video_send_stream.h
+++ b/video/video_send_stream.h
@@ -14,9 +14,11 @@
#include <vector>
#include "webrtc/common_video/libyuv/include/webrtc_libyuv.h"
+#include "webrtc/video/encoded_frame_callback_adapter.h"
#include "webrtc/video/transport_adapter.h"
#include "webrtc/video_receive_stream.h"
#include "webrtc/video_send_stream.h"
+#include "webrtc/system_wrappers/interface/critical_section_wrapper.h"
namespace webrtc {
@@ -49,21 +51,20 @@
virtual VideoSendStreamInput* Input() OVERRIDE;
- virtual void StartSend() OVERRIDE;
+ virtual void StartSending() OVERRIDE;
- virtual void StopSend() OVERRIDE;
+ virtual void StopSending() OVERRIDE;
- virtual bool SetTargetBitrate(int min_bitrate, int max_bitrate,
- const std::vector<SimulcastStream>& streams)
- OVERRIDE;
-
- virtual void GetSendCodec(VideoCodec* send_codec) OVERRIDE;
+ virtual bool SetCodec(const VideoCodec& codec) OVERRIDE;
+ virtual VideoCodec GetCodec() OVERRIDE;
public:
bool DeliverRtcp(const uint8_t* packet, size_t length);
private:
TransportAdapter transport_adapter_;
+ EncodedFrameCallbackAdapter encoded_frame_proxy_;
+ scoped_ptr<CriticalSectionWrapper> codec_lock_;
VideoSendStream::Config config_;
ViEBase* video_engine_base_;
diff --git a/video/video_send_stream_tests.cc b/video/video_send_stream_tests.cc
index 358ecf3..9507bf0 100644
--- a/video/video_send_stream_tests.cc
+++ b/video/video_send_stream_tests.cc
@@ -25,31 +25,14 @@
#include "webrtc/test/direct_transport.h"
#include "webrtc/test/fake_encoder.h"
#include "webrtc/test/frame_generator_capturer.h"
+#include "webrtc/test/generate_ssrcs.h"
#include "webrtc/test/null_transport.h"
+#include "webrtc/test/rtp_rtcp_observer.h"
#include "webrtc/video/transport_adapter.h"
#include "webrtc/video_send_stream.h"
namespace webrtc {
-class SendTransportObserver : public test::NullTransport {
- public:
- explicit SendTransportObserver(unsigned long timeout_ms)
- : rtp_header_parser_(RtpHeaderParser::Create()),
- send_test_complete_(EventWrapper::Create()),
- timeout_ms_(timeout_ms) {}
-
- EventTypeWrapper Wait() { return send_test_complete_->Wait(timeout_ms_); }
-
- virtual void Stop() {}
-
- protected:
- scoped_ptr<RtpHeaderParser> rtp_header_parser_;
- scoped_ptr<EventWrapper> send_test_complete_;
-
- private:
- unsigned long timeout_ms_;
-};
-
class VideoSendStreamTest : public ::testing::Test {
public:
VideoSendStreamTest() : fake_encoder_(Clock::GetRealTimeClock()) {}
@@ -57,75 +40,175 @@
protected:
void RunSendTest(Call* call,
const VideoSendStream::Config& config,
- SendTransportObserver* observer) {
- VideoSendStream* send_stream = call->CreateSendStream(config);
+ test::RtpRtcpObserver* observer) {
+ send_stream_ = call->CreateVideoSendStream(config);
scoped_ptr<test::FrameGeneratorCapturer> frame_generator_capturer(
test::FrameGeneratorCapturer::Create(
- send_stream->Input(), 320, 240, 30, Clock::GetRealTimeClock()));
- send_stream->StartSend();
+ send_stream_->Input(), 320, 240, 30, Clock::GetRealTimeClock()));
+ send_stream_->StartSending();
frame_generator_capturer->Start();
EXPECT_EQ(kEventSignaled, observer->Wait());
- observer->Stop();
+ observer->StopSending();
frame_generator_capturer->Stop();
- send_stream->StopSend();
- call->DestroySendStream(send_stream);
+ send_stream_->StopSending();
+ call->DestroyVideoSendStream(send_stream_);
}
- VideoSendStream::Config GetSendTestConfig(Call* call) {
+ VideoSendStream::Config GetSendTestConfig(Call* call,
+ size_t number_of_streams) {
+ assert(number_of_streams <= kNumSendSsrcs);
VideoSendStream::Config config = call->GetDefaultSendConfig();
config.encoder = &fake_encoder_;
config.internal_source = false;
- config.rtp.ssrcs.push_back(kSendSsrc);
- test::FakeEncoder::SetCodecSettings(&config.codec, 1);
+ for (size_t i = 0; i < number_of_streams; ++i)
+ config.rtp.ssrcs.push_back(kSendSsrcs[i]);
+ config.pacing = true;
+ test::FakeEncoder::SetCodecSettings(&config.codec, number_of_streams);
+ config.codec.plType = kFakeSendPayloadType;
return config;
}
- void TestNackRetransmission(uint32_t retransmit_ssrc);
+ void TestNackRetransmission(uint32_t retransmit_ssrc,
+ uint8_t retransmit_payload_type,
+ bool enable_pacing);
+ void SendsSetSsrcs(size_t num_ssrcs, bool send_single_ssrc_first);
+
+ enum { kNumSendSsrcs = 3 };
+ static const uint8_t kSendPayloadType;
+ static const uint8_t kSendRtxPayloadType;
+ static const uint8_t kFakeSendPayloadType;
static const uint32_t kSendSsrc;
static const uint32_t kSendRtxSsrc;
+ static const uint32_t kSendSsrcs[kNumSendSsrcs];
+ VideoSendStream* send_stream_;
test::FakeEncoder fake_encoder_;
};
-const uint32_t VideoSendStreamTest::kSendSsrc = 0xC0FFEE;
+const uint8_t VideoSendStreamTest::kSendPayloadType = 100;
+const uint8_t VideoSendStreamTest::kFakeSendPayloadType = 125;
+const uint8_t VideoSendStreamTest::kSendRtxPayloadType = 98;
const uint32_t VideoSendStreamTest::kSendRtxSsrc = 0xBADCAFE;
+const uint32_t VideoSendStreamTest::kSendSsrcs[kNumSendSsrcs] = { 0xC0FFED,
+ 0xC0FFEE, 0xC0FFEF };
+const uint32_t VideoSendStreamTest::kSendSsrc =
+ VideoSendStreamTest::kSendSsrcs[0];
-TEST_F(VideoSendStreamTest, SendsSetSsrc) {
- class SendSsrcObserver : public SendTransportObserver {
+void VideoSendStreamTest::SendsSetSsrcs(size_t num_ssrcs,
+ bool send_single_ssrc_first) {
+ class SendSsrcObserver : public test::RtpRtcpObserver {
public:
- SendSsrcObserver() : SendTransportObserver(30 * 1000) {}
-
- virtual bool SendRTP(const uint8_t* packet, size_t length) OVERRIDE {
- RTPHeader header;
- EXPECT_TRUE(
- rtp_header_parser_->Parse(packet, static_cast<int>(length), &header));
-
- if (header.ssrc == kSendSsrc)
- send_test_complete_->Set();
-
- return true;
+ SendSsrcObserver(const uint32_t* ssrcs,
+ size_t num_ssrcs,
+ bool send_single_ssrc_first)
+ : RtpRtcpObserver(30 * 1000),
+ ssrcs_to_observe_(num_ssrcs),
+ expect_single_ssrc_(send_single_ssrc_first) {
+ for (size_t i = 0; i < num_ssrcs; ++i)
+ valid_ssrcs_[ssrcs[i]] = true;
}
- } observer;
- Call::Config call_config(&observer);
+ virtual Action OnSendRtp(const uint8_t* packet, size_t length) OVERRIDE {
+ RTPHeader header;
+ EXPECT_TRUE(parser_->Parse(packet, static_cast<int>(length), &header));
+
+ // TODO(pbos): Reenable this part of the test when #1695 is resolved and
+ // all SSRCs are allocated on startup. This test was observed
+ // to fail on TSan as the codec gets set before the SSRCs are
+ // set up and some frames are sent on a random-generated SSRC
+ // before the correct SSRC gets set.
+ //EXPECT_TRUE(valid_ssrcs_[header.ssrc])
+ // << "Received unknown SSRC: " << header.ssrc;
+ //
+ //if (!valid_ssrcs_[header.ssrc])
+ // observation_complete_->Set();
+
+ if (!is_observed_[header.ssrc]) {
+ is_observed_[header.ssrc] = true;
+ --ssrcs_to_observe_;
+ if (expect_single_ssrc_) {
+ expect_single_ssrc_ = false;
+ observation_complete_->Set();
+ }
+ }
+
+ if (ssrcs_to_observe_ == 0)
+ observation_complete_->Set();
+
+ return SEND_PACKET;
+ }
+
+ private:
+ std::map<uint32_t, bool> valid_ssrcs_;
+ std::map<uint32_t, bool> is_observed_;
+ size_t ssrcs_to_observe_;
+ bool expect_single_ssrc_;
+ } observer(kSendSsrcs, num_ssrcs, send_single_ssrc_first);
+
+ Call::Config call_config(observer.SendTransport());
scoped_ptr<Call> call(Call::Create(call_config));
- VideoSendStream::Config send_config = GetSendTestConfig(call.get());
- send_config.rtp.max_packet_size = 128;
+ VideoSendStream::Config send_config =
+ GetSendTestConfig(call.get(), num_ssrcs);
- RunSendTest(call.get(), send_config, &observer);
+ if (num_ssrcs > 1) {
+ // Set low simulcast bitrates to not have to wait for bandwidth ramp-up.
+ for (size_t i = 0; i < num_ssrcs; ++i) {
+ send_config.codec.simulcastStream[i].minBitrate = 10;
+ send_config.codec.simulcastStream[i].targetBitrate = 10;
+ send_config.codec.simulcastStream[i].maxBitrate = 10;
+ }
+ }
+
+ if (send_single_ssrc_first)
+ send_config.codec.numberOfSimulcastStreams = 1;
+
+ send_stream_ = call->CreateVideoSendStream(send_config);
+ scoped_ptr<test::FrameGeneratorCapturer> frame_generator_capturer(
+ test::FrameGeneratorCapturer::Create(
+ send_stream_->Input(), 320, 240, 30, Clock::GetRealTimeClock()));
+ send_stream_->StartSending();
+ frame_generator_capturer->Start();
+
+ EXPECT_EQ(kEventSignaled, observer.Wait())
+ << "Timed out while waiting for "
+ << (send_single_ssrc_first ? "first SSRC." : "SSRCs.");
+
+ if (send_single_ssrc_first) {
+ // Set full simulcast and continue with the rest of the SSRCs.
+ send_config.codec.numberOfSimulcastStreams =
+ static_cast<unsigned char>(num_ssrcs);
+ send_stream_->SetCodec(send_config.codec);
+ EXPECT_EQ(kEventSignaled, observer.Wait())
+ << "Timed out while waiting on additional SSRCs.";
+ }
+
+ observer.StopSending();
+ frame_generator_capturer->Stop();
+ send_stream_->StopSending();
+ call->DestroyVideoSendStream(send_stream_);
+}
+
+TEST_F(VideoSendStreamTest, SendsSetSsrc) { SendsSetSsrcs(1, false); }
+
+TEST_F(VideoSendStreamTest, SendsSetSimulcastSsrcs) {
+ SendsSetSsrcs(kNumSendSsrcs, false);
+}
+
+TEST_F(VideoSendStreamTest, CanSwitchToUseAllSsrcs) {
+ SendsSetSsrcs(kNumSendSsrcs, true);
}
TEST_F(VideoSendStreamTest, SupportsCName) {
static std::string kCName = "PjQatC14dGfbVwGPUOA9IH7RlsFDbWl4AhXEiDsBizo=";
- class CNameObserver : public SendTransportObserver {
+ class CNameObserver : public test::RtpRtcpObserver {
public:
- CNameObserver() : SendTransportObserver(30 * 1000) {}
+ CNameObserver() : RtpRtcpObserver(30 * 1000) {}
- virtual bool SendRTCP(const uint8_t* packet, size_t length) OVERRIDE {
+ virtual Action OnSendRtcp(const uint8_t* packet, size_t length) OVERRIDE {
RTCPUtility::RTCPParserV2 parser(packet, length, true);
EXPECT_TRUE(parser.IsValid());
@@ -133,20 +216,20 @@
while (packet_type != RTCPUtility::kRtcpNotValidCode) {
if (packet_type == RTCPUtility::kRtcpSdesChunkCode) {
EXPECT_EQ(parser.Packet().CName.CName, kCName);
- send_test_complete_->Set();
+ observation_complete_->Set();
}
packet_type = parser.Iterate();
}
- return true;
+ return SEND_PACKET;
}
} observer;
- Call::Config call_config(&observer);
+ Call::Config call_config(observer.SendTransport());
scoped_ptr<Call> call(Call::Create(call_config));
- VideoSendStream::Config send_config = GetSendTestConfig(call.get());
+ VideoSendStream::Config send_config = GetSendTestConfig(call.get(), 1);
send_config.rtp.c_name = kCName;
RunSendTest(call.get(), send_config, &observer);
@@ -154,31 +237,31 @@
TEST_F(VideoSendStreamTest, SupportsAbsoluteSendTime) {
static const uint8_t kAbsSendTimeExtensionId = 13;
- class AbsoluteSendTimeObserver : public SendTransportObserver {
+ class AbsoluteSendTimeObserver : public test::RtpRtcpObserver {
public:
- AbsoluteSendTimeObserver() : SendTransportObserver(30 * 1000) {
- EXPECT_TRUE(rtp_header_parser_->RegisterRtpHeaderExtension(
+ AbsoluteSendTimeObserver() : RtpRtcpObserver(30 * 1000) {
+ EXPECT_TRUE(parser_->RegisterRtpHeaderExtension(
kRtpExtensionAbsoluteSendTime, kAbsSendTimeExtensionId));
}
- virtual bool SendRTP(const uint8_t* packet, size_t length) OVERRIDE {
+ virtual Action OnSendRtp(const uint8_t* packet, size_t length) OVERRIDE {
RTPHeader header;
EXPECT_TRUE(
- rtp_header_parser_->Parse(packet, static_cast<int>(length), &header));
+ parser_->Parse(packet, static_cast<int>(length), &header));
if (header.extension.absoluteSendTime > 0)
- send_test_complete_->Set();
+ observation_complete_->Set();
- return true;
+ return SEND_PACKET;
}
} observer;
- Call::Config call_config(&observer);
+ Call::Config call_config(observer.SendTransport());
scoped_ptr<Call> call(Call::Create(call_config));
- VideoSendStream::Config send_config = GetSendTestConfig(call.get());
+ VideoSendStream::Config send_config = GetSendTestConfig(call.get(), 1);
send_config.rtp.extensions.push_back(
- RtpExtension("abs-send-time", kAbsSendTimeExtensionId));
+ RtpExtension(RtpExtension::kAbsSendTime, kAbsSendTimeExtensionId));
RunSendTest(call.get(), send_config, &observer);
}
@@ -199,32 +282,32 @@
}
} encoder(Clock::GetRealTimeClock());
- class TransmissionTimeOffsetObserver : public SendTransportObserver {
+ class TransmissionTimeOffsetObserver : public test::RtpRtcpObserver {
public:
- TransmissionTimeOffsetObserver() : SendTransportObserver(30 * 1000) {
- EXPECT_TRUE(rtp_header_parser_->RegisterRtpHeaderExtension(
+ TransmissionTimeOffsetObserver() : RtpRtcpObserver(30 * 1000) {
+ EXPECT_TRUE(parser_->RegisterRtpHeaderExtension(
kRtpExtensionTransmissionTimeOffset, kTOffsetExtensionId));
}
- virtual bool SendRTP(const uint8_t* packet, size_t length) OVERRIDE {
+ virtual Action OnSendRtp(const uint8_t* packet, size_t length) OVERRIDE {
RTPHeader header;
EXPECT_TRUE(
- rtp_header_parser_->Parse(packet, static_cast<int>(length), &header));
+ parser_->Parse(packet, static_cast<int>(length), &header));
EXPECT_GT(header.extension.transmissionTimeOffset, 0);
- send_test_complete_->Set();
+ observation_complete_->Set();
- return true;
+ return SEND_PACKET;
}
} observer;
- Call::Config call_config(&observer);
+ Call::Config call_config(observer.SendTransport());
scoped_ptr<Call> call(Call::Create(call_config));
- VideoSendStream::Config send_config = GetSendTestConfig(call.get());
+ VideoSendStream::Config send_config = GetSendTestConfig(call.get(), 1);
send_config.encoder = &encoder;
send_config.rtp.extensions.push_back(
- RtpExtension("toffset", kTOffsetExtensionId));
+ RtpExtension(RtpExtension::kTOffset, kTOffsetExtensionId));
RunSendTest(call.get(), send_config, &observer);
}
@@ -288,23 +371,19 @@
TEST_F(VideoSendStreamTest, SupportsFec) {
static const int kRedPayloadType = 118;
static const int kUlpfecPayloadType = 119;
- class FecObserver : public SendTransportObserver {
+ class FecObserver : public test::RtpRtcpObserver {
public:
FecObserver()
- : SendTransportObserver(30 * 1000),
- transport_adapter_(&transport_),
+ : RtpRtcpObserver(30 * 1000),
+ transport_adapter_(SendTransport()),
send_count_(0),
received_media_(false),
received_fec_(false) {}
- void SetReceiver(PacketReceiver* receiver) {
- transport_.SetReceiver(receiver);
- }
-
- virtual bool SendRTP(const uint8_t* packet, size_t length) OVERRIDE {
+ virtual Action OnSendRtp(const uint8_t* packet, size_t length) OVERRIDE {
RTPHeader header;
EXPECT_TRUE(
- rtp_header_parser_->Parse(packet, static_cast<int>(length), &header));
+ parser_->Parse(packet, static_cast<int>(length), &header));
// Send lossy receive reports to trigger FEC enabling.
if (send_count_++ % 2 != 0) {
@@ -334,51 +413,49 @@
}
if (received_media_ && received_fec_)
- send_test_complete_->Set();
+ observation_complete_->Set();
- return true;
+ return SEND_PACKET;
}
- virtual void Stop() OVERRIDE { transport_.StopSending(); }
-
private:
internal::TransportAdapter transport_adapter_;
- test::DirectTransport transport_;
int send_count_;
bool received_media_;
bool received_fec_;
} observer;
- Call::Config call_config(&observer);
+ Call::Config call_config(observer.SendTransport());
scoped_ptr<Call> call(Call::Create(call_config));
- observer.SetReceiver(call->Receiver());
+ observer.SetReceivers(call->Receiver(), NULL);
- VideoSendStream::Config send_config = GetSendTestConfig(call.get());
+ VideoSendStream::Config send_config = GetSendTestConfig(call.get(), 1);
send_config.rtp.fec.red_payload_type = kRedPayloadType;
send_config.rtp.fec.ulpfec_payload_type = kUlpfecPayloadType;
RunSendTest(call.get(), send_config, &observer);
}
-void VideoSendStreamTest::TestNackRetransmission(uint32_t retransmit_ssrc) {
- class NackObserver : public SendTransportObserver {
+void VideoSendStreamTest::TestNackRetransmission(
+ uint32_t retransmit_ssrc,
+ uint8_t retransmit_payload_type,
+ bool enable_pacing) {
+ class NackObserver : public test::RtpRtcpObserver {
public:
- explicit NackObserver(uint32_t retransmit_ssrc)
- : SendTransportObserver(30 * 1000),
- transport_adapter_(&transport_),
+ explicit NackObserver(uint32_t retransmit_ssrc,
+ uint8_t retransmit_payload_type)
+ : RtpRtcpObserver(30 * 1000),
+ transport_adapter_(SendTransport()),
send_count_(0),
retransmit_ssrc_(retransmit_ssrc),
+ retransmit_payload_type_(retransmit_payload_type),
nacked_sequence_number_(0) {}
- void SetReceiver(PacketReceiver* receiver) {
- transport_.SetReceiver(receiver);
- }
-
- virtual bool SendRTP(const uint8_t* packet, size_t length) OVERRIDE {
+ virtual Action OnSendRtp(const uint8_t* packet, size_t length) OVERRIDE {
RTPHeader header;
EXPECT_TRUE(
- rtp_header_parser_->Parse(packet, static_cast<int>(length), &header));
+ parser_->Parse(packet, static_cast<int>(length), &header));
// Nack second packet after receiving the third one.
if (++send_count_ == 3) {
@@ -408,28 +485,29 @@
if (sequence_number == nacked_sequence_number_) {
EXPECT_EQ(retransmit_ssrc_, header.ssrc);
- send_test_complete_->Set();
+ EXPECT_EQ(retransmit_payload_type_, header.payloadType);
+ observation_complete_->Set();
}
- return true;
+ return SEND_PACKET;
}
- virtual void Stop() OVERRIDE { transport_.StopSending(); }
-
private:
internal::TransportAdapter transport_adapter_;
- test::DirectTransport transport_;
int send_count_;
uint32_t retransmit_ssrc_;
+ uint8_t retransmit_payload_type_;
uint16_t nacked_sequence_number_;
- } observer(retransmit_ssrc);
+ } observer(retransmit_ssrc, retransmit_payload_type);
- Call::Config call_config(&observer);
+ Call::Config call_config(observer.SendTransport());
scoped_ptr<Call> call(Call::Create(call_config));
- observer.SetReceiver(call->Receiver());
+ observer.SetReceivers(call->Receiver(), NULL);
- VideoSendStream::Config send_config = GetSendTestConfig(call.get());
+ VideoSendStream::Config send_config = GetSendTestConfig(call.get(), 1);
send_config.rtp.nack.rtp_history_ms = 1000;
+ send_config.rtp.rtx.rtx_payload_type = retransmit_payload_type;
+ send_config.pacing = enable_pacing;
if (retransmit_ssrc != kSendSsrc)
send_config.rtp.rtx.ssrcs.push_back(retransmit_ssrc);
@@ -438,24 +516,29 @@
TEST_F(VideoSendStreamTest, RetransmitsNack) {
// Normal NACKs should use the send SSRC.
- TestNackRetransmission(kSendSsrc);
+ TestNackRetransmission(kSendSsrc, kFakeSendPayloadType, false);
}
TEST_F(VideoSendStreamTest, RetransmitsNackOverRtx) {
// NACKs over RTX should use a separate SSRC.
- TestNackRetransmission(kSendRtxSsrc);
+ TestNackRetransmission(kSendRtxSsrc, kSendRtxPayloadType, false);
+}
+
+TEST_F(VideoSendStreamTest, RetransmitsNackOverRtxWithPacing) {
+ // NACKs over RTX should use a separate SSRC.
+ TestNackRetransmission(kSendRtxSsrc, kSendRtxPayloadType, true);
}
TEST_F(VideoSendStreamTest, MaxPacketSize) {
- class PacketSizeObserver : public SendTransportObserver {
+ class PacketSizeObserver : public test::RtpRtcpObserver {
public:
- PacketSizeObserver(size_t max_length) : SendTransportObserver(30 * 1000),
+ PacketSizeObserver(size_t max_length) : RtpRtcpObserver(30 * 1000),
max_length_(max_length), accumulated_size_(0) {}
- virtual bool SendRTP(const uint8_t* packet, size_t length) OVERRIDE {
+ virtual Action OnSendRtp(const uint8_t* packet, size_t length) OVERRIDE {
RTPHeader header;
EXPECT_TRUE(
- rtp_header_parser_->Parse(packet, static_cast<int>(length), &header));
+ parser_->Parse(packet, static_cast<int>(length), &header));
EXPECT_LE(length, max_length_);
@@ -466,12 +549,12 @@
if (accumulated_size_ + length > max_length_) {
// The packet was fragmented, total size was larger than max size,
// but size of individual fragments were within size limit => pass!
- send_test_complete_->Set();
+ observation_complete_->Set();
}
accumulated_size_ = 0; // Last fragment, reset packet size
}
- return true;
+ return SEND_PACKET;
}
private:
@@ -482,36 +565,100 @@
static const uint32_t kMaxPacketSize = 128;
PacketSizeObserver observer(kMaxPacketSize);
- Call::Config call_config(&observer);
+ Call::Config call_config(observer.SendTransport());
scoped_ptr<Call> call(Call::Create(call_config));
- VideoSendStream::Config send_config = GetSendTestConfig(call.get());
+ VideoSendStream::Config send_config = GetSendTestConfig(call.get(), 1);
send_config.rtp.max_packet_size = kMaxPacketSize;
RunSendTest(call.get(), send_config, &observer);
}
+TEST_F(VideoSendStreamTest, CanChangeSendCodec) {
+ static const uint8_t kFirstPayloadType = 121;
+ static const uint8_t kSecondPayloadType = 122;
+
+ class CodecChangeObserver : public test::RtpRtcpObserver {
+ public:
+ CodecChangeObserver(VideoSendStream** send_stream_ptr)
+ : RtpRtcpObserver(30 * 1000),
+ received_first_payload_(EventWrapper::Create()),
+ send_stream_ptr_(send_stream_ptr) {}
+
+ virtual Action OnSendRtp(const uint8_t* packet, size_t length) OVERRIDE {
+ RTPHeader header;
+ EXPECT_TRUE(parser_->Parse(packet, static_cast<int>(length), &header));
+
+ if (header.payloadType == kFirstPayloadType) {
+ received_first_payload_->Set();
+ } else if (header.payloadType == kSecondPayloadType) {
+ observation_complete_->Set();
+ }
+
+ return SEND_PACKET;
+ }
+
+ virtual EventTypeWrapper Wait() OVERRIDE {
+ EXPECT_EQ(kEventSignaled, received_first_payload_->Wait(30 * 1000))
+ << "Timed out while waiting for first payload.";
+
+ EXPECT_TRUE((*send_stream_ptr_)->SetCodec(second_codec_));
+
+ EXPECT_EQ(kEventSignaled, RtpRtcpObserver::Wait())
+ << "Timed out while waiting for second payload type.";
+
+ // Return OK regardless, prevents double error reporting.
+ return kEventSignaled;
+ }
+
+ void SetSecondCodec(const VideoCodec& codec) {
+ second_codec_ = codec;
+ }
+
+ private:
+ scoped_ptr<EventWrapper> received_first_payload_;
+ VideoSendStream** send_stream_ptr_;
+ VideoCodec second_codec_;
+ } observer(&send_stream_);
+
+ Call::Config call_config(observer.SendTransport());
+ scoped_ptr<Call> call(Call::Create(call_config));
+
+ std::vector<VideoCodec> codecs = call->GetVideoCodecs();
+ ASSERT_GE(codecs.size(), 2u)
+ << "Test needs at least 2 separate codecs to work.";
+ codecs[0].plType = kFirstPayloadType;
+ codecs[1].plType = kSecondPayloadType;
+ observer.SetSecondCodec(codecs[1]);
+
+ VideoSendStream::Config send_config = GetSendTestConfig(call.get(), 1);
+ send_config.codec = codecs[0];
+ send_config.encoder = NULL;
+
+ RunSendTest(call.get(), send_config, &observer);
+}
+
// The test will go through a number of phases.
// 1. Start sending packets.
// 2. As soon as the RTP stream has been detected, signal a low REMB value to
-// activate the auto muter.
-// 3. Wait until |kMuteTimeFrames| have been captured without seeing any RTP
+// suspend the stream.
+// 3. Wait until |kSuspendTimeFrames| have been captured without seeing any RTP
// packets.
-// 4. Signal a high REMB and the wait for the RTP stream to start again.
+// 4. Signal a high REMB and then wait for the RTP stream to start again.
// When the stream is detected again, the test ends.
-TEST_F(VideoSendStreamTest, AutoMute) {
- static const int kMuteTimeFrames = 60; // Mute for 2 seconds @ 30 fps.
+TEST_F(VideoSendStreamTest, SuspendBelowMinBitrate) {
+ static const int kSuspendTimeFrames = 60; // Suspend for 2 seconds @ 30 fps.
- class RembObserver : public SendTransportObserver, public I420FrameCallback {
+ class RembObserver : public test::RtpRtcpObserver, public I420FrameCallback {
public:
RembObserver()
- : SendTransportObserver(30 * 1000), // Timeout after 30 seconds.
+ : RtpRtcpObserver(30 * 1000), // Timeout after 30 seconds.
transport_adapter_(&transport_),
clock_(Clock::GetRealTimeClock()),
- test_state_(kBeforeMute),
+ test_state_(kBeforeSuspend),
rtp_count_(0),
last_sequence_number_(0),
- mute_frame_count_(0),
+ suspended_frame_count_(0),
low_remb_bps_(0),
high_remb_bps_(0),
crit_sect_(CriticalSectionWrapper::CreateCriticalSection()) {}
@@ -520,39 +667,50 @@
transport_.SetReceiver(receiver);
}
- virtual bool SendRTCP(const uint8_t* packet, size_t length) OVERRIDE {
+ virtual Action OnSendRtcp(const uint8_t* packet, size_t length) OVERRIDE {
// Receive statistics reporting having lost 0% of the packets.
// This is needed for the send-side bitrate controller to work properly.
CriticalSectionScoped lock(crit_sect_.get());
SendRtcpFeedback(0); // REMB is only sent if value is > 0.
- return true;
+ return SEND_PACKET;
}
- virtual bool SendRTP(const uint8_t* packet, size_t length) OVERRIDE {
+ virtual Action OnSendRtp(const uint8_t* packet, size_t length) OVERRIDE {
CriticalSectionScoped lock(crit_sect_.get());
++rtp_count_;
RTPHeader header;
- EXPECT_TRUE(
- rtp_header_parser_->Parse(packet, static_cast<int>(length), &header));
+ EXPECT_TRUE(parser_->Parse(packet, static_cast<int>(length), &header));
last_sequence_number_ = header.sequenceNumber;
- if (test_state_ == kBeforeMute) {
- // The stream has started. Try to mute it.
+ if (test_state_ == kBeforeSuspend) {
+ // The stream has started. Try to suspend it.
SendRtcpFeedback(low_remb_bps_);
- test_state_ = kDuringMute;
- } else if (test_state_ == kDuringMute) {
- mute_frame_count_ = 0;
+ test_state_ = kDuringSuspend;
+ } else if (test_state_ == kDuringSuspend) {
+ if (header.paddingLength == 0) {
+ // Received non-padding packet during suspension period. Reset the
+ // counter.
+ // TODO(hlundin): We should probably make this test more advanced in
+ // the future, so that it verifies that the bitrate can go below the
+ // min_bitrate. This requires that the fake encoder sees the
+ // min_bitrate, and never goes below it. See WebRTC Issue 2655.
+ suspended_frame_count_ = 0;
+ }
} else if (test_state_ == kWaitingForPacket) {
- send_test_complete_->Set();
+ if (header.paddingLength == 0) {
+ // Non-padding packet observed. Test is complete.
+ observation_complete_->Set();
+ }
}
- return true;
+ return SEND_PACKET;
}
// This method implements the I420FrameCallback.
void FrameCallback(I420VideoFrame* video_frame) OVERRIDE {
CriticalSectionScoped lock(crit_sect_.get());
- if (test_state_ == kDuringMute && ++mute_frame_count_ > kMuteTimeFrames) {
+ if (test_state_ == kDuringSuspend &&
+ ++suspended_frame_count_ > kSuspendTimeFrames) {
SendRtcpFeedback(high_remb_bps_);
test_state_ = kWaitingForPacket;
}
@@ -562,14 +720,14 @@
void set_high_remb_bps(int value) { high_remb_bps_ = value; }
- virtual void Stop() OVERRIDE { transport_.StopSending(); }
+ virtual void Stop() { transport_.StopSending(); }
private:
enum TestState {
- kBeforeMute,
- kDuringMute,
+ kBeforeSuspend,
+ kDuringSuspend,
kWaitingForPacket,
- kAfterMute
+ kAfterSuspend
};
virtual void SendRtcpFeedback(int remb_value) {
@@ -594,20 +752,20 @@
TestState test_state_;
int rtp_count_;
int last_sequence_number_;
- int mute_frame_count_;
+ int suspended_frame_count_;
int low_remb_bps_;
int high_remb_bps_;
scoped_ptr<CriticalSectionWrapper> crit_sect_;
} observer;
- Call::Config call_config(&observer);
+ Call::Config call_config(observer.SendTransport());
scoped_ptr<Call> call(Call::Create(call_config));
observer.SetReceiver(call->Receiver());
- VideoSendStream::Config send_config = GetSendTestConfig(call.get());
+ VideoSendStream::Config send_config = GetSendTestConfig(call.get(), 1);
send_config.rtp.nack.rtp_history_ms = 1000;
send_config.pre_encode_callback = &observer;
- send_config.auto_mute = true;
+ send_config.suspend_below_min_bitrate = true;
unsigned int min_bitrate_bps =
send_config.codec.simulcastStream[0].minBitrate * 1000;
observer.set_low_remb_bps(min_bitrate_bps - 10000);
@@ -619,4 +777,78 @@
RunSendTest(call.get(), send_config, &observer);
}
+TEST_F(VideoSendStreamTest, NoPaddingWhenVideoIsMuted) {
+ class PacketObserver : public test::RtpRtcpObserver {
+ public:
+ PacketObserver()
+ : RtpRtcpObserver(30 * 1000), // Timeout after 30 seconds.
+ clock_(Clock::GetRealTimeClock()),
+ last_packet_time_ms_(-1),
+ transport_adapter_(ReceiveTransport()),
+ capturer_(NULL),
+ crit_sect_(CriticalSectionWrapper::CreateCriticalSection()) {}
+
+ void SetCapturer(test::FrameGeneratorCapturer* capturer) {
+ capturer_ = capturer;
+ }
+
+ virtual Action OnSendRtp(const uint8_t* packet, size_t length) OVERRIDE {
+ CriticalSectionScoped lock(crit_sect_.get());
+ last_packet_time_ms_ = clock_->TimeInMilliseconds();
+ capturer_->Stop();
+ return SEND_PACKET;
+ }
+
+ virtual Action OnSendRtcp(const uint8_t* packet, size_t length) OVERRIDE {
+ CriticalSectionScoped lock(crit_sect_.get());
+ const int kVideoMutedThresholdMs = 10000;
+ if (last_packet_time_ms_ > 0 && clock_->TimeInMilliseconds() -
+ last_packet_time_ms_ > kVideoMutedThresholdMs)
+ observation_complete_->Set();
+ // Receive statistics reporting having lost 50% of the packets.
+ FakeReceiveStatistics receive_stats(kSendSsrcs[0], 1, 1, 0);
+ RTCPSender rtcp_sender(
+ 0, false, Clock::GetRealTimeClock(), &receive_stats);
+ EXPECT_EQ(0, rtcp_sender.RegisterSendTransport(&transport_adapter_));
+
+ rtcp_sender.SetRTCPStatus(kRtcpNonCompound);
+ rtcp_sender.SetRemoteSSRC(kSendSsrcs[0]);
+
+ RTCPSender::FeedbackState feedback_state;
+
+ EXPECT_EQ(0, rtcp_sender.SendRTCP(feedback_state, kRtcpRr));
+ return SEND_PACKET;
+ }
+
+ private:
+ Clock* clock_;
+ int64_t last_packet_time_ms_;
+ internal::TransportAdapter transport_adapter_;
+ test::FrameGeneratorCapturer* capturer_;
+ scoped_ptr<CriticalSectionWrapper> crit_sect_;
+ } observer;
+
+ Call::Config call_config(observer.SendTransport());
+ scoped_ptr<Call> call(Call::Create(call_config));
+ observer.SetReceivers(call->Receiver(), call->Receiver());
+
+ VideoSendStream::Config send_config = GetSendTestConfig(call.get(), 3);
+
+ send_stream_ = call->CreateVideoSendStream(send_config);
+ scoped_ptr<test::FrameGeneratorCapturer> frame_generator_capturer(
+ test::FrameGeneratorCapturer::Create(
+ send_stream_->Input(), 320, 240, 30, Clock::GetRealTimeClock()));
+ observer.SetCapturer(frame_generator_capturer.get());
+ send_stream_->StartSending();
+ frame_generator_capturer->Start();
+
+ EXPECT_EQ(kEventSignaled, observer.Wait())
+ << "Timed out while waiting for RTP packets to stop being sent.";
+
+ observer.StopSending();
+ frame_generator_capturer->Stop();
+ send_stream_->StopSending();
+ call->DestroyVideoSendStream(send_stream_);
+}
+
} // namespace webrtc
diff --git a/video/webrtc_video.gypi b/video/webrtc_video.gypi
index e26db0b..82e17f4 100644
--- a/video/webrtc_video.gypi
+++ b/video/webrtc_video.gypi
@@ -11,6 +11,8 @@
'<(webrtc_root)/video_engine/video_engine.gyp:video_engine_core',
],
'webrtc_video_sources': [
+ 'video/encoded_frame_callback_adapter.cc',
+ 'video/encoded_frame_callback_adapter.h',
'video/transport_adapter.cc',
'video/transport_adapter.h',
'video/video_receive_stream.cc',
diff --git a/video_engine/call_stats.cc b/video_engine/call_stats.cc
index 0184e5b..a79a7d2 100644
--- a/video_engine/call_stats.cc
+++ b/video_engine/call_stats.cc
@@ -24,7 +24,7 @@
// Time interval for updating the observers.
const int kUpdateIntervalMs = 1000;
-class RtcpObserver : public RtcpRttObserver {
+class RtcpObserver : public RtcpRttStats {
public:
explicit RtcpObserver(CallStats* owner) : owner_(owner) {}
virtual ~RtcpObserver() {}
@@ -33,6 +33,10 @@
owner_->OnRttUpdate(rtt);
}
+ virtual uint32_t LastProcessedRtt() const {
+ return owner_->last_processed_rtt_ms();
+ }
+
private:
CallStats* owner_;
@@ -41,8 +45,9 @@
CallStats::CallStats()
: crit_(CriticalSectionWrapper::CreateCriticalSection()),
- rtcp_rtt_observer_(new RtcpObserver(this)),
- last_process_time_(TickTime::MillisecondTimestamp()) {
+ rtcp_rtt_stats_(new RtcpObserver(this)),
+ last_process_time_(TickTime::MillisecondTimestamp()),
+ last_processed_rtt_ms_(0) {
}
CallStats::~CallStats() {
@@ -81,12 +86,18 @@
(*it)->OnRttUpdate(max_rtt);
}
}
+ last_processed_rtt_ms_ = max_rtt;
last_process_time_ = time_now;
return 0;
}
-RtcpRttObserver* CallStats::rtcp_rtt_observer() const {
- return rtcp_rtt_observer_.get();
+uint32_t CallStats::last_processed_rtt_ms() const {
+ CriticalSectionScoped cs(crit_.get());
+ return last_processed_rtt_ms_;
+}
+
+RtcpRttStats* CallStats::rtcp_rtt_stats() const {
+ return rtcp_rtt_stats_.get();
}
void CallStats::RegisterStatsObserver(CallStatsObserver* observer) {
diff --git a/video_engine/call_stats.h b/video_engine/call_stats.h
index 5fd93a7..5c021a4 100644
--- a/video_engine/call_stats.h
+++ b/video_engine/call_stats.h
@@ -19,9 +19,9 @@
namespace webrtc {
-class CriticalSectionWrapper;
-class RtcpRttObserver;
class CallStatsObserver;
+class CriticalSectionWrapper;
+class RtcpRttStats;
// CallStats keeps track of statistics for a call.
class CallStats : public Module {
@@ -35,9 +35,9 @@
virtual int32_t TimeUntilNextProcess();
virtual int32_t Process();
- // Returns a RtcpRttObserver to register at a statistics provider. The object
+ // Returns a RtcpRttStats to register at a statistics provider. The object
// has the same lifetime as the CallStats instance.
- RtcpRttObserver* rtcp_rtt_observer() const;
+ RtcpRttStats* rtcp_rtt_stats() const;
// Registers/deregisters a new observer to receive statistics updates.
void RegisterStatsObserver(CallStatsObserver* observer);
@@ -46,6 +46,8 @@
protected:
void OnRttUpdate(uint32_t rtt);
+ uint32_t last_processed_rtt_ms() const;
+
private:
// Helper struct keeping track of the time a rtt value is reported.
struct RttTime {
@@ -58,9 +60,11 @@
// Protecting all members.
scoped_ptr<CriticalSectionWrapper> crit_;
// Observer receiving statistics updates.
- scoped_ptr<RtcpRttObserver> rtcp_rtt_observer_;
+ scoped_ptr<RtcpRttStats> rtcp_rtt_stats_;
// The last time 'Process' resulted in statistic update.
int64_t last_process_time_;
+ // The last RTT in the statistics update (zero if there is no valid estimate).
+ uint32_t last_processed_rtt_ms_;
// All Rtt reports within valid time interval, oldest first.
std::list<RttTime> reports_;
diff --git a/video_engine/call_stats_unittest.cc b/video_engine/call_stats_unittest.cc
index e5b2361..f504094 100644
--- a/video_engine/call_stats_unittest.cc
+++ b/video_engine/call_stats_unittest.cc
@@ -41,15 +41,24 @@
TEST_F(CallStatsTest, AddAndTriggerCallback) {
MockStatsObserver stats_observer;
- RtcpRttObserver* rtcp_observer = call_stats_->rtcp_rtt_observer();
+ RtcpRttStats* rtcp_rtt_stats = call_stats_->rtcp_rtt_stats();
call_stats_->RegisterStatsObserver(&stats_observer);
TickTime::AdvanceFakeClock(1000);
+ EXPECT_EQ(0U, rtcp_rtt_stats->LastProcessedRtt());
- uint32_t rtt = 25;
- rtcp_observer->OnRttUpdate(rtt);
- EXPECT_CALL(stats_observer, OnRttUpdate(rtt))
+ const uint32_t kRtt = 25;
+ rtcp_rtt_stats->OnRttUpdate(kRtt);
+ EXPECT_CALL(stats_observer, OnRttUpdate(kRtt))
.Times(1);
call_stats_->Process();
+ EXPECT_EQ(kRtt, rtcp_rtt_stats->LastProcessedRtt());
+
+ const int kRttTimeOutMs = 1500 + 10;
+ TickTime::AdvanceFakeClock(kRttTimeOutMs);
+ EXPECT_CALL(stats_observer, OnRttUpdate(_))
+ .Times(0);
+ call_stats_->Process();
+ EXPECT_EQ(0U, rtcp_rtt_stats->LastProcessedRtt());
call_stats_->DeregisterStatsObserver(&stats_observer);
}
@@ -57,8 +66,8 @@
TEST_F(CallStatsTest, ProcessTime) {
MockStatsObserver stats_observer;
call_stats_->RegisterStatsObserver(&stats_observer);
- RtcpRttObserver* rtcp_observer = call_stats_->rtcp_rtt_observer();
- rtcp_observer->OnRttUpdate(100);
+ RtcpRttStats* rtcp_rtt_stats = call_stats_->rtcp_rtt_stats();
+ rtcp_rtt_stats->OnRttUpdate(100);
// Time isn't updated yet.
EXPECT_CALL(stats_observer, OnRttUpdate(_))
@@ -73,7 +82,7 @@
// Advance clock just too little to get an update.
TickTime::AdvanceFakeClock(999);
- rtcp_observer->OnRttUpdate(100);
+ rtcp_rtt_stats->OnRttUpdate(100);
EXPECT_CALL(stats_observer, OnRttUpdate(_))
.Times(0);
call_stats_->Process();
@@ -98,9 +107,9 @@
call_stats_->RegisterStatsObserver(&stats_observer_2);
call_stats_->RegisterStatsObserver(&stats_observer_2);
- RtcpRttObserver* rtcp_observer = call_stats_->rtcp_rtt_observer();
+ RtcpRttStats* rtcp_rtt_stats = call_stats_->rtcp_rtt_stats();
uint32_t rtt = 100;
- rtcp_observer->OnRttUpdate(rtt);
+ rtcp_rtt_stats->OnRttUpdate(rtt);
// Verify both observers are updated.
TickTime::AdvanceFakeClock(1000);
@@ -113,7 +122,7 @@
// Deregister the second observer and verify update is only sent to the first
// observer.
call_stats_->DeregisterStatsObserver(&stats_observer_2);
- rtcp_observer->OnRttUpdate(rtt);
+ rtcp_rtt_stats->OnRttUpdate(rtt);
TickTime::AdvanceFakeClock(1000);
EXPECT_CALL(stats_observer_1, OnRttUpdate(rtt))
.Times(1);
@@ -123,7 +132,7 @@
// Deregister the first observer.
call_stats_->DeregisterStatsObserver(&stats_observer_1);
- rtcp_observer->OnRttUpdate(rtt);
+ rtcp_rtt_stats->OnRttUpdate(rtt);
TickTime::AdvanceFakeClock(1000);
EXPECT_CALL(stats_observer_1, OnRttUpdate(rtt))
.Times(0);
@@ -136,14 +145,14 @@
TEST_F(CallStatsTest, ChangeRtt) {
MockStatsObserver stats_observer;
call_stats_->RegisterStatsObserver(&stats_observer);
- RtcpRttObserver* rtcp_observer = call_stats_->rtcp_rtt_observer();
+ RtcpRttStats* rtcp_rtt_stats = call_stats_->rtcp_rtt_stats();
// Advance clock to be ready for an update.
TickTime::AdvanceFakeClock(1000);
// Set a first value and verify the callback is triggered.
const uint32_t first_rtt = 100;
- rtcp_observer->OnRttUpdate(first_rtt);
+ rtcp_rtt_stats->OnRttUpdate(first_rtt);
EXPECT_CALL(stats_observer, OnRttUpdate(first_rtt))
.Times(1);
call_stats_->Process();
@@ -151,7 +160,7 @@
// Increase rtt and verify the new value is reported.
TickTime::AdvanceFakeClock(1000);
const uint32_t high_rtt = first_rtt + 20;
- rtcp_observer->OnRttUpdate(high_rtt);
+ rtcp_rtt_stats->OnRttUpdate(high_rtt);
EXPECT_CALL(stats_observer, OnRttUpdate(high_rtt))
.Times(1);
call_stats_->Process();
@@ -161,13 +170,13 @@
// in the callback.
TickTime::AdvanceFakeClock(1000);
const uint32_t low_rtt = first_rtt - 20;
- rtcp_observer->OnRttUpdate(low_rtt);
+ rtcp_rtt_stats->OnRttUpdate(low_rtt);
EXPECT_CALL(stats_observer, OnRttUpdate(high_rtt))
.Times(1);
call_stats_->Process();
- // Advance time to make the high report invalid, the lower rtt should no be in
- // the callback.
+ // Advance time to make the high report invalid, the lower rtt should now be
+ // in the callback.
TickTime::AdvanceFakeClock(1000);
EXPECT_CALL(stats_observer, OnRttUpdate(low_rtt))
.Times(1);
diff --git a/video_engine/include/vie_base.h b/video_engine/include/vie_base.h
index 1ac3fd1..1801e7b 100644
--- a/video_engine/include/vie_base.h
+++ b/video_engine/include/vie_base.h
@@ -120,6 +120,12 @@
virtual int RegisterCpuOveruseObserver(int channel,
CpuOveruseObserver* observer) = 0;
+ // Gets the last cpu overuse measure.
+ // TODO(asapersson): Remove default implementation.
+ virtual int CpuOveruseMeasures(int channel,
+ int* capture_jitter_ms,
+ int* avg_encode_time_ms) { return -1; }
+
// Specifies the VoiceEngine and VideoEngine channel pair to use for
// audio/video synchronization.
virtual int ConnectAudioChannel(const int video_channel,
diff --git a/video_engine/include/vie_codec.h b/video_engine/include/vie_codec.h
index f470438..bddcc3a 100644
--- a/video_engine/include/vie_codec.h
+++ b/video_engine/include/vie_codec.h
@@ -36,10 +36,10 @@
const unsigned int framerate,
const unsigned int bitrate) = 0;
- // This method is called whenever the state of the AutoMuter changes, i.e.,
- // when |is_muted| toggles.
+ // This method is called whenever the state of the SuspendBelowMinBitrate
+ // changes, i.e., when |is_suspended| toggles.
// TODO(hlundin): Remove the default implementation when possible.
- virtual void VideoAutoMuted(int video_channel, bool is_muted) {}
+ virtual void SuspendChange(int video_channel, bool is_suspended) {}
protected:
virtual ~ViEEncoderObserver() {}
@@ -193,12 +193,12 @@
// Disables recording of debugging information.
virtual int StopDebugRecording(int video_channel) = 0;
- // Enables AutoMuter to turn off video when the rate drops below
+ // Lets the sender suspend video when the rate drops below
// |threshold_bps|, and turns back on when the rate goes back up above
// |threshold_bps| + |window_bps|.
// This is under development; not tested.
// TODO(hlundin): Remove the default implementation when possible.
- virtual void EnableAutoMuting(int video_channel) {}
+ virtual void SuspendBelowMinBitrate(int video_channel) {}
protected:
ViECodec() {}
diff --git a/video_engine/include/vie_image_process.h b/video_engine/include/vie_image_process.h
index 9a12748..aff2d61 100644
--- a/video_engine/include/vie_image_process.h
+++ b/video_engine/include/vie_image_process.h
@@ -18,12 +18,11 @@
#define WEBRTC_VIDEO_ENGINE_INCLUDE_VIE_IMAGE_PROCESS_H_
#include "webrtc/common_types.h"
-#include "webrtc/common_video/interface/i420_video_frame.h"
namespace webrtc {
+class EncodedImageCallback;
class I420FrameCallback;
-
class VideoEngine;
// This class declares an abstract interface for a user defined effect filter.
@@ -98,6 +97,16 @@
I420FrameCallback* pre_encode_callback) = 0;
virtual void DeRegisterPreEncodeCallback(int video_channel) = 0;
+ virtual void RegisterPostEncodeImageCallback(
+ int video_channel,
+ EncodedImageCallback* post_encode_callback) {}
+ virtual void DeRegisterPostEncodeCallback(int video_channel) {}
+
+ virtual void RegisterPreDecodeImageCallback(
+ int video_channel,
+ EncodedImageCallback* pre_decode_callback) {}
+ virtual void DeRegisterPreDecodeCallback(int video_channel) {}
+
virtual void RegisterPreRenderCallback(
int video_channel,
I420FrameCallback* pre_render_callback) = 0;
diff --git a/video_engine/include/vie_render.h b/video_engine/include/vie_render.h
index 48afc1a..ab61d7a 100644
--- a/video_engine/include/vie_render.h
+++ b/video_engine/include/vie_render.h
@@ -22,10 +22,11 @@
class VideoEngine;
class VideoRender;
+class VideoRenderCallback;
// This class declares an abstract interface to be used for external renderers.
// The user implemented derived class is registered using AddRenderer().
-class WEBRTC_DLLEXPORT ExternalRenderer {
+class ExternalRenderer {
public:
// This method will be called when the stream to be rendered changes in
// resolution or number of streams mixed in the image.
@@ -51,7 +52,7 @@
virtual ~ExternalRenderer() {}
};
-class WEBRTC_DLLEXPORT ViERender {
+class ViERender {
public:
// Factory for the ViERender sub‐API and increases an internal reference
// counter if successful. Returns NULL if the API is not supported or if
@@ -111,6 +112,13 @@
RawVideoType video_input_format,
ExternalRenderer* renderer) = 0;
+ // Propagating VideoRenderCallback down to the VideoRender module for new API.
+ // Contains default-implementation not to break code mocking this interface.
+ // (Ugly, but temporary.)
+ virtual int AddRenderCallback(int render_id, VideoRenderCallback* callback) {
+ return 0;
+ }
+
protected:
ViERender() {}
virtual ~ViERender() {}
diff --git a/video_engine/include/vie_rtp_rtcp.h b/video_engine/include/vie_rtp_rtcp.h
index 135f7f8..9d899ad 100644
--- a/video_engine/include/vie_rtp_rtcp.h
+++ b/video_engine/include/vie_rtp_rtcp.h
@@ -254,6 +254,11 @@
bool enable,
int id) = 0;
+ // Enables/disables RTCP Receiver Reference Time Report Block extension/
+ // DLRR Report Block extension (RFC 3611).
+ // TODO(asapersson): Remove default implementation.
+ virtual int SetRtcpXrRrtrStatus(int video_channel, bool enable) { return -1; }
+
// Enables transmission smoothening, i.e. packets belonging to the same frame
// will be sent over a longer period of time instead of sending them
// back-to-back.
@@ -262,29 +267,97 @@
// This function returns our locally created statistics of the received RTP
// stream.
- virtual int GetReceivedRTCPStatistics(
- const int video_channel,
- unsigned short& fraction_lost,
- unsigned int& cumulative_lost,
- unsigned int& extended_max,
- unsigned int& jitter,
- int& rtt_ms) const = 0;
+ virtual int GetReceiveChannelRtcpStatistics(const int video_channel,
+ RtcpStatistics& basic_stats,
+ int& rtt_ms) const = 0;
// This function returns statistics reported by the remote client in a RTCP
// packet.
+ virtual int GetSendChannelRtcpStatistics(const int video_channel,
+ RtcpStatistics& basic_stats,
+ int& rtt_ms) const = 0;
+
+ // TODO(sprang): Temporary hacks to prevent libjingle build from failing,
+ // remove when libjingle has been lifted to support webrtc issue 2589
+ virtual int GetReceivedRTCPStatistics(const int video_channel,
+ unsigned short& fraction_lost,
+ unsigned int& cumulative_lost,
+ unsigned int& extended_max,
+ unsigned int& jitter,
+ int& rtt_ms) const {
+ RtcpStatistics stats;
+ int ret_code = GetReceiveChannelRtcpStatistics(video_channel,
+ stats,
+ rtt_ms);
+ fraction_lost = stats.fraction_lost;
+ cumulative_lost = stats.cumulative_lost;
+ extended_max = stats.extended_max_sequence_number;
+ jitter = stats.jitter;
+ return ret_code;
+ }
virtual int GetSentRTCPStatistics(const int video_channel,
- unsigned short& fraction_lost,
- unsigned int& cumulative_lost,
- unsigned int& extended_max,
- unsigned int& jitter,
- int& rtt_ms) const = 0;
+ unsigned short& fraction_lost,
+ unsigned int& cumulative_lost,
+ unsigned int& extended_max,
+ unsigned int& jitter,
+ int& rtt_ms) const {
+ RtcpStatistics stats;
+ int ret_code = GetSendChannelRtcpStatistics(video_channel,
+ stats,
+ rtt_ms);
+ fraction_lost = stats.fraction_lost;
+ cumulative_lost = stats.cumulative_lost;
+ extended_max = stats.extended_max_sequence_number;
+ jitter = stats.jitter;
+ return ret_code;
+ }
+
+
+ virtual int RegisterSendChannelRtcpStatisticsCallback(
+ int video_channel, RtcpStatisticsCallback* callback) = 0;
+
+ virtual int DeregisterSendChannelRtcpStatisticsCallback(
+ int video_channel, RtcpStatisticsCallback* callback) = 0;
+
+ virtual int RegisterReceiveChannelRtcpStatisticsCallback(
+ int video_channel, RtcpStatisticsCallback* callback) = 0;
+
+ virtual int DeregisterReceiveChannelRtcpStatisticsCallback(
+ int video_channel, RtcpStatisticsCallback* callback) = 0;
// The function gets statistics from the sent and received RTP streams.
+ virtual int GetRtpStatistics(const int video_channel,
+ StreamDataCounters& sent,
+ StreamDataCounters& received) const = 0;
+
+ // TODO(sprang): Temporary hacks to prevent libjingle build from failing,
+ // remove when libjingle has been lifted to support webrtc issue 2589
virtual int GetRTPStatistics(const int video_channel,
- unsigned int& bytes_sent,
- unsigned int& packets_sent,
- unsigned int& bytes_received,
- unsigned int& packets_received) const = 0;
+ unsigned int& bytes_sent,
+ unsigned int& packets_sent,
+ unsigned int& bytes_received,
+ unsigned int& packets_received) const {
+ StreamDataCounters sent;
+ StreamDataCounters received;
+ int ret_code = GetRtpStatistics(video_channel, sent, received);
+ bytes_sent = sent.bytes;
+ packets_sent = sent.packets;
+ bytes_received = received.bytes;
+ packets_received = received.packets;
+ return ret_code;
+ }
+
+ virtual int RegisterSendChannelRtpStatisticsCallback(
+ int video_channel, StreamDataCountersCallback* callback) = 0;
+
+ virtual int DeregisterSendChannelRtpStatisticsCallback(
+ int video_channel, StreamDataCountersCallback* callback) = 0;
+
+ virtual int RegisterReceiveChannelRtpStatisticsCallback(
+ int video_channel, StreamDataCountersCallback* callback) = 0;
+
+ virtual int DeregisterReceiveChannelRtpStatisticsCallback(
+ int video_channel, StreamDataCountersCallback* callback) = 0;
// The function gets bandwidth usage statistics from the sent RTP streams in
// bits/s.
@@ -294,6 +367,15 @@
unsigned int& fec_bitrate_sent,
unsigned int& nackBitrateSent) const = 0;
+ // (De)Register an observer, called whenever the send bitrate is updated
+ virtual int RegisterSendBitrateObserver(
+ int video_channel,
+ BitrateStatisticsObserver* observer) = 0;
+
+ virtual int DeregisterSendBitrateObserver(
+ int video_channel,
+ BitrateStatisticsObserver* observer) = 0;
+
// This function gets the send-side estimated bandwidth available for video,
// including overhead, in bits/s.
virtual int GetEstimatedSendBandwidth(
@@ -334,8 +416,15 @@
// Removes a registered instance of ViERTCPObserver.
virtual int DeregisterRTCPObserver(const int video_channel) = 0;
+ // Registers and instance of a user implementation of ViEFrameCountObserver
+ virtual int RegisterSendFrameCountObserver(
+ int video_channel, FrameCountObserver* observer) = 0;
+
+ // Removes a registered instance of a ViEFrameCountObserver
+ virtual int DeregisterSendFrameCountObserver(
+ int video_channel, FrameCountObserver* observer) = 0;
+
protected:
- ViERTP_RTCP() {}
virtual ~ViERTP_RTCP() {}
};
diff --git a/video_engine/overuse_frame_detector.cc b/video_engine/overuse_frame_detector.cc
index 96cea17..c284f64 100644
--- a/video_engine/overuse_frame_detector.cc
+++ b/video_engine/overuse_frame_detector.cc
@@ -47,6 +47,8 @@
// Expontential back-off factor, to prevent annoying up-down behaviour.
const double kRampUpBackoffFactor = 2.0;
+// The initial average encode time (set to a fairly small value).
+const float kInitialAvgEncodeTimeMs = 5.0f;
} // namespace
Statistics::Statistics() :
@@ -116,7 +118,8 @@
last_rampup_time_(0),
in_quick_rampup_(false),
current_rampup_delay_ms_(kStandardRampUpDelayMs),
- num_pixels_(0) {}
+ num_pixels_(0),
+ avg_encode_time_ms_(kInitialAvgEncodeTimeMs) {}
OveruseFrameDetector::~OveruseFrameDetector() {
}
@@ -144,6 +147,23 @@
last_capture_time_ = time;
}
+void OveruseFrameDetector::FrameEncoded(int encode_time_ms) {
+ CriticalSectionScoped cs(crit_.get());
+ const float kWeight = 0.1f;
+ avg_encode_time_ms_ = kWeight * encode_time_ms +
+ (1.0f - kWeight) * avg_encode_time_ms_;
+}
+
+int OveruseFrameDetector::last_capture_jitter_ms() const {
+ CriticalSectionScoped cs(crit_.get());
+ return static_cast<int>(capture_deltas_.StdDev() + 0.5);
+}
+
+int OveruseFrameDetector::avg_encode_time_ms() const {
+ CriticalSectionScoped cs(crit_.get());
+ return static_cast<int>(avg_encode_time_ms_ + 0.5);
+}
+
int32_t OveruseFrameDetector::TimeUntilNextProcess() {
CriticalSectionScoped cs(crit_.get());
return next_process_time_ - clock_->TimeInMilliseconds();
diff --git a/video_engine/overuse_frame_detector.h b/video_engine/overuse_frame_detector.h
index 4717a60..356e27f 100644
--- a/video_engine/overuse_frame_detector.h
+++ b/video_engine/overuse_frame_detector.h
@@ -71,6 +71,14 @@
// Called for each captured frame.
void FrameCaptured(int width, int height);
+ void FrameEncoded(int encode_time_ms);
+
+ int last_capture_jitter_ms() const;
+
+ // Running average of reported encode time (FrameEncoded()).
+ // Only used for stats.
+ int avg_encode_time_ms() const;
+
// Implements Module.
virtual int32_t TimeUntilNextProcess() OVERRIDE;
virtual int32_t Process() OVERRIDE;
@@ -105,6 +113,8 @@
// Number of pixels of last captured frame.
int num_pixels_;
+ float avg_encode_time_ms_;
+
DISALLOW_COPY_AND_ASSIGN(OveruseFrameDetector);
};
diff --git a/video_engine/overuse_frame_detector_unittest.cc b/video_engine/overuse_frame_detector_unittest.cc
index d199569..131bbfd 100644
--- a/video_engine/overuse_frame_detector_unittest.cc
+++ b/video_engine/overuse_frame_detector_unittest.cc
@@ -95,4 +95,18 @@
TriggerOveruse();
}
+TEST_F(OveruseFrameDetectorTest, LastCaptureJitter) {
+ EXPECT_EQ(0, overuse_detector_->last_capture_jitter_ms());
+ TriggerOveruse();
+ EXPECT_GT(overuse_detector_->last_capture_jitter_ms(), 0);
+}
+
+TEST_F(OveruseFrameDetectorTest, EncodedFrame) {
+ const int kInitialAvgEncodeTimeInMs = 5;
+ EXPECT_EQ(kInitialAvgEncodeTimeInMs, overuse_detector_->avg_encode_time_ms());
+ for (int i = 0; i < 30; i++)
+ overuse_detector_->FrameEncoded(2);
+ EXPECT_EQ(2, overuse_detector_->avg_encode_time_ms());
+}
+
} // namespace webrtc
diff --git a/video_engine/stream_synchronization.cc b/video_engine/stream_synchronization.cc
index fcff782..6192dfa 100644
--- a/video_engine/stream_synchronization.cc
+++ b/video_engine/stream_synchronization.cc
@@ -12,6 +12,7 @@
#include <assert.h>
#include <math.h>
+#include <stdlib.h>
#include <algorithm>
diff --git a/video_engine/test/android/jni/android_media_codec_decoder.cc b/video_engine/test/android/jni/android_media_codec_decoder.cc
index 9e45c8e..b9712ea 100644
--- a/video_engine/test/android/jni/android_media_codec_decoder.cc
+++ b/video_engine/test/android/jni/android_media_codec_decoder.cc
@@ -58,7 +58,7 @@
__android_log_print(ANDROID_LOG_DEBUG, LOG_TAG,
"Could not attach thread to JVM (%d, %p)", ret,
env_);
- return WEBRTC_VIDEO_CODEC_UNINITIALIZED;
+ return WEBRTC_VIDEO_CODEC_ERROR;
} else {
vm_attached_ = true;
}
@@ -68,9 +68,13 @@
mediaCodecDecoder_ = env_->NewGlobalRef(env_->NewObject(decoderClass_, mid));
mid = env_->GetMethodID(
- decoderClass_, "configure", "(Landroid/view/SurfaceView;II)V");
- env_->CallVoidMethod(mediaCodecDecoder_, mid, surface_,
- codecSettings->width, codecSettings->height);
+ decoderClass_, "configure", "(Landroid/view/SurfaceView;II)Z");
+ bool success = env_->CallBooleanMethod(
+ mediaCodecDecoder_, mid, surface_, codecSettings->width,
+ codecSettings->height);
+ if (!success) {
+ return WEBRTC_VIDEO_CODEC_ERROR;
+ }
setEncodedImageID_ = env_->GetMethodID(
decoderClass_, "setEncodedImage", "(Ljava/nio/ByteBuffer;J)V");
diff --git a/video_engine/test/android/src/org/webrtc/videoengine/ViEMediaCodecDecoder.java b/video_engine/test/android/src/org/webrtc/videoengine/ViEMediaCodecDecoder.java
index 9ca4125..1d5bb39 100644
--- a/video_engine/test/android/src/org/webrtc/videoengine/ViEMediaCodecDecoder.java
+++ b/video_engine/test/android/src/org/webrtc/videoengine/ViEMediaCodecDecoder.java
@@ -20,6 +20,7 @@
import android.view.Surface;
import android.view.SurfaceView;
+import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.LinkedList;
@@ -245,7 +246,7 @@
private Thread mLooperThread;
- public void configure(SurfaceView surfaceView, int width, int height) {
+ public boolean configure(SurfaceView surfaceView, int width, int height) {
mSurfaceView = surfaceView;
Log.d(TAG, "configure " + "width" + width + "height" + height + mSurfaceView.toString());
@@ -253,18 +254,30 @@
format.setString(MediaFormat.KEY_MIME, "video/x-vnd.on2.vp8");
format.setInteger(MediaFormat.KEY_WIDTH, width);
format.setInteger(MediaFormat.KEY_HEIGHT, height);
- MediaCodec codec = MediaCodec.createDecoderByType("video/x-vnd.on2.vp8");
- // SW VP8 decoder
- // MediaCodec codec = MediaCodec.createByCodecName("OMX.google.vpx.decoder");
- // Nexus10 HW VP8 decoder
- // MediaCodec codec = MediaCodec.createByCodecName("OMX.Exynos.VP8.Decoder");
+
Surface surface = mSurfaceView.getHolder().getSurface();
Log.d(TAG, "Surface " + surface.isValid());
- codec.configure(
- format, surface, null, 0);
- mCodecState = new CodecState(this, format, codec);
+ MediaCodec codec;
+ try {
+ codec = MediaCodec.createDecoderByType("video/x-vnd.on2.vp8");
+ // SW VP8 decoder
+ // codec = MediaCodec.createByCodecName("OMX.google.vpx.decoder");
+ // Nexus10 HW VP8 decoder
+ // codec = MediaCodec.createByCodecName("OMX.Exynos.VP8.Decoder");
+ } catch (Exception e) {
+ // TODO(dwkang): replace this instanceof/throw with a narrower catch clause
+ // once the SDK advances.
+ if (e instanceof IOException) {
+ Log.e(TAG, "Failed to create MediaCodec for VP8.", e);
+ return false;
+ }
+ throw new RuntimeException(e);
+ }
+ codec.configure(format, surface, null, 0);
+ mCodecState = new CodecState(this, format, codec);
initMediaCodecView();
+ return true;
}
public void setEncodedImage(ByteBuffer buffer, long renderTimeMs) {
diff --git a/video_engine/test/auto_test/interface/vie_autotest_defines.h b/video_engine/test/auto_test/interface/vie_autotest_defines.h
index 83476e0..3e8d762 100644
--- a/video_engine/test/auto_test/interface/vie_autotest_defines.h
+++ b/video_engine/test/auto_test/interface/vie_autotest_defines.h
@@ -49,9 +49,8 @@
#undef RGB
#define RGB(r,g,b) r|g<<8|b<<16
-enum {
- kAutoTestSleepTimeMs = 5000
-};
+enum { kAutoTestSleepTimeMs = 5000 };
+enum { kAutoTestFullStackSleepTimeMs = 20000 };
struct AutoTestSize {
unsigned int width;
diff --git a/video_engine/test/auto_test/primitives/framedrop_primitives.cc b/video_engine/test/auto_test/primitives/framedrop_primitives.cc
index a79f7d5..99531d5 100644
--- a/video_engine/test/auto_test/primitives/framedrop_primitives.cc
+++ b/video_engine/test/auto_test/primitives/framedrop_primitives.cc
@@ -218,7 +218,7 @@
decode_filter));
// Send video.
EXPECT_EQ(0, base_interface->StartSend(video_channel));
- AutoTestSleep(kAutoTestSleepTimeMs);
+ AutoTestSleep(kAutoTestFullStackSleepTimeMs);
ViETest::Log("Done!");
diff --git a/video_engine/test/auto_test/source/vie_autotest_codec.cc b/video_engine/test/auto_test/source/vie_autotest_codec.cc
index 3fa2bc4..0ebfbec 100644
--- a/video_engine/test/auto_test/source/vie_autotest_codec.cc
+++ b/video_engine/test/auto_test/source/vie_autotest_codec.cc
@@ -44,7 +44,7 @@
unsigned int last_outgoing_bitrate_;
unsigned int last_incoming_framerate_;
unsigned int last_incoming_bitrate_;
- unsigned int video_auto_muted_called_;
+ unsigned int suspend_change_called_;
webrtc::VideoCodec incoming_codec_;
@@ -60,7 +60,7 @@
last_outgoing_bitrate_(0),
last_incoming_framerate_(0),
last_incoming_bitrate_(0),
- video_auto_muted_called_(0) {
+ suspend_change_called_(0) {
memset(&incoming_codec_, 0, sizeof(incoming_codec_));
}
virtual void IncomingCodecChanged(const int video_channel,
@@ -100,8 +100,8 @@
last_outgoing_bitrate_ += bitrate;
}
- virtual void VideoAutoMuted(int video_channel, bool is_muted) {
- video_auto_muted_called_++;
+ virtual void SuspendChange(int video_channel, bool is_suspended) OVERRIDE {
+ suspend_change_called_++;
}
virtual void RequestNewKeyFrame(const int video_channel) {
diff --git a/video_engine/test/auto_test/source/vie_autotest_custom_call.cc b/video_engine/test/auto_test/source/vie_autotest_custom_call.cc
index 8d7613d..f9a5c22 100644
--- a/video_engine/test/auto_test/source/vie_autotest_custom_call.cc
+++ b/video_engine/test/auto_test/source/vie_autotest_custom_call.cc
@@ -73,8 +73,8 @@
<< " BR: " << bitrate << std::endl;
}
- virtual void VideoAutoMuted(int video_channel, bool is_muted) {
- std::cout << "VideoAutoMuted: " << is_muted << std::endl;
+ virtual void SuspendChange(int video_channel, bool is_suspended) OVERRIDE {
+ std::cout << "SuspendChange: " << is_suspended << std::endl;
}
};
@@ -1509,10 +1509,7 @@
StatisticsType stat_type) {
int error = 0;
int number_of_errors = 0;
- uint16_t fraction_lost = 0;
- unsigned int cumulative_lost = 0;
- unsigned int extended_max = 0;
- unsigned int jitter = 0;
+ webrtc::RtcpStatistics rtcp_stats;
int rtt_ms = 0;
switch (stat_type) {
@@ -1520,11 +1517,9 @@
std::cout << "RTCP Received statistics"
<< std::endl;
// Get and print the Received RTCP Statistics
- error = vie_rtp_rtcp->GetReceivedRTCPStatistics(video_channel,
- fraction_lost,
- cumulative_lost,
- extended_max,
- jitter, rtt_ms);
+ error = vie_rtp_rtcp->GetReceiveChannelRtcpStatistics(video_channel,
+ rtcp_stats,
+ rtt_ms);
number_of_errors += ViETest::TestError(error == 0,
"ERROR: %s at line %d",
__FUNCTION__, __LINE__);
@@ -1533,22 +1528,22 @@
std::cout << "RTCP Sent statistics"
<< std::endl;
// Get and print the Sent RTCP Statistics
- error = vie_rtp_rtcp->GetSentRTCPStatistics(video_channel, fraction_lost,
- cumulative_lost, extended_max,
- jitter, rtt_ms);
+ error = vie_rtp_rtcp->GetSendChannelRtcpStatistics(video_channel,
+ rtcp_stats,
+ rtt_ms);
number_of_errors += ViETest::TestError(error == 0,
"ERROR: %s at line %d",
__FUNCTION__, __LINE__);
break;
}
std::cout << "\tRTCP fraction of lost packets: "
- << fraction_lost << std::endl;
+ << rtcp_stats.fraction_lost << std::endl;
std::cout << "\tRTCP cumulative number of lost packets: "
- << cumulative_lost << std::endl;
+ << rtcp_stats.cumulative_lost << std::endl;
std::cout << "\tRTCP max received sequence number "
- << extended_max << std::endl;
+ << rtcp_stats.extended_max_sequence_number << std::endl;
std::cout << "\tRTCP jitter: "
- << jitter << std::endl;
+ << rtcp_stats.jitter << std::endl;
std::cout << "\tRTCP round trip (ms): "
<< rtt_ms << std::endl;
}
@@ -1557,29 +1552,25 @@
int video_channel) {
int error = 0;
int number_of_errors = 0;
- unsigned int bytes_sent = 0;
- unsigned int packets_sent = 0;
- unsigned int bytes_received = 0;
- unsigned int packets_received = 0;
+ webrtc::StreamDataCounters sent;
+ webrtc::StreamDataCounters received;
std::cout << "RTP statistics"
<< std::endl;
// Get and print the RTP Statistics
- error = vie_rtp_rtcp->GetRTPStatistics(video_channel, bytes_sent,
- packets_sent, bytes_received,
- packets_received);
+ error = vie_rtp_rtcp->GetRtpStatistics(video_channel, sent, received);
number_of_errors += ViETest::TestError(error == 0,
"ERROR: %s at line %d",
__FUNCTION__, __LINE__);
std::cout << "\tRTP bytes sent: "
- << bytes_sent << std::endl;
+ << sent.bytes << std::endl;
std::cout << "\tRTP packets sent: "
- << packets_sent << std::endl;
+ << sent.packets << std::endl;
std::cout << "\tRTP bytes received: "
- << bytes_received << std::endl;
+ << received.bytes << std::endl;
std::cout << "\tRTP packets received: "
- << packets_received << std::endl;
+ << received.packets << std::endl;
}
void PrintBandwidthUsage(webrtc::ViERTP_RTCP* vie_rtp_rtcp,
diff --git a/video_engine/test/auto_test/source/vie_autotest_rtp_rtcp.cc b/video_engine/test/auto_test/source/vie_autotest_rtp_rtcp.cc
index 3b4753e..56a6a6a 100644
--- a/video_engine/test/auto_test/source/vie_autotest_rtp_rtcp.cc
+++ b/video_engine/test/auto_test/source/vie_autotest_rtp_rtcp.cc
@@ -12,6 +12,7 @@
#include "webrtc/engine_configurations.h"
#include "webrtc/test/testsupport/fileutils.h"
+#include "webrtc/video_engine/include/vie_rtp_rtcp.h"
#include "webrtc/video_engine/test/auto_test/interface/vie_autotest.h"
#include "webrtc/video_engine/test/auto_test/interface/vie_autotest_defines.h"
#include "webrtc/video_engine/test/libvietest/include/tb_capture_device.h"
@@ -164,10 +165,7 @@
//
// Pacing
//
- unsigned short recFractionsLost = 0;
- unsigned int recCumulativeLost = 0;
- unsigned int recExtendedMax = 0;
- unsigned int recJitter = 0;
+ webrtc::RtcpStatistics received;
int recRttMs = 0;
unsigned int sentTotalBitrate = 0;
unsigned int sentVideoBitrate = 0;
@@ -193,9 +191,8 @@
AutoTestSleep(kAutoTestSleepTimeMs);
- EXPECT_EQ(0, ViE.rtp_rtcp->GetReceivedRTCPStatistics(
- tbChannel.videoChannel, recFractionsLost, recCumulativeLost,
- recExtendedMax, recJitter, recRttMs));
+ EXPECT_EQ(0, ViE.rtp_rtcp->GetReceiveChannelRtcpStatistics(
+ tbChannel.videoChannel, received, recRttMs));
EXPECT_EQ(0, ViE.rtp_rtcp->GetBandwidthUsage(
tbChannel.videoChannel, sentTotalBitrate, sentVideoBitrate,
sentFecBitrate, sentNackBitrate));
@@ -211,7 +208,7 @@
EXPECT_GT(num_rtcp_packets, 0);
EXPECT_GT(sentTotalBitrate, 0u);
EXPECT_EQ(sentNackBitrate, 0u);
- EXPECT_EQ(recCumulativeLost, 0u);
+ EXPECT_EQ(received.cumulative_lost, 0u);
//
// RTX
@@ -248,9 +245,8 @@
myTransport.SetNetworkParameters(network);
AutoTestSleep(kAutoTestSleepTimeMs);
- EXPECT_EQ(0, ViE.rtp_rtcp->GetReceivedRTCPStatistics(
- tbChannel.videoChannel, recFractionsLost, recCumulativeLost,
- recExtendedMax, recJitter, recRttMs));
+ EXPECT_EQ(0, ViE.rtp_rtcp->GetReceiveChannelRtcpStatistics(
+ tbChannel.videoChannel, received, recRttMs));
EXPECT_EQ(0, ViE.rtp_rtcp->GetBandwidthUsage(
tbChannel.videoChannel, sentTotalBitrate, sentVideoBitrate,
sentFecBitrate, sentNackBitrate));
@@ -292,10 +288,7 @@
AutoTestSleep(kAutoTestSleepTimeMs);
- unsigned short sentFractionsLost = 0;
- unsigned int sentCumulativeLost = 0;
- unsigned int sentExtendedMax = 0;
- unsigned int sentJitter = 0;
+ webrtc::RtcpStatistics sent;
int sentRttMs = 0;
EXPECT_EQ(0, ViE.rtp_rtcp->GetBandwidthUsage(
@@ -310,21 +303,19 @@
AutoTestSleep(2000);
- EXPECT_EQ(0, ViE.rtp_rtcp->GetSentRTCPStatistics(
- tbChannel.videoChannel, sentFractionsLost, sentCumulativeLost,
- sentExtendedMax, sentJitter, sentRttMs));
- EXPECT_GT(sentCumulativeLost, 0u);
- EXPECT_GT(sentExtendedMax, startSequenceNumber);
- EXPECT_GT(sentJitter, 0u);
+ EXPECT_EQ(0, ViE.rtp_rtcp->GetSendChannelRtcpStatistics(
+ tbChannel.videoChannel, sent, sentRttMs));
+ EXPECT_GT(sent.cumulative_lost, 0u);
+ EXPECT_GT(sent.extended_max_sequence_number, startSequenceNumber);
+ EXPECT_GT(sent.jitter, 0u);
EXPECT_GT(sentRttMs, 0);
- EXPECT_EQ(0, ViE.rtp_rtcp->GetReceivedRTCPStatistics(
- tbChannel.videoChannel, recFractionsLost, recCumulativeLost,
- recExtendedMax, recJitter, recRttMs));
+ EXPECT_EQ(0, ViE.rtp_rtcp->GetReceiveChannelRtcpStatistics(
+ tbChannel.videoChannel, received, recRttMs));
- EXPECT_GT(recCumulativeLost, 0u);
- EXPECT_GT(recExtendedMax, startSequenceNumber);
- EXPECT_GT(recJitter, 0u);
+ EXPECT_GT(received.cumulative_lost, 0u);
+ EXPECT_GT(received.extended_max_sequence_number, startSequenceNumber);
+ EXPECT_GT(received.jitter, 0u);
EXPECT_GT(recRttMs, 0);
unsigned int estimated_bandwidth = 0;
@@ -351,7 +342,8 @@
}
// Check that rec stats extended max is greater than what we've sent.
- EXPECT_GE(recExtendedMax, sentExtendedMax);
+ EXPECT_GE(received.extended_max_sequence_number,
+ sent.extended_max_sequence_number);
EXPECT_EQ(0, ViE.base->StopSend(tbChannel.videoChannel));
//
@@ -486,62 +478,51 @@
EXPECT_EQ(0, ViE.rtp_rtcp->SetNACKStatus(tbChannel.videoChannel, true));
EXPECT_EQ(0, ViE.rtp_rtcp->SetTransmissionSmoothingStatus(
tbChannel.videoChannel, true));
- unsigned int bytes_sent_before = 0;
- unsigned int packets_sent_before = 0;
- unsigned int bytes_received_before = 0;
- unsigned int packets_received_before = 0;
- unsigned int bytes_sent_after = 0;
- unsigned int packets_sent_after = 0;
- unsigned int bytes_received_after = 0;
- unsigned int packets_received_after = 0;
- EXPECT_EQ(0, ViE.rtp_rtcp->GetRTPStatistics(tbChannel.videoChannel,
- bytes_sent_before,
- packets_sent_before,
- bytes_received_before,
- packets_received_before));
+
+ webrtc::StreamDataCounters sent_before;
+ webrtc::StreamDataCounters received_before;
+ webrtc::StreamDataCounters sent_after;
+ webrtc::StreamDataCounters received_after;
+
+ EXPECT_EQ(0, ViE.rtp_rtcp->GetRtpStatistics(tbChannel.videoChannel,
+ sent_before,
+ received_before));
EXPECT_EQ(0, ViE.base->StartReceive(tbChannel.videoChannel));
EXPECT_EQ(0, ViE.base->StartSend(tbChannel.videoChannel));
// Real-time mode.
AutoTestSleep(kAutoTestSleepTimeMs);
- EXPECT_EQ(0, ViE.rtp_rtcp->GetRTPStatistics(tbChannel.videoChannel,
- bytes_sent_after,
- packets_sent_after,
- bytes_received_after,
- packets_received_after));
+ EXPECT_EQ(0, ViE.rtp_rtcp->GetRtpStatistics(tbChannel.videoChannel,
+ sent_after, received_after));
if (FLAGS_include_timing_dependent_tests) {
- EXPECT_GT(bytes_received_after, bytes_received_before);
+ EXPECT_GT(received_after.bytes, received_before.bytes);
}
// Simulate lost reception and verify that nothing is sent during that time.
ViE.network->SetNetworkTransmissionState(tbChannel.videoChannel, false);
// Allow the encoder to finish the current frame before we expect that no
// additional packets will be sent.
AutoTestSleep(kAutoTestSleepTimeMs);
- bytes_received_before = bytes_received_after;
+ received_before.bytes = received_after.bytes;
ViETest::Log("Network Down...\n");
AutoTestSleep(kAutoTestSleepTimeMs);
- EXPECT_EQ(0, ViE.rtp_rtcp->GetRTPStatistics(tbChannel.videoChannel,
- bytes_sent_after,
- packets_sent_after,
- bytes_received_after,
- packets_received_after));
+ EXPECT_EQ(0, ViE.rtp_rtcp->GetRtpStatistics(tbChannel.videoChannel,
+ sent_before,
+ received_before));
if (FLAGS_include_timing_dependent_tests) {
- EXPECT_EQ(bytes_received_before, bytes_received_after);
+ EXPECT_EQ(received_before.bytes, received_after.bytes);
}
// Network reception back. Video should now be sent.
ViE.network->SetNetworkTransmissionState(tbChannel.videoChannel, true);
ViETest::Log("Network Up...\n");
AutoTestSleep(kAutoTestSleepTimeMs);
- EXPECT_EQ(0, ViE.rtp_rtcp->GetRTPStatistics(tbChannel.videoChannel,
- bytes_sent_before,
- packets_sent_before,
- bytes_received_before,
- packets_received_before));
+ EXPECT_EQ(0, ViE.rtp_rtcp->GetRtpStatistics(tbChannel.videoChannel,
+ sent_before,
+ received_before));
if (FLAGS_include_timing_dependent_tests) {
- EXPECT_GT(bytes_received_before, bytes_received_after);
+ EXPECT_GT(received_before.bytes, received_after.bytes);
}
- bytes_received_after = bytes_received_before;
+ received_after.bytes = received_before.bytes;
// Buffering mode.
EXPECT_EQ(0, ViE.base->StopSend(tbChannel.videoChannel));
EXPECT_EQ(0, ViE.base->StopReceive(tbChannel.videoChannel));
@@ -561,35 +542,29 @@
// Allow the encoder to finish the current frame before we expect that no
// additional packets will be sent.
AutoTestSleep(kAutoTestSleepTimeMs);
- EXPECT_EQ(0, ViE.rtp_rtcp->GetRTPStatistics(tbChannel.videoChannel,
- bytes_sent_before,
- packets_sent_before,
- bytes_received_before,
- packets_received_before));
+ EXPECT_EQ(0, ViE.rtp_rtcp->GetRtpStatistics(tbChannel.videoChannel,
+ sent_before,
+ received_before));
if (FLAGS_include_timing_dependent_tests) {
- EXPECT_GT(bytes_received_before, bytes_received_after);
+ EXPECT_GT(received_before.bytes, received_after.bytes);
}
- bytes_received_after = bytes_received_before;
+ received_after.bytes = received_before.bytes;
AutoTestSleep(kAutoTestSleepTimeMs);
- EXPECT_EQ(0, ViE.rtp_rtcp->GetRTPStatistics(tbChannel.videoChannel,
- bytes_sent_after,
- packets_sent_after,
- bytes_received_after,
- packets_received_after));
+ EXPECT_EQ(0, ViE.rtp_rtcp->GetRtpStatistics(tbChannel.videoChannel,
+ sent_before,
+ received_before));
if (FLAGS_include_timing_dependent_tests) {
- EXPECT_EQ(bytes_received_after, bytes_received_before);
+ EXPECT_EQ(received_after.bytes, received_before.bytes);
}
// Network reception back. Video should now be sent.
ViETest::Log("Network Up...\n");
ViE.network->SetNetworkTransmissionState(tbChannel.videoChannel, true);
AutoTestSleep(kAutoTestSleepTimeMs);
- EXPECT_EQ(0, ViE.rtp_rtcp->GetRTPStatistics(tbChannel.videoChannel,
- bytes_sent_before,
- packets_sent_before,
- bytes_received_before,
- packets_received_before));
+ EXPECT_EQ(0, ViE.rtp_rtcp->GetRtpStatistics(tbChannel.videoChannel,
+ sent_before,
+ received_before));
if (FLAGS_include_timing_dependent_tests) {
- EXPECT_GT(bytes_received_before, bytes_received_after);
+ EXPECT_GT(received_before.bytes, received_after.bytes);
}
// TODO(holmer): Verify that the decoded framerate doesn't decrease on an
// outage when in buffering mode. This isn't currently possible because we
diff --git a/video_engine/test/auto_test/source/vie_autotest_win.cc b/video_engine/test/auto_test/source/vie_autotest_win.cc
index 8d5cb49..98e37a4 100755
--- a/video_engine/test/auto_test/source/vie_autotest_win.cc
+++ b/video_engine/test/auto_test/source/vie_autotest_win.cc
@@ -64,7 +64,7 @@
ViEDestroyWindow(_hwnd1);
}
if (_hwnd2) {
- ViEDestroyWindow(_hwnd1);
+ ViEDestroyWindow(_hwnd2);
}
delete &_crit;
}
diff --git a/video_engine/vie_base_impl.cc b/video_engine/vie_base_impl.cc
index 53d2f99..8d6aeb2 100644
--- a/video_engine/vie_base_impl.cc
+++ b/video_engine/vie_base_impl.cc
@@ -117,6 +117,36 @@
return 0;
}
+int ViEBaseImpl::CpuOveruseMeasures(int video_channel,
+ int* capture_jitter_ms,
+ int* avg_encode_time_ms) {
+ ViEChannelManagerScoped cs(*(shared_data_.channel_manager()));
+ ViEChannel* vie_channel = cs.Channel(video_channel);
+ if (!vie_channel) {
+ WEBRTC_TRACE(kTraceError,
+ kTraceVideo,
+ ViEId(shared_data_.instance_id()),
+ "%s: channel %d doesn't exist",
+ __FUNCTION__,
+ video_channel);
+ shared_data_.SetLastError(kViEBaseInvalidChannelId);
+ return -1;
+ }
+ ViEEncoder* vie_encoder = cs.Encoder(video_channel);
+ assert(vie_encoder);
+
+ ViEInputManagerScoped is(*(shared_data_.input_manager()));
+ ViEFrameProviderBase* provider = is.FrameProvider(vie_encoder);
+ if (provider) {
+ ViECapturer* capturer = is.Capture(provider->Id());
+ if (capturer) {
+ capturer->CpuOveruseMeasures(capture_jitter_ms, avg_encode_time_ms);
+ return 0;
+ }
+ }
+ return -1;
+}
+
int ViEBaseImpl::CreateChannel(int& video_channel) { // NOLINT
WEBRTC_TRACE(kTraceApiCall, kTraceVideo, ViEId(shared_data_.instance_id()),
"%s", __FUNCTION__);
@@ -347,7 +377,7 @@
// Add WebRTC Version.
std::stringstream version_stream;
- version_stream << "VideoEngine 3.46.0" << std::endl;
+ version_stream << "VideoEngine 3.47.0" << std::endl;
// Add build info.
version_stream << "Build: svn:" << WEBRTC_SVNREVISION << " " << BUILDINFO
diff --git a/video_engine/vie_base_impl.h b/video_engine/vie_base_impl.h
index fca63f4..1411caf 100644
--- a/video_engine/vie_base_impl.h
+++ b/video_engine/vie_base_impl.h
@@ -33,6 +33,9 @@
virtual int SetVoiceEngine(VoiceEngine* voice_engine);
virtual int RegisterCpuOveruseObserver(int channel,
CpuOveruseObserver* observer);
+ virtual int CpuOveruseMeasures(int channel,
+ int* capture_jitter_ms,
+ int* avg_encode_time_ms);
virtual int CreateChannel(int& video_channel); // NOLINT
virtual int CreateChannel(int& video_channel, // NOLINT
int original_channel);
diff --git a/video_engine/vie_capturer.cc b/video_engine/vie_capturer.cc
index e380bc3..d7e33e7 100644
--- a/video_engine/vie_capturer.cc
+++ b/video_engine/vie_capturer.cc
@@ -43,6 +43,7 @@
external_capture_module_(NULL),
module_process_thread_(module_process_thread),
capture_id_(capture_id),
+ incoming_frame_cs_(CriticalSectionWrapper::CreateCriticalSection()),
capture_thread_(*ThreadWrapper::CreateThread(ViECaptureThreadFunction,
this, kHighPriority,
"ViECaptureThread")),
@@ -266,6 +267,12 @@
overuse_detector_->SetObserver(observer);
}
+void ViECapturer::CpuOveruseMeasures(int* capture_jitter_ms,
+ int* avg_encode_time_ms) const {
+ *capture_jitter_ms = overuse_detector_->last_capture_jitter_ms();
+ *avg_encode_time_ms = overuse_detector_->avg_encode_time_ms();
+}
+
int32_t ViECapturer::SetCaptureDelay(int32_t delay_ms) {
return capture_module_->SetCaptureDelay(delay_ms);
}
@@ -323,17 +330,32 @@
return -1;
}
- VideoFrameI420 frame;
- frame.width = video_frame.width;
- frame.height = video_frame.height;
- frame.y_plane = video_frame.y_plane;
- frame.u_plane = video_frame.u_plane;
- frame.v_plane = video_frame.v_plane;
- frame.y_pitch = video_frame.y_pitch;
- frame.u_pitch = video_frame.u_pitch;
- frame.v_pitch = video_frame.v_pitch;
+ int size_y = video_frame.height * video_frame.y_pitch;
+ int size_u = video_frame.u_pitch * ((video_frame.height + 1) / 2);
+ int size_v = video_frame.v_pitch * ((video_frame.height + 1) / 2);
+ CriticalSectionScoped cs(incoming_frame_cs_.get());
+ int ret = incoming_frame_.CreateFrame(size_y,
+ video_frame.y_plane,
+ size_u,
+ video_frame.u_plane,
+ size_v,
+ video_frame.v_plane,
+ video_frame.width,
+ video_frame.height,
+ video_frame.y_pitch,
+ video_frame.u_pitch,
+ video_frame.v_pitch);
- return external_capture_module_->IncomingFrameI420(frame, capture_time);
+ if (ret < 0) {
+ WEBRTC_TRACE(kTraceError,
+ kTraceVideo,
+ ViEId(engine_id_, capture_id_),
+ "Failed to create I420VideoFrame");
+ return -1;
+ }
+
+ return external_capture_module_->IncomingI420VideoFrame(&incoming_frame_,
+ capture_time);
}
void ViECapturer::OnIncomingCapturedFrame(const int32_t capture_id,
@@ -512,8 +534,10 @@
bool ViECapturer::ViECaptureProcess() {
if (capture_event_.Wait(kThreadWaitTimeMs) == kEventSignaled) {
+ int64_t encode_start_time = -1;
deliver_cs_->Enter();
if (SwapCapturedAndDeliverFrameIfAvailable()) {
+ encode_start_time = Clock::GetRealTimeClock()->TimeInMilliseconds();
DeliverI420Frame(&deliver_frame_);
}
deliver_cs_->Leave();
@@ -524,6 +548,11 @@
reported_brightness_level_ = current_brightness_level_;
}
}
+ // Update the overuse detector with the duration.
+ if (encode_start_time != -1) {
+ overuse_detector_->FrameEncoded(
+ Clock::GetRealTimeClock()->TimeInMilliseconds() - encode_start_time);
+ }
}
// We're done!
return true;
diff --git a/video_engine/vie_capturer.h b/video_engine/vie_capturer.h
index 59c59b9..e47700f 100644
--- a/video_engine/vie_capturer.h
+++ b/video_engine/vie_capturer.h
@@ -104,6 +104,9 @@
void RegisterCpuOveruseObserver(CpuOveruseObserver* observer);
+ void CpuOveruseMeasures(int* capture_jitter_ms,
+ int* avg_encode_time_ms) const;
+
protected:
ViECapturer(int capture_id,
int engine_id,
@@ -154,6 +157,10 @@
ProcessThread& module_process_thread_;
const int capture_id_;
+ // Frame used in IncomingFrameI420.
+ scoped_ptr<CriticalSectionWrapper> incoming_frame_cs_;
+ I420VideoFrame incoming_frame_;
+
// Capture thread.
ThreadWrapper& capture_thread_;
EventWrapper& capture_event_;
@@ -178,8 +185,6 @@
CaptureCapability requested_capability_;
- I420VideoFrame capture_device_image_;
-
scoped_ptr<OveruseFrameDetector> overuse_detector_;
};
diff --git a/video_engine/vie_channel.cc b/video_engine/vie_channel.cc
index fcb15e1..4a85a18 100644
--- a/video_engine/vie_channel.cc
+++ b/video_engine/vie_channel.cc
@@ -62,7 +62,7 @@
RtcpIntraFrameObserver* intra_frame_observer,
RtcpBandwidthObserver* bandwidth_observer,
RemoteBitrateEstimator* remote_bitrate_estimator,
- RtcpRttObserver* rtt_observer,
+ RtcpRttStats* rtt_stats,
PacedSender* paced_sender,
RtpRtcp* default_rtp_rtcp,
bool sender)
@@ -85,7 +85,7 @@
rtp_observer_(NULL),
rtcp_observer_(NULL),
intra_frame_observer_(intra_frame_observer),
- rtt_observer_(rtt_observer),
+ rtt_stats_(rtt_stats),
paced_sender_(paced_sender),
bandwidth_observer_(bandwidth_observer),
send_timestamp_extension_id_(kInvalidRtpExtensionId),
@@ -115,7 +115,7 @@
configuration.rtcp_feedback = this;
configuration.intra_frame_callback = intra_frame_observer;
configuration.bandwidth_callback = bandwidth_observer;
- configuration.rtt_observer = rtt_observer;
+ configuration.rtt_stats = rtt_stats;
configuration.remote_bitrate_estimator = remote_bitrate_estimator;
configuration.paced_sender = paced_sender;
configuration.receive_statistics = vie_receiver_.GetReceiveStatistics();
@@ -319,7 +319,7 @@
configuration.outgoing_transport = &vie_sender_;
configuration.intra_frame_callback = intra_frame_observer_;
configuration.bandwidth_callback = bandwidth_observer_.get();
- configuration.rtt_observer = rtt_observer_;
+ configuration.rtt_stats = rtt_stats_;
configuration.paced_sender = paced_sender_;
RtpRtcp* rtp_rtcp = RtpRtcp::CreateRtpRtcp(configuration);
@@ -406,6 +406,7 @@
rtp_rtcp->DeregisterSendRtpHeaderExtension(
kRtpExtensionAbsoluteSendTime);
}
+ rtp_rtcp->SetRtcpXrRrtrStatus(rtp_rtcp_->RtcpXrRrtrStatus());
}
// |RegisterSimulcastRtpRtcpModules| resets all old weak pointers and old
// modules can be deleted after this step.
@@ -922,6 +923,15 @@
return receive_absolute_send_time_enabled_;
}
+void ViEChannel::SetRtcpXrRrtrStatus(bool enable) {
+ CriticalSectionScoped cs(rtp_rtcp_cs_.get());
+ rtp_rtcp_->SetRtcpXrRrtrStatus(enable);
+ for (std::list<RtpRtcp*>::iterator it = simulcast_rtp_rtcp_.begin();
+ it != simulcast_rtp_rtcp_.end(); it++) {
+ (*it)->SetRtcpXrRrtrStatus(enable);
+ }
+}
+
void ViEChannel::SetTransmissionSmoothingStatus(bool enable) {
assert(paced_sender_ && "No paced sender registered.");
paced_sender_->SetStatus(enable);
@@ -1860,6 +1870,12 @@
pre_render_callback_ = pre_render_callback;
}
+void ViEChannel::RegisterPreDecodeImageCallback(
+ EncodedImageCallback* pre_decode_callback) {
+ CriticalSectionScoped cs(callback_cs_.get());
+ vcm_.RegisterPreDecodeImageCallback(pre_decode_callback);
+}
+
void ViEChannel::OnApplicationDataReceived(const int32_t id,
const uint8_t sub_type,
const uint32_t name,
diff --git a/video_engine/vie_channel.h b/video_engine/vie_channel.h
index f774311..f6bc3fc 100644
--- a/video_engine/vie_channel.h
+++ b/video_engine/vie_channel.h
@@ -33,11 +33,12 @@
class ChannelStatsObserver;
class Config;
class CriticalSectionWrapper;
+class EncodedImageCallback;
class Encryption;
class I420FrameCallback;
class PacedSender;
class ProcessThread;
-class RtcpRttObserver;
+class RtcpRttStats;
class RtpRtcp;
class ThreadWrapper;
class ViEDecoderObserver;
@@ -69,7 +70,7 @@
RtcpIntraFrameObserver* intra_frame_observer,
RtcpBandwidthObserver* bandwidth_observer,
RemoteBitrateEstimator* remote_bitrate_estimator,
- RtcpRttObserver* rtt_observer,
+ RtcpRttStats* rtt_stats,
PacedSender* paced_sender,
RtpRtcp* default_rtp_rtcp,
bool sender);
@@ -125,6 +126,7 @@
int SetSendAbsoluteSendTimeStatus(bool enable, int id);
int SetReceiveAbsoluteSendTimeStatus(bool enable, int id);
bool GetReceiveAbsoluteSendTimeStatus() const;
+ void SetRtcpXrRrtrStatus(bool enable);
void SetTransmissionSmoothingStatus(bool enable);
int32_t EnableTMMBR(const bool enable);
int32_t EnableKeyFrameRequestCallback(const bool enable);
@@ -319,8 +321,10 @@
int32_t RegisterEffectFilter(ViEEffectFilter* effect_filter);
- // New-style callback, used by VideoReceiveStream.
+ // New-style callbacks, used by VideoReceiveStream.
void RegisterPreRenderCallback(I420FrameCallback* pre_render_callback);
+ void RegisterPreDecodeImageCallback(
+ EncodedImageCallback* pre_decode_callback);
protected:
static bool ChannelDecodeThreadFunction(void* obj);
@@ -370,7 +374,7 @@
ViERTPObserver* rtp_observer_;
ViERTCPObserver* rtcp_observer_;
RtcpIntraFrameObserver* intra_frame_observer_;
- RtcpRttObserver* rtt_observer_;
+ RtcpRttStats* rtt_stats_;
PacedSender* paced_sender_;
scoped_ptr<RtcpBandwidthObserver> bandwidth_observer_;
diff --git a/video_engine/vie_channel_manager.cc b/video_engine/vie_channel_manager.cc
index 02b991c..5fdbde5 100644
--- a/video_engine/vie_channel_manager.cc
+++ b/video_engine/vie_channel_manager.cc
@@ -104,12 +104,12 @@
group->GetRemoteBitrateEstimator();
EncoderStateFeedback* encoder_state_feedback =
group->GetEncoderStateFeedback();
- RtcpRttObserver* rtcp_rtt_observer =
- group->GetCallStats()->rtcp_rtt_observer();
+ RtcpRttStats* rtcp_rtt_stats =
+ group->GetCallStats()->rtcp_rtt_stats();
if (!(vie_encoder->Init() &&
CreateChannelObject(new_channel_id, vie_encoder, bandwidth_observer,
- remote_bitrate_estimator, rtcp_rtt_observer,
+ remote_bitrate_estimator, rtcp_rtt_stats,
encoder_state_feedback->GetRtcpIntraFrameObserver(),
true))) {
delete vie_encoder;
@@ -156,8 +156,8 @@
channel_group->GetRemoteBitrateEstimator();
EncoderStateFeedback* encoder_state_feedback =
channel_group->GetEncoderStateFeedback();
- RtcpRttObserver* rtcp_rtt_observer =
- channel_group->GetCallStats()->rtcp_rtt_observer();
+ RtcpRttStats* rtcp_rtt_stats =
+ channel_group->GetCallStats()->rtcp_rtt_stats();
ViEEncoder* vie_encoder = NULL;
if (sender) {
@@ -172,7 +172,7 @@
vie_encoder,
bandwidth_observer,
remote_bitrate_estimator,
- rtcp_rtt_observer,
+ rtcp_rtt_stats,
encoder_state_feedback->GetRtcpIntraFrameObserver(),
sender))) {
delete vie_encoder;
@@ -191,7 +191,7 @@
vie_encoder,
bandwidth_observer,
remote_bitrate_estimator,
- rtcp_rtt_observer,
+ rtcp_rtt_stats,
encoder_state_feedback->GetRtcpIntraFrameObserver(),
sender)) {
vie_encoder = NULL;
@@ -421,7 +421,7 @@
ViEEncoder* vie_encoder,
RtcpBandwidthObserver* bandwidth_observer,
RemoteBitrateEstimator* remote_bitrate_estimator,
- RtcpRttObserver* rtcp_rtt_observer,
+ RtcpRttStats* rtcp_rtt_stats,
RtcpIntraFrameObserver* intra_frame_observer,
bool sender) {
PacedSender* paced_sender = vie_encoder->GetPacedSender();
@@ -436,7 +436,7 @@
intra_frame_observer,
bandwidth_observer,
remote_bitrate_estimator,
- rtcp_rtt_observer,
+ rtcp_rtt_stats,
paced_sender,
send_rtp_rtcp_module,
sender);
diff --git a/video_engine/vie_channel_manager.h b/video_engine/vie_channel_manager.h
index 5ded956..9776435 100644
--- a/video_engine/vie_channel_manager.h
+++ b/video_engine/vie_channel_manager.h
@@ -28,7 +28,7 @@
class Config;
class CriticalSectionWrapper;
class ProcessThread;
-class RtcpRttObserver;
+class RtcpRttStats;
class ViEChannel;
class ViEEncoder;
class VoEVideoSync;
@@ -89,7 +89,7 @@
ViEEncoder* vie_encoder,
RtcpBandwidthObserver* bandwidth_observer,
RemoteBitrateEstimator* remote_bitrate_estimator,
- RtcpRttObserver* rtcp_rtt_observer,
+ RtcpRttStats* rtcp_rtt_stats,
RtcpIntraFrameObserver* intra_frame_observer,
bool sender);
diff --git a/video_engine/vie_codec_impl.cc b/video_engine/vie_codec_impl.cc
index 4d927b5..364862f 100644
--- a/video_engine/vie_codec_impl.cc
+++ b/video_engine/vie_codec_impl.cc
@@ -715,7 +715,7 @@
return vie_encoder->StopDebugRecording();
}
-void ViECodecImpl::EnableAutoMuting(int video_channel) {
+void ViECodecImpl::SuspendBelowMinBitrate(int video_channel) {
ViEChannelManagerScoped cs(*(shared_data_->channel_manager()));
ViEEncoder* vie_encoder = cs.Encoder(video_channel);
if (!vie_encoder) {
@@ -724,7 +724,18 @@
"%s: No encoder %d", __FUNCTION__, video_channel);
return;
}
- return vie_encoder->EnableAutoMuting();
+ vie_encoder->SuspendBelowMinBitrate();
+ ViEChannel* vie_channel = cs.Channel(video_channel);
+ if (!vie_channel) {
+ WEBRTC_TRACE(kTraceError, kTraceVideo,
+ ViEId(shared_data_->instance_id(), video_channel),
+ "%s: No channel %d", __FUNCTION__, video_channel);
+ return;
+ }
+ // Must enable pacing when enabling SuspendBelowMinBitrate. Otherwise, no
+ // padding will be sent when the video is suspended so the video will be
+ // unable to recover.
+ vie_channel->SetTransmissionSmoothingStatus(true);
}
bool ViECodecImpl::CodecValid(const VideoCodec& video_codec) {
diff --git a/video_engine/vie_codec_impl.h b/video_engine/vie_codec_impl.h
index 8bcac41..372ffc9 100644
--- a/video_engine/vie_codec_impl.h
+++ b/video_engine/vie_codec_impl.h
@@ -70,7 +70,7 @@
virtual int StartDebugRecording(int video_channel,
const char* file_name_utf8);
virtual int StopDebugRecording(int video_channel);
- virtual void EnableAutoMuting(int video_channel);
+ virtual void SuspendBelowMinBitrate(int video_channel);
protected:
explicit ViECodecImpl(ViESharedData* shared_data);
diff --git a/video_engine/vie_encoder.cc b/video_engine/vie_encoder.cc
index 889842e..f226ada 100644
--- a/video_engine/vie_encoder.cc
+++ b/video_engine/vie_encoder.cc
@@ -14,6 +14,7 @@
#include <algorithm>
+#include "webrtc/common_video/interface/video_image.h"
#include "webrtc/common_video/libyuv/include/webrtc_libyuv.h"
#include "webrtc/modules/pacing/include/paced_sender.h"
#include "webrtc/modules/rtp_rtcp/interface/rtp_rtcp.h"
@@ -21,6 +22,7 @@
#include "webrtc/modules/video_coding/codecs/interface/video_codec_interface.h"
#include "webrtc/modules/video_coding/main/interface/video_coding.h"
#include "webrtc/modules/video_coding/main/interface/video_coding_defines.h"
+#include "webrtc/modules/video_coding/main/source/encoded_frame.h"
#include "webrtc/system_wrappers/interface/critical_section_wrapper.h"
#include "webrtc/system_wrappers/interface/logging.h"
#include "webrtc/system_wrappers/interface/tick_util.h"
@@ -163,7 +165,7 @@
has_received_rpsi_(false),
picture_id_rpsi_(0),
qm_callback_(NULL),
- video_auto_muted_(false),
+ video_suspended_(false),
pre_encode_callback_(NULL) {
WEBRTC_TRACE(webrtc::kTraceMemory, webrtc::kTraceVideo,
ViEId(engine_id, channel_id),
@@ -525,7 +527,12 @@
}
int ViEEncoder::TimeToSendPadding(int bytes) {
- if (send_padding_) {
+ bool send_padding;
+ {
+ CriticalSectionScoped cs(data_cs_.get());
+ send_padding = send_padding_ || video_suspended_;
+ }
+ if (send_padding) {
return default_rtp_rtcp_->TimeToSendPadding(bytes);
}
return 0;
@@ -839,10 +846,15 @@
// Disable external frame-droppers.
vcm_.EnableFrameDropper(false);
vpm_.EnableTemporalDecimation(false);
+ // We don't put any limits on the pacer queue when running in buffered mode
+ // since the encoder will be paused if the queue grow too large.
+ paced_sender_->set_max_queue_length_ms(-1);
} else {
// Real-time mode - enable frame droppers.
vpm_.EnableTemporalDecimation(true);
vcm_.EnableFrameDropper(true);
+ paced_sender_->set_max_queue_length_ms(
+ PacedSender::kDefaultMaxQueueLengthMs);
}
}
@@ -1050,7 +1062,7 @@
__FUNCTION__, bitrate_bps, fraction_lost, round_trip_time_ms);
vcm_.SetChannelParameters(bitrate_bps, fraction_lost, round_trip_time_ms);
- bool video_is_muted = vcm_.VideoMuted();
+ bool video_is_suspended = vcm_.VideoSuspended();
int bitrate_kbps = bitrate_bps / 1000;
VideoCodec send_codec;
if (vcm_.SendCodec(&send_codec) != 0) {
@@ -1087,10 +1099,10 @@
pad_up_to_bitrate_kbps += stream_configs[i].targetBitrate;
}
}
- if (video_is_muted || send_codec.numberOfSimulcastStreams > 1) {
+ if (video_is_suspended || send_codec.numberOfSimulcastStreams > 1) {
pad_up_to_bitrate_kbps = std::min(bitrate_kbps, pad_up_to_bitrate_kbps);
} else {
- // Disable padding if only sending one stream and video isn't muted.
+ // Disable padding if only sending one stream and video isn't suspended.
pad_up_to_bitrate_kbps = 0;
}
@@ -1107,16 +1119,19 @@
max_padding_bitrate_kbps,
pad_up_to_bitrate_kbps);
default_rtp_rtcp_->SetTargetSendBitrate(stream_bitrates);
- if (video_is_muted != video_auto_muted_) {
- // State changed now. Send callback to inform about that.
- video_auto_muted_ = video_is_muted;
- if (codec_observer_) {
- WEBRTC_TRACE(webrtc::kTraceInfo, webrtc::kTraceVideo,
- ViEId(engine_id_, channel_id_),
- "%s: video_auto_muted_ changed to %i",
- __FUNCTION__, video_auto_muted_);
- codec_observer_->VideoAutoMuted(channel_id_, video_auto_muted_);
- }
+ {
+ CriticalSectionScoped cs(data_cs_.get());
+ if (video_suspended_ == video_is_suspended)
+ return;
+ video_suspended_ = video_is_suspended;
+ }
+ // State changed, inform codec observer.
+ if (codec_observer_) {
+ WEBRTC_TRACE(webrtc::kTraceInfo, webrtc::kTraceVideo,
+ ViEId(engine_id_, channel_id_),
+ "%s: video_suspended_ changed to %i",
+ __FUNCTION__, video_is_suspended);
+ codec_observer_->SuspendChange(channel_id_, video_is_suspended);
}
}
@@ -1159,8 +1174,8 @@
return vcm_.StopDebugRecording();
}
-void ViEEncoder::EnableAutoMuting() {
- vcm_.EnableAutoMuting();
+void ViEEncoder::SuspendBelowMinBitrate() {
+ vcm_.SuspendBelowMinBitrate();
bitrate_controller_->EnforceMinBitrate(false);
}
@@ -1175,6 +1190,15 @@
pre_encode_callback_ = NULL;
}
+void ViEEncoder::RegisterPostEncodeImageCallback(
+ EncodedImageCallback* post_encode_callback) {
+ vcm_.RegisterPostEncodeImageCallback(post_encode_callback);
+}
+
+void ViEEncoder::DeRegisterPostEncodeImageCallback() {
+ vcm_.RegisterPostEncodeImageCallback(NULL);
+}
+
QMVideoSettingsCallback::QMVideoSettingsCallback(VideoProcessingModule* vpm)
: vpm_(vpm) {
}
diff --git a/video_engine/vie_encoder.h b/video_engine/vie_encoder.h
index f80699c..cd8328e 100644
--- a/video_engine/vie_encoder.h
+++ b/video_engine/vie_encoder.h
@@ -29,6 +29,7 @@
class Config;
class CriticalSectionWrapper;
+class EncodedImageCallback;
class PacedSender;
class ProcessThread;
class QMVideoSettingsCallback;
@@ -163,16 +164,20 @@
// Disables recording of debugging information.
virtual int StopDebugRecording();
- // Enables AutoMuter to turn off video when the rate drops below
+ // Lets the sender suspend video when the rate drops below
// |threshold_bps|, and turns back on when the rate goes back up above
// |threshold_bps| + |window_bps|.
- virtual void EnableAutoMuting();
+ virtual void SuspendBelowMinBitrate();
- // New-style callback, used by VideoSendStream.
+ // New-style callbacks, used by VideoSendStream.
void RegisterPreEncodeCallback(I420FrameCallback* pre_encode_callback);
void DeRegisterPreEncodeCallback();
+ void RegisterPostEncodeImageCallback(
+ EncodedImageCallback* post_encode_callback);
+ void DeRegisterPostEncodeImageCallback();
int channel_id() const { return channel_id_; }
+
protected:
// Called by BitrateObserver.
void OnNetworkChanged(const uint32_t bitrate_bps,
@@ -226,7 +231,7 @@
// Quality modes callback
QMVideoSettingsCallback* qm_callback_;
- bool video_auto_muted_;
+ bool video_suspended_;
I420FrameCallback* pre_encode_callback_;
};
diff --git a/video_engine/vie_image_process_impl.cc b/video_engine/vie_image_process_impl.cc
index 92fe697..b10e774 100644
--- a/video_engine/vie_image_process_impl.cc
+++ b/video_engine/vie_image_process_impl.cc
@@ -284,6 +284,36 @@
vie_encoder->DeRegisterPreEncodeCallback();
}
+void ViEImageProcessImpl::RegisterPostEncodeImageCallback(
+ int video_channel,
+ EncodedImageCallback* post_encode_callback) {
+ ViEChannelManagerScoped cs(*(shared_data_->channel_manager()));
+ ViEEncoder* vie_encoder = cs.Encoder(video_channel);
+ assert(vie_encoder != NULL);
+ vie_encoder->RegisterPostEncodeImageCallback(post_encode_callback);
+}
+
+void ViEImageProcessImpl::DeRegisterPostEncodeCallback(int video_channel) {
+ ViEChannelManagerScoped cs(*(shared_data_->channel_manager()));
+ ViEEncoder* vie_encoder = cs.Encoder(video_channel);
+ assert(vie_encoder != NULL);
+ vie_encoder->DeRegisterPostEncodeImageCallback();
+}
+
+void ViEImageProcessImpl::RegisterPreDecodeImageCallback(
+ int video_channel,
+ EncodedImageCallback* pre_decode_callback) {
+ ViEChannelManagerScoped cs(*(shared_data_->channel_manager()));
+ ViEChannel* channel = cs.Channel(video_channel);
+ channel->RegisterPreDecodeImageCallback(pre_decode_callback);
+}
+
+void ViEImageProcessImpl::DeRegisterPreDecodeCallback(int video_channel) {
+ ViEChannelManagerScoped cs(*(shared_data_->channel_manager()));
+ ViEChannel* channel = cs.Channel(video_channel);
+ channel->RegisterPreDecodeImageCallback(NULL);
+}
+
void ViEImageProcessImpl::RegisterPreRenderCallback(
int video_channel,
I420FrameCallback* pre_render_callback) {
diff --git a/video_engine/vie_image_process_impl.h b/video_engine/vie_image_process_impl.h
index 9b43e7e..74a7ff0 100644
--- a/video_engine/vie_image_process_impl.h
+++ b/video_engine/vie_image_process_impl.h
@@ -43,6 +43,16 @@
I420FrameCallback* pre_encode_callback) OVERRIDE;
virtual void DeRegisterPreEncodeCallback(int video_channel) OVERRIDE;
+ virtual void RegisterPostEncodeImageCallback(
+ int video_channel,
+ EncodedImageCallback* post_encode_callback) OVERRIDE;
+ virtual void DeRegisterPostEncodeCallback(int video_channel) OVERRIDE;
+
+ virtual void RegisterPreDecodeImageCallback(
+ int video_channel,
+ EncodedImageCallback* post_encode_callback) OVERRIDE;
+ virtual void DeRegisterPreDecodeCallback(int video_channel) OVERRIDE;
+
virtual void RegisterPreRenderCallback(
int video_channel,
I420FrameCallback* pre_render_callback) OVERRIDE;
diff --git a/video_engine/vie_render_impl.cc b/video_engine/vie_render_impl.cc
index 507e05a..b7b7c94 100644
--- a/video_engine/vie_render_impl.cc
+++ b/video_engine/vie_render_impl.cc
@@ -394,4 +394,35 @@
}
}
+int ViERenderImpl::AddRenderCallback(int render_id,
+ VideoRenderCallback* callback) {
+ if (render_id < kViEChannelIdBase || render_id > kViEChannelIdMax)
+ return -1;
+ // This is a channel.
+ ViEChannelManagerScoped cm(*(shared_data_->channel_manager()));
+ ViEFrameProviderBase* frame_provider = cm.Channel(render_id);
+ if (!frame_provider) {
+ WEBRTC_TRACE(kTraceError,
+ kTraceVideo,
+ ViEId(shared_data_->instance_id()),
+ "%s: FrameProvider id %d doesn't exist",
+ __FUNCTION__,
+ render_id);
+ shared_data_->SetLastError(kViERenderInvalidRenderId);
+ return -1;
+ }
+ ViERenderer* renderer = shared_data_->render_manager()->AddRenderStream(
+ render_id, NULL, 0, 0.0f, 0.0f, 1.0f, 1.0f);
+ if (!renderer) {
+ shared_data_->SetLastError(kViERenderUnknownError);
+ return -1;
+ }
+ if (renderer->SetVideoRenderCallback(render_id, callback) != 0) {
+ shared_data_->SetLastError(kViERenderUnknownError);
+ return -1;
+ }
+
+ return frame_provider->RegisterFrameCallback(render_id, renderer);
+}
+
} // namespace webrtc
diff --git a/video_engine/vie_render_impl.h b/video_engine/vie_render_impl.h
index c419794..370a7a3 100644
--- a/video_engine/vie_render_impl.h
+++ b/video_engine/vie_render_impl.h
@@ -45,6 +45,8 @@
const bool mirror_yaxis);
virtual int AddRenderer(const int render_id, RawVideoType video_input_format,
ExternalRenderer* renderer);
+ virtual int AddRenderCallback(int render_id,
+ VideoRenderCallback* callback) OVERRIDE;
protected:
explicit ViERenderImpl(ViESharedData* shared_data);
diff --git a/video_engine/vie_renderer.cc b/video_engine/vie_renderer.cc
index 35c68aa..e18ebe9 100644
--- a/video_engine/vie_renderer.cc
+++ b/video_engine/vie_renderer.cc
@@ -104,6 +104,11 @@
incoming_external_callback_);
}
+int32_t ViERenderer::SetVideoRenderCallback(int32_t render_id,
+ VideoRenderCallback* callback) {
+ return render_module_.AddExternalRenderCallback(render_id, callback);
+}
+
ViERenderer::ViERenderer(const int32_t render_id,
const int32_t engine_id,
VideoRender& render_module,
diff --git a/video_engine/vie_renderer.h b/video_engine/vie_renderer.h
index 4f73e51..907735c 100644
--- a/video_engine/vie_renderer.h
+++ b/video_engine/vie_renderer.h
@@ -86,6 +86,9 @@
RawVideoType video_input_format,
ExternalRenderer* external_renderer);
+ int32_t SetVideoRenderCallback(const int32_t render_id,
+ VideoRenderCallback* callback);
+
private:
ViERenderer(const int32_t render_id, const int32_t engine_id,
VideoRender& render_module,
diff --git a/video_engine/vie_rtp_rtcp_impl.cc b/video_engine/vie_rtp_rtcp_impl.cc
index ecfa1b6..7d3c8ad 100644
--- a/video_engine/vie_rtp_rtcp_impl.cc
+++ b/video_engine/vie_rtp_rtcp_impl.cc
@@ -804,6 +804,25 @@
return 0;
}
+int ViERTP_RTCPImpl::SetRtcpXrRrtrStatus(int video_channel, bool enable) {
+ WEBRTC_TRACE(kTraceApiCall, kTraceVideo,
+ ViEId(shared_data_->instance_id(), video_channel),
+ "ViERTP_RTCPImpl::SetRtcpXrRrtrStatus(%d, %d)",
+ video_channel, enable);
+
+ ViEChannelManagerScoped cs(*(shared_data_->channel_manager()));
+ ViEChannel* vie_channel = cs.Channel(video_channel);
+ if (!vie_channel) {
+ WEBRTC_TRACE(kTraceError, kTraceVideo,
+ ViEId(shared_data_->instance_id(), video_channel),
+ "%s: Channel %d doesn't exist", __FUNCTION__, video_channel);
+ shared_data_->SetLastError(kViERtpRtcpInvalidChannelId);
+ return -1;
+ }
+ vie_channel->SetRtcpXrRrtrStatus(enable);
+ return 0;
+}
+
int ViERTP_RTCPImpl::SetTransmissionSmoothingStatus(int video_channel,
bool enable) {
WEBRTC_TRACE(kTraceApiCall, kTraceVideo,
@@ -823,11 +842,8 @@
return 0;
}
-int ViERTP_RTCPImpl::GetReceivedRTCPStatistics(const int video_channel,
- uint16_t& fraction_lost,
- unsigned int& cumulative_lost,
- unsigned int& extended_max,
- unsigned int& jitter,
+int ViERTP_RTCPImpl::GetReceiveChannelRtcpStatistics(const int video_channel,
+ RtcpStatistics& basic_stats,
int& rtt_ms) const {
WEBRTC_TRACE(kTraceApiCall, kTraceVideo,
ViEId(shared_data_->instance_id(), video_channel),
@@ -841,22 +857,22 @@
shared_data_->SetLastError(kViERtpRtcpInvalidChannelId);
return -1;
}
- if (vie_channel->GetReceivedRtcpStatistics(&fraction_lost,
- &cumulative_lost,
- &extended_max,
- &jitter,
- &rtt_ms) != 0) {
+
+ // TODO(sprang): Clean this up when stats struct is propagated all the way.
+ uint16_t frac_loss;
+ if (vie_channel->GetReceivedRtcpStatistics(
+ &frac_loss, &basic_stats.cumulative_lost,
+ &basic_stats.extended_max_sequence_number, &basic_stats.jitter,
+ &rtt_ms) != 0) {
+ basic_stats.fraction_lost = frac_loss;
shared_data_->SetLastError(kViERtpRtcpUnknownError);
return -1;
}
return 0;
}
-int ViERTP_RTCPImpl::GetSentRTCPStatistics(const int video_channel,
- uint16_t& fraction_lost,
- unsigned int& cumulative_lost,
- unsigned int& extended_max,
- unsigned int& jitter,
+int ViERTP_RTCPImpl::GetSendChannelRtcpStatistics(const int video_channel,
+ RtcpStatistics& basic_stats,
int& rtt_ms) const {
WEBRTC_TRACE(kTraceApiCall, kTraceVideo,
ViEId(shared_data_->instance_id(), video_channel),
@@ -871,20 +887,22 @@
return -1;
}
- if (vie_channel->GetSendRtcpStatistics(&fraction_lost, &cumulative_lost,
- &extended_max, &jitter,
- &rtt_ms) != 0) {
+ // TODO(sprang): Clean this up when stats struct is propagated all the way.
+ uint16_t frac_loss;
+ if (vie_channel->GetSendRtcpStatistics(
+ &frac_loss, &basic_stats.cumulative_lost,
+ &basic_stats.extended_max_sequence_number, &basic_stats.jitter,
+ &rtt_ms) != 0) {
+ basic_stats.fraction_lost = frac_loss;
shared_data_->SetLastError(kViERtpRtcpUnknownError);
return -1;
}
return 0;
}
-int ViERTP_RTCPImpl::GetRTPStatistics(const int video_channel,
- unsigned int& bytes_sent,
- unsigned int& packets_sent,
- unsigned int& bytes_received,
- unsigned int& packets_received) const {
+int ViERTP_RTCPImpl::GetRtpStatistics(const int video_channel,
+ StreamDataCounters& sent,
+ StreamDataCounters& received) const {
WEBRTC_TRACE(kTraceApiCall, kTraceVideo,
ViEId(shared_data_->instance_id(), video_channel),
"%s(channel: %d)", __FUNCTION__, video_channel);
@@ -897,10 +915,10 @@
shared_data_->SetLastError(kViERtpRtcpInvalidChannelId);
return -1;
}
- if (vie_channel->GetRtpStatistics(&bytes_sent,
- &packets_sent,
- &bytes_received,
- &packets_received) != 0) {
+ if (vie_channel->GetRtpStatistics(&sent.bytes,
+ &sent.packets,
+ &received.bytes,
+ &received.packets) != 0) {
shared_data_->SetLastError(kViERtpRtcpUnknownError);
return -1;
}
@@ -1100,4 +1118,76 @@
return 0;
}
+int ViERTP_RTCPImpl::RegisterSendChannelRtcpStatisticsCallback(
+ int channel, RtcpStatisticsCallback* callback) {
+ // TODO(sprang): Implement
+ return -1;
+}
+
+int ViERTP_RTCPImpl::DeregisterSendChannelRtcpStatisticsCallback(
+ int channel, RtcpStatisticsCallback* callback) {
+ // TODO(sprang): Implement
+ return -1;
+}
+
+int ViERTP_RTCPImpl::RegisterReceiveChannelRtcpStatisticsCallback(
+ int channel, RtcpStatisticsCallback* callback) {
+ // TODO(sprang): Implement
+ return -1;
+}
+
+int ViERTP_RTCPImpl::DeregisterReceiveChannelRtcpStatisticsCallback(
+ int channel, RtcpStatisticsCallback* callback) {
+ // TODO(sprang): Implement
+ return -1;
+}
+
+int ViERTP_RTCPImpl::RegisterSendChannelRtpStatisticsCallback(
+ int channel, StreamDataCountersCallback* callback) {
+ // TODO(sprang): Implement
+ return -1;
+}
+
+int ViERTP_RTCPImpl::DeregisterSendChannelRtpStatisticsCallback(
+ int channel, StreamDataCountersCallback* callback) {
+ // TODO(sprang): Implement
+ return -1;
+}
+
+int ViERTP_RTCPImpl::RegisterReceiveChannelRtpStatisticsCallback(
+ int channel, StreamDataCountersCallback* callback) {
+ // TODO(sprang): Implement
+ return -1;
+}
+
+int ViERTP_RTCPImpl::DeregisterReceiveChannelRtpStatisticsCallback(
+ int channel, StreamDataCountersCallback* callback) {
+ // TODO(sprang): Implement
+ return -1;
+}
+int ViERTP_RTCPImpl::RegisterSendBitrateObserver(
+ int channel, BitrateStatisticsObserver* callback) {
+ // TODO(sprang): Implement
+ return -1;
+}
+
+int ViERTP_RTCPImpl::DeregisterSendBitrateObserver(
+ int channel, BitrateStatisticsObserver* callback) {
+ // TODO(sprang): Implement
+ return -1;
+}
+
+int ViERTP_RTCPImpl::RegisterSendFrameCountObserver(
+ int channel, FrameCountObserver* callback) {
+ // TODO(sprang): Implement
+ return -1;
+}
+
+int ViERTP_RTCPImpl::DeregisterSendFrameCountObserver(
+ int channel, FrameCountObserver* callback) {
+ // TODO(sprang): Implement
+ return -1;
+}
+
+
} // namespace webrtc
diff --git a/video_engine/vie_rtp_rtcp_impl.h b/video_engine/vie_rtp_rtcp_impl.h
index 9af4b0c..caabd2c 100644
--- a/video_engine/vie_rtp_rtcp_impl.h
+++ b/video_engine/vie_rtp_rtcp_impl.h
@@ -88,23 +88,17 @@
virtual int SetReceiveAbsoluteSendTimeStatus(int video_channel,
bool enable,
int id);
+ virtual int SetRtcpXrRrtrStatus(int video_channel, bool enable);
virtual int SetTransmissionSmoothingStatus(int video_channel, bool enable);
- virtual int GetReceivedRTCPStatistics(const int video_channel,
- uint16_t& fraction_lost,
- unsigned int& cumulative_lost,
- unsigned int& extended_max,
- unsigned int& jitter,
- int& rtt_ms) const;
- virtual int GetSentRTCPStatistics(const int video_channel,
- uint16_t& fraction_lost,
- unsigned int& cumulative_lost,
- unsigned int& extended_max,
- unsigned int& jitter, int& rtt_ms) const;
- virtual int GetRTPStatistics(const int video_channel,
- unsigned int& bytes_sent,
- unsigned int& packets_sent,
- unsigned int& bytes_received,
- unsigned int& packets_received) const;
+ virtual int GetReceiveChannelRtcpStatistics(const int video_channel,
+ RtcpStatistics& basic_stats,
+ int& rtt_ms) const;
+ virtual int GetSendChannelRtcpStatistics(const int video_channel,
+ RtcpStatistics& basic_stats,
+ int& rtt_ms) const;
+ virtual int GetRtpStatistics(const int video_channel,
+ StreamDataCounters& sent,
+ StreamDataCounters& received) const;
virtual int GetBandwidthUsage(const int video_channel,
unsigned int& total_bitrate_sent,
unsigned int& video_bitrate_sent,
@@ -127,6 +121,31 @@
ViERTCPObserver& observer);
virtual int DeregisterRTCPObserver(const int video_channel);
+ virtual int RegisterSendChannelRtcpStatisticsCallback(
+ int channel, RtcpStatisticsCallback* callback);
+ virtual int DeregisterSendChannelRtcpStatisticsCallback(
+ int channel, RtcpStatisticsCallback* callback);
+ virtual int RegisterReceiveChannelRtcpStatisticsCallback(
+ int channel, RtcpStatisticsCallback* callback);
+ virtual int DeregisterReceiveChannelRtcpStatisticsCallback(
+ int channel, RtcpStatisticsCallback* callback);
+ virtual int RegisterSendChannelRtpStatisticsCallback(
+ int channel, StreamDataCountersCallback* callback);
+ virtual int DeregisterSendChannelRtpStatisticsCallback(
+ int channel, StreamDataCountersCallback* callback);
+ virtual int RegisterReceiveChannelRtpStatisticsCallback(
+ int channel, StreamDataCountersCallback* callback);
+ virtual int DeregisterReceiveChannelRtpStatisticsCallback(
+ int channel, StreamDataCountersCallback* callback);
+ virtual int RegisterSendBitrateObserver(
+ int channel, BitrateStatisticsObserver* callback);
+ virtual int DeregisterSendBitrateObserver(
+ int channel, BitrateStatisticsObserver* callback);
+ virtual int RegisterSendFrameCountObserver(
+ int channel, FrameCountObserver* callback);
+ virtual int DeregisterSendFrameCountObserver(
+ int channel, FrameCountObserver* callback);
+
protected:
explicit ViERTP_RTCPImpl(ViESharedData* shared_data);
virtual ~ViERTP_RTCPImpl();
diff --git a/video_engine_tests.isolate b/video_engine_tests.isolate
index e3d2381..af98afd 100644
--- a/video_engine_tests.isolate
+++ b/video_engine_tests.isolate
@@ -26,6 +26,7 @@
],
'isolate_dependency_tracked': [
'../DEPS',
+ '../data/voice_engine/audio_long16.pcm',
'../resources/foreman_cif.yuv',
'../resources/paris_qcif.yuv',
'../testing/test_env.py',
diff --git a/video_receive_stream.h b/video_receive_stream.h
index f968c66..8b4f643 100644
--- a/video_receive_stream.h
+++ b/video_receive_stream.h
@@ -164,8 +164,8 @@
StatsCallback* stats_callback;
};
- virtual void StartReceive() = 0;
- virtual void StopReceive() = 0;
+ virtual void StartReceiving() = 0;
+ virtual void StopReceiving() = 0;
// TODO(mflodman) Replace this with callback.
virtual void GetCurrentReceiveCodec(VideoCodec* receive_codec) = 0;
diff --git a/video_send_stream.h b/video_send_stream.h
index af33b7c..2358042 100644
--- a/video_send_stream.h
+++ b/video_send_stream.h
@@ -23,8 +23,6 @@
class VideoEncoder;
-struct SendStreamState;
-
// Class to deliver captured frame to the video send stream.
class VideoSendStreamInput {
public:
@@ -73,7 +71,7 @@
struct Config {
Config()
: pre_encode_callback(NULL),
- encoded_callback(NULL),
+ post_encode_callback(NULL),
local_renderer(NULL),
render_delay_ms(0),
encoder(NULL),
@@ -81,8 +79,7 @@
target_delay_ms(0),
pacing(false),
stats_callback(NULL),
- start_state(NULL),
- auto_mute(false) {}
+ suspend_below_min_bitrate(false) {}
VideoCodec codec;
static const size_t kDefaultMaxPacketSize = 1500 - 40; // TCP over IPv4.
@@ -116,7 +113,7 @@
// Called for each encoded frame, e.g. used for file storage. 'NULL'
// disables the callback.
- EncodedFrameObserver* encoded_callback;
+ EncodedFrameObserver* post_encode_callback;
// Renderer for local preview. The local renderer will be called even if
// sending hasn't started. 'NULL' disables local rendering.
@@ -145,28 +142,23 @@
// Callback for periodically receiving send stats.
StatsCallback* stats_callback;
- // Set to resume a previously destroyed send stream.
- SendStreamState* start_state;
-
- // True if video should be muted when video goes under the minimum video
- // bitrate. Unless muted, video will be sent at a bitrate higher than
- // estimated available.
- bool auto_mute;
+ // True if the stream should be suspended when the available bitrate fall
+ // below the minimum configured bitrate. If this variable is false, the
+ // stream may send at a rate higher than the estimated available bitrate.
+ // Enabling suspend_below_min_bitrate will also enable pacing and padding,
+ // otherwise, the video will be unable to recover from suspension.
+ bool suspend_below_min_bitrate;
};
// Gets interface used to insert captured frames. Valid as long as the
// VideoSendStream is valid.
virtual VideoSendStreamInput* Input() = 0;
- virtual void StartSend() = 0;
- virtual void StopSend() = 0;
+ virtual void StartSending() = 0;
+ virtual void StopSending() = 0;
- // TODO(mflodman) Change VideoCodec struct and use here.
- virtual bool SetTargetBitrate(
- int min_bitrate, int max_bitrate,
- const std::vector<SimulcastStream>& streams) = 0;
-
- virtual void GetSendCodec(VideoCodec* send_codec) = 0;
+ virtual bool SetCodec(const VideoCodec& codec) = 0;
+ virtual VideoCodec GetCodec() = 0;
protected:
virtual ~VideoSendStream() {}
diff --git a/voice_engine/test/cmd_test/voe_cmd_test.cc b/voice_engine/test/cmd_test/voe_cmd_test.cc
index 10fc198..68d096d 100644
--- a/voice_engine/test/cmd_test/voe_cmd_test.cc
+++ b/voice_engine/test/cmd_test/voe_cmd_test.cc
@@ -23,6 +23,7 @@
#include "webrtc/common_types.h"
#include "webrtc/engine_configurations.h"
#include "webrtc/modules/audio_coding/main/interface/audio_coding_module.h"
+#include "webrtc/modules/audio_processing/include/audio_processing.h"
#include "webrtc/system_wrappers/interface/scoped_ptr.h"
#include "webrtc/test/channel_transport/include/channel_transport.h"
#include "webrtc/test/testsupport/fileutils.h"
@@ -242,6 +243,7 @@
bool muted = false;
bool on_hold = false;
bool opus_stereo = false;
+ bool experimental_ns_enabled = false;
#if defined(WEBRTC_ANDROID)
std::string resource_path = "/sdcard/";
@@ -432,6 +434,7 @@
printf("%i. Toggle CNG\n", option_index++);
printf("%i. Toggle AGC\n", option_index++);
printf("%i. Toggle NS\n", option_index++);
+ printf("%i. Toggle experimental NS\n", option_index++);
printf("%i. Toggle EC\n", option_index++);
printf("%i. Select AEC\n", option_index++);
printf("%i. Select AECM\n", option_index++);
@@ -497,6 +500,16 @@
else
printf("\n NS is now off! \n");
} else if (option_selection == option_index++) {
+ experimental_ns_enabled = !experimental_ns_enabled;
+ res = base1->audio_processing()->EnableExperimentalNs(
+ experimental_ns_enabled);
+ VALIDATE;
+ if (experimental_ns_enabled) {
+ printf("\n Experimental NS is now on!\n");
+ } else {
+ printf("\n Experimental NS is now off!\n");
+ }
+ } else if (option_selection == option_index++) {
enable_aec = !enable_aec;
res = apm->SetEcStatus(enable_aec, kEcUnchanged);
VALIDATE;
diff --git a/voice_engine/voice_engine_impl.cc b/voice_engine/voice_engine_impl.cc
index 703b6a4..4af15f9 100644
--- a/voice_engine/voice_engine_impl.cc
+++ b/voice_engine/voice_engine_impl.cc
@@ -149,7 +149,11 @@
{
#ifdef WEBRTC_ANDROID
#ifdef WEBRTC_ANDROID_OPENSLES
- AudioManagerJni::SetAndroidAudioDeviceObjects(javaVM, env, context);
+ if (javaVM && env && context) {
+ AudioManagerJni::SetAndroidAudioDeviceObjects(javaVM, env, context);
+ } else {
+ AudioManagerJni::ClearAndroidAudioDeviceObjects();
+ }
return 0;
#else
return AudioDeviceAndroidJni::SetAndroidAudioDeviceObjects(
diff --git a/webrtc.gyp b/webrtc.gyp
index 9fe514c..a223007 100644
--- a/webrtc.gyp
+++ b/webrtc.gyp
@@ -61,6 +61,7 @@
'sources': [
'call.cc',
'call.h',
+ 'config.cc',
'config.h',
'frame_callback.h',
'transport.h',
diff --git a/webrtc_tests.gypi b/webrtc_tests.gypi
index bcf3b13..6ea74f4 100644
--- a/webrtc_tests.gypi
+++ b/webrtc_tests.gypi
@@ -36,6 +36,8 @@
'video/full_stack.cc',
'video/rampup_tests.cc',
'video/video_send_stream_tests.cc',
+ 'voice_engine/test/auto_test/resource_manager.cc',
+ 'voice_engine/test/auto_test/resource_manager.h',
'test/test_main.cc',
],
'dependencies': [