aboutsummaryrefslogtreecommitdiff
path: root/webrtc/test
diff options
context:
space:
mode:
Diffstat (limited to 'webrtc/test')
-rw-r--r--webrtc/test/BUILD.gn107
-rw-r--r--webrtc/test/OWNERS10
-rw-r--r--webrtc/test/call_test.cc244
-rw-r--r--webrtc/test/call_test.h129
-rw-r--r--webrtc/test/channel_transport/OWNERS2
-rw-r--r--webrtc/test/channel_transport/channel_transport.cc83
-rw-r--r--webrtc/test/channel_transport/include/channel_transport.h56
-rw-r--r--webrtc/test/channel_transport/traffic_control_win.cc252
-rw-r--r--webrtc/test/channel_transport/traffic_control_win.h99
-rw-r--r--webrtc/test/channel_transport/udp_socket2_manager_win.cc613
-rw-r--r--webrtc/test/channel_transport/udp_socket2_manager_win.h162
-rw-r--r--webrtc/test/channel_transport/udp_socket2_win.cc1374
-rw-r--r--webrtc/test/channel_transport/udp_socket2_win.h179
-rw-r--r--webrtc/test/channel_transport/udp_socket_manager_posix.cc403
-rw-r--r--webrtc/test/channel_transport/udp_socket_manager_posix.h91
-rw-r--r--webrtc/test/channel_transport/udp_socket_manager_unittest.cc84
-rw-r--r--webrtc/test/channel_transport/udp_socket_manager_wrapper.cc72
-rw-r--r--webrtc/test/channel_transport/udp_socket_manager_wrapper.h70
-rw-r--r--webrtc/test/channel_transport/udp_socket_posix.cc285
-rw-r--r--webrtc/test/channel_transport/udp_socket_posix.h94
-rw-r--r--webrtc/test/channel_transport/udp_socket_wrapper.cc159
-rw-r--r--webrtc/test/channel_transport/udp_socket_wrapper.h112
-rw-r--r--webrtc/test/channel_transport/udp_socket_wrapper_unittest.cc67
-rw-r--r--webrtc/test/channel_transport/udp_transport.h381
-rw-r--r--webrtc/test/channel_transport/udp_transport_impl.cc2994
-rw-r--r--webrtc/test/channel_transport/udp_transport_impl.h259
-rw-r--r--webrtc/test/channel_transport/udp_transport_unittest.cc143
-rw-r--r--webrtc/test/common_unittest.cc80
-rw-r--r--webrtc/test/configurable_frame_size_encoder.cc92
-rw-r--r--webrtc/test/configurable_frame_size_encoder.h58
-rw-r--r--webrtc/test/constants.cc20
-rw-r--r--webrtc/test/constants.h18
-rw-r--r--webrtc/test/direct_transport.cc104
-rw-r--r--webrtc/test/direct_transport.h66
-rw-r--r--webrtc/test/encoder_settings.cc71
-rw-r--r--webrtc/test/encoder_settings.h25
-rw-r--r--webrtc/test/fake_audio_device.cc151
-rw-r--r--webrtc/test/fake_audio_device.h69
-rw-r--r--webrtc/test/fake_decoder.cc87
-rw-r--r--webrtc/test/fake_decoder.h74
-rw-r--r--webrtc/test/fake_encoder.cc197
-rw-r--r--webrtc/test/fake_encoder.h85
-rw-r--r--webrtc/test/fake_network_pipe.cc217
-rw-r--r--webrtc/test/fake_network_pipe.h99
-rw-r--r--webrtc/test/fake_network_pipe_unittest.cc301
-rw-r--r--webrtc/test/fake_texture_frame.h56
-rw-r--r--webrtc/test/fake_voice_engine.cc70
-rw-r--r--webrtc/test/fake_voice_engine.h504
-rw-r--r--webrtc/test/field_trial.cc103
-rw-r--r--webrtc/test/field_trial.h48
-rw-r--r--webrtc/test/frame_generator.cc280
-rw-r--r--webrtc/test/frame_generator.h64
-rw-r--r--webrtc/test/frame_generator_capturer.cc137
-rw-r--r--webrtc/test/frame_generator_capturer.h77
-rw-r--r--webrtc/test/frame_generator_unittest.cc149
-rw-r--r--webrtc/test/gl/gl_renderer.cc112
-rw-r--r--webrtc/test/gl/gl_renderer.h51
-rw-r--r--webrtc/test/histogram.cc82
-rw-r--r--webrtc/test/histogram.h33
-rw-r--r--webrtc/test/layer_filtering_transport.cc106
-rw-r--r--webrtc/test/layer_filtering_transport.h51
-rw-r--r--webrtc/test/linux/glx_renderer.cc178
-rw-r--r--webrtc/test/linux/glx_renderer.h48
-rw-r--r--webrtc/test/linux/video_renderer_linux.cc27
-rw-r--r--webrtc/test/mac/run_test.mm75
-rw-r--r--webrtc/test/mac/video_renderer_mac.h41
-rw-r--r--webrtc/test/mac/video_renderer_mac.mm134
-rw-r--r--webrtc/test/metrics.gyp32
-rw-r--r--webrtc/test/mock_transport.h28
-rw-r--r--webrtc/test/null_platform_renderer.cc22
-rw-r--r--webrtc/test/null_transport.cc26
-rw-r--r--webrtc/test/null_transport.h30
-rw-r--r--webrtc/test/random.cc57
-rw-r--r--webrtc/test/random.h49
-rw-r--r--webrtc/test/rtcp_packet_parser.cc142
-rw-r--r--webrtc/test/rtcp_packet_parser.h705
-rw-r--r--webrtc/test/rtp_file_reader.cc674
-rw-r--r--webrtc/test/rtp_file_reader.h49
-rw-r--r--webrtc/test/rtp_file_reader_unittest.cc125
-rw-r--r--webrtc/test/rtp_file_writer.cc108
-rw-r--r--webrtc/test/rtp_file_writer.h33
-rw-r--r--webrtc/test/rtp_file_writer_unittest.cc75
-rw-r--r--webrtc/test/rtp_rtcp_observer.h141
-rw-r--r--webrtc/test/run_all_unittests.cc16
-rw-r--r--webrtc/test/run_loop.cc22
-rw-r--r--webrtc/test/run_loop.h22
-rw-r--r--webrtc/test/run_test.cc23
-rw-r--r--webrtc/test/run_test.h22
-rw-r--r--webrtc/test/statistics.cc41
-rw-r--r--webrtc/test/statistics.h36
-rw-r--r--webrtc/test/test.gyp251
-rw-r--r--webrtc/test/test_main.cc32
-rw-r--r--webrtc/test/test_suite.cc62
-rw-r--r--webrtc/test/test_suite.h50
-rw-r--r--webrtc/test/test_support_unittests.isolate24
-rw-r--r--webrtc/test/testsupport/always_passing_unittest.cc19
-rw-r--r--webrtc/test/testsupport/fileutils.cc268
-rw-r--r--webrtc/test/testsupport/fileutils.h159
-rw-r--r--webrtc/test/testsupport/fileutils_unittest.cc142
-rw-r--r--webrtc/test/testsupport/frame_reader.cc85
-rw-r--r--webrtc/test/testsupport/frame_reader.h74
-rw-r--r--webrtc/test/testsupport/frame_reader_unittest.cc71
-rw-r--r--webrtc/test/testsupport/frame_writer.cc70
-rw-r--r--webrtc/test/testsupport/frame_writer.h70
-rw-r--r--webrtc/test/testsupport/frame_writer_unittest.cc63
-rw-r--r--webrtc/test/testsupport/gtest_disable.h57
-rw-r--r--webrtc/test/testsupport/gtest_prod_util.h38
-rw-r--r--webrtc/test/testsupport/iosfileutils.mm60
-rw-r--r--webrtc/test/testsupport/mac/run_threaded_main_mac.h22
-rw-r--r--webrtc/test/testsupport/mac/run_threaded_main_mac.mm90
-rw-r--r--webrtc/test/testsupport/metrics/video_metrics.cc198
-rw-r--r--webrtc/test/testsupport/metrics/video_metrics.h121
-rw-r--r--webrtc/test/testsupport/metrics/video_metrics_unittest.cc147
-rw-r--r--webrtc/test/testsupport/mock/mock_frame_reader.h33
-rw-r--r--webrtc/test/testsupport/mock/mock_frame_writer.h32
-rw-r--r--webrtc/test/testsupport/packet_reader.cc49
-rw-r--r--webrtc/test/testsupport/packet_reader.h54
-rw-r--r--webrtc/test/testsupport/packet_reader_unittest.cc125
-rw-r--r--webrtc/test/testsupport/perf_test.cc177
-rw-r--r--webrtc/test/testsupport/perf_test.h122
-rw-r--r--webrtc/test/testsupport/perf_test_unittest.cc34
-rw-r--r--webrtc/test/testsupport/trace_to_stderr.cc63
-rw-r--r--webrtc/test/testsupport/trace_to_stderr.h52
-rw-r--r--webrtc/test/testsupport/unittest_utils.h56
-rw-r--r--webrtc/test/vcm_capturer.cc107
-rw-r--r--webrtc/test/vcm_capturer.h50
-rw-r--r--webrtc/test/video_capturer.cc54
-rw-r--r--webrtc/test/video_capturer.h42
-rw-r--r--webrtc/test/video_renderer.cc37
-rw-r--r--webrtc/test/video_renderer.h39
-rw-r--r--webrtc/test/webrtc_test_common.gyp176
-rw-r--r--webrtc/test/win/d3d_renderer.cc219
-rw-r--r--webrtc/test/win/d3d_renderer.h54
-rw-r--r--webrtc/test/win/run_loop_win.cc33
134 files changed, 19023 insertions, 0 deletions
diff --git a/webrtc/test/BUILD.gn b/webrtc/test/BUILD.gn
new file mode 100644
index 0000000000..ed1dc75078
--- /dev/null
+++ b/webrtc/test/BUILD.gn
@@ -0,0 +1,107 @@
+# Copyright (c) 2014 The WebRTC project authors. All Rights Reserved.
+#
+# Use of this source code is governed by a BSD-style license
+# that can be found in the LICENSE file in the root of the source
+# tree. An additional intellectual property rights grant can be found
+# in the file PATENTS. All contributing project authors may
+# be found in the AUTHORS file in the root of the source tree.
+
+# TODO(kjellander): Convert the rest of the test.gyp targets and put here.
+
+source_set("test") {
+ testonly = true
+
+ deps = [
+ ":field_trial",
+ ":test_support",
+ ":test_support_main",
+ ]
+}
+
+source_set("field_trial") {
+ sources = [
+ "field_trial.cc",
+ "field_trial.h",
+ ]
+
+ deps = [
+ "..:webrtc_common",
+ "../system_wrappers",
+ ]
+
+ configs += [ "..:common_config" ]
+ public_configs = [ "..:common_inherited_config" ]
+}
+
+source_set("histogram") {
+ sources = [
+ "histogram.cc",
+ "histogram.h",
+ ]
+
+ deps = [
+ "..:webrtc_common",
+ "../system_wrappers",
+ ]
+
+ configs += [ "..:common_config" ]
+ public_configs = [ "..:common_inherited_config" ]
+}
+
+source_set("test_support") {
+ testonly = true
+
+ sources = [
+ "testsupport/fileutils.cc",
+ "testsupport/fileutils.h",
+ "testsupport/frame_reader.cc",
+ "testsupport/frame_reader.h",
+ "testsupport/frame_writer.cc",
+ "testsupport/frame_writer.h",
+ "testsupport/gtest_disable.h",
+ "testsupport/mock/mock_frame_reader.h",
+ "testsupport/mock/mock_frame_writer.h",
+ "testsupport/packet_reader.cc",
+ "testsupport/packet_reader.h",
+ "testsupport/perf_test.cc",
+ "testsupport/perf_test.h",
+ "testsupport/trace_to_stderr.cc",
+ "testsupport/trace_to_stderr.h",
+ ]
+
+ deps = [
+ "//testing/gmock",
+ "//testing/gtest",
+ "..:gtest_prod",
+ "../system_wrappers",
+ ]
+
+ if (is_android) {
+ deps += [ "//base:base" ]
+ }
+
+ configs += [ "..:common_config" ]
+ public_configs = [ "..:common_inherited_config" ]
+}
+
+source_set("test_support_main") {
+ testonly = true
+
+ sources = [
+ "run_all_unittests.cc",
+ "test_suite.cc",
+ "test_suite.h",
+ ]
+
+ deps = [
+ ":field_trial",
+ ":histogram",
+ ":test_support",
+ "//testing/gmock",
+ "//testing/gtest",
+ "//third_party/gflags",
+ ]
+
+ configs += [ "..:common_config" ]
+ public_configs = [ "..:common_inherited_config" ]
+}
diff --git a/webrtc/test/OWNERS b/webrtc/test/OWNERS
new file mode 100644
index 0000000000..98a0d4d1b5
--- /dev/null
+++ b/webrtc/test/OWNERS
@@ -0,0 +1,10 @@
+kjellander@webrtc.org
+pbos@webrtc.org
+phoglund@webrtc.org
+
+per-file *.isolate=kjellander@webrtc.org
+
+# These are for the common case of adding or renaming files. If you're doing
+# structural changes, please get a review from a reviewer in this file.
+per-file *.gyp=*
+per-file *.gypi=*
diff --git a/webrtc/test/call_test.cc b/webrtc/test/call_test.cc
new file mode 100644
index 0000000000..0a8b686974
--- /dev/null
+++ b/webrtc/test/call_test.cc
@@ -0,0 +1,244 @@
+/*
+ * Copyright (c) 2014 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+#include "webrtc/test/call_test.h"
+#include "webrtc/test/encoder_settings.h"
+
+namespace webrtc {
+namespace test {
+
+namespace {
+const int kVideoRotationRtpExtensionId = 4;
+}
+
+CallTest::CallTest()
+ : clock_(Clock::GetRealTimeClock()),
+ send_config_(nullptr),
+ send_stream_(NULL),
+ fake_encoder_(clock_) {
+}
+
+CallTest::~CallTest() {
+}
+
+void CallTest::RunBaseTest(BaseTest* test,
+ const FakeNetworkPipe::Config& config) {
+ CreateSenderCall(test->GetSenderCallConfig());
+ if (test->ShouldCreateReceivers())
+ CreateReceiverCall(test->GetReceiverCallConfig());
+ send_transport_.reset(new PacketTransport(
+ sender_call_.get(), test, test::PacketTransport::kSender, config));
+ receive_transport_.reset(new PacketTransport(
+ nullptr, test, test::PacketTransport::kReceiver, config));
+ test->OnTransportsCreated(send_transport_.get(), receive_transport_.get());
+ test->OnCallsCreated(sender_call_.get(), receiver_call_.get());
+
+ if (test->ShouldCreateReceivers()) {
+ send_transport_->SetReceiver(receiver_call_->Receiver());
+ receive_transport_->SetReceiver(sender_call_->Receiver());
+ } else {
+ // Sender-only call delivers to itself.
+ send_transport_->SetReceiver(sender_call_->Receiver());
+ receive_transport_->SetReceiver(nullptr);
+ }
+
+ CreateSendConfig(test->GetNumStreams(), send_transport_.get());
+ if (test->ShouldCreateReceivers()) {
+ CreateMatchingReceiveConfigs(receive_transport_.get());
+ }
+ test->ModifyConfigs(&send_config_, &receive_configs_, &encoder_config_);
+ CreateStreams();
+ test->OnStreamsCreated(send_stream_, receive_streams_);
+
+ CreateFrameGeneratorCapturer();
+ test->OnFrameGeneratorCapturerCreated(frame_generator_capturer_.get());
+
+ Start();
+ test->PerformTest();
+ send_transport_->StopSending();
+ receive_transport_->StopSending();
+ Stop();
+
+ DestroyStreams();
+}
+
+void CallTest::Start() {
+ send_stream_->Start();
+ for (size_t i = 0; i < receive_streams_.size(); ++i)
+ receive_streams_[i]->Start();
+ if (frame_generator_capturer_.get() != NULL)
+ frame_generator_capturer_->Start();
+}
+
+void CallTest::Stop() {
+ if (frame_generator_capturer_.get() != NULL)
+ frame_generator_capturer_->Stop();
+ for (size_t i = 0; i < receive_streams_.size(); ++i)
+ receive_streams_[i]->Stop();
+ send_stream_->Stop();
+}
+
+void CallTest::CreateCalls(const Call::Config& sender_config,
+ const Call::Config& receiver_config) {
+ CreateSenderCall(sender_config);
+ CreateReceiverCall(receiver_config);
+}
+
+void CallTest::CreateSenderCall(const Call::Config& config) {
+ sender_call_.reset(Call::Create(config));
+}
+
+void CallTest::CreateReceiverCall(const Call::Config& config) {
+ receiver_call_.reset(Call::Create(config));
+}
+
+void CallTest::DestroyCalls() {
+ sender_call_.reset(nullptr);
+ receiver_call_.reset(nullptr);
+}
+
+void CallTest::CreateSendConfig(size_t num_streams,
+ Transport* send_transport) {
+ assert(num_streams <= kNumSsrcs);
+ send_config_ = VideoSendStream::Config(send_transport);
+ send_config_.encoder_settings.encoder = &fake_encoder_;
+ send_config_.encoder_settings.payload_name = "FAKE";
+ send_config_.encoder_settings.payload_type = kFakeSendPayloadType;
+ send_config_.rtp.extensions.push_back(
+ RtpExtension(RtpExtension::kAbsSendTime, kAbsSendTimeExtensionId));
+ encoder_config_.streams = test::CreateVideoStreams(num_streams);
+ for (size_t i = 0; i < num_streams; ++i)
+ send_config_.rtp.ssrcs.push_back(kSendSsrcs[i]);
+ send_config_.rtp.extensions.push_back(
+ RtpExtension(RtpExtension::kVideoRotation, kVideoRotationRtpExtensionId));
+}
+
+void CallTest::CreateMatchingReceiveConfigs(
+ Transport* rtcp_send_transport) {
+ assert(!send_config_.rtp.ssrcs.empty());
+ assert(receive_configs_.empty());
+ assert(allocated_decoders_.empty());
+ VideoReceiveStream::Config config(rtcp_send_transport);
+ config.rtp.remb = true;
+ config.rtp.local_ssrc = kReceiverLocalSsrc;
+ for (const RtpExtension& extension : send_config_.rtp.extensions)
+ config.rtp.extensions.push_back(extension);
+ for (size_t i = 0; i < send_config_.rtp.ssrcs.size(); ++i) {
+ VideoReceiveStream::Decoder decoder =
+ test::CreateMatchingDecoder(send_config_.encoder_settings);
+ allocated_decoders_.push_back(decoder.decoder);
+ config.decoders.clear();
+ config.decoders.push_back(decoder);
+ config.rtp.remote_ssrc = send_config_.rtp.ssrcs[i];
+ receive_configs_.push_back(config);
+ }
+}
+
+void CallTest::CreateFrameGeneratorCapturer() {
+ VideoStream stream = encoder_config_.streams.back();
+ frame_generator_capturer_.reset(
+ test::FrameGeneratorCapturer::Create(send_stream_->Input(),
+ stream.width,
+ stream.height,
+ stream.max_framerate,
+ clock_));
+}
+
+void CallTest::CreateStreams() {
+ assert(send_stream_ == NULL);
+ assert(receive_streams_.empty());
+
+ send_stream_ =
+ sender_call_->CreateVideoSendStream(send_config_, encoder_config_);
+
+ for (size_t i = 0; i < receive_configs_.size(); ++i) {
+ receive_streams_.push_back(
+ receiver_call_->CreateVideoReceiveStream(receive_configs_[i]));
+ }
+}
+
+void CallTest::DestroyStreams() {
+ if (send_stream_ != NULL)
+ sender_call_->DestroyVideoSendStream(send_stream_);
+ send_stream_ = NULL;
+ for (size_t i = 0; i < receive_streams_.size(); ++i)
+ receiver_call_->DestroyVideoReceiveStream(receive_streams_[i]);
+ receive_streams_.clear();
+ allocated_decoders_.clear();
+}
+
+const unsigned int CallTest::kDefaultTimeoutMs = 30 * 1000;
+const unsigned int CallTest::kLongTimeoutMs = 120 * 1000;
+const uint8_t CallTest::kSendPayloadType = 100;
+const uint8_t CallTest::kFakeSendPayloadType = 125;
+const uint8_t CallTest::kSendRtxPayloadType = 98;
+const uint8_t CallTest::kRedPayloadType = 118;
+const uint8_t CallTest::kRtxRedPayloadType = 99;
+const uint8_t CallTest::kUlpfecPayloadType = 119;
+const uint32_t CallTest::kSendRtxSsrcs[kNumSsrcs] = {0xBADCAFD, 0xBADCAFE,
+ 0xBADCAFF};
+const uint32_t CallTest::kSendSsrcs[kNumSsrcs] = {0xC0FFED, 0xC0FFEE, 0xC0FFEF};
+const uint32_t CallTest::kReceiverLocalSsrc = 0x123456;
+const int CallTest::kNackRtpHistoryMs = 1000;
+
+BaseTest::BaseTest(unsigned int timeout_ms) : RtpRtcpObserver(timeout_ms) {
+}
+
+BaseTest::~BaseTest() {
+}
+
+Call::Config BaseTest::GetSenderCallConfig() {
+ return Call::Config();
+}
+
+Call::Config BaseTest::GetReceiverCallConfig() {
+ return Call::Config();
+}
+
+void BaseTest::OnCallsCreated(Call* sender_call, Call* receiver_call) {
+}
+
+void BaseTest::OnTransportsCreated(PacketTransport* send_transport,
+ PacketTransport* receive_transport) {}
+
+size_t BaseTest::GetNumStreams() const {
+ return 1;
+}
+
+void BaseTest::ModifyConfigs(
+ VideoSendStream::Config* send_config,
+ std::vector<VideoReceiveStream::Config>* receive_configs,
+ VideoEncoderConfig* encoder_config) {
+}
+
+void BaseTest::OnStreamsCreated(
+ VideoSendStream* send_stream,
+ const std::vector<VideoReceiveStream*>& receive_streams) {
+}
+
+void BaseTest::OnFrameGeneratorCapturerCreated(
+ FrameGeneratorCapturer* frame_generator_capturer) {
+}
+
+SendTest::SendTest(unsigned int timeout_ms) : BaseTest(timeout_ms) {
+}
+
+bool SendTest::ShouldCreateReceivers() const {
+ return false;
+}
+
+EndToEndTest::EndToEndTest(unsigned int timeout_ms) : BaseTest(timeout_ms) {
+}
+
+bool EndToEndTest::ShouldCreateReceivers() const {
+ return true;
+}
+
+} // namespace test
+} // namespace webrtc
diff --git a/webrtc/test/call_test.h b/webrtc/test/call_test.h
new file mode 100644
index 0000000000..cf024d9c65
--- /dev/null
+++ b/webrtc/test/call_test.h
@@ -0,0 +1,129 @@
+/*
+ * Copyright (c) 2014 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+#ifndef WEBRTC_TEST_COMMON_CALL_TEST_H_
+#define WEBRTC_TEST_COMMON_CALL_TEST_H_
+
+#include <vector>
+
+#include "webrtc/call.h"
+#include "webrtc/system_wrappers/include/scoped_vector.h"
+#include "webrtc/test/fake_decoder.h"
+#include "webrtc/test/fake_encoder.h"
+#include "webrtc/test/frame_generator_capturer.h"
+#include "webrtc/test/rtp_rtcp_observer.h"
+
+namespace webrtc {
+namespace test {
+
+class BaseTest;
+
+class CallTest : public ::testing::Test {
+ public:
+ CallTest();
+ ~CallTest();
+
+ static const size_t kNumSsrcs = 3;
+
+ static const unsigned int kDefaultTimeoutMs;
+ static const unsigned int kLongTimeoutMs;
+ static const uint8_t kSendPayloadType;
+ static const uint8_t kSendRtxPayloadType;
+ static const uint8_t kFakeSendPayloadType;
+ static const uint8_t kRedPayloadType;
+ static const uint8_t kRtxRedPayloadType;
+ static const uint8_t kUlpfecPayloadType;
+ static const uint32_t kSendRtxSsrcs[kNumSsrcs];
+ static const uint32_t kSendSsrcs[kNumSsrcs];
+ static const uint32_t kReceiverLocalSsrc;
+ static const int kNackRtpHistoryMs;
+
+ protected:
+ void RunBaseTest(BaseTest* test, const FakeNetworkPipe::Config& config);
+
+ void CreateCalls(const Call::Config& sender_config,
+ const Call::Config& receiver_config);
+ void CreateSenderCall(const Call::Config& config);
+ void CreateReceiverCall(const Call::Config& config);
+ void DestroyCalls();
+
+ void CreateSendConfig(size_t num_streams, Transport* send_transport);
+ void CreateMatchingReceiveConfigs(Transport* rtcp_send_transport);
+
+ void CreateFrameGeneratorCapturer();
+
+ void CreateStreams();
+ void Start();
+ void Stop();
+ void DestroyStreams();
+
+ Clock* const clock_;
+
+ rtc::scoped_ptr<Call> sender_call_;
+ rtc::scoped_ptr<PacketTransport> send_transport_;
+ VideoSendStream::Config send_config_;
+ VideoEncoderConfig encoder_config_;
+ VideoSendStream* send_stream_;
+
+ rtc::scoped_ptr<Call> receiver_call_;
+ rtc::scoped_ptr<PacketTransport> receive_transport_;
+ std::vector<VideoReceiveStream::Config> receive_configs_;
+ std::vector<VideoReceiveStream*> receive_streams_;
+
+ rtc::scoped_ptr<test::FrameGeneratorCapturer> frame_generator_capturer_;
+ test::FakeEncoder fake_encoder_;
+ ScopedVector<VideoDecoder> allocated_decoders_;
+};
+
+class BaseTest : public RtpRtcpObserver {
+ public:
+ explicit BaseTest(unsigned int timeout_ms);
+ virtual ~BaseTest();
+
+ virtual void PerformTest() = 0;
+ virtual bool ShouldCreateReceivers() const = 0;
+
+ virtual size_t GetNumStreams() const;
+
+ virtual Call::Config GetSenderCallConfig();
+ virtual Call::Config GetReceiverCallConfig();
+ virtual void OnCallsCreated(Call* sender_call, Call* receiver_call);
+ virtual void OnTransportsCreated(PacketTransport* send_transport,
+ PacketTransport* receive_transport);
+
+ virtual void ModifyConfigs(
+ VideoSendStream::Config* send_config,
+ std::vector<VideoReceiveStream::Config>* receive_configs,
+ VideoEncoderConfig* encoder_config);
+ virtual void OnStreamsCreated(
+ VideoSendStream* send_stream,
+ const std::vector<VideoReceiveStream*>& receive_streams);
+
+ virtual void OnFrameGeneratorCapturerCreated(
+ FrameGeneratorCapturer* frame_generator_capturer);
+};
+
+class SendTest : public BaseTest {
+ public:
+ explicit SendTest(unsigned int timeout_ms);
+
+ bool ShouldCreateReceivers() const override;
+};
+
+class EndToEndTest : public BaseTest {
+ public:
+ explicit EndToEndTest(unsigned int timeout_ms);
+
+ bool ShouldCreateReceivers() const override;
+};
+
+} // namespace test
+} // namespace webrtc
+
+#endif // WEBRTC_TEST_COMMON_CALL_TEST_H_
diff --git a/webrtc/test/channel_transport/OWNERS b/webrtc/test/channel_transport/OWNERS
new file mode 100644
index 0000000000..1acb902bc0
--- /dev/null
+++ b/webrtc/test/channel_transport/OWNERS
@@ -0,0 +1,2 @@
+henrikg@webrtc.org
+
diff --git a/webrtc/test/channel_transport/channel_transport.cc b/webrtc/test/channel_transport/channel_transport.cc
new file mode 100644
index 0000000000..25eb59d887
--- /dev/null
+++ b/webrtc/test/channel_transport/channel_transport.cc
@@ -0,0 +1,83 @@
+/*
+ * 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/channel_transport/include/channel_transport.h"
+
+#include <stdio.h>
+
+#if !defined(WEBRTC_ANDROID) && !defined(WEBRTC_IOS)
+#include "testing/gtest/include/gtest/gtest.h"
+#endif
+#include "webrtc/test/channel_transport/udp_transport.h"
+#include "webrtc/video_engine/vie_defines.h"
+#include "webrtc/voice_engine/include/voe_network.h"
+
+#if defined(WEBRTC_ANDROID) || defined(WEBRTC_IOS)
+#undef NDEBUG
+#include <assert.h>
+#endif
+
+namespace webrtc {
+namespace test {
+
+VoiceChannelTransport::VoiceChannelTransport(VoENetwork* voe_network,
+ int channel)
+ : channel_(channel),
+ voe_network_(voe_network) {
+ uint8_t socket_threads = 1;
+ socket_transport_ = UdpTransport::Create(channel, socket_threads);
+ int registered = voe_network_->RegisterExternalTransport(channel,
+ *socket_transport_);
+#if !defined(WEBRTC_ANDROID) && !defined(WEBRTC_IOS)
+ EXPECT_EQ(0, registered);
+#else
+ assert(registered == 0);
+#endif
+}
+
+VoiceChannelTransport::~VoiceChannelTransport() {
+ voe_network_->DeRegisterExternalTransport(channel_);
+ UdpTransport::Destroy(socket_transport_);
+}
+
+void VoiceChannelTransport::IncomingRTPPacket(
+ const int8_t* incoming_rtp_packet,
+ const size_t packet_length,
+ const char* /*from_ip*/,
+ const uint16_t /*from_port*/) {
+ voe_network_->ReceivedRTPPacket(
+ channel_, incoming_rtp_packet, packet_length, PacketTime());
+}
+
+void VoiceChannelTransport::IncomingRTCPPacket(
+ const int8_t* incoming_rtcp_packet,
+ const size_t packet_length,
+ const char* /*from_ip*/,
+ const uint16_t /*from_port*/) {
+ voe_network_->ReceivedRTCPPacket(channel_, incoming_rtcp_packet,
+ packet_length);
+}
+
+int VoiceChannelTransport::SetLocalReceiver(uint16_t rtp_port) {
+ int return_value = socket_transport_->InitializeReceiveSockets(this,
+ rtp_port);
+ if (return_value == 0) {
+ return socket_transport_->StartReceiving(kViENumReceiveSocketBuffers);
+ }
+ return return_value;
+}
+
+int VoiceChannelTransport::SetSendDestination(const char* ip_address,
+ uint16_t rtp_port) {
+ return socket_transport_->InitializeSendSockets(ip_address, rtp_port);
+}
+
+} // namespace test
+} // namespace webrtc
diff --git a/webrtc/test/channel_transport/include/channel_transport.h b/webrtc/test/channel_transport/include/channel_transport.h
new file mode 100644
index 0000000000..8b84517de0
--- /dev/null
+++ b/webrtc/test/channel_transport/include/channel_transport.h
@@ -0,0 +1,56 @@
+/*
+ * 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_CHANNEL_TRANSPORT_INCLUDE_CHANNEL_TRANSPORT_H_
+#define WEBRTC_TEST_CHANNEL_TRANSPORT_INCLUDE_CHANNEL_TRANSPORT_H_
+
+#include "webrtc/test/channel_transport/udp_transport.h"
+
+namespace webrtc {
+
+class VoENetwork;
+
+namespace test {
+
+// Helper class for VoiceEngine tests.
+class VoiceChannelTransport : public UdpTransportData {
+ public:
+ VoiceChannelTransport(VoENetwork* voe_network, int channel);
+
+ virtual ~VoiceChannelTransport();
+
+ // Start implementation of UdpTransportData.
+ void IncomingRTPPacket(const int8_t* incoming_rtp_packet,
+ const size_t packet_length,
+ const char* /*from_ip*/,
+ const uint16_t /*from_port*/) override;
+
+ void IncomingRTCPPacket(const int8_t* incoming_rtcp_packet,
+ const size_t packet_length,
+ const char* /*from_ip*/,
+ const uint16_t /*from_port*/) override;
+ // End implementation of UdpTransportData.
+
+ // Specifies the ports to receive RTP packets on.
+ int SetLocalReceiver(uint16_t rtp_port);
+
+ // Specifies the destination port and IP address for a specified channel.
+ int SetSendDestination(const char* ip_address, uint16_t rtp_port);
+
+ private:
+ int channel_;
+ VoENetwork* voe_network_;
+ UdpTransport* socket_transport_;
+};
+
+} // namespace test
+} // namespace webrtc
+
+#endif // WEBRTC_TEST_CHANNEL_TRANSPORT_INCLUDE_CHANNEL_TRANSPORT_H_
diff --git a/webrtc/test/channel_transport/traffic_control_win.cc b/webrtc/test/channel_transport/traffic_control_win.cc
new file mode 100644
index 0000000000..3584359758
--- /dev/null
+++ b/webrtc/test/channel_transport/traffic_control_win.cc
@@ -0,0 +1,252 @@
+/*
+ * Copyright (c) 2011 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/channel_transport/traffic_control_win.h"
+
+#include <assert.h>
+
+#include "webrtc/system_wrappers/include/trace.h"
+
+namespace webrtc {
+namespace test {
+
+TrafficControlWindows* TrafficControlWindows::instance = NULL;
+uint32_t TrafficControlWindows::refCounter = 0;
+
+TrafficControlWindows::TrafficControlWindows(const int32_t id)
+{
+}
+
+TrafficControlWindows* TrafficControlWindows::GetInstance(
+ const int32_t id)
+{
+ if(instance != NULL)
+ {
+ WEBRTC_TRACE(
+ kTraceDebug,
+ kTraceTransport,
+ id,
+ "TrafficControlWindows - Returning already created object");
+ refCounter++;
+ return instance;
+ }
+
+ WEBRTC_TRACE(kTraceMemory, kTraceTransport, id,
+ "TrafficControlWindows - Creating new object");
+ instance = new TrafficControlWindows(id);
+ if(instance == NULL)
+ {
+ WEBRTC_TRACE(kTraceMemory, kTraceTransport, id,
+ "TrafficControlWindows - Error allocating memory");
+ return NULL;
+ }
+
+ instance->tcRegister = NULL;
+ instance->tcDeregister = NULL;
+
+ instance->tcEnumerate = NULL;
+ instance->tcOpenInterface = NULL;
+ instance->tcCloseInterface = NULL;
+
+ instance->tcAddFlow = NULL;
+ instance->tcDeleteFlow = NULL;
+
+ instance->tcAddFilter = NULL;
+ instance->tcDeleteFilter = NULL;
+
+ HMODULE trafficLib = LoadLibrary(TEXT("traffic.dll"));
+ if(trafficLib == NULL)
+ {
+ WEBRTC_TRACE(
+ kTraceWarning,
+ kTraceTransport,
+ id,
+ "TrafficControlWindows - No QOS support, LoadLibrary returned NULL,\
+ last error: %d\n",
+ GetLastError());
+ delete instance;
+ instance = NULL;
+ return NULL;
+ }
+
+ instance->tcRegister = (registerFn)GetProcAddress(trafficLib,
+ "TcRegisterClient");
+ instance->tcDeregister = (deregisterFn)GetProcAddress(trafficLib,
+ "TcDeregisterClient");
+ instance->tcEnumerate = (enumerateFn)GetProcAddress(
+ trafficLib,
+ "TcEnumerateInterfaces");
+ instance->tcOpenInterface = (openInterfaceFn)GetProcAddress(
+ trafficLib,
+ "TcOpenInterfaceW");
+ instance->tcCloseInterface = (closeInterfaceFn)GetProcAddress(
+ trafficLib,
+ "TcCloseInterface");
+ instance->tcAddFlow = (flowAddFn)GetProcAddress(trafficLib,
+ "TcAddFlow");
+ instance->tcDeleteFlow = (flowDeleteFn)GetProcAddress(trafficLib,
+ "TcDeleteFlow");
+
+ instance->tcAddFilter = (filterAddFn)GetProcAddress(trafficLib,
+ "TcAddFilter");
+ instance->tcDeleteFilter = (filterDeleteFn)GetProcAddress(trafficLib,
+ "TcDeleteFilter");
+
+ if(instance->tcRegister == NULL ||
+ instance->tcDeregister == NULL ||
+ instance->tcEnumerate == NULL ||
+ instance->tcOpenInterface == NULL ||
+ instance->tcCloseInterface == NULL ||
+ instance->tcAddFlow == NULL ||
+ instance->tcAddFilter == NULL ||
+ instance->tcDeleteFlow == NULL ||
+ instance->tcDeleteFilter == NULL)
+ {
+ delete instance;
+ instance = NULL;
+ WEBRTC_TRACE(
+ kTraceError,
+ kTraceTransport,
+ id,
+ "TrafficControlWindows - Could not find function pointer for\
+ traffic control functions");
+ WEBRTC_TRACE(
+ kTraceError,
+ kTraceTransport,
+ id,
+ "Tcregister : %x, tcDeregister: %x, tcEnumerate: %x,\
+ tcOpenInterface: %x, tcCloseInterface: %x, tcAddFlow: %x, tcAddFilter: %x,\
+ tcDeleteFlow: %x, tcDeleteFilter: %x",
+ instance->tcRegister,
+ instance->tcDeregister,
+ instance->tcEnumerate,
+ instance->tcOpenInterface,
+ instance->tcCloseInterface,
+ instance->tcAddFlow,
+ instance->tcAddFilter,
+ instance->tcDeleteFlow,
+ instance->tcDeleteFilter );
+ return NULL;
+ }
+ refCounter++;
+ return instance;
+}
+
+void TrafficControlWindows::Release(TrafficControlWindows* gtc)
+{
+ if (0 == refCounter)
+ {
+ WEBRTC_TRACE(kTraceError, kTraceTransport, -1,
+ "TrafficControlWindows - Cannot release, refCounter is 0");
+ return;
+ }
+ if (NULL == gtc)
+ {
+ WEBRTC_TRACE(kTraceDebug, kTraceTransport, -1,
+ "TrafficControlWindows - Not releasing, gtc is NULL");
+ return;
+ }
+
+ WEBRTC_TRACE(kTraceDebug, kTraceTransport, -1,
+ "TrafficControlWindows - Releasing object");
+ refCounter--;
+ if ((0 == refCounter) && instance)
+ {
+ WEBRTC_TRACE(kTraceMemory, kTraceTransport, -1,
+ "TrafficControlWindows - Deleting object");
+ delete instance;
+ instance = NULL;
+ }
+}
+
+ULONG TrafficControlWindows::TcRegisterClient(
+ ULONG TciVersion,
+ HANDLE ClRegCtx,
+ PTCI_CLIENT_FUNC_LIST ClientHandlerList,
+ PHANDLE pClientHandle)
+{
+ assert(tcRegister != NULL);
+
+ return tcRegister(TciVersion, ClRegCtx, ClientHandlerList, pClientHandle);
+}
+
+ULONG TrafficControlWindows::TcDeregisterClient(HANDLE clientHandle)
+{
+ assert(tcDeregister != NULL);
+
+ return tcDeregister(clientHandle);
+}
+
+
+ULONG TrafficControlWindows::TcEnumerateInterfaces(
+ HANDLE ClientHandle,
+ PULONG pBufferSize,
+ PTC_IFC_DESCRIPTOR interfaceBuffer)
+{
+ assert(tcEnumerate != NULL);
+
+ return tcEnumerate(ClientHandle, pBufferSize, interfaceBuffer);
+}
+
+
+ULONG TrafficControlWindows::TcOpenInterfaceW(LPWSTR pInterfaceName,
+ HANDLE ClientHandle,
+ HANDLE ClIfcCtx,
+ PHANDLE pIfcHandle)
+{
+ assert(tcOpenInterface != NULL);
+
+ return tcOpenInterface(pInterfaceName, ClientHandle, ClIfcCtx, pIfcHandle);
+
+}
+
+ULONG TrafficControlWindows::TcCloseInterface(HANDLE IfcHandle)
+{
+ assert(tcCloseInterface != NULL);
+
+ return tcCloseInterface(IfcHandle);
+}
+
+ULONG TrafficControlWindows::TcAddFlow(HANDLE IfcHandle, HANDLE ClFlowCtx,
+ ULONG Flags, PTC_GEN_FLOW pGenericFlow,
+ PHANDLE pFlowHandle)
+{
+ assert(tcAddFlow != NULL);
+ return tcAddFlow(IfcHandle, ClFlowCtx, Flags, pGenericFlow, pFlowHandle);
+}
+
+ULONG TrafficControlWindows::TcAddFilter(HANDLE FlowHandle,
+ PTC_GEN_FILTER pGenericFilter,
+ PHANDLE pFilterHandle)
+{
+ assert(tcAddFilter != NULL);
+ return tcAddFilter(FlowHandle, pGenericFilter, pFilterHandle);
+}
+
+ULONG TrafficControlWindows::TcDeleteFlow(HANDLE FlowHandle)
+{
+ assert(tcDeleteFlow != NULL);
+ return tcDeleteFlow(FlowHandle);
+
+}
+
+ULONG TrafficControlWindows::TcDeleteFilter(HANDLE FilterHandle)
+{
+ assert(tcDeleteFilter != NULL);
+ return tcDeleteFilter(FilterHandle);
+}
+
+void MyClNotifyHandler(HANDLE ClRegCtx, HANDLE ClIfcCtx, ULONG Event,
+ HANDLE SubCode, ULONG BufSize, PVOID Buffer)
+{
+}
+
+} // namespace test
+} // namespace webrtc
diff --git a/webrtc/test/channel_transport/traffic_control_win.h b/webrtc/test/channel_transport/traffic_control_win.h
new file mode 100644
index 0000000000..1197b94b20
--- /dev/null
+++ b/webrtc/test/channel_transport/traffic_control_win.h
@@ -0,0 +1,99 @@
+/*
+ * Copyright (c) 2011 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_CHANNEL_TRANSPORT_TRAFFIC_CONTROL_WINDOWS_H_
+#define WEBRTC_TEST_CHANNEL_TRANSPORT_TRAFFIC_CONTROL_WINDOWS_H_
+
+#ifndef WIN32_LEAN_AND_MEAN
+#define WIN32_LEAN_AND_MEAN
+#endif
+
+// Disable deprication warning from traffic.h
+#pragma warning(disable : 4995)
+
+#include <windows.h>
+#include <qos.h>
+#include <ntddndis.h>
+#include <traffic.h>
+
+#include "webrtc/system_wrappers/include/trace.h"
+
+namespace webrtc {
+namespace test {
+void MyClNotifyHandler(HANDLE ClRegCtx, HANDLE ClIfcCtx, ULONG Event,
+ HANDLE SubCode, ULONG BufSize, PVOID Buffer);
+
+
+typedef ULONG (WINAPI *registerFn)(ULONG, HANDLE, PTCI_CLIENT_FUNC_LIST,
+ PHANDLE);
+typedef ULONG (WINAPI *deregisterFn)(HANDLE);
+typedef ULONG (WINAPI *enumerateFn)(HANDLE, PULONG, PTC_IFC_DESCRIPTOR);
+typedef ULONG (WINAPI *openInterfaceFn)(LPWSTR, HANDLE, HANDLE, PHANDLE);
+typedef ULONG (WINAPI *closeInterfaceFn)(HANDLE);
+typedef ULONG (WINAPI *flowAddFn)(HANDLE, HANDLE, ULONG, PTC_GEN_FLOW, PHANDLE);
+typedef ULONG (WINAPI *filterAddFn)(HANDLE, PTC_GEN_FILTER, PHANDLE);
+typedef ULONG (WINAPI *flowDeleteFn)(HANDLE);
+typedef ULONG (WINAPI *filterDeleteFn)(HANDLE);
+
+class TrafficControlWindows
+{
+ public:
+ // Factory method. Constructor disabled.
+ static TrafficControlWindows* GetInstance(const int32_t id);
+ static void Release(TrafficControlWindows* gtc);
+
+ ULONG TcRegisterClient(ULONG TciVersion, HANDLE ClRegCtx,
+ PTCI_CLIENT_FUNC_LIST ClientHandlerList,
+ PHANDLE pClientHandle);
+
+ ULONG TcDeregisterClient(HANDLE clientHandle);
+
+ ULONG TcEnumerateInterfaces(HANDLE ClientHandle, PULONG pBufferSize,
+ PTC_IFC_DESCRIPTOR interfaceBuffer);
+
+ ULONG TcOpenInterfaceW(LPWSTR pInterfaceName, HANDLE ClientHandle,
+ HANDLE ClIfcCtx, PHANDLE pIfcHandle);
+
+ ULONG TcCloseInterface(HANDLE IfcHandle);
+
+ ULONG TcAddFlow(HANDLE IfcHandle, HANDLE ClFlowCtx, ULONG Flags,
+ PTC_GEN_FLOW pGenericFlow, PHANDLE pFlowHandle);
+
+ ULONG TcAddFilter(HANDLE FlowHandle, PTC_GEN_FILTER pGenericFilter,
+ PHANDLE pFilterHandle);
+
+ ULONG TcDeleteFlow(HANDLE FlowHandle);
+ ULONG TcDeleteFilter(HANDLE FilterHandle);
+private:
+ TrafficControlWindows(const int32_t id);
+ TCI_CLIENT_FUNC_LIST QoSFunctions;
+
+ static TrafficControlWindows* instance;
+
+ registerFn tcRegister;
+ deregisterFn tcDeregister;
+
+ enumerateFn tcEnumerate;
+ openInterfaceFn tcOpenInterface;
+ closeInterfaceFn tcCloseInterface;
+
+ flowAddFn tcAddFlow;
+ flowDeleteFn tcDeleteFlow;
+
+ filterAddFn tcAddFilter;
+ filterDeleteFn tcDeleteFilter;
+
+ static uint32_t refCounter;
+};
+
+} // namespace test
+} // namespace webrtc
+
+#endif // WEBRTC_TEST_CHANNEL_TRANSPORT_TRAFFIC_CONTROL_WINDOWS_H_
diff --git a/webrtc/test/channel_transport/udp_socket2_manager_win.cc b/webrtc/test/channel_transport/udp_socket2_manager_win.cc
new file mode 100644
index 0000000000..5a11abbd6e
--- /dev/null
+++ b/webrtc/test/channel_transport/udp_socket2_manager_win.cc
@@ -0,0 +1,613 @@
+/*
+ * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "webrtc/test/channel_transport/udp_socket2_manager_win.h"
+
+#include <assert.h>
+#include <stdio.h>
+
+#include "webrtc/system_wrappers/include/aligned_malloc.h"
+#include "webrtc/test/channel_transport/udp_socket2_win.h"
+
+namespace webrtc {
+namespace test {
+
+uint32_t UdpSocket2ManagerWindows::_numOfActiveManagers = 0;
+bool UdpSocket2ManagerWindows::_wsaInit = false;
+
+UdpSocket2ManagerWindows::UdpSocket2ManagerWindows()
+ : UdpSocketManager(),
+ _id(-1),
+ _stopped(false),
+ _init(false),
+ _pCrit(CriticalSectionWrapper::CreateCriticalSection()),
+ _ioCompletionHandle(NULL),
+ _numActiveSockets(0),
+ _event(EventWrapper::Create())
+{
+ _managerNumber = _numOfActiveManagers++;
+
+ if(_numOfActiveManagers == 1)
+ {
+ WORD wVersionRequested = MAKEWORD(2, 2);
+ WSADATA wsaData;
+ _wsaInit = WSAStartup(wVersionRequested, &wsaData) == 0;
+ // TODO (hellner): seems safer to use RAII for this. E.g. what happens
+ // if a UdpSocket2ManagerWindows() created and destroyed
+ // without being initialized.
+ }
+}
+
+UdpSocket2ManagerWindows::~UdpSocket2ManagerWindows()
+{
+ WEBRTC_TRACE(kTraceDebug, kTraceTransport, _id,
+ "UdpSocket2ManagerWindows(%d)::~UdpSocket2ManagerWindows()",
+ _managerNumber);
+
+ if(_init)
+ {
+ _pCrit->Enter();
+ if(_numActiveSockets)
+ {
+ _pCrit->Leave();
+ _event->Wait(INFINITE);
+ }
+ else
+ {
+ _pCrit->Leave();
+ }
+ StopWorkerThreads();
+
+ for (WorkerList::iterator iter = _workerThreadsList.begin();
+ iter != _workerThreadsList.end(); ++iter) {
+ delete *iter;
+ }
+ _workerThreadsList.clear();
+ _ioContextPool.Free();
+
+ _numOfActiveManagers--;
+ if(_ioCompletionHandle)
+ {
+ CloseHandle(_ioCompletionHandle);
+ }
+ if (_numOfActiveManagers == 0)
+ {
+ if(_wsaInit)
+ {
+ WSACleanup();
+ }
+ }
+ }
+ if(_pCrit)
+ {
+ delete _pCrit;
+ }
+ if(_event)
+ {
+ delete _event;
+ }
+}
+
+bool UdpSocket2ManagerWindows::Init(int32_t id,
+ uint8_t& numOfWorkThreads) {
+ CriticalSectionScoped cs(_pCrit);
+ if ((_id != -1) || (_numOfWorkThreads != 0)) {
+ assert(_id != -1);
+ assert(_numOfWorkThreads != 0);
+ return false;
+ }
+ _id = id;
+ _numOfWorkThreads = numOfWorkThreads;
+ return true;
+}
+
+bool UdpSocket2ManagerWindows::Start()
+{
+ WEBRTC_TRACE(kTraceDebug, kTraceTransport, _id,
+ "UdpSocket2ManagerWindows(%d)::Start()",_managerNumber);
+ if(!_init)
+ {
+ StartWorkerThreads();
+ }
+
+ if(!_init)
+ {
+ return false;
+ }
+ _pCrit->Enter();
+ // Start worker threads.
+ _stopped = false;
+ int32_t error = 0;
+ for (WorkerList::iterator iter = _workerThreadsList.begin();
+ iter != _workerThreadsList.end() && !error; ++iter) {
+ if(!(*iter)->Start())
+ error = 1;
+ }
+ if(error)
+ {
+ WEBRTC_TRACE(
+ kTraceError,
+ kTraceTransport,
+ _id,
+ "UdpSocket2ManagerWindows(%d)::Start() error starting worker\
+ threads",
+ _managerNumber);
+ _pCrit->Leave();
+ return false;
+ }
+ _pCrit->Leave();
+ return true;
+}
+
+bool UdpSocket2ManagerWindows::StartWorkerThreads()
+{
+ if(!_init)
+ {
+ _pCrit->Enter();
+
+ _ioCompletionHandle = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL,
+ 0, 0);
+ if(_ioCompletionHandle == NULL)
+ {
+ int32_t error = GetLastError();
+ WEBRTC_TRACE(
+ kTraceError,
+ kTraceTransport,
+ _id,
+ "UdpSocket2ManagerWindows(%d)::StartWorkerThreads()"
+ "_ioCompletioHandle == NULL: error:%d",
+ _managerNumber,error);
+ _pCrit->Leave();
+ return false;
+ }
+
+ // Create worker threads.
+ uint32_t i = 0;
+ bool error = false;
+ while(i < _numOfWorkThreads && !error)
+ {
+ UdpSocket2WorkerWindows* pWorker =
+ new UdpSocket2WorkerWindows(_ioCompletionHandle);
+ if(pWorker->Init() != 0)
+ {
+ error = true;
+ delete pWorker;
+ break;
+ }
+ _workerThreadsList.push_front(pWorker);
+ i++;
+ }
+ if(error)
+ {
+ WEBRTC_TRACE(
+ kTraceError,
+ kTraceTransport,
+ _id,
+ "UdpSocket2ManagerWindows(%d)::StartWorkerThreads() error "
+ "creating work threads",
+ _managerNumber);
+ // Delete worker threads.
+ for (WorkerList::iterator iter = _workerThreadsList.begin();
+ iter != _workerThreadsList.end(); ++iter) {
+ delete *iter;
+ }
+ _workerThreadsList.clear();
+ _pCrit->Leave();
+ return false;
+ }
+ if(_ioContextPool.Init())
+ {
+ WEBRTC_TRACE(
+ kTraceError,
+ kTraceTransport,
+ _id,
+ "UdpSocket2ManagerWindows(%d)::StartWorkerThreads() error "
+ "initiating _ioContextPool",
+ _managerNumber);
+ _pCrit->Leave();
+ return false;
+ }
+ _init = true;
+ WEBRTC_TRACE(
+ kTraceDebug,
+ kTraceTransport,
+ _id,
+ "UdpSocket2ManagerWindows::StartWorkerThreads %d number of work "
+ "threads created and initialized",
+ _numOfWorkThreads);
+ _pCrit->Leave();
+ }
+ return true;
+}
+
+bool UdpSocket2ManagerWindows::Stop()
+{
+ WEBRTC_TRACE(kTraceDebug, kTraceTransport, _id,
+ "UdpSocket2ManagerWindows(%d)::Stop()",_managerNumber);
+
+ if(!_init)
+ {
+ return false;
+ }
+ _pCrit->Enter();
+ _stopped = true;
+ if(_numActiveSockets)
+ {
+ WEBRTC_TRACE(
+ kTraceError,
+ kTraceTransport,
+ _id,
+ "UdpSocket2ManagerWindows(%d)::Stop() there is still active\
+ sockets",
+ _managerNumber);
+ _pCrit->Leave();
+ return false;
+ }
+ // No active sockets. Stop all worker threads.
+ bool result = StopWorkerThreads();
+ _pCrit->Leave();
+ return result;
+}
+
+bool UdpSocket2ManagerWindows::StopWorkerThreads()
+{
+ int32_t error = 0;
+ WEBRTC_TRACE(
+ kTraceDebug,
+ kTraceTransport,
+ _id,
+ "UdpSocket2ManagerWindows(%d)::StopWorkerThreads() Worker\
+ threadsStoped, numActicve Sockets=%d",
+ _managerNumber,
+ _numActiveSockets);
+
+ // Release all threads waiting for GetQueuedCompletionStatus(..).
+ if(_ioCompletionHandle)
+ {
+ uint32_t i = 0;
+ for(i = 0; i < _workerThreadsList.size(); i++)
+ {
+ PostQueuedCompletionStatus(_ioCompletionHandle, 0 ,0 , NULL);
+ }
+ }
+ for (WorkerList::iterator iter = _workerThreadsList.begin();
+ iter != _workerThreadsList.end(); ++iter) {
+ if((*iter)->Stop() == false)
+ {
+ error = -1;
+ WEBRTC_TRACE(kTraceWarning, kTraceTransport, -1,
+ "failed to stop worker thread");
+ }
+ }
+
+ if(error)
+ {
+ WEBRTC_TRACE(
+ kTraceError,
+ kTraceTransport,
+ _id,
+ "UdpSocket2ManagerWindows(%d)::StopWorkerThreads() error stopping\
+ worker threads",
+ _managerNumber);
+ return false;
+ }
+ return true;
+}
+
+bool UdpSocket2ManagerWindows::AddSocketPrv(UdpSocket2Windows* s)
+{
+ WEBRTC_TRACE(kTraceDebug, kTraceTransport, _id,
+ "UdpSocket2ManagerWindows(%d)::AddSocketPrv()",_managerNumber);
+ if(!_init)
+ {
+ WEBRTC_TRACE(
+ kTraceError,
+ kTraceTransport,
+ _id,
+ "UdpSocket2ManagerWindows(%d)::AddSocketPrv() manager not\
+ initialized",
+ _managerNumber);
+ return false;
+ }
+ _pCrit->Enter();
+ if(s == NULL)
+ {
+ WEBRTC_TRACE(
+ kTraceError,
+ kTraceTransport,
+ _id,
+ "UdpSocket2ManagerWindows(%d)::AddSocketPrv() socket == NULL",
+ _managerNumber);
+ _pCrit->Leave();
+ return false;
+ }
+ if(s->GetFd() == NULL || s->GetFd() == INVALID_SOCKET)
+ {
+ WEBRTC_TRACE(
+ kTraceError,
+ kTraceTransport,
+ _id,
+ "UdpSocket2ManagerWindows(%d)::AddSocketPrv() socket->GetFd() ==\
+ %d",
+ _managerNumber,
+ (int32_t)s->GetFd());
+ _pCrit->Leave();
+ return false;
+
+ }
+ _ioCompletionHandle = CreateIoCompletionPort((HANDLE)s->GetFd(),
+ _ioCompletionHandle,
+ (ULONG_PTR)(s), 0);
+ if(_ioCompletionHandle == NULL)
+ {
+ int32_t error = GetLastError();
+ WEBRTC_TRACE(
+ kTraceError,
+ kTraceTransport,
+ _id,
+ "UdpSocket2ManagerWindows(%d)::AddSocketPrv() Error adding to IO\
+ completion: %d",
+ _managerNumber,
+ error);
+ _pCrit->Leave();
+ return false;
+ }
+ _numActiveSockets++;
+ _pCrit->Leave();
+ return true;
+}
+bool UdpSocket2ManagerWindows::RemoveSocketPrv(UdpSocket2Windows* s)
+{
+ if(!_init)
+ {
+ return false;
+ }
+ _pCrit->Enter();
+ _numActiveSockets--;
+ if(_numActiveSockets == 0)
+ {
+ _event->Set();
+ }
+ _pCrit->Leave();
+ return true;
+}
+
+PerIoContext* UdpSocket2ManagerWindows::PopIoContext()
+{
+ if(!_init)
+ {
+ return NULL;
+ }
+
+ PerIoContext* pIoC = NULL;
+ if(!_stopped)
+ {
+ pIoC = _ioContextPool.PopIoContext();
+ }else
+ {
+ WEBRTC_TRACE(
+ kTraceError,
+ kTraceTransport,
+ _id,
+ "UdpSocket2ManagerWindows(%d)::PopIoContext() Manager Not started",
+ _managerNumber);
+ }
+ return pIoC;
+}
+
+int32_t UdpSocket2ManagerWindows::PushIoContext(PerIoContext* pIoContext)
+{
+ return _ioContextPool.PushIoContext(pIoContext);
+}
+
+IoContextPool::IoContextPool()
+ : _pListHead(NULL),
+ _init(false),
+ _size(0),
+ _inUse(0)
+{
+}
+
+IoContextPool::~IoContextPool()
+{
+ Free();
+ assert(_size.Value() == 0);
+ AlignedFree(_pListHead);
+}
+
+int32_t IoContextPool::Init(uint32_t /*increaseSize*/)
+{
+ if(_init)
+ {
+ return 0;
+ }
+
+ _pListHead = (PSLIST_HEADER)AlignedMalloc(sizeof(SLIST_HEADER),
+ MEMORY_ALLOCATION_ALIGNMENT);
+ if(_pListHead == NULL)
+ {
+ return -1;
+ }
+ InitializeSListHead(_pListHead);
+ _init = true;
+ return 0;
+}
+
+PerIoContext* IoContextPool::PopIoContext()
+{
+ if(!_init)
+ {
+ return NULL;
+ }
+
+ PSLIST_ENTRY pListEntry = InterlockedPopEntrySList(_pListHead);
+ if(pListEntry == NULL)
+ {
+ IoContextPoolItem* item = (IoContextPoolItem*)
+ AlignedMalloc(
+ sizeof(IoContextPoolItem),
+ MEMORY_ALLOCATION_ALIGNMENT);
+ if(item == NULL)
+ {
+ return NULL;
+ }
+ memset(&item->payload.ioContext,0,sizeof(PerIoContext));
+ item->payload.base = item;
+ pListEntry = &(item->itemEntry);
+ ++_size;
+ }
+ ++_inUse;
+ return &((IoContextPoolItem*)pListEntry)->payload.ioContext;
+}
+
+int32_t IoContextPool::PushIoContext(PerIoContext* pIoContext)
+{
+ // TODO (hellner): Overlapped IO should be completed at this point. Perhaps
+ // add an assert?
+ const bool overlappedIOCompleted = HasOverlappedIoCompleted(
+ (LPOVERLAPPED)pIoContext);
+
+ IoContextPoolItem* item = ((IoContextPoolItemPayload*)pIoContext)->base;
+
+ const int32_t usedItems = --_inUse;
+ const int32_t totalItems = _size.Value();
+ const int32_t freeItems = totalItems - usedItems;
+ if(freeItems < 0)
+ {
+ assert(false);
+ AlignedFree(item);
+ return -1;
+ }
+ if((freeItems >= totalItems>>1) &&
+ overlappedIOCompleted)
+ {
+ AlignedFree(item);
+ --_size;
+ return 0;
+ }
+ InterlockedPushEntrySList(_pListHead, &(item->itemEntry));
+ return 0;
+}
+
+int32_t IoContextPool::Free()
+{
+ if(!_init)
+ {
+ return 0;
+ }
+
+ int32_t itemsFreed = 0;
+ PSLIST_ENTRY pListEntry = InterlockedPopEntrySList(_pListHead);
+ while(pListEntry != NULL)
+ {
+ IoContextPoolItem* item = ((IoContextPoolItem*)pListEntry);
+ AlignedFree(item);
+ --_size;
+ itemsFreed++;
+ pListEntry = InterlockedPopEntrySList(_pListHead);
+ }
+ return itemsFreed;
+}
+
+int32_t UdpSocket2WorkerWindows::_numOfWorkers = 0;
+
+UdpSocket2WorkerWindows::UdpSocket2WorkerWindows(HANDLE ioCompletionHandle)
+ : _ioCompletionHandle(ioCompletionHandle),
+ _init(false)
+{
+ _workerNumber = _numOfWorkers++;
+ WEBRTC_TRACE(kTraceMemory, kTraceTransport, -1,
+ "UdpSocket2WorkerWindows created");
+}
+
+UdpSocket2WorkerWindows::~UdpSocket2WorkerWindows()
+{
+ WEBRTC_TRACE(kTraceMemory, kTraceTransport, -1,
+ "UdpSocket2WorkerWindows deleted");
+}
+
+bool UdpSocket2WorkerWindows::Start()
+{
+ WEBRTC_TRACE(kTraceStateInfo, kTraceTransport, -1,
+ "Start UdpSocket2WorkerWindows");
+ if (!_pThread->Start())
+ return false;
+
+ _pThread->SetPriority(kRealtimePriority);
+ return true;
+}
+
+bool UdpSocket2WorkerWindows::Stop()
+{
+ WEBRTC_TRACE(kTraceStateInfo, kTraceTransport, -1,
+ "Stop UdpSocket2WorkerWindows");
+ return _pThread->Stop();
+}
+
+int32_t UdpSocket2WorkerWindows::Init()
+{
+ if(!_init)
+ {
+ const char* threadName = "UdpSocket2ManagerWindows_thread";
+ _pThread = ThreadWrapper::CreateThread(Run, this, threadName);
+ _init = true;
+ }
+ return 0;
+}
+
+bool UdpSocket2WorkerWindows::Run(void* obj)
+{
+ UdpSocket2WorkerWindows* pWorker =
+ static_cast<UdpSocket2WorkerWindows*>(obj);
+ return pWorker->Process();
+}
+
+// Process should always return true. Stopping the worker threads is done in
+// the UdpSocket2ManagerWindows::StopWorkerThreads() function.
+bool UdpSocket2WorkerWindows::Process()
+{
+ int32_t success = 0;
+ DWORD ioSize = 0;
+ UdpSocket2Windows* pSocket = NULL;
+ PerIoContext* pIOContext = 0;
+ OVERLAPPED* pOverlapped = 0;
+ success = GetQueuedCompletionStatus(_ioCompletionHandle,
+ &ioSize,
+ (ULONG_PTR*)&pSocket, &pOverlapped, 200);
+
+ uint32_t error = 0;
+ if(!success)
+ {
+ error = GetLastError();
+ if(error == WAIT_TIMEOUT)
+ {
+ return true;
+ }
+ // This may happen if e.g. PostQueuedCompletionStatus() has been called.
+ // The IO context still needs to be reclaimed or re-used which is done
+ // in UdpSocket2Windows::IOCompleted(..).
+ }
+ if(pSocket == NULL)
+ {
+ WEBRTC_TRACE(
+ kTraceDebug,
+ kTraceTransport,
+ -1,
+ "UdpSocket2WorkerWindows(%d)::Process(), pSocket == 0, end thread",
+ _workerNumber);
+ return true;
+ }
+ pIOContext = (PerIoContext*)pOverlapped;
+ pSocket->IOCompleted(pIOContext,ioSize,error);
+ return true;
+}
+
+} // namespace test
+} // namespace webrtc
diff --git a/webrtc/test/channel_transport/udp_socket2_manager_win.h b/webrtc/test/channel_transport/udp_socket2_manager_win.h
new file mode 100644
index 0000000000..c6af03a702
--- /dev/null
+++ b/webrtc/test/channel_transport/udp_socket2_manager_win.h
@@ -0,0 +1,162 @@
+/*
+ * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#ifndef WEBRTC_TEST_CHANNEL_TRANSPORT_UDP_SOCKET2_MANAGER_WINDOWS_H_
+#define WEBRTC_TEST_CHANNEL_TRANSPORT_UDP_SOCKET2_MANAGER_WINDOWS_H_
+
+#include <winsock2.h>
+#include <list>
+
+#include "webrtc/system_wrappers/include/atomic32.h"
+#include "webrtc/system_wrappers/include/critical_section_wrapper.h"
+#include "webrtc/system_wrappers/include/event_wrapper.h"
+#include "webrtc/system_wrappers/include/thread_wrapper.h"
+#include "webrtc/test/channel_transport/udp_socket2_win.h"
+#include "webrtc/test/channel_transport/udp_socket_manager_wrapper.h"
+#include "webrtc/test/channel_transport/udp_transport.h"
+
+#define MAX_IO_BUFF_SIZE 1600
+
+namespace webrtc {
+namespace test {
+
+enum IO_OPERATION {
+ OP_READ,
+ OP_WRITE
+};
+
+class UdpSocket2Windows;
+
+// Struct used for all socket I/O operations.
+struct PerIoContext {
+ WSAOVERLAPPED overlapped;
+ char buffer[MAX_IO_BUFF_SIZE];
+ WSABUF wsabuf;
+ size_t nTotalBytes;
+ int nSentBytes;
+ int bytes;
+ IO_OPERATION ioOperation;
+ SocketAddress from;
+ int fromLen;
+ // Should be set to true if the I/O context was passed to the system by
+ // a thread not controlled by the socket implementation.
+ bool ioInitiatedByThreadWrapper;
+ // TODO (hellner): Not used. Delete it.
+ PerIoContext* pNextFree;
+};
+
+struct IoContextPoolItem;
+struct IoContextPoolItemPayload
+{
+ PerIoContext ioContext;
+ IoContextPoolItem* base;
+};
+
+struct IoContextPoolItem
+{
+ // Atomic single linked list entry header.
+ SLIST_ENTRY itemEntry;
+ // Atomic single linked list payload
+ IoContextPoolItemPayload payload;
+};
+
+class IoContextPool
+{
+public:
+ IoContextPool();
+ virtual ~IoContextPool();
+ virtual int32_t Init(uint32_t increaseSize = 128);
+ // Re-use an old unused IO context or create a new one.
+ virtual PerIoContext* PopIoContext();
+ virtual int32_t PushIoContext(PerIoContext* pIoContext);
+ virtual inline int32_t GetSize(uint32_t* inUse = 0)
+ {return _size.Value();}
+ virtual int32_t Free();
+private:
+ // Sample code for use of msfts single linked atomic list can be found here:
+ // http://msdn.microsoft.com/en-us/library/ms686962(VS.85).aspx
+
+ // Atomic single linked list head.
+ PSLIST_HEADER _pListHead;
+
+ bool _init;
+ Atomic32 _size;
+ Atomic32 _inUse;
+};
+
+class UdpSocket2WorkerWindows
+{
+public:
+ UdpSocket2WorkerWindows(HANDLE ioCompletionHandle);
+ virtual ~UdpSocket2WorkerWindows();
+
+ virtual bool Start();
+ virtual bool Stop();
+ virtual int32_t Init();
+protected:
+ static bool Run(void* obj);
+ bool Process();
+private:
+ HANDLE _ioCompletionHandle;
+ rtc::scoped_ptr<ThreadWrapper> _pThread;
+ static int32_t _numOfWorkers;
+ int32_t _workerNumber;
+ volatile bool _stop;
+ bool _init;
+};
+
+class UdpSocket2ManagerWindows : public UdpSocketManager
+{
+public:
+ UdpSocket2ManagerWindows();
+ virtual ~UdpSocket2ManagerWindows();
+
+ virtual bool Init(int32_t id, uint8_t& numOfWorkThreads);
+
+ virtual bool Start();
+ virtual bool Stop();
+
+ virtual inline bool AddSocket(UdpSocketWrapper* s)
+ {if(s) return AddSocketPrv(reinterpret_cast<UdpSocket2Windows*>(s));
+ return false;}
+ virtual bool RemoveSocket(UdpSocketWrapper* s)
+ {if(s) return RemoveSocketPrv(reinterpret_cast<UdpSocket2Windows*>(s));
+ return false;}
+
+ PerIoContext* PopIoContext(void);
+ int32_t PushIoContext(PerIoContext* pIoContext);
+
+private:
+ typedef std::list<UdpSocket2WorkerWindows*> WorkerList;
+ bool StopWorkerThreads();
+ bool StartWorkerThreads();
+ bool AddSocketPrv(UdpSocket2Windows* s);
+ bool RemoveSocketPrv(UdpSocket2Windows* s);
+
+ static uint32_t _numOfActiveManagers;
+ static bool _wsaInit;
+
+ int32_t _id;
+ CriticalSectionWrapper* _pCrit;
+ int32_t _managerNumber;
+ volatile bool _stopped;
+ bool _init;
+ int32_t _numActiveSockets;
+ WorkerList _workerThreadsList;
+ EventWrapper* _event;
+
+ HANDLE _ioCompletionHandle;
+ IoContextPool _ioContextPool;
+};
+
+} // namespace test
+} // namespace webrtc
+
+#endif // WEBRTC_TEST_CHANNEL_TRANSPORT_UDP_SOCKET2_MANAGER_WINDOWS_H_
diff --git a/webrtc/test/channel_transport/udp_socket2_win.cc b/webrtc/test/channel_transport/udp_socket2_win.cc
new file mode 100644
index 0000000000..4c63dc938d
--- /dev/null
+++ b/webrtc/test/channel_transport/udp_socket2_win.cc
@@ -0,0 +1,1374 @@
+/*
+ * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "webrtc/test/channel_transport/udp_socket2_win.h"
+
+#include <assert.h>
+#include <stdlib.h>
+#include <winsock2.h>
+
+#include "webrtc/base/format_macros.h"
+#include "webrtc/system_wrappers/include/sleep.h"
+#include "webrtc/test/channel_transport/traffic_control_win.h"
+#include "webrtc/test/channel_transport/udp_socket2_manager_win.h"
+
+#pragma warning(disable : 4311)
+
+namespace webrtc {
+namespace test {
+
+typedef struct _QOS_DESTADDR
+{
+ QOS_OBJECT_HDR ObjectHdr;
+ const struct sockaddr* SocketAddress;
+ ULONG SocketAddressLength;
+} QOS_DESTADDR, *LPQOS_DESTADDR;
+
+typedef const QOS_DESTADDR* LPCQOS_DESTADDR;
+
+// TODO (patrikw): seems to be defined in ws2ipdef.h as 3. How come it's
+// redefined here (as a different value)?
+#define IP_TOS 8
+
+#define QOS_GENERAL_ID_BASE 2000
+#define QOS_OBJECT_DESTADDR (0x00000004 + QOS_GENERAL_ID_BASE)
+
+UdpSocket2Windows::UdpSocket2Windows(const int32_t id,
+ UdpSocketManager* mgr, bool ipV6Enable,
+ bool disableGQOS)
+ : _id(id),
+ _qos(true),
+ _iProtocol(0),
+ _outstandingCalls(0),
+ _outstandingCallComplete(0),
+ _terminate(false),
+ _addedToMgr(false),
+ _safeTodelete(false),
+ _outstandingCallsDisabled(false),
+ _clientHandle(NULL),
+ _flowHandle(NULL),
+ _filterHandle(NULL),
+ _flow(NULL),
+ _gtc(NULL),
+ _pcp(-2),
+ _receiveBuffers(0)
+{
+ WEBRTC_TRACE(kTraceMemory, kTraceTransport, _id,
+ "UdpSocket2Windows::UdpSocket2Windows()");
+
+ _wantsIncoming = false;
+ _mgr = static_cast<UdpSocket2ManagerWindows *>(mgr);
+
+ _obj = NULL;
+ _incomingCb = NULL;
+ _socket = INVALID_SOCKET;
+ _pCrit = CriticalSectionWrapper::CreateCriticalSection();
+ _ptrCbRWLock = RWLockWrapper::CreateRWLock();
+ _ptrDestRWLock = RWLockWrapper::CreateRWLock();
+ _ptrSocketRWLock = RWLockWrapper::CreateRWLock();
+ _ptrDeleteCrit = CriticalSectionWrapper::CreateCriticalSection();
+ _ptrDeleteCond = ConditionVariableWrapper::CreateConditionVariable();
+
+ // Check if QoS is supported.
+ BOOL bProtocolFound = FALSE;
+ WSAPROTOCOL_INFO *lpProtocolBuf = NULL;
+ WSAPROTOCOL_INFO pProtocolInfo;
+
+ if(!disableGQOS)
+ {
+ DWORD dwBufLen = 0;
+ // Set dwBufLen to the size needed to retreive all the requested
+ // information from WSAEnumProtocols.
+ int32_t nRet = WSAEnumProtocols(NULL, lpProtocolBuf, &dwBufLen);
+ lpProtocolBuf = (WSAPROTOCOL_INFO*)malloc(dwBufLen);
+ nRet = WSAEnumProtocols(NULL, lpProtocolBuf, &dwBufLen);
+
+ if (ipV6Enable)
+ {
+ _iProtocol=AF_INET6;
+ } else {
+ _iProtocol=AF_INET;
+ }
+
+ for (int32_t i=0; i<nRet; i++)
+ {
+ if (_iProtocol == lpProtocolBuf[i].iAddressFamily &&
+ IPPROTO_UDP == lpProtocolBuf[i].iProtocol)
+ {
+ if ((XP1_QOS_SUPPORTED ==
+ (XP1_QOS_SUPPORTED & lpProtocolBuf[i].dwServiceFlags1)))
+ {
+ pProtocolInfo = lpProtocolBuf[i];
+ bProtocolFound = TRUE;
+ break;
+ }
+ }
+ }
+ }
+
+ if(!bProtocolFound)
+ {
+ free(lpProtocolBuf);
+ _qos=false;
+ WEBRTC_TRACE(
+ kTraceError,
+ kTraceTransport,
+ _id,
+ "UdpSocket2Windows::UdpSocket2Windows(), SOCKET_ERROR_NO_QOS,\
+ !bProtocolFound");
+ } else {
+
+ _socket = WSASocket(FROM_PROTOCOL_INFO, FROM_PROTOCOL_INFO,
+ FROM_PROTOCOL_INFO,&pProtocolInfo, 0,
+ WSA_FLAG_OVERLAPPED);
+ free(lpProtocolBuf);
+
+ if (_socket != INVALID_SOCKET)
+ {
+ return;
+ } else {
+ _qos = false;
+ WEBRTC_TRACE(
+ kTraceError,
+ kTraceTransport,
+ _id,
+ "UdpSocket2Windows::UdpSocket2Windows(), SOCKET_ERROR_NO_QOS");
+ }
+ }
+ // QoS not supported.
+ if(ipV6Enable)
+ {
+ _socket = WSASocket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP, 0 , 0,
+ WSA_FLAG_OVERLAPPED);
+ }else
+ {
+ _socket = WSASocket(AF_INET, SOCK_DGRAM, IPPROTO_UDP, 0 , 0,
+ WSA_FLAG_OVERLAPPED);
+ }
+ if (_socket == INVALID_SOCKET)
+ {
+ WEBRTC_TRACE(
+ kTraceError,
+ kTraceTransport,
+ _id,
+ "UdpSocket2Windows::UdpSocket2Windows(), INVALID_SOCKET,\
+ WSAerror: %d",
+ WSAGetLastError());
+ }
+
+ // Disable send buffering on the socket to improve CPU usage.
+ // This is done by setting SO_SNDBUF to 0.
+ int32_t nZero = 0;
+ int32_t nRet = setsockopt(_socket, SOL_SOCKET, SO_SNDBUF,
+ (char*)&nZero, sizeof(nZero));
+ if( nRet == SOCKET_ERROR )
+ {
+ WEBRTC_TRACE(
+ kTraceError,
+ kTraceTransport,
+ _id,
+ "UdpSocket2Windows::UdpSocket2Windows(), SOCKET_ERROR,\
+ WSAerror: %d",
+ WSAGetLastError());
+ }
+}
+
+UdpSocket2Windows::~UdpSocket2Windows()
+{
+ WEBRTC_TRACE(kTraceMemory, kTraceTransport, _id,
+ "UdpSocket2Windows::~UdpSocket2Windows()");
+
+ WaitForOutstandingCalls();
+
+ delete _ptrCbRWLock;
+ delete _ptrDeleteCrit;
+ delete _ptrDeleteCond;
+ delete _ptrDestRWLock;
+ delete _ptrSocketRWLock;
+
+ if(_pCrit)
+ delete _pCrit;
+
+ if (_flow)
+ {
+ free(_flow);
+ _flow = NULL;
+ }
+
+ if (_gtc)
+ {
+ if(_filterHandle)
+ {
+ _gtc->TcDeleteFilter(_filterHandle);
+ }
+ if(_flowHandle)
+ {
+ _gtc->TcDeleteFlow(_flowHandle);
+ }
+ TrafficControlWindows::Release( _gtc);
+ }
+}
+
+bool UdpSocket2Windows::ValidHandle()
+{
+ return GetFd() != INVALID_SOCKET;
+}
+
+bool UdpSocket2Windows::SetCallback(CallbackObj obj, IncomingSocketCallback cb)
+{
+ _ptrCbRWLock->AcquireLockExclusive();
+ _obj = obj;
+ _incomingCb = cb;
+ _ptrCbRWLock->ReleaseLockExclusive();
+
+ WEBRTC_TRACE(kTraceDebug, kTraceTransport, _id,
+ "UdpSocket2Windows(%d)::SetCallback ",(int32_t)this);
+ if(_addedToMgr)
+ {
+ WEBRTC_TRACE(kTraceDebug, kTraceTransport, _id,
+ "UdpSocket2Windows(%d)::SetCallback alreadey added",
+ (int32_t) this);
+ return false;
+
+ }
+ if (_mgr->AddSocket(this))
+ {
+ WEBRTC_TRACE(
+ kTraceDebug, kTraceTransport, _id,
+ "UdpSocket2Windows(%d)::SetCallback socket added to manager",
+ (int32_t)this);
+ _addedToMgr = true;
+ return true;
+ }
+
+ WEBRTC_TRACE(kTraceDebug, kTraceTransport, _id,
+ "UdpSocket2Windows(%d)::SetCallback error adding me to mgr",
+ (int32_t) this);
+ return false;
+}
+
+bool UdpSocket2Windows::SetSockopt(int32_t level, int32_t optname,
+ const int8_t* optval, int32_t optlen)
+{
+ bool returnValue = true;
+ if(!AquireSocket())
+ {
+ return false;
+ }
+ if(0 != setsockopt(_socket, level, optname,
+ reinterpret_cast<const char*>(optval), optlen ))
+ {
+ WEBRTC_TRACE(kTraceError, kTraceTransport, _id,
+ "UdpSocket2Windows::SetSockopt(), WSAerror:%d",
+ WSAGetLastError());
+ returnValue = false;
+ }
+ ReleaseSocket();
+ return returnValue;
+}
+
+bool UdpSocket2Windows::StartReceiving(uint32_t receiveBuffers)
+{
+ WEBRTC_TRACE(kTraceDebug, kTraceTransport, _id,
+ "UdpSocket2Windows(%d)::StartReceiving(%d)", (int32_t)this,
+ receiveBuffers);
+
+ _wantsIncoming = true;
+
+ int32_t numberOfReceiveBuffersToCreate =
+ receiveBuffers - _receiveBuffers.Value();
+ numberOfReceiveBuffersToCreate = (numberOfReceiveBuffersToCreate < 0) ?
+ 0 : numberOfReceiveBuffersToCreate;
+
+ int32_t error = 0;
+ for(int32_t i = 0;
+ i < numberOfReceiveBuffersToCreate;
+ i++)
+ {
+ if(PostRecv())
+ {
+ WEBRTC_TRACE(kTraceError, kTraceTransport, _id,
+ "UdpSocket2Windows::StartReceiving() i=%d", i);
+ error = -1;
+ break;
+ }
+ ++_receiveBuffers;
+ }
+ if(error == -1)
+ {
+ return false;
+ }
+
+ WEBRTC_TRACE(kTraceDebug, kTraceTransport, _id,
+ "Socket receiving using:%d number of buffers",
+ _receiveBuffers.Value());
+ return true;
+}
+
+bool UdpSocket2Windows::StopReceiving()
+{
+ WEBRTC_TRACE(kTraceDebug, kTraceTransport, _id,
+ "UdpSocket2Windows::StopReceiving()");
+ _wantsIncoming = false;
+ return true;
+}
+
+bool UdpSocket2Windows::Bind(const SocketAddress& name)
+{
+ const struct sockaddr* addr =
+ reinterpret_cast<const struct sockaddr*>(&name);
+ bool returnValue = true;
+ if(!AquireSocket())
+ {
+ return false;
+ }
+ if (0 != bind(_socket, addr, sizeof(SocketAddress)))
+ {
+ WEBRTC_TRACE(kTraceError, kTraceTransport, _id,
+ "UdpSocket2Windows::Bind() WSAerror: %d",
+ WSAGetLastError());
+ returnValue = false;
+ }
+ ReleaseSocket();
+ return returnValue;
+}
+
+int32_t UdpSocket2Windows::SendTo(const int8_t* buf, size_t len,
+ const SocketAddress& to)
+{
+ int32_t retVal = 0;
+ int32_t error = 0;
+ PerIoContext* pIoContext = _mgr->PopIoContext();
+ if(pIoContext == 0)
+ {
+ WEBRTC_TRACE(kTraceError, kTraceTransport, _id,
+ "UdpSocket2Windows(%d)::SendTo(), pIoContext==0",
+ (int32_t) this);
+ return -1;
+ }
+ // sizeof(pIoContext->buffer) is smaller than the highest number that
+ // can be represented by a size_t.
+ if(len >= sizeof(pIoContext->buffer))
+ {
+ WEBRTC_TRACE(
+ kTraceError,
+ kTraceTransport,
+ _id,
+ "UdpSocket2Windows(%d)::SendTo(), len= %" PRIuS
+ " > buffer_size = %d",
+ (int32_t) this,
+ len,sizeof(pIoContext->buffer));
+ len = sizeof(pIoContext->buffer);
+ }
+
+ memcpy(pIoContext->buffer,buf,len);
+ pIoContext->wsabuf.buf = pIoContext->buffer;
+ pIoContext->wsabuf.len = static_cast<ULONG>(len);
+ pIoContext->fromLen=sizeof(SocketAddress);
+ pIoContext->ioOperation = OP_WRITE;
+ pIoContext->nTotalBytes = len;
+ pIoContext->nSentBytes=0;
+
+ DWORD numOfbytesSent = 0;
+ const struct sockaddr* addr = reinterpret_cast<const struct sockaddr*>(&to);
+
+ if(!AquireSocket())
+ {
+ _mgr->PushIoContext(pIoContext);
+ return -1;
+ }
+ // Assume that the WSASendTo call will be successfull to make sure that
+ // _outstandingCalls is positive. Roll back if WSASendTo failed.
+ if(!NewOutstandingCall())
+ {
+ _mgr->PushIoContext(pIoContext);
+ ReleaseSocket();
+ return -1;
+ }
+ retVal = WSASendTo(_socket, &pIoContext->wsabuf, 1, &numOfbytesSent,
+ 0, addr, sizeof(SocketAddress),
+ &(pIoContext->overlapped), 0);
+ ReleaseSocket();
+
+ if( retVal == SOCKET_ERROR )
+ {
+ error = WSAGetLastError();
+ if(error != ERROR_IO_PENDING)
+ {
+ WEBRTC_TRACE(kTraceError, kTraceTransport, _id,
+ "UdpSocket2Windows::SendTo() WSAerror: %d",error);
+ }
+ }
+ if(retVal == 0 || (retVal == SOCKET_ERROR && error == ERROR_IO_PENDING))
+ {
+ return static_cast<int32_t>(len);
+ }
+ error = _mgr->PushIoContext(pIoContext);
+ if(error)
+ {
+ WEBRTC_TRACE(
+ kTraceError,
+ kTraceTransport,
+ _id,
+ "UdpSocket2Windows(%d)::SendTo(), error:%d pushing ioContext",
+ (int32_t)this, error);
+ }
+
+ // Roll back.
+ OutstandingCallCompleted();
+ return -1;
+}
+
+void UdpSocket2Windows::IOCompleted(PerIoContext* pIOContext,
+ uint32_t ioSize, uint32_t error)
+{
+ if(pIOContext == NULL || error == ERROR_OPERATION_ABORTED)
+ {
+ if ((pIOContext != NULL) &&
+ !pIOContext->ioInitiatedByThreadWrapper &&
+ (error == ERROR_OPERATION_ABORTED) &&
+ (pIOContext->ioOperation == OP_READ) &&
+ _outstandingCallsDisabled)
+ {
+ // !pIOContext->initiatedIOByThreadWrapper indicate that the I/O
+ // was not initiated by a ThreadWrapper thread.
+ // This may happen if the thread that initiated receiving (e.g.
+ // by calling StartListen())) is deleted before any packets have
+ // been received.
+ // In this case there is no packet in the PerIoContext. Re-use it
+ // to post a new PostRecv(..).
+ // Note 1: the PerIoContext will henceforth be posted by a thread
+ // that is controlled by the socket implementation.
+ // Note 2: This is more likely to happen to RTCP packets as
+ // they are less frequent than RTP packets.
+ // Note 3: _outstandingCallsDisabled being false indicates
+ // that the socket isn't being shut down.
+ // Note 4: This should only happen buffers set to receive packets
+ // (OP_READ).
+ } else {
+ if(pIOContext == NULL)
+ {
+ WEBRTC_TRACE(
+ kTraceError,
+ kTraceTransport,
+ _id,
+ "UdpSocket2Windows::IOCompleted(%d,%d,%d), %d",
+ (int32_t)pIOContext,
+ ioSize,
+ error,
+ pIOContext ? (int32_t)pIOContext->ioOperation : -1);
+ } else {
+ WEBRTC_TRACE(
+ kTraceDebug,
+ kTraceTransport,
+ _id,
+ "UdpSocket2Windows::IOCompleted() Operation aborted");
+ }
+ if(pIOContext)
+ {
+ int32_t remainingReceiveBuffers = --_receiveBuffers;
+ if(remainingReceiveBuffers < 0)
+ {
+ assert(false);
+ }
+ int32_t err = _mgr->PushIoContext(pIOContext);
+ if(err)
+ {
+ WEBRTC_TRACE(
+ kTraceError,
+ kTraceTransport,
+ _id,
+ "UdpSocket2Windows::IOCompleted(), err = %d, when\
+ pushing ioContext after error",
+ err);
+ }
+ }
+ OutstandingCallCompleted();
+ return;
+ }
+ } // if (pIOContext == NULL || error == ERROR_OPERATION_ABORTED)
+
+ if(pIOContext->ioOperation == OP_WRITE)
+ {
+ _mgr->PushIoContext(pIOContext);
+ }
+ else if(pIOContext->ioOperation == OP_READ)
+ {
+ if(!error && ioSize != 0)
+ {
+ _ptrCbRWLock->AcquireLockShared();
+ if(_wantsIncoming && _incomingCb)
+ {
+ _incomingCb(_obj,
+ reinterpret_cast<const int8_t*>(
+ pIOContext->wsabuf.buf),
+ ioSize,
+ &pIOContext->from);
+ }
+ _ptrCbRWLock->ReleaseLockShared();
+ }
+ int32_t err = PostRecv(pIOContext);
+ if(err == 0)
+ {
+ // The PerIoContext was posted by a thread controlled by the socket
+ // implementation.
+ pIOContext->ioInitiatedByThreadWrapper = true;
+ }
+ OutstandingCallCompleted();
+ return;
+ } else {
+ // Unknown operation. Should not happen. Return pIOContext to avoid
+ // memory leak.
+ assert(false);
+ _mgr->PushIoContext(pIOContext);
+ }
+ OutstandingCallCompleted();
+ // Don't touch any members after OutstandingCallCompleted() since the socket
+ // may be deleted at this point.
+}
+
+int32_t UdpSocket2Windows::PostRecv()
+{
+ PerIoContext* pIoContext=_mgr->PopIoContext();
+ if(pIoContext == 0)
+ {
+ WEBRTC_TRACE(kTraceError, kTraceTransport, _id,
+ "UdpSocket2Windows(%d)::PostRecv(), pIoContext == 0",
+ (int32_t)this);
+ return -1;
+ }
+ // This function may have been called by thread not controlled by the socket
+ // implementation.
+ pIoContext->ioInitiatedByThreadWrapper = false;
+ return PostRecv(pIoContext);
+}
+
+int32_t UdpSocket2Windows::PostRecv(PerIoContext* pIoContext)
+{
+ if(pIoContext==0)
+ {
+ WEBRTC_TRACE(kTraceError, kTraceTransport, _id,
+ "UdpSocket2Windows(%d)::PostRecv(?), pIoContext==0",
+ (int32_t)this);
+ return -1;
+ }
+
+ DWORD numOfRecivedBytes = 0;
+ DWORD flags = 0;
+ pIoContext->wsabuf.buf = pIoContext->buffer;
+ pIoContext->wsabuf.len = sizeof(pIoContext->buffer);
+ pIoContext->fromLen = sizeof(SocketAddress);
+ pIoContext->ioOperation = OP_READ;
+ int32_t rxError = 0;
+ int32_t nRet = 0;
+ int32_t postingSucessfull = false;
+
+ if(!AquireSocket())
+ {
+ _mgr->PushIoContext(pIoContext);
+ return -1;
+ }
+
+ // Assume that the WSARecvFrom() call will be successfull to make sure that
+ // _outstandingCalls is positive. Roll back if WSARecvFrom() failed.
+ if(!NewOutstandingCall())
+ {
+ _mgr->PushIoContext(pIoContext);
+ ReleaseSocket();
+ return -1;
+ }
+ for(int32_t tries = 0; tries < 10; tries++)
+ {
+ nRet = WSARecvFrom(
+ _socket,
+ &(pIoContext->wsabuf),
+ 1,
+ &numOfRecivedBytes,
+ &flags,
+ reinterpret_cast<struct sockaddr*>(&(pIoContext->from)),
+ &(pIoContext->fromLen),
+ &(pIoContext->overlapped),
+ 0);
+
+ if( nRet == SOCKET_ERROR)
+ {
+ rxError = WSAGetLastError();
+ if(rxError != ERROR_IO_PENDING)
+ {
+ WEBRTC_TRACE(
+ kTraceError,
+ kTraceTransport,
+ _id,
+ "UdpSocket2Windows(%d)::PostRecv(?), WSAerror:%d when\
+ posting new recieve,trie:%d",
+ (int32_t)this,
+ rxError,
+ tries);
+ // Tell the OS that this is a good place to context switch if
+ // it wants to.
+ SleepMs(0);
+ }
+ }
+ if((rxError == ERROR_IO_PENDING) || (nRet == 0))
+ {
+ postingSucessfull = true;
+ break;
+ }
+ }
+ ReleaseSocket();
+
+ if(postingSucessfull)
+ {
+ return 0;
+ }
+ int32_t remainingReceiveBuffers = --_receiveBuffers;
+ if(remainingReceiveBuffers < 0)
+ {
+ assert(false);
+ }
+ int32_t error = _mgr->PushIoContext(pIoContext);
+ if(error)
+ {
+ WEBRTC_TRACE(
+ kTraceError,
+ kTraceTransport,
+ _id,
+ "UdpSocket2Windows(%d)::PostRecv(?), error:%d when PushIoContext",
+ (int32_t)this,
+ error);
+ }
+ // Roll back.
+ OutstandingCallCompleted();
+ return -1;
+}
+
+void UdpSocket2Windows::CloseBlocking()
+{
+ LINGER lingerStruct;
+
+ lingerStruct.l_onoff = 1;
+ lingerStruct.l_linger = 0;
+ if(AquireSocket())
+ {
+ setsockopt(_socket, SOL_SOCKET, SO_LINGER,
+ reinterpret_cast<const char*>(&lingerStruct),
+ sizeof(lingerStruct));
+ ReleaseSocket();
+ }
+
+ _wantsIncoming = false;
+ // Reclaims the socket and prevents it from being used again.
+ InvalidateSocket();
+ DisableNewOutstandingCalls();
+ WaitForOutstandingCalls();
+ delete this;
+}
+
+bool UdpSocket2Windows::SetQos(int32_t serviceType,
+ int32_t tokenRate,
+ int32_t bucketSize,
+ int32_t peekBandwith,
+ int32_t minPolicedSize,
+ int32_t maxSduSize,
+ const SocketAddress &stRemName,
+ int32_t overrideDSCP)
+{
+ if(_qos == false)
+ {
+ WEBRTC_TRACE(kTraceError, kTraceTransport, _id,
+ "UdpSocket2Windows::SetQos(), socket not capable of QOS");
+ return false;
+ }
+ if(overrideDSCP != 0)
+ {
+ FLOWSPEC f;
+ int32_t err = CreateFlowSpec(serviceType, tokenRate, bucketSize,
+ peekBandwith, minPolicedSize,
+ maxSduSize, &f);
+ if(err == -1)
+ {
+ return false;
+ }
+
+ SocketAddress socketName;
+ struct sockaddr_in* name =
+ reinterpret_cast<struct sockaddr_in*>(&socketName);
+ int nameLength = sizeof(SocketAddress);
+ if(AquireSocket())
+ {
+ getsockname(_socket, (struct sockaddr*)name, &nameLength);
+ ReleaseSocket();
+ }
+
+ if(serviceType == 0)
+ {
+ // Disable TOS byte setting.
+ return SetTrafficControl(0, -1, name, &f, &f) == 0;
+ }
+ return SetTrafficControl(overrideDSCP, -1, name, &f, &f) == 0;
+ }
+
+ QOS Qos;
+ DWORD BytesRet;
+ QOS_DESTADDR QosDestaddr;
+
+ memset (&Qos, QOS_NOT_SPECIFIED, sizeof(QOS));
+
+ Qos.SendingFlowspec.ServiceType = serviceType;
+ Qos.SendingFlowspec.TokenRate = tokenRate;
+ Qos.SendingFlowspec.TokenBucketSize = QOS_NOT_SPECIFIED;
+ Qos.SendingFlowspec.PeakBandwidth = QOS_NOT_SPECIFIED;
+ Qos.SendingFlowspec.DelayVariation = QOS_NOT_SPECIFIED;
+ Qos.SendingFlowspec.Latency = QOS_NOT_SPECIFIED;
+ Qos.SendingFlowspec.MinimumPolicedSize = QOS_NOT_SPECIFIED;
+ Qos.SendingFlowspec.MaxSduSize = QOS_NOT_SPECIFIED;
+
+ // Only ServiceType is needed for receiving.
+ Qos.ReceivingFlowspec.ServiceType = serviceType;
+ Qos.ReceivingFlowspec.TokenRate = QOS_NOT_SPECIFIED;
+ Qos.ReceivingFlowspec.TokenBucketSize = QOS_NOT_SPECIFIED;
+ Qos.ReceivingFlowspec.PeakBandwidth = QOS_NOT_SPECIFIED;
+ Qos.ReceivingFlowspec.Latency = QOS_NOT_SPECIFIED;
+ Qos.ReceivingFlowspec.DelayVariation = QOS_NOT_SPECIFIED;
+ Qos.ReceivingFlowspec.MinimumPolicedSize = QOS_NOT_SPECIFIED;
+ Qos.ReceivingFlowspec.MaxSduSize = QOS_NOT_SPECIFIED;
+
+ Qos.ProviderSpecific.len = 0;
+
+ Qos.ProviderSpecific.buf = NULL;
+
+ ZeroMemory((int8_t *)&QosDestaddr, sizeof(QosDestaddr));
+
+ OSVERSIONINFOEX osvie;
+ osvie.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
+ GetVersionEx((LPOSVERSIONINFO)&osvie);
+
+// Operating system Version number dwMajorVersion dwMinorVersion
+// Windows 7 6.1 6 1
+// Windows Server 2008 R2 6.1 6 1
+// Windows Server 2008 6.0 6 0
+// Windows Vista 6.0 6 0
+// Windows Server 2003 R2 5.2 5 2
+// Windows Server 2003 5.2 5 2
+// Windows XP 5.1 5 1
+// Windows 2000 5.0 5 0
+
+ // SERVICE_NO_QOS_SIGNALING and QOS_DESTADDR should not be used if version
+ // is 6.0 or greater.
+ if(osvie.dwMajorVersion >= 6)
+ {
+ Qos.SendingFlowspec.MinimumPolicedSize = QOS_NOT_SPECIFIED;
+ Qos.ReceivingFlowspec.ServiceType = serviceType;
+
+ } else {
+ Qos.SendingFlowspec.MinimumPolicedSize =
+ QOS_NOT_SPECIFIED | SERVICE_NO_QOS_SIGNALING;
+ Qos.ReceivingFlowspec.ServiceType =
+ serviceType | SERVICE_NO_QOS_SIGNALING;
+
+ QosDestaddr.ObjectHdr.ObjectType = QOS_OBJECT_DESTADDR;
+ QosDestaddr.ObjectHdr.ObjectLength = sizeof(QosDestaddr);
+ QosDestaddr.SocketAddress = (SOCKADDR *)&stRemName;
+ if (AF_INET6 == _iProtocol)
+ {
+ QosDestaddr.SocketAddressLength = sizeof(SocketAddressInVersion6);
+ } else {
+ QosDestaddr.SocketAddressLength = sizeof(SocketAddressIn);
+ }
+
+ Qos.ProviderSpecific.len = QosDestaddr.ObjectHdr.ObjectLength;
+ Qos.ProviderSpecific.buf = (char*)&QosDestaddr;
+ }
+
+ if(!AquireSocket()) {
+ return false;
+ }
+ // To set QoS with SIO_SET_QOS the socket must be locally bound first
+ // or the call will fail with error code 10022.
+ int32_t result = WSAIoctl(GetFd(), SIO_SET_QOS, &Qos, sizeof(QOS),
+ NULL, 0, &BytesRet, NULL,NULL);
+ ReleaseSocket();
+ if (result == SOCKET_ERROR)
+ {
+ WEBRTC_TRACE(kTraceError, kTraceTransport, _id,
+ "UdpSocket2Windows::SetQos() WSAerror : %d",
+ WSAGetLastError());
+ return false;
+ }
+ return true;
+}
+
+int32_t UdpSocket2Windows::SetTOS(int32_t serviceType)
+{
+ SocketAddress socketName;
+
+ struct sockaddr_in* name =
+ reinterpret_cast<struct sockaddr_in*>(&socketName);
+ int nameLength = sizeof(SocketAddress);
+ if(AquireSocket())
+ {
+ getsockname(_socket, (struct sockaddr*)name, &nameLength);
+ ReleaseSocket();
+ }
+
+ int32_t res = SetTrafficControl(serviceType, -1, name);
+ if (res == -1)
+ {
+ OSVERSIONINFO OsVersion;
+ OsVersion.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
+ GetVersionEx (&OsVersion);
+
+ if ((OsVersion.dwMajorVersion == 4)) // NT 4.0
+ {
+ if(SetSockopt(IPPROTO_IP,IP_TOS ,
+ (int8_t*)&serviceType, 4) != 0)
+ {
+ return -1;
+ }
+ }
+ }
+ return res;
+}
+
+int32_t UdpSocket2Windows::SetPCP(int32_t pcp)
+{
+ SocketAddress socketName;
+ struct sockaddr_in* name =
+ reinterpret_cast<struct sockaddr_in*>(&socketName);
+ int nameLength = sizeof(SocketAddress);
+ if(AquireSocket())
+ {
+ getsockname(_socket, (struct sockaddr*)name, &nameLength);
+ ReleaseSocket();
+ }
+ return SetTrafficControl(-1, pcp, name);
+}
+
+int32_t UdpSocket2Windows::SetTrafficControl(
+ int32_t dscp,
+ int32_t pcp,
+ const struct sockaddr_in* name,
+ FLOWSPEC* send, FLOWSPEC* recv)
+{
+ if (pcp == _pcp)
+ {
+ // No change.
+ pcp = -1;
+ }
+ if ((-1 == pcp) && (-1 == dscp))
+ {
+ return 0;
+ }
+ if (!_gtc)
+ {
+ _gtc = TrafficControlWindows::GetInstance(_id);
+ }
+ if (!_gtc)
+ {
+ return -1;
+ }
+ if(_filterHandle)
+ {
+ _gtc->TcDeleteFilter(_filterHandle);
+ _filterHandle = NULL;
+ }
+ if(_flowHandle)
+ {
+ _gtc->TcDeleteFlow(_flowHandle);
+ _flowHandle = NULL;
+ }
+ if(_clientHandle)
+ {
+ _gtc->TcDeregisterClient(_clientHandle);
+ _clientHandle = NULL;
+ }
+ if ((0 == dscp) && (-2 == _pcp) && (-1 == pcp))
+ {
+ // TODO (pwestin): why is this not done before deleting old filter and
+ // flow? This scenario should probably be documented in
+ // the function declaration.
+ return 0;
+ }
+
+ TCI_CLIENT_FUNC_LIST QoSFunctions;
+ QoSFunctions.ClAddFlowCompleteHandler = NULL;
+ QoSFunctions.ClDeleteFlowCompleteHandler = NULL;
+ QoSFunctions.ClModifyFlowCompleteHandler = NULL;
+ QoSFunctions.ClNotifyHandler = (TCI_NOTIFY_HANDLER)MyClNotifyHandler;
+ // Register the client with Traffic control interface.
+ HANDLE ClientHandle;
+ ULONG result = _gtc->TcRegisterClient(CURRENT_TCI_VERSION, NULL,
+ &QoSFunctions,&ClientHandle);
+ if(result != NO_ERROR)
+ {
+ // This is likely caused by the application not being run as
+ // administrator.
+ WEBRTC_TRACE(kTraceError, kTraceTransport, _id,
+ "TcRegisterClient returned %d", result);
+ return result;
+ }
+
+ // Find traffic control-enabled network interfaces that matches this
+ // socket's IP address.
+ ULONG BufferSize = 0;
+ result = _gtc->TcEnumerateInterfaces(ClientHandle, &BufferSize, NULL);
+
+ if(result != NO_ERROR && result != ERROR_INSUFFICIENT_BUFFER)
+ {
+ _gtc->TcDeregisterClient(ClientHandle);
+ return result;
+ }
+
+ if(result != ERROR_INSUFFICIENT_BUFFER)
+ {
+ // Empty buffer contains all control-enabled network interfaces. I.e.
+ // QoS is not enabled.
+ WEBRTC_TRACE(kTraceError, kTraceTransport, _id,
+ "QOS faild since QOS is not installed on the interface");
+
+ _gtc->TcDeregisterClient(ClientHandle);
+ return -1;
+ }
+
+ PTC_IFC_DESCRIPTOR pInterfaceBuffer =
+ (PTC_IFC_DESCRIPTOR)malloc(BufferSize);
+ if(pInterfaceBuffer == NULL)
+ {
+ WEBRTC_TRACE(kTraceError, kTraceTransport, _id,
+ "Out ot memory failure");
+ _gtc->TcDeregisterClient(ClientHandle);
+ return ERROR_NOT_ENOUGH_MEMORY;
+ }
+
+ result = _gtc->TcEnumerateInterfaces(ClientHandle, &BufferSize,
+ pInterfaceBuffer);
+
+ if(result != NO_ERROR)
+ {
+ WEBRTC_TRACE(
+ kTraceError,
+ kTraceTransport,
+ _id,
+ "Critical: error enumerating interfaces when passing in correct\
+ buffer size: %d", result);
+ _gtc->TcDeregisterClient(ClientHandle);
+ free(pInterfaceBuffer);
+ return result;
+ }
+
+ PTC_IFC_DESCRIPTOR oneinterface;
+ HANDLE ifcHandle, iFilterHandle, iflowHandle;
+ bool addrFound = false;
+ ULONG filterSourceAddress = ULONG_MAX;
+
+ // Find the interface corresponding to the local address.
+ for(oneinterface = pInterfaceBuffer;
+ oneinterface != (PTC_IFC_DESCRIPTOR)
+ (((int8_t*)pInterfaceBuffer) + BufferSize);
+ oneinterface = (PTC_IFC_DESCRIPTOR)
+ ((int8_t *)oneinterface + oneinterface->Length))
+ {
+
+ char interfaceName[500];
+ WideCharToMultiByte(CP_ACP, 0, oneinterface->pInterfaceName, -1,
+ interfaceName, sizeof(interfaceName), 0, 0 );
+
+ PNETWORK_ADDRESS_LIST addresses =
+ &(oneinterface->AddressListDesc.AddressList);
+ for(LONG i = 0; i < addresses->AddressCount ; i++)
+ {
+ // Only look at TCP/IP addresses.
+ if(addresses->Address[i].AddressType != NDIS_PROTOCOL_ID_TCP_IP)
+ {
+ continue;
+ }
+
+ NETWORK_ADDRESS_IP* pIpAddr =
+ (NETWORK_ADDRESS_IP*)&(addresses->Address[i].Address);
+ struct in_addr in;
+ in.S_un.S_addr = pIpAddr->in_addr;
+ if(pIpAddr->in_addr == name->sin_addr.S_un.S_addr)
+ {
+ filterSourceAddress = pIpAddr->in_addr;
+ addrFound = true;
+ }
+ }
+ if(!addrFound)
+ {
+ continue;
+ } else
+ {
+ break;
+ }
+ }
+ if(!addrFound)
+ {
+ WEBRTC_TRACE(kTraceError, kTraceTransport, _id,
+ "QOS faild since address is not found");
+ _gtc->TcDeregisterClient(ClientHandle);
+ free(pInterfaceBuffer);
+ return -1;
+ }
+ result = _gtc->TcOpenInterfaceW(oneinterface->pInterfaceName, ClientHandle,
+ NULL, &ifcHandle);
+ if(result != NO_ERROR)
+ {
+ WEBRTC_TRACE(kTraceError, kTraceTransport, _id,
+ "Error opening interface: %d", result);
+ _gtc->TcDeregisterClient(ClientHandle);
+ free(pInterfaceBuffer);
+ return result;
+ }
+
+ // Create flow if one doesn't exist.
+ if (!_flow)
+ {
+ bool addPCP = ((pcp >= 0) || ((-1 == pcp) && (_pcp >= 0)));
+ int allocSize = sizeof(TC_GEN_FLOW) + sizeof(QOS_DS_CLASS) +
+ (addPCP ? sizeof(QOS_TRAFFIC_CLASS) : 0);
+ _flow = (PTC_GEN_FLOW)malloc(allocSize);
+
+ _flow->SendingFlowspec.DelayVariation = QOS_NOT_SPECIFIED;
+ _flow->SendingFlowspec.Latency = QOS_NOT_SPECIFIED;
+ _flow->SendingFlowspec.MaxSduSize = QOS_NOT_SPECIFIED;
+ _flow->SendingFlowspec.MinimumPolicedSize = QOS_NOT_SPECIFIED;
+ _flow->SendingFlowspec.PeakBandwidth = QOS_NOT_SPECIFIED;
+ _flow->SendingFlowspec.ServiceType = SERVICETYPE_BESTEFFORT;
+ _flow->SendingFlowspec.TokenBucketSize = QOS_NOT_SPECIFIED;
+ _flow->SendingFlowspec.TokenRate = QOS_NOT_SPECIFIED;
+
+ _flow->ReceivingFlowspec.DelayVariation = QOS_NOT_SPECIFIED;
+ _flow->ReceivingFlowspec.Latency = QOS_NOT_SPECIFIED;
+ _flow->ReceivingFlowspec.MaxSduSize = QOS_NOT_SPECIFIED;
+ _flow->ReceivingFlowspec.MinimumPolicedSize = QOS_NOT_SPECIFIED;
+ _flow->ReceivingFlowspec.PeakBandwidth = QOS_NOT_SPECIFIED;
+ _flow->ReceivingFlowspec.ServiceType = SERVICETYPE_BESTEFFORT;
+ _flow->ReceivingFlowspec.TokenBucketSize = QOS_NOT_SPECIFIED;
+ _flow->ReceivingFlowspec.TokenRate = QOS_NOT_SPECIFIED;
+
+ QOS_DS_CLASS* dsClass = (QOS_DS_CLASS*)_flow->TcObjects;
+ dsClass->DSField = 0;
+ dsClass->ObjectHdr.ObjectType = QOS_OBJECT_DS_CLASS;
+ dsClass->ObjectHdr.ObjectLength = sizeof(QOS_DS_CLASS);
+
+ if (addPCP)
+ {
+ QOS_TRAFFIC_CLASS* trafficClass = (QOS_TRAFFIC_CLASS*)(dsClass + 1);
+ trafficClass->TrafficClass = 0;
+ trafficClass->ObjectHdr.ObjectType = QOS_OBJECT_TRAFFIC_CLASS;
+ trafficClass->ObjectHdr.ObjectLength = sizeof(QOS_TRAFFIC_CLASS);
+ }
+
+ _flow->TcObjectsLength = sizeof(QOS_DS_CLASS) +
+ (addPCP ? sizeof(QOS_TRAFFIC_CLASS) : 0);
+ } else if (-1 != pcp) {
+ // Reallocate memory since pcp has changed.
+ PTC_GEN_FLOW oldFlow = _flow;
+ bool addPCP = (pcp >= 0);
+ int allocSize = sizeof(TC_GEN_FLOW) + sizeof(QOS_DS_CLASS) +
+ (addPCP ? sizeof(QOS_TRAFFIC_CLASS) : 0);
+ _flow = (PTC_GEN_FLOW)malloc(allocSize);
+
+ // Copy old flow.
+ _flow->ReceivingFlowspec = oldFlow->ReceivingFlowspec;
+ _flow->SendingFlowspec = oldFlow->SendingFlowspec;
+ // The DS info is always the first object.
+ QOS_DS_CLASS* dsClass = (QOS_DS_CLASS*)_flow->TcObjects;
+ QOS_DS_CLASS* oldDsClass = (QOS_DS_CLASS*)oldFlow->TcObjects;
+ dsClass->DSField = oldDsClass->DSField;
+ dsClass->ObjectHdr.ObjectType = oldDsClass->ObjectHdr.ObjectType;
+ dsClass->ObjectHdr.ObjectLength = oldDsClass->ObjectHdr.ObjectLength;
+
+ if (addPCP)
+ {
+ QOS_TRAFFIC_CLASS* trafficClass = (QOS_TRAFFIC_CLASS*)(dsClass + 1);
+ trafficClass->TrafficClass = 0;
+ trafficClass->ObjectHdr.ObjectType = QOS_OBJECT_TRAFFIC_CLASS;
+ trafficClass->ObjectHdr.ObjectLength = sizeof(QOS_TRAFFIC_CLASS);
+ }
+
+ _flow->TcObjectsLength = sizeof(QOS_DS_CLASS) +
+ (addPCP ? sizeof(QOS_TRAFFIC_CLASS) : 0);
+ free(oldFlow);
+ }
+
+ // Setup send and receive flow and DS object.
+ if (dscp >= 0)
+ {
+ if (!send || (0 == dscp))
+ {
+ _flow->SendingFlowspec.DelayVariation = QOS_NOT_SPECIFIED;
+ _flow->SendingFlowspec.Latency = QOS_NOT_SPECIFIED;
+ _flow->SendingFlowspec.MaxSduSize = QOS_NOT_SPECIFIED;
+ _flow->SendingFlowspec.MinimumPolicedSize = QOS_NOT_SPECIFIED;
+ _flow->SendingFlowspec.PeakBandwidth =
+ (0 == dscp ? QOS_NOT_SPECIFIED : POSITIVE_INFINITY_RATE);
+ _flow->SendingFlowspec.ServiceType = SERVICETYPE_BESTEFFORT;
+ _flow->SendingFlowspec.TokenBucketSize = QOS_NOT_SPECIFIED;
+ // 128000 * 10 is 10mbit/s.
+ _flow->SendingFlowspec.TokenRate =
+ (0 == dscp ? QOS_NOT_SPECIFIED : 128000 * 10);
+ }
+ else
+ {
+ _flow->SendingFlowspec.DelayVariation = send->DelayVariation;
+ _flow->SendingFlowspec.Latency = send->Latency;
+ _flow->SendingFlowspec.MaxSduSize = send->MaxSduSize;
+ _flow->SendingFlowspec.MinimumPolicedSize =
+ send->MinimumPolicedSize;
+ _flow->SendingFlowspec.PeakBandwidth = send->PeakBandwidth;
+ _flow->SendingFlowspec.PeakBandwidth = POSITIVE_INFINITY_RATE;
+ _flow->SendingFlowspec.ServiceType = send->ServiceType;
+ _flow->SendingFlowspec.TokenBucketSize = send->TokenBucketSize;
+ _flow->SendingFlowspec.TokenRate = send->TokenRate;
+ }
+
+ if (!recv || (0 == dscp))
+ {
+ _flow->ReceivingFlowspec.DelayVariation =
+ _flow->SendingFlowspec.DelayVariation;
+ _flow->ReceivingFlowspec.Latency = _flow->SendingFlowspec.Latency;
+ _flow->ReceivingFlowspec.MaxSduSize =
+ _flow->SendingFlowspec.MaxSduSize;
+ _flow->ReceivingFlowspec.MinimumPolicedSize =
+ _flow->SendingFlowspec.MinimumPolicedSize;
+ _flow->ReceivingFlowspec.PeakBandwidth = QOS_NOT_SPECIFIED;
+ _flow->ReceivingFlowspec.ServiceType =
+ 0 == dscp ? SERVICETYPE_BESTEFFORT : SERVICETYPE_CONTROLLEDLOAD;
+ _flow->ReceivingFlowspec.TokenBucketSize =
+ _flow->SendingFlowspec.TokenBucketSize;
+ _flow->ReceivingFlowspec.TokenRate =
+ _flow->SendingFlowspec.TokenRate;
+ } else {
+ _flow->ReceivingFlowspec.DelayVariation = recv->DelayVariation;
+ _flow->ReceivingFlowspec.Latency = recv->Latency;
+ _flow->ReceivingFlowspec.MaxSduSize = recv->MaxSduSize;
+ _flow->ReceivingFlowspec.MinimumPolicedSize =
+ recv->MinimumPolicedSize;
+ _flow->ReceivingFlowspec.PeakBandwidth = recv->PeakBandwidth;
+ _flow->ReceivingFlowspec.ServiceType = recv->ServiceType;
+ _flow->ReceivingFlowspec.TokenBucketSize = recv->TokenBucketSize;
+ _flow->ReceivingFlowspec.TokenRate = QOS_NOT_SPECIFIED;
+ }
+
+ // Setup DS (for DSCP value).
+ // DS is always the first object.
+ QOS_DS_CLASS* dsClass = (QOS_DS_CLASS*)_flow->TcObjects;
+ dsClass->DSField = dscp;
+ }
+
+ // Setup PCP (802.1p priority in 802.1Q/VLAN tagging)
+ if (pcp >= 0)
+ {
+ // DS is always first object.
+ QOS_DS_CLASS* dsClass = (QOS_DS_CLASS*)_flow->TcObjects;
+ QOS_TRAFFIC_CLASS* trafficClass = (QOS_TRAFFIC_CLASS*)(dsClass + 1);
+ trafficClass->TrafficClass = pcp;
+ }
+
+ result = _gtc->TcAddFlow(ifcHandle, NULL, 0, _flow, &iflowHandle);
+ if(result != NO_ERROR)
+ {
+ _gtc->TcCloseInterface(ifcHandle);
+ _gtc->TcDeregisterClient(ClientHandle);
+ free(pInterfaceBuffer);
+ return -1;
+ }
+
+ IP_PATTERN filterPattern, mask;
+
+ ZeroMemory((int8_t*)&filterPattern, sizeof(IP_PATTERN));
+ ZeroMemory((int8_t*)&mask, sizeof(IP_PATTERN));
+
+ filterPattern.ProtocolId = IPPROTO_UDP;
+ // "name" fields already in network order.
+ filterPattern.S_un.S_un_ports.s_srcport = name->sin_port;
+ filterPattern.SrcAddr = filterSourceAddress;
+
+ // Unsigned max of a type corresponds to a bitmask with all bits set to 1.
+ // I.e. the filter should allow all ProtocolIds, any source port and any
+ // IP address
+ mask.ProtocolId = UCHAR_MAX;
+ mask.S_un.S_un_ports.s_srcport = USHRT_MAX;
+ mask.SrcAddr = ULONG_MAX;
+
+ TC_GEN_FILTER filter;
+
+ filter.AddressType = NDIS_PROTOCOL_ID_TCP_IP;
+ filter.Mask = (LPVOID)&mask;
+ filter.Pattern = (LPVOID)&filterPattern;
+ filter.PatternSize = sizeof(IP_PATTERN);
+
+ result = _gtc->TcAddFilter(iflowHandle, &filter, &iFilterHandle);
+ if(result != NO_ERROR)
+ {
+ _gtc->TcDeleteFlow(iflowHandle);
+ _gtc->TcCloseInterface(ifcHandle);
+ _gtc->TcDeregisterClient(ClientHandle);
+ free(pInterfaceBuffer);
+ return result;
+ }
+
+ _flowHandle = iflowHandle;
+ _filterHandle = iFilterHandle;
+ _clientHandle = ClientHandle;
+ if (-1 != pcp)
+ {
+ _pcp = pcp;
+ }
+
+ _gtc->TcCloseInterface(ifcHandle);
+ free(pInterfaceBuffer);
+
+ return 0;
+}
+
+int32_t UdpSocket2Windows::CreateFlowSpec(int32_t serviceType,
+ int32_t tokenRate,
+ int32_t bucketSize,
+ int32_t peekBandwith,
+ int32_t minPolicedSize,
+ int32_t maxSduSize,
+ FLOWSPEC* f)
+{
+ if (!f)
+ {
+ return -1;
+ }
+
+ f->ServiceType = serviceType;
+ f->TokenRate = tokenRate;
+ f->TokenBucketSize = QOS_NOT_SPECIFIED;
+ f->PeakBandwidth = QOS_NOT_SPECIFIED;
+ f->DelayVariation = QOS_NOT_SPECIFIED;
+ f->Latency = QOS_NOT_SPECIFIED;
+ f->MaxSduSize = QOS_NOT_SPECIFIED;
+ f->MinimumPolicedSize = QOS_NOT_SPECIFIED;
+ return 0;
+}
+
+bool UdpSocket2Windows::NewOutstandingCall()
+{
+ assert(!_outstandingCallsDisabled);
+
+ ++_outstandingCalls;
+ return true;
+}
+
+void UdpSocket2Windows::OutstandingCallCompleted()
+{
+ _ptrDestRWLock->AcquireLockShared();
+ ++_outstandingCallComplete;
+ if((--_outstandingCalls == 0) && _outstandingCallsDisabled)
+ {
+ // When there are no outstanding calls and new outstanding calls are
+ // disabled it is time to terminate.
+ _terminate = true;
+ }
+ _ptrDestRWLock->ReleaseLockShared();
+
+ if((--_outstandingCallComplete == 0) &&
+ (_terminate))
+ {
+ // Only one thread will enter here. The thread with the last outstanding
+ // call.
+ CriticalSectionScoped cs(_ptrDeleteCrit);
+ _safeTodelete = true;
+ _ptrDeleteCond->Wake();
+ }
+}
+
+void UdpSocket2Windows::DisableNewOutstandingCalls()
+{
+ _ptrDestRWLock->AcquireLockExclusive();
+ if(_outstandingCallsDisabled)
+ {
+ // Outstandning calls are already disabled.
+ _ptrDestRWLock->ReleaseLockExclusive();
+ return;
+ }
+ _outstandingCallsDisabled = true;
+ const bool noOutstandingCalls = (_outstandingCalls.Value() == 0);
+ _ptrDestRWLock->ReleaseLockExclusive();
+
+ RemoveSocketFromManager();
+
+ if(noOutstandingCalls)
+ {
+ CriticalSectionScoped cs(_ptrDeleteCrit);
+ _safeTodelete = true;
+ _ptrDeleteCond->Wake();
+ }
+}
+
+void UdpSocket2Windows::WaitForOutstandingCalls()
+{
+ CriticalSectionScoped cs(_ptrDeleteCrit);
+ while(!_safeTodelete)
+ {
+ _ptrDeleteCond->SleepCS(*_ptrDeleteCrit);
+ }
+}
+
+void UdpSocket2Windows::RemoveSocketFromManager()
+{
+ // New outstanding calls should be disabled at this point.
+ assert(_outstandingCallsDisabled);
+
+ if(_addedToMgr)
+ {
+ WEBRTC_TRACE(kTraceDebug, kTraceTransport, _id,
+ "calling UdpSocketManager::RemoveSocket()");
+ if(_mgr->RemoveSocket(this))
+ {
+ _addedToMgr=false;
+ }
+ }
+}
+
+bool UdpSocket2Windows::AquireSocket()
+{
+ _ptrSocketRWLock->AcquireLockShared();
+ const bool returnValue = _socket != INVALID_SOCKET;
+ if(!returnValue)
+ {
+ _ptrSocketRWLock->ReleaseLockShared();
+ }
+ return returnValue;
+}
+
+void UdpSocket2Windows::ReleaseSocket()
+{
+ _ptrSocketRWLock->ReleaseLockShared();
+}
+
+bool UdpSocket2Windows::InvalidateSocket()
+{
+ _ptrSocketRWLock->AcquireLockExclusive();
+ if(_socket == INVALID_SOCKET)
+ {
+ _ptrSocketRWLock->ReleaseLockExclusive();
+ return true;
+ }
+ // Give the socket back to the system. All socket calls will fail from now
+ // on.
+ if(closesocket(_socket) == SOCKET_ERROR)
+ {
+ WEBRTC_TRACE(kTraceError, kTraceTransport, _id,
+ "UdpSocket2Windows(%d)::InvalidateSocket() WSAerror: %d",
+ (int32_t)this, WSAGetLastError());
+ }
+ _socket = INVALID_SOCKET;
+ _ptrSocketRWLock->ReleaseLockExclusive();
+ return true;
+}
+
+} // namespace test
+} // namespace webrtc
diff --git a/webrtc/test/channel_transport/udp_socket2_win.h b/webrtc/test/channel_transport/udp_socket2_win.h
new file mode 100644
index 0000000000..ea37ac47c5
--- /dev/null
+++ b/webrtc/test/channel_transport/udp_socket2_win.h
@@ -0,0 +1,179 @@
+/*
+ * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#ifndef WEBRTC_TEST_CHANNEL_TRANSPORT_UDP_SOCKET2_WINDOWS_H_
+#define WEBRTC_TEST_CHANNEL_TRANSPORT_UDP_SOCKET2_WINDOWS_H_
+
+// Disable deprication warning from traffic.h
+#pragma warning(disable : 4995)
+
+// Don't change include order for these header files.
+#include <Winsock2.h>
+#include <Ntddndis.h>
+#include <traffic.h>
+
+#include "webrtc/system_wrappers/include/atomic32.h"
+#include "webrtc/system_wrappers/include/condition_variable_wrapper.h"
+#include "webrtc/system_wrappers/include/critical_section_wrapper.h"
+#include "webrtc/system_wrappers/include/event_wrapper.h"
+#include "webrtc/system_wrappers/include/rw_lock_wrapper.h"
+#include "webrtc/system_wrappers/include/trace.h"
+#include "webrtc/test/channel_transport/udp_socket2_manager_win.h"
+#include "webrtc/test/channel_transport/udp_socket_wrapper.h"
+
+namespace webrtc {
+namespace test {
+
+class UdpSocket2ManagerWindows;
+class TrafficControlWindows;
+struct PerIoContext;
+
+class UdpSocket2Windows : public UdpSocketWrapper
+{
+public:
+ UdpSocket2Windows(const int32_t id, UdpSocketManager* mgr,
+ bool ipV6Enable = false, bool disableGQOS = false);
+ virtual ~UdpSocket2Windows();
+
+ bool ValidHandle() override;
+
+ bool SetCallback(CallbackObj, IncomingSocketCallback) override;
+
+ bool Bind(const SocketAddress& name) override;
+ bool SetSockopt(int32_t level,
+ int32_t optname,
+ const int8_t* optval,
+ int32_t optlen) override;
+
+ bool StartReceiving(const uint32_t receiveBuffers) override;
+ inline bool StartReceiving() override { return StartReceiving(8); }
+ bool StopReceiving() override;
+
+ int32_t SendTo(const int8_t* buf,
+ size_t len,
+ const SocketAddress& to) override;
+
+ void CloseBlocking() override;
+
+ SOCKET GetFd() { return _socket;}
+
+ bool SetQos(int32_t serviceType,
+ int32_t tokenRate,
+ int32_t bucketSize,
+ int32_t peekBandwith,
+ int32_t minPolicedSize,
+ int32_t maxSduSize,
+ const SocketAddress& stRemName,
+ int32_t overrideDSCP = 0) override;
+
+ int32_t SetTOS(const int32_t serviceType) override;
+ int32_t SetPCP(const int32_t pcp) override;
+
+ uint32_t ReceiveBuffers() override { return _receiveBuffers.Value(); }
+
+protected:
+ void IOCompleted(PerIoContext* pIOContext, uint32_t ioSize, uint32_t error);
+
+ int32_t PostRecv();
+ // Use pIoContext to post a new WSARecvFrom(..).
+ int32_t PostRecv(PerIoContext* pIoContext);
+
+private:
+ friend class UdpSocket2WorkerWindows;
+
+ // Set traffic control (TC) flow adding it the interface that matches this
+ // sockets address.
+ // A filter is created and added to the flow.
+ // The flow consists of:
+ // (1) QoS send and receive information (flow specifications).
+ // (2) A DS object (for specifying exact DSCP value).
+ // (3) Possibly a traffic object (for specifying exact 802.1p priority (PCP)
+ // value).
+ //
+ // dscp values:
+ // -1 don't change the current dscp value.
+ // 0 don't add any flow to TC, unless pcp is specified.
+ // 1-63 Add a flow to TC with the specified dscp value.
+ // pcp values:
+ // -2 Don't add pcp info to the flow, (3) will not be added.
+ // -1 Don't change the current value.
+ // 0-7 Add pcp info to the flow with the specified value,
+ // (3) will be added.
+ //
+ // If both dscp and pcp are -1 no flow will be created or added to TC.
+ // If dscp is 0 and pcp is 0-7 (1), (2) and (3) will be created.
+ // Note: input parameter values are assumed to be in valid range, checks
+ // must be done by caller.
+ int32_t SetTrafficControl(int32_t dscp, int32_t pcp,
+ const struct sockaddr_in* name,
+ FLOWSPEC* send = NULL,
+ FLOWSPEC* recv = NULL);
+ int32_t CreateFlowSpec(int32_t serviceType,
+ int32_t tokenRate,
+ int32_t bucketSize,
+ int32_t peekBandwith,
+ int32_t minPolicedSize,
+ int32_t maxSduSize, FLOWSPEC *f);
+
+ int32_t _id;
+ RWLockWrapper* _ptrCbRWLock;
+ IncomingSocketCallback _incomingCb;
+ CallbackObj _obj;
+ bool _qos;
+
+ SocketAddress _remoteAddr;
+ SOCKET _socket;
+ int32_t _iProtocol;
+ UdpSocket2ManagerWindows* _mgr;
+
+ CriticalSectionWrapper* _pCrit;
+ Atomic32 _outstandingCalls;
+ Atomic32 _outstandingCallComplete;
+ volatile bool _terminate;
+ volatile bool _addedToMgr;
+
+ CriticalSectionWrapper* _ptrDeleteCrit;
+ ConditionVariableWrapper* _ptrDeleteCond;
+ bool _safeTodelete;
+
+ RWLockWrapper* _ptrDestRWLock;
+ bool _outstandingCallsDisabled;
+ bool NewOutstandingCall();
+ void OutstandingCallCompleted();
+ void DisableNewOutstandingCalls();
+ void WaitForOutstandingCalls();
+
+ void RemoveSocketFromManager();
+
+ // RWLockWrapper is used as a reference counter for the socket. Write lock
+ // is used for creating and deleting socket. Read lock is used for
+ // accessing the socket.
+ RWLockWrapper* _ptrSocketRWLock;
+ bool AquireSocket();
+ void ReleaseSocket();
+ bool InvalidateSocket();
+
+ // Traffic control handles and structure pointers.
+ HANDLE _clientHandle;
+ HANDLE _flowHandle;
+ HANDLE _filterHandle;
+ PTC_GEN_FLOW _flow;
+ // TrafficControlWindows implements TOS and PCP.
+ TrafficControlWindows* _gtc;
+ // Holds the current pcp value. Can be -2 or 0 - 7.
+ int _pcp;
+
+ Atomic32 _receiveBuffers;
+};
+
+} // namespace test
+} // namespace webrtc
+
+#endif // WEBRTC_TEST_CHANNEL_TRANSPORT_UDP_SOCKET2_WINDOWS_H_
diff --git a/webrtc/test/channel_transport/udp_socket_manager_posix.cc b/webrtc/test/channel_transport/udp_socket_manager_posix.cc
new file mode 100644
index 0000000000..145efcbc58
--- /dev/null
+++ b/webrtc/test/channel_transport/udp_socket_manager_posix.cc
@@ -0,0 +1,403 @@
+/*
+ * Copyright (c) 2011 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/channel_transport/udp_socket_manager_posix.h"
+
+#include <stdio.h>
+#include <strings.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <time.h>
+#include <unistd.h>
+
+#include "webrtc/system_wrappers/include/sleep.h"
+#include "webrtc/system_wrappers/include/trace.h"
+#include "webrtc/test/channel_transport/udp_socket_posix.h"
+
+namespace webrtc {
+namespace test {
+
+UdpSocketManagerPosix::UdpSocketManagerPosix()
+ : UdpSocketManager(),
+ _id(-1),
+ _critSect(CriticalSectionWrapper::CreateCriticalSection()),
+ _numberOfSocketMgr(-1),
+ _incSocketMgrNextTime(0),
+ _nextSocketMgrToAssign(0),
+ _socketMgr()
+{
+}
+
+bool UdpSocketManagerPosix::Init(int32_t id, uint8_t& numOfWorkThreads) {
+ CriticalSectionScoped cs(_critSect);
+ if ((_id != -1) || (_numOfWorkThreads != 0)) {
+ assert(_id != -1);
+ assert(_numOfWorkThreads != 0);
+ return false;
+ }
+
+ _id = id;
+ _numberOfSocketMgr = numOfWorkThreads;
+ _numOfWorkThreads = numOfWorkThreads;
+
+ if(MAX_NUMBER_OF_SOCKET_MANAGERS_LINUX < _numberOfSocketMgr)
+ {
+ _numberOfSocketMgr = MAX_NUMBER_OF_SOCKET_MANAGERS_LINUX;
+ }
+ for(int i = 0;i < _numberOfSocketMgr; i++)
+ {
+ _socketMgr[i] = new UdpSocketManagerPosixImpl();
+ }
+ return true;
+}
+
+
+UdpSocketManagerPosix::~UdpSocketManagerPosix()
+{
+ Stop();
+ WEBRTC_TRACE(kTraceDebug, kTraceTransport, _id,
+ "UdpSocketManagerPosix(%d)::UdpSocketManagerPosix()",
+ _numberOfSocketMgr);
+
+ for(int i = 0;i < _numberOfSocketMgr; i++)
+ {
+ delete _socketMgr[i];
+ }
+ delete _critSect;
+}
+
+bool UdpSocketManagerPosix::Start()
+{
+ WEBRTC_TRACE(kTraceDebug, kTraceTransport, _id,
+ "UdpSocketManagerPosix(%d)::Start()",
+ _numberOfSocketMgr);
+
+ _critSect->Enter();
+ bool retVal = true;
+ for(int i = 0;i < _numberOfSocketMgr && retVal; i++)
+ {
+ retVal = _socketMgr[i]->Start();
+ }
+ if(!retVal)
+ {
+ WEBRTC_TRACE(
+ kTraceError,
+ kTraceTransport,
+ _id,
+ "UdpSocketManagerPosix(%d)::Start() error starting socket managers",
+ _numberOfSocketMgr);
+ }
+ _critSect->Leave();
+ return retVal;
+}
+
+bool UdpSocketManagerPosix::Stop()
+{
+ WEBRTC_TRACE(kTraceDebug, kTraceTransport, _id,
+ "UdpSocketManagerPosix(%d)::Stop()",_numberOfSocketMgr);
+
+ _critSect->Enter();
+ bool retVal = true;
+ for(int i = 0; i < _numberOfSocketMgr && retVal; i++)
+ {
+ retVal = _socketMgr[i]->Stop();
+ }
+ if(!retVal)
+ {
+ WEBRTC_TRACE(
+ kTraceError,
+ kTraceTransport,
+ _id,
+ "UdpSocketManagerPosix(%d)::Stop() there are still active socket "
+ "managers",
+ _numberOfSocketMgr);
+ }
+ _critSect->Leave();
+ return retVal;
+}
+
+bool UdpSocketManagerPosix::AddSocket(UdpSocketWrapper* s)
+{
+ WEBRTC_TRACE(kTraceDebug, kTraceTransport, _id,
+ "UdpSocketManagerPosix(%d)::AddSocket()",_numberOfSocketMgr);
+
+ _critSect->Enter();
+ bool retVal = _socketMgr[_nextSocketMgrToAssign]->AddSocket(s);
+ if(!retVal)
+ {
+ WEBRTC_TRACE(
+ kTraceError,
+ kTraceTransport,
+ _id,
+ "UdpSocketManagerPosix(%d)::AddSocket() failed to add socket to\
+ manager",
+ _numberOfSocketMgr);
+ }
+
+ // Distribute sockets on UdpSocketManagerPosixImpls in a round-robin
+ // fashion.
+ if(_incSocketMgrNextTime == 0)
+ {
+ _incSocketMgrNextTime++;
+ } else {
+ _incSocketMgrNextTime = 0;
+ _nextSocketMgrToAssign++;
+ if(_nextSocketMgrToAssign >= _numberOfSocketMgr)
+ {
+ _nextSocketMgrToAssign = 0;
+ }
+ }
+ _critSect->Leave();
+ return retVal;
+}
+
+bool UdpSocketManagerPosix::RemoveSocket(UdpSocketWrapper* s)
+{
+ WEBRTC_TRACE(kTraceDebug, kTraceTransport, _id,
+ "UdpSocketManagerPosix(%d)::RemoveSocket()",
+ _numberOfSocketMgr);
+
+ _critSect->Enter();
+ bool retVal = false;
+ for(int i = 0;i < _numberOfSocketMgr && (retVal == false); i++)
+ {
+ retVal = _socketMgr[i]->RemoveSocket(s);
+ }
+ if(!retVal)
+ {
+ WEBRTC_TRACE(
+ kTraceError,
+ kTraceTransport,
+ _id,
+ "UdpSocketManagerPosix(%d)::RemoveSocket() failed to remove socket\
+ from manager",
+ _numberOfSocketMgr);
+ }
+ _critSect->Leave();
+ return retVal;
+}
+
+
+UdpSocketManagerPosixImpl::UdpSocketManagerPosixImpl()
+{
+ _critSectList = CriticalSectionWrapper::CreateCriticalSection();
+ _thread = ThreadWrapper::CreateThread(UdpSocketManagerPosixImpl::Run, this,
+ "UdpSocketManagerPosixImplThread");
+ FD_ZERO(&_readFds);
+ WEBRTC_TRACE(kTraceMemory, kTraceTransport, -1,
+ "UdpSocketManagerPosix created");
+}
+
+UdpSocketManagerPosixImpl::~UdpSocketManagerPosixImpl()
+{
+ if (_critSectList != NULL)
+ {
+ UpdateSocketMap();
+
+ _critSectList->Enter();
+ for (std::map<SOCKET, UdpSocketPosix*>::iterator it =
+ _socketMap.begin();
+ it != _socketMap.end();
+ ++it) {
+ delete it->second;
+ }
+ _socketMap.clear();
+ _critSectList->Leave();
+
+ delete _critSectList;
+ }
+
+ WEBRTC_TRACE(kTraceMemory, kTraceTransport, -1,
+ "UdpSocketManagerPosix deleted");
+}
+
+bool UdpSocketManagerPosixImpl::Start()
+{
+ if (!_thread)
+ {
+ return false;
+ }
+
+ WEBRTC_TRACE(kTraceStateInfo, kTraceTransport, -1,
+ "Start UdpSocketManagerPosix");
+ if (!_thread->Start())
+ return false;
+ _thread->SetPriority(kRealtimePriority);
+ return true;
+}
+
+bool UdpSocketManagerPosixImpl::Stop()
+{
+ if (!_thread)
+ {
+ return true;
+ }
+
+ WEBRTC_TRACE(kTraceStateInfo, kTraceTransport, -1,
+ "Stop UdpSocketManagerPosix");
+ return _thread->Stop();
+}
+
+bool UdpSocketManagerPosixImpl::Process()
+{
+ bool doSelect = false;
+ // Timeout = 1 second.
+ struct timeval timeout;
+ timeout.tv_sec = 0;
+ timeout.tv_usec = 10000;
+
+ FD_ZERO(&_readFds);
+
+ UpdateSocketMap();
+
+ SOCKET maxFd = 0;
+ for (std::map<SOCKET, UdpSocketPosix*>::iterator it = _socketMap.begin();
+ it != _socketMap.end();
+ ++it) {
+ doSelect = true;
+ if (it->first > maxFd)
+ maxFd = it->first;
+ FD_SET(it->first, &_readFds);
+ }
+
+ int num = 0;
+ if (doSelect)
+ {
+ num = select(maxFd+1, &_readFds, NULL, NULL, &timeout);
+
+ if (num == SOCKET_ERROR)
+ {
+ // Timeout = 10 ms.
+ SleepMs(10);
+ return true;
+ }
+ }else
+ {
+ // Timeout = 10 ms.
+ SleepMs(10);
+ return true;
+ }
+
+ for (std::map<SOCKET, UdpSocketPosix*>::iterator it = _socketMap.begin();
+ it != _socketMap.end();
+ ++it) {
+ if (FD_ISSET(it->first, &_readFds)) {
+ it->second->HasIncoming();
+ --num;
+ }
+ }
+
+ return true;
+}
+
+bool UdpSocketManagerPosixImpl::Run(void* obj)
+{
+ UdpSocketManagerPosixImpl* mgr =
+ static_cast<UdpSocketManagerPosixImpl*>(obj);
+ return mgr->Process();
+}
+
+bool UdpSocketManagerPosixImpl::AddSocket(UdpSocketWrapper* s)
+{
+ UdpSocketPosix* sl = static_cast<UdpSocketPosix*>(s);
+ if(sl->GetFd() == INVALID_SOCKET || !(sl->GetFd() < FD_SETSIZE))
+ {
+ return false;
+ }
+ _critSectList->Enter();
+ _addList.push_back(s);
+ _critSectList->Leave();
+ return true;
+}
+
+bool UdpSocketManagerPosixImpl::RemoveSocket(UdpSocketWrapper* s)
+{
+ // Put in remove list if this is the correct UdpSocketManagerPosixImpl.
+ _critSectList->Enter();
+
+ // If the socket is in the add list it's safe to remove and delete it.
+ for (SocketList::iterator iter = _addList.begin();
+ iter != _addList.end(); ++iter) {
+ UdpSocketPosix* addSocket = static_cast<UdpSocketPosix*>(*iter);
+ unsigned int addFD = addSocket->GetFd();
+ unsigned int removeFD = static_cast<UdpSocketPosix*>(s)->GetFd();
+ if(removeFD == addFD)
+ {
+ _removeList.push_back(removeFD);
+ _critSectList->Leave();
+ return true;
+ }
+ }
+
+ // Checking the socket map is safe since all Erase and Insert calls to this
+ // map are also protected by _critSectList.
+ if (_socketMap.find(static_cast<UdpSocketPosix*>(s)->GetFd()) !=
+ _socketMap.end()) {
+ _removeList.push_back(static_cast<UdpSocketPosix*>(s)->GetFd());
+ _critSectList->Leave();
+ return true;
+ }
+ _critSectList->Leave();
+ return false;
+}
+
+void UdpSocketManagerPosixImpl::UpdateSocketMap()
+{
+ // Remove items in remove list.
+ _critSectList->Enter();
+ for (FdList::iterator iter = _removeList.begin();
+ iter != _removeList.end(); ++iter) {
+ UdpSocketPosix* deleteSocket = NULL;
+ SOCKET removeFD = *iter;
+
+ // If the socket is in the add list it hasn't been added to the socket
+ // map yet. Just remove the socket from the add list.
+ for (SocketList::iterator iter = _addList.begin();
+ iter != _addList.end(); ++iter) {
+ UdpSocketPosix* addSocket = static_cast<UdpSocketPosix*>(*iter);
+ SOCKET addFD = addSocket->GetFd();
+ if(removeFD == addFD)
+ {
+ deleteSocket = addSocket;
+ _addList.erase(iter);
+ break;
+ }
+ }
+
+ // Find and remove socket from _socketMap.
+ std::map<SOCKET, UdpSocketPosix*>::iterator it =
+ _socketMap.find(removeFD);
+ if(it != _socketMap.end())
+ {
+ deleteSocket = it->second;
+ _socketMap.erase(it);
+ }
+ if(deleteSocket)
+ {
+ deleteSocket->ReadyForDeletion();
+ delete deleteSocket;
+ }
+ }
+ _removeList.clear();
+
+ // Add sockets from add list.
+ for (SocketList::iterator iter = _addList.begin();
+ iter != _addList.end(); ++iter) {
+ UdpSocketPosix* s = static_cast<UdpSocketPosix*>(*iter);
+ if(s) {
+ _socketMap[s->GetFd()] = s;
+ }
+ }
+ _addList.clear();
+ _critSectList->Leave();
+}
+
+} // namespace test
+} // namespace webrtc
diff --git a/webrtc/test/channel_transport/udp_socket_manager_posix.h b/webrtc/test/channel_transport/udp_socket_manager_posix.h
new file mode 100644
index 0000000000..64156fd20f
--- /dev/null
+++ b/webrtc/test/channel_transport/udp_socket_manager_posix.h
@@ -0,0 +1,91 @@
+/*
+ * Copyright (c) 2011 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_CHANNEL_TRANSPORT_UDP_SOCKET_MANAGER_POSIX_H_
+#define WEBRTC_TEST_CHANNEL_TRANSPORT_UDP_SOCKET_MANAGER_POSIX_H_
+
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <list>
+#include <map>
+
+#include "webrtc/system_wrappers/include/critical_section_wrapper.h"
+#include "webrtc/system_wrappers/include/thread_wrapper.h"
+#include "webrtc/test/channel_transport/udp_socket_manager_wrapper.h"
+#include "webrtc/test/channel_transport/udp_socket_wrapper.h"
+
+namespace webrtc {
+
+class ConditionVariableWrapper;
+
+namespace test {
+
+class UdpSocketPosix;
+class UdpSocketManagerPosixImpl;
+#define MAX_NUMBER_OF_SOCKET_MANAGERS_LINUX 8
+
+class UdpSocketManagerPosix : public UdpSocketManager
+{
+public:
+ UdpSocketManagerPosix();
+ virtual ~UdpSocketManagerPosix();
+
+ bool Init(int32_t id, uint8_t& numOfWorkThreads) override;
+
+ bool Start() override;
+ bool Stop() override;
+
+ bool AddSocket(UdpSocketWrapper* s) override;
+ bool RemoveSocket(UdpSocketWrapper* s) override;
+
+private:
+ int32_t _id;
+ CriticalSectionWrapper* _critSect;
+ uint8_t _numberOfSocketMgr;
+ uint8_t _incSocketMgrNextTime;
+ uint8_t _nextSocketMgrToAssign;
+ UdpSocketManagerPosixImpl* _socketMgr[MAX_NUMBER_OF_SOCKET_MANAGERS_LINUX];
+};
+
+class UdpSocketManagerPosixImpl
+{
+public:
+ UdpSocketManagerPosixImpl();
+ virtual ~UdpSocketManagerPosixImpl();
+
+ virtual bool Start();
+ virtual bool Stop();
+
+ virtual bool AddSocket(UdpSocketWrapper* s);
+ virtual bool RemoveSocket(UdpSocketWrapper* s);
+
+protected:
+ static bool Run(void* obj);
+ bool Process();
+ void UpdateSocketMap();
+
+private:
+ typedef std::list<UdpSocketWrapper*> SocketList;
+ typedef std::list<SOCKET> FdList;
+ rtc::scoped_ptr<ThreadWrapper> _thread;
+ CriticalSectionWrapper* _critSectList;
+
+ fd_set _readFds;
+
+ std::map<SOCKET, UdpSocketPosix*> _socketMap;
+ SocketList _addList;
+ FdList _removeList;
+};
+
+} // namespace test
+} // namespace webrtc
+
+#endif // WEBRTC_TEST_CHANNEL_TRANSPORT_UDP_SOCKET_MANAGER_POSIX_H_
diff --git a/webrtc/test/channel_transport/udp_socket_manager_unittest.cc b/webrtc/test/channel_transport/udp_socket_manager_unittest.cc
new file mode 100644
index 0000000000..b49021bffe
--- /dev/null
+++ b/webrtc/test/channel_transport/udp_socket_manager_unittest.cc
@@ -0,0 +1,84 @@
+/*
+ * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+// Tests for the UdpSocketManager interface.
+// Note: This tests UdpSocketManager together with UdpSocketWrapper,
+// due to the way the code is full of static-casts to the platform dependent
+// subtypes.
+// It also uses the static UdpSocketManager object.
+// The most important property of these tests is that they do not leak memory.
+
+#include "testing/gtest/include/gtest/gtest.h"
+#include "webrtc/system_wrappers/include/trace.h"
+#include "webrtc/test/channel_transport/udp_socket_manager_wrapper.h"
+#include "webrtc/test/channel_transport/udp_socket_wrapper.h"
+
+namespace webrtc {
+namespace test {
+
+TEST(UdpSocketManager, CreateCallsInitAndDoesNotLeakMemory) {
+ int32_t id = 42;
+ uint8_t threads = 1;
+ UdpSocketManager* mgr = UdpSocketManager::Create(id, threads);
+ // Create is supposed to have called init on the object.
+ EXPECT_FALSE(mgr->Init(id, threads))
+ << "Init should return false since Create is supposed to call it.";
+ UdpSocketManager::Return();
+}
+
+// Creates a socket and adds it to the socket manager, and then removes it
+// before destroying the socket manager.
+TEST(UdpSocketManager, AddAndRemoveSocketDoesNotLeakMemory) {
+ int32_t id = 42;
+ uint8_t threads = 1;
+ UdpSocketManager* mgr = UdpSocketManager::Create(id, threads);
+ UdpSocketWrapper* socket =
+ UdpSocketWrapper::CreateSocket(id,
+ mgr,
+ NULL, // CallbackObj
+ NULL, // IncomingSocketCallback
+ false, // ipV6Enable
+ false); // disableGQOS
+ // The constructor will do AddSocket on the manager.
+ // RemoveSocket indirectly calls Delete.
+ EXPECT_EQ(true, mgr->RemoveSocket(socket));
+ UdpSocketManager::Return();
+}
+
+// Creates a socket and add it to the socket manager, but does not remove it
+// before destroying the socket manager.
+// On Posix, this destroys the socket.
+// On Winsock2 Windows, it enters an infinite wait for all the sockets
+// to go away.
+TEST(UdpSocketManager, UnremovedSocketsGetCollectedAtManagerDeletion) {
+#if defined(_WIN32)
+ // It's hard to test an infinite wait, so we don't.
+#else
+ int32_t id = 42;
+ uint8_t threads = 1;
+ UdpSocketManager* mgr = UdpSocketManager::Create(id, threads);
+ UdpSocketWrapper* unused_socket = UdpSocketWrapper::CreateSocket(
+ id,
+ mgr,
+ NULL, // CallbackObj
+ NULL, // IncomingSocketCallback
+ false, // ipV6Enable
+ false); // disableGQOS
+ // The constructor will do AddSocket on the manager.
+ // Call a member funtion to work around "set but not used" compliation
+ // error on ChromeOS ARM.
+ unused_socket->SetEventToNull();
+ unused_socket = NULL;
+ UdpSocketManager::Return();
+#endif
+}
+
+} // namespace test
+} // namespace webrtc
diff --git a/webrtc/test/channel_transport/udp_socket_manager_wrapper.cc b/webrtc/test/channel_transport/udp_socket_manager_wrapper.cc
new file mode 100644
index 0000000000..3127767cbc
--- /dev/null
+++ b/webrtc/test/channel_transport/udp_socket_manager_wrapper.cc
@@ -0,0 +1,72 @@
+/*
+ * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "webrtc/test/channel_transport/udp_socket_manager_wrapper.h"
+
+#include <assert.h>
+
+#ifdef _WIN32
+#include "webrtc/system_wrappers/include/fix_interlocked_exchange_pointer_win.h"
+#include "webrtc/test/channel_transport/udp_socket2_manager_win.h"
+#else
+#include "webrtc/test/channel_transport/udp_socket_manager_posix.h"
+#endif
+
+namespace webrtc {
+namespace test {
+
+UdpSocketManager* UdpSocketManager::CreateInstance()
+{
+#if defined(_WIN32)
+ return static_cast<UdpSocketManager*>(new UdpSocket2ManagerWindows());
+#else
+ return new UdpSocketManagerPosix();
+#endif
+}
+
+UdpSocketManager* UdpSocketManager::StaticInstance(
+ CountOperation count_operation,
+ const int32_t id,
+ uint8_t& numOfWorkThreads)
+{
+ UdpSocketManager* impl =
+ GetStaticInstance<UdpSocketManager>(count_operation);
+ if (count_operation == kAddRef && impl != NULL) {
+ if (impl->Init(id, numOfWorkThreads)) {
+ impl->Start();
+ }
+ }
+ return impl;
+}
+
+UdpSocketManager* UdpSocketManager::Create(const int32_t id,
+ uint8_t& numOfWorkThreads)
+{
+ return UdpSocketManager::StaticInstance(kAddRef, id, numOfWorkThreads);
+}
+
+void UdpSocketManager::Return()
+{
+ uint8_t numOfWorkThreads = 0;
+ UdpSocketManager::StaticInstance(kRelease, -1,
+ numOfWorkThreads);
+}
+
+UdpSocketManager::UdpSocketManager() : _numOfWorkThreads(0)
+{
+}
+
+uint8_t UdpSocketManager::WorkThreads() const
+{
+ return _numOfWorkThreads;
+}
+
+} // namespace test
+} // namespace webrtc
diff --git a/webrtc/test/channel_transport/udp_socket_manager_wrapper.h b/webrtc/test/channel_transport/udp_socket_manager_wrapper.h
new file mode 100644
index 0000000000..0c3c3850d9
--- /dev/null
+++ b/webrtc/test/channel_transport/udp_socket_manager_wrapper.h
@@ -0,0 +1,70 @@
+/*
+ * Copyright (c) 2011 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_CHANNEL_TRANSPORT_UDP_SOCKET_MANAGER_WRAPPER_H_
+#define WEBRTC_TEST_CHANNEL_TRANSPORT_UDP_SOCKET_MANAGER_WRAPPER_H_
+
+#include "webrtc/system_wrappers/include/static_instance.h"
+#include "webrtc/typedefs.h"
+
+namespace webrtc {
+namespace test {
+
+class UdpSocketWrapper;
+
+class UdpSocketManager
+{
+public:
+ static UdpSocketManager* Create(const int32_t id,
+ uint8_t& numOfWorkThreads);
+ static void Return();
+
+ // Initializes the socket manager. Returns true if the manager wasn't
+ // already initialized.
+ virtual bool Init(int32_t id, uint8_t& numOfWorkThreads) = 0;
+
+ // Start listening to sockets that have been registered via the
+ // AddSocket(..) API.
+ virtual bool Start() = 0;
+ // Stop listening to sockets.
+ virtual bool Stop() = 0;
+
+ virtual uint8_t WorkThreads() const;
+
+ // Register a socket with the socket manager.
+ virtual bool AddSocket(UdpSocketWrapper* s) = 0;
+ // Unregister a socket from the manager.
+ virtual bool RemoveSocket(UdpSocketWrapper* s) = 0;
+
+protected:
+ UdpSocketManager();
+ virtual ~UdpSocketManager() {}
+
+ uint8_t _numOfWorkThreads;
+
+ // Factory method.
+ static UdpSocketManager* CreateInstance();
+
+private:
+ // Friend function to allow the UDP destructor to be accessed from the
+ // instance template.
+ friend UdpSocketManager* webrtc::GetStaticInstance<UdpSocketManager>(
+ CountOperation count_operation);
+
+ static UdpSocketManager* StaticInstance(
+ CountOperation count_operation,
+ const int32_t id,
+ uint8_t& numOfWorkThreads);
+};
+
+} // namespace test
+} // namespace webrtc
+
+#endif // WEBRTC_TEST_CHANNEL_TRANSPORT_UDP_SOCKET_MANAGER_WRAPPER_H_
diff --git a/webrtc/test/channel_transport/udp_socket_posix.cc b/webrtc/test/channel_transport/udp_socket_posix.cc
new file mode 100644
index 0000000000..639d444f55
--- /dev/null
+++ b/webrtc/test/channel_transport/udp_socket_posix.cc
@@ -0,0 +1,285 @@
+/*
+ * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "webrtc/test/channel_transport/udp_socket_posix.h"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <netdb.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <time.h>
+#include <unistd.h>
+
+#include "webrtc/system_wrappers/include/trace.h"
+#include "webrtc/test/channel_transport/udp_socket_manager_wrapper.h"
+#include "webrtc/test/channel_transport/udp_socket_wrapper.h"
+
+namespace webrtc {
+namespace test {
+UdpSocketPosix::UdpSocketPosix(const int32_t id, UdpSocketManager* mgr,
+ bool ipV6Enable) : _id(id)
+{
+ WEBRTC_TRACE(kTraceMemory, kTraceTransport, id,
+ "UdpSocketPosix::UdpSocketPosix()");
+
+ _wantsIncoming = false;
+ _mgr = mgr;
+
+ _obj = NULL;
+ _incomingCb = NULL;
+ _readyForDeletionCond = ConditionVariableWrapper::CreateConditionVariable();
+ _closeBlockingCompletedCond =
+ ConditionVariableWrapper::CreateConditionVariable();
+ _cs = CriticalSectionWrapper::CreateCriticalSection();
+ _readyForDeletion = false;
+ _closeBlockingActive = false;
+ _closeBlockingCompleted= false;
+ if(ipV6Enable)
+ {
+ _socket = socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP);
+ }
+ else {
+ _socket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
+ }
+
+ // Set socket to nonblocking mode.
+ int enable_non_blocking = 1;
+ if(ioctl(_socket, FIONBIO, &enable_non_blocking) == -1)
+ {
+ WEBRTC_TRACE(kTraceWarning, kTraceTransport, id,
+ "Failed to make socket nonblocking");
+ }
+ // Enable close on fork for file descriptor so that it will not block until
+ // forked process terminates.
+ if(fcntl(_socket, F_SETFD, FD_CLOEXEC) == -1)
+ {
+ WEBRTC_TRACE(kTraceWarning, kTraceTransport, id,
+ "Failed to set FD_CLOEXEC for socket");
+ }
+}
+
+UdpSocketPosix::~UdpSocketPosix()
+{
+ if(_socket != INVALID_SOCKET)
+ {
+ close(_socket);
+ _socket = INVALID_SOCKET;
+ }
+ if(_readyForDeletionCond)
+ {
+ delete _readyForDeletionCond;
+ }
+
+ if(_closeBlockingCompletedCond)
+ {
+ delete _closeBlockingCompletedCond;
+ }
+
+ if(_cs)
+ {
+ delete _cs;
+ }
+}
+
+bool UdpSocketPosix::SetCallback(CallbackObj obj, IncomingSocketCallback cb)
+{
+ _obj = obj;
+ _incomingCb = cb;
+
+ WEBRTC_TRACE(kTraceDebug, kTraceTransport, _id,
+ "UdpSocketPosix(%p)::SetCallback", this);
+
+ if (_mgr->AddSocket(this))
+ {
+ WEBRTC_TRACE(kTraceDebug, kTraceTransport, _id,
+ "UdpSocketPosix(%p)::SetCallback socket added to manager",
+ this);
+ return true; // socket is now ready for action
+ }
+
+ WEBRTC_TRACE(kTraceDebug, kTraceTransport, _id,
+ "UdpSocketPosix(%p)::SetCallback error adding me to mgr",
+ this);
+ return false;
+}
+
+bool UdpSocketPosix::SetSockopt(int32_t level, int32_t optname,
+ const int8_t* optval, int32_t optlen)
+{
+ if(0 == setsockopt(_socket, level, optname, optval, optlen ))
+ {
+ return true;
+ }
+
+ WEBRTC_TRACE(kTraceError, kTraceTransport, _id,
+ "UdpSocketPosix::SetSockopt(), error:%d", errno);
+ return false;
+}
+
+int32_t UdpSocketPosix::SetTOS(int32_t serviceType)
+{
+ if (SetSockopt(IPPROTO_IP, IP_TOS ,(int8_t*)&serviceType ,4) != 0)
+ {
+ return -1;
+ }
+ return 0;
+}
+
+bool UdpSocketPosix::Bind(const SocketAddress& name)
+{
+ int size = sizeof(sockaddr);
+ if (0 == bind(_socket, reinterpret_cast<const sockaddr*>(&name),size))
+ {
+ return true;
+ }
+ WEBRTC_TRACE(kTraceError, kTraceTransport, _id,
+ "UdpSocketPosix::Bind() error: %d", errno);
+ return false;
+}
+
+int32_t UdpSocketPosix::SendTo(const int8_t* buf, size_t len,
+ const SocketAddress& to)
+{
+ int size = sizeof(sockaddr);
+ int retVal = sendto(_socket,buf, len, 0,
+ reinterpret_cast<const sockaddr*>(&to), size);
+ if(retVal == SOCKET_ERROR)
+ {
+ WEBRTC_TRACE(kTraceError, kTraceTransport, _id,
+ "UdpSocketPosix::SendTo() error: %d", errno);
+ }
+
+ return retVal;
+}
+
+SOCKET UdpSocketPosix::GetFd() { return _socket; }
+
+bool UdpSocketPosix::ValidHandle()
+{
+ return _socket != INVALID_SOCKET;
+}
+
+bool UdpSocketPosix::SetQos(int32_t /*serviceType*/,
+ int32_t /*tokenRate*/,
+ int32_t /*bucketSize*/,
+ int32_t /*peekBandwith*/,
+ int32_t /*minPolicedSize*/,
+ int32_t /*maxSduSize*/,
+ const SocketAddress& /*stRemName*/,
+ int32_t /*overrideDSCP*/) {
+ return false;
+}
+
+void UdpSocketPosix::HasIncoming()
+{
+ // replace 2048 with a mcro define and figure out
+ // where 2048 comes from
+ int8_t buf[2048];
+ int retval;
+ SocketAddress from;
+#if defined(WEBRTC_MAC)
+ sockaddr sockaddrfrom;
+ memset(&from, 0, sizeof(from));
+ memset(&sockaddrfrom, 0, sizeof(sockaddrfrom));
+ socklen_t fromlen = sizeof(sockaddrfrom);
+#else
+ memset(&from, 0, sizeof(from));
+ socklen_t fromlen = sizeof(from);
+#endif
+
+#if defined(WEBRTC_MAC)
+ retval = recvfrom(_socket,buf, sizeof(buf), 0,
+ reinterpret_cast<sockaddr*>(&sockaddrfrom), &fromlen);
+ memcpy(&from, &sockaddrfrom, fromlen);
+ from._sockaddr_storage.sin_family = sockaddrfrom.sa_family;
+#else
+ retval = recvfrom(_socket,buf, sizeof(buf), 0,
+ reinterpret_cast<sockaddr*>(&from), &fromlen);
+#endif
+
+ switch(retval)
+ {
+ case 0:
+ // The peer has performed an orderly shutdown.
+ break;
+ case SOCKET_ERROR:
+ break;
+ default:
+ if (_wantsIncoming && _incomingCb)
+ {
+ _incomingCb(_obj, buf, retval, &from);
+ }
+ break;
+ }
+}
+
+bool UdpSocketPosix::WantsIncoming() { return _wantsIncoming; }
+
+void UdpSocketPosix::CloseBlocking()
+{
+ _cs->Enter();
+ _closeBlockingActive = true;
+ if(!CleanUp())
+ {
+ _closeBlockingActive = false;
+ _cs->Leave();
+ return;
+ }
+
+ while(!_readyForDeletion)
+ {
+ _readyForDeletionCond->SleepCS(*_cs);
+ }
+ _closeBlockingCompleted = true;
+ _closeBlockingCompletedCond->Wake();
+ _cs->Leave();
+}
+
+void UdpSocketPosix::ReadyForDeletion()
+{
+ _cs->Enter();
+ if(!_closeBlockingActive)
+ {
+ _cs->Leave();
+ return;
+ }
+ close(_socket);
+ _socket = INVALID_SOCKET;
+ _readyForDeletion = true;
+ _readyForDeletionCond->Wake();
+ while(!_closeBlockingCompleted)
+ {
+ _closeBlockingCompletedCond->SleepCS(*_cs);
+ }
+ _cs->Leave();
+}
+
+bool UdpSocketPosix::CleanUp()
+{
+ _wantsIncoming = false;
+
+ if (_socket == INVALID_SOCKET)
+ {
+ return false;
+ }
+
+ WEBRTC_TRACE(kTraceDebug, kTraceTransport, _id,
+ "calling UdpSocketManager::RemoveSocket()...");
+ _mgr->RemoveSocket(this);
+ // After this, the socket should may be or will be as deleted. Return
+ // immediately.
+ return true;
+}
+
+} // namespace test
+} // namespace webrtc
diff --git a/webrtc/test/channel_transport/udp_socket_posix.h b/webrtc/test/channel_transport/udp_socket_posix.h
new file mode 100644
index 0000000000..c391b2e397
--- /dev/null
+++ b/webrtc/test/channel_transport/udp_socket_posix.h
@@ -0,0 +1,94 @@
+/*
+ * Copyright (c) 2011 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_CHANNEL_TRANSPORT_UDP_SOCKET_POSIX_H_
+#define WEBRTC_TEST_CHANNEL_TRANSPORT_UDP_SOCKET_POSIX_H_
+
+#include <arpa/inet.h>
+#include <netinet/in.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+
+#include "webrtc/system_wrappers/include/condition_variable_wrapper.h"
+#include "webrtc/system_wrappers/include/critical_section_wrapper.h"
+#include "webrtc/test/channel_transport/udp_socket_wrapper.h"
+
+namespace webrtc {
+namespace test {
+
+#define SOCKET_ERROR -1
+
+class UdpSocketPosix : public UdpSocketWrapper
+{
+public:
+ UdpSocketPosix(const int32_t id, UdpSocketManager* mgr,
+ bool ipV6Enable = false);
+
+ virtual ~UdpSocketPosix();
+
+ bool SetCallback(CallbackObj obj, IncomingSocketCallback cb) override;
+
+ bool Bind(const SocketAddress& name) override;
+
+ bool SetSockopt(int32_t level,
+ int32_t optname,
+ const int8_t* optval,
+ int32_t optlen) override;
+
+ int32_t SetTOS(const int32_t serviceType) override;
+
+ int32_t SendTo(const int8_t* buf,
+ size_t len,
+ const SocketAddress& to) override;
+
+ // Deletes socket in addition to closing it.
+ // TODO (hellner): make destructor protected.
+ void CloseBlocking() override;
+
+ SOCKET GetFd();
+
+ bool ValidHandle() override;
+
+ bool SetQos(int32_t /*serviceType*/,
+ int32_t /*tokenRate*/,
+ int32_t /*bucketSize*/,
+ int32_t /*peekBandwith*/,
+ int32_t /*minPolicedSize*/,
+ int32_t /*maxSduSize*/,
+ const SocketAddress& /*stRemName*/,
+ int32_t /*overrideDSCP*/) override;
+
+ bool CleanUp();
+ void HasIncoming();
+ bool WantsIncoming();
+ void ReadyForDeletion();
+private:
+ friend class UdpSocketManagerPosix;
+
+ const int32_t _id;
+ IncomingSocketCallback _incomingCb;
+ CallbackObj _obj;
+
+ SOCKET _socket;
+ UdpSocketManager* _mgr;
+ ConditionVariableWrapper* _closeBlockingCompletedCond;
+ ConditionVariableWrapper* _readyForDeletionCond;
+
+ bool _closeBlockingActive;
+ bool _closeBlockingCompleted;
+ bool _readyForDeletion;
+
+ CriticalSectionWrapper* _cs;
+};
+
+} // namespace test
+} // namespace webrtc
+
+#endif // WEBRTC_TEST_CHANNEL_TRANSPORT_UDP_SOCKET_POSIX_H_
diff --git a/webrtc/test/channel_transport/udp_socket_wrapper.cc b/webrtc/test/channel_transport/udp_socket_wrapper.cc
new file mode 100644
index 0000000000..f4fa3e950b
--- /dev/null
+++ b/webrtc/test/channel_transport/udp_socket_wrapper.cc
@@ -0,0 +1,159 @@
+/*
+ * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "webrtc/test/channel_transport/udp_socket_wrapper.h"
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "webrtc/system_wrappers/include/event_wrapper.h"
+#include "webrtc/system_wrappers/include/trace.h"
+#include "webrtc/test/channel_transport/udp_socket_manager_wrapper.h"
+
+#if defined(_WIN32)
+ #include "webrtc/test/channel_transport/udp_socket2_win.h"
+#else
+ #include "webrtc/test/channel_transport/udp_socket_posix.h"
+#endif
+
+
+namespace webrtc {
+namespace test {
+
+bool UdpSocketWrapper::_initiated = false;
+
+// Temporary Android hack. The value 1024 is taken from
+// <ndk>/build/platforms/android-1.5/arch-arm/usr/include/linux/posix_types.h
+// TODO (tomasl): can we remove this now?
+#ifndef FD_SETSIZE
+#define FD_SETSIZE 1024
+#endif
+
+UdpSocketWrapper::UdpSocketWrapper()
+ : _wantsIncoming(false),
+ _deleteEvent(NULL)
+{
+}
+
+UdpSocketWrapper::~UdpSocketWrapper()
+{
+ if(_deleteEvent)
+ {
+ _deleteEvent->Set();
+ _deleteEvent = NULL;
+ }
+}
+
+void UdpSocketWrapper::SetEventToNull()
+{
+ if (_deleteEvent)
+ {
+ _deleteEvent = NULL;
+ }
+}
+
+UdpSocketWrapper* UdpSocketWrapper::CreateSocket(const int32_t id,
+ UdpSocketManager* mgr,
+ CallbackObj obj,
+ IncomingSocketCallback cb,
+ bool ipV6Enable,
+ bool disableGQOS)
+
+{
+ WEBRTC_TRACE(kTraceMemory, kTraceTransport, id,
+ "UdpSocketWrapper::CreateSocket");
+
+ UdpSocketWrapper* s = 0;
+
+#ifdef _WIN32
+ if (!_initiated)
+ {
+ WSADATA wsaData;
+ WORD wVersionRequested = MAKEWORD( 2, 2 );
+ int32_t err = WSAStartup( wVersionRequested, &wsaData);
+ if (err != 0)
+ {
+ WEBRTC_TRACE(
+ kTraceError,
+ kTraceTransport,
+ id,
+ "UdpSocketWrapper::CreateSocket failed to initialize sockets\
+ WSAStartup error:%d",
+ err);
+ return NULL;
+ }
+
+ _initiated = true;
+ }
+
+ s = new UdpSocket2Windows(id, mgr, ipV6Enable, disableGQOS);
+
+#else
+ if (!_initiated)
+ {
+ _initiated = true;
+ }
+ s = new UdpSocketPosix(id, mgr, ipV6Enable);
+ if (s)
+ {
+ UdpSocketPosix* sl = static_cast<UdpSocketPosix*>(s);
+ if (sl->GetFd() != INVALID_SOCKET && sl->GetFd() < FD_SETSIZE)
+ {
+ // ok
+ } else
+ {
+ WEBRTC_TRACE(
+ kTraceError,
+ kTraceTransport,
+ id,
+ "UdpSocketWrapper::CreateSocket failed to initialize socket");
+ delete s;
+ s = NULL;
+ }
+ }
+#endif
+ if (s)
+ {
+ s->_deleteEvent = NULL;
+ if (!s->SetCallback(obj, cb))
+ {
+ WEBRTC_TRACE(
+ kTraceError,
+ kTraceTransport,
+ id,
+ "UdpSocketWrapper::CreateSocket failed to ser callback");
+ return(NULL);
+ }
+ }
+ return s;
+}
+
+bool UdpSocketWrapper::StartReceiving()
+{
+ _wantsIncoming = true;
+ return true;
+}
+
+bool UdpSocketWrapper::StartReceiving(const uint32_t /*receiveBuffers*/) {
+ return StartReceiving();
+}
+
+bool UdpSocketWrapper::StopReceiving()
+{
+ _wantsIncoming = false;
+ return true;
+}
+
+int32_t UdpSocketWrapper::SetPCP(const int32_t /*pcp*/) { return -1; }
+
+uint32_t UdpSocketWrapper::ReceiveBuffers() { return 0; }
+
+} // namespace test
+} // namespace webrtc
diff --git a/webrtc/test/channel_transport/udp_socket_wrapper.h b/webrtc/test/channel_transport/udp_socket_wrapper.h
new file mode 100644
index 0000000000..d2a1ce6b61
--- /dev/null
+++ b/webrtc/test/channel_transport/udp_socket_wrapper.h
@@ -0,0 +1,112 @@
+/*
+ * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#ifndef WEBRTC_TEST_CHANNEL_TRANSPORT_UDP_SOCKET_WRAPPER_H_
+#define WEBRTC_TEST_CHANNEL_TRANSPORT_UDP_SOCKET_WRAPPER_H_
+
+#include "webrtc/test/channel_transport/udp_transport.h"
+
+namespace webrtc {
+
+class EventWrapper;
+
+namespace test {
+
+class UdpSocketManager;
+
+#define SOCKET_ERROR_NO_QOS -1000
+
+#ifndef _WIN32
+typedef int SOCKET;
+#endif
+
+#ifndef INVALID_SOCKET
+#define INVALID_SOCKET (SOCKET)(~0)
+
+#ifndef AF_INET
+#define AF_INET 2
+#endif
+
+#endif
+
+typedef void* CallbackObj;
+typedef void(*IncomingSocketCallback)(CallbackObj obj, const int8_t* buf,
+ size_t len, const SocketAddress* from);
+
+class UdpSocketWrapper
+{
+public:
+ static UdpSocketWrapper* CreateSocket(const int32_t id,
+ UdpSocketManager* mgr,
+ CallbackObj obj,
+ IncomingSocketCallback cb,
+ bool ipV6Enable = false,
+ bool disableGQOS = false);
+
+ // Register cb for receiving callbacks when there are incoming packets.
+ // Register obj so that it will be passed in calls to cb.
+ virtual bool SetCallback(CallbackObj obj, IncomingSocketCallback cb) = 0;
+
+ // Socket to local address specified by name.
+ virtual bool Bind(const SocketAddress& name) = 0;
+
+ // Start receiving UDP data.
+ virtual bool StartReceiving();
+ virtual bool StartReceiving(const uint32_t /*receiveBuffers*/);
+ // Stop receiving UDP data.
+ virtual bool StopReceiving();
+
+ virtual bool ValidHandle() = 0;
+
+ // Set socket options.
+ virtual bool SetSockopt(int32_t level, int32_t optname,
+ const int8_t* optval, int32_t optlen) = 0;
+
+ // Set TOS for outgoing packets.
+ virtual int32_t SetTOS(const int32_t serviceType) = 0;
+
+ // Set 802.1Q PCP field (802.1p) for outgoing VLAN traffic.
+ virtual int32_t SetPCP(const int32_t /*pcp*/);
+
+ // Send buf of length len to the address specified by to.
+ virtual int32_t SendTo(const int8_t* buf, size_t len,
+ const SocketAddress& to) = 0;
+
+ virtual void SetEventToNull();
+
+ // Close socket and don't return until completed.
+ virtual void CloseBlocking() {}
+
+ // tokenRate is in bit/s. peakBandwidt is in byte/s
+ virtual bool SetQos(int32_t serviceType, int32_t tokenRate,
+ int32_t bucketSize, int32_t peekBandwith,
+ int32_t minPolicedSize, int32_t maxSduSize,
+ const SocketAddress &stRemName,
+ int32_t overrideDSCP = 0) = 0;
+
+ virtual uint32_t ReceiveBuffers();
+
+protected:
+ // Creating the socket is done via CreateSocket().
+ UdpSocketWrapper();
+ // Destroying the socket is done via CloseBlocking().
+ virtual ~UdpSocketWrapper();
+
+ bool _wantsIncoming;
+ EventWrapper* _deleteEvent;
+
+private:
+ static bool _initiated;
+};
+
+} // namespac test
+} // namespace webrtc
+
+#endif // WEBRTC_TEST_CHANNEL_TRANSPORT_UDP_SOCKET_WRAPPER_H_
diff --git a/webrtc/test/channel_transport/udp_socket_wrapper_unittest.cc b/webrtc/test/channel_transport/udp_socket_wrapper_unittest.cc
new file mode 100644
index 0000000000..885e19c237
--- /dev/null
+++ b/webrtc/test/channel_transport/udp_socket_wrapper_unittest.cc
@@ -0,0 +1,67 @@
+/*
+ * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+// Tests for the UdpSocketWrapper interface.
+// This will test the UdpSocket implementations on various platforms.
+// Note that this test is using a real SocketManager, which starts up
+// an extra worker thread, making the testing more complex than it
+// should be.
+// This is because on Posix, the CloseBlocking function waits for the
+// ReadyForDeletion function to be called, which has to be called after
+// CloseBlocking, and thus has to be called from another thread.
+// The manager is the one actually doing the deleting.
+// This is done differently in the Winsock2 code, but that code
+// will also hang if the destructor is called directly.
+
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "webrtc/test/channel_transport/udp_socket_manager_wrapper.h"
+#include "webrtc/test/channel_transport/udp_socket_wrapper.h"
+
+using ::testing::_;
+using ::testing::Return;
+
+namespace webrtc {
+namespace test {
+
+class MockSocketManager : public UdpSocketManager {
+ public:
+ MockSocketManager() {}
+ // Access to protected destructor.
+ void Destroy() {
+ delete this;
+ }
+ MOCK_METHOD2(Init, bool(int32_t, uint8_t&));
+ MOCK_METHOD0(Start, bool());
+ MOCK_METHOD0(Stop, bool());
+ MOCK_METHOD1(AddSocket, bool(UdpSocketWrapper*));
+ MOCK_METHOD1(RemoveSocket, bool(UdpSocketWrapper*));
+};
+
+// Creates a socket using the static constructor method and verifies that
+// it's added to the socket manager.
+TEST(UdpSocketWrapper, CreateSocket) {
+ int32_t id = 42;
+ // We can't test deletion of sockets without a socket manager.
+ uint8_t threads = 1;
+ UdpSocketManager* mgr = UdpSocketManager::Create(id, threads);
+ UdpSocketWrapper* socket =
+ UdpSocketWrapper::CreateSocket(id,
+ mgr,
+ NULL, // CallbackObj
+ NULL, // IncomingSocketCallback
+ false, // ipV6Enable
+ false); // disableGQOS
+ socket->CloseBlocking();
+ UdpSocketManager::Return();
+}
+
+} // namespace test
+} // namespace webrtc
diff --git a/webrtc/test/channel_transport/udp_transport.h b/webrtc/test/channel_transport/udp_transport.h
new file mode 100644
index 0000000000..0c5079e69f
--- /dev/null
+++ b/webrtc/test/channel_transport/udp_transport.h
@@ -0,0 +1,381 @@
+/*
+ * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#ifndef WEBRTC_TEST_CHANNEL_TRANSPORT_UDP_TRANSPORT_H_
+#define WEBRTC_TEST_CHANNEL_TRANSPORT_UDP_TRANSPORT_H_
+
+#include "webrtc/common_types.h"
+#include "webrtc/transport.h"
+#include "webrtc/typedefs.h"
+
+/*
+ * WARNING
+ * This code is not use in production/testing and might have security issues
+ * for example: http://code.google.com/p/webrtc/issues/detail?id=1028
+ *
+ */
+
+#define SS_MAXSIZE 128
+#define SS_ALIGNSIZE (sizeof (uint64_t))
+#define SS_PAD1SIZE (SS_ALIGNSIZE - sizeof(int16_t))
+#define SS_PAD2SIZE (SS_MAXSIZE - (sizeof(int16_t) + SS_PAD1SIZE +\
+ SS_ALIGNSIZE))
+
+// BSD requires use of HAVE_STRUCT_SOCKADDR_SA_LEN
+namespace webrtc {
+namespace test {
+
+struct SocketAddressIn {
+ // sin_family should be either AF_INET (IPv4) or AF_INET6 (IPv6)
+#ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
+ int8_t sin_length;
+ int8_t sin_family;
+#else
+ int16_t sin_family;
+#endif
+ uint16_t sin_port;
+ uint32_t sin_addr;
+ int8_t sin_zero[8];
+};
+
+struct Version6InAddress {
+ union {
+ uint8_t _s6_u8[16];
+ uint32_t _s6_u32[4];
+ uint64_t _s6_u64[2];
+ } Version6AddressUnion;
+};
+
+struct SocketAddressInVersion6 {
+ // sin_family should be either AF_INET (IPv4) or AF_INET6 (IPv6)
+#ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
+ int8_t sin_length;
+ int8_t sin_family;
+#else
+ int16_t sin_family;
+#endif
+ // Transport layer port number.
+ uint16_t sin6_port;
+ // IPv6 traffic class and flow info or ip4 address.
+ uint32_t sin6_flowinfo;
+ // IPv6 address
+ struct Version6InAddress sin6_addr;
+ // Set of interfaces for a scope.
+ uint32_t sin6_scope_id;
+};
+
+struct SocketAddressStorage {
+ // sin_family should be either AF_INET (IPv4) or AF_INET6 (IPv6)
+#ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
+ int8_t sin_length;
+ int8_t sin_family;
+#else
+ int16_t sin_family;
+#endif
+ int8_t __ss_pad1[SS_PAD1SIZE];
+ uint64_t __ss_align;
+ int8_t __ss_pad2[SS_PAD2SIZE];
+};
+
+struct SocketAddress {
+ union {
+ struct SocketAddressIn _sockaddr_in;
+ struct SocketAddressInVersion6 _sockaddr_in6;
+ struct SocketAddressStorage _sockaddr_storage;
+ };
+};
+
+// Callback class that receives packets from UdpTransport.
+class UdpTransportData {
+ public:
+ virtual ~UdpTransportData() {};
+
+ virtual void IncomingRTPPacket(const int8_t* incomingRtpPacket,
+ const size_t rtpPacketLength,
+ const char* fromIP,
+ const uint16_t fromPort) = 0;
+
+ virtual void IncomingRTCPPacket(const int8_t* incomingRtcpPacket,
+ const size_t rtcpPacketLength,
+ const char* fromIP,
+ const uint16_t fromPort) = 0;
+};
+
+class UdpTransport : public Transport {
+ public:
+ enum
+ {
+ kIpAddressVersion6Length = 64,
+ kIpAddressVersion4Length = 16
+ };
+ enum ErrorCode
+ {
+ kNoSocketError = 0,
+ kFailedToBindPort = 1,
+ kIpAddressInvalid = 2,
+ kAddressInvalid = 3,
+ kSocketInvalid = 4,
+ kPortInvalid = 5,
+ kTosInvalid = 6,
+ kMulticastAddressInvalid = 7,
+ kQosError = 8,
+ kSocketAlreadyInitialized = 9,
+ kIpVersion6Error = 10,
+ FILTER_ERROR = 11,
+ kStartReceiveError = 12,
+ kStopReceiveError = 13,
+ kCannotFindLocalIp = 14,
+ kTosError = 16,
+ kNotInitialized = 17,
+ kPcpError = 18
+ };
+
+ // Factory method. Constructor disabled.
+ static UdpTransport* Create(const int32_t id, uint8_t& numSocketThreads);
+ static void Destroy(UdpTransport* module);
+
+ // Prepares the class for sending RTP packets to ipAddr:rtpPort and RTCP
+ // packets to ipAddr:rtpPort+1 if rtcpPort is zero. Otherwise to
+ // ipAddr:rtcpPort.
+ virtual int32_t InitializeSendSockets(const char* ipAddr,
+ const uint16_t rtpPort,
+ const uint16_t rtcpPort = 0) = 0;
+
+ // Register packetCallback for receiving incoming packets. Set the local
+ // RTP port to rtpPort. Bind local IP address to ipAddr. If ipAddr is NULL
+ // bind to local IP ANY. Set the local rtcp port to rtcpPort or rtpPort + 1
+ // if rtcpPort is 0.
+ virtual int32_t InitializeReceiveSockets(
+ UdpTransportData* const packetCallback,
+ const uint16_t rtpPort,
+ const char* ipAddr = NULL,
+ const char* multicastIpAddr = NULL,
+ const uint16_t rtcpPort = 0) = 0;
+
+ // Set local RTP port to rtpPort and RTCP port to rtcpPort or rtpPort + 1 if
+ // rtcpPort is 0. These ports will be used for sending instead of the local
+ // ports set by InitializeReceiveSockets(..).
+ virtual int32_t InitializeSourcePorts(const uint16_t rtpPort,
+ const uint16_t rtcpPort = 0) = 0;
+
+ // Retrieve local ports used for sending if other than the ports specified
+ // by InitializeReceiveSockets(..). rtpPort is set to the RTP port.
+ // rtcpPort is set to the RTCP port.
+ virtual int32_t SourcePorts(uint16_t& rtpPort,
+ uint16_t& rtcpPort) const = 0;
+
+ // Set ipAddr to the IP address that is currently being listened on. rtpPort
+ // to the RTP port listened to. rtcpPort to the RTCP port listened on.
+ // multicastIpAddr to the multicast IP address group joined (the address
+ // is NULL terminated).
+ virtual int32_t ReceiveSocketInformation(
+ char ipAddr[kIpAddressVersion6Length],
+ uint16_t& rtpPort,
+ uint16_t& rtcpPort,
+ char multicastIpAddr[kIpAddressVersion6Length]) const = 0;
+
+ // Set ipAddr to the IP address being sent from. rtpPort to the local RTP
+ // port used for sending and rtcpPort to the local RTCP port used for
+ // sending.
+ virtual int32_t SendSocketInformation(char ipAddr[kIpAddressVersion6Length],
+ uint16_t& rtpPort,
+ uint16_t& rtcpPort) const = 0;
+
+ // Put the IP address, RTP port and RTCP port from the last received packet
+ // into ipAddr, rtpPort and rtcpPort respectively.
+ virtual int32_t RemoteSocketInformation(
+ char ipAddr[kIpAddressVersion6Length],
+ uint16_t& rtpPort,
+ uint16_t& rtcpPort) const = 0;
+
+ // Enable/disable quality of service if QoS is true or false respectively.
+ // Set the type of service to serviceType, max bitrate in kbit/s to
+ // maxBitrate and override DSCP if overrideDSCP is not 0.
+ // Note: Must be called both InitializeSendSockets() and
+ // InitializeReceiveSockets() has been called.
+ virtual int32_t SetQoS(const bool QoS,
+ const int32_t serviceType,
+ const uint32_t maxBitrate = 0,
+ const int32_t overrideDSCP = 0,
+ const bool audio = false) = 0;
+
+ // Set QoS to true if quality of service has been turned on. If QoS is true,
+ // also set serviceType to type of service and overrideDSCP to override
+ // DSCP.
+ virtual int32_t QoS(bool& QoS,
+ int32_t& serviceType,
+ int32_t& overrideDSCP) const = 0;
+
+ // Set type of service.
+ virtual int32_t SetToS(const int32_t DSCP,
+ const bool useSetSockOpt = false) = 0;
+
+ // Get type of service configuration.
+ virtual int32_t ToS(int32_t& DSCP,
+ bool& useSetSockOpt) const = 0;
+
+ // Set Priority Code Point (IEEE 802.1Q)
+ // Note: for Linux this function will set the priority for the socket,
+ // which then can be mapped to a PCP value with vconfig.
+ virtual int32_t SetPCP(const int32_t PCP) = 0;
+
+ // Get Priority Code Point
+ virtual int32_t PCP(int32_t& PCP) const = 0;
+
+ // Enable IPv6.
+ // Note: this API must be called before any call to
+ // InitializeReceiveSockets() or InitializeSendSockets(). It is not
+ // possible to go back to IPv4 (default) after this call.
+ virtual int32_t EnableIpV6() = 0;
+
+ // Return true if IPv6 has been enabled.
+ virtual bool IpV6Enabled() const = 0;
+
+ // Only allow packets received from filterIPAddress to be processed.
+ // Note: must be called after EnableIPv6(), if IPv6 is used.
+ virtual int32_t SetFilterIP(
+ const char filterIPAddress[kIpAddressVersion6Length]) = 0;
+
+ // Write the filter IP address (if any) to filterIPAddress.
+ virtual int32_t FilterIP(
+ char filterIPAddress[kIpAddressVersion6Length]) const = 0;
+
+ // Only allow RTP packets from rtpFilterPort and RTCP packets from
+ // rtcpFilterPort be processed.
+ // Note: must be called after EnableIPv6(), if IPv6 is used.
+ virtual int32_t SetFilterPorts(const uint16_t rtpFilterPort,
+ const uint16_t rtcpFilterPort) = 0;
+
+ // Set rtpFilterPort to the filter RTP port and rtcpFilterPort to the
+ // filter RTCP port (if filtering based on port is enabled).
+ virtual int32_t FilterPorts(uint16_t& rtpFilterPort,
+ uint16_t& rtcpFilterPort) const = 0;
+
+ // Set the number of buffers that the socket implementation may use for
+ // receiving packets to numberOfSocketBuffers. I.e. the number of packets
+ // that can be received in parallell.
+ // Note: this API only has effect on Windows.
+ virtual int32_t StartReceiving(const uint32_t numberOfSocketBuffers) = 0;
+
+ // Stop receive incoming packets.
+ virtual int32_t StopReceiving() = 0;
+
+ // Return true incoming packets are received.
+ virtual bool Receiving() const = 0;
+
+ // Return true if send sockets have been initialized.
+ virtual bool SendSocketsInitialized() const = 0;
+
+ // Return true if local ports for sending has been set.
+ virtual bool SourcePortsInitialized() const = 0;
+
+ // Return true if receive sockets have been initialized.
+ virtual bool ReceiveSocketsInitialized() const = 0;
+
+ // Send data with size length to ip:portnr. The same port as the set
+ // with InitializeSendSockets(..) is used if portnr is 0. The same IP
+ // address as set with InitializeSendSockets(..) is used if ip is NULL.
+ // If isRTCP is true the port used will be the RTCP port.
+ virtual int32_t SendRaw(const int8_t* data,
+ size_t length,
+ int32_t isRTCP,
+ uint16_t portnr = 0,
+ const char* ip = NULL) = 0;
+
+ // Send RTP data with size length to the address specified by to.
+ virtual int32_t SendRTPPacketTo(const int8_t* data,
+ size_t length,
+ const SocketAddress& to) = 0;
+
+
+ // Send RTCP data with size length to the address specified by to.
+ virtual int32_t SendRTCPPacketTo(const int8_t* data,
+ size_t length,
+ const SocketAddress& to) = 0;
+
+ // Send RTP data with size length to ip:rtpPort where ip is the ip set by
+ // the InitializeSendSockets(..) call.
+ virtual int32_t SendRTPPacketTo(const int8_t* data,
+ size_t length,
+ uint16_t rtpPort) = 0;
+
+
+ // Send RTCP data with size length to ip:rtcpPort where ip is the ip set by
+ // the InitializeSendSockets(..) call.
+ virtual int32_t SendRTCPPacketTo(const int8_t* data,
+ size_t length,
+ uint16_t rtcpPort) = 0;
+
+ // Set the IP address to which packets are sent to ipaddr.
+ virtual int32_t SetSendIP(
+ const char ipaddr[kIpAddressVersion6Length]) = 0;
+
+ // Set the send RTP and RTCP port to rtpPort and rtcpPort respectively.
+ virtual int32_t SetSendPorts(const uint16_t rtpPort,
+ const uint16_t rtcpPort = 0) = 0;
+
+ // Retreive the last registered error code.
+ virtual ErrorCode LastError() const = 0;
+
+ // Put the local IPv4 address in localIP.
+ // Note: this API is for IPv4 only.
+ static int32_t LocalHostAddress(uint32_t& localIP);
+
+ // Put the local IP6 address in localIP.
+ // Note: this API is for IPv6 only.
+ static int32_t LocalHostAddressIPV6(char localIP[16]);
+
+ // Return a copy of hostOrder (host order) in network order.
+ static uint16_t Htons(uint16_t hostOrder);
+
+ // Return a copy of hostOrder (host order) in network order.
+ static uint32_t Htonl(uint32_t hostOrder);
+
+ // Return IPv4 address in ip as 32 bit integer.
+ static uint32_t InetAddrIPV4(const char* ip);
+
+ // Convert the character string src into a network address structure in
+ // the af address family and put it in dst.
+ // Note: same functionality as inet_pton(..)
+ static int32_t InetPresentationToNumeric(int32_t af,
+ const char* src,
+ void* dst);
+
+ // Set ip and sourcePort according to address. As input parameter ipSize
+ // is the length of ip. As output parameter it's the number of characters
+ // written to ip (not counting the '\0' character).
+ // Note: this API is only implemented on Windows and Linux.
+ static int32_t IPAddress(const SocketAddress& address,
+ char* ip,
+ uint32_t& ipSize,
+ uint16_t& sourcePort);
+
+ // Set ip and sourcePort according to address. As input parameter ipSize
+ // is the length of ip. As output parameter it's the number of characters
+ // written to ip (not counting the '\0' character).
+ // Note: this API is only implemented on Windows and Linux.
+ // Additional note: this API caches the address of the last call to it. If
+ // address is likley to be the same for multiple calls it may be beneficial
+ // to call this API instead of IPAddress().
+ virtual int32_t IPAddressCached(const SocketAddress& address,
+ char* ip,
+ uint32_t& ipSize,
+ uint16_t& sourcePort) = 0;
+
+ // Return true if ipaddr is a valid IP address.
+ // If ipV6 is false ipaddr is interpreted as an IPv4 address otherwise it
+ // is interptreted as IPv6.
+ static bool IsIpAddressValid(const char* ipaddr, const bool ipV6);
+};
+
+} // namespace test
+} // namespace webrtc
+
+#endif // WEBRTC_TEST_CHANNEL_TRANSPORT_UDP_TRANSPORT_H_
diff --git a/webrtc/test/channel_transport/udp_transport_impl.cc b/webrtc/test/channel_transport/udp_transport_impl.cc
new file mode 100644
index 0000000000..c7049aa8a2
--- /dev/null
+++ b/webrtc/test/channel_transport/udp_transport_impl.cc
@@ -0,0 +1,2994 @@
+/*
+ * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "webrtc/test/channel_transport/udp_transport_impl.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+
+#if defined(_WIN32)
+#include <winsock2.h>
+#include <ws2tcpip.h>
+#elif defined(WEBRTC_LINUX) || defined(WEBRTC_MAC)
+#include <arpa/inet.h>
+#include <ctype.h>
+#include <fcntl.h>
+#include <net/if.h>
+#include <netdb.h>
+#include <netinet/in.h>
+#include <stdlib.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <sys/time.h>
+#include <unistd.h>
+#ifndef WEBRTC_IOS
+#include <net/if_arp.h>
+#endif
+#endif // defined(WEBRTC_LINUX) || defined(WEBRTC_MAC)
+
+#if defined(WEBRTC_MAC)
+#include <ifaddrs.h>
+#include <machine/types.h>
+#endif
+#if defined(WEBRTC_LINUX)
+#include <linux/netlink.h>
+#include <linux/rtnetlink.h>
+#endif
+
+#include "webrtc/common_types.h"
+#include "webrtc/system_wrappers/include/critical_section_wrapper.h"
+#include "webrtc/system_wrappers/include/rw_lock_wrapper.h"
+#include "webrtc/system_wrappers/include/trace.h"
+#include "webrtc/test/channel_transport/udp_socket_manager_wrapper.h"
+#include "webrtc/typedefs.h"
+
+#if defined(WEBRTC_LINUX) || defined(WEBRTC_MAC)
+#define GetLastError() errno
+
+#define IFRSIZE ((int)(size * sizeof (struct ifreq)))
+
+#define NLMSG_OK_NO_WARNING(nlh,len) \
+ ((len) >= (int)sizeof(struct nlmsghdr) && \
+ (int)(nlh)->nlmsg_len >= (int)sizeof(struct nlmsghdr) && \
+ (int)(nlh)->nlmsg_len <= (len))
+
+#endif // defined(WEBRTC_LINUX) || defined(WEBRTC_MAC)
+
+namespace webrtc {
+namespace test {
+
+class SocketFactory : public UdpTransportImpl::SocketFactoryInterface {
+ public:
+ UdpSocketWrapper* CreateSocket(const int32_t id,
+ UdpSocketManager* mgr,
+ CallbackObj obj,
+ IncomingSocketCallback cb,
+ bool ipV6Enable,
+ bool disableGQOS) override {
+ return UdpSocketWrapper::CreateSocket(id, mgr, obj, cb, ipV6Enable,
+ disableGQOS);
+ }
+};
+
+// Creates an UdpTransport using the definition of SocketFactory above,
+// and passes (creating if needed) a pointer to the static singleton
+// UdpSocketManager.
+UdpTransport* UdpTransport::Create(const int32_t id,
+ uint8_t& numSocketThreads)
+{
+ return new UdpTransportImpl(id,
+ new SocketFactory(),
+ UdpSocketManager::Create(id, numSocketThreads));
+}
+
+// Deletes the UdpTransport and decrements the refcount of the
+// static singleton UdpSocketManager, possibly destroying it.
+// Should only be used on UdpTransports that are created using Create.
+void UdpTransport::Destroy(UdpTransport* module)
+{
+ if(module)
+ {
+ delete module;
+ UdpSocketManager::Return();
+ }
+}
+
+UdpTransportImpl::UdpTransportImpl(const int32_t id,
+ SocketFactoryInterface* maker,
+ UdpSocketManager* socket_manager)
+ : _id(id),
+ _socket_creator(maker),
+ _crit(CriticalSectionWrapper::CreateCriticalSection()),
+ _critFilter(CriticalSectionWrapper::CreateCriticalSection()),
+ _critPacketCallback(CriticalSectionWrapper::CreateCriticalSection()),
+ _mgr(socket_manager),
+ _lastError(kNoSocketError),
+ _destPort(0),
+ _destPortRTCP(0),
+ _localPort(0),
+ _localPortRTCP(0),
+ _srcPort(0),
+ _srcPortRTCP(0),
+ _fromPort(0),
+ _fromPortRTCP(0),
+ _fromIP(),
+ _destIP(),
+ _localIP(),
+ _localMulticastIP(),
+ _ptrRtpSocket(NULL),
+ _ptrRtcpSocket(NULL),
+ _ptrSendRtpSocket(NULL),
+ _ptrSendRtcpSocket(NULL),
+ _remoteRTPAddr(),
+ _remoteRTCPAddr(),
+ _localRTPAddr(),
+ _localRTCPAddr(),
+ _tos(0),
+ _receiving(false),
+ _useSetSockOpt(false),
+ _qos(false),
+ _pcp(0),
+ _ipV6Enabled(false),
+ _serviceType(0),
+ _overrideDSCP(0),
+ _maxBitrate(0),
+ _cachLock(RWLockWrapper::CreateRWLock()),
+ _previousAddress(),
+ _previousIP(),
+ _previousIPSize(0),
+ _previousSourcePort(0),
+ _filterIPAddress(),
+ _rtpFilterPort(0),
+ _rtcpFilterPort(0),
+ _packetCallback(0)
+{
+ memset(&_remoteRTPAddr, 0, sizeof(_remoteRTPAddr));
+ memset(&_remoteRTCPAddr, 0, sizeof(_remoteRTCPAddr));
+ memset(&_localRTPAddr, 0, sizeof(_localRTPAddr));
+ memset(&_localRTCPAddr, 0, sizeof(_localRTCPAddr));
+
+ memset(_fromIP, 0, sizeof(_fromIP));
+ memset(_destIP, 0, sizeof(_destIP));
+ memset(_localIP, 0, sizeof(_localIP));
+ memset(_localMulticastIP, 0, sizeof(_localMulticastIP));
+
+ memset(&_filterIPAddress, 0, sizeof(_filterIPAddress));
+
+ WEBRTC_TRACE(kTraceMemory, kTraceTransport, id, "%s created", __FUNCTION__);
+}
+
+UdpTransportImpl::~UdpTransportImpl()
+{
+ CloseSendSockets();
+ CloseReceiveSockets();
+ delete _crit;
+ delete _critFilter;
+ delete _critPacketCallback;
+ delete _cachLock;
+ delete _socket_creator;
+
+ WEBRTC_TRACE(kTraceMemory, kTraceTransport, _id, "%s deleted",
+ __FUNCTION__);
+}
+
+UdpTransport::ErrorCode UdpTransportImpl::LastError() const
+{
+ return _lastError;
+}
+
+bool SameAddress(const SocketAddress& address1, const SocketAddress& address2)
+{
+ return (memcmp(&address1,&address2,sizeof(address1)) == 0);
+}
+
+void UdpTransportImpl::GetCachedAddress(char* ip,
+ uint32_t& ipSize,
+ uint16_t& sourcePort)
+{
+ const uint32_t originalIPSize = ipSize;
+ // If the incoming string is too small, fill it as much as there is room
+ // for. Make sure that there is room for the '\0' character.
+ ipSize = (ipSize - 1 < _previousIPSize) ? ipSize - 1 : _previousIPSize;
+ memcpy(ip,_previousIP,sizeof(int8_t)*(ipSize + 1));
+ ip[originalIPSize - 1] = '\0';
+ sourcePort = _previousSourcePort;
+}
+
+int32_t UdpTransportImpl::IPAddressCached(const SocketAddress& address,
+ char* ip,
+ uint32_t& ipSize,
+ uint16_t& sourcePort)
+{
+ {
+ ReadLockScoped rl(*_cachLock);
+ // Check if the old address can be re-used (is the same).
+ if(SameAddress(address,_previousAddress))
+ {
+ GetCachedAddress(ip,ipSize,sourcePort);
+ return 0;
+ }
+ }
+ // Get the new address and store it.
+ WriteLockScoped wl(*_cachLock);
+ ipSize = kIpAddressVersion6Length;
+ if(IPAddress(address,_previousIP,ipSize,_previousSourcePort) != 0)
+ {
+ return -1;
+ }
+ _previousIPSize = ipSize;
+ memcpy(&_previousAddress, &address, sizeof(address));
+ // Address has been cached at this point.
+ GetCachedAddress(ip,ipSize,sourcePort);
+ return 0;
+}
+
+int32_t UdpTransportImpl::InitializeReceiveSockets(
+ UdpTransportData* const packetCallback,
+ const uint16_t portnr,
+ const char* ip,
+ const char* multicastIpAddr,
+ const uint16_t rtcpPort)
+{
+ {
+ CriticalSectionScoped cs(_critPacketCallback);
+ _packetCallback = packetCallback;
+
+ if(packetCallback == NULL)
+ {
+ WEBRTC_TRACE(kTraceStateInfo, kTraceTransport, _id,
+ "Closing down receive sockets");
+ return 0;
+ }
+ }
+
+ CriticalSectionScoped cs(_crit);
+ CloseReceiveSockets();
+
+ if(portnr == 0)
+ {
+ // TODO (hellner): why not just fail here?
+ if(_destPort == 0)
+ {
+ WEBRTC_TRACE(kTraceError, kTraceTransport, _id,
+ "InitializeReceiveSockets port 0 not allowed");
+ _lastError = kPortInvalid;
+ return -1;
+ }
+ _localPort = _destPort;
+ } else {
+ _localPort = portnr;
+ }
+ if(rtcpPort)
+ {
+ _localPortRTCP = rtcpPort;
+ }else {
+ _localPortRTCP = _localPort + 1;
+ WEBRTC_TRACE(
+ kTraceStateInfo,
+ kTraceTransport,
+ _id,
+ "InitializeReceiveSockets RTCP port not configured using RTP\
+ port+1=%d",
+ _localPortRTCP);
+ }
+
+ if(ip)
+ {
+ if(IsIpAddressValid(ip,IpV6Enabled()))
+ {
+ strncpy(_localIP, ip,kIpAddressVersion6Length);
+ } else
+ {
+ WEBRTC_TRACE(kTraceError, kTraceTransport, _id,
+ "InitializeReceiveSockets invalid IP address");
+ _lastError = kIpAddressInvalid;
+ return -1;
+ }
+ }else
+ {
+ // Don't bind to a specific IP address.
+ if(! IpV6Enabled())
+ {
+ strncpy(_localIP, "0.0.0.0",16);
+ } else
+ {
+ strncpy(_localIP, "0000:0000:0000:0000:0000:0000:0000:0000",
+ kIpAddressVersion6Length);
+ }
+ }
+ if(multicastIpAddr && !IpV6Enabled())
+ {
+ if(IsIpAddressValid(multicastIpAddr,IpV6Enabled()))
+ {
+ strncpy(_localMulticastIP, multicastIpAddr,
+ kIpAddressVersion6Length);
+ } else
+ {
+ WEBRTC_TRACE(kTraceError, kTraceTransport, _id,
+ "InitializeReceiveSockets invalid IP address");
+ _lastError = kIpAddressInvalid;
+ return -1;
+ }
+ }
+ if(_mgr == NULL)
+ {
+ WEBRTC_TRACE(kTraceError, kTraceTransport, _id,
+ "InitializeReceiveSockets no socket manager");
+ return -1;
+ }
+
+ _useSetSockOpt=false;
+ _tos=0;
+ _pcp=0;
+
+ _ptrRtpSocket = _socket_creator->CreateSocket(_id, _mgr, this,
+ IncomingRTPCallback,
+ IpV6Enabled(), false);
+
+ _ptrRtcpSocket = _socket_creator->CreateSocket(_id, _mgr, this,
+ IncomingRTCPCallback,
+ IpV6Enabled(), false);
+
+ ErrorCode retVal = BindLocalRTPSocket();
+ if(retVal != kNoSocketError)
+ {
+ WEBRTC_TRACE(kTraceError, kTraceTransport, _id,
+ "InitializeReceiveSockets faild to bind RTP socket");
+ _lastError = retVal;
+ CloseReceiveSockets();
+ return -1;
+ }
+ retVal = BindLocalRTCPSocket();
+ if(retVal != kNoSocketError)
+ {
+ _lastError = retVal;
+ WEBRTC_TRACE(kTraceError, kTraceTransport, _id,
+ "InitializeReceiveSockets faild to bind RTCP socket");
+ CloseReceiveSockets();
+ return -1;
+ }
+ return 0;
+}
+
+int32_t UdpTransportImpl::ReceiveSocketInformation(
+ char ipAddr[kIpAddressVersion6Length],
+ uint16_t& rtpPort,
+ uint16_t& rtcpPort,
+ char multicastIpAddr[kIpAddressVersion6Length]) const
+{
+ CriticalSectionScoped cs(_crit);
+ rtpPort = _localPort;
+ rtcpPort = _localPortRTCP;
+ if (ipAddr)
+ {
+ strncpy(ipAddr, _localIP, IpV6Enabled() ?
+ UdpTransport::kIpAddressVersion6Length :
+ UdpTransport::kIpAddressVersion4Length);
+ }
+ if (multicastIpAddr)
+ {
+ strncpy(multicastIpAddr, _localMulticastIP, IpV6Enabled() ?
+ UdpTransport::kIpAddressVersion6Length :
+ UdpTransport::kIpAddressVersion4Length);
+ }
+ return 0;
+}
+
+int32_t UdpTransportImpl::SendSocketInformation(
+ char ipAddr[kIpAddressVersion6Length],
+ uint16_t& rtpPort,
+ uint16_t& rtcpPort) const
+{
+ CriticalSectionScoped cs(_crit);
+ rtpPort = _destPort;
+ rtcpPort = _destPortRTCP;
+ strncpy(ipAddr, _destIP, IpV6Enabled() ?
+ UdpTransport::kIpAddressVersion6Length :
+ UdpTransport::kIpAddressVersion4Length);
+ return 0;
+}
+
+int32_t UdpTransportImpl::RemoteSocketInformation(
+ char ipAddr[kIpAddressVersion6Length],
+ uint16_t& rtpPort,
+ uint16_t& rtcpPort) const
+{
+ CriticalSectionScoped cs(_crit);
+ rtpPort = _fromPort;
+ rtcpPort = _fromPortRTCP;
+ if(ipAddr)
+ {
+ strncpy(ipAddr, _fromIP, IpV6Enabled() ?
+ kIpAddressVersion6Length :
+ kIpAddressVersion4Length);
+ }
+ return 0;
+}
+
+int32_t UdpTransportImpl::FilterPorts(
+ uint16_t& rtpFilterPort,
+ uint16_t& rtcpFilterPort) const
+{
+ CriticalSectionScoped cs(_critFilter);
+ rtpFilterPort = _rtpFilterPort;
+ rtcpFilterPort = _rtcpFilterPort;
+ return 0;
+}
+
+int32_t UdpTransportImpl::SetQoS(bool QoS, int32_t serviceType,
+ uint32_t maxBitrate,
+ int32_t overrideDSCP, bool audio)
+{
+ if(QoS)
+ {
+ return EnableQoS(serviceType, audio, maxBitrate, overrideDSCP);
+ }else
+ {
+ return DisableQoS();
+ }
+}
+
+int32_t UdpTransportImpl::EnableQoS(int32_t serviceType,
+ bool audio, uint32_t maxBitrate,
+ int32_t overrideDSCP)
+{
+ if (_ipV6Enabled)
+ {
+ WEBRTC_TRACE(
+ kTraceError,
+ kTraceTransport,
+ _id,
+ "QOS is enabled but will be ignored since IPv6 is enabled");
+ _lastError = kQosError;
+ return -1;
+ }
+ if (_tos)
+ {
+ WEBRTC_TRACE(
+ kTraceError,
+ kTraceTransport,
+ _id,
+ "TOS already enabled, can't use TOS and QoS at the same time");
+ _lastError = kQosError;
+ return -1;
+ }
+ if (_pcp)
+ {
+ WEBRTC_TRACE(
+ kTraceError,
+ kTraceTransport,
+ _id,
+ "PCP already enabled, can't use PCP and QoS at the same time");
+ _lastError = kQosError;
+ return -1;
+ }
+ if(_destPort == 0)
+ {
+ WEBRTC_TRACE(
+ kTraceError,
+ kTraceTransport,
+ _id,
+ "QOS is enabled but not started since we have not yet configured\
+ the send destination");
+ return -1;
+ }
+ if(_qos)
+ {
+ if(_overrideDSCP == 0 && overrideDSCP != 0)
+ {
+ WEBRTC_TRACE(
+ kTraceError,
+ kTraceTransport,
+ _id,
+ "QOS is already enabled and overrideDSCP differs, not allowed");
+ return -1;
+ }
+ }
+ CriticalSectionScoped cs(_crit);
+
+ UdpSocketWrapper* rtpSock = _ptrSendRtpSocket ?
+ _ptrSendRtpSocket :
+ _ptrRtpSocket;
+ if (!rtpSock || !rtpSock->ValidHandle())
+ {
+ WEBRTC_TRACE(
+ kTraceError,
+ kTraceTransport,
+ _id,
+ "QOS is enabled but not started since we have not yet created the\
+ RTP socket");
+ return -1;
+ }
+ UdpSocketWrapper* rtcpSock = _ptrSendRtcpSocket ?
+ _ptrSendRtcpSocket :
+ _ptrRtcpSocket;
+ if (!rtcpSock || !rtcpSock->ValidHandle())
+ {
+ WEBRTC_TRACE(
+ kTraceError,
+ kTraceTransport,
+ _id,
+ "QOS is enabled but not started since we have not yet created the\
+ RTCP socket");
+ return -1;
+ }
+
+ // Minimum packet size in bytes for which the requested quality of service
+ // will be provided. The smallest RTP header is 12 byte.
+ const int32_t min_policed_size = 12;
+ // Max SDU, maximum packet size permitted or used in the traffic flow, in
+ // bytes.
+ const int32_t max_sdu_size = 1500;
+
+ // Enable QoS for RTP sockets.
+ if(maxBitrate)
+ {
+ // Note: 1 kbit is 125 bytes.
+ // Token Rate is typically set to the average bit rate from peak to
+ // peak.
+ // Bucket size is normally set to the largest average frame size.
+ if(audio)
+ {
+ WEBRTC_TRACE(kTraceStateInfo,
+ kTraceTransport,
+ _id,
+ "Enable QOS for audio with max bitrate:%d",
+ maxBitrate);
+
+ const int32_t token_rate = maxBitrate*125;
+ // The largest audio packets are 60ms frames. This is a fraction
+ // more than 16 packets/second. These 16 frames are sent, at max,
+ // at a bitrate of maxBitrate*125 -> 1 frame is maxBitrate*125/16 ~
+ // maxBitrate * 8.
+ const int32_t bucket_size = maxBitrate * 8;
+ const int32_t peek_bandwith = maxBitrate * 125;
+ if (!rtpSock->SetQos(serviceType, token_rate, bucket_size,
+ peek_bandwith, min_policed_size,
+ max_sdu_size, _remoteRTPAddr, overrideDSCP))
+ {
+ WEBRTC_TRACE(kTraceError, kTraceTransport, _id,
+ "QOS failed on the RTP socket");
+ _lastError = kQosError;
+ return -1;
+ }
+ }else
+ {
+ WEBRTC_TRACE(kTraceStateInfo, kTraceTransport, _id,
+ "Enable QOS for video with max bitrate:%d",
+ maxBitrate);
+
+ // Allow for a token rate that is twice that of the maximum bitrate
+ // (in bytes).
+ const int32_t token_rate = maxBitrate*250;
+ // largest average frame size (key frame size). Assuming that a
+ // keyframe is 25% of the bitrate during the second its sent
+ // Assume that a key frame is 25% of the bitrate the second that it
+ // is sent. The largest frame size is then maxBitrate* 125 * 0.25 ~
+ // 31.
+ const int32_t bucket_size = maxBitrate*31;
+ const int32_t peek_bandwith = maxBitrate*125;
+ if (!rtpSock->SetQos(serviceType, token_rate, bucket_size,
+ peek_bandwith, min_policed_size, max_sdu_size,
+ _remoteRTPAddr, overrideDSCP))
+ {
+ WEBRTC_TRACE(kTraceError, kTraceTransport, _id,
+ "QOS failed on the RTP socket");
+ _lastError = kQosError;
+ return -1;
+ }
+ }
+ } else if(audio)
+ {
+ // No max bitrate set. Audio.
+ WEBRTC_TRACE(kTraceStateInfo, kTraceTransport, _id,
+ "Enable QOS for audio with default max bitrate");
+
+ // Let max bitrate be 240kbit/s.
+ const int32_t token_rate = 30000;
+ const int32_t bucket_size = 2000;
+ const int32_t peek_bandwith = 30000;
+ if (!rtpSock->SetQos(serviceType, token_rate, bucket_size,
+ peek_bandwith, min_policed_size, max_sdu_size,
+ _remoteRTPAddr, overrideDSCP))
+ {
+ WEBRTC_TRACE(kTraceError, kTraceTransport, _id,
+ "QOS failed on the RTP socket");
+ _lastError = kQosError;
+ return -1;
+ }
+ }else
+ {
+ // No max bitrate set. Video.
+ WEBRTC_TRACE(kTraceStateInfo, kTraceTransport, _id,
+ "Enable QOS for video with default max bitrate");
+
+ // Let max bitrate be 10mbit/s.
+ const int32_t token_rate = 128000*10;
+ const int32_t bucket_size = 32000;
+ const int32_t peek_bandwith = 256000;
+ if (!rtpSock->SetQos(serviceType, token_rate, bucket_size,
+ peek_bandwith, min_policed_size, max_sdu_size,
+ _remoteRTPAddr, overrideDSCP))
+ {
+ WEBRTC_TRACE(kTraceError, kTraceTransport, _id,
+ "QOS failed on the RTP socket");
+ _lastError = kQosError;
+ return -1;
+ }
+ }
+
+ // Enable QoS for RTCP sockets.
+ // TODO (hellner): shouldn't RTCP be based on 5% of the maximum bandwidth?
+ if(audio)
+ {
+ const int32_t token_rate = 200;
+ const int32_t bucket_size = 200;
+ const int32_t peek_bandwith = 400;
+ if (!rtcpSock->SetQos(serviceType, token_rate, bucket_size,
+ peek_bandwith, min_policed_size, max_sdu_size,
+ _remoteRTCPAddr, overrideDSCP))
+ {
+ WEBRTC_TRACE(kTraceWarning, kTraceTransport, _id,
+ "QOS failed on the RTCP socket");
+ _lastError = kQosError;
+ }
+ }else
+ {
+ const int32_t token_rate = 5000;
+ const int32_t bucket_size = 100;
+ const int32_t peek_bandwith = 10000;
+ if (!rtcpSock->SetQos(serviceType, token_rate, bucket_size,
+ peek_bandwith, min_policed_size, max_sdu_size,
+ _remoteRTCPAddr, _overrideDSCP))
+ {
+ WEBRTC_TRACE(kTraceWarning, kTraceTransport, _id,
+ "QOS failed on the RTCP socket");
+ _lastError = kQosError;
+ }
+ }
+ _qos = true;
+ _serviceType = serviceType;
+ _maxBitrate = maxBitrate;
+ _overrideDSCP = overrideDSCP;
+ return 0;
+}
+
+int32_t UdpTransportImpl::DisableQoS()
+{
+ if(_qos == false)
+ {
+ return 0;
+ }
+ CriticalSectionScoped cs(_crit);
+
+ UdpSocketWrapper* rtpSock = (_ptrSendRtpSocket ?
+ _ptrSendRtpSocket : _ptrRtpSocket);
+ if (!rtpSock || !rtpSock->ValidHandle())
+ {
+ WEBRTC_TRACE(
+ kTraceError,
+ kTraceTransport,
+ _id,
+ "QOS is enabled but not started since we have not yet created the\
+ RTP socket");
+ return -1;
+ }
+ UdpSocketWrapper* rtcpSock = (_ptrSendRtcpSocket ?
+ _ptrSendRtcpSocket : _ptrRtcpSocket);
+ if (!rtcpSock || !rtcpSock->ValidHandle())
+ {
+ WEBRTC_TRACE(
+ kTraceError,
+ kTraceTransport,
+ _id,
+ "QOS is enabled but not started since we have not yet created the\
+ RTCP socket");
+ return -1;
+ }
+
+ const int32_t service_type = 0; // = SERVICETYPE_NOTRAFFIC
+ const int32_t not_specified = -1;
+ if (!rtpSock->SetQos(service_type, not_specified, not_specified,
+ not_specified, not_specified, not_specified,
+ _remoteRTPAddr, _overrideDSCP))
+ {
+ _lastError = kQosError;
+ return -1;
+ }
+ if (!rtcpSock->SetQos(service_type, not_specified, not_specified,
+ not_specified, not_specified, not_specified,
+ _remoteRTCPAddr,_overrideDSCP))
+ {
+ _lastError = kQosError;
+ }
+ _qos = false;
+ return 0;
+}
+
+int32_t UdpTransportImpl::QoS(bool& QoS, int32_t& serviceType,
+ int32_t& overrideDSCP) const
+{
+ CriticalSectionScoped cs(_crit);
+ QoS = _qos;
+ serviceType = _serviceType;
+ overrideDSCP = _overrideDSCP;
+ return 0;
+}
+
+int32_t UdpTransportImpl::SetToS(int32_t DSCP, bool useSetSockOpt)
+{
+ if (_qos)
+ {
+ WEBRTC_TRACE(kTraceError, kTraceTransport, _id, "QoS already enabled");
+ _lastError = kQosError;
+ return -1;
+ }
+ if (DSCP < 0 || DSCP > 63)
+ {
+ WEBRTC_TRACE(kTraceError, kTraceTransport, _id, "Invalid DSCP");
+ _lastError = kTosInvalid;
+ return -1;
+ }
+ if(_tos)
+ {
+ if(useSetSockOpt != _useSetSockOpt)
+ {
+ WEBRTC_TRACE(
+ kTraceError,
+ kTraceTransport,
+ _id,
+ "Can't switch SetSockOpt method without disabling TOS first");
+ _lastError = kTosInvalid;
+ return -1;
+ }
+ }
+ CriticalSectionScoped cs(_crit);
+ UdpSocketWrapper* rtpSock = NULL;
+ UdpSocketWrapper* rtcpSock = NULL;
+ if(_ptrSendRtpSocket)
+ {
+ rtpSock = _ptrSendRtpSocket;
+ }else
+ {
+ rtpSock = _ptrRtpSocket;
+ }
+ if (rtpSock == NULL)
+ {
+ _lastError = kSocketInvalid;
+ return -1;
+ }
+ if(!rtpSock->ValidHandle())
+ {
+ _lastError = kSocketInvalid;
+ return -1;
+ }
+ if(_ptrSendRtcpSocket)
+ {
+ rtcpSock = _ptrSendRtcpSocket;
+ }else
+ {
+ rtcpSock = _ptrRtcpSocket;
+ }
+ if (rtcpSock == NULL)
+ {
+ _lastError = kSocketInvalid;
+ return -1;
+ }
+ if(!rtcpSock->ValidHandle())
+ {
+ _lastError = kSocketInvalid;
+ return -1;
+ }
+
+ if (useSetSockOpt)
+ {
+#ifdef _WIN32
+ OSVERSIONINFO OsVersion;
+ OsVersion.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
+ GetVersionEx(&OsVersion);
+ // Disable QoS before setting ToS on Windows XP. This is done by closing
+ // and re-opening the sockets.
+ // TODO (hellner): why not just fail here and force the user to
+ // re-initialize sockets? Doing this may trick the user
+ // into thinking that the sockets are in a state which
+ // they aren't.
+ if (OsVersion.dwMajorVersion == 5 &&
+ OsVersion.dwMinorVersion == 1)
+ {
+ if(!_useSetSockOpt)
+ {
+ if(_ptrSendRtpSocket)
+ {
+ CloseSendSockets();
+ _ptrSendRtpSocket =
+ _socket_creator->CreateSocket(_id, _mgr, NULL,
+ NULL, IpV6Enabled(),
+ true);
+ _ptrSendRtcpSocket =
+ _socket_creator->CreateSocket(_id, _mgr, NULL,
+ NULL, IpV6Enabled(),
+ true);
+ rtpSock=_ptrSendRtpSocket;
+ rtcpSock=_ptrSendRtcpSocket;
+ ErrorCode retVal = BindRTPSendSocket();
+ if(retVal != kNoSocketError)
+ {
+ _lastError = retVal;
+ return -1;
+ }
+ retVal = BindRTCPSendSocket();
+ if(retVal != kNoSocketError)
+ {
+ _lastError = retVal;
+ return -1;
+ }
+ }
+ else
+ {
+ bool receiving=_receiving;
+ uint32_t noOfReceiveBuffers = 0;
+ if(receiving)
+ {
+ noOfReceiveBuffers=_ptrRtpSocket->ReceiveBuffers();
+ if(StopReceiving()!=0)
+ {
+ return -1;
+ }
+ }
+ CloseReceiveSockets();
+ _ptrRtpSocket = _socket_creator->CreateSocket(
+ _id, _mgr, this, IncomingRTPCallback, IpV6Enabled(),
+ true);
+ _ptrRtcpSocket = _socket_creator->CreateSocket(
+ _id, _mgr, this, IncomingRTCPCallback, IpV6Enabled(),
+ true);
+ rtpSock=_ptrRtpSocket;
+ rtcpSock=_ptrRtcpSocket;
+ ErrorCode retVal = BindLocalRTPSocket();
+ if(retVal != kNoSocketError)
+ {
+ _lastError = retVal;
+ return -1;
+ }
+ retVal = BindLocalRTCPSocket();
+ if(retVal != kNoSocketError)
+ {
+ _lastError = retVal;
+ return -1;
+ }
+ if(receiving)
+ {
+ if(StartReceiving(noOfReceiveBuffers) !=
+ kNoSocketError)
+ {
+ return -1;
+ }
+ }
+ }
+ }
+ }
+#endif // #ifdef _WIN32
+ WEBRTC_TRACE(kTraceDebug, kTraceTransport, _id,
+ "Setting TOS using SetSockopt");
+ int32_t TOSShifted = DSCP << 2;
+ if (!rtpSock->SetSockopt(IPPROTO_IP, IP_TOS,
+ (int8_t*) &TOSShifted, 4))
+ {
+ WEBRTC_TRACE(kTraceError, kTraceTransport, _id,
+ "Could not SetSockopt tos value on RTP socket");
+ _lastError = kTosInvalid;
+ return -1;
+ }
+ if (!rtcpSock->SetSockopt(IPPROTO_IP, IP_TOS,
+ (int8_t*) &TOSShifted, 4))
+ {
+ WEBRTC_TRACE(kTraceError, kTraceTransport, _id,
+ "Could not sSetSockopt tos value on RTCP socket");
+ _lastError = kTosInvalid;
+ return -1;
+ }
+ } else
+ {
+ WEBRTC_TRACE(kTraceDebug, kTraceTransport, _id,
+ "Setting TOS NOT using SetSockopt");
+ if (rtpSock->SetTOS(DSCP) != 0)
+ {
+ WEBRTC_TRACE(kTraceError, kTraceTransport, _id,
+ "Could not set tos value on RTP socket");
+ _lastError = kTosError;
+ return -1;
+ }
+ if (rtcpSock->SetTOS(DSCP) != 0)
+ {
+ WEBRTC_TRACE(kTraceError, kTraceTransport, _id,
+ "Could not set tos value on RTCP socket");
+ _lastError = kTosError;
+ return -1;
+ }
+ }
+ _useSetSockOpt = useSetSockOpt;
+ _tos = DSCP;
+ return 0;
+}
+
+int32_t UdpTransportImpl::ToS(int32_t& DSCP,
+ bool& useSetSockOpt) const
+{
+ CriticalSectionScoped cs(_crit);
+ DSCP = _tos;
+ useSetSockOpt = _useSetSockOpt;
+ return 0;
+}
+
+int32_t UdpTransportImpl::SetPCP(int32_t PCP)
+{
+
+ if (_qos)
+ {
+ WEBRTC_TRACE(kTraceError, kTraceTransport, _id, "QoS already enabled");
+ _lastError = kQosError;
+ return -1;
+ }
+ if ((PCP < 0) || (PCP > 7))
+ {
+ WEBRTC_TRACE(kTraceError, kTraceTransport, _id, "Invalid PCP");
+ _lastError = kPcpError;
+ return -1;
+ }
+
+ CriticalSectionScoped cs(_crit);
+ UdpSocketWrapper* rtpSock = NULL;
+ UdpSocketWrapper* rtcpSock = NULL;
+ if(_ptrSendRtpSocket)
+ {
+ rtpSock = _ptrSendRtpSocket;
+ }else
+ {
+ rtpSock = _ptrRtpSocket;
+ }
+ if (rtpSock == NULL)
+ {
+ _lastError = kSocketInvalid;
+ return -1;
+ }
+ if(!rtpSock->ValidHandle())
+ {
+ _lastError = kSocketInvalid;
+ return -1;
+ }
+ if(_ptrSendRtcpSocket)
+ {
+ rtcpSock = _ptrSendRtcpSocket;
+ }else
+ {
+ rtcpSock = _ptrRtcpSocket;
+ }
+ if (rtcpSock == NULL)
+ {
+ _lastError = kSocketInvalid;
+ return -1;
+ }
+ if(!rtcpSock->ValidHandle())
+ {
+ _lastError = kSocketInvalid;
+ return -1;
+ }
+
+#if defined(_WIN32)
+ if (rtpSock->SetPCP(PCP) != 0)
+ {
+ WEBRTC_TRACE(kTraceError, kTraceTransport, _id,
+ "Could not set PCP value on RTP socket");
+ _lastError = kPcpError;
+ return -1;
+ }
+ if (rtcpSock->SetPCP(PCP) != 0)
+ {
+ WEBRTC_TRACE(kTraceError, kTraceTransport, _id,
+ "Could not set PCP value on RTCP socket");
+ _lastError = kPcpError;
+ return -1;
+ }
+
+#elif defined(WEBRTC_LINUX)
+ if (!rtpSock->SetSockopt(SOL_SOCKET, SO_PRIORITY, (int8_t*) &PCP,
+ sizeof(PCP)))
+ {
+ WEBRTC_TRACE(kTraceError, kTraceTransport, _id,
+ "Could not SetSockopt PCP value on RTP socket");
+ _lastError = kPcpError;
+ return -1;
+ }
+ if (!rtcpSock->SetSockopt(SOL_SOCKET, SO_PRIORITY, (int8_t*) &PCP,
+ sizeof(PCP)))
+ {
+ WEBRTC_TRACE(kTraceError, kTraceTransport, _id,
+ "Could not SetSockopt PCP value on RTCP socket");
+ _lastError = kPcpError;
+ return -1;
+ }
+#else
+ // Not supported on other platforms (WEBRTC_MAC)
+ _lastError = kPcpError;
+ return -1;
+#endif
+ _pcp = PCP;
+ return 0;
+}
+
+int32_t UdpTransportImpl::PCP(int32_t& PCP) const
+{
+ CriticalSectionScoped cs(_crit);
+ PCP = _pcp;
+ return 0;
+}
+
+bool UdpTransportImpl::SetSockOptUsed()
+{
+ return _useSetSockOpt;
+}
+
+int32_t UdpTransportImpl::EnableIpV6() {
+
+ CriticalSectionScoped cs(_crit);
+ const bool initialized = (_ptrSendRtpSocket || _ptrRtpSocket);
+
+ if (_ipV6Enabled) {
+ return 0;
+ }
+ if (initialized) {
+ _lastError = kIpVersion6Error;
+ return -1;
+ }
+ _ipV6Enabled = true;
+ return 0;
+}
+
+int32_t UdpTransportImpl::FilterIP(
+ char filterIPAddress[kIpAddressVersion6Length]) const
+{
+
+ if(filterIPAddress == NULL)
+ {
+ WEBRTC_TRACE(kTraceError, kTraceTransport, _id,
+ "FilterIP: Invalid argument");
+ return -1;
+ }
+ if(_filterIPAddress._sockaddr_storage.sin_family == 0)
+ {
+ WEBRTC_TRACE(kTraceError, kTraceTransport, _id, "No Filter configured");
+ return -1;
+ }
+ CriticalSectionScoped cs(_critFilter);
+ uint32_t ipSize = kIpAddressVersion6Length;
+ uint16_t sourcePort;
+ return IPAddress(_filterIPAddress, filterIPAddress, ipSize, sourcePort);
+}
+
+int32_t UdpTransportImpl::SetFilterIP(
+ const char filterIPAddress[kIpAddressVersion6Length])
+{
+ if(filterIPAddress == NULL)
+ {
+ memset(&_filterIPAddress, 0, sizeof(_filterIPAddress));
+ WEBRTC_TRACE(kTraceDebug, kTraceTransport, _id, "Filter IP reset");
+ return 0;
+ }
+ CriticalSectionScoped cs(_critFilter);
+ if (_ipV6Enabled)
+ {
+ _filterIPAddress._sockaddr_storage.sin_family = AF_INET6;
+
+ if (InetPresentationToNumeric(
+ AF_INET6,
+ filterIPAddress,
+ &_filterIPAddress._sockaddr_in6.sin6_addr) < 0)
+ {
+ WEBRTC_TRACE(kTraceError, kTraceTransport, _id, "Failed to set\
+ filter IP for IPv6");
+ _lastError = FILTER_ERROR;
+ return -1;
+ }
+ }
+ else
+ {
+ _filterIPAddress._sockaddr_storage.sin_family = AF_INET;
+
+ if(InetPresentationToNumeric(
+ AF_INET,
+ filterIPAddress,
+ &_filterIPAddress._sockaddr_in.sin_addr) < 0)
+ {
+ WEBRTC_TRACE(kTraceError, kTraceTransport, _id,
+ "Failed to set filter IP for IPv4");
+ _lastError = FILTER_ERROR;
+ return -1;
+ }
+ }
+ WEBRTC_TRACE(kTraceDebug, kTraceTransport, _id, "Filter IP set");
+ return 0;
+}
+
+int32_t UdpTransportImpl::SetFilterPorts(uint16_t rtpFilterPort,
+ uint16_t rtcpFilterPort)
+{
+ CriticalSectionScoped cs(_critFilter);
+ _rtpFilterPort = rtpFilterPort;
+ _rtcpFilterPort = rtcpFilterPort;
+ return 0;
+}
+
+bool UdpTransportImpl::SendSocketsInitialized() const
+{
+ CriticalSectionScoped cs(_crit);
+ if(_ptrSendRtpSocket)
+ {
+ return true;
+ }
+ if(_destPort !=0)
+ {
+ return true;
+ }
+ return false;
+}
+
+bool UdpTransportImpl::ReceiveSocketsInitialized() const
+{
+ if(_ptrRtpSocket)
+ {
+ return true;
+ }
+ return false;
+}
+
+bool UdpTransportImpl::SourcePortsInitialized() const
+{
+ if(_ptrSendRtpSocket)
+ {
+ return true;
+ }
+ return false;
+}
+
+bool UdpTransportImpl::IpV6Enabled() const
+{
+ WEBRTC_TRACE(kTraceStream, kTraceTransport, _id, "%s", __FUNCTION__);
+ return _ipV6Enabled;
+}
+
+void UdpTransportImpl::BuildRemoteRTPAddr()
+{
+ if(_ipV6Enabled)
+ {
+#ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
+ _remoteRTPAddr.sin_length = 0;
+ _remoteRTPAddr.sin_family = PF_INET6;
+#else
+ _remoteRTPAddr._sockaddr_storage.sin_family = PF_INET6;
+#endif
+
+ _remoteRTPAddr._sockaddr_in6.sin6_flowinfo=0;
+ _remoteRTPAddr._sockaddr_in6.sin6_scope_id=0;
+ _remoteRTPAddr._sockaddr_in6.sin6_port = Htons(_destPort);
+ InetPresentationToNumeric(AF_INET6,_destIP,
+ &_remoteRTPAddr._sockaddr_in6.sin6_addr);
+ } else
+ {
+#ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
+ _remoteRTPAddr.sin_length = 0;
+ _remoteRTPAddr.sin_family = PF_INET;
+#else
+ _remoteRTPAddr._sockaddr_storage.sin_family = PF_INET;
+#endif
+ _remoteRTPAddr._sockaddr_in.sin_port = Htons(_destPort);
+ _remoteRTPAddr._sockaddr_in.sin_addr = InetAddrIPV4(_destIP);
+ }
+}
+
+void UdpTransportImpl::BuildRemoteRTCPAddr()
+{
+ if(_ipV6Enabled)
+ {
+#ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
+ _remoteRTCPAddr.sin_length = 0;
+ _remoteRTCPAddr.sin_family = PF_INET6;
+#else
+ _remoteRTCPAddr._sockaddr_storage.sin_family = PF_INET6;
+#endif
+
+ _remoteRTCPAddr._sockaddr_in6.sin6_flowinfo=0;
+ _remoteRTCPAddr._sockaddr_in6.sin6_scope_id=0;
+ _remoteRTCPAddr._sockaddr_in6.sin6_port = Htons(_destPortRTCP);
+ InetPresentationToNumeric(AF_INET6,_destIP,
+ &_remoteRTCPAddr._sockaddr_in6.sin6_addr);
+
+ } else
+ {
+#ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
+ _remoteRTCPAddr.sin_length = 0;
+ _remoteRTCPAddr.sin_family = PF_INET;
+#else
+ _remoteRTCPAddr._sockaddr_storage.sin_family = PF_INET;
+#endif
+ _remoteRTCPAddr._sockaddr_in.sin_port = Htons(_destPortRTCP);
+ _remoteRTCPAddr._sockaddr_in.sin_addr= InetAddrIPV4(_destIP);
+ }
+}
+
+UdpTransportImpl::ErrorCode UdpTransportImpl::BindRTPSendSocket()
+{
+ if(!_ptrSendRtpSocket)
+ {
+ return kSocketInvalid;
+ }
+ if(!_ptrSendRtpSocket->ValidHandle())
+ {
+ return kIpAddressInvalid;
+ }
+ if(_ipV6Enabled)
+ {
+#ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
+ _localRTPAddr.sin_length = 0;
+ _localRTPAddr.sin_family = PF_INET6;
+#else
+ _localRTPAddr._sockaddr_storage.sin_family = PF_INET6;
+#endif
+ _localRTPAddr._sockaddr_in6.sin6_flowinfo=0;
+ _localRTPAddr._sockaddr_in6.sin6_scope_id=0;
+ _localRTPAddr._sockaddr_in6.sin6_addr.Version6AddressUnion._s6_u32[0] =
+ 0; // = INADDR_ANY
+ _localRTPAddr._sockaddr_in6.sin6_addr.Version6AddressUnion._s6_u32[1] =
+ 0;
+ _localRTPAddr._sockaddr_in6.sin6_addr.Version6AddressUnion._s6_u32[2] =
+ 0;
+ _localRTPAddr._sockaddr_in6.sin6_addr.Version6AddressUnion._s6_u32[3] =
+ 0;
+ _localRTPAddr._sockaddr_in6.sin6_port = Htons(_srcPort);
+ if(_ptrSendRtpSocket->Bind(_localRTPAddr) == false)
+ {
+ WEBRTC_TRACE(kTraceWarning, kTraceTransport, _id,
+ "Failed to bind to port:%d ", _srcPort);
+ return kFailedToBindPort;
+ }
+
+ } else {
+#ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
+ _localRTPAddr.sin_length = 0;
+ _localRTPAddr.sin_family = PF_INET;
+#else
+ _localRTPAddr._sockaddr_storage.sin_family = PF_INET;
+#endif
+ _localRTPAddr._sockaddr_in.sin_addr = 0;
+ _localRTPAddr._sockaddr_in.sin_port = Htons(_srcPort);
+ if(_ptrSendRtpSocket->Bind(_localRTPAddr) == false)
+ {
+ WEBRTC_TRACE(kTraceWarning, kTraceTransport, _id,
+ "Failed to bind to port:%d ", _srcPort);
+ return kFailedToBindPort;
+ }
+ }
+ return kNoSocketError;
+}
+
+UdpTransportImpl::ErrorCode UdpTransportImpl::BindRTCPSendSocket()
+{
+ if(!_ptrSendRtcpSocket)
+ {
+ return kSocketInvalid;
+ }
+
+ if(_ipV6Enabled)
+ {
+#ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
+ _localRTCPAddr.sin_length = 0;
+ _localRTCPAddr.sin_family = PF_INET6;
+#else
+ _localRTCPAddr._sockaddr_storage.sin_family = PF_INET6;
+#endif
+ _localRTCPAddr._sockaddr_in6.sin6_flowinfo=0;
+ _localRTCPAddr._sockaddr_in6.sin6_scope_id=0;
+ _localRTCPAddr._sockaddr_in6.sin6_addr.Version6AddressUnion._s6_u32[0] =
+ 0; // = INADDR_ANY
+ _localRTCPAddr._sockaddr_in6.sin6_addr.Version6AddressUnion._s6_u32[1] =
+ 0;
+ _localRTCPAddr._sockaddr_in6.sin6_addr.Version6AddressUnion._s6_u32[2] =
+ 0;
+ _localRTCPAddr._sockaddr_in6.sin6_addr.Version6AddressUnion._s6_u32[3] =
+ 0;
+ _localRTCPAddr._sockaddr_in6.sin6_port = Htons(_srcPortRTCP);
+ if(_ptrSendRtcpSocket->Bind(_localRTCPAddr) == false)
+ {
+ WEBRTC_TRACE(kTraceWarning, kTraceTransport, _id,
+ "Failed to bind to port:%d ", _srcPortRTCP);
+ return kFailedToBindPort;
+ }
+ } else {
+#ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
+ _localRTCPAddr.sin_length = 0;
+ _localRTCPAddr.sin_family = PF_INET;
+#else
+ _localRTCPAddr._sockaddr_storage.sin_family = PF_INET;
+#endif
+ _localRTCPAddr._sockaddr_in.sin_addr= 0;
+ _localRTCPAddr._sockaddr_in.sin_port = Htons(_srcPortRTCP);
+ if(_ptrSendRtcpSocket->Bind(_localRTCPAddr) == false)
+ {
+ WEBRTC_TRACE(kTraceWarning, kTraceTransport, _id,
+ "Failed to bind to port:%d ", _srcPortRTCP);
+ return kFailedToBindPort;
+ }
+ }
+ return kNoSocketError;
+}
+
+UdpTransportImpl::ErrorCode UdpTransportImpl::BindLocalRTPSocket()
+{
+ if(!_ptrRtpSocket)
+ {
+ return kSocketInvalid;
+ }
+ if(!IpV6Enabled())
+ {
+ SocketAddress recAddr;
+ memset(&recAddr, 0, sizeof(SocketAddress));
+ recAddr._sockaddr_storage.sin_family = AF_INET;
+#ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
+ recAddr.sin_length = 0;
+ recAddr.sin_family = PF_INET;
+#else
+ recAddr._sockaddr_storage.sin_family = PF_INET;
+#endif
+ recAddr._sockaddr_in.sin_addr = InetAddrIPV4(_localIP);
+ recAddr._sockaddr_in.sin_port = Htons(_localPort);
+
+ if (!_ptrRtpSocket->Bind(recAddr))
+ {
+ WEBRTC_TRACE(kTraceWarning, kTraceTransport, _id,
+ "Failed to bind to port:%d ", _localPort);
+ return kFailedToBindPort;
+ }
+ }
+ else
+ {
+ SocketAddress stLclName;
+#ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
+ stLclName.sin_lenght = 0;
+ stLclName.sin_family = PF_INET6;
+#else
+ stLclName._sockaddr_storage.sin_family = PF_INET6;
+#endif
+ InetPresentationToNumeric(AF_INET6,_localIP,
+ &stLclName._sockaddr_in6.sin6_addr);
+ stLclName._sockaddr_in6.sin6_port = Htons(_localPort);
+ stLclName._sockaddr_in6.sin6_flowinfo = 0;
+ stLclName._sockaddr_in6.sin6_scope_id = 0;
+
+ if (!_ptrRtpSocket->Bind(stLclName))
+ {
+ WEBRTC_TRACE(kTraceWarning, kTraceTransport, _id,
+ "Failed to bind to port:%d ", _localPort);
+ return kFailedToBindPort;
+ }
+ }
+
+ if(_localMulticastIP[0] != 0)
+ {
+ // Join the multicast group from which to receive datagrams.
+ struct ip_mreq mreq;
+ mreq.imr_multiaddr.s_addr = InetAddrIPV4(_localMulticastIP);
+ mreq.imr_interface.s_addr = INADDR_ANY;
+
+ if (!_ptrRtpSocket->SetSockopt(IPPROTO_IP,IP_ADD_MEMBERSHIP,
+ (int8_t*)&mreq,sizeof (mreq)))
+ {
+ WEBRTC_TRACE(
+ kTraceError,
+ kTraceTransport,
+ _id,
+ "setsockopt() for multicast failed, not closing socket");
+ }else
+ {
+ WEBRTC_TRACE(kTraceInfo, kTraceTransport, _id,
+ "multicast group successfully joined");
+ }
+ }
+ return kNoSocketError;
+}
+
+UdpTransportImpl::ErrorCode UdpTransportImpl::BindLocalRTCPSocket()
+{
+ if(!_ptrRtcpSocket)
+ {
+ return kSocketInvalid;
+ }
+ if(! IpV6Enabled())
+ {
+ SocketAddress recAddr;
+ memset(&recAddr, 0, sizeof(SocketAddress));
+#ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
+ recAddr.sin_length = 0;
+ recAddr.sin_family = AF_INET;
+#else
+ recAddr._sockaddr_storage.sin_family = AF_INET;
+#endif
+ recAddr._sockaddr_in.sin_addr = InetAddrIPV4(_localIP);
+ recAddr._sockaddr_in.sin_port = Htons(_localPortRTCP);
+
+ if (!_ptrRtcpSocket->Bind(recAddr))
+ {
+ WEBRTC_TRACE(kTraceWarning, kTraceTransport, _id,
+ "Failed to bind to port:%d ", _localPortRTCP);
+ return kFailedToBindPort;
+ }
+ }
+ else
+ {
+ SocketAddress stLclName;
+#ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
+ stLclName.sin_length = 0;
+ stLclName.sin_family = PF_INET6;
+#else
+ stLclName._sockaddr_storage.sin_family = PF_INET6;
+#endif
+ stLclName._sockaddr_in6.sin6_flowinfo = 0;
+ stLclName._sockaddr_in6.sin6_scope_id = 0;
+ stLclName._sockaddr_in6.sin6_port = Htons(_localPortRTCP);
+
+ InetPresentationToNumeric(AF_INET6,_localIP,
+ &stLclName._sockaddr_in6.sin6_addr);
+ if (!_ptrRtcpSocket->Bind(stLclName))
+ {
+ WEBRTC_TRACE(kTraceWarning, kTraceTransport, _id,
+ "Failed to bind to port:%d ", _localPortRTCP);
+ return kFailedToBindPort;
+ }
+ }
+ if(_localMulticastIP[0] != 0)
+ {
+ // Join the multicast group from which to receive datagrams.
+ struct ip_mreq mreq;
+ mreq.imr_multiaddr.s_addr = InetAddrIPV4(_localMulticastIP);
+ mreq.imr_interface.s_addr = INADDR_ANY;
+
+ if (!_ptrRtcpSocket->SetSockopt(IPPROTO_IP,IP_ADD_MEMBERSHIP,
+ (int8_t*)&mreq,sizeof (mreq)))
+ {
+ WEBRTC_TRACE(
+ kTraceError,
+ kTraceTransport,
+ _id,
+ "setsockopt() for multicast failed, not closing socket");
+ }else
+ {
+ WEBRTC_TRACE(kTraceInfo, kTraceTransport, _id,
+ "multicast group successfully joined");
+ }
+ }
+ return kNoSocketError;
+}
+
+int32_t UdpTransportImpl::InitializeSourcePorts(uint16_t rtpPort,
+ uint16_t rtcpPort)
+{
+
+ if(rtpPort == 0)
+ {
+ WEBRTC_TRACE(kTraceError, kTraceTransport, _id,
+ "InitializeSourcePorts port 0 not allowed");
+ _lastError = kPortInvalid;
+ return -1;
+ }
+
+ CriticalSectionScoped cs(_crit);
+
+ CloseSendSockets();
+
+ if(_mgr == NULL)
+ {
+ return -1;
+ }
+
+ _srcPort = rtpPort;
+ if(rtcpPort == 0)
+ {
+ _srcPortRTCP = rtpPort+1;
+ } else
+ {
+ _srcPortRTCP = rtcpPort;
+ }
+ _useSetSockOpt =false;
+ _tos=0;
+ _pcp=0;
+
+ _ptrSendRtpSocket = _socket_creator->CreateSocket(_id, _mgr, NULL, NULL,
+ IpV6Enabled(), false);
+ _ptrSendRtcpSocket = _socket_creator->CreateSocket(_id, _mgr, NULL, NULL,
+ IpV6Enabled(), false);
+
+ ErrorCode retVal = BindRTPSendSocket();
+ if(retVal != kNoSocketError)
+ {
+ _lastError = retVal;
+ return -1;
+ }
+ retVal = BindRTCPSendSocket();
+ if(retVal != kNoSocketError)
+ {
+ _lastError = retVal;
+ return -1;
+ }
+ return 0;
+}
+
+int32_t UdpTransportImpl::SourcePorts(uint16_t& rtpPort,
+ uint16_t& rtcpPort) const
+{
+ CriticalSectionScoped cs(_crit);
+
+ rtpPort = (_srcPort != 0) ? _srcPort : _localPort;
+ rtcpPort = (_srcPortRTCP != 0) ? _srcPortRTCP : _localPortRTCP;
+ return 0;
+}
+
+
+#ifdef _WIN32
+int32_t UdpTransportImpl::StartReceiving(uint32_t numberOfSocketBuffers)
+#else
+int32_t UdpTransportImpl::StartReceiving(uint32_t /*numberOfSocketBuffers*/)
+#endif
+{
+ CriticalSectionScoped cs(_crit);
+ if(_receiving)
+ {
+ return 0;
+ }
+ if(_ptrRtpSocket)
+ {
+#ifdef _WIN32
+ if(!_ptrRtpSocket->StartReceiving(numberOfSocketBuffers))
+#else
+ if(!_ptrRtpSocket->StartReceiving())
+#endif
+ {
+ WEBRTC_TRACE(kTraceError, kTraceTransport, _id,
+ "Failed to start receive on RTP socket");
+ _lastError = kStartReceiveError;
+ return -1;
+ }
+ }
+ if(_ptrRtcpSocket)
+ {
+ if(!_ptrRtcpSocket->StartReceiving())
+ {
+ WEBRTC_TRACE(kTraceError, kTraceTransport, _id,
+ "Failed to start receive on RTCP socket");
+ _lastError = kStartReceiveError;
+ return -1;
+ }
+ }
+ if( _ptrRtpSocket == NULL &&
+ _ptrRtcpSocket == NULL)
+ {
+ WEBRTC_TRACE(kTraceError, kTraceTransport, _id,
+ "Failed to StartReceiving, no socket initialized");
+ _lastError = kStartReceiveError;
+ return -1;
+ }
+ _receiving = true;
+ return 0;
+}
+
+bool UdpTransportImpl::Receiving() const
+{
+ return _receiving;
+}
+
+int32_t UdpTransportImpl::StopReceiving()
+{
+
+ CriticalSectionScoped cs(_crit);
+
+ _receiving = false;
+
+ if (_ptrRtpSocket)
+ {
+ if (!_ptrRtpSocket->StopReceiving())
+ {
+ WEBRTC_TRACE(kTraceError, kTraceTransport, _id,
+ "Failed to stop receiving on RTP socket");
+ _lastError = kStopReceiveError;
+ return -1;
+ }
+ }
+ if (_ptrRtcpSocket)
+ {
+ if (!_ptrRtcpSocket->StopReceiving())
+ {
+ WEBRTC_TRACE(kTraceError, kTraceTransport, _id,
+ "Failed to stop receiving on RTCP socket");
+ _lastError = kStopReceiveError;
+ return -1;
+ }
+ }
+ return 0;
+}
+
+int32_t UdpTransportImpl::InitializeSendSockets(
+ const char* ipaddr,
+ const uint16_t rtpPort,
+ const uint16_t rtcpPort)
+{
+ {
+ CriticalSectionScoped cs(_crit);
+ _destPort = rtpPort;
+ if(rtcpPort == 0)
+ {
+ _destPortRTCP = _destPort+1;
+ } else
+ {
+ _destPortRTCP = rtcpPort;
+ }
+
+ if(ipaddr == NULL)
+ {
+ if (!IsIpAddressValid(_destIP, IpV6Enabled()))
+ {
+ _destPort = 0;
+ _destPortRTCP = 0;
+ _lastError = kIpAddressInvalid;
+ return -1;
+ }
+ } else
+ {
+ if (IsIpAddressValid(ipaddr, IpV6Enabled()))
+ {
+ strncpy(
+ _destIP,
+ ipaddr,
+ IpV6Enabled() ? kIpAddressVersion6Length :
+ kIpAddressVersion4Length);
+ } else {
+ _destPort = 0;
+ _destPortRTCP = 0;
+ _lastError = kIpAddressInvalid;
+ return -1;
+ }
+ }
+ BuildRemoteRTPAddr();
+ BuildRemoteRTCPAddr();
+ }
+
+ if (_ipV6Enabled)
+ {
+ if (_qos)
+ {
+ WEBRTC_TRACE(
+ kTraceWarning,
+ kTraceTransport,
+ _id,
+ "QOS is enabled but will be ignored since IPv6 is enabled");
+ }
+ }else
+ {
+ // TODO (grunell): Multicast support is experimantal.
+
+ // Put the first digit of the remote address in val.
+ int32_t val = ntohl(_remoteRTPAddr._sockaddr_in.sin_addr)>> 24;
+
+ if((val > 223) && (val < 240))
+ {
+ // Multicast address.
+ CriticalSectionScoped cs(_crit);
+
+ UdpSocketWrapper* rtpSock = (_ptrSendRtpSocket ?
+ _ptrSendRtpSocket : _ptrRtpSocket);
+ if (!rtpSock || !rtpSock->ValidHandle())
+ {
+ _lastError = kSocketInvalid;
+ return -1;
+ }
+ UdpSocketWrapper* rtcpSock = (_ptrSendRtcpSocket ?
+ _ptrSendRtcpSocket : _ptrRtcpSocket);
+ if (!rtcpSock || !rtcpSock->ValidHandle())
+ {
+ _lastError = kSocketInvalid;
+ return -1;
+ }
+
+ // Set Time To Live to same region
+ int32_t iOptVal = 64;
+ if (!rtpSock->SetSockopt(IPPROTO_IP, IP_MULTICAST_TTL,
+ (int8_t*)&iOptVal,
+ sizeof (int32_t)))
+ {
+ WEBRTC_TRACE(kTraceError, kTraceTransport, _id,
+ "setsockopt for multicast error on RTP socket");
+ _ptrRtpSocket->CloseBlocking();
+ _ptrRtpSocket = NULL;
+ _lastError = kMulticastAddressInvalid;
+ return -1;
+ }
+ if (!rtcpSock->SetSockopt(IPPROTO_IP, IP_MULTICAST_TTL,
+ (int8_t*)&iOptVal,
+ sizeof (int32_t)))
+ {
+ WEBRTC_TRACE(kTraceError, kTraceTransport, _id,
+ "setsockopt for multicast error on RTCP socket");
+ _ptrRtpSocket->CloseBlocking();
+ _ptrRtpSocket = NULL;
+ _lastError = kMulticastAddressInvalid;
+ return -1;
+ }
+ }
+ }
+ return 0;
+}
+
+void UdpTransportImpl::BuildSockaddrIn(uint16_t portnr,
+ const char* ip,
+ SocketAddress& remoteAddr) const
+{
+ if(_ipV6Enabled)
+ {
+#ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
+ remoteAddr.sin_length = 0;
+ remoteAddr.sin_family = PF_INET6;
+#else
+ remoteAddr._sockaddr_storage.sin_family = PF_INET6;
+#endif
+ remoteAddr._sockaddr_in6.sin6_port = Htons(portnr);
+ InetPresentationToNumeric(AF_INET6, ip,
+ &remoteAddr._sockaddr_in6.sin6_addr);
+ remoteAddr._sockaddr_in6.sin6_flowinfo=0;
+ remoteAddr._sockaddr_in6.sin6_scope_id=0;
+ } else
+ {
+#ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
+ remoteAddr.sin_length = 0;
+ remoteAddr.sin_family = PF_INET;
+#else
+ remoteAddr._sockaddr_storage.sin_family = PF_INET;
+#endif
+ remoteAddr._sockaddr_in.sin_port = Htons(portnr);
+ remoteAddr._sockaddr_in.sin_addr= InetAddrIPV4(
+ const_cast<char*>(ip));
+ }
+}
+
+int32_t UdpTransportImpl::SendRaw(const int8_t *data,
+ size_t length,
+ int32_t isRTCP,
+ uint16_t portnr,
+ const char* ip)
+{
+ CriticalSectionScoped cs(_crit);
+ if(isRTCP)
+ {
+ UdpSocketWrapper* rtcpSock = NULL;
+ if(_ptrSendRtcpSocket)
+ {
+ rtcpSock = _ptrSendRtcpSocket;
+ } else if(_ptrRtcpSocket)
+ {
+ rtcpSock = _ptrRtcpSocket;
+ } else
+ {
+ return -1;
+ }
+ if(portnr == 0 && ip == NULL)
+ {
+ return rtcpSock->SendTo(data,length,_remoteRTCPAddr);
+
+ } else if(portnr != 0 && ip != NULL)
+ {
+ SocketAddress remoteAddr;
+ BuildSockaddrIn(portnr, ip, remoteAddr);
+ return rtcpSock->SendTo(data,length,remoteAddr);
+ } else if(ip != NULL)
+ {
+ SocketAddress remoteAddr;
+ BuildSockaddrIn(_destPortRTCP, ip, remoteAddr);
+ return rtcpSock->SendTo(data,length,remoteAddr);
+ } else
+ {
+ SocketAddress remoteAddr;
+ BuildSockaddrIn(portnr, _destIP, remoteAddr);
+ return rtcpSock->SendTo(data,length,remoteAddr);
+ }
+ } else {
+ UdpSocketWrapper* rtpSock = NULL;
+ if(_ptrSendRtpSocket)
+ {
+ rtpSock = _ptrSendRtpSocket;
+
+ } else if(_ptrRtpSocket)
+ {
+ rtpSock = _ptrRtpSocket;
+ } else
+ {
+ return -1;
+ }
+ if(portnr == 0 && ip == NULL)
+ {
+ return rtpSock->SendTo(data,length,_remoteRTPAddr);
+
+ } else if(portnr != 0 && ip != NULL)
+ {
+ SocketAddress remoteAddr;
+ BuildSockaddrIn(portnr, ip, remoteAddr);
+ return rtpSock->SendTo(data,length,remoteAddr);
+ } else if(ip != NULL)
+ {
+ SocketAddress remoteAddr;
+ BuildSockaddrIn(_destPort, ip, remoteAddr);
+ return rtpSock->SendTo(data,length,remoteAddr);
+ } else
+ {
+ SocketAddress remoteAddr;
+ BuildSockaddrIn(portnr, _destIP, remoteAddr);
+ return rtpSock->SendTo(data,length,remoteAddr);
+ }
+ }
+}
+
+int32_t UdpTransportImpl::SendRTPPacketTo(const int8_t* data,
+ size_t length,
+ const SocketAddress& to)
+{
+ CriticalSectionScoped cs(_crit);
+ if(_ptrSendRtpSocket)
+ {
+ return _ptrSendRtpSocket->SendTo(data,length,to);
+
+ } else if(_ptrRtpSocket)
+ {
+ return _ptrRtpSocket->SendTo(data,length,to);
+ }
+ return -1;
+}
+
+int32_t UdpTransportImpl::SendRTCPPacketTo(const int8_t* data,
+ size_t length,
+ const SocketAddress& to)
+{
+
+ CriticalSectionScoped cs(_crit);
+
+ if(_ptrSendRtcpSocket)
+ {
+ return _ptrSendRtcpSocket->SendTo(data,length,to);
+
+ } else if(_ptrRtcpSocket)
+ {
+ return _ptrRtcpSocket->SendTo(data,length,to);
+ }
+ return -1;
+}
+
+int32_t UdpTransportImpl::SendRTPPacketTo(const int8_t* data,
+ size_t length,
+ const uint16_t rtpPort)
+{
+ CriticalSectionScoped cs(_crit);
+ // Use the current SocketAdress but update it with rtpPort.
+ SocketAddress to;
+ memcpy(&to, &_remoteRTPAddr, sizeof(SocketAddress));
+
+ if(_ipV6Enabled)
+ {
+ to._sockaddr_in6.sin6_port = Htons(rtpPort);
+ } else
+ {
+ to._sockaddr_in.sin_port = Htons(rtpPort);
+ }
+
+ if(_ptrSendRtpSocket)
+ {
+ return _ptrSendRtpSocket->SendTo(data,length,to);
+
+ } else if(_ptrRtpSocket)
+ {
+ return _ptrRtpSocket->SendTo(data,length,to);
+ }
+ return -1;
+}
+
+int32_t UdpTransportImpl::SendRTCPPacketTo(const int8_t* data,
+ size_t length,
+ const uint16_t rtcpPort)
+{
+ CriticalSectionScoped cs(_crit);
+
+ // Use the current SocketAdress but update it with rtcpPort.
+ SocketAddress to;
+ memcpy(&to, &_remoteRTCPAddr, sizeof(SocketAddress));
+
+ if(_ipV6Enabled)
+ {
+ to._sockaddr_in6.sin6_port = Htons(rtcpPort);
+ } else
+ {
+ to._sockaddr_in.sin_port = Htons(rtcpPort);
+ }
+
+ if(_ptrSendRtcpSocket)
+ {
+ return _ptrSendRtcpSocket->SendTo(data,length,to);
+
+ } else if(_ptrRtcpSocket)
+ {
+ return _ptrRtcpSocket->SendTo(data,length,to);
+ }
+ return -1;
+}
+
+bool UdpTransportImpl::SendRtp(const uint8_t* data,
+ size_t length,
+ const PacketOptions& packet_options) {
+ WEBRTC_TRACE(kTraceStream, kTraceTransport, _id, "%s", __FUNCTION__);
+
+ CriticalSectionScoped cs(_crit);
+
+ if(_destIP[0] == 0)
+ {
+ return false;
+ }
+ if(_destPort == 0)
+ {
+ return false;
+ }
+
+ // Create socket if it hasn't been set up already.
+ // TODO (hellner): why not fail here instead. Sockets not being initialized
+ // indicates that there is a problem somewhere.
+ if( _ptrSendRtpSocket == NULL &&
+ _ptrRtpSocket == NULL)
+ {
+ WEBRTC_TRACE(
+ kTraceStateInfo,
+ kTraceTransport,
+ _id,
+ "Creating RTP socket since no receive or source socket is\
+ configured");
+
+ _ptrRtpSocket = _socket_creator->CreateSocket(_id, _mgr, this,
+ IncomingRTPCallback,
+ IpV6Enabled(), false);
+
+ // Don't bind to a specific IP address.
+ if(! IpV6Enabled())
+ {
+ strncpy(_localIP, "0.0.0.0",16);
+ } else
+ {
+ strncpy(_localIP, "0000:0000:0000:0000:0000:0000:0000:0000",
+ kIpAddressVersion6Length);
+ }
+ _localPort = _destPort;
+
+ ErrorCode retVal = BindLocalRTPSocket();
+ if(retVal != kNoSocketError)
+ {
+ WEBRTC_TRACE(kTraceError, kTraceTransport, _id,
+ "SendPacket() failed to bind RTP socket");
+ _lastError = retVal;
+ CloseReceiveSockets();
+ return false;
+ }
+ }
+
+ if(_ptrSendRtpSocket)
+ {
+ return _ptrSendRtpSocket->SendTo((const int8_t*)data, length,
+ _remoteRTPAddr) >= 0;
+
+ } else if(_ptrRtpSocket)
+ {
+ return _ptrRtpSocket->SendTo((const int8_t*)data, length,
+ _remoteRTPAddr) >= 0;
+ }
+ return false;
+}
+
+bool UdpTransportImpl::SendRtcp(const uint8_t* data, size_t length) {
+ CriticalSectionScoped cs(_crit);
+ if(_destIP[0] == 0)
+ {
+ return false;
+ }
+ if(_destPortRTCP == 0)
+ {
+ return false;
+ }
+
+ // Create socket if it hasn't been set up already.
+ // TODO (hellner): why not fail here instead. Sockets not being initialized
+ // indicates that there is a problem somewhere.
+ if( _ptrSendRtcpSocket == NULL &&
+ _ptrRtcpSocket == NULL)
+ {
+ WEBRTC_TRACE(
+ kTraceStateInfo,
+ kTraceTransport,
+ _id,
+ "Creating RTCP socket since no receive or source socket is\
+ configured");
+
+ _ptrRtcpSocket = _socket_creator->CreateSocket(_id, _mgr, this,
+ IncomingRTCPCallback,
+ IpV6Enabled(), false);
+
+ // Don't bind to a specific IP address.
+ if(! IpV6Enabled())
+ {
+ strncpy(_localIP, "0.0.0.0",16);
+ } else
+ {
+ strncpy(_localIP, "0000:0000:0000:0000:0000:0000:0000:0000",
+ kIpAddressVersion6Length);
+ }
+ _localPortRTCP = _destPortRTCP;
+
+ ErrorCode retVal = BindLocalRTCPSocket();
+ if(retVal != kNoSocketError)
+ {
+ _lastError = retVal;
+ WEBRTC_TRACE(kTraceError, kTraceTransport, _id,
+ "SendRtcp() failed to bind RTCP socket");
+ CloseReceiveSockets();
+ return false;
+ }
+ }
+
+ if(_ptrSendRtcpSocket)
+ {
+ return _ptrSendRtcpSocket->SendTo((const int8_t*)data, length,
+ _remoteRTCPAddr) >= 0;
+ } else if(_ptrRtcpSocket)
+ {
+ return _ptrRtcpSocket->SendTo((const int8_t*)data, length,
+ _remoteRTCPAddr) >= 0;
+ }
+ return false;
+}
+
+int32_t UdpTransportImpl::SetSendIP(const char* ipaddr)
+{
+ if(!IsIpAddressValid(ipaddr,IpV6Enabled()))
+ {
+ return kIpAddressInvalid;
+ }
+ CriticalSectionScoped cs(_crit);
+ strncpy(_destIP, ipaddr,kIpAddressVersion6Length);
+ BuildRemoteRTPAddr();
+ BuildRemoteRTCPAddr();
+ return 0;
+}
+
+int32_t UdpTransportImpl::SetSendPorts(uint16_t rtpPort, uint16_t rtcpPort)
+{
+ CriticalSectionScoped cs(_crit);
+ _destPort = rtpPort;
+ if(rtcpPort == 0)
+ {
+ _destPortRTCP = _destPort+1;
+ } else
+ {
+ _destPortRTCP = rtcpPort;
+ }
+ BuildRemoteRTPAddr();
+ BuildRemoteRTCPAddr();
+ return 0;
+}
+
+void UdpTransportImpl::IncomingRTPCallback(CallbackObj obj,
+ const int8_t* rtpPacket,
+ size_t rtpPacketLength,
+ const SocketAddress* from)
+{
+ if (rtpPacket && rtpPacketLength > 0)
+ {
+ UdpTransportImpl* socketTransport = (UdpTransportImpl*) obj;
+ socketTransport->IncomingRTPFunction(rtpPacket, rtpPacketLength, from);
+ }
+}
+
+void UdpTransportImpl::IncomingRTCPCallback(CallbackObj obj,
+ const int8_t* rtcpPacket,
+ size_t rtcpPacketLength,
+ const SocketAddress* from)
+{
+ if (rtcpPacket && rtcpPacketLength > 0)
+ {
+ UdpTransportImpl* socketTransport = (UdpTransportImpl*) obj;
+ socketTransport->IncomingRTCPFunction(rtcpPacket, rtcpPacketLength,
+ from);
+ }
+}
+
+void UdpTransportImpl::IncomingRTPFunction(const int8_t* rtpPacket,
+ size_t rtpPacketLength,
+ const SocketAddress* fromSocket)
+{
+ char ipAddress[kIpAddressVersion6Length];
+ uint32_t ipAddressLength = kIpAddressVersion6Length;
+ uint16_t portNr = 0;
+
+ {
+ CriticalSectionScoped cs(_critFilter);
+ if (FilterIPAddress(fromSocket) == false)
+ {
+ // Packet should be filtered out. Drop it.
+ WEBRTC_TRACE(kTraceStream, kTraceTransport, _id,
+ "Incoming RTP packet blocked by IP filter");
+ return;
+ }
+
+ if (IPAddressCached(*fromSocket, ipAddress, ipAddressLength, portNr) <
+ 0)
+ {
+ WEBRTC_TRACE(
+ kTraceError,
+ kTraceTransport,
+ _id,
+ "UdpTransportImpl::IncomingRTPFunction - Cannot get sender\
+ information");
+ }else
+ {
+ // Make sure ipAddress is null terminated.
+ ipAddress[kIpAddressVersion6Length - 1] = 0;
+ strncpy(_fromIP, ipAddress, kIpAddressVersion6Length - 1);
+ }
+
+ // Filter based on port.
+ if (_rtpFilterPort != 0 &&
+ _rtpFilterPort != portNr)
+ {
+ // Drop packet.
+ memset(_fromIP, 0, sizeof(_fromIP));
+ WEBRTC_TRACE(
+ kTraceStream,
+ kTraceTransport,
+ _id,
+ "Incoming RTP packet blocked by filter incoming from port:%d\
+ allowed port:%d",
+ portNr,
+ _rtpFilterPort);
+ return;
+ }
+ _fromPort = portNr;
+ }
+
+ CriticalSectionScoped cs(_critPacketCallback);
+ if (_packetCallback)
+ {
+ WEBRTC_TRACE(kTraceStream, kTraceTransport, _id,
+ "Incoming RTP packet from ip:%s port:%d", ipAddress, portNr);
+ _packetCallback->IncomingRTPPacket(rtpPacket, rtpPacketLength,
+ ipAddress, portNr);
+ }
+}
+
+void UdpTransportImpl::IncomingRTCPFunction(const int8_t* rtcpPacket,
+ size_t rtcpPacketLength,
+ const SocketAddress* fromSocket)
+{
+ char ipAddress[kIpAddressVersion6Length];
+ uint32_t ipAddressLength = kIpAddressVersion6Length;
+ uint16_t portNr = 0;
+
+ {
+ CriticalSectionScoped cs(_critFilter);
+ if (FilterIPAddress(fromSocket) == false)
+ {
+ // Packet should be filtered out. Drop it.
+ WEBRTC_TRACE(kTraceStream, kTraceTransport, _id,
+ "Incoming RTCP packet blocked by IP filter");
+ return;
+ }
+ if (IPAddress(*fromSocket, ipAddress, ipAddressLength, portNr) < 0)
+ {
+ WEBRTC_TRACE(
+ kTraceError,
+ kTraceTransport,
+ _id,
+ "UdpTransportImpl::IncomingRTCPFunction - Cannot get sender\
+ information");
+ }else {
+ // Make sure ipAddress is null terminated.
+ ipAddress[kIpAddressVersion6Length - 1] = 0;
+ strncpy(_fromIP, ipAddress, kIpAddressVersion6Length - 1);
+ }
+
+ // Filter based on port.
+ if (_rtcpFilterPort != 0 &&
+ _rtcpFilterPort != portNr)
+ {
+ // Drop packet.
+ WEBRTC_TRACE(
+ kTraceStream,
+ kTraceTransport,
+ _id,
+ "Incoming RTCP packet blocked by filter incoming from port:%d\
+ allowed port:%d",
+ portNr,
+ _rtpFilterPort);
+ return;
+ }
+ _fromPortRTCP = portNr;
+ }
+
+ CriticalSectionScoped cs(_critPacketCallback);
+ if (_packetCallback)
+ {
+ WEBRTC_TRACE(kTraceStream, kTraceTransport, _id,
+ "Incoming RTCP packet from ip:%s port:%d", ipAddress,
+ portNr);
+ _packetCallback->IncomingRTCPPacket(rtcpPacket, rtcpPacketLength,
+ ipAddress, portNr);
+ }
+}
+
+bool UdpTransportImpl::FilterIPAddress(const SocketAddress* fromAddress)
+{
+ if(fromAddress->_sockaddr_storage.sin_family == AF_INET)
+ {
+ if (_filterIPAddress._sockaddr_storage.sin_family == AF_INET)
+ {
+ // IP is stored in sin_addr.
+ if (_filterIPAddress._sockaddr_in.sin_addr != 0 &&
+ (_filterIPAddress._sockaddr_in.sin_addr !=
+ fromAddress->_sockaddr_in.sin_addr))
+ {
+ return false;
+ }
+ }
+ }
+ else if(fromAddress->_sockaddr_storage.sin_family == AF_INET6)
+ {
+ if (_filterIPAddress._sockaddr_storage.sin_family == AF_INET6)
+ {
+ // IP is stored in sin_6addr.
+ for (int32_t i = 0; i < 4; i++)
+ {
+ if (_filterIPAddress._sockaddr_in6.sin6_addr.Version6AddressUnion._s6_u32[i] != 0 &&
+ _filterIPAddress._sockaddr_in6.sin6_addr.Version6AddressUnion._s6_u32[i] != fromAddress->_sockaddr_in6.sin6_addr.Version6AddressUnion._s6_u32[i])
+ {
+ return false;
+ }
+ }
+ }
+ }
+ else
+ {
+ WEBRTC_TRACE(
+ kTraceError,
+ kTraceTransport,
+ _id,
+ "UdpTransportImpl::FilterIPAddress() unknown address family");
+ return false;
+ }
+ return true;
+}
+
+void UdpTransportImpl::CloseReceiveSockets()
+{
+ if(_ptrRtpSocket)
+ {
+ _ptrRtpSocket->CloseBlocking();
+ _ptrRtpSocket = NULL;
+ }
+ if(_ptrRtcpSocket)
+ {
+ _ptrRtcpSocket->CloseBlocking();
+ _ptrRtcpSocket = NULL;
+ }
+ _receiving = false;
+}
+
+void UdpTransportImpl::CloseSendSockets()
+{
+ if(_ptrSendRtpSocket)
+ {
+ _ptrSendRtpSocket->CloseBlocking();
+ _ptrSendRtpSocket = 0;
+ }
+ if(_ptrSendRtcpSocket)
+ {
+ _ptrSendRtcpSocket->CloseBlocking();
+ _ptrSendRtcpSocket = 0;
+ }
+}
+
+uint16_t UdpTransport::Htons(const uint16_t port)
+{
+ return htons(port);
+}
+
+uint32_t UdpTransport::Htonl(const uint32_t a)
+{
+ return htonl(a);
+}
+
+uint32_t UdpTransport::InetAddrIPV4(const char* ip)
+{
+ return ::inet_addr(ip);
+}
+
+int32_t UdpTransport::InetPresentationToNumeric(int32_t af,
+ const char* src,
+ void* dst)
+{
+#if defined(WEBRTC_LINUX) || defined(WEBRTC_MAC)
+ const int32_t result = inet_pton(af, src, dst);
+ return result > 0 ? 0 : -1;
+
+#elif defined(_WIN32)
+ SocketAddress temp;
+ int length=sizeof(SocketAddress);
+
+ if(af == AF_INET)
+ {
+ int32_t result = WSAStringToAddressA(
+ (const LPSTR)src,
+ af,
+ 0,
+ reinterpret_cast<struct sockaddr*>(&temp),
+ &length);
+ if(result != 0)
+ {
+ return -1;
+ }
+ memcpy(dst,&(temp._sockaddr_in.sin_addr),
+ sizeof(temp._sockaddr_in.sin_addr));
+ return 0;
+ }
+ else if(af == AF_INET6)
+ {
+ int32_t result = WSAStringToAddressA(
+ (const LPSTR)src,
+ af,
+ 0,
+ reinterpret_cast<struct sockaddr*>(&temp),
+ &length);
+ if(result !=0)
+ {
+ return -1;
+ }
+ memcpy(dst,&(temp._sockaddr_in6.sin6_addr),
+ sizeof(temp._sockaddr_in6.sin6_addr));
+ return 0;
+
+ }else
+ {
+ return -1;
+ }
+#else
+ return -1;
+#endif
+}
+
+int32_t UdpTransport::LocalHostAddressIPV6(char n_localIP[16])
+{
+
+#if defined(_WIN32)
+ struct addrinfo *result = NULL;
+ struct addrinfo *ptr = NULL;
+ struct addrinfo hints;
+
+ ZeroMemory(&hints, sizeof(hints));
+ hints.ai_family = AF_INET6;
+
+ char szHostName[256] = "";
+ if(::gethostname(szHostName, sizeof(szHostName) - 1))
+ {
+ WEBRTC_TRACE(kTraceWarning, kTraceTransport, -1, "gethostname failed");
+ return -1;
+ }
+
+ DWORD dwRetval = getaddrinfo(szHostName, NULL, &hints, &result);
+ if ( dwRetval != 0 )
+ {
+ WEBRTC_TRACE(kTraceWarning, kTraceTransport, -1,
+ "getaddrinfo failed, error:%d", dwRetval);
+ return -1;
+ }
+ for(ptr=result; ptr != NULL ;ptr=ptr->ai_next)
+ {
+ switch (ptr->ai_family)
+ {
+ case AF_INET6:
+ {
+ for(int i = 0; i< 16; i++)
+ {
+ n_localIP[i] = (*(SocketAddress*)ptr->ai_addr).
+ _sockaddr_in6.sin6_addr.Version6AddressUnion._s6_u8[i];
+ }
+ bool islocalIP = true;
+
+ for(int n = 0; n< 15; n++)
+ {
+ if(n_localIP[n] != 0)
+ {
+ islocalIP = false;
+ break;
+ }
+ }
+
+ if(islocalIP && n_localIP[15] != 1)
+ {
+ islocalIP = false;
+ }
+
+ if(islocalIP && ptr->ai_next)
+ {
+ continue;
+ }
+ if(n_localIP[0] == 0xfe &&
+ n_localIP[1] == 0x80 && ptr->ai_next)
+ {
+ continue;
+ }
+ freeaddrinfo(result);
+ }
+ return 0;
+ default:
+ break;
+ };
+ }
+ freeaddrinfo(result);
+ WEBRTC_TRACE(kTraceWarning, kTraceTransport, -1,
+ "getaddrinfo failed to find address");
+ return -1;
+
+#elif defined(WEBRTC_MAC)
+ struct ifaddrs* ptrIfAddrs = NULL;
+ struct ifaddrs* ptrIfAddrsStart = NULL;
+
+ getifaddrs(&ptrIfAddrsStart);
+ ptrIfAddrs = ptrIfAddrsStart;
+ while(ptrIfAddrs)
+ {
+ if(ptrIfAddrs->ifa_addr->sa_family == AF_INET6)
+ {
+ const struct sockaddr_in6* sock_in6 =
+ reinterpret_cast<struct sockaddr_in6*>(ptrIfAddrs->ifa_addr);
+ const struct in6_addr* sin6_addr = &sock_in6->sin6_addr;
+
+ if (IN6_IS_ADDR_LOOPBACK(sin6_addr) ||
+ IN6_IS_ADDR_LINKLOCAL(sin6_addr)) {
+ ptrIfAddrs = ptrIfAddrs->ifa_next;
+ continue;
+ }
+ memcpy(n_localIP, sin6_addr->s6_addr, sizeof(sin6_addr->s6_addr));
+ freeifaddrs(ptrIfAddrsStart);
+ return 0;
+ }
+ ptrIfAddrs = ptrIfAddrs->ifa_next;
+ }
+ freeifaddrs(ptrIfAddrsStart);
+ return -1;
+#elif defined(WEBRTC_ANDROID)
+ return -1;
+#else // WEBRTC_LINUX
+ struct
+ {
+ struct nlmsghdr n;
+ struct ifaddrmsg r;
+ } req;
+
+ struct rtattr* rta = NULL;
+ int status;
+ char buf[16384]; // = 16 * 1024 (16 kB)
+ struct nlmsghdr* nlmp;
+ struct ifaddrmsg* rtmp;
+ struct rtattr* rtatp;
+ int rtattrlen;
+ struct in6_addr* in6p;
+
+ int fd = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE);
+ if (fd == -1)
+ {
+ return -1;
+ }
+
+ // RTM_GETADDR is used to fetch the ip address from the kernel interface
+ // table. Populate the msg structure (req) the size of the message buffer
+ // is specified to netlinkmessage header, and flags values are set as
+ // NLM_F_ROOT | NLM_F_REQUEST.
+ // The request flag must be set for all messages requesting the data from
+ // kernel. The root flag is used to notify the kernel to return the full
+ // tabel. Another flag (not used) is NLM_F_MATCH. This is used to get only
+ // specified entries in the table. At the time of writing this program this
+ // flag is not implemented in kernel
+
+ memset(&req, 0, sizeof(req));
+ req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifaddrmsg));
+ req.n.nlmsg_flags = NLM_F_REQUEST | NLM_F_ROOT;
+ req.n.nlmsg_type = RTM_GETADDR;
+ req.r.ifa_family = AF_INET6;
+
+ // Fill up all the attributes for the rtnetlink header.
+ // The lenght is very important. 16 signifies the ipv6 address.
+ rta = (struct rtattr*)(((char*)&req) + NLMSG_ALIGN(req.n.nlmsg_len));
+ rta->rta_len = RTA_LENGTH(16);
+
+ status = send(fd, &req, req.n.nlmsg_len, 0);
+ if (status < 0)
+ {
+ close(fd);
+ return -1;
+ }
+ status = recv(fd, buf, sizeof(buf), 0);
+ if (status < 0)
+ {
+ close(fd);
+ return -1;
+ }
+ if(status == 0)
+ {
+ close(fd);
+ return -1;
+ }
+ close(fd);
+
+ // The message is stored in buff. Parse the message to get the requested
+ // data.
+ {
+ nlmp = (struct nlmsghdr*)buf;
+ int len = nlmp->nlmsg_len;
+ int req_len = len - sizeof(*nlmp);
+
+ if (req_len < 0 || len > status)
+ {
+ return -1;
+ }
+ if (!NLMSG_OK_NO_WARNING(nlmp, status))
+ {
+ return -1;
+ }
+ rtmp = (struct ifaddrmsg*)NLMSG_DATA(nlmp);
+ rtatp = (struct rtattr*)IFA_RTA(rtmp);
+
+ rtattrlen = IFA_PAYLOAD(nlmp);
+
+ for (; RTA_OK(rtatp, rtattrlen); rtatp = RTA_NEXT(rtatp, rtattrlen))
+ {
+
+ // Here we hit the fist chunk of the message. Time to validate the
+ // type. For more info on the different types see
+ // "man(7) rtnetlink" The table below is taken from man pages.
+ // Attributes
+ // rta_type value type description
+ // -------------------------------------------------------------
+ // IFA_UNSPEC - unspecified.
+ // IFA_ADDRESS raw protocol address interface address
+ // IFA_LOCAL raw protocol address local address
+ // IFA_LABEL asciiz string name of the interface
+ // IFA_BROADCAST raw protocol address broadcast address.
+ // IFA_ANYCAST raw protocol address anycast address
+ // IFA_CACHEINFO struct ifa_cacheinfo Address information.
+
+ if(rtatp->rta_type == IFA_ADDRESS)
+ {
+ bool islocalIP = true;
+ in6p = (struct in6_addr*)RTA_DATA(rtatp);
+ for(int n = 0; n< 15; n++)
+ {
+ if(in6p->s6_addr[n] != 0)
+ {
+ islocalIP = false;
+ break;
+ }
+ }
+ if(islocalIP && in6p->s6_addr[15] != 1)
+ {
+ islocalIP = false;
+ }
+ if(!islocalIP)
+ {
+ for(int i = 0; i< 16; i++)
+ {
+ n_localIP[i] = in6p->s6_addr[i];
+ }
+ if(n_localIP[0] == static_cast<char> (0xfe)
+ && n_localIP[1] == static_cast<char>(0x80) )
+ {
+ // Auto configured IP.
+ continue;
+ }
+ break;
+ }
+ }
+ }
+ }
+ return 0;
+#endif
+}
+
+int32_t UdpTransport::LocalHostAddress(uint32_t& localIP)
+{
+ #if defined(_WIN32)
+ hostent* localHost;
+ localHost = gethostbyname( "" );
+ if(localHost)
+ {
+ if(localHost->h_addrtype != AF_INET)
+ {
+ WEBRTC_TRACE(
+ kTraceError,
+ kTraceTransport,
+ -1,
+ "LocalHostAddress can only get local IP for IP Version 4");
+ return -1;
+ }
+ localIP= Htonl(
+ (*(struct in_addr *)localHost->h_addr_list[0]).S_un.S_addr);
+ return 0;
+ }
+ else
+ {
+ int32_t error = WSAGetLastError();
+ WEBRTC_TRACE(kTraceWarning, kTraceTransport, -1,
+ "gethostbyname failed, error:%d", error);
+ return -1;
+ }
+#elif (defined(WEBRTC_MAC))
+ char localname[255];
+ if (gethostname(localname, 255) != -1)
+ {
+ hostent* localHost;
+ localHost = gethostbyname(localname);
+ if(localHost)
+ {
+ if(localHost->h_addrtype != AF_INET)
+ {
+ WEBRTC_TRACE(
+ kTraceError,
+ kTraceTransport,
+ -1,
+ "LocalHostAddress can only get local IP for IP Version 4");
+ return -1;
+ }
+ localIP = Htonl((*(struct in_addr*)*localHost->h_addr_list).s_addr);
+ return 0;
+ }
+ }
+ WEBRTC_TRACE(kTraceWarning, kTraceTransport, -1, "gethostname failed");
+ return -1;
+#else // WEBRTC_LINUX
+ int sockfd, size = 1;
+ struct ifreq* ifr;
+ struct ifconf ifc;
+
+ if (0 > (sockfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP)))
+ {
+ return -1;
+ }
+ ifc.ifc_len = IFRSIZE;
+ ifc.ifc_req = NULL;
+ do
+ {
+ ++size;
+ // Buffer size needed is unknown. Try increasing it until no overflow
+ // occurs.
+ if (NULL == (ifc.ifc_req = (ifreq*)realloc(ifc.ifc_req, IFRSIZE))) {
+ fprintf(stderr, "Out of memory.\n");
+ exit(EXIT_FAILURE);
+ }
+ ifc.ifc_len = IFRSIZE;
+ if (ioctl(sockfd, SIOCGIFCONF, &ifc))
+ {
+ free(ifc.ifc_req);
+ close(sockfd);
+ return -1;
+ }
+ } while (IFRSIZE <= ifc.ifc_len);
+
+ ifr = ifc.ifc_req;
+ for (;(char *) ifr < (char *) ifc.ifc_req + ifc.ifc_len; ++ifr)
+ {
+ if (ifr->ifr_addr.sa_data == (ifr+1)->ifr_addr.sa_data)
+ {
+ continue; // duplicate, skip it
+ }
+ if (ioctl(sockfd, SIOCGIFFLAGS, ifr))
+ {
+ continue; // failed to get flags, skip it
+ }
+ if(strncmp(ifr->ifr_name, "lo",3) == 0)
+ {
+ continue;
+ }else
+ {
+ struct sockaddr* saddr = &(ifr->ifr_addr);
+ SocketAddress* socket_addess = reinterpret_cast<SocketAddress*>(
+ saddr);
+ localIP = Htonl(socket_addess->_sockaddr_in.sin_addr);
+ close(sockfd);
+ free(ifc.ifc_req);
+ return 0;
+ }
+ }
+ free(ifc.ifc_req);
+ close(sockfd);
+ return -1;
+#endif
+}
+
+int32_t UdpTransport::IPAddress(const SocketAddress& address,
+ char* ip,
+ uint32_t& ipSize,
+ uint16_t& sourcePort)
+{
+ #if defined(_WIN32)
+ DWORD dwIPSize = ipSize;
+ int32_t returnvalue = WSAAddressToStringA((LPSOCKADDR)(&address),
+ sizeof(SocketAddress),
+ NULL,
+ ip,
+ &dwIPSize);
+ if(returnvalue == -1)
+ {
+ return -1;
+ }
+
+ uint16_t source_port = 0;
+ if(address._sockaddr_storage.sin_family == AF_INET)
+ {
+ // Parse IP assuming format "a.b.c.d:port".
+ char* ipEnd = strchr(ip,':');
+ if(ipEnd != NULL)
+ {
+ *ipEnd = '\0';
+ }
+ ipSize = (int32_t)strlen(ip);
+ if(ipSize == 0)
+ {
+ return -1;
+ }
+ source_port = address._sockaddr_in.sin_port;
+ }
+ else
+ {
+ // Parse IP assuming format "[address]:port".
+ char* ipEnd = strchr(ip,']');
+ if(ipEnd != NULL)
+ {
+ // Calculate length
+ int32_t adrSize = int32_t(ipEnd - ip) - 1;
+ memmove(ip, &ip[1], adrSize); // Remove '['
+ *(ipEnd - 1) = '\0';
+ }
+ ipSize = (int32_t)strlen(ip);
+ if(ipSize == 0)
+ {
+ return -1;
+ }
+
+ source_port = address._sockaddr_in6.sin6_port;
+ }
+ // Convert port number to network byte order.
+ sourcePort = htons(source_port);
+ return 0;
+
+ #elif defined(WEBRTC_LINUX) || defined(WEBRTC_MAC)
+ int32_t ipFamily = address._sockaddr_storage.sin_family;
+ const void* ptrNumericIP = NULL;
+
+ if(ipFamily == AF_INET)
+ {
+ ptrNumericIP = &(address._sockaddr_in.sin_addr);
+ }
+ else if(ipFamily == AF_INET6)
+ {
+ ptrNumericIP = &(address._sockaddr_in6.sin6_addr);
+ }
+ else
+ {
+ return -1;
+ }
+ if(inet_ntop(ipFamily, ptrNumericIP, ip, ipSize) == NULL)
+ {
+ return -1;
+ }
+ uint16_t source_port;
+ if(ipFamily == AF_INET)
+ {
+ source_port = address._sockaddr_in.sin_port;
+ } else
+ {
+ source_port = address._sockaddr_in6.sin6_port;
+ }
+ // Convert port number to network byte order.
+ sourcePort = htons(source_port);
+ return 0;
+ #else
+ return -1;
+ #endif
+}
+
+bool UdpTransport::IsIpAddressValid(const char* ipadr, const bool ipV6)
+{
+ if(ipV6)
+ {
+ int32_t len = (int32_t)strlen(ipadr);
+ if( len>39 || len == 0)
+ {
+ return false;
+ }
+
+ int32_t i;
+ int32_t colonPos[7] = {0,0,0,0,0,0,0};
+ int32_t lastColonPos = -2;
+ int32_t nColons = 0;
+ int32_t nDubbleColons = 0;
+ int32_t nDots = 0;
+ int32_t error = 0;
+ char c;
+ for(i = 0; i < len ; i++)
+ {
+ c=ipadr[i];
+ if(isxdigit(c))
+ ;
+ else if(c == ':')
+ {
+ if(nColons < 7)
+ colonPos[nColons] = i;
+ if((i-lastColonPos)==1)
+ nDubbleColons++;
+ lastColonPos=i;
+ if(nDots != 0)
+ {
+ error = 1;
+ }
+ nColons++;
+ }
+ else if(c == '.')
+ {
+ nDots++;
+ }
+ else
+ {
+ error = 1;
+ }
+
+ }
+ if(error)
+ {
+ return false;
+ }
+ if(nDubbleColons > 1)
+ {
+ return false;
+ }
+ if(nColons > 7 || nColons < 2)
+ {
+ return false;
+ }
+ if(!(nDots == 3 || nDots == 0))
+ {
+ return false;
+ }
+ lastColonPos = -1;
+ int32_t charsBeforeColon = 0;
+ for(i = 0; i < nColons; i++)
+ {
+ charsBeforeColon=colonPos[i]-lastColonPos-1;
+ if(charsBeforeColon > 4)
+ {
+ return false;
+ }
+ lastColonPos=colonPos[i];
+ }
+ int32_t lengthAfterLastColon = len - lastColonPos - 1;
+ if(nDots == 0)
+ {
+ if(lengthAfterLastColon > 4)
+ return false;
+ }
+ if(nDots == 3 && lengthAfterLastColon > 0)
+ {
+ return IsIpAddressValid((ipadr+lastColonPos+1),false);
+ }
+
+ }
+ else
+ {
+ int32_t len = (int32_t)strlen(ipadr);
+ if((len>15)||(len==0))
+ {
+ return false;
+ }
+
+ // IPv4 should be [0-255].[0-255].[0-255].[0-255]
+ int32_t i;
+ int32_t nDots = 0;
+ int32_t iDotPos[4] = {0,0,0,0};
+
+ for (i = 0; (i < len) && (nDots < 4); i++)
+ {
+ if (ipadr[i] == (char)'.')
+ {
+ // Store index of dots and count number of dots.
+ iDotPos[nDots++] = i;
+ }
+ }
+
+ bool allUnder256 = false;
+ // TODO (hellner): while loop seems to be abused here to get
+ // label like functionality. Fix later to avoid introducing bugs now.
+
+ // Check that all numbers are smaller than 256.
+ do
+ {
+ if (nDots != 3 )
+ {
+ break;
+ }
+
+ if (iDotPos[0] <= 3)
+ {
+ char nr[4];
+ memset(nr,0,4);
+ strncpy(nr,&ipadr[0],iDotPos[0]);
+ int32_t num = atoi(nr);
+ if (num > 255)
+ {
+ break;
+ }
+ } else {
+ break;
+ }
+
+ if (iDotPos[1] - iDotPos[0] <= 4)
+ {
+ char nr[4];
+ memset(nr,0,4);
+ strncpy(nr,&ipadr[iDotPos[0]+1], iDotPos[1] - iDotPos[0] - 1);
+ int32_t num = atoi(nr);
+ if (num > 255)
+ break;
+ } else {
+ break;
+ }
+
+ if (iDotPos[2] - iDotPos[1] <= 4)
+ {
+ char nr[4];
+ memset(nr,0,4);
+ strncpy(nr,&ipadr[iDotPos[1]+1], iDotPos[1] - iDotPos[0] - 1);
+ int32_t num = atoi(nr);
+ if (num > 255)
+ break;
+
+ memset(nr,0,4);
+ strncpy(nr,&ipadr[iDotPos[2]+1], len - iDotPos[2] -1);
+ num = atoi(nr);
+ if (num > 255)
+ break;
+ else
+ allUnder256 = true;
+ } else
+ break;
+ } while(false);
+
+ if (nDots != 3 || !allUnder256)
+ {
+ return false;
+ }
+ }
+ return true;
+}
+
+} // namespace test
+} // namespace webrtc
diff --git a/webrtc/test/channel_transport/udp_transport_impl.h b/webrtc/test/channel_transport/udp_transport_impl.h
new file mode 100644
index 0000000000..f80ee02d71
--- /dev/null
+++ b/webrtc/test/channel_transport/udp_transport_impl.h
@@ -0,0 +1,259 @@
+/*
+ * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#ifndef WEBRTC_TEST_CHANNEL_TRANSPORT_UDP_TRANSPORT_IMPL_H_
+#define WEBRTC_TEST_CHANNEL_TRANSPORT_UDP_TRANSPORT_IMPL_H_
+
+#include "webrtc/test/channel_transport/udp_socket_wrapper.h"
+#include "webrtc/test/channel_transport/udp_transport.h"
+
+namespace webrtc {
+
+class CriticalSectionWrapper;
+class RWLockWrapper;
+
+namespace test {
+
+class UdpSocketManager;
+
+class UdpTransportImpl : public UdpTransport
+{
+public:
+ // A factory that returns a wrapped UDP socket or equivalent.
+ class SocketFactoryInterface {
+ public:
+ virtual ~SocketFactoryInterface() {}
+ virtual UdpSocketWrapper* CreateSocket(const int32_t id,
+ UdpSocketManager* mgr,
+ CallbackObj obj,
+ IncomingSocketCallback cb,
+ bool ipV6Enable,
+ bool disableGQOS) = 0;
+ };
+
+ // Constructor, only called by UdpTransport::Create and tests.
+ // The constructor takes ownership of the "maker".
+ // The constructor does not take ownership of socket_manager.
+ UdpTransportImpl(const int32_t id,
+ SocketFactoryInterface* maker,
+ UdpSocketManager* socket_manager);
+ virtual ~UdpTransportImpl();
+
+ // UdpTransport functions
+ int32_t InitializeSendSockets(const char* ipAddr,
+ const uint16_t rtpPort,
+ const uint16_t rtcpPort = 0) override;
+ int32_t InitializeReceiveSockets(UdpTransportData* const packetCallback,
+ const uint16_t rtpPort,
+ const char* ipAddr = NULL,
+ const char* multicastIpAddr = NULL,
+ const uint16_t rtcpPort = 0) override;
+ int32_t InitializeSourcePorts(const uint16_t rtpPort,
+ const uint16_t rtcpPort = 0) override;
+ int32_t SourcePorts(uint16_t& rtpPort, uint16_t& rtcpPort) const override;
+ int32_t ReceiveSocketInformation(
+ char ipAddr[kIpAddressVersion6Length],
+ uint16_t& rtpPort,
+ uint16_t& rtcpPort,
+ char multicastIpAddr[kIpAddressVersion6Length]) const override;
+ int32_t SendSocketInformation(char ipAddr[kIpAddressVersion6Length],
+ uint16_t& rtpPort,
+ uint16_t& rtcpPort) const override;
+ int32_t RemoteSocketInformation(char ipAddr[kIpAddressVersion6Length],
+ uint16_t& rtpPort,
+ uint16_t& rtcpPort) const override;
+ int32_t SetQoS(const bool QoS,
+ const int32_t serviceType,
+ const uint32_t maxBitrate = 0,
+ const int32_t overrideDSCP = 0,
+ const bool audio = false) override;
+ int32_t QoS(bool& QoS,
+ int32_t& serviceType,
+ int32_t& overrideDSCP) const override;
+ int32_t SetToS(const int32_t DSCP,
+ const bool useSetSockOpt = false) override;
+ int32_t ToS(int32_t& DSCP, bool& useSetSockOpt) const override;
+ int32_t SetPCP(const int32_t PCP) override;
+ int32_t PCP(int32_t& PCP) const override;
+ int32_t EnableIpV6() override;
+ bool IpV6Enabled() const override;
+ int32_t SetFilterIP(
+ const char filterIPAddress[kIpAddressVersion6Length]) override;
+ int32_t FilterIP(
+ char filterIPAddress[kIpAddressVersion6Length]) const override;
+ int32_t SetFilterPorts(const uint16_t rtpFilterPort,
+ const uint16_t rtcpFilterPort) override;
+ int32_t FilterPorts(uint16_t& rtpFilterPort,
+ uint16_t& rtcpFilterPort) const override;
+ int32_t StartReceiving(const uint32_t numberOfSocketBuffers) override;
+ int32_t StopReceiving() override;
+ bool Receiving() const override;
+ bool SendSocketsInitialized() const override;
+ bool SourcePortsInitialized() const override;
+ bool ReceiveSocketsInitialized() const override;
+ int32_t SendRaw(const int8_t* data,
+ size_t length,
+ int32_t isRTCP,
+ uint16_t portnr = 0,
+ const char* ip = NULL) override;
+ int32_t SendRTPPacketTo(const int8_t* data,
+ size_t length,
+ const SocketAddress& to) override;
+ int32_t SendRTCPPacketTo(const int8_t* data,
+ size_t length,
+ const SocketAddress& to) override;
+ int32_t SendRTPPacketTo(const int8_t* data,
+ size_t length,
+ uint16_t rtpPort) override;
+ int32_t SendRTCPPacketTo(const int8_t* data,
+ size_t length,
+ uint16_t rtcpPort) override;
+ // Transport functions
+ bool SendRtp(const uint8_t* data,
+ size_t length,
+ const PacketOptions& packet_options) override;
+ bool SendRtcp(const uint8_t* data, size_t length) override;
+
+ // UdpTransport functions continue.
+ int32_t SetSendIP(const char* ipaddr) override;
+ int32_t SetSendPorts(const uint16_t rtpPort,
+ const uint16_t rtcpPort = 0) override;
+
+ ErrorCode LastError() const override;
+
+ int32_t IPAddressCached(const SocketAddress& address,
+ char* ip,
+ uint32_t& ipSize,
+ uint16_t& sourcePort) override;
+
+ int32_t Id() const {return _id;}
+protected:
+ // IncomingSocketCallback signature functions for receiving callbacks from
+ // UdpSocketWrapper.
+ static void IncomingRTPCallback(CallbackObj obj,
+ const int8_t* rtpPacket,
+ size_t rtpPacketLength,
+ const SocketAddress* from);
+ static void IncomingRTCPCallback(CallbackObj obj,
+ const int8_t* rtcpPacket,
+ size_t rtcpPacketLength,
+ const SocketAddress* from);
+
+ void CloseSendSockets();
+ void CloseReceiveSockets();
+
+ // Update _remoteRTPAddr according to _destPort and _destIP
+ void BuildRemoteRTPAddr();
+ // Update _remoteRTCPAddr according to _destPortRTCP and _destIP
+ void BuildRemoteRTCPAddr();
+
+ void BuildSockaddrIn(uint16_t portnr, const char* ip,
+ SocketAddress& remoteAddr) const;
+
+ ErrorCode BindLocalRTPSocket();
+ ErrorCode BindLocalRTCPSocket();
+
+ ErrorCode BindRTPSendSocket();
+ ErrorCode BindRTCPSendSocket();
+
+ void IncomingRTPFunction(const int8_t* rtpPacket,
+ size_t rtpPacketLength,
+ const SocketAddress* from);
+ void IncomingRTCPFunction(const int8_t* rtcpPacket,
+ size_t rtcpPacketLength,
+ const SocketAddress* from);
+
+ bool FilterIPAddress(const SocketAddress* fromAddress);
+
+ bool SetSockOptUsed();
+
+ int32_t EnableQoS(int32_t serviceType, bool audio,
+ uint32_t maxBitrate, int32_t overrideDSCP);
+
+ int32_t DisableQoS();
+
+private:
+ void GetCachedAddress(char* ip, uint32_t& ipSize,
+ uint16_t& sourcePort);
+
+ int32_t _id;
+ SocketFactoryInterface* _socket_creator;
+ // Protects the sockets from being re-configured while receiving packets.
+ CriticalSectionWrapper* _crit;
+ CriticalSectionWrapper* _critFilter;
+ // _packetCallback's critical section.
+ CriticalSectionWrapper* _critPacketCallback;
+ UdpSocketManager* _mgr;
+ ErrorCode _lastError;
+
+ // Remote RTP and RTCP ports.
+ uint16_t _destPort;
+ uint16_t _destPortRTCP;
+
+ // Local RTP and RTCP ports.
+ uint16_t _localPort;
+ uint16_t _localPortRTCP;
+
+ // Local port number when the local port for receiving and local port number
+ // for sending are not the same.
+ uint16_t _srcPort;
+ uint16_t _srcPortRTCP;
+
+ // Remote port from which last received packet was sent.
+ uint16_t _fromPort;
+ uint16_t _fromPortRTCP;
+
+ char _fromIP[kIpAddressVersion6Length];
+ char _destIP[kIpAddressVersion6Length];
+ char _localIP[kIpAddressVersion6Length];
+ char _localMulticastIP[kIpAddressVersion6Length];
+
+ UdpSocketWrapper* _ptrRtpSocket;
+ UdpSocketWrapper* _ptrRtcpSocket;
+
+ // Local port when the local port for receiving and local port for sending
+ // are not the same.
+ UdpSocketWrapper* _ptrSendRtpSocket;
+ UdpSocketWrapper* _ptrSendRtcpSocket;
+
+ SocketAddress _remoteRTPAddr;
+ SocketAddress _remoteRTCPAddr;
+
+ SocketAddress _localRTPAddr;
+ SocketAddress _localRTCPAddr;
+
+ int32_t _tos;
+ bool _receiving;
+ bool _useSetSockOpt;
+ bool _qos;
+ int32_t _pcp;
+ bool _ipV6Enabled;
+ int32_t _serviceType;
+ int32_t _overrideDSCP;
+ uint32_t _maxBitrate;
+
+ // Cache used by GetCachedAddress(..).
+ RWLockWrapper* _cachLock;
+ SocketAddress _previousAddress;
+ char _previousIP[kIpAddressVersion6Length];
+ uint32_t _previousIPSize;
+ uint16_t _previousSourcePort;
+
+ SocketAddress _filterIPAddress;
+ uint16_t _rtpFilterPort;
+ uint16_t _rtcpFilterPort;
+
+ UdpTransportData* _packetCallback;
+};
+
+} // namespace test
+} // namespace webrtc
+
+#endif // WEBRTC_TEST_CHANNEL_TRANSPORT_UDP_TRANSPORT_IMPL_H_
diff --git a/webrtc/test/channel_transport/udp_transport_unittest.cc b/webrtc/test/channel_transport/udp_transport_unittest.cc
new file mode 100644
index 0000000000..975f52b756
--- /dev/null
+++ b/webrtc/test/channel_transport/udp_transport_unittest.cc
@@ -0,0 +1,143 @@
+/*
+ * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include <vector>
+
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "webrtc/test/channel_transport/udp_transport.h"
+// We include the implementation header file to get at the dependency-injecting
+// constructor.
+#include "webrtc/test/channel_transport/udp_transport_impl.h"
+// We must mock the socket manager, for which we need its definition.
+#include "webrtc/test/channel_transport/udp_socket_manager_wrapper.h"
+
+using ::testing::_;
+using ::testing::Return;
+
+namespace webrtc {
+namespace test {
+
+class MockUdpSocketWrapper : public UdpSocketWrapper {
+ public:
+ // The following methods have to be mocked because they are pure.
+ MOCK_METHOD2(SetCallback, bool(CallbackObj, IncomingSocketCallback));
+ MOCK_METHOD1(Bind, bool(const SocketAddress&));
+ MOCK_METHOD0(ValidHandle, bool());
+ MOCK_METHOD4(SetSockopt, bool(int32_t, int32_t,
+ const int8_t*,
+ int32_t));
+ MOCK_METHOD1(SetTOS, int32_t(int32_t));
+ MOCK_METHOD3(SendTo, int32_t(const int8_t*, size_t, const SocketAddress&));
+ MOCK_METHOD8(SetQos, bool(int32_t, int32_t,
+ int32_t, int32_t,
+ int32_t, int32_t,
+ const SocketAddress &,
+ int32_t));
+};
+
+class MockUdpSocketManager : public UdpSocketManager {
+ public:
+ // Access to protected destructor.
+ void Destroy() {
+ delete this;
+ }
+ MOCK_METHOD2(Init, bool(int32_t, uint8_t&));
+ MOCK_METHOD0(Start, bool());
+ MOCK_METHOD0(Stop, bool());
+ MOCK_METHOD1(AddSocket, bool(UdpSocketWrapper*));
+ MOCK_METHOD1(RemoveSocket, bool(UdpSocketWrapper*));
+};
+
+class MockSocketFactory :
+ public UdpTransportImpl::SocketFactoryInterface {
+ public:
+ MockSocketFactory(std::vector<MockUdpSocketWrapper*>* socket_counter)
+ : socket_counter_(socket_counter) {
+ }
+ UdpSocketWrapper* CreateSocket(const int32_t id,
+ UdpSocketManager* mgr,
+ CallbackObj obj,
+ IncomingSocketCallback cb,
+ bool ipV6Enable,
+ bool disableGQOS) {
+ MockUdpSocketWrapper* socket = new MockUdpSocketWrapper();
+ // We instrument the socket with calls that are expected, but do
+ // not matter for any specific test, in order to avoid warning messages.
+ EXPECT_CALL(*socket, ValidHandle()).WillRepeatedly(Return(true));
+ EXPECT_CALL(*socket, Bind(_)).WillOnce(Return(true));
+ socket_counter_->push_back(socket);
+ return socket;
+ }
+ std::vector<MockUdpSocketWrapper*>* socket_counter_;
+};
+
+class UDPTransportTest : public ::testing::Test {
+ public:
+ UDPTransportTest()
+ : sockets_created_(0) {
+ }
+
+ ~UDPTransportTest() {
+ // In production, sockets register themselves at creation time with
+ // an UdpSocketManager, and the UdpSocketManager is responsible for
+ // deleting them. In this test, we just delete them after the test.
+ while (!sockets_created_.empty()) {
+ delete sockets_created_.back();
+ sockets_created_.pop_back();
+ }
+ }
+
+ int NumSocketsCreated() {
+ return sockets_created_.size();
+ }
+
+ std::vector<MockUdpSocketWrapper*>* sockets_created() {
+ return &sockets_created_;
+ }
+private:
+ std::vector<MockUdpSocketWrapper*> sockets_created_;
+};
+
+TEST_F(UDPTransportTest, CreateTransport) {
+ int32_t id = 0;
+ uint8_t threads = 1;
+ UdpTransport* transport = UdpTransport::Create(id, threads);
+ UdpTransport::Destroy(transport);
+}
+
+// This test verifies that the mock_socket is not called from the constructor.
+TEST_F(UDPTransportTest, ConstructorDoesNotCreateSocket) {
+ int32_t id = 0;
+ UdpTransportImpl::SocketFactoryInterface* null_maker = NULL;
+ UdpSocketManager* null_manager = NULL;
+ UdpTransport* transport = new UdpTransportImpl(id,
+ null_maker,
+ null_manager);
+ delete transport;
+}
+
+TEST_F(UDPTransportTest, InitializeSourcePorts) {
+ int32_t id = 0;
+ UdpTransportImpl::SocketFactoryInterface* mock_maker
+ = new MockSocketFactory(sockets_created());
+ MockUdpSocketManager* mock_manager = new MockUdpSocketManager();
+ UdpTransport* transport = new UdpTransportImpl(id,
+ mock_maker,
+ mock_manager);
+ EXPECT_EQ(0, transport->InitializeSourcePorts(4711, 4712));
+ EXPECT_EQ(2, NumSocketsCreated());
+
+ delete transport;
+ mock_manager->Destroy();
+}
+
+} // namespace test
+} // namespace webrtc
diff --git a/webrtc/test/common_unittest.cc b/webrtc/test/common_unittest.cc
new file mode 100644
index 0000000000..082c18c2c7
--- /dev/null
+++ b/webrtc/test/common_unittest.cc
@@ -0,0 +1,80 @@
+/*
+ * 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/common.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace webrtc {
+namespace {
+
+struct MyExperiment {
+ static const int kDefaultFactor;
+ static const int kDefaultOffset;
+
+ MyExperiment()
+ : factor(kDefaultFactor), offset(kDefaultOffset) {}
+
+ MyExperiment(int factor, int offset)
+ : factor(factor), offset(offset) {}
+
+ int factor;
+ int offset;
+};
+
+const int MyExperiment::kDefaultFactor = 1;
+const int MyExperiment::kDefaultOffset = 2;
+
+TEST(Config, ReturnsDefaultInstanceIfNotConfigured) {
+ Config config;
+ const MyExperiment& my_exp = config.Get<MyExperiment>();
+ EXPECT_EQ(MyExperiment::kDefaultFactor, my_exp.factor);
+ EXPECT_EQ(MyExperiment::kDefaultOffset, my_exp.offset);
+}
+
+TEST(Config, ReturnOptionWhenSet) {
+ Config config;
+ config.Set<MyExperiment>(new MyExperiment(5, 1));
+ const MyExperiment& my_exp = config.Get<MyExperiment>();
+ EXPECT_EQ(5, my_exp.factor);
+ EXPECT_EQ(1, my_exp.offset);
+}
+
+TEST(Config, SetNullSetsTheOptionBackToDefault) {
+ Config config;
+ config.Set<MyExperiment>(new MyExperiment(5, 1));
+ config.Set<MyExperiment>(NULL);
+ const MyExperiment& my_exp = config.Get<MyExperiment>();
+ EXPECT_EQ(MyExperiment::kDefaultFactor, my_exp.factor);
+ EXPECT_EQ(MyExperiment::kDefaultOffset, my_exp.offset);
+}
+
+struct Algo1_CostFunction {
+ Algo1_CostFunction() {}
+
+ virtual int cost(int x) const {
+ return x;
+ }
+
+ virtual ~Algo1_CostFunction() {}
+};
+
+struct SqrCost : Algo1_CostFunction {
+ virtual int cost(int x) const {
+ return x*x;
+ }
+};
+
+TEST(Config, SupportsPolymorphism) {
+ Config config;
+ config.Set<Algo1_CostFunction>(new SqrCost());
+ EXPECT_EQ(25, config.Get<Algo1_CostFunction>().cost(5));
+}
+} // namespace
+} // namespace webrtc
diff --git a/webrtc/test/configurable_frame_size_encoder.cc b/webrtc/test/configurable_frame_size_encoder.cc
new file mode 100644
index 0000000000..2cd47504a5
--- /dev/null
+++ b/webrtc/test/configurable_frame_size_encoder.cc
@@ -0,0 +1,92 @@
+/*
+ * 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/configurable_frame_size_encoder.h"
+
+#include <string.h>
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+#include "webrtc/common_video/interface/video_image.h"
+#include "webrtc/modules/video_coding/codecs/interface/video_codec_interface.h"
+
+namespace webrtc {
+namespace test {
+
+ConfigurableFrameSizeEncoder::ConfigurableFrameSizeEncoder(
+ size_t max_frame_size)
+ : callback_(NULL),
+ max_frame_size_(max_frame_size),
+ current_frame_size_(max_frame_size),
+ buffer_(new uint8_t[max_frame_size]) {
+ memset(buffer_.get(), 0, max_frame_size);
+}
+
+ConfigurableFrameSizeEncoder::~ConfigurableFrameSizeEncoder() {}
+
+int32_t ConfigurableFrameSizeEncoder::InitEncode(
+ const VideoCodec* codec_settings,
+ int32_t number_of_cores,
+ size_t max_payload_size) {
+ return WEBRTC_VIDEO_CODEC_OK;
+}
+
+int32_t ConfigurableFrameSizeEncoder::Encode(
+ const VideoFrame& inputImage,
+ const CodecSpecificInfo* codecSpecificInfo,
+ const std::vector<FrameType>* frame_types) {
+ EncodedImage encodedImage(
+ buffer_.get(), current_frame_size_, max_frame_size_);
+ encodedImage._completeFrame = true;
+ encodedImage._encodedHeight = inputImage.height();
+ encodedImage._encodedWidth = inputImage.width();
+ encodedImage._frameType = kVideoFrameKey;
+ encodedImage._timeStamp = inputImage.timestamp();
+ encodedImage.capture_time_ms_ = inputImage.render_time_ms();
+ RTPFragmentationHeader* fragmentation = NULL;
+ CodecSpecificInfo specific;
+ memset(&specific, 0, sizeof(specific));
+ callback_->Encoded(encodedImage, &specific, fragmentation);
+
+ return WEBRTC_VIDEO_CODEC_OK;
+}
+
+int32_t ConfigurableFrameSizeEncoder::RegisterEncodeCompleteCallback(
+ EncodedImageCallback* callback) {
+ callback_ = callback;
+ return WEBRTC_VIDEO_CODEC_OK;
+}
+
+int32_t ConfigurableFrameSizeEncoder::Release() {
+ return WEBRTC_VIDEO_CODEC_OK;
+}
+
+int32_t ConfigurableFrameSizeEncoder::SetChannelParameters(uint32_t packet_loss,
+ int64_t rtt) {
+ return WEBRTC_VIDEO_CODEC_OK;
+}
+
+int32_t ConfigurableFrameSizeEncoder::SetRates(uint32_t new_bit_rate,
+ uint32_t frame_rate) {
+ return WEBRTC_VIDEO_CODEC_OK;
+}
+
+int32_t ConfigurableFrameSizeEncoder::SetPeriodicKeyFrames(bool enable) {
+ return WEBRTC_VIDEO_CODEC_OK;
+}
+
+int32_t ConfigurableFrameSizeEncoder::SetFrameSize(size_t size) {
+ assert(size <= max_frame_size_);
+ current_frame_size_ = size;
+ return WEBRTC_VIDEO_CODEC_OK;
+}
+
+} // namespace test
+} // namespace webrtc
diff --git a/webrtc/test/configurable_frame_size_encoder.h b/webrtc/test/configurable_frame_size_encoder.h
new file mode 100644
index 0000000000..3794e8db08
--- /dev/null
+++ b/webrtc/test/configurable_frame_size_encoder.h
@@ -0,0 +1,58 @@
+/*
+ * 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_CONFIGURABLE_FRAME_SIZE_ENCODER_H_
+#define WEBRTC_TEST_CONFIGURABLE_FRAME_SIZE_ENCODER_H_
+
+#include <vector>
+
+#include "webrtc/base/scoped_ptr.h"
+#include "webrtc/video_encoder.h"
+
+namespace webrtc {
+namespace test {
+
+class ConfigurableFrameSizeEncoder : public VideoEncoder {
+ public:
+ explicit ConfigurableFrameSizeEncoder(size_t max_frame_size);
+ virtual ~ConfigurableFrameSizeEncoder();
+
+ int32_t InitEncode(const VideoCodec* codec_settings,
+ int32_t number_of_cores,
+ size_t max_payload_size) override;
+
+ int32_t Encode(const VideoFrame& input_image,
+ const CodecSpecificInfo* codec_specific_info,
+ const std::vector<FrameType>* frame_types) override;
+
+ int32_t RegisterEncodeCompleteCallback(
+ EncodedImageCallback* callback) override;
+
+ int32_t Release() override;
+
+ int32_t SetChannelParameters(uint32_t packet_loss, int64_t rtt) override;
+
+ int32_t SetRates(uint32_t new_bit_rate, uint32_t frame_rate) override;
+
+ int32_t SetPeriodicKeyFrames(bool enable) override;
+
+ int32_t SetFrameSize(size_t size);
+
+ private:
+ EncodedImageCallback* callback_;
+ const size_t max_frame_size_;
+ size_t current_frame_size_;
+ rtc::scoped_ptr<uint8_t[]> buffer_;
+};
+
+} // namespace test
+} // namespace webrtc
+
+#endif // WEBRTC_TEST_CONFIGURABLE_FRAME_SIZE_ENCODER_H_
diff --git a/webrtc/test/constants.cc b/webrtc/test/constants.cc
new file mode 100644
index 0000000000..7e94fe58fb
--- /dev/null
+++ b/webrtc/test/constants.cc
@@ -0,0 +1,20 @@
+/*
+ * Copyright (c) 2015 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/constants.h"
+
+namespace webrtc {
+namespace test {
+
+const int kTOffsetExtensionId = 6;
+const int kAbsSendTimeExtensionId = 7;
+const int kTransportSequenceNumberExtensionId = 8;
+} // namespace test
+} // namespace webrtc
diff --git a/webrtc/test/constants.h b/webrtc/test/constants.h
new file mode 100644
index 0000000000..14b2ba65bc
--- /dev/null
+++ b/webrtc/test/constants.h
@@ -0,0 +1,18 @@
+/*
+ * Copyright (c) 2015 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.
+ */
+
+namespace webrtc {
+namespace test {
+
+extern const int kTOffsetExtensionId;
+extern const int kAbsSendTimeExtensionId;
+extern const int kTransportSequenceNumberExtensionId;
+} // namespace test
+} // namespace webrtc
diff --git a/webrtc/test/direct_transport.cc b/webrtc/test/direct_transport.cc
new file mode 100644
index 0000000000..6dcba81c88
--- /dev/null
+++ b/webrtc/test/direct_transport.cc
@@ -0,0 +1,104 @@
+/*
+ * 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/direct_transport.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+#include "webrtc/call.h"
+#include "webrtc/system_wrappers/include/clock.h"
+
+namespace webrtc {
+namespace test {
+
+DirectTransport::DirectTransport(Call* send_call)
+ : send_call_(send_call),
+ packet_event_(EventWrapper::Create()),
+ thread_(
+ ThreadWrapper::CreateThread(NetworkProcess, this, "NetworkProcess")),
+ clock_(Clock::GetRealTimeClock()),
+ shutting_down_(false),
+ fake_network_(FakeNetworkPipe::Config()) {
+ EXPECT_TRUE(thread_->Start());
+}
+
+DirectTransport::DirectTransport(const FakeNetworkPipe::Config& config,
+ Call* send_call)
+ : send_call_(send_call),
+ packet_event_(EventWrapper::Create()),
+ thread_(
+ ThreadWrapper::CreateThread(NetworkProcess, this, "NetworkProcess")),
+ clock_(Clock::GetRealTimeClock()),
+ shutting_down_(false),
+ fake_network_(config) {
+ EXPECT_TRUE(thread_->Start());
+}
+
+DirectTransport::~DirectTransport() { StopSending(); }
+
+void DirectTransport::SetConfig(const FakeNetworkPipe::Config& config) {
+ fake_network_.SetConfig(config);
+}
+
+void DirectTransport::StopSending() {
+ {
+ rtc::CritScope crit(&lock_);
+ shutting_down_ = true;
+ }
+
+ packet_event_->Set();
+ EXPECT_TRUE(thread_->Stop());
+}
+
+void DirectTransport::SetReceiver(PacketReceiver* receiver) {
+ fake_network_.SetReceiver(receiver);
+}
+
+bool DirectTransport::SendRtp(const uint8_t* data,
+ size_t length,
+ const PacketOptions& options) {
+ if (send_call_) {
+ rtc::SentPacket sent_packet(options.packet_id,
+ clock_->TimeInMilliseconds());
+ send_call_->OnSentPacket(sent_packet);
+ }
+ fake_network_.SendPacket(data, length);
+ packet_event_->Set();
+ return true;
+}
+
+bool DirectTransport::SendRtcp(const uint8_t* data, size_t length) {
+ fake_network_.SendPacket(data, length);
+ packet_event_->Set();
+ return true;
+}
+
+bool DirectTransport::NetworkProcess(void* transport) {
+ return static_cast<DirectTransport*>(transport)->SendPackets();
+}
+
+bool DirectTransport::SendPackets() {
+ fake_network_.Process();
+ int64_t wait_time_ms = fake_network_.TimeUntilNextProcess();
+ if (wait_time_ms > 0) {
+ switch (packet_event_->Wait(static_cast<unsigned long>(wait_time_ms))) {
+ case kEventSignaled:
+ break;
+ case kEventTimeout:
+ break;
+ case kEventError:
+ // TODO(pbos): Log a warning here?
+ return true;
+ }
+ }
+ rtc::CritScope crit(&lock_);
+ return shutting_down_ ? false : true;
+}
+} // namespace test
+} // namespace webrtc
diff --git a/webrtc/test/direct_transport.h b/webrtc/test/direct_transport.h
new file mode 100644
index 0000000000..241a5bc110
--- /dev/null
+++ b/webrtc/test/direct_transport.h
@@ -0,0 +1,66 @@
+/*
+ * 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_ENGINE_TEST_COMMON_DIRECT_TRANSPORT_H_
+#define WEBRTC_VIDEO_ENGINE_TEST_COMMON_DIRECT_TRANSPORT_H_
+
+#include <assert.h>
+
+#include <deque>
+
+#include "webrtc/base/criticalsection.h"
+#include "webrtc/base/scoped_ptr.h"
+#include "webrtc/system_wrappers/include/event_wrapper.h"
+#include "webrtc/system_wrappers/include/thread_wrapper.h"
+#include "webrtc/test/fake_network_pipe.h"
+#include "webrtc/transport.h"
+
+namespace webrtc {
+
+class Call;
+class Clock;
+class PacketReceiver;
+
+namespace test {
+
+class DirectTransport : public Transport {
+ public:
+ explicit DirectTransport(Call* send_call);
+ DirectTransport(const FakeNetworkPipe::Config& config, Call* send_call);
+ ~DirectTransport();
+
+ void SetConfig(const FakeNetworkPipe::Config& config);
+
+ virtual void StopSending();
+ // TODO(holmer): Look into moving this to the constructor.
+ virtual void SetReceiver(PacketReceiver* receiver);
+
+ bool SendRtp(const uint8_t* data,
+ size_t length,
+ const PacketOptions& options) override;
+ bool SendRtcp(const uint8_t* data, size_t length) override;
+
+ private:
+ static bool NetworkProcess(void* transport);
+ bool SendPackets();
+
+ rtc::CriticalSection lock_;
+ Call* const send_call_;
+ rtc::scoped_ptr<EventWrapper> packet_event_;
+ rtc::scoped_ptr<ThreadWrapper> thread_;
+ Clock* const clock_;
+
+ bool shutting_down_;
+
+ FakeNetworkPipe fake_network_;
+};
+} // namespace test
+} // namespace webrtc
+
+#endif // WEBRTC_VIDEO_ENGINE_TEST_COMMON_DIRECT_TRANSPORT_H_
diff --git a/webrtc/test/encoder_settings.cc b/webrtc/test/encoder_settings.cc
new file mode 100644
index 0000000000..bae13505c0
--- /dev/null
+++ b/webrtc/test/encoder_settings.cc
@@ -0,0 +1,71 @@
+/*
+ * Copyright (c) 2014 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+#include "webrtc/test/encoder_settings.h"
+
+#include <assert.h>
+#include <string.h>
+
+#include "webrtc/test/fake_decoder.h"
+#include "webrtc/video_decoder.h"
+
+namespace webrtc {
+namespace test {
+std::vector<VideoStream> CreateVideoStreams(size_t num_streams) {
+ assert(num_streams > 0);
+
+ // Add more streams to the settings above with reasonable values if required.
+ static const size_t kNumSettings = 3;
+ assert(num_streams <= kNumSettings);
+
+ std::vector<VideoStream> stream_settings(kNumSettings);
+
+ stream_settings[0].width = 320;
+ stream_settings[0].height = 180;
+ stream_settings[0].max_framerate = 30;
+ stream_settings[0].min_bitrate_bps = 50000;
+ stream_settings[0].target_bitrate_bps = stream_settings[0].max_bitrate_bps =
+ 150000;
+ stream_settings[0].max_qp = 56;
+
+ stream_settings[1].width = 640;
+ stream_settings[1].height = 360;
+ stream_settings[1].max_framerate = 30;
+ stream_settings[1].min_bitrate_bps = 200000;
+ stream_settings[1].target_bitrate_bps = stream_settings[1].max_bitrate_bps =
+ 450000;
+ stream_settings[1].max_qp = 56;
+
+ stream_settings[2].width = 1280;
+ stream_settings[2].height = 720;
+ stream_settings[2].max_framerate = 30;
+ stream_settings[2].min_bitrate_bps = 700000;
+ stream_settings[2].target_bitrate_bps = stream_settings[2].max_bitrate_bps =
+ 1500000;
+ stream_settings[2].max_qp = 56;
+ stream_settings.resize(num_streams);
+ return stream_settings;
+}
+
+VideoReceiveStream::Decoder CreateMatchingDecoder(
+ const VideoSendStream::Config::EncoderSettings& encoder_settings) {
+ VideoReceiveStream::Decoder decoder;
+ decoder.payload_type = encoder_settings.payload_type;
+ decoder.payload_name = encoder_settings.payload_name;
+ if (encoder_settings.payload_name == "VP8") {
+ decoder.decoder = VideoDecoder::Create(VideoDecoder::kVp8);
+ } else if (encoder_settings.payload_name == "VP9") {
+ decoder.decoder = VideoDecoder::Create(VideoDecoder::kVp9);
+ } else {
+ decoder.decoder = new FakeDecoder();
+ }
+ return decoder;
+}
+} // namespace test
+} // namespace webrtc
diff --git a/webrtc/test/encoder_settings.h b/webrtc/test/encoder_settings.h
new file mode 100644
index 0000000000..a44d3661e1
--- /dev/null
+++ b/webrtc/test/encoder_settings.h
@@ -0,0 +1,25 @@
+/*
+ * Copyright (c) 2014 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+#ifndef WEBRTC_TEST_ENCODER_SETTINGS_H_
+#define WEBRTC_TEST_ENCODER_SETTINGS_H_
+
+#include "webrtc/video_receive_stream.h"
+#include "webrtc/video_send_stream.h"
+
+namespace webrtc {
+namespace test {
+std::vector<VideoStream> CreateVideoStreams(size_t num_streams);
+
+VideoReceiveStream::Decoder CreateMatchingDecoder(
+ const VideoSendStream::Config::EncoderSettings& encoder_settings);
+} // namespace test
+} // namespace webrtc
+
+#endif // WEBRTC_TEST_ENCODER_SETTINGS_H_
diff --git a/webrtc/test/fake_audio_device.cc b/webrtc/test/fake_audio_device.cc
new file mode 100644
index 0000000000..e307dd7664
--- /dev/null
+++ b/webrtc/test/fake_audio_device.cc
@@ -0,0 +1,151 @@
+/*
+ * 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/include/clock.h"
+#include "webrtc/system_wrappers/include/event_wrapper.h"
+#include "webrtc/system_wrappers/include/file_wrapper.h"
+#include "webrtc/system_wrappers/include/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_(EventTimerWrapper::Create()),
+ 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() {
+ rtc::CritScope cs(&lock_);
+ if (file_utility_->InitPCMReading(*input_stream_.get()) != 0)
+ return -1;
+
+ if (!tick_->StartTimer(true, 10))
+ return -1;
+ thread_ = ThreadWrapper::CreateThread(FakeAudioDevice::Run, this,
+ "FakeAudioDevice");
+ if (thread_.get() == NULL)
+ return -1;
+ if (!thread_->Start()) {
+ thread_.reset();
+ return -1;
+ }
+ thread_->SetPriority(webrtc::kHighPriority);
+ return 0;
+}
+
+int32_t FakeAudioDevice::RegisterAudioCallback(AudioTransport* callback) {
+ rtc::CritScope cs(&lock_);
+ audio_callback_ = callback;
+ return 0;
+}
+
+bool FakeAudioDevice::Playing() const {
+ rtc::CritScope cs(&lock_);
+ return capturing_;
+}
+
+int32_t FakeAudioDevice::PlayoutDelay(uint16_t* delay_ms) const {
+ *delay_ms = 0;
+ return 0;
+}
+
+bool FakeAudioDevice::Recording() const {
+ rtc::CritScope cs(&lock_);
+ return capturing_;
+}
+
+bool FakeAudioDevice::Run(void* obj) {
+ static_cast<FakeAudioDevice*>(obj)->CaptureAudio();
+ return true;
+}
+
+void FakeAudioDevice::CaptureAudio() {
+ {
+ rtc::CritScope cs(&lock_);
+ if (capturing_) {
+ int bytes_read = file_utility_->ReadPCMData(
+ *input_stream_.get(), captured_audio_, kBufferSizeBytes);
+ if (bytes_read <= 0)
+ return;
+ // 2 bytes per sample.
+ size_t num_samples = static_cast<size_t>(bytes_read / 2);
+ 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));
+ size_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(
+ static_cast<size_t>(kFrequencyHz / time_since_last_playout_ms),
+ kBufferSizeBytes / 2);
+ }
+ size_t samples_out = 0;
+ int64_t elapsed_time_ms = -1;
+ int64_t ntp_time_ms = -1;
+ EXPECT_EQ(0,
+ audio_callback_->NeedMorePlayData(samples_needed,
+ 2,
+ 1,
+ kFrequencyHz,
+ playout_buffer_,
+ samples_out,
+ &elapsed_time_ms,
+ &ntp_time_ms));
+ }
+ }
+ tick_->Wait(WEBRTC_EVENT_INFINITE);
+}
+
+void FakeAudioDevice::Start() {
+ rtc::CritScope cs(&lock_);
+ capturing_ = true;
+}
+
+void FakeAudioDevice::Stop() {
+ rtc::CritScope cs(&lock_);
+ capturing_ = false;
+}
+} // namespace test
+} // namespace webrtc
diff --git a/webrtc/test/fake_audio_device.h b/webrtc/test/fake_audio_device.h
new file mode 100644
index 0000000000..bdc672892c
--- /dev/null
+++ b/webrtc/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/base/criticalsection.h"
+#include "webrtc/base/scoped_ptr.h"
+#include "webrtc/modules/audio_device/include/fake_audio_device.h"
+#include "webrtc/typedefs.h"
+
+namespace webrtc {
+
+class Clock;
+class EventTimerWrapper;
+class FileWrapper;
+class ModuleFileUtility;
+class ThreadWrapper;
+
+namespace test {
+
+class FakeAudioDevice : public FakeAudioDeviceModule {
+ public:
+ FakeAudioDevice(Clock* clock, const std::string& filename);
+
+ virtual ~FakeAudioDevice();
+
+ int32_t Init() override;
+ int32_t RegisterAudioCallback(AudioTransport* callback) override;
+
+ bool Playing() const override;
+ int32_t PlayoutDelay(uint16_t* delay_ms) const override;
+ bool Recording() const override;
+
+ void Start();
+ void Stop();
+
+ private:
+ static bool Run(void* obj);
+ void CaptureAudio();
+
+ static const uint32_t kFrequencyHz = 16000;
+ static const size_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_;
+ rtc::scoped_ptr<EventTimerWrapper> tick_;
+ mutable rtc::CriticalSection lock_;
+ rtc::scoped_ptr<ThreadWrapper> thread_;
+ rtc::scoped_ptr<ModuleFileUtility> file_utility_;
+ rtc::scoped_ptr<FileWrapper> input_stream_;
+};
+} // namespace test
+} // namespace webrtc
+
+#endif // WEBRTC_TEST_FAKE_AUDIO_DEVICE_H_
diff --git a/webrtc/test/fake_decoder.cc b/webrtc/test/fake_decoder.cc
new file mode 100644
index 0000000000..63316e0dab
--- /dev/null
+++ b/webrtc/test/fake_decoder.cc
@@ -0,0 +1,87 @@
+/*
+ * 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_decoder.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace webrtc {
+namespace test {
+
+FakeDecoder::FakeDecoder() : callback_(NULL) {}
+
+int32_t FakeDecoder::InitDecode(const VideoCodec* config,
+ int32_t number_of_cores) {
+ config_ = *config;
+ size_t width = config->width;
+ size_t height = config->height;
+ frame_.CreateEmptyFrame(static_cast<int>(width),
+ static_cast<int>(height),
+ static_cast<int>(width),
+ static_cast<int>((width + 1) / 2),
+ static_cast<int>((width + 1) / 2));
+ return WEBRTC_VIDEO_CODEC_OK;
+}
+
+int32_t FakeDecoder::Decode(const EncodedImage& input,
+ bool missing_frames,
+ const RTPFragmentationHeader* fragmentation,
+ const CodecSpecificInfo* codec_specific_info,
+ int64_t render_time_ms) {
+ frame_.set_timestamp(input._timeStamp);
+ frame_.set_ntp_time_ms(input.ntp_time_ms_);
+ frame_.set_render_time_ms(render_time_ms);
+
+ callback_->Decoded(frame_);
+
+ return WEBRTC_VIDEO_CODEC_OK;
+}
+
+int32_t FakeDecoder::RegisterDecodeCompleteCallback(
+ DecodedImageCallback* callback) {
+ callback_ = callback;
+ return WEBRTC_VIDEO_CODEC_OK;
+}
+
+int32_t FakeDecoder::Release() {
+ return WEBRTC_VIDEO_CODEC_OK;
+}
+int32_t FakeDecoder::Reset() {
+ return WEBRTC_VIDEO_CODEC_OK;
+}
+
+int32_t FakeH264Decoder::Decode(const EncodedImage& input,
+ bool missing_frames,
+ const RTPFragmentationHeader* fragmentation,
+ const CodecSpecificInfo* codec_specific_info,
+ int64_t render_time_ms) {
+ uint8_t value = 0;
+ for (size_t i = 0; i < input._length; ++i) {
+ uint8_t kStartCode[] = {0, 0, 0, 1};
+ if (i < input._length - sizeof(kStartCode) &&
+ !memcmp(&input._buffer[i], kStartCode, sizeof(kStartCode))) {
+ i += sizeof(kStartCode) + 1; // Skip start code and NAL header.
+ }
+ if (input._buffer[i] != value) {
+ EXPECT_EQ(value, input._buffer[i])
+ << "Bitstream mismatch between sender and receiver.";
+ return -1;
+ }
+ ++value;
+ }
+ return FakeDecoder::Decode(input,
+ missing_frames,
+ fragmentation,
+ codec_specific_info,
+ render_time_ms);
+}
+
+} // namespace test
+} // namespace webrtc
diff --git a/webrtc/test/fake_decoder.h b/webrtc/test/fake_decoder.h
new file mode 100644
index 0000000000..593af512f8
--- /dev/null
+++ b/webrtc/test/fake_decoder.h
@@ -0,0 +1,74 @@
+/*
+ * 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_ENGINE_TEST_COMMON_FAKE_DECODER_H_
+#define WEBRTC_VIDEO_ENGINE_TEST_COMMON_FAKE_DECODER_H_
+
+#include <vector>
+
+#include "webrtc/modules/video_coding/codecs/interface/video_codec_interface.h"
+#include "webrtc/system_wrappers/include/clock.h"
+
+namespace webrtc {
+namespace test {
+
+class FakeDecoder : public VideoDecoder {
+ public:
+ FakeDecoder();
+ virtual ~FakeDecoder() {}
+
+ int32_t InitDecode(const VideoCodec* config,
+ int32_t number_of_cores) override;
+
+ int32_t Decode(const EncodedImage& input,
+ bool missing_frames,
+ const RTPFragmentationHeader* fragmentation,
+ const CodecSpecificInfo* codec_specific_info,
+ int64_t render_time_ms) override;
+
+ int32_t RegisterDecodeCompleteCallback(
+ DecodedImageCallback* callback) override;
+
+ int32_t Release() override;
+ int32_t Reset() override;
+
+ private:
+ VideoCodec config_;
+ VideoFrame frame_;
+ DecodedImageCallback* callback_;
+};
+
+class FakeH264Decoder : public FakeDecoder {
+ public:
+ virtual ~FakeH264Decoder() {}
+
+ int32_t Decode(const EncodedImage& input,
+ bool missing_frames,
+ const RTPFragmentationHeader* fragmentation,
+ const CodecSpecificInfo* codec_specific_info,
+ int64_t render_time_ms) override;
+};
+
+class FakeNullDecoder : public FakeDecoder {
+ public:
+ virtual ~FakeNullDecoder() {}
+
+ int32_t Decode(const EncodedImage& input,
+ bool missing_frames,
+ const RTPFragmentationHeader* fragmentation,
+ const CodecSpecificInfo* codec_specific_info,
+ int64_t render_time_ms) override {
+ return 0;
+ }
+};
+} // namespace test
+} // namespace webrtc
+
+#endif // WEBRTC_VIDEO_ENGINE_TEST_COMMON_FAKE_DECODER_H_
diff --git a/webrtc/test/fake_encoder.cc b/webrtc/test/fake_encoder.cc
new file mode 100644
index 0000000000..a3ade6e97a
--- /dev/null
+++ b/webrtc/test/fake_encoder.cc
@@ -0,0 +1,197 @@
+/*
+ * 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_encoder.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+#include "webrtc/modules/video_coding/codecs/interface/video_codec_interface.h"
+#include "webrtc/system_wrappers/include/sleep.h"
+
+namespace webrtc {
+namespace test {
+
+FakeEncoder::FakeEncoder(Clock* clock)
+ : clock_(clock),
+ callback_(NULL),
+ target_bitrate_kbps_(0),
+ max_target_bitrate_kbps_(-1),
+ last_encode_time_ms_(0) {
+ // 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::SetMaxBitrate(int max_kbps) {
+ assert(max_kbps >= -1); // max_kbps == -1 disables it.
+ max_target_bitrate_kbps_ = max_kbps;
+}
+
+int32_t FakeEncoder::InitEncode(const VideoCodec* config,
+ int32_t number_of_cores,
+ size_t max_payload_size) {
+ config_ = *config;
+ target_bitrate_kbps_ = config_.startBitrate;
+ return 0;
+}
+
+int32_t FakeEncoder::Encode(const VideoFrame& input_image,
+ const CodecSpecificInfo* codec_specific_info,
+ const std::vector<FrameType>* frame_types) {
+ assert(config_.maxFramerate > 0);
+ int64_t time_since_last_encode_ms = 1000 / config_.maxFramerate;
+ int64_t time_now_ms = clock_->TimeInMilliseconds();
+ const bool first_encode = last_encode_time_ms_ == 0;
+ if (!first_encode) {
+ // For all frames but the first we can estimate the display time by looking
+ // at the display time of the previous frame.
+ time_since_last_encode_ms = time_now_ms - last_encode_time_ms_;
+ }
+
+ size_t bits_available =
+ static_cast<size_t>(target_bitrate_kbps_ * time_since_last_encode_ms);
+ size_t min_bits = static_cast<size_t>(
+ config_.simulcastStream[0].minBitrate * time_since_last_encode_ms);
+ if (bits_available < min_bits)
+ bits_available = min_bits;
+ size_t max_bits =
+ static_cast<size_t>(max_target_bitrate_kbps_ * time_since_last_encode_ms);
+ if (max_bits > 0 && max_bits < bits_available)
+ bits_available = max_bits;
+ last_encode_time_ms_ = time_now_ms;
+
+ assert(config_.numberOfSimulcastStreams > 0);
+ for (unsigned char i = 0; i < config_.numberOfSimulcastStreams; ++i) {
+ CodecSpecificInfo specifics;
+ memset(&specifics, 0, sizeof(specifics));
+ specifics.codecType = kVideoCodecGeneric;
+ specifics.codecSpecific.generic.simulcast_idx = i;
+ size_t min_stream_bits = static_cast<size_t>(
+ config_.simulcastStream[i].minBitrate * time_since_last_encode_ms);
+ size_t max_stream_bits = static_cast<size_t>(
+ config_.simulcastStream[i].maxBitrate * time_since_last_encode_ms);
+ size_t stream_bits = (bits_available > max_stream_bits) ? max_stream_bits :
+ bits_available;
+ size_t stream_bytes = (stream_bits + 7) / 8;
+ if (first_encode) {
+ // The first frame is a key frame and should be larger.
+ // TODO(holmer): The FakeEncoder should store the bits_available between
+ // encodes so that it can compensate for oversized frames.
+ stream_bytes *= 10;
+ }
+ if (stream_bytes > sizeof(encoded_buffer_))
+ stream_bytes = sizeof(encoded_buffer_);
+
+ EncodedImage encoded(
+ encoded_buffer_, stream_bytes, sizeof(encoded_buffer_));
+ encoded._timeStamp = input_image.timestamp();
+ encoded.capture_time_ms_ = input_image.render_time_ms();
+ encoded._frameType = (*frame_types)[i];
+ encoded._encodedWidth = config_.simulcastStream[i].width;
+ encoded._encodedHeight = config_.simulcastStream[i].height;
+ // Always encode something on the first frame.
+ if (min_stream_bits > bits_available && i > 0)
+ continue;
+ assert(callback_ != NULL);
+ if (callback_->Encoded(encoded, &specifics, NULL) != 0)
+ return -1;
+ bits_available -= std::min(encoded._length * 8, bits_available);
+ }
+ return 0;
+}
+
+int32_t FakeEncoder::RegisterEncodeCompleteCallback(
+ EncodedImageCallback* callback) {
+ callback_ = callback;
+ return 0;
+}
+
+int32_t FakeEncoder::Release() { return 0; }
+
+int32_t FakeEncoder::SetChannelParameters(uint32_t packet_loss, int64_t rtt) {
+ return 0;
+}
+
+int32_t FakeEncoder::SetRates(uint32_t new_target_bitrate, uint32_t framerate) {
+ target_bitrate_kbps_ = new_target_bitrate;
+ return 0;
+}
+
+FakeH264Encoder::FakeH264Encoder(Clock* clock)
+ : FakeEncoder(clock), callback_(NULL), idr_counter_(0) {
+ FakeEncoder::RegisterEncodeCompleteCallback(this);
+}
+
+int32_t FakeH264Encoder::RegisterEncodeCompleteCallback(
+ EncodedImageCallback* callback) {
+ callback_ = callback;
+ return 0;
+}
+
+int32_t FakeH264Encoder::Encoded(const EncodedImage& encoded_image,
+ const CodecSpecificInfo* codec_specific_info,
+ const RTPFragmentationHeader* fragments) {
+ const size_t kSpsSize = 8;
+ const size_t kPpsSize = 11;
+ const int kIdrFrequency = 10;
+ RTPFragmentationHeader fragmentation;
+ if (idr_counter_++ % kIdrFrequency == 0 &&
+ encoded_image._length > kSpsSize + kPpsSize + 1) {
+ const size_t kNumSlices = 3;
+ fragmentation.VerifyAndAllocateFragmentationHeader(kNumSlices);
+ fragmentation.fragmentationOffset[0] = 0;
+ fragmentation.fragmentationLength[0] = kSpsSize;
+ fragmentation.fragmentationOffset[1] = kSpsSize;
+ fragmentation.fragmentationLength[1] = kPpsSize;
+ fragmentation.fragmentationOffset[2] = kSpsSize + kPpsSize;
+ fragmentation.fragmentationLength[2] =
+ encoded_image._length - (kSpsSize + kPpsSize);
+ const size_t kSpsNalHeader = 0x67;
+ const size_t kPpsNalHeader = 0x68;
+ const size_t kIdrNalHeader = 0x65;
+ encoded_image._buffer[fragmentation.fragmentationOffset[0]] = kSpsNalHeader;
+ encoded_image._buffer[fragmentation.fragmentationOffset[1]] = kPpsNalHeader;
+ encoded_image._buffer[fragmentation.fragmentationOffset[2]] = kIdrNalHeader;
+ } else {
+ const size_t kNumSlices = 1;
+ fragmentation.VerifyAndAllocateFragmentationHeader(kNumSlices);
+ fragmentation.fragmentationOffset[0] = 0;
+ fragmentation.fragmentationLength[0] = encoded_image._length;
+ const size_t kNalHeader = 0x41;
+ encoded_image._buffer[fragmentation.fragmentationOffset[0]] = kNalHeader;
+ }
+ uint8_t value = 0;
+ int fragment_counter = 0;
+ for (size_t i = 0; i < encoded_image._length; ++i) {
+ if (fragment_counter == fragmentation.fragmentationVectorSize ||
+ i != fragmentation.fragmentationOffset[fragment_counter]) {
+ encoded_image._buffer[i] = value++;
+ } else {
+ ++fragment_counter;
+ }
+ }
+ return callback_->Encoded(encoded_image, NULL, &fragmentation);
+}
+
+DelayedEncoder::DelayedEncoder(Clock* clock, int delay_ms)
+ : test::FakeEncoder(clock),
+ delay_ms_(delay_ms) {}
+
+int32_t DelayedEncoder::Encode(const VideoFrame& input_image,
+ const CodecSpecificInfo* codec_specific_info,
+ const std::vector<FrameType>* frame_types) {
+ SleepMs(delay_ms_);
+ return FakeEncoder::Encode(input_image, codec_specific_info, frame_types);
+}
+} // namespace test
+} // namespace webrtc
diff --git a/webrtc/test/fake_encoder.h b/webrtc/test/fake_encoder.h
new file mode 100644
index 0000000000..d677b92e4d
--- /dev/null
+++ b/webrtc/test/fake_encoder.h
@@ -0,0 +1,85 @@
+/*
+ * 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_ENGINE_TEST_COMMON_FAKE_ENCODER_H_
+#define WEBRTC_VIDEO_ENGINE_TEST_COMMON_FAKE_ENCODER_H_
+
+#include <vector>
+
+#include "webrtc/common_types.h"
+#include "webrtc/system_wrappers/include/clock.h"
+#include "webrtc/video_encoder.h"
+
+namespace webrtc {
+namespace test {
+
+class FakeEncoder : public VideoEncoder {
+ public:
+ explicit FakeEncoder(Clock* clock);
+ virtual ~FakeEncoder();
+
+ // Sets max bitrate. Not thread-safe, call before registering the encoder.
+ void SetMaxBitrate(int max_kbps);
+
+ int32_t InitEncode(const VideoCodec* config,
+ int32_t number_of_cores,
+ size_t max_payload_size) override;
+ int32_t Encode(const VideoFrame& input_image,
+ const CodecSpecificInfo* codec_specific_info,
+ const std::vector<FrameType>* frame_types) override;
+ int32_t RegisterEncodeCompleteCallback(
+ EncodedImageCallback* callback) override;
+ int32_t Release() override;
+ int32_t SetChannelParameters(uint32_t packet_loss, int64_t rtt) override;
+ int32_t SetRates(uint32_t new_target_bitrate, uint32_t framerate) override;
+
+ protected:
+ Clock* const clock_;
+ VideoCodec config_;
+ EncodedImageCallback* callback_;
+ int target_bitrate_kbps_;
+ int max_target_bitrate_kbps_;
+ int64_t last_encode_time_ms_;
+ uint8_t encoded_buffer_[100000];
+};
+
+class FakeH264Encoder : public FakeEncoder, public EncodedImageCallback {
+ public:
+ explicit FakeH264Encoder(Clock* clock);
+ virtual ~FakeH264Encoder() {}
+
+ int32_t RegisterEncodeCompleteCallback(
+ EncodedImageCallback* callback) override;
+
+ int32_t Encoded(const EncodedImage& encodedImage,
+ const CodecSpecificInfo* codecSpecificInfo,
+ const RTPFragmentationHeader* fragments) override;
+
+ private:
+ EncodedImageCallback* callback_;
+ int idr_counter_;
+};
+
+class DelayedEncoder : public test::FakeEncoder {
+ public:
+ DelayedEncoder(Clock* clock, int delay_ms);
+ virtual ~DelayedEncoder() {}
+
+ int32_t Encode(const VideoFrame& input_image,
+ const CodecSpecificInfo* codec_specific_info,
+ const std::vector<FrameType>* frame_types) override;
+
+ private:
+ const int delay_ms_;
+};
+} // namespace test
+} // namespace webrtc
+
+#endif // WEBRTC_VIDEO_ENGINE_TEST_COMMON_FAKE_ENCODER_H_
diff --git a/webrtc/test/fake_network_pipe.cc b/webrtc/test/fake_network_pipe.cc
new file mode 100644
index 0000000000..c36059356a
--- /dev/null
+++ b/webrtc/test/fake_network_pipe.cc
@@ -0,0 +1,217 @@
+/*
+ * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "webrtc/test/fake_network_pipe.h"
+
+#include <assert.h>
+#include <math.h>
+#include <string.h>
+#include <algorithm>
+
+#include "webrtc/call.h"
+#include "webrtc/system_wrappers/include/tick_util.h"
+
+namespace webrtc {
+
+const double kPi = 3.14159265;
+
+static int GaussianRandom(int mean_delay_ms, int standard_deviation_ms) {
+ // Creating a Normal distribution variable from two independent uniform
+ // variables based on the Box-Muller transform.
+ double uniform1 = (rand() + 1.0) / (RAND_MAX + 1.0); // NOLINT
+ double uniform2 = (rand() + 1.0) / (RAND_MAX + 1.0); // NOLINT
+ return static_cast<int>(mean_delay_ms + standard_deviation_ms *
+ sqrt(-2 * log(uniform1)) * cos(2 * kPi * uniform2));
+}
+
+static bool UniformLoss(int loss_percent) {
+ int outcome = rand() % 100;
+ return outcome < loss_percent;
+}
+
+class NetworkPacket {
+ public:
+ NetworkPacket(const uint8_t* data, size_t length, int64_t send_time,
+ int64_t arrival_time)
+ : data_(NULL),
+ data_length_(length),
+ send_time_(send_time),
+ arrival_time_(arrival_time) {
+ data_ = new uint8_t[length];
+ memcpy(data_, data, length);
+ }
+ ~NetworkPacket() {
+ delete [] data_;
+ }
+
+ uint8_t* data() const { return data_; }
+ size_t data_length() const { return data_length_; }
+ int64_t send_time() const { return send_time_; }
+ int64_t arrival_time() const { return arrival_time_; }
+ void IncrementArrivalTime(int64_t extra_delay) {
+ arrival_time_+= extra_delay;
+ }
+
+ private:
+ // The packet data.
+ uint8_t* data_;
+ // Length of data_.
+ size_t data_length_;
+ // The time the packet was sent out on the network.
+ const int64_t send_time_;
+ // The time the packet should arrive at the reciver.
+ int64_t arrival_time_;
+};
+
+FakeNetworkPipe::FakeNetworkPipe(const FakeNetworkPipe::Config& config)
+ : packet_receiver_(NULL),
+ config_(config),
+ dropped_packets_(0),
+ sent_packets_(0),
+ total_packet_delay_(0),
+ next_process_time_(TickTime::MillisecondTimestamp()) {
+}
+
+FakeNetworkPipe::~FakeNetworkPipe() {
+ while (!capacity_link_.empty()) {
+ delete capacity_link_.front();
+ capacity_link_.pop();
+ }
+ while (!delay_link_.empty()) {
+ delete delay_link_.front();
+ delay_link_.pop();
+ }
+}
+
+void FakeNetworkPipe::SetReceiver(PacketReceiver* receiver) {
+ packet_receiver_ = receiver;
+}
+
+void FakeNetworkPipe::SetConfig(const FakeNetworkPipe::Config& config) {
+ rtc::CritScope crit(&lock_);
+ config_ = config; // Shallow copy of the struct.
+}
+
+void FakeNetworkPipe::SendPacket(const uint8_t* data, size_t data_length) {
+ // A NULL packet_receiver_ means that this pipe will terminate the flow of
+ // packets.
+ if (packet_receiver_ == NULL)
+ return;
+ rtc::CritScope crit(&lock_);
+ if (config_.queue_length_packets > 0 &&
+ capacity_link_.size() >= config_.queue_length_packets) {
+ // Too many packet on the link, drop this one.
+ ++dropped_packets_;
+ return;
+ }
+
+ int64_t time_now = TickTime::MillisecondTimestamp();
+
+ // Delay introduced by the link capacity.
+ int64_t capacity_delay_ms = 0;
+ if (config_.link_capacity_kbps > 0)
+ capacity_delay_ms = data_length / (config_.link_capacity_kbps / 8);
+ int64_t network_start_time = time_now;
+
+ // Check if there already are packets on the link and change network start
+ // time if there is.
+ if (capacity_link_.size() > 0)
+ network_start_time = capacity_link_.back()->arrival_time();
+
+ int64_t arrival_time = network_start_time + capacity_delay_ms;
+ NetworkPacket* packet = new NetworkPacket(data, data_length, time_now,
+ arrival_time);
+ capacity_link_.push(packet);
+}
+
+float FakeNetworkPipe::PercentageLoss() {
+ rtc::CritScope crit(&lock_);
+ if (sent_packets_ == 0)
+ return 0;
+
+ return static_cast<float>(dropped_packets_) /
+ (sent_packets_ + dropped_packets_);
+}
+
+int FakeNetworkPipe::AverageDelay() {
+ rtc::CritScope crit(&lock_);
+ if (sent_packets_ == 0)
+ return 0;
+
+ return total_packet_delay_ / static_cast<int>(sent_packets_);
+}
+
+void FakeNetworkPipe::Process() {
+ int64_t time_now = TickTime::MillisecondTimestamp();
+ std::queue<NetworkPacket*> packets_to_deliver;
+ {
+ rtc::CritScope crit(&lock_);
+ // Check the capacity link first.
+ while (capacity_link_.size() > 0 &&
+ time_now >= capacity_link_.front()->arrival_time()) {
+ // Time to get this packet.
+ NetworkPacket* packet = capacity_link_.front();
+ capacity_link_.pop();
+
+ // Packets are randomly dropped after being affected by the bottleneck.
+ if (UniformLoss(config_.loss_percent)) {
+ delete packet;
+ continue;
+ }
+
+ // Add extra delay and jitter, but make sure the arrival time is not
+ // earlier than the last packet in the queue.
+ int extra_delay = GaussianRandom(config_.queue_delay_ms,
+ config_.delay_standard_deviation_ms);
+ if (delay_link_.size() > 0 &&
+ packet->arrival_time() + extra_delay <
+ delay_link_.back()->arrival_time()) {
+ extra_delay = delay_link_.back()->arrival_time() -
+ packet->arrival_time();
+ }
+ packet->IncrementArrivalTime(extra_delay);
+ if (packet->arrival_time() < next_process_time_)
+ next_process_time_ = packet->arrival_time();
+ delay_link_.push(packet);
+ }
+
+ // Check the extra delay queue.
+ while (delay_link_.size() > 0 &&
+ time_now >= delay_link_.front()->arrival_time()) {
+ // Deliver this packet.
+ NetworkPacket* packet = delay_link_.front();
+ packets_to_deliver.push(packet);
+ delay_link_.pop();
+ // |time_now| might be later than when the packet should have arrived, due
+ // to NetworkProcess being called too late. For stats, use the time it
+ // should have been on the link.
+ total_packet_delay_ += packet->arrival_time() - packet->send_time();
+ }
+ sent_packets_ += packets_to_deliver.size();
+ }
+ while (!packets_to_deliver.empty()) {
+ NetworkPacket* packet = packets_to_deliver.front();
+ packets_to_deliver.pop();
+ packet_receiver_->DeliverPacket(MediaType::ANY, packet->data(),
+ packet->data_length(), PacketTime());
+ delete packet;
+ }
+}
+
+int64_t FakeNetworkPipe::TimeUntilNextProcess() const {
+ rtc::CritScope crit(&lock_);
+ const int64_t kDefaultProcessIntervalMs = 30;
+ if (capacity_link_.size() == 0 || delay_link_.size() == 0)
+ return kDefaultProcessIntervalMs;
+ return std::max<int64_t>(
+ next_process_time_ - TickTime::MillisecondTimestamp(), 0);
+}
+
+} // namespace webrtc
diff --git a/webrtc/test/fake_network_pipe.h b/webrtc/test/fake_network_pipe.h
new file mode 100644
index 0000000000..74189a594c
--- /dev/null
+++ b/webrtc/test/fake_network_pipe.h
@@ -0,0 +1,99 @@
+/*
+ * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#ifndef WEBRTC_TEST_FAKE_NETWORK_PIPE_H_
+#define WEBRTC_TEST_FAKE_NETWORK_PIPE_H_
+
+#include <queue>
+
+#include "webrtc/base/constructormagic.h"
+#include "webrtc/base/criticalsection.h"
+#include "webrtc/base/scoped_ptr.h"
+#include "webrtc/system_wrappers/include/event_wrapper.h"
+#include "webrtc/typedefs.h"
+
+namespace webrtc {
+
+class CriticalSectionWrapper;
+class NetworkPacket;
+class PacketReceiver;
+
+// Class faking a network link. This is a simple and naive solution just faking
+// capacity and adding an extra transport delay in addition to the capacity
+// introduced delay.
+
+// TODO(mflodman) Add random and bursty packet loss.
+class FakeNetworkPipe {
+ public:
+ struct Config {
+ Config()
+ : queue_length_packets(0),
+ queue_delay_ms(0),
+ delay_standard_deviation_ms(0),
+ link_capacity_kbps(0),
+ loss_percent(0) {
+ }
+ // Queue length in number of packets.
+ size_t queue_length_packets;
+ // Delay in addition to capacity induced delay.
+ int queue_delay_ms;
+ // Standard deviation of the extra delay.
+ int delay_standard_deviation_ms;
+ // Link capacity in kbps.
+ int link_capacity_kbps;
+ // Random packet loss.
+ int loss_percent;
+ };
+
+ explicit FakeNetworkPipe(const FakeNetworkPipe::Config& config);
+ ~FakeNetworkPipe();
+
+ // Must not be called in parallel with SendPacket or Process.
+ void SetReceiver(PacketReceiver* receiver);
+
+ // Sets a new configuration. This won't affect packets already in the pipe.
+ void SetConfig(const FakeNetworkPipe::Config& config);
+
+ // Sends a new packet to the link.
+ void SendPacket(const uint8_t* packet, size_t packet_length);
+
+ // Processes the network queues and trigger PacketReceiver::IncomingPacket for
+ // packets ready to be delivered.
+ void Process();
+ int64_t TimeUntilNextProcess() const;
+
+ // Get statistics.
+ float PercentageLoss();
+ int AverageDelay();
+ size_t dropped_packets() { return dropped_packets_; }
+ size_t sent_packets() { return sent_packets_; }
+
+ private:
+ mutable rtc::CriticalSection lock_;
+ PacketReceiver* packet_receiver_;
+ std::queue<NetworkPacket*> capacity_link_;
+ std::queue<NetworkPacket*> delay_link_;
+
+ // Link configuration.
+ Config config_;
+
+ // Statistics.
+ size_t dropped_packets_;
+ size_t sent_packets_;
+ int total_packet_delay_;
+
+ int64_t next_process_time_;
+
+ RTC_DISALLOW_COPY_AND_ASSIGN(FakeNetworkPipe);
+};
+
+} // namespace webrtc
+
+#endif // WEBRTC_TEST_FAKE_NETWORK_PIPE_H_
diff --git a/webrtc/test/fake_network_pipe_unittest.cc b/webrtc/test/fake_network_pipe_unittest.cc
new file mode 100644
index 0000000000..02438c59f3
--- /dev/null
+++ b/webrtc/test/fake_network_pipe_unittest.cc
@@ -0,0 +1,301 @@
+/*
+ * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+#include "webrtc/base/scoped_ptr.h"
+#include "webrtc/call.h"
+#include "webrtc/system_wrappers/include/tick_util.h"
+#include "webrtc/test/fake_network_pipe.h"
+
+using ::testing::_;
+using ::testing::AnyNumber;
+using ::testing::Return;
+using ::testing::Invoke;
+
+namespace webrtc {
+
+class MockReceiver : public PacketReceiver {
+ public:
+ MockReceiver() {}
+ virtual ~MockReceiver() {}
+
+ void IncomingPacket(const uint8_t* data, size_t length) {
+ DeliverPacket(MediaType::ANY, data, length, PacketTime());
+ delete [] data;
+ }
+
+ MOCK_METHOD4(
+ DeliverPacket,
+ DeliveryStatus(MediaType, const uint8_t*, size_t, const PacketTime&));
+};
+
+class FakeNetworkPipeTest : public ::testing::Test {
+ protected:
+ virtual void SetUp() {
+ TickTime::UseFakeClock(12345);
+ receiver_.reset(new MockReceiver());
+ ON_CALL(*receiver_, DeliverPacket(_, _, _, _))
+ .WillByDefault(Return(PacketReceiver::DELIVERY_OK));
+ }
+
+ virtual void TearDown() {
+ }
+
+ void SendPackets(FakeNetworkPipe* pipe, int number_packets, int kPacketSize) {
+ rtc::scoped_ptr<uint8_t[]> packet(new uint8_t[kPacketSize]);
+ for (int i = 0; i < number_packets; ++i) {
+ pipe->SendPacket(packet.get(), kPacketSize);
+ }
+ }
+
+ int PacketTimeMs(int capacity_kbps, int kPacketSize) const {
+ return 8 * kPacketSize / capacity_kbps;
+ }
+
+ rtc::scoped_ptr<MockReceiver> receiver_;
+};
+
+void DeleteMemory(uint8_t* data, int length) { delete [] data; }
+
+// Test the capacity link and verify we get as many packets as we expect.
+TEST_F(FakeNetworkPipeTest, CapacityTest) {
+ FakeNetworkPipe::Config config;
+ config.queue_length_packets = 20;
+ config.link_capacity_kbps = 80;
+ rtc::scoped_ptr<FakeNetworkPipe> pipe(new FakeNetworkPipe(config));
+ pipe->SetReceiver(receiver_.get());
+
+ // Add 10 packets of 1000 bytes, = 80 kb, and verify it takes one second to
+ // get through the pipe.
+ const int kNumPackets = 10;
+ const int kPacketSize = 1000;
+ SendPackets(pipe.get(), kNumPackets , kPacketSize);
+
+ // Time to get one packet through the link.
+ const int kPacketTimeMs = PacketTimeMs(config.link_capacity_kbps,
+ kPacketSize);
+
+ // Time haven't increased yet, so we souldn't get any packets.
+ EXPECT_CALL(*receiver_, DeliverPacket(_, _, _, _)).Times(0);
+ pipe->Process();
+
+ // Advance enough time to release one packet.
+ TickTime::AdvanceFakeClock(kPacketTimeMs);
+ EXPECT_CALL(*receiver_, DeliverPacket(_, _, _, _)).Times(1);
+ pipe->Process();
+
+ // Release all but one packet
+ TickTime::AdvanceFakeClock(9 * kPacketTimeMs - 1);
+ EXPECT_CALL(*receiver_, DeliverPacket(_, _, _, _)).Times(8);
+ pipe->Process();
+
+ // And the last one.
+ TickTime::AdvanceFakeClock(1);
+ EXPECT_CALL(*receiver_, DeliverPacket(_, _, _, _)).Times(1);
+ pipe->Process();
+}
+
+// Test the extra network delay.
+TEST_F(FakeNetworkPipeTest, ExtraDelayTest) {
+ FakeNetworkPipe::Config config;
+ config.queue_length_packets = 20;
+ config.queue_delay_ms = 100;
+ config.link_capacity_kbps = 80;
+ rtc::scoped_ptr<FakeNetworkPipe> pipe(new FakeNetworkPipe(config));
+ pipe->SetReceiver(receiver_.get());
+
+ const int kNumPackets = 2;
+ const int kPacketSize = 1000;
+ SendPackets(pipe.get(), kNumPackets , kPacketSize);
+
+ // Time to get one packet through the link.
+ const int kPacketTimeMs = PacketTimeMs(config.link_capacity_kbps,
+ kPacketSize);
+
+ // Increase more than kPacketTimeMs, but not more than the extra delay.
+ TickTime::AdvanceFakeClock(kPacketTimeMs);
+ EXPECT_CALL(*receiver_, DeliverPacket(_, _, _, _)).Times(0);
+ pipe->Process();
+
+ // Advance the network delay to get the first packet.
+ TickTime::AdvanceFakeClock(config.queue_delay_ms);
+ EXPECT_CALL(*receiver_, DeliverPacket(_, _, _, _)).Times(1);
+ pipe->Process();
+
+ // Advance one more kPacketTimeMs to get the last packet.
+ TickTime::AdvanceFakeClock(kPacketTimeMs);
+ EXPECT_CALL(*receiver_, DeliverPacket(_, _, _, _)).Times(1);
+ pipe->Process();
+}
+
+// Test the number of buffers and packets are dropped when sending too many
+// packets too quickly.
+TEST_F(FakeNetworkPipeTest, QueueLengthTest) {
+ FakeNetworkPipe::Config config;
+ config.queue_length_packets = 2;
+ config.link_capacity_kbps = 80;
+ rtc::scoped_ptr<FakeNetworkPipe> pipe(new FakeNetworkPipe(config));
+ pipe->SetReceiver(receiver_.get());
+
+ const int kPacketSize = 1000;
+ const int kPacketTimeMs = PacketTimeMs(config.link_capacity_kbps,
+ kPacketSize);
+
+ // Send three packets and verify only 2 are delivered.
+ SendPackets(pipe.get(), 3, kPacketSize);
+
+ // Increase time enough to deliver all three packets, verify only two are
+ // delivered.
+ TickTime::AdvanceFakeClock(3 * kPacketTimeMs);
+ EXPECT_CALL(*receiver_, DeliverPacket(_, _, _, _)).Times(2);
+ pipe->Process();
+}
+
+// Test we get statistics as expected.
+TEST_F(FakeNetworkPipeTest, StatisticsTest) {
+ FakeNetworkPipe::Config config;
+ config.queue_length_packets = 2;
+ config.queue_delay_ms = 20;
+ config.link_capacity_kbps = 80;
+ rtc::scoped_ptr<FakeNetworkPipe> pipe(new FakeNetworkPipe(config));
+ pipe->SetReceiver(receiver_.get());
+
+ const int kPacketSize = 1000;
+ const int kPacketTimeMs = PacketTimeMs(config.link_capacity_kbps,
+ kPacketSize);
+
+ // Send three packets and verify only 2 are delivered.
+ SendPackets(pipe.get(), 3, kPacketSize);
+ TickTime::AdvanceFakeClock(3 * kPacketTimeMs + config.queue_delay_ms);
+
+ EXPECT_CALL(*receiver_, DeliverPacket(_, _, _, _)).Times(2);
+ pipe->Process();
+
+ // Packet 1: kPacketTimeMs + config.queue_delay_ms,
+ // packet 2: 2 * kPacketTimeMs + config.queue_delay_ms => 170 ms average.
+ EXPECT_EQ(pipe->AverageDelay(), 170);
+ EXPECT_EQ(pipe->sent_packets(), 2u);
+ EXPECT_EQ(pipe->dropped_packets(), 1u);
+ EXPECT_EQ(pipe->PercentageLoss(), 1/3.f);
+}
+
+// Change the link capacity half-way through the test and verify that the
+// delivery times change accordingly.
+TEST_F(FakeNetworkPipeTest, ChangingCapacityWithEmptyPipeTest) {
+ FakeNetworkPipe::Config config;
+ config.queue_length_packets = 20;
+ config.link_capacity_kbps = 80;
+ rtc::scoped_ptr<FakeNetworkPipe> pipe(new FakeNetworkPipe(config));
+ pipe->SetReceiver(receiver_.get());
+
+ // Add 10 packets of 1000 bytes, = 80 kb, and verify it takes one second to
+ // get through the pipe.
+ const int kNumPackets = 10;
+ const int kPacketSize = 1000;
+ SendPackets(pipe.get(), kNumPackets, kPacketSize);
+
+ // Time to get one packet through the link.
+ int packet_time_ms = PacketTimeMs(config.link_capacity_kbps, kPacketSize);
+
+ // Time hasn't increased yet, so we souldn't get any packets.
+ EXPECT_CALL(*receiver_, DeliverPacket(_, _, _, _)).Times(0);
+ pipe->Process();
+
+ // Advance time in steps to release one packet at a time.
+ for (int i = 0; i < kNumPackets; ++i) {
+ TickTime::AdvanceFakeClock(packet_time_ms);
+ EXPECT_CALL(*receiver_, DeliverPacket(_, _, _, _)).Times(1);
+ pipe->Process();
+ }
+
+ // Change the capacity.
+ config.link_capacity_kbps /= 2; // Reduce to 50%.
+ pipe->SetConfig(config);
+
+ // Add another 10 packets of 1000 bytes, = 80 kb, and verify it takes two
+ // seconds to get them through the pipe.
+ SendPackets(pipe.get(), kNumPackets, kPacketSize);
+
+ // Time to get one packet through the link.
+ packet_time_ms = PacketTimeMs(config.link_capacity_kbps, kPacketSize);
+
+ // Time hasn't increased yet, so we souldn't get any packets.
+ EXPECT_CALL(*receiver_, DeliverPacket(_, _, _, _)).Times(0);
+ pipe->Process();
+
+ // Advance time in steps to release one packet at a time.
+ for (int i = 0; i < kNumPackets; ++i) {
+ TickTime::AdvanceFakeClock(packet_time_ms);
+ EXPECT_CALL(*receiver_, DeliverPacket(_, _, _, _)).Times(1);
+ pipe->Process();
+ }
+
+ // Check that all the packets were sent.
+ EXPECT_EQ(static_cast<size_t>(2 * kNumPackets), pipe->sent_packets());
+ TickTime::AdvanceFakeClock(pipe->TimeUntilNextProcess());
+ EXPECT_CALL(*receiver_, DeliverPacket(_, _, _, _)).Times(0);
+ pipe->Process();
+}
+
+// Change the link capacity half-way through the test and verify that the
+// delivery times change accordingly.
+TEST_F(FakeNetworkPipeTest, ChangingCapacityWithPacketsInPipeTest) {
+ FakeNetworkPipe::Config config;
+ config.queue_length_packets = 20;
+ config.link_capacity_kbps = 80;
+ rtc::scoped_ptr<FakeNetworkPipe> pipe(new FakeNetworkPipe(config));
+ pipe->SetReceiver(receiver_.get());
+
+ // Add 10 packets of 1000 bytes, = 80 kb.
+ const int kNumPackets = 10;
+ const int kPacketSize = 1000;
+ SendPackets(pipe.get(), kNumPackets, kPacketSize);
+
+ // Time to get one packet through the link at the initial speed.
+ int packet_time_1_ms = PacketTimeMs(config.link_capacity_kbps, kPacketSize);
+
+ // Change the capacity.
+ config.link_capacity_kbps *= 2; // Double the capacity.
+ pipe->SetConfig(config);
+
+ // Add another 10 packets of 1000 bytes, = 80 kb, and verify it takes two
+ // seconds to get them through the pipe.
+ SendPackets(pipe.get(), kNumPackets, kPacketSize);
+
+ // Time to get one packet through the link at the new capacity.
+ int packet_time_2_ms = PacketTimeMs(config.link_capacity_kbps, kPacketSize);
+
+ // Time hasn't increased yet, so we souldn't get any packets.
+ EXPECT_CALL(*receiver_, DeliverPacket(_, _, _, _)).Times(0);
+ pipe->Process();
+
+ // Advance time in steps to release one packet at a time.
+ for (int i = 0; i < kNumPackets; ++i) {
+ TickTime::AdvanceFakeClock(packet_time_1_ms);
+ EXPECT_CALL(*receiver_, DeliverPacket(_, _, _, _)).Times(1);
+ pipe->Process();
+ }
+
+ // Advance time in steps to release one packet at a time.
+ for (int i = 0; i < kNumPackets; ++i) {
+ TickTime::AdvanceFakeClock(packet_time_2_ms);
+ EXPECT_CALL(*receiver_, DeliverPacket(_, _, _, _)).Times(1);
+ pipe->Process();
+ }
+
+ // Check that all the packets were sent.
+ EXPECT_EQ(static_cast<size_t>(2 * kNumPackets), pipe->sent_packets());
+ TickTime::AdvanceFakeClock(pipe->TimeUntilNextProcess());
+ EXPECT_CALL(*receiver_, DeliverPacket(_, _, _, _)).Times(0);
+ pipe->Process();
+}
+} // namespace webrtc
diff --git a/webrtc/test/fake_texture_frame.h b/webrtc/test/fake_texture_frame.h
new file mode 100644
index 0000000000..dc6abaf745
--- /dev/null
+++ b/webrtc/test/fake_texture_frame.h
@@ -0,0 +1,56 @@
+/*
+ * Copyright (c) 2015 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_TEXTURE_FRAME_H_
+#define WEBRTC_TEST_FAKE_TEXTURE_FRAME_H_
+
+#include "webrtc/base/checks.h"
+#include "webrtc/common_video/interface/video_frame_buffer.h"
+#include "webrtc/video_frame.h"
+
+namespace webrtc {
+namespace test {
+
+class FakeNativeHandle {};
+
+class FakeNativeHandleBuffer : public NativeHandleBuffer {
+ public:
+ FakeNativeHandleBuffer(void* native_handle, int width, int height)
+ : NativeHandleBuffer(native_handle, width, height) {}
+
+ ~FakeNativeHandleBuffer() {
+ delete reinterpret_cast<FakeNativeHandle*>(native_handle_);
+ }
+
+ private:
+ rtc::scoped_refptr<VideoFrameBuffer> NativeToI420Buffer() override {
+ rtc::scoped_refptr<VideoFrameBuffer> buffer(
+ new rtc::RefCountedObject<I420Buffer>(width_, height_));
+ int half_height = (height_ + 1) / 2;
+ int half_width = (width_ + 1) / 2;
+ memset(buffer->MutableData(kYPlane), 0, height_ * width_);
+ memset(buffer->MutableData(kUPlane), 0, half_height * half_width);
+ memset(buffer->MutableData(kVPlane), 0, half_height * half_width);
+ return buffer;
+ }
+};
+
+static VideoFrame CreateFakeNativeHandleFrame(FakeNativeHandle* native_handle,
+ int width,
+ int height,
+ uint32_t timestamp,
+ int64_t render_time_ms,
+ VideoRotation rotation) {
+ return VideoFrame(new rtc::RefCountedObject<FakeNativeHandleBuffer>(
+ native_handle, width, height),
+ timestamp, render_time_ms, rotation);
+}
+} // namespace test
+} // namespace webrtc
+#endif // WEBRTC_TEST_FAKE_TEXTURE_FRAME_H_
diff --git a/webrtc/test/fake_voice_engine.cc b/webrtc/test/fake_voice_engine.cc
new file mode 100644
index 0000000000..1a32e082b7
--- /dev/null
+++ b/webrtc/test/fake_voice_engine.cc
@@ -0,0 +1,70 @@
+/*
+ * Copyright (c) 2015 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_voice_engine.h"
+
+namespace {
+
+webrtc::AudioDecodingCallStats MakeAudioDecodingCallStats() {
+ webrtc::AudioDecodingCallStats stats;
+ stats.calls_to_silence_generator = 234;
+ stats.calls_to_neteq = 567;
+ stats.decoded_normal = 890;
+ stats.decoded_plc = 123;
+ stats.decoded_cng = 456;
+ stats.decoded_plc_cng = 789;
+ return stats;
+}
+} // namespace
+
+namespace webrtc {
+namespace test {
+
+const int FakeVoiceEngine::kSendChannelId = 1;
+const int FakeVoiceEngine::kRecvChannelId = 2;
+const uint32_t FakeVoiceEngine::kSendSsrc = 665;
+const uint32_t FakeVoiceEngine::kRecvSsrc = 667;
+const int FakeVoiceEngine::kSendEchoDelayMedian = 254;
+const int FakeVoiceEngine::kSendEchoDelayStdDev = -3;
+const int FakeVoiceEngine::kSendEchoReturnLoss = -65;
+const int FakeVoiceEngine::kSendEchoReturnLossEnhancement = 101;
+const int FakeVoiceEngine::kRecvJitterBufferDelay = -7;
+const int FakeVoiceEngine::kRecvPlayoutBufferDelay = 302;
+const unsigned int FakeVoiceEngine::kSendSpeechInputLevel = 96;
+const unsigned int FakeVoiceEngine::kRecvSpeechOutputLevel = 99;
+
+const CallStatistics FakeVoiceEngine::kSendCallStats = {
+ 1345, 1678, 1901, 1234, 112, 13456, 17890, 1567, -1890, -1123
+};
+
+const CodecInst FakeVoiceEngine::kSendCodecInst = {
+ -121, "codec_name_send", 48000, -231, -451, -671
+};
+
+const ReportBlock FakeVoiceEngine::kSendReportBlock = {
+ 456, 780, 123, 567, 890, 132, 143, 13354
+};
+
+const CallStatistics FakeVoiceEngine::kRecvCallStats = {
+ 345, 678, 901, 234, -12, 3456, 7890, 567, 890, 123
+};
+
+const CodecInst FakeVoiceEngine::kRecvCodecInst = {
+ 123, "codec_name_recv", 96000, -187, -198, -103
+};
+
+const NetworkStatistics FakeVoiceEngine::kRecvNetworkStats = {
+ 123, 456, false, 0, 0, 789, 12, 345, 678, 901, -1, -1, -1, -1, -1, 0
+};
+
+const AudioDecodingCallStats FakeVoiceEngine::kRecvAudioDecodingCallStats =
+ MakeAudioDecodingCallStats();
+} // namespace test
+} // namespace webrtc
diff --git a/webrtc/test/fake_voice_engine.h b/webrtc/test/fake_voice_engine.h
new file mode 100644
index 0000000000..8f08929720
--- /dev/null
+++ b/webrtc/test/fake_voice_engine.h
@@ -0,0 +1,504 @@
+/*
+ * Copyright (c) 2015 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_AUDIO_FAKE_VOICE_ENGINE_H_
+#define WEBRTC_AUDIO_FAKE_VOICE_ENGINE_H_
+
+#include <vector>
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+#include "webrtc/voice_engine/voice_engine_impl.h"
+
+namespace webrtc {
+namespace test {
+
+// NOTE: This class inherits from VoiceEngineImpl so that its clients will be
+// able to get the various interfaces as usual, via T::GetInterface().
+class FakeVoiceEngine final : public VoiceEngineImpl {
+ public:
+ static const int kSendChannelId;
+ static const int kRecvChannelId;
+ static const uint32_t kSendSsrc;
+ static const uint32_t kRecvSsrc;
+ static const int kSendEchoDelayMedian;
+ static const int kSendEchoDelayStdDev;
+ static const int kSendEchoReturnLoss;
+ static const int kSendEchoReturnLossEnhancement;
+ static const int kRecvJitterBufferDelay;
+ static const int kRecvPlayoutBufferDelay;
+ static const unsigned int kSendSpeechInputLevel;
+ static const unsigned int kRecvSpeechOutputLevel;
+ static const CallStatistics kSendCallStats;
+ static const CodecInst kSendCodecInst;
+ static const ReportBlock kSendReportBlock;
+ static const CallStatistics kRecvCallStats;
+ static const CodecInst kRecvCodecInst;
+ static const NetworkStatistics kRecvNetworkStats;
+ static const AudioDecodingCallStats kRecvAudioDecodingCallStats;
+
+ FakeVoiceEngine() : VoiceEngineImpl(new Config(), true) {
+ // Increase ref count so this object isn't automatically deleted whenever
+ // interfaces are Release():d.
+ ++_ref_count;
+ }
+ ~FakeVoiceEngine() override {
+ // Decrease ref count before base class d-tor is called; otherwise it will
+ // trigger an assertion.
+ --_ref_count;
+ }
+
+ // VoEAudioProcessing
+ int SetNsStatus(bool enable, NsModes mode = kNsUnchanged) override {
+ return -1;
+ }
+ int GetNsStatus(bool& enabled, NsModes& mode) override { return -1; }
+ int SetAgcStatus(bool enable, AgcModes mode = kAgcUnchanged) override {
+ return -1;
+ }
+ int GetAgcStatus(bool& enabled, AgcModes& mode) override { return -1; }
+ int SetAgcConfig(AgcConfig config) override { return -1; }
+ int GetAgcConfig(AgcConfig& config) override { return -1; }
+ int SetEcStatus(bool enable, EcModes mode = kEcUnchanged) override {
+ return -1;
+ }
+ int GetEcStatus(bool& enabled, EcModes& mode) override { return -1; }
+ int EnableDriftCompensation(bool enable) override { return -1; }
+ bool DriftCompensationEnabled() override { return false; }
+ void SetDelayOffsetMs(int offset) override {}
+ int DelayOffsetMs() override { return -1; }
+ int SetAecmMode(AecmModes mode = kAecmSpeakerphone,
+ bool enableCNG = true) override { return -1; }
+ int GetAecmMode(AecmModes& mode, bool& enabledCNG) override { return -1; }
+ int EnableHighPassFilter(bool enable) override { return -1; }
+ bool IsHighPassFilterEnabled() override { return false; }
+ int SetRxNsStatus(int channel,
+ bool enable,
+ NsModes mode = kNsUnchanged) override { return -1; }
+ int GetRxNsStatus(int channel, bool& enabled, NsModes& mode) override {
+ return -1;
+ }
+ int SetRxAgcStatus(int channel,
+ bool enable,
+ AgcModes mode = kAgcUnchanged) override { return -1; }
+ int GetRxAgcStatus(int channel, bool& enabled, AgcModes& mode) override {
+ return -1;
+ }
+ int SetRxAgcConfig(int channel, AgcConfig config) override { return -1; }
+ int GetRxAgcConfig(int channel, AgcConfig& config) override { return -1; }
+ int RegisterRxVadObserver(int channel,
+ VoERxVadCallback& observer) override { return -1; }
+ int DeRegisterRxVadObserver(int channel) override { return -1; }
+ int VoiceActivityIndicator(int channel) override { return -1; }
+ int SetEcMetricsStatus(bool enable) override { return -1; }
+ int GetEcMetricsStatus(bool& enabled) override {
+ enabled = true;
+ return 0;
+ }
+ int GetEchoMetrics(int& ERL, int& ERLE, int& RERL, int& A_NLP) override {
+ ERL = kSendEchoReturnLoss;
+ ERLE = kSendEchoReturnLossEnhancement;
+ RERL = -123456789;
+ A_NLP = 123456789;
+ return 0;
+ }
+ int GetEcDelayMetrics(int& delay_median,
+ int& delay_std,
+ float& fraction_poor_delays) override {
+ delay_median = kSendEchoDelayMedian;
+ delay_std = kSendEchoDelayStdDev;
+ fraction_poor_delays = -12345.7890f;
+ return 0;
+ }
+ int StartDebugRecording(const char* fileNameUTF8) override { return -1; }
+ int StartDebugRecording(FILE* file_handle) override { return -1; }
+ int StopDebugRecording() override { return -1; }
+ int SetTypingDetectionStatus(bool enable) override { return -1; }
+ int GetTypingDetectionStatus(bool& enabled) override { return -1; }
+ int TimeSinceLastTyping(int& seconds) override { return -1; }
+ int SetTypingDetectionParameters(int timeWindow,
+ int costPerTyping,
+ int reportingThreshold,
+ int penaltyDecay,
+ int typeEventDelay = 0) override {
+ return -1;
+ }
+ void EnableStereoChannelSwapping(bool enable) override {}
+ bool IsStereoChannelSwappingEnabled() override { return false; }
+
+ // VoEBase
+ int RegisterVoiceEngineObserver(VoiceEngineObserver& observer) override {
+ return -1;
+ }
+ int DeRegisterVoiceEngineObserver() override { return -1; }
+ int Init(AudioDeviceModule* external_adm = NULL,
+ AudioProcessing* audioproc = NULL) override { return -1; }
+ AudioProcessing* audio_processing() override { return nullptr; }
+ int Terminate() override { return -1; }
+ int CreateChannel() override { return -1; }
+ int CreateChannel(const Config& config) override { return -1; }
+ int DeleteChannel(int channel) override { return -1; }
+ int StartReceive(int channel) override { return -1; }
+ int StopReceive(int channel) override { return -1; }
+ int StartPlayout(int channel) override { return -1; }
+ int StopPlayout(int channel) override { return -1; }
+ int StartSend(int channel) override { return -1; }
+ int StopSend(int channel) override { return -1; }
+ int GetVersion(char version[1024]) override { return -1; }
+ int LastError() override { return -1; }
+ AudioTransport* audio_transport() { return nullptr; }
+ int AssociateSendChannel(int channel, int accociate_send_channel) override {
+ return -1;
+ }
+
+ // VoECodec
+ int NumOfCodecs() override { return -1; }
+ int GetCodec(int index, CodecInst& codec) override { return -1; }
+ int SetSendCodec(int channel, const CodecInst& codec) override { return -1; }
+ int GetSendCodec(int channel, CodecInst& codec) override {
+ EXPECT_EQ(channel, kSendChannelId);
+ codec = kSendCodecInst;
+ return 0;
+ }
+ int SetBitRate(int channel, int bitrate_bps) override { return -1; }
+ int GetRecCodec(int channel, CodecInst& codec) override {
+ EXPECT_EQ(channel, kRecvChannelId);
+ codec = kRecvCodecInst;
+ return 0;
+ }
+ int SetRecPayloadType(int channel, const CodecInst& codec) override {
+ return -1;
+ }
+ int GetRecPayloadType(int channel, CodecInst& codec) override { return -1; }
+ int SetSendCNPayloadType(int channel, int type,
+ PayloadFrequencies frequency = kFreq16000Hz) override { return -1; }
+ int SetVADStatus(int channel,
+ bool enable,
+ VadModes mode = kVadConventional,
+ bool disableDTX = false) override { return -1; }
+ int GetVADStatus(int channel,
+ bool& enabled,
+ VadModes& mode,
+ bool& disabledDTX) override { return -1; }
+ int SetOpusMaxPlaybackRate(int channel, int frequency_hz) override {
+ return -1;
+ }
+ int SetOpusDtx(int channel, bool enable_dtx) override { return -1; }
+ RtcEventLog* GetEventLog() override { return nullptr; }
+
+ // VoEDtmf
+ int SendTelephoneEvent(int channel,
+ int eventCode,
+ bool outOfBand = true,
+ int lengthMs = 160,
+ int attenuationDb = 10) override { return -1; }
+ int SetSendTelephoneEventPayloadType(int channel,
+ unsigned char type) override {
+ return -1;
+ }
+ int GetSendTelephoneEventPayloadType(int channel,
+ unsigned char& type) override {
+ return -1;
+ }
+ int SetDtmfFeedbackStatus(bool enable,
+ bool directFeedback = false) override { return -1; }
+ int GetDtmfFeedbackStatus(bool& enabled, bool& directFeedback) override {
+ return -1;
+ }
+ int PlayDtmfTone(int eventCode,
+ int lengthMs = 200,
+ int attenuationDb = 10) override { return -1; }
+
+ // VoEExternalMedia
+ int RegisterExternalMediaProcessing(
+ int channel,
+ ProcessingTypes type,
+ VoEMediaProcess& processObject) override { return -1; }
+ int DeRegisterExternalMediaProcessing(int channel,
+ ProcessingTypes type) override {
+ return -1;
+ }
+ int GetAudioFrame(int channel,
+ int desired_sample_rate_hz,
+ AudioFrame* frame) override { return -1; }
+ int SetExternalMixing(int channel, bool enable) override { return -1; }
+
+ // VoEFile
+ int StartPlayingFileLocally(
+ int channel,
+ const char fileNameUTF8[1024],
+ bool loop = false,
+ FileFormats format = kFileFormatPcm16kHzFile,
+ float volumeScaling = 1.0,
+ int startPointMs = 0,
+ int stopPointMs = 0) override { return -1; }
+ int StartPlayingFileLocally(
+ int channel,
+ InStream* stream,
+ FileFormats format = kFileFormatPcm16kHzFile,
+ float volumeScaling = 1.0,
+ int startPointMs = 0,
+ int stopPointMs = 0) override { return -1; }
+ int StopPlayingFileLocally(int channel) override { return -1; }
+ int IsPlayingFileLocally(int channel) override { return -1; }
+ int StartPlayingFileAsMicrophone(
+ int channel,
+ const char fileNameUTF8[1024],
+ bool loop = false,
+ bool mixWithMicrophone = false,
+ FileFormats format = kFileFormatPcm16kHzFile,
+ float volumeScaling = 1.0) override { return -1; }
+ int StartPlayingFileAsMicrophone(
+ int channel,
+ InStream* stream,
+ bool mixWithMicrophone = false,
+ FileFormats format = kFileFormatPcm16kHzFile,
+ float volumeScaling = 1.0) override { return -1; }
+ int StopPlayingFileAsMicrophone(int channel) override { return -1; }
+ int IsPlayingFileAsMicrophone(int channel) override { return -1; }
+ int StartRecordingPlayout(int channel,
+ const char* fileNameUTF8,
+ CodecInst* compression = NULL,
+ int maxSizeBytes = -1) override { return -1; }
+ int StopRecordingPlayout(int channel) override { return -1; }
+ int StartRecordingPlayout(int channel,
+ OutStream* stream,
+ CodecInst* compression = NULL) override {
+ return -1;
+ }
+ int StartRecordingMicrophone(const char* fileNameUTF8,
+ CodecInst* compression = NULL,
+ int maxSizeBytes = -1) override { return -1; }
+ int StartRecordingMicrophone(OutStream* stream,
+ CodecInst* compression = NULL) override {
+ return -1;
+ }
+ int StopRecordingMicrophone() override { return -1; }
+
+ // VoEHardware
+ int GetNumOfRecordingDevices(int& devices) override { return -1; }
+
+ // Gets the number of audio devices available for playout.
+ int GetNumOfPlayoutDevices(int& devices) override { return -1; }
+
+ // Gets the name of a specific recording device given by an |index|.
+ // On Windows Vista/7, it also retrieves an additional unique ID
+ // (GUID) for the recording device.
+ int GetRecordingDeviceName(int index,
+ char strNameUTF8[128],
+ char strGuidUTF8[128]) override { return -1; }
+
+ // Gets the name of a specific playout device given by an |index|.
+ // On Windows Vista/7, it also retrieves an additional unique ID
+ // (GUID) for the playout device.
+ int GetPlayoutDeviceName(int index,
+ char strNameUTF8[128],
+ char strGuidUTF8[128]) override { return -1; }
+
+ // Sets the audio device used for recording.
+ int SetRecordingDevice(
+ int index,
+ StereoChannel recordingChannel = kStereoBoth) override { return -1; }
+
+ // Sets the audio device used for playout.
+ int SetPlayoutDevice(int index) override { return -1; }
+
+ // Sets the type of audio device layer to use.
+ int SetAudioDeviceLayer(AudioLayers audioLayer) override { return -1; }
+
+ // Gets the currently used (active) audio device layer.
+ int GetAudioDeviceLayer(AudioLayers& audioLayer) override { return -1; }
+
+ // Native sample rate controls (samples/sec)
+ int SetRecordingSampleRate(unsigned int samples_per_sec) override {
+ return -1;
+ }
+ int RecordingSampleRate(unsigned int* samples_per_sec) const override {
+ return -1;
+ }
+ int SetPlayoutSampleRate(unsigned int samples_per_sec) override {
+ return -1;
+ }
+ int PlayoutSampleRate(unsigned int* samples_per_sec) const override {
+ return -1;
+ }
+
+ // Queries and controls platform audio effects on Android devices.
+ bool BuiltInAECIsAvailable() const override { return false; }
+ int EnableBuiltInAEC(bool enable) override { return -1; }
+ bool BuiltInAGCIsAvailable() const override { return false; }
+ int EnableBuiltInAGC(bool enable) override { return -1; }
+ bool BuiltInNSIsAvailable() const override { return false; }
+ int EnableBuiltInNS(bool enable) override { return -1; }
+
+ // VoENetwork
+ int RegisterExternalTransport(int channel, Transport& transport) override {
+ return -1;
+ }
+ int DeRegisterExternalTransport(int channel) override { return -1; }
+ int ReceivedRTPPacket(int channel,
+ const void* data,
+ size_t length) override { return -1; }
+ int ReceivedRTPPacket(int channel,
+ const void* data,
+ size_t length,
+ const PacketTime& packet_time) override { return -1; }
+ int ReceivedRTCPPacket(int channel,
+ const void* data,
+ size_t length) { return -1; }
+
+ // VoENetEqStats
+ int GetNetworkStatistics(int channel, NetworkStatistics& stats) override {
+ EXPECT_EQ(channel, kRecvChannelId);
+ stats = kRecvNetworkStats;
+ return 0;
+ }
+ int GetDecodingCallStatistics(int channel,
+ AudioDecodingCallStats* stats) const override {
+ EXPECT_EQ(channel, kRecvChannelId);
+ EXPECT_NE(nullptr, stats);
+ *stats = kRecvAudioDecodingCallStats;
+ return 0;
+ }
+
+ // VoERTP_RTCP
+ int SetLocalSSRC(int channel, unsigned int ssrc) override { return -1; }
+ int GetLocalSSRC(int channel, unsigned int& ssrc) override {
+ EXPECT_EQ(channel, kSendChannelId);
+ ssrc = 0;
+ return 0;
+ }
+ int GetRemoteSSRC(int channel, unsigned int& ssrc) override {
+ EXPECT_EQ(channel, kRecvChannelId);
+ ssrc = 0;
+ return 0;
+ }
+ int SetSendAudioLevelIndicationStatus(int channel,
+ bool enable,
+ unsigned char id = 1) override {
+ return -1;
+ }
+ int SetSendAbsoluteSenderTimeStatus(int channel,
+ bool enable,
+ unsigned char id) override { return -1; }
+ int SetReceiveAbsoluteSenderTimeStatus(int channel,
+ bool enable,
+ unsigned char id) override {
+ return -1;
+ }
+ int SetRTCPStatus(int channel, bool enable) override { return -1; }
+ int GetRTCPStatus(int channel, bool& enabled) override { return -1; }
+ int SetRTCP_CNAME(int channel, const char cName[256]) override { return -1; }
+ int GetRTCP_CNAME(int channel, char cName[256]) { return -1; }
+ int GetRemoteRTCP_CNAME(int channel, char cName[256]) override { return -1; }
+ int GetRemoteRTCPData(int channel,
+ unsigned int& NTPHigh,
+ unsigned int& NTPLow,
+ unsigned int& timestamp,
+ unsigned int& playoutTimestamp,
+ unsigned int* jitter = NULL,
+ unsigned short* fractionLost = NULL) override {
+ return -1;
+ }
+ int GetRTPStatistics(int channel,
+ unsigned int& averageJitterMs,
+ unsigned int& maxJitterMs,
+ unsigned int& discardedPackets) override { return -1; }
+ int GetRTCPStatistics(int channel, CallStatistics& stats) override {
+ if (channel == kSendChannelId) {
+ stats = kSendCallStats;
+ } else {
+ EXPECT_EQ(channel, kRecvChannelId);
+ stats = kRecvCallStats;
+ }
+ return 0;
+ }
+ int GetRemoteRTCPReportBlocks(
+ int channel,
+ std::vector<ReportBlock>* receive_blocks) override {
+ EXPECT_EQ(channel, kSendChannelId);
+ EXPECT_NE(receive_blocks, nullptr);
+ EXPECT_EQ(receive_blocks->size(), 0u);
+ webrtc::ReportBlock block = kSendReportBlock;
+ receive_blocks->push_back(block); // Has wrong SSRC.
+ block.source_SSRC = kSendSsrc;
+ receive_blocks->push_back(block); // Correct block.
+ block.fraction_lost = 0;
+ receive_blocks->push_back(block); // Duplicate SSRC, bad fraction_lost.
+ return 0;
+ }
+ int SetNACKStatus(int channel, bool enable, int maxNoPackets) override {
+ return -1;
+ }
+
+ // VoEVideoSync
+ int GetPlayoutBufferSize(int& buffer_ms) override { return -1; }
+ int SetMinimumPlayoutDelay(int channel, int delay_ms) override { return -1; }
+ int SetInitialPlayoutDelay(int channel, int delay_ms) override { return -1; }
+ int GetDelayEstimate(int channel,
+ int* jitter_buffer_delay_ms,
+ int* playout_buffer_delay_ms) override {
+ EXPECT_EQ(channel, kRecvChannelId);
+ *jitter_buffer_delay_ms = kRecvJitterBufferDelay;
+ *playout_buffer_delay_ms = kRecvPlayoutBufferDelay;
+ return 0;
+ }
+ int GetLeastRequiredDelayMs(int channel) const override { return -1; }
+ int SetInitTimestamp(int channel, unsigned int timestamp) override {
+ return -1;
+ }
+ int SetInitSequenceNumber(int channel, short sequenceNumber) override {
+ return -1;
+ }
+ int GetPlayoutTimestamp(int channel, unsigned int& timestamp) override {
+ return -1;
+ }
+ int GetRtpRtcp(int channel,
+ RtpRtcp** rtpRtcpModule,
+ RtpReceiver** rtp_receiver) override { return -1; }
+
+ // VoEVolumeControl
+ int SetSpeakerVolume(unsigned int volume) override { return -1; }
+ int GetSpeakerVolume(unsigned int& volume) override { return -1; }
+ int SetMicVolume(unsigned int volume) override { return -1; }
+ int GetMicVolume(unsigned int& volume) override { return -1; }
+ int SetInputMute(int channel, bool enable) override { return -1; }
+ int GetInputMute(int channel, bool& enabled) override { return -1; }
+ int GetSpeechInputLevel(unsigned int& level) override { return -1; }
+ int GetSpeechOutputLevel(int channel, unsigned int& level) override {
+ return -1;
+ }
+ int GetSpeechInputLevelFullRange(unsigned int& level) override {
+ level = kSendSpeechInputLevel;
+ return 0;
+ }
+ int GetSpeechOutputLevelFullRange(int channel,
+ unsigned int& level) override {
+ EXPECT_EQ(channel, kRecvChannelId);
+ level = kRecvSpeechOutputLevel;
+ return 0;
+ }
+ int SetChannelOutputVolumeScaling(int channel, float scaling) override {
+ return -1;
+ }
+ int GetChannelOutputVolumeScaling(int channel, float& scaling) override {
+ return -1;
+ }
+ int SetOutputVolumePan(int channel, float left, float right) override {
+ return -1;
+ }
+ int GetOutputVolumePan(int channel, float& left, float& right) override {
+ return -1;
+ }
+};
+} // namespace test
+} // namespace webrtc
+
+#endif // WEBRTC_AUDIO_FAKE_VOICE_ENGINE_H_
diff --git a/webrtc/test/field_trial.cc b/webrtc/test/field_trial.cc
new file mode 100644
index 0000000000..613fb67679
--- /dev/null
+++ b/webrtc/test/field_trial.cc
@@ -0,0 +1,103 @@
+/*
+ * Copyright (c) 2014 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "webrtc/test/field_trial.h"
+
+#include <algorithm>
+#include <cassert>
+#include <cstdio>
+#include <cstdlib>
+#include <map>
+#include <string>
+
+#include "webrtc/system_wrappers/include/field_trial.h"
+
+namespace webrtc {
+namespace {
+// Clients of this library have show a clear intent to setup field trials by
+// linking with it. As so try to crash if they forget to call
+// InitFieldTrialsFromString before webrtc tries to access a field trial.
+bool field_trials_initiated_ = false;
+std::map<std::string, std::string> field_trials_;
+} // namespace
+
+namespace field_trial {
+std::string FindFullName(const std::string& trial_name) {
+ assert(field_trials_initiated_);
+ std::map<std::string, std::string>::const_iterator it =
+ field_trials_.find(trial_name);
+ if (it == field_trials_.end())
+ return std::string();
+ return it->second;
+}
+} // namespace field_trial
+
+namespace test {
+// Note: this code is copied from src/base/metrics/field_trial.cc since the aim
+// is to mimic chromium --force-fieldtrials.
+void InitFieldTrialsFromString(const std::string& trials_string) {
+ static const char kPersistentStringSeparator = '/';
+
+ // Catch an error if this is called more than once.
+ assert(!field_trials_initiated_);
+ field_trials_initiated_ = true;
+
+ if (trials_string.empty())
+ return;
+
+ size_t next_item = 0;
+ while (next_item < trials_string.length()) {
+ size_t name_end = trials_string.find(kPersistentStringSeparator, next_item);
+ if (name_end == trials_string.npos || next_item == name_end)
+ break;
+ size_t group_name_end = trials_string.find(kPersistentStringSeparator,
+ name_end + 1);
+ if (group_name_end == trials_string.npos || name_end + 1 == group_name_end)
+ break;
+ std::string name(trials_string, next_item, name_end - next_item);
+ std::string group_name(trials_string, name_end + 1,
+ group_name_end - name_end - 1);
+ next_item = group_name_end + 1;
+
+ // Fail if duplicate with different group name.
+ if (field_trials_.find(name) != field_trials_.end() &&
+ field_trials_.find(name)->second != group_name)
+ break;
+
+ field_trials_[name] = group_name;
+
+ // Successfully parsed all field trials from the string.
+ if (next_item == trials_string.length())
+ return;
+ }
+ // Using fprintf as LOG does not print when this is called early in main.
+ fprintf(stderr, "Invalid field trials string.\n");
+
+ // Using abort so it crashes in both debug and release mode.
+ abort();
+}
+
+ScopedFieldTrials::ScopedFieldTrials(const std::string& config)
+ : previous_field_trials_(field_trials_) {
+ assert(field_trials_initiated_);
+ field_trials_initiated_ = false;
+ field_trials_.clear();
+ InitFieldTrialsFromString(config);
+}
+
+ScopedFieldTrials::~ScopedFieldTrials() {
+ // Should still be initialized, since InitFieldTrials is called from ctor.
+ // That's why we don't restore the flag.
+ assert(field_trials_initiated_);
+ field_trials_ = previous_field_trials_;
+}
+
+} // namespace test
+} // namespace webrtc
diff --git a/webrtc/test/field_trial.h b/webrtc/test/field_trial.h
new file mode 100644
index 0000000000..d448f3411d
--- /dev/null
+++ b/webrtc/test/field_trial.h
@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) 2014 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#ifndef WEBRTC_TEST_FIELD_TRIAL_H_
+#define WEBRTC_TEST_FIELD_TRIAL_H_
+
+#include <string>
+#include <map>
+
+namespace webrtc {
+namespace test {
+
+// Parses enabled field trials from a string config, such as the one passed
+// to chrome's argument --force-fieldtrials and initializes webrtc::field_trial
+// with such a config.
+// E.g.:
+// "WebRTC-experimentFoo/Enabled/WebRTC-experimentBar/Enabled100kbps/"
+// Assigns the process to group "Enabled" on WebRTCExperimentFoo trial
+// and to group "Enabled100kbps" on WebRTCExperimentBar.
+//
+// E.g. invalid config:
+// "WebRTC-experiment1/Enabled" (note missing / separator at the end).
+//
+// Note: This method crashes with an error message if an invalid config is
+// passed to it. That can be used to find out if a binary is parsing the flags.
+void InitFieldTrialsFromString(const std::string& config);
+
+// This class is used to override field-trial configs within specific tests.
+// After this class goes out of scope previous field trials will be restored.
+class ScopedFieldTrials {
+ public:
+ explicit ScopedFieldTrials(const std::string& config);
+ ~ScopedFieldTrials();
+ private:
+ const std::map<std::string, std::string> previous_field_trials_;
+};
+
+} // namespace test
+} // namespace webrtc
+
+#endif // WEBRTC_TEST_FIELD_TRIAL_H_
diff --git a/webrtc/test/frame_generator.cc b/webrtc/test/frame_generator.cc
new file mode 100644
index 0000000000..589dde4bad
--- /dev/null
+++ b/webrtc/test/frame_generator.cc
@@ -0,0 +1,280 @@
+/*
+ * 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/frame_generator.h"
+
+#include <math.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "webrtc/base/checks.h"
+#include "webrtc/common_video/libyuv/include/webrtc_libyuv.h"
+#include "webrtc/system_wrappers/include/clock.h"
+
+namespace webrtc {
+namespace test {
+namespace {
+
+class ChromaGenerator : public FrameGenerator {
+ public:
+ ChromaGenerator(size_t width, size_t height)
+ : angle_(0.0), width_(width), height_(height) {
+ assert(width > 0);
+ assert(height > 0);
+ }
+
+ VideoFrame* NextFrame() override {
+ frame_.CreateEmptyFrame(static_cast<int>(width_),
+ static_cast<int>(height_),
+ static_cast<int>(width_),
+ static_cast<int>((width_ + 1) / 2),
+ static_cast<int>((width_ + 1) / 2));
+ angle_ += 30.0;
+ uint8_t u = fabs(sin(angle_)) * 0xFF;
+ uint8_t v = fabs(cos(angle_)) * 0xFF;
+
+ memset(frame_.buffer(kYPlane), 0x80, frame_.allocated_size(kYPlane));
+ memset(frame_.buffer(kUPlane), u, frame_.allocated_size(kUPlane));
+ memset(frame_.buffer(kVPlane), v, frame_.allocated_size(kVPlane));
+ return &frame_;
+ }
+
+ private:
+ double angle_;
+ size_t width_;
+ size_t height_;
+ VideoFrame frame_;
+};
+
+class YuvFileGenerator : public FrameGenerator {
+ public:
+ YuvFileGenerator(std::vector<FILE*> files,
+ size_t width,
+ size_t height,
+ int frame_repeat_count)
+ : file_index_(0),
+ files_(files),
+ width_(width),
+ height_(height),
+ frame_size_(CalcBufferSize(kI420,
+ static_cast<int>(width_),
+ static_cast<int>(height_))),
+ frame_buffer_(new uint8_t[frame_size_]),
+ frame_display_count_(frame_repeat_count),
+ current_display_count_(0) {
+ assert(width > 0);
+ assert(height > 0);
+ assert(frame_repeat_count > 0);
+ }
+
+ virtual ~YuvFileGenerator() {
+ for (FILE* file : files_)
+ fclose(file);
+ }
+
+ VideoFrame* NextFrame() override {
+ if (current_display_count_ == 0)
+ ReadNextFrame();
+ if (++current_display_count_ >= frame_display_count_)
+ current_display_count_ = 0;
+
+ // If this is the last repeatition of this frame, it's OK to use the
+ // original instance, otherwise use a copy.
+ if (current_display_count_ == frame_display_count_)
+ return &last_read_frame_;
+
+ temp_frame_copy_.CopyFrame(last_read_frame_);
+ return &temp_frame_copy_;
+ }
+
+ void ReadNextFrame() {
+ size_t bytes_read =
+ fread(frame_buffer_.get(), 1, frame_size_, files_[file_index_]);
+ if (bytes_read < frame_size_) {
+ // No more frames to read in this file, rewind and move to next file.
+ rewind(files_[file_index_]);
+ file_index_ = (file_index_ + 1) % files_.size();
+ bytes_read = fread(frame_buffer_.get(), 1, frame_size_,
+ files_[file_index_]);
+ assert(bytes_read >= frame_size_);
+ }
+
+ last_read_frame_.CreateEmptyFrame(
+ static_cast<int>(width_), static_cast<int>(height_),
+ static_cast<int>(width_), static_cast<int>((width_ + 1) / 2),
+ static_cast<int>((width_ + 1) / 2));
+
+ ConvertToI420(kI420, frame_buffer_.get(), 0, 0, static_cast<int>(width_),
+ static_cast<int>(height_), 0, kVideoRotation_0,
+ &last_read_frame_);
+ }
+
+ private:
+ size_t file_index_;
+ const std::vector<FILE*> files_;
+ const size_t width_;
+ const size_t height_;
+ const size_t frame_size_;
+ const rtc::scoped_ptr<uint8_t[]> frame_buffer_;
+ const int frame_display_count_;
+ int current_display_count_;
+ VideoFrame last_read_frame_;
+ VideoFrame temp_frame_copy_;
+};
+
+class ScrollingImageFrameGenerator : public FrameGenerator {
+ public:
+ ScrollingImageFrameGenerator(Clock* clock,
+ const std::vector<FILE*>& files,
+ size_t source_width,
+ size_t source_height,
+ size_t target_width,
+ size_t target_height,
+ int64_t scroll_time_ms,
+ int64_t pause_time_ms)
+ : clock_(clock),
+ start_time_(clock->TimeInMilliseconds()),
+ scroll_time_(scroll_time_ms),
+ pause_time_(pause_time_ms),
+ num_frames_(files.size()),
+ current_frame_num_(num_frames_ - 1),
+ current_source_frame_(nullptr),
+ file_generator_(files, source_width, source_height, 1) {
+ RTC_DCHECK(clock_ != nullptr);
+ RTC_DCHECK_GT(num_frames_, 0u);
+ RTC_DCHECK_GE(source_height, target_height);
+ RTC_DCHECK_GE(source_width, target_width);
+ RTC_DCHECK_GE(scroll_time_ms, 0);
+ RTC_DCHECK_GE(pause_time_ms, 0);
+ RTC_DCHECK_GT(scroll_time_ms + pause_time_ms, 0);
+ current_frame_.CreateEmptyFrame(static_cast<int>(target_width),
+ static_cast<int>(target_height),
+ static_cast<int>(target_width),
+ static_cast<int>((target_width + 1) / 2),
+ static_cast<int>((target_width + 1) / 2));
+ }
+
+ virtual ~ScrollingImageFrameGenerator() {}
+
+ VideoFrame* NextFrame() override {
+ const int64_t kFrameDisplayTime = scroll_time_ + pause_time_;
+ const int64_t now = clock_->TimeInMilliseconds();
+ int64_t ms_since_start = now - start_time_;
+
+ size_t frame_num = (ms_since_start / kFrameDisplayTime) % num_frames_;
+ UpdateSourceFrame(frame_num);
+
+ double scroll_factor;
+ int64_t time_into_frame = ms_since_start % kFrameDisplayTime;
+ if (time_into_frame < scroll_time_) {
+ scroll_factor = static_cast<double>(time_into_frame) / scroll_time_;
+ } else {
+ scroll_factor = 1.0;
+ }
+ CropSourceToScrolledImage(scroll_factor);
+
+ return &current_frame_;
+ }
+
+ void UpdateSourceFrame(size_t frame_num) {
+ while (current_frame_num_ != frame_num) {
+ current_source_frame_ = file_generator_.NextFrame();
+ current_frame_num_ = (current_frame_num_ + 1) % num_frames_;
+ }
+ RTC_DCHECK(current_source_frame_ != nullptr);
+ }
+
+ void CropSourceToScrolledImage(double scroll_factor) {
+ const int kTargetWidth = current_frame_.width();
+ const int kTargetHeight = current_frame_.height();
+ int scroll_margin_x = current_source_frame_->width() - kTargetWidth;
+ int pixels_scrolled_x =
+ static_cast<int>(scroll_margin_x * scroll_factor + 0.5);
+ int scroll_margin_y = current_source_frame_->height() - kTargetHeight;
+ int pixels_scrolled_y =
+ static_cast<int>(scroll_margin_y * scroll_factor + 0.5);
+
+ int offset_y = (current_source_frame_->stride(PlaneType::kYPlane) *
+ pixels_scrolled_y) +
+ pixels_scrolled_x;
+ int offset_u = (current_source_frame_->stride(PlaneType::kUPlane) *
+ (pixels_scrolled_y / 2)) +
+ (pixels_scrolled_x / 2);
+ int offset_v = (current_source_frame_->stride(PlaneType::kVPlane) *
+ (pixels_scrolled_y / 2)) +
+ (pixels_scrolled_x / 2);
+
+ current_frame_.CreateFrame(
+ &current_source_frame_->buffer(PlaneType::kYPlane)[offset_y],
+ &current_source_frame_->buffer(PlaneType::kUPlane)[offset_u],
+ &current_source_frame_->buffer(PlaneType::kVPlane)[offset_v],
+ kTargetWidth, kTargetHeight,
+ current_source_frame_->stride(PlaneType::kYPlane),
+ current_source_frame_->stride(PlaneType::kUPlane),
+ current_source_frame_->stride(PlaneType::kVPlane));
+ }
+
+ Clock* const clock_;
+ const int64_t start_time_;
+ const int64_t scroll_time_;
+ const int64_t pause_time_;
+ const size_t num_frames_;
+ size_t current_frame_num_;
+ VideoFrame* current_source_frame_;
+ VideoFrame current_frame_;
+ YuvFileGenerator file_generator_;
+};
+
+} // namespace
+
+FrameGenerator* FrameGenerator::CreateChromaGenerator(size_t width,
+ size_t height) {
+ return new ChromaGenerator(width, height);
+}
+
+FrameGenerator* FrameGenerator::CreateFromYuvFile(
+ std::vector<std::string> filenames,
+ size_t width,
+ size_t height,
+ int frame_repeat_count) {
+ assert(!filenames.empty());
+ std::vector<FILE*> files;
+ for (const std::string& filename : filenames) {
+ FILE* file = fopen(filename.c_str(), "rb");
+ RTC_DCHECK(file != nullptr);
+ files.push_back(file);
+ }
+
+ return new YuvFileGenerator(files, width, height, frame_repeat_count);
+}
+
+FrameGenerator* FrameGenerator::CreateScrollingInputFromYuvFiles(
+ Clock* clock,
+ std::vector<std::string> filenames,
+ size_t source_width,
+ size_t source_height,
+ size_t target_width,
+ size_t target_height,
+ int64_t scroll_time_ms,
+ int64_t pause_time_ms) {
+ assert(!filenames.empty());
+ std::vector<FILE*> files;
+ for (const std::string& filename : filenames) {
+ FILE* file = fopen(filename.c_str(), "rb");
+ RTC_DCHECK(file != nullptr);
+ files.push_back(file);
+ }
+
+ return new ScrollingImageFrameGenerator(
+ clock, files, source_width, source_height, target_width, target_height,
+ scroll_time_ms, pause_time_ms);
+}
+
+} // namespace test
+} // namespace webrtc
diff --git a/webrtc/test/frame_generator.h b/webrtc/test/frame_generator.h
new file mode 100644
index 0000000000..7f20c749e8
--- /dev/null
+++ b/webrtc/test/frame_generator.h
@@ -0,0 +1,64 @@
+/*
+ * 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_COMMON_VIDEO_TEST_FRAME_GENERATOR_H_
+#define WEBRTC_COMMON_VIDEO_TEST_FRAME_GENERATOR_H_
+
+#include <string>
+#include <vector>
+
+#include "webrtc/typedefs.h"
+#include "webrtc/video_frame.h"
+
+namespace webrtc {
+class Clock;
+namespace test {
+
+class FrameGenerator {
+ public:
+ FrameGenerator() {}
+ virtual ~FrameGenerator() {}
+
+ // Returns video frame that remains valid until next call.
+ virtual VideoFrame* NextFrame() = 0;
+
+ // Creates a test frame generator that creates fully saturated frames with
+ // varying U, V values over time.
+ static FrameGenerator* CreateChromaGenerator(size_t width, size_t height);
+
+ // Creates a frame generator that repeatedly plays a set of yuv files.
+ // The frame_repeat_count determines how many times each frame is shown,
+ // with 1 = show each frame once, etc.
+ static FrameGenerator* CreateFromYuvFile(std::vector<std::string> files,
+ size_t width,
+ size_t height,
+ int frame_repeat_count);
+
+ // Creates a frame generator which takes a set of yuv files (wrapping a
+ // frame generator created by CreateFromYuvFile() above), but outputs frames
+ // that have been cropped to specified resolution: source_width/source_height
+ // is the size of the source images, target_width/target_height is the size of
+ // the cropped output. For each source image read, the cropped viewport will
+ // be scrolled top to bottom/left to right for scroll_tim_ms milliseconds.
+ // After that the image will stay in place for pause_time_ms milliseconds,
+ // and then this will be repeated with the next file from the input set.
+ static FrameGenerator* CreateScrollingInputFromYuvFiles(
+ Clock* clock,
+ std::vector<std::string> filenames,
+ size_t source_width,
+ size_t source_height,
+ size_t target_width,
+ size_t target_height,
+ int64_t scroll_time_ms,
+ int64_t pause_time_ms);
+};
+} // namespace test
+} // namespace webrtc
+
+#endif // WEBRTC_COMMON_VIDEO_TEST_FRAME_GENERATOR_H_
diff --git a/webrtc/test/frame_generator_capturer.cc b/webrtc/test/frame_generator_capturer.cc
new file mode 100644
index 0000000000..70e2c85698
--- /dev/null
+++ b/webrtc/test/frame_generator_capturer.cc
@@ -0,0 +1,137 @@
+/*
+ * 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/frame_generator_capturer.h"
+
+#include "webrtc/base/criticalsection.h"
+#include "webrtc/test/frame_generator.h"
+#include "webrtc/system_wrappers/include/clock.h"
+#include "webrtc/system_wrappers/include/event_wrapper.h"
+#include "webrtc/system_wrappers/include/sleep.h"
+#include "webrtc/system_wrappers/include/thread_wrapper.h"
+#include "webrtc/video_send_stream.h"
+
+namespace webrtc {
+namespace test {
+
+FrameGeneratorCapturer* FrameGeneratorCapturer::Create(VideoCaptureInput* input,
+ size_t width,
+ size_t height,
+ int target_fps,
+ Clock* clock) {
+ FrameGeneratorCapturer* capturer = new FrameGeneratorCapturer(
+ clock, input, FrameGenerator::CreateChromaGenerator(width, height),
+ target_fps);
+ if (!capturer->Init()) {
+ delete capturer;
+ return NULL;
+ }
+
+ return capturer;
+}
+
+FrameGeneratorCapturer* FrameGeneratorCapturer::CreateFromYuvFile(
+ VideoCaptureInput* input,
+ const std::string& file_name,
+ size_t width,
+ size_t height,
+ int target_fps,
+ Clock* clock) {
+ FrameGeneratorCapturer* capturer = new FrameGeneratorCapturer(
+ clock, input,
+ FrameGenerator::CreateFromYuvFile(std::vector<std::string>(1, file_name),
+ width, height, 1),
+ target_fps);
+ if (!capturer->Init()) {
+ delete capturer;
+ return NULL;
+ }
+
+ return capturer;
+}
+
+FrameGeneratorCapturer::FrameGeneratorCapturer(Clock* clock,
+ VideoCaptureInput* input,
+ FrameGenerator* frame_generator,
+ int target_fps)
+ : VideoCapturer(input),
+ clock_(clock),
+ sending_(false),
+ tick_(EventTimerWrapper::Create()),
+ frame_generator_(frame_generator),
+ target_fps_(target_fps),
+ first_frame_capture_time_(-1) {
+ assert(input != NULL);
+ assert(frame_generator != NULL);
+ assert(target_fps > 0);
+}
+
+FrameGeneratorCapturer::~FrameGeneratorCapturer() {
+ Stop();
+
+ if (thread_.get() != NULL)
+ thread_->Stop();
+}
+
+bool FrameGeneratorCapturer::Init() {
+ // This check is added because frame_generator_ might be file based and should
+ // not crash because a file moved.
+ if (frame_generator_.get() == NULL)
+ return false;
+
+ if (!tick_->StartTimer(true, 1000 / target_fps_))
+ return false;
+ thread_ = ThreadWrapper::CreateThread(FrameGeneratorCapturer::Run, this,
+ "FrameGeneratorCapturer");
+ if (thread_.get() == NULL)
+ return false;
+ if (!thread_->Start()) {
+ thread_.reset();
+ return false;
+ }
+ thread_->SetPriority(webrtc::kHighPriority);
+ return true;
+}
+
+bool FrameGeneratorCapturer::Run(void* obj) {
+ static_cast<FrameGeneratorCapturer*>(obj)->InsertFrame();
+ return true;
+}
+
+void FrameGeneratorCapturer::InsertFrame() {
+ {
+ rtc::CritScope cs(&lock_);
+ if (sending_) {
+ VideoFrame* frame = frame_generator_->NextFrame();
+ frame->set_ntp_time_ms(clock_->CurrentNtpInMilliseconds());
+ if (first_frame_capture_time_ == -1) {
+ first_frame_capture_time_ = frame->ntp_time_ms();
+ }
+ input_->IncomingCapturedFrame(*frame);
+ }
+ }
+ tick_->Wait(WEBRTC_EVENT_INFINITE);
+}
+
+void FrameGeneratorCapturer::Start() {
+ rtc::CritScope cs(&lock_);
+ sending_ = true;
+}
+
+void FrameGeneratorCapturer::Stop() {
+ rtc::CritScope cs(&lock_);
+ sending_ = false;
+}
+
+void FrameGeneratorCapturer::ForceFrame() {
+ tick_->Set();
+}
+} // test
+} // webrtc
diff --git a/webrtc/test/frame_generator_capturer.h b/webrtc/test/frame_generator_capturer.h
new file mode 100644
index 0000000000..aff906dfa2
--- /dev/null
+++ b/webrtc/test/frame_generator_capturer.h
@@ -0,0 +1,77 @@
+/*
+ * 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_ENGINE_TEST_COMMON_FRAME_GENERATOR_CAPTURER_H_
+#define WEBRTC_VIDEO_ENGINE_TEST_COMMON_FRAME_GENERATOR_CAPTURER_H_
+
+#include <string>
+
+#include "webrtc/base/criticalsection.h"
+#include "webrtc/base/scoped_ptr.h"
+#include "webrtc/test/video_capturer.h"
+#include "webrtc/typedefs.h"
+
+namespace webrtc {
+
+class CriticalSectionWrapper;
+class EventTimerWrapper;
+class ThreadWrapper;
+
+namespace test {
+
+class FrameGenerator;
+
+class FrameGeneratorCapturer : public VideoCapturer {
+ public:
+ static FrameGeneratorCapturer* Create(VideoCaptureInput* input,
+ size_t width,
+ size_t height,
+ int target_fps,
+ Clock* clock);
+
+ static FrameGeneratorCapturer* CreateFromYuvFile(VideoCaptureInput* input,
+ const std::string& file_name,
+ size_t width,
+ size_t height,
+ int target_fps,
+ Clock* clock);
+ virtual ~FrameGeneratorCapturer();
+
+ void Start() override;
+ void Stop() override;
+ void ForceFrame();
+
+ int64_t first_frame_capture_time() const { return first_frame_capture_time_; }
+
+ FrameGeneratorCapturer(Clock* clock,
+ VideoCaptureInput* input,
+ FrameGenerator* frame_generator,
+ int target_fps);
+ bool Init();
+
+ private:
+ void InsertFrame();
+ static bool Run(void* obj);
+
+ Clock* const clock_;
+ bool sending_;
+
+ rtc::scoped_ptr<EventTimerWrapper> tick_;
+ rtc::CriticalSection lock_;
+ rtc::scoped_ptr<ThreadWrapper> thread_;
+ rtc::scoped_ptr<FrameGenerator> frame_generator_;
+
+ int target_fps_;
+
+ int64_t first_frame_capture_time_;
+};
+} // test
+} // webrtc
+
+#endif // WEBRTC_VIDEO_ENGINE_TEST_COMMON_FRAME_GENERATOR_CAPTURER_H_
diff --git a/webrtc/test/frame_generator_unittest.cc b/webrtc/test/frame_generator_unittest.cc
new file mode 100644
index 0000000000..6376e2c221
--- /dev/null
+++ b/webrtc/test/frame_generator_unittest.cc
@@ -0,0 +1,149 @@
+/*
+ * Copyright (c) 2015 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 <stdio.h>
+#include <string>
+
+#include "testing/gtest/include/gtest/gtest.h"
+#include "webrtc/base/scoped_ptr.h"
+#include "webrtc/test/frame_generator.h"
+#include "webrtc/test/testsupport/fileutils.h"
+
+namespace webrtc {
+namespace test {
+
+static const int kFrameWidth = 4;
+static const int kFrameHeight = 4;
+
+class FrameGeneratorTest : public ::testing::Test {
+ public:
+ void SetUp() override {
+ two_frame_filename_ =
+ test::TempFilename(test::OutputPath(), "2_frame_yuv_file");
+ one_frame_filename_ =
+ test::TempFilename(test::OutputPath(), "1_frame_yuv_file");
+
+ FILE* file = fopen(two_frame_filename_.c_str(), "wb");
+ WriteYuvFile(file, 0, 0, 0);
+ WriteYuvFile(file, 127, 127, 127);
+ fclose(file);
+ file = fopen(one_frame_filename_.c_str(), "wb");
+ WriteYuvFile(file, 255, 255, 255);
+ fclose(file);
+ }
+ void TearDown() override {
+ remove(one_frame_filename_.c_str());
+ remove(two_frame_filename_.c_str());
+ }
+
+ protected:
+ void WriteYuvFile(FILE* file, uint8_t y, uint8_t u, uint8_t v) {
+ assert(file);
+ rtc::scoped_ptr<uint8_t[]> plane_buffer(new uint8_t[y_size]);
+ memset(plane_buffer.get(), y, y_size);
+ fwrite(plane_buffer.get(), 1, y_size, file);
+ memset(plane_buffer.get(), u, uv_size);
+ fwrite(plane_buffer.get(), 1, uv_size, file);
+ memset(plane_buffer.get(), v, uv_size);
+ fwrite(plane_buffer.get(), 1, uv_size, file);
+ }
+
+ void CheckFrameAndMutate(VideoFrame* frame, uint8_t y, uint8_t u, uint8_t v) {
+ // Check that frame is valid, has the correct color and timestamp are clean.
+ ASSERT_NE(nullptr, frame);
+ uint8_t* buffer;
+ ASSERT_EQ(y_size, frame->allocated_size(PlaneType::kYPlane));
+ buffer = frame->buffer(PlaneType::kYPlane);
+ for (int i = 0; i < y_size; ++i)
+ ASSERT_EQ(y, buffer[i]);
+ ASSERT_EQ(uv_size, frame->allocated_size(PlaneType::kUPlane));
+ buffer = frame->buffer(PlaneType::kUPlane);
+ for (int i = 0; i < uv_size; ++i)
+ ASSERT_EQ(u, buffer[i]);
+ ASSERT_EQ(uv_size, frame->allocated_size(PlaneType::kVPlane));
+ buffer = frame->buffer(PlaneType::kVPlane);
+ for (int i = 0; i < uv_size; ++i)
+ ASSERT_EQ(v, buffer[i]);
+ EXPECT_EQ(0, frame->ntp_time_ms());
+ EXPECT_EQ(0, frame->render_time_ms());
+ EXPECT_EQ(0u, frame->timestamp());
+
+ // Mutate to something arbitrary non-zero.
+ frame->set_ntp_time_ms(11);
+ frame->set_render_time_ms(12);
+ frame->set_timestamp(13);
+ }
+
+ std::string two_frame_filename_;
+ std::string one_frame_filename_;
+ const int y_size = kFrameWidth * kFrameHeight;
+ const int uv_size = ((kFrameHeight + 1) / 2) * ((kFrameWidth + 1) / 2);
+};
+
+TEST_F(FrameGeneratorTest, SingleFrameFile) {
+ rtc::scoped_ptr<FrameGenerator> generator(FrameGenerator::CreateFromYuvFile(
+ std::vector<std::string>(1, one_frame_filename_), kFrameWidth,
+ kFrameHeight, 1));
+ CheckFrameAndMutate(generator->NextFrame(), 255, 255, 255);
+ CheckFrameAndMutate(generator->NextFrame(), 255, 255, 255);
+}
+
+TEST_F(FrameGeneratorTest, TwoFrameFile) {
+ rtc::scoped_ptr<FrameGenerator> generator(FrameGenerator::CreateFromYuvFile(
+ std::vector<std::string>(1, two_frame_filename_), kFrameWidth,
+ kFrameHeight, 1));
+ CheckFrameAndMutate(generator->NextFrame(), 0, 0, 0);
+ CheckFrameAndMutate(generator->NextFrame(), 127, 127, 127);
+ CheckFrameAndMutate(generator->NextFrame(), 0, 0, 0);
+}
+
+TEST_F(FrameGeneratorTest, MultipleFrameFiles) {
+ std::vector<std::string> files;
+ files.push_back(two_frame_filename_);
+ files.push_back(one_frame_filename_);
+
+ rtc::scoped_ptr<FrameGenerator> generator(
+ FrameGenerator::CreateFromYuvFile(files, kFrameWidth, kFrameHeight, 1));
+ CheckFrameAndMutate(generator->NextFrame(), 0, 0, 0);
+ CheckFrameAndMutate(generator->NextFrame(), 127, 127, 127);
+ CheckFrameAndMutate(generator->NextFrame(), 255, 255, 255);
+ CheckFrameAndMutate(generator->NextFrame(), 0, 0, 0);
+}
+
+TEST_F(FrameGeneratorTest, TwoFrameFileWithRepeat) {
+ const int kRepeatCount = 3;
+ rtc::scoped_ptr<FrameGenerator> generator(FrameGenerator::CreateFromYuvFile(
+ std::vector<std::string>(1, two_frame_filename_), kFrameWidth,
+ kFrameHeight, kRepeatCount));
+ for (int i = 0; i < kRepeatCount; ++i)
+ CheckFrameAndMutate(generator->NextFrame(), 0, 0, 0);
+ for (int i = 0; i < kRepeatCount; ++i)
+ CheckFrameAndMutate(generator->NextFrame(), 127, 127, 127);
+ CheckFrameAndMutate(generator->NextFrame(), 0, 0, 0);
+}
+
+TEST_F(FrameGeneratorTest, MultipleFrameFilesWithRepeat) {
+ const int kRepeatCount = 3;
+ std::vector<std::string> files;
+ files.push_back(two_frame_filename_);
+ files.push_back(one_frame_filename_);
+ rtc::scoped_ptr<FrameGenerator> generator(FrameGenerator::CreateFromYuvFile(
+ files, kFrameWidth, kFrameHeight, kRepeatCount));
+ for (int i = 0; i < kRepeatCount; ++i)
+ CheckFrameAndMutate(generator->NextFrame(), 0, 0, 0);
+ for (int i = 0; i < kRepeatCount; ++i)
+ CheckFrameAndMutate(generator->NextFrame(), 127, 127, 127);
+ for (int i = 0; i < kRepeatCount; ++i)
+ CheckFrameAndMutate(generator->NextFrame(), 255, 255, 255);
+ CheckFrameAndMutate(generator->NextFrame(), 0, 0, 0);
+}
+
+} // namespace test
+} // namespace webrtc
diff --git a/webrtc/test/gl/gl_renderer.cc b/webrtc/test/gl/gl_renderer.cc
new file mode 100644
index 0000000000..ff87d9999a
--- /dev/null
+++ b/webrtc/test/gl/gl_renderer.cc
@@ -0,0 +1,112 @@
+/*
+ * 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/gl/gl_renderer.h"
+
+#include <string.h>
+
+#include "webrtc/common_video/libyuv/include/webrtc_libyuv.h"
+
+namespace webrtc {
+namespace test {
+
+GlRenderer::GlRenderer()
+ : is_init_(false), buffer_(NULL), width_(0), height_(0) {}
+
+void GlRenderer::Init() {
+ assert(!is_init_);
+ is_init_ = true;
+
+ glGenTextures(1, &texture_);
+}
+
+void GlRenderer::Destroy() {
+ if (!is_init_) {
+ return;
+ }
+
+ is_init_ = false;
+
+ delete[] buffer_;
+ buffer_ = NULL;
+
+ glDeleteTextures(1, &texture_);
+}
+
+void GlRenderer::ResizeViewport(size_t width, size_t height) {
+ // TODO(pbos): Aspect ratio, letterbox the video.
+ glViewport(0, 0, width, height);
+
+ glMatrixMode(GL_PROJECTION);
+ glLoadIdentity();
+ glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
+ glOrtho(0.0f, 1.0f, 1.0f, 0.0f, -1.0f, 1.0f);
+ glMatrixMode(GL_MODELVIEW);
+}
+
+void GlRenderer::ResizeVideo(size_t width, size_t height) {
+ assert(is_init_);
+ width_ = width;
+ height_ = height;
+
+ buffer_size_ = width * height * 4; // BGRA
+
+ delete[] buffer_;
+ buffer_ = new uint8_t[buffer_size_];
+ assert(buffer_ != NULL);
+ memset(buffer_, 0, buffer_size_);
+ glBindTexture(GL_TEXTURE_2D, texture_);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 0);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0);
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_BGRA,
+ GL_UNSIGNED_INT_8_8_8_8, static_cast<GLvoid*>(buffer_));
+}
+
+void GlRenderer::RenderFrame(const webrtc::VideoFrame& frame,
+ int /*render_delay_ms*/) {
+ assert(is_init_);
+
+ if (static_cast<size_t>(frame.width()) != width_ ||
+ static_cast<size_t>(frame.height()) != height_) {
+ ResizeVideo(frame.width(), frame.height());
+ }
+
+ webrtc::ConvertFromI420(frame, kBGRA, 0, buffer_);
+
+ glEnable(GL_TEXTURE_2D);
+ glBindTexture(GL_TEXTURE_2D, texture_);
+ glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width_, height_, GL_BGRA,
+ GL_UNSIGNED_INT_8_8_8_8, buffer_);
+
+ glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+
+ glLoadIdentity();
+
+ glBegin(GL_QUADS);
+ {
+ glTexCoord2f(0.0f, 0.0f);
+ glVertex3f(0.0f, 0.0f, 0.0f);
+
+ glTexCoord2f(0.0f, 1.0f);
+ glVertex3f(0.0f, 1.0f, 0.0f);
+
+ glTexCoord2f(1.0f, 1.0f);
+ glVertex3f(1.0f, 1.0f, 0.0f);
+
+ glTexCoord2f(1.0f, 0.0f);
+ glVertex3f(1.0f, 0.0f, 0.0f);
+ }
+ glEnd();
+
+ glBindTexture(GL_TEXTURE_2D, 0);
+ glFlush();
+}
+} // test
+} // webrtc
diff --git a/webrtc/test/gl/gl_renderer.h b/webrtc/test/gl/gl_renderer.h
new file mode 100644
index 0000000000..27749468a2
--- /dev/null
+++ b/webrtc/test/gl/gl_renderer.h
@@ -0,0 +1,51 @@
+/*
+ * 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_ENGINE_TEST_COMMON_GL_GL_RENDERER_H_
+#define WEBRTC_VIDEO_ENGINE_TEST_COMMON_GL_GL_RENDERER_H_
+
+#ifdef WEBRTC_MAC
+#include <OpenGL/gl.h>
+#else
+#include <GL/gl.h>
+#endif
+
+#include "webrtc/test/video_renderer.h"
+#include "webrtc/typedefs.h"
+
+
+namespace webrtc {
+namespace test {
+
+class GlRenderer : public VideoRenderer {
+ public:
+ void RenderFrame(const webrtc::VideoFrame& frame,
+ int time_to_render_ms) override;
+
+ protected:
+ GlRenderer();
+
+ void Init();
+ void Destroy();
+
+ void ResizeViewport(size_t width, size_t height);
+
+ private:
+ bool is_init_;
+ uint8_t* buffer_;
+ GLuint texture_;
+ size_t width_, height_, buffer_size_;
+
+ void ResizeVideo(size_t width, size_t height);
+};
+} // test
+} // webrtc
+
+#endif // WEBRTC_VIDEO_ENGINE_TEST_COMMON_GL_GL_RENDERER_H_
diff --git a/webrtc/test/histogram.cc b/webrtc/test/histogram.cc
new file mode 100644
index 0000000000..6fcdb6864f
--- /dev/null
+++ b/webrtc/test/histogram.cc
@@ -0,0 +1,82 @@
+/*
+ * Copyright (c) 2015 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/histogram.h"
+
+#include <map>
+
+#include "webrtc/base/criticalsection.h"
+#include "webrtc/base/thread_annotations.h"
+#include "webrtc/system_wrappers/include/metrics.h"
+
+// Test implementation of histogram methods in
+// webrtc/system_wrappers/include/metrics.h.
+
+namespace webrtc {
+namespace {
+struct SampleInfo {
+ SampleInfo(int sample)
+ : last(sample), total(1) {}
+ int last; // Last added sample.
+ int total; // Total number of added samples.
+};
+
+rtc::CriticalSection histogram_crit_;
+// Map holding info about added samples to a histogram (mapped by the histogram
+// name).
+std::map<std::string, SampleInfo> histograms_ GUARDED_BY(histogram_crit_);
+} // namespace
+
+namespace metrics {
+Histogram* HistogramFactoryGetCounts(const std::string& name, int min, int max,
+ int bucket_count) { return NULL; }
+
+Histogram* HistogramFactoryGetEnumeration(const std::string& name,
+ int boundary) { return NULL; }
+
+void HistogramAdd(
+ Histogram* histogram_pointer, const std::string& name, int sample) {
+ rtc::CritScope cs(&histogram_crit_);
+ auto it = histograms_.find(name);
+ if (it == histograms_.end()) {
+ histograms_.insert(std::make_pair(name, SampleInfo(sample)));
+ return;
+ }
+ it->second.last = sample;
+ ++it->second.total;
+}
+} // namespace metrics
+
+namespace test {
+int LastHistogramSample(const std::string& name) {
+ rtc::CritScope cs(&histogram_crit_);
+ const auto it = histograms_.find(name);
+ if (it == histograms_.end()) {
+ return -1;
+ }
+ return it->second.last;
+}
+
+int NumHistogramSamples(const std::string& name) {
+ rtc::CritScope cs(&histogram_crit_);
+ const auto it = histograms_.find(name);
+ if (it == histograms_.end()) {
+ return 0;
+ }
+ return it->second.total;
+}
+
+void ClearHistograms() {
+ rtc::CritScope cs(&histogram_crit_);
+ histograms_.clear();
+}
+} // namespace test
+} // namespace webrtc
+
diff --git a/webrtc/test/histogram.h b/webrtc/test/histogram.h
new file mode 100644
index 0000000000..44ce32b4f4
--- /dev/null
+++ b/webrtc/test/histogram.h
@@ -0,0 +1,33 @@
+/*
+ * Copyright (c) 2015 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_HISTOGRAM_H_
+#define WEBRTC_TEST_HISTOGRAM_H_
+
+#include <string>
+
+namespace webrtc {
+namespace test {
+
+// Returns the last added sample to a histogram (or -1 if the histogram is not
+// found).
+int LastHistogramSample(const std::string& name);
+
+// Returns the number of added samples to a histogram.
+int NumHistogramSamples(const std::string& name);
+
+// Removes all histograms.
+void ClearHistograms();
+
+} // namespace test
+} // namespace webrtc
+
+#endif // WEBRTC_TEST_HISTOGRAM_H_
+
diff --git a/webrtc/test/layer_filtering_transport.cc b/webrtc/test/layer_filtering_transport.cc
new file mode 100644
index 0000000000..a4ebf47f93
--- /dev/null
+++ b/webrtc/test/layer_filtering_transport.cc
@@ -0,0 +1,106 @@
+/*
+ * Copyright (c) 2015 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/base/checks.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/byte_io.h"
+#include "webrtc/modules/rtp_rtcp/source/rtp_format.h"
+#include "webrtc/modules/rtp_rtcp/source/rtp_utility.h"
+#include "webrtc/test/layer_filtering_transport.h"
+
+namespace webrtc {
+namespace test {
+
+LayerFilteringTransport::LayerFilteringTransport(
+ const FakeNetworkPipe::Config& config,
+ Call* send_call,
+ uint8_t vp8_video_payload_type,
+ uint8_t vp9_video_payload_type,
+ uint8_t tl_discard_threshold,
+ uint8_t sl_discard_threshold)
+ : test::DirectTransport(config, send_call),
+ vp8_video_payload_type_(vp8_video_payload_type),
+ vp9_video_payload_type_(vp9_video_payload_type),
+ tl_discard_threshold_(tl_discard_threshold),
+ sl_discard_threshold_(sl_discard_threshold) {}
+
+uint16_t LayerFilteringTransport::NextSequenceNumber(uint32_t ssrc) {
+ auto it = current_seq_nums_.find(ssrc);
+ if (it == current_seq_nums_.end())
+ return current_seq_nums_[ssrc] = 10000;
+ return ++it->second;
+}
+
+bool LayerFilteringTransport::SendRtp(const uint8_t* packet,
+ size_t length,
+ const PacketOptions& options) {
+ if (tl_discard_threshold_ == 0 && sl_discard_threshold_ == 0) {
+ // Nothing to change, forward the packet immediately.
+ return test::DirectTransport::SendRtp(packet, length, options);
+ }
+
+ bool set_marker_bit = false;
+ rtc::scoped_ptr<RtpHeaderParser> parser(RtpHeaderParser::Create());
+ RTPHeader header;
+ parser->Parse(packet, length, &header);
+
+ if (header.payloadType == vp8_video_payload_type_ ||
+ header.payloadType == vp9_video_payload_type_) {
+ const uint8_t* payload = packet + header.headerLength;
+ RTC_DCHECK_GT(length, header.headerLength);
+ const size_t payload_length = length - header.headerLength;
+ RTC_DCHECK_GT(payload_length, header.paddingLength);
+ const size_t payload_data_length = payload_length - header.paddingLength;
+
+ const bool is_vp8 = header.payloadType == vp8_video_payload_type_;
+ rtc::scoped_ptr<RtpDepacketizer> depacketizer(
+ RtpDepacketizer::Create(is_vp8 ? kRtpVideoVp8 : kRtpVideoVp9));
+ RtpDepacketizer::ParsedPayload parsed_payload;
+ if (depacketizer->Parse(&parsed_payload, payload, payload_data_length)) {
+ const uint8_t temporalIdx =
+ is_vp8 ? parsed_payload.type.Video.codecHeader.VP8.temporalIdx
+ : parsed_payload.type.Video.codecHeader.VP9.temporal_idx;
+ const uint8_t spatialIdx =
+ is_vp8 ? kNoSpatialIdx
+ : parsed_payload.type.Video.codecHeader.VP9.spatial_idx;
+ if (sl_discard_threshold_ > 0 &&
+ spatialIdx == sl_discard_threshold_ - 1 &&
+ parsed_payload.type.Video.codecHeader.VP9.end_of_frame) {
+ // This layer is now the last in the superframe.
+ set_marker_bit = true;
+ }
+ if ((tl_discard_threshold_ > 0 && temporalIdx != kNoTemporalIdx &&
+ temporalIdx >= tl_discard_threshold_) ||
+ (sl_discard_threshold_ > 0 && spatialIdx != kNoSpatialIdx &&
+ spatialIdx >= sl_discard_threshold_)) {
+ return true; // Discard the packet.
+ }
+ } else {
+ RTC_NOTREACHED() << "Parse error";
+ }
+ }
+
+ uint8_t temp_buffer[IP_PACKET_SIZE];
+ memcpy(temp_buffer, packet, length);
+
+ // We are discarding some of the packets (specifically, whole layers), so
+ // make sure the marker bit is set properly, and that sequence numbers are
+ // continuous.
+ if (set_marker_bit)
+ temp_buffer[1] |= kRtpMarkerBitMask;
+
+ uint16_t seq_num = NextSequenceNumber(header.ssrc);
+ ByteWriter<uint16_t>::WriteBigEndian(&temp_buffer[2], seq_num);
+ return test::DirectTransport::SendRtp(temp_buffer, length, options);
+}
+
+} // namespace test
+} // namespace webrtc
diff --git a/webrtc/test/layer_filtering_transport.h b/webrtc/test/layer_filtering_transport.h
new file mode 100644
index 0000000000..3f2389a51b
--- /dev/null
+++ b/webrtc/test/layer_filtering_transport.h
@@ -0,0 +1,51 @@
+/*
+ * Copyright (c) 2015 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_LAYER_FILTERING_TRANSPORT_H_
+#define WEBRTC_TEST_LAYER_FILTERING_TRANSPORT_H_
+
+#include "webrtc/call.h"
+#include "webrtc/test/direct_transport.h"
+#include "webrtc/test/fake_network_pipe.h"
+
+#include <map>
+
+namespace webrtc {
+
+namespace test {
+
+class LayerFilteringTransport : public test::DirectTransport {
+ public:
+ LayerFilteringTransport(const FakeNetworkPipe::Config& config,
+ Call* send_call,
+ uint8_t vp8_video_payload_type,
+ uint8_t vp9_video_payload_type,
+ uint8_t tl_discard_threshold,
+ uint8_t sl_discard_threshold);
+ bool SendRtp(const uint8_t* data,
+ size_t length,
+ const PacketOptions& options) override;
+
+ private:
+ uint16_t NextSequenceNumber(uint32_t ssrc);
+ // Used to distinguish between VP8 and VP9.
+ const uint8_t vp8_video_payload_type_;
+ const uint8_t vp9_video_payload_type_;
+ // Discard all temporal/spatial layers with id greater or equal the
+ // threshold. 0 to disable.
+ const uint8_t tl_discard_threshold_;
+ const uint8_t sl_discard_threshold_;
+ // Current sequence number for each SSRC separately.
+ std::map<uint32_t, uint16_t> current_seq_nums_;
+};
+
+} // namespace test
+} // namespace webrtc
+
+#endif // WEBRTC_TEST_LAYER_FILTERING_TRANSPORT_H_
diff --git a/webrtc/test/linux/glx_renderer.cc b/webrtc/test/linux/glx_renderer.cc
new file mode 100644
index 0000000000..450c6bd8a5
--- /dev/null
+++ b/webrtc/test/linux/glx_renderer.cc
@@ -0,0 +1,178 @@
+/*
+ * 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/linux/glx_renderer.h"
+
+#include <assert.h>
+
+#include <X11/Xatom.h>
+#include <X11/Xlib.h>
+
+#include "webrtc/common_video/libyuv/include/webrtc_libyuv.h"
+
+namespace webrtc {
+namespace test {
+
+GlxRenderer::GlxRenderer(size_t width, size_t height)
+ : width_(width),
+ height_(height),
+ display_(NULL),
+ context_(NULL) {
+ assert(width > 0);
+ assert(height > 0);
+}
+
+GlxRenderer::~GlxRenderer() { Destroy(); }
+
+bool GlxRenderer::Init(const char* window_title) {
+ if ((display_ = XOpenDisplay(NULL)) == NULL) {
+ Destroy();
+ return false;
+ }
+
+ int screen = DefaultScreen(display_);
+
+ XVisualInfo* vi;
+ int attr_list[] = { GLX_DOUBLEBUFFER, GLX_RGBA, GLX_RED_SIZE, 4,
+ GLX_GREEN_SIZE, 4, GLX_BLUE_SIZE, 4, GLX_DEPTH_SIZE, 16,
+ None, };
+
+ if ((vi = glXChooseVisual(display_, screen, attr_list)) == NULL) {
+ Destroy();
+ return false;
+ }
+
+ context_ = glXCreateContext(display_, vi, 0, true);
+ if (context_ == NULL) {
+ Destroy();
+ return false;
+ }
+
+ XSetWindowAttributes window_attributes;
+ window_attributes.colormap = XCreateColormap(
+ display_, RootWindow(display_, vi->screen), vi->visual, AllocNone);
+ window_attributes.border_pixel = 0;
+ window_attributes.event_mask = StructureNotifyMask | ExposureMask;
+ window_ = XCreateWindow(display_, RootWindow(display_, vi->screen), 0, 0,
+ width_, height_, 0, vi->depth, InputOutput,
+ vi->visual, CWBorderPixel | CWColormap | CWEventMask,
+ &window_attributes);
+ XFree(vi);
+
+ XSetStandardProperties(display_, window_, window_title, window_title, None,
+ NULL, 0, NULL);
+
+ Atom wm_delete = XInternAtom(display_, "WM_DELETE_WINDOW", True);
+ if (wm_delete != None) {
+ XSetWMProtocols(display_, window_, &wm_delete, 1);
+ }
+
+ XMapRaised(display_, window_);
+
+ if (!glXMakeCurrent(display_, window_, context_)) {
+ Destroy();
+ return false;
+ }
+ GlRenderer::Init();
+ if (!glXMakeCurrent(display_, None, NULL)) {
+ Destroy();
+ return false;
+ }
+
+ Resize(width_, height_);
+ return true;
+}
+
+void GlxRenderer::Destroy() {
+ if (context_ != NULL) {
+ glXMakeCurrent(display_, window_, context_);
+ GlRenderer::Destroy();
+ glXMakeCurrent(display_, None, NULL);
+ glXDestroyContext(display_, context_);
+ context_ = NULL;
+ }
+
+ if (display_ != NULL) {
+ XCloseDisplay(display_);
+ display_ = NULL;
+ }
+}
+
+GlxRenderer* GlxRenderer::Create(const char* window_title, size_t width,
+ size_t height) {
+ GlxRenderer* glx_renderer = new GlxRenderer(width, height);
+ if (!glx_renderer->Init(window_title)) {
+ // TODO(pbos): Add GLX-failed warning here?
+ delete glx_renderer;
+ return NULL;
+ }
+ return glx_renderer;
+}
+
+void GlxRenderer::Resize(size_t width, size_t height) {
+ width_ = width;
+ height_ = height;
+ if (!glXMakeCurrent(display_, window_, context_)) {
+ abort();
+ }
+ GlRenderer::ResizeViewport(width_, height_);
+ if (!glXMakeCurrent(display_, None, NULL)) {
+ abort();
+ }
+
+ XSizeHints* size_hints = XAllocSizeHints();
+ if (size_hints == NULL) {
+ abort();
+ }
+ size_hints->flags = PAspect;
+ size_hints->min_aspect.x = size_hints->max_aspect.x = width_;
+ size_hints->min_aspect.y = size_hints->max_aspect.y = height_;
+ XSetWMNormalHints(display_, window_, size_hints);
+ XFree(size_hints);
+
+ XWindowChanges wc;
+ wc.width = static_cast<int>(width);
+ wc.height = static_cast<int>(height);
+ XConfigureWindow(display_, window_, CWWidth | CWHeight, &wc);
+}
+
+void GlxRenderer::RenderFrame(const webrtc::VideoFrame& frame,
+ int /*render_delay_ms*/) {
+ if (static_cast<size_t>(frame.width()) != width_ ||
+ static_cast<size_t>(frame.height()) != height_) {
+ Resize(static_cast<size_t>(frame.width()),
+ static_cast<size_t>(frame.height()));
+ }
+
+ XEvent event;
+ if (!glXMakeCurrent(display_, window_, context_)) {
+ abort();
+ }
+ while (XPending(display_)) {
+ XNextEvent(display_, &event);
+ switch (event.type) {
+ case ConfigureNotify:
+ GlRenderer::ResizeViewport(event.xconfigure.width,
+ event.xconfigure.height);
+ break;
+ default:
+ break;
+ }
+ }
+
+ GlRenderer::RenderFrame(frame, 0);
+ glXSwapBuffers(display_, window_);
+
+ if (!glXMakeCurrent(display_, None, NULL)) {
+ abort();
+ }
+}
+} // test
+} // webrtc
diff --git a/webrtc/test/linux/glx_renderer.h b/webrtc/test/linux/glx_renderer.h
new file mode 100644
index 0000000000..517f22a038
--- /dev/null
+++ b/webrtc/test/linux/glx_renderer.h
@@ -0,0 +1,48 @@
+/*
+ * 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_ENGINE_TEST_COMMON_LINUX_GLX_RENDERER_H_
+#define WEBRTC_VIDEO_ENGINE_TEST_COMMON_LINUX_GLX_RENDERER_H_
+
+#include <GL/glx.h>
+#include <X11/Xlib.h>
+
+#include "webrtc/test/gl/gl_renderer.h"
+#include "webrtc/typedefs.h"
+
+namespace webrtc {
+namespace test {
+
+class GlxRenderer : public GlRenderer {
+ public:
+ static GlxRenderer* Create(const char* window_title, size_t width,
+ size_t height);
+ virtual ~GlxRenderer();
+
+ void RenderFrame(const webrtc::VideoFrame& frame, int delta) override;
+ bool IsTextureSupported() const override { return false; }
+
+ private:
+ GlxRenderer(size_t width, size_t height);
+
+ bool Init(const char* window_title);
+ void Resize(size_t width, size_t height);
+ void Destroy();
+
+ size_t width_, height_;
+
+ Display* display_;
+ Window window_;
+ GLXContext context_;
+};
+} // test
+} // webrtc
+
+#endif // WEBRTC_VIDEO_ENGINE_TEST_COMMON_LINUX_GLX_RENDERER_H_
diff --git a/webrtc/test/linux/video_renderer_linux.cc b/webrtc/test/linux/video_renderer_linux.cc
new file mode 100644
index 0000000000..6f69dd7498
--- /dev/null
+++ b/webrtc/test/linux/video_renderer_linux.cc
@@ -0,0 +1,27 @@
+/*
+ * 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/video_renderer.h"
+
+#include "webrtc/test/linux/glx_renderer.h"
+
+namespace webrtc {
+namespace test {
+
+VideoRenderer* VideoRenderer::CreatePlatformRenderer(const char* window_title,
+ size_t width,
+ size_t height) {
+ GlxRenderer* glx_renderer = GlxRenderer::Create(window_title, width, height);
+ if (glx_renderer != NULL) {
+ return glx_renderer;
+ }
+ return NULL;
+}
+} // test
+} // webrtc
diff --git a/webrtc/test/mac/run_test.mm b/webrtc/test/mac/run_test.mm
new file mode 100644
index 0000000000..4e0093a9b6
--- /dev/null
+++ b/webrtc/test/mac/run_test.mm
@@ -0,0 +1,75 @@
+/*
+ * 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.
+ */
+
+#import <Cocoa/Cocoa.h>
+
+#include "webrtc/test/run_test.h"
+
+// Converting a C++ function pointer to an Objective-C block.
+typedef void(^TestBlock)();
+TestBlock functionToBlock(void(*function)()) {
+ return [^(void) { function(); } copy];
+}
+
+// Class calling the test function on the platform specific thread.
+@interface TestRunner : NSObject {
+ BOOL running_;
+}
+- (void)runAllTests:(TestBlock)ignored;
+- (BOOL)running;
+@end
+
+@implementation TestRunner
+- (id)init {
+ self = [super init];
+ if (self) {
+ running_ = YES;
+ }
+ return self;
+}
+
+- (void)runAllTests:(TestBlock)testBlock {
+ NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
+ testBlock();
+ running_ = NO;
+ [pool release];
+}
+
+- (BOOL)running {
+ return running_;
+}
+@end
+
+namespace webrtc {
+namespace test {
+
+void RunTest(void(*test)()) {
+ NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
+ [NSApplication sharedApplication];
+
+ // Convert the function pointer to an Objective-C block and call on a
+ // separate thread, to avoid blocking the main thread.
+ TestRunner *testRunner = [[TestRunner alloc] init];
+ TestBlock testBlock = functionToBlock(test);
+ [NSThread detachNewThreadSelector:@selector(runAllTests:)
+ toTarget:testRunner
+ withObject:testBlock];
+
+ NSRunLoop *runLoop = [NSRunLoop currentRunLoop];
+ while ([testRunner running] &&
+ [runLoop runMode:NSDefaultRunLoopMode
+ beforeDate:[NSDate dateWithTimeIntervalSinceNow:0.1]]);
+
+ [testRunner release];
+ [pool release];
+}
+
+} // namespace test
+} // namespace webrtc
diff --git a/webrtc/test/mac/video_renderer_mac.h b/webrtc/test/mac/video_renderer_mac.h
new file mode 100644
index 0000000000..2e55538954
--- /dev/null
+++ b/webrtc/test/mac/video_renderer_mac.h
@@ -0,0 +1,41 @@
+/*
+ * 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_ENGINE_TEST_COMMON_MAC_VIDEO_RENDERER_MAC_H_
+#define WEBRTC_VIDEO_ENGINE_TEST_COMMON_MAC_VIDEO_RENDERER_MAC_H_
+
+#include "webrtc/base/constructormagic.h"
+#include "webrtc/test/gl/gl_renderer.h"
+
+@class CocoaWindow;
+
+namespace webrtc {
+namespace test {
+
+class MacRenderer : public GlRenderer {
+ public:
+ MacRenderer();
+ virtual ~MacRenderer();
+
+ bool Init(const char* window_title, int width, int height);
+
+ // Implements GlRenderer.
+ void RenderFrame(const VideoFrame& frame, int delta) override;
+ bool IsTextureSupported() const override { return false; }
+
+ private:
+ CocoaWindow* window_;
+
+ RTC_DISALLOW_COPY_AND_ASSIGN(MacRenderer);
+};
+} // test
+} // webrtc
+
+#endif // WEBRTC_VIDEO_ENGINE_TEST_COMMON_MAC_VIDEO_RENDERER_MAC_H_
diff --git a/webrtc/test/mac/video_renderer_mac.mm b/webrtc/test/mac/video_renderer_mac.mm
new file mode 100644
index 0000000000..9cde95a982
--- /dev/null
+++ b/webrtc/test/mac/video_renderer_mac.mm
@@ -0,0 +1,134 @@
+/*
+ * 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/mac/video_renderer_mac.h"
+
+#import <Cocoa/Cocoa.h>
+
+// Creates a Cocoa Window with an OpenGL context, used together with an OpenGL
+// renderer.
+@interface CocoaWindow : NSObject {
+ @private
+ NSWindow *window_;
+ NSOpenGLContext *context_;
+ NSString *title_;
+ int width_;
+ int height_;
+}
+
+- (id)initWithTitle:(NSString *)title width:(int)width height:(int)height;
+// 'createWindow' must be called on the main thread.
+- (void)createWindow:(NSObject *)ignored;
+- (void)makeCurrentContext;
+
+@end
+
+@implementation CocoaWindow
+ static NSInteger nextXOrigin_;
+ static NSInteger nextYOrigin_;
+
+- (id)initWithTitle:(NSString *)title width:(int)width height:(int)height {
+ if (self = [super init]) {
+ title_ = title;
+ width_ = width;
+ height_ = height;
+ }
+ return self;
+}
+
+- (void)dealloc {
+ [window_ release];
+ [super dealloc];
+}
+
+- (void)createWindow:(NSObject *)ignored {
+ NSInteger xOrigin = nextXOrigin_;
+ NSRect screenFrame = [[NSScreen mainScreen] frame];
+ if (nextXOrigin_ + width_ < screenFrame.size.width) {
+ nextXOrigin_ += width_;
+ } else {
+ xOrigin = 0;
+ nextXOrigin_ = 0;
+ nextYOrigin_ += height_;
+ }
+ if (nextYOrigin_ + height_ > screenFrame.size.height) {
+ xOrigin = 0;
+ nextXOrigin_ = 0;
+ nextYOrigin_ = 0;
+ }
+ NSInteger yOrigin = nextYOrigin_;
+ NSRect windowFrame = NSMakeRect(xOrigin, yOrigin, width_, height_);
+ window_ = [[NSWindow alloc] initWithContentRect:windowFrame
+ styleMask:NSTitledWindowMask
+ backing:NSBackingStoreBuffered
+ defer:NO];
+
+ NSRect viewFrame = NSMakeRect(0, 0, width_, height_);
+ NSOpenGLView *view = [[[NSOpenGLView alloc] initWithFrame:viewFrame
+ pixelFormat:nil] autorelease];
+ context_ = [view openGLContext];
+
+ [[window_ contentView] addSubview:view];
+ [window_ setTitle:title_];
+ [window_ makeKeyAndOrderFront:NSApp];
+}
+
+- (void)makeCurrentContext {
+ [context_ makeCurrentContext];
+}
+
+@end
+
+namespace webrtc {
+namespace test {
+
+VideoRenderer* VideoRenderer::CreatePlatformRenderer(const char* window_title,
+ size_t width,
+ size_t height) {
+ MacRenderer* renderer = new MacRenderer();
+ if (!renderer->Init(window_title, width, height)) {
+ delete renderer;
+ return NULL;
+ }
+ return renderer;
+}
+
+MacRenderer::MacRenderer()
+ : window_(NULL) {}
+
+MacRenderer::~MacRenderer() {
+ GlRenderer::Destroy();
+ [window_ release];
+}
+
+bool MacRenderer::Init(const char* window_title, int width, int height) {
+ window_ = [[CocoaWindow alloc]
+ initWithTitle:[NSString stringWithUTF8String:window_title]
+ width:width
+ height:height];
+ if (!window_)
+ return false;
+ [window_ performSelectorOnMainThread:@selector(createWindow:)
+ withObject:nil
+ waitUntilDone:YES];
+
+ [window_ makeCurrentContext];
+ GlRenderer::Init();
+ GlRenderer::ResizeViewport(width, height);
+ return true;
+}
+
+void MacRenderer::RenderFrame(const VideoFrame& frame, int /*delta*/) {
+ [window_ makeCurrentContext];
+ GlRenderer::RenderFrame(frame, 0);
+}
+
+} // test
+} // webrtc
diff --git a/webrtc/test/metrics.gyp b/webrtc/test/metrics.gyp
new file mode 100644
index 0000000000..eda0b054ee
--- /dev/null
+++ b/webrtc/test/metrics.gyp
@@ -0,0 +1,32 @@
+# Copyright (c) 2011 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.
+
+{
+ 'includes': [
+ '../build/common.gypi',
+ ],
+ 'targets': [
+ {
+ # The metrics code must be kept in its own GYP file in order to
+ # avoid a circular dependency error due to the dependency on libyuv.
+ # If the code would be put in test.gyp a circular dependency error during
+ # GYP generation would occur, because the libyuv.gypi unittest target
+ # depends on test_support_main. See issue #160 for more info.
+ 'target_name': 'metrics',
+ 'type': 'static_library',
+ 'dependencies': [
+ '<(webrtc_root)/common_video/common_video.gyp:common_video',
+ '<(webrtc_root)/system_wrappers/system_wrappers.gyp:system_wrappers',
+ ],
+ 'sources': [
+ 'testsupport/metrics/video_metrics.h',
+ 'testsupport/metrics/video_metrics.cc',
+ ],
+ },
+ ], # targets
+}
diff --git a/webrtc/test/mock_transport.h b/webrtc/test/mock_transport.h
new file mode 100644
index 0000000000..4937134512
--- /dev/null
+++ b/webrtc/test/mock_transport.h
@@ -0,0 +1,28 @@
+/*
+ * 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_MOCK_TRANSPORT_H_
+#define WEBRTC_TEST_MOCK_TRANSPORT_H_
+
+#include "testing/gmock/include/gmock/gmock.h"
+#include "webrtc/transport.h"
+
+namespace webrtc {
+
+class MockTransport : public Transport {
+ public:
+ MOCK_METHOD3(SendRtp,
+ bool(const uint8_t* data,
+ size_t len,
+ const PacketOptions& options));
+ MOCK_METHOD2(SendRtcp, bool(const uint8_t* data, size_t len));
+};
+} // namespace webrtc
+#endif // WEBRTC_TEST_MOCK_TRANSPORT_H_
diff --git a/webrtc/test/null_platform_renderer.cc b/webrtc/test/null_platform_renderer.cc
new file mode 100644
index 0000000000..362f7db762
--- /dev/null
+++ b/webrtc/test/null_platform_renderer.cc
@@ -0,0 +1,22 @@
+/*
+ * 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/video_renderer.h"
+
+namespace webrtc {
+namespace test {
+
+VideoRenderer* VideoRenderer::CreatePlatformRenderer(const char* window_title,
+ size_t width,
+ size_t height) {
+ return NULL;
+}
+} // test
+} // webrtc
diff --git a/webrtc/test/null_transport.cc b/webrtc/test/null_transport.cc
new file mode 100644
index 0000000000..7fa36d1246
--- /dev/null
+++ b/webrtc/test/null_transport.cc
@@ -0,0 +1,26 @@
+/*
+ * 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/null_transport.h"
+
+namespace webrtc {
+namespace test {
+
+bool NullTransport::SendRtp(const uint8_t* packet,
+ size_t length,
+ const PacketOptions& options) {
+ return true;
+}
+
+bool NullTransport::SendRtcp(const uint8_t* packet, size_t length) {
+ return true;
+}
+
+} // namespace test
+} // namespace webrtc
diff --git a/webrtc/test/null_transport.h b/webrtc/test/null_transport.h
new file mode 100644
index 0000000000..f4b704634d
--- /dev/null
+++ b/webrtc/test/null_transport.h
@@ -0,0 +1,30 @@
+/*
+ * 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_ENGINE_TEST_COMMON_NULL_TRANSPORT_H_
+#define WEBRTC_VIDEO_ENGINE_TEST_COMMON_NULL_TRANSPORT_H_
+
+#include "webrtc/transport.h"
+
+namespace webrtc {
+
+class PacketReceiver;
+
+namespace test {
+class NullTransport : public Transport {
+ public:
+ bool SendRtp(const uint8_t* packet,
+ size_t length,
+ const PacketOptions& options) override;
+ bool SendRtcp(const uint8_t* packet, size_t length) override;
+};
+} // namespace test
+} // namespace webrtc
+
+#endif // WEBRTC_VIDEO_ENGINE_TEST_COMMON_NULL_TRANSPORT_H_
diff --git a/webrtc/test/random.cc b/webrtc/test/random.cc
new file mode 100644
index 0000000000..c4c405f6b8
--- /dev/null
+++ b/webrtc/test/random.cc
@@ -0,0 +1,57 @@
+/*
+ * Copyright (c) 2015 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/random.h"
+
+#include <math.h>
+
+#include "webrtc/base/checks.h"
+
+namespace webrtc {
+
+namespace test {
+
+Random::Random(uint32_t seed) : a_(0x531FDB97 ^ seed), b_(0x6420ECA8 + seed) {
+}
+
+float Random::Rand() {
+ const double kScale = 1.0f / (static_cast<uint64_t>(1) << 32);
+ double result = kScale * b_;
+ a_ ^= b_;
+ b_ += a_;
+ return static_cast<float>(result);
+}
+
+int Random::Rand(int low, int high) {
+ RTC_DCHECK(low <= high);
+ float uniform = Rand() * (high - low + 1) + low;
+ return static_cast<int>(uniform);
+}
+
+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 * sqrt(-2 * log(u1)) * cos(2 * kPi * u2));
+}
+
+int Random::Exponential(float lambda) {
+ float uniform = Rand();
+ return static_cast<int>(-log(uniform) / lambda);
+}
+} // namespace test
+} // namespace webrtc
diff --git a/webrtc/test/random.h b/webrtc/test/random.h
new file mode 100644
index 0000000000..5cc54f2129
--- /dev/null
+++ b/webrtc/test/random.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2015 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_RANDOM_H_
+#define WEBRTC_TEST_RANDOM_H_
+
+#include "webrtc/typedefs.h"
+#include "webrtc/base/constructormagic.h"
+
+namespace webrtc {
+
+namespace test {
+
+class Random {
+ public:
+ explicit Random(uint32_t seed);
+
+ // Return pseudo-random number in the interval [0.0, 1.0).
+ float Rand();
+
+ // Return pseudo-random number mapped to the interval [low, high].
+ int Rand(int low, int high);
+
+ // Normal Distribution.
+ int Gaussian(int mean, int standard_deviation);
+
+ // Exponential Distribution.
+ int Exponential(float lambda);
+
+ // TODO(solenberg): Random from histogram.
+ // template<typename T> int Distribution(const std::vector<T> histogram) {
+
+ private:
+ uint32_t a_;
+ uint32_t b_;
+
+ RTC_DISALLOW_IMPLICIT_CONSTRUCTORS(Random);
+};
+} // namespace test
+} // namespace webrtc
+
+#endif // WEBRTC_TEST_RANDOM_H_
diff --git a/webrtc/test/rtcp_packet_parser.cc b/webrtc/test/rtcp_packet_parser.cc
new file mode 100644
index 0000000000..8ce249e0b6
--- /dev/null
+++ b/webrtc/test/rtcp_packet_parser.cc
@@ -0,0 +1,142 @@
+/*
+ * Copyright (c) 2014 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "webrtc/test/rtcp_packet_parser.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace webrtc {
+namespace test {
+
+using namespace RTCPUtility;
+
+RtcpPacketParser::RtcpPacketParser() {}
+
+RtcpPacketParser::~RtcpPacketParser() {}
+
+void RtcpPacketParser::Parse(const void *data, size_t len) {
+ const uint8_t* packet = static_cast<const uint8_t*>(data);
+ RTCPUtility::RTCPParserV2 parser(packet, len, true);
+ EXPECT_TRUE(parser.IsValid());
+ for (RTCPUtility::RTCPPacketTypes type = parser.Begin();
+ type != RTCPPacketTypes::kInvalid; type = parser.Iterate()) {
+ switch (type) {
+ case RTCPPacketTypes::kSr:
+ sender_report_.Set(parser.Packet().SR);
+ break;
+ case RTCPPacketTypes::kRr:
+ receiver_report_.Set(parser.Packet().RR);
+ break;
+ case RTCPPacketTypes::kReportBlockItem:
+ report_block_.Set(parser.Packet().ReportBlockItem);
+ ++report_blocks_per_ssrc_[parser.Packet().ReportBlockItem.SSRC];
+ break;
+ case RTCPPacketTypes::kSdes:
+ sdes_.Set();
+ break;
+ case RTCPPacketTypes::kSdesChunk:
+ sdes_chunk_.Set(parser.Packet().CName);
+ break;
+ case RTCPPacketTypes::kBye:
+ bye_.Set(parser.Packet().BYE);
+ break;
+ case RTCPPacketTypes::kApp:
+ app_.Set(parser.Packet().APP);
+ break;
+ case RTCPPacketTypes::kAppItem:
+ app_item_.Set(parser.Packet().APP);
+ break;
+ case RTCPPacketTypes::kExtendedIj:
+ ij_.Set();
+ break;
+ case RTCPPacketTypes::kExtendedIjItem:
+ ij_item_.Set(parser.Packet().ExtendedJitterReportItem);
+ break;
+ case RTCPPacketTypes::kPsfbPli:
+ pli_.Set(parser.Packet().PLI);
+ break;
+ case RTCPPacketTypes::kPsfbSli:
+ sli_.Set(parser.Packet().SLI);
+ break;
+ case RTCPPacketTypes::kPsfbSliItem:
+ sli_item_.Set(parser.Packet().SLIItem);
+ break;
+ case RTCPPacketTypes::kPsfbRpsi:
+ rpsi_.Set(parser.Packet().RPSI);
+ break;
+ case RTCPPacketTypes::kPsfbFir:
+ fir_.Set(parser.Packet().FIR);
+ break;
+ case RTCPPacketTypes::kPsfbFirItem:
+ fir_item_.Set(parser.Packet().FIRItem);
+ break;
+ case RTCPPacketTypes::kRtpfbNack:
+ nack_.Set(parser.Packet().NACK);
+ nack_item_.Clear();
+ break;
+ case RTCPPacketTypes::kRtpfbNackItem:
+ nack_item_.Set(parser.Packet().NACKItem);
+ break;
+ case RTCPPacketTypes::kPsfbApp:
+ psfb_app_.Set(parser.Packet().PSFBAPP);
+ break;
+ case RTCPPacketTypes::kPsfbRembItem:
+ remb_item_.Set(parser.Packet().REMBItem);
+ break;
+ case RTCPPacketTypes::kRtpfbTmmbr:
+ tmmbr_.Set(parser.Packet().TMMBR);
+ break;
+ case RTCPPacketTypes::kRtpfbTmmbrItem:
+ tmmbr_item_.Set(parser.Packet().TMMBRItem);
+ break;
+ case RTCPPacketTypes::kRtpfbTmmbn:
+ tmmbn_.Set(parser.Packet().TMMBN);
+ tmmbn_items_.Clear();
+ break;
+ case RTCPPacketTypes::kRtpfbTmmbnItem:
+ tmmbn_items_.Set(parser.Packet().TMMBNItem);
+ break;
+ case RTCPPacketTypes::kXrHeader:
+ xr_header_.Set(parser.Packet().XR);
+ dlrr_items_.Clear();
+ break;
+ case RTCPPacketTypes::kXrReceiverReferenceTime:
+ rrtr_.Set(parser.Packet().XRReceiverReferenceTimeItem);
+ break;
+ case RTCPPacketTypes::kXrDlrrReportBlock:
+ dlrr_.Set();
+ break;
+ case RTCPPacketTypes::kXrDlrrReportBlockItem:
+ dlrr_items_.Set(parser.Packet().XRDLRRReportBlockItem);
+ break;
+ case RTCPPacketTypes::kXrVoipMetric:
+ voip_metric_.Set(parser.Packet().XRVOIPMetricItem);
+ break;
+ default:
+ break;
+ }
+ }
+}
+
+uint64_t Rpsi::PictureId() const {
+ assert(num_packets_ > 0);
+ uint16_t num_bytes = rpsi_.NumberOfValidBits / 8;
+ assert(num_bytes > 0);
+ uint64_t picture_id = 0;
+ for (uint16_t i = 0; i < num_bytes - 1; ++i) {
+ picture_id += (rpsi_.NativeBitString[i] & 0x7f);
+ picture_id <<= 7;
+ }
+ picture_id += (rpsi_.NativeBitString[num_bytes - 1] & 0x7f);
+ return picture_id;
+}
+
+} // namespace test
+} // namespace webrtc
diff --git a/webrtc/test/rtcp_packet_parser.h b/webrtc/test/rtcp_packet_parser.h
new file mode 100644
index 0000000000..cc890b4f2f
--- /dev/null
+++ b/webrtc/test/rtcp_packet_parser.h
@@ -0,0 +1,705 @@
+/*
+ * Copyright (c) 2014 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ *
+ */
+
+#ifndef WEBRTC_TEST_RTCP_PACKET_PARSER_H_
+#define WEBRTC_TEST_RTCP_PACKET_PARSER_H_
+
+#include <map>
+#include <string>
+#include <vector>
+
+#include "webrtc/modules/rtp_rtcp/source/rtcp_utility.h"
+#include "webrtc/typedefs.h"
+
+namespace webrtc {
+namespace test {
+
+class RtcpPacketParser;
+
+class PacketType {
+ public:
+ virtual ~PacketType() {}
+
+ int num_packets() const { return num_packets_; }
+
+ protected:
+ PacketType() : num_packets_(0) {}
+
+ int num_packets_;
+};
+
+class SenderReport : public PacketType {
+ public:
+ SenderReport() {}
+ virtual ~SenderReport() {}
+
+ uint32_t Ssrc() const { return sr_.SenderSSRC; }
+ uint32_t NtpSec() const { return sr_.NTPMostSignificant; }
+ uint32_t NtpFrac() const { return sr_.NTPLeastSignificant; }
+ uint32_t RtpTimestamp() const { return sr_.RTPTimestamp; }
+ uint32_t PacketCount() const { return sr_.SenderPacketCount; }
+ uint32_t OctetCount() const { return sr_.SenderOctetCount; }
+
+ private:
+ friend class RtcpPacketParser;
+
+ void Set(const RTCPUtility::RTCPPacketSR& sr) {
+ sr_ = sr;
+ ++num_packets_;
+ }
+
+ RTCPUtility::RTCPPacketSR sr_;
+};
+
+class ReceiverReport : public PacketType {
+ public:
+ ReceiverReport() {}
+ virtual ~ReceiverReport() {}
+
+ uint32_t Ssrc() const { return rr_.SenderSSRC; }
+
+ private:
+ friend class RtcpPacketParser;
+
+ void Set(const RTCPUtility::RTCPPacketRR& rr) {
+ rr_ = rr;
+ ++num_packets_;
+ }
+
+ RTCPUtility::RTCPPacketRR rr_;
+};
+
+class ReportBlock : public PacketType {
+ public:
+ ReportBlock() {}
+ virtual ~ReportBlock() {}
+
+ uint32_t Ssrc() const { return rb_.SSRC; }
+ uint8_t FractionLost() const { return rb_.FractionLost; }
+ uint32_t CumPacketLost() const { return rb_.CumulativeNumOfPacketsLost; }
+ uint32_t ExtHighestSeqNum() const { return rb_.ExtendedHighestSequenceNumber;}
+ uint32_t Jitter() const { return rb_.Jitter; }
+ uint32_t LastSr() const { return rb_.LastSR; }
+ uint32_t DelayLastSr()const { return rb_.DelayLastSR; }
+
+ private:
+ friend class RtcpPacketParser;
+
+ void Set(const RTCPUtility::RTCPPacketReportBlockItem& rb) {
+ rb_ = rb;
+ ++num_packets_;
+ }
+
+ RTCPUtility::RTCPPacketReportBlockItem rb_;
+};
+
+class Ij : public PacketType {
+ public:
+ Ij() {}
+ virtual ~Ij() {}
+
+ private:
+ friend class RtcpPacketParser;
+
+ void Set() { ++num_packets_; }
+};
+
+class IjItem : public PacketType {
+ public:
+ IjItem() {}
+ virtual ~IjItem() {}
+
+ uint32_t Jitter() const { return ij_item_.Jitter; }
+
+ private:
+ friend class RtcpPacketParser;
+
+ void Set(const RTCPUtility::RTCPPacketExtendedJitterReportItem& ij_item) {
+ ij_item_ = ij_item;
+ ++num_packets_;
+ }
+
+ RTCPUtility::RTCPPacketExtendedJitterReportItem ij_item_;
+};
+
+class Sdes : public PacketType {
+ public:
+ Sdes() {}
+ virtual ~Sdes() {}
+
+ private:
+ friend class RtcpPacketParser;
+
+ void Set() { ++num_packets_; }
+};
+
+class SdesChunk : public PacketType {
+ public:
+ SdesChunk() {}
+ virtual ~SdesChunk() {}
+
+ uint32_t Ssrc() const { return cname_.SenderSSRC; }
+ std::string Cname() const { return cname_.CName; }
+
+ private:
+ friend class RtcpPacketParser;
+
+ void Set(const RTCPUtility::RTCPPacketSDESCName& cname) {
+ cname_ = cname;
+ ++num_packets_;
+ }
+
+ RTCPUtility::RTCPPacketSDESCName cname_;
+};
+
+class Bye : public PacketType {
+ public:
+ Bye() {}
+ virtual ~Bye() {}
+
+ uint32_t Ssrc() const { return bye_.SenderSSRC; }
+
+ private:
+ friend class RtcpPacketParser;
+
+ void Set(const RTCPUtility::RTCPPacketBYE& bye) {
+ bye_ = bye;
+ ++num_packets_;
+ }
+
+ RTCPUtility::RTCPPacketBYE bye_;
+};
+
+class Rpsi : public PacketType {
+ public:
+ Rpsi() {}
+ virtual ~Rpsi() {}
+
+ uint32_t Ssrc() const { return rpsi_.SenderSSRC; }
+ uint32_t MediaSsrc() const { return rpsi_.MediaSSRC; }
+ uint8_t PayloadType() const { return rpsi_.PayloadType; }
+ uint16_t NumberOfValidBits() const { return rpsi_.NumberOfValidBits; }
+ uint64_t PictureId() const;
+
+ private:
+ friend class RtcpPacketParser;
+
+ void Set(const RTCPUtility::RTCPPacketPSFBRPSI& rpsi) {
+ rpsi_ = rpsi;
+ ++num_packets_;
+ }
+
+ RTCPUtility::RTCPPacketPSFBRPSI rpsi_;
+};
+
+class App : public PacketType {
+ public:
+ App() {}
+ virtual ~App() {}
+
+ uint8_t SubType() const { return app_.SubType; }
+ uint32_t Name() const { return app_.Name; }
+
+ private:
+ friend class RtcpPacketParser;
+
+ void Set(const RTCPUtility::RTCPPacketAPP& app) {
+ app_ = app;
+ ++num_packets_;
+ }
+
+ RTCPUtility::RTCPPacketAPP app_;
+};
+
+class AppItem : public PacketType {
+ public:
+ AppItem() {}
+ virtual ~AppItem() {}
+
+ uint8_t* Data() { return app_item_.Data; }
+ uint16_t DataLength() const { return app_item_.Size; }
+
+ private:
+ friend class RtcpPacketParser;
+
+ void Set(const RTCPUtility::RTCPPacketAPP& app) {
+ app_item_ = app;
+ ++num_packets_;
+ }
+
+ RTCPUtility::RTCPPacketAPP app_item_;
+};
+
+class Pli : public PacketType {
+ public:
+ Pli() {}
+ virtual ~Pli() {}
+
+ uint32_t Ssrc() const { return pli_.SenderSSRC; }
+ uint32_t MediaSsrc() const { return pli_.MediaSSRC; }
+
+ private:
+ friend class RtcpPacketParser;
+
+ void Set(const RTCPUtility::RTCPPacketPSFBPLI& pli) {
+ pli_ = pli;
+ ++num_packets_;
+ }
+
+ RTCPUtility::RTCPPacketPSFBPLI pli_;
+};
+
+class Sli : public PacketType {
+ public:
+ Sli() {}
+ virtual ~Sli() {}
+
+ uint32_t Ssrc() const { return sli_.SenderSSRC; }
+ uint32_t MediaSsrc() const { return sli_.MediaSSRC; }
+
+ private:
+ friend class RtcpPacketParser;
+
+ void Set(const RTCPUtility::RTCPPacketPSFBSLI& sli) {
+ sli_ = sli;
+ ++num_packets_;
+ }
+
+ RTCPUtility::RTCPPacketPSFBSLI sli_;
+};
+
+class SliItem : public PacketType {
+ public:
+ SliItem() {}
+ virtual ~SliItem() {}
+
+ uint16_t FirstMb() const { return sli_item_.FirstMB; }
+ uint16_t NumberOfMb() const { return sli_item_.NumberOfMB; }
+ uint8_t PictureId() const { return sli_item_.PictureId; }
+
+ private:
+ friend class RtcpPacketParser;
+
+ void Set(const RTCPUtility::RTCPPacketPSFBSLIItem& sli_item) {
+ sli_item_ = sli_item;
+ ++num_packets_;
+ }
+
+ RTCPUtility::RTCPPacketPSFBSLIItem sli_item_;
+};
+
+class Fir : public PacketType {
+ public:
+ Fir() {}
+ virtual ~Fir() {}
+
+ uint32_t Ssrc() const { return fir_.SenderSSRC; }
+
+ private:
+ friend class RtcpPacketParser;
+
+ void Set(const RTCPUtility::RTCPPacketPSFBFIR& fir) {
+ fir_ = fir;
+ ++num_packets_;
+ }
+
+ RTCPUtility::RTCPPacketPSFBFIR fir_;
+};
+
+class FirItem : public PacketType {
+ public:
+ FirItem() {}
+ virtual ~FirItem() {}
+
+ uint32_t Ssrc() const { return fir_item_.SSRC; }
+ uint8_t SeqNum() const { return fir_item_.CommandSequenceNumber; }
+
+ private:
+ friend class RtcpPacketParser;
+
+ void Set(const RTCPUtility::RTCPPacketPSFBFIRItem& fir_item) {
+ fir_item_ = fir_item;
+ ++num_packets_;
+ }
+
+ RTCPUtility::RTCPPacketPSFBFIRItem fir_item_;
+};
+
+class Nack : public PacketType {
+ public:
+ Nack() {}
+ virtual ~Nack() {}
+
+ uint32_t Ssrc() const { return nack_.SenderSSRC; }
+ uint32_t MediaSsrc() const { return nack_.MediaSSRC; }
+
+ private:
+ friend class RtcpPacketParser;
+
+ void Set(const RTCPUtility::RTCPPacketRTPFBNACK& nack) {
+ nack_ = nack;
+ ++num_packets_;
+ }
+
+ RTCPUtility::RTCPPacketRTPFBNACK nack_;
+};
+
+class NackItem : public PacketType {
+ public:
+ NackItem() {}
+ virtual ~NackItem() {}
+
+ std::vector<uint16_t> last_nack_list() const {
+ return last_nack_list_;
+ }
+
+ private:
+ friend class RtcpPacketParser;
+
+ void Set(const RTCPUtility::RTCPPacketRTPFBNACKItem& nack_item) {
+ last_nack_list_.push_back(nack_item.PacketID);
+ for (int i = 0; i < 16; ++i) {
+ if (nack_item.BitMask & (1 << i)) {
+ last_nack_list_.push_back(nack_item.PacketID + i + 1);
+ }
+ }
+ ++num_packets_;
+ }
+ void Clear() { last_nack_list_.clear(); }
+
+ std::vector<uint16_t> last_nack_list_;
+};
+
+class PsfbApp : public PacketType {
+ public:
+ PsfbApp() {}
+ virtual ~PsfbApp() {}
+
+ uint32_t Ssrc() const { return psfb_app_.SenderSSRC; }
+
+ private:
+ friend class RtcpPacketParser;
+
+ void Set(const RTCPUtility::RTCPPacketPSFBAPP& psfb_app) {
+ psfb_app_ = psfb_app;
+ ++num_packets_;
+ }
+
+ RTCPUtility::RTCPPacketPSFBAPP psfb_app_;
+};
+
+class RembItem : public PacketType {
+ public:
+ RembItem() : last_bitrate_bps_(0) {}
+ virtual ~RembItem() {}
+
+ int last_bitrate_bps() const { return last_bitrate_bps_; }
+ std::vector<uint32_t> last_ssrc_list() {
+ return last_ssrc_list_;
+ }
+
+ private:
+ friend class RtcpPacketParser;
+
+ void Set(const RTCPUtility::RTCPPacketPSFBREMBItem& remb_item) {
+ last_bitrate_bps_ = remb_item.BitRate;
+ last_ssrc_list_.clear();
+ last_ssrc_list_.insert(
+ last_ssrc_list_.end(),
+ remb_item.SSRCs,
+ remb_item.SSRCs + remb_item.NumberOfSSRCs);
+ ++num_packets_;
+ }
+
+ uint32_t last_bitrate_bps_;
+ std::vector<uint32_t> last_ssrc_list_;
+};
+
+class Tmmbr : public PacketType {
+ public:
+ Tmmbr() {}
+ virtual ~Tmmbr() {}
+
+ uint32_t Ssrc() const { return tmmbr_.SenderSSRC; }
+
+ private:
+ friend class RtcpPacketParser;
+
+ void Set(const RTCPUtility::RTCPPacketRTPFBTMMBR& tmmbr) {
+ tmmbr_ = tmmbr;
+ ++num_packets_;
+ }
+
+ RTCPUtility::RTCPPacketRTPFBTMMBR tmmbr_;
+};
+
+class TmmbrItem : public PacketType {
+ public:
+ TmmbrItem() {}
+ virtual ~TmmbrItem() {}
+
+ uint32_t Ssrc() const { return tmmbr_item_.SSRC; }
+ uint32_t BitrateKbps() const { return tmmbr_item_.MaxTotalMediaBitRate; }
+ uint32_t Overhead() const { return tmmbr_item_.MeasuredOverhead; }
+
+ private:
+ friend class RtcpPacketParser;
+
+ void Set(const RTCPUtility::RTCPPacketRTPFBTMMBRItem& tmmbr_item) {
+ tmmbr_item_ = tmmbr_item;
+ ++num_packets_;
+ }
+
+ RTCPUtility::RTCPPacketRTPFBTMMBRItem tmmbr_item_;
+};
+
+
+class Tmmbn : public PacketType {
+ public:
+ Tmmbn() {}
+ virtual ~Tmmbn() {}
+
+ uint32_t Ssrc() const { return tmmbn_.SenderSSRC; }
+
+ private:
+ friend class RtcpPacketParser;
+
+ void Set(const RTCPUtility::RTCPPacketRTPFBTMMBN& tmmbn) {
+ tmmbn_ = tmmbn;
+ ++num_packets_;
+ }
+
+ RTCPUtility::RTCPPacketRTPFBTMMBN tmmbn_;
+};
+
+class TmmbnItems : public PacketType {
+ public:
+ TmmbnItems() {}
+ virtual ~TmmbnItems() {}
+
+ uint32_t Ssrc(uint8_t num) const {
+ assert(num < tmmbns_.size());
+ return tmmbns_[num].SSRC;
+ }
+ uint32_t BitrateKbps(uint8_t num) const {
+ assert(num < tmmbns_.size());
+ return tmmbns_[num].MaxTotalMediaBitRate;
+ }
+ uint32_t Overhead(uint8_t num) const {
+ assert(num < tmmbns_.size());
+ return tmmbns_[num].MeasuredOverhead;
+ }
+
+ private:
+ friend class RtcpPacketParser;
+
+ void Set(const RTCPUtility::RTCPPacketRTPFBTMMBNItem& tmmbn_item) {
+ tmmbns_.push_back(tmmbn_item);
+ ++num_packets_;
+ }
+ void Clear() { tmmbns_.clear(); }
+
+ std::vector<RTCPUtility::RTCPPacketRTPFBTMMBNItem> tmmbns_;
+};
+
+class XrHeader : public PacketType {
+ public:
+ XrHeader() {}
+ virtual ~XrHeader() {}
+
+ uint32_t Ssrc() const { return xr_header_.OriginatorSSRC; }
+
+ private:
+ friend class RtcpPacketParser;
+
+ void Set(const RTCPUtility::RTCPPacketXR& xr_header) {
+ xr_header_ = xr_header;
+ ++num_packets_;
+ }
+
+ RTCPUtility::RTCPPacketXR xr_header_;
+};
+
+class Rrtr : public PacketType {
+ public:
+ Rrtr() {}
+ virtual ~Rrtr() {}
+
+ uint32_t NtpSec() const { return rrtr_.NTPMostSignificant; }
+ uint32_t NtpFrac() const { return rrtr_.NTPLeastSignificant; }
+
+ private:
+ friend class RtcpPacketParser;
+
+ void Set(const RTCPUtility::RTCPPacketXRReceiverReferenceTimeItem& rrtr) {
+ rrtr_ = rrtr;
+ ++num_packets_;
+ }
+
+ RTCPUtility::RTCPPacketXRReceiverReferenceTimeItem rrtr_;
+};
+
+class Dlrr : public PacketType {
+ public:
+ Dlrr() {}
+ virtual ~Dlrr() {}
+
+ private:
+ friend class RtcpPacketParser;
+
+ void Set() { ++num_packets_; }
+};
+
+class DlrrItems : public PacketType {
+ public:
+ DlrrItems() {}
+ virtual ~DlrrItems() {}
+
+ uint32_t Ssrc(uint8_t num) const {
+ assert(num < dlrrs_.size());
+ return dlrrs_[num].SSRC;
+ }
+ uint32_t LastRr(uint8_t num) const {
+ assert(num < dlrrs_.size());
+ return dlrrs_[num].LastRR;
+ }
+ uint32_t DelayLastRr(uint8_t num) const {
+ assert(num < dlrrs_.size());
+ return dlrrs_[num].DelayLastRR;
+ }
+
+ private:
+ friend class RtcpPacketParser;
+
+ void Set(const RTCPUtility::RTCPPacketXRDLRRReportBlockItem& dlrr) {
+ dlrrs_.push_back(dlrr);
+ ++num_packets_;
+ }
+ void Clear() { dlrrs_.clear(); }
+
+ std::vector<RTCPUtility::RTCPPacketXRDLRRReportBlockItem> dlrrs_;
+};
+
+class VoipMetric : public PacketType {
+ public:
+ VoipMetric() {}
+ virtual ~VoipMetric() {}
+
+ uint32_t Ssrc() const { return voip_metric_.SSRC; }
+ uint8_t LossRate() { return voip_metric_.lossRate; }
+ uint8_t DiscardRate() { return voip_metric_.discardRate; }
+ uint8_t BurstDensity() { return voip_metric_.burstDensity; }
+ uint8_t GapDensity() { return voip_metric_.gapDensity; }
+ uint16_t BurstDuration() { return voip_metric_.burstDuration; }
+ uint16_t GapDuration() { return voip_metric_.gapDuration; }
+ uint16_t RoundTripDelay() { return voip_metric_.roundTripDelay; }
+ uint16_t EndSystemDelay() { return voip_metric_.endSystemDelay; }
+ uint8_t SignalLevel() { return voip_metric_.signalLevel; }
+ uint8_t NoiseLevel() { return voip_metric_.noiseLevel; }
+ uint8_t Rerl() { return voip_metric_.RERL; }
+ uint8_t Gmin() { return voip_metric_.Gmin; }
+ uint8_t Rfactor() { return voip_metric_.Rfactor; }
+ uint8_t ExtRfactor() { return voip_metric_.extRfactor; }
+ uint8_t MosLq() { return voip_metric_.MOSLQ; }
+ uint8_t MosCq() { return voip_metric_.MOSCQ; }
+ uint8_t RxConfig() { return voip_metric_.RXconfig; }
+ uint16_t JbNominal() { return voip_metric_.JBnominal; }
+ uint16_t JbMax() { return voip_metric_.JBmax; }
+ uint16_t JbAbsMax() { return voip_metric_.JBabsMax; }
+
+ private:
+ friend class RtcpPacketParser;
+
+ void Set(const RTCPUtility::RTCPPacketXRVOIPMetricItem& voip_metric) {
+ voip_metric_ = voip_metric;
+ ++num_packets_;
+ }
+
+ RTCPUtility::RTCPPacketXRVOIPMetricItem voip_metric_;
+};
+
+class RtcpPacketParser {
+ public:
+ RtcpPacketParser();
+ ~RtcpPacketParser();
+
+ void Parse(const void *packet, size_t packet_len);
+
+ SenderReport* sender_report() { return &sender_report_; }
+ ReceiverReport* receiver_report() { return &receiver_report_; }
+ ReportBlock* report_block() { return &report_block_; }
+ Sdes* sdes() { return &sdes_; }
+ SdesChunk* sdes_chunk() { return &sdes_chunk_; }
+ Bye* bye() { return &bye_; }
+ App* app() { return &app_; }
+ AppItem* app_item() { return &app_item_; }
+ Ij* ij() { return &ij_; }
+ IjItem* ij_item() { return &ij_item_; }
+ Pli* pli() { return &pli_; }
+ Sli* sli() { return &sli_; }
+ SliItem* sli_item() { return &sli_item_; }
+ Rpsi* rpsi() { return &rpsi_; }
+ Fir* fir() { return &fir_; }
+ FirItem* fir_item() { return &fir_item_; }
+ Nack* nack() { return &nack_; }
+ NackItem* nack_item() { return &nack_item_; }
+ PsfbApp* psfb_app() { return &psfb_app_; }
+ RembItem* remb_item() { return &remb_item_; }
+ Tmmbr* tmmbr() { return &tmmbr_; }
+ TmmbrItem* tmmbr_item() { return &tmmbr_item_; }
+ Tmmbn* tmmbn() { return &tmmbn_; }
+ TmmbnItems* tmmbn_items() { return &tmmbn_items_; }
+ XrHeader* xr_header() { return &xr_header_; }
+ Rrtr* rrtr() { return &rrtr_; }
+ Dlrr* dlrr() { return &dlrr_; }
+ DlrrItems* dlrr_items() { return &dlrr_items_; }
+ VoipMetric* voip_metric() { return &voip_metric_; }
+
+ int report_blocks_per_ssrc(uint32_t ssrc) {
+ return report_blocks_per_ssrc_[ssrc];
+ }
+
+ private:
+ SenderReport sender_report_;
+ ReceiverReport receiver_report_;
+ ReportBlock report_block_;
+ Sdes sdes_;
+ SdesChunk sdes_chunk_;
+ Bye bye_;
+ App app_;
+ AppItem app_item_;
+ Ij ij_;
+ IjItem ij_item_;
+ Pli pli_;
+ Sli sli_;
+ SliItem sli_item_;
+ Rpsi rpsi_;
+ Fir fir_;
+ FirItem fir_item_;
+ Nack nack_;
+ NackItem nack_item_;
+ PsfbApp psfb_app_;
+ RembItem remb_item_;
+ Tmmbr tmmbr_;
+ TmmbrItem tmmbr_item_;
+ Tmmbn tmmbn_;
+ TmmbnItems tmmbn_items_;
+ XrHeader xr_header_;
+ Rrtr rrtr_;
+ Dlrr dlrr_;
+ DlrrItems dlrr_items_;
+ VoipMetric voip_metric_;
+
+ std::map<uint32_t, int> report_blocks_per_ssrc_;
+};
+} // namespace test
+} // namespace webrtc
+#endif // WEBRTC_TEST_RTCP_PACKET_PARSER_H_
diff --git a/webrtc/test/rtp_file_reader.cc b/webrtc/test/rtp_file_reader.cc
new file mode 100644
index 0000000000..cb0e40705f
--- /dev/null
+++ b/webrtc/test/rtp_file_reader.cc
@@ -0,0 +1,674 @@
+/*
+ * Copyright (c) 2014 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "webrtc/test/rtp_file_reader.h"
+
+#include <stdio.h>
+
+#include <map>
+#include <string>
+#include <vector>
+
+#include "webrtc/base/checks.h"
+#include "webrtc/base/format_macros.h"
+#include "webrtc/base/scoped_ptr.h"
+#include "webrtc/modules/rtp_rtcp/source/rtp_utility.h"
+
+namespace webrtc {
+namespace test {
+
+static const size_t kFirstLineLength = 40;
+static uint16_t kPacketHeaderSize = 8;
+
+#if 1
+# define DEBUG_LOG(text)
+# define DEBUG_LOG1(text, arg)
+#else
+# define DEBUG_LOG(text) (printf(text "\n"))
+# define DEBUG_LOG1(text, arg) (printf(text "\n", arg))
+#endif
+
+#define TRY(expr) \
+ do { \
+ if (!(expr)) { \
+ DEBUG_LOG1("FAIL at " __FILE__ ":%d", __LINE__); \
+ return false; \
+ } \
+ } while (0)
+
+bool ReadUint32(uint32_t* out, FILE* file) {
+ *out = 0;
+ for (size_t i = 0; i < 4; ++i) {
+ *out <<= 8;
+ uint8_t tmp;
+ if (fread(&tmp, 1, sizeof(uint8_t), file) != sizeof(uint8_t))
+ return false;
+ *out |= tmp;
+ }
+ return true;
+}
+
+bool ReadUint16(uint16_t* out, FILE* file) {
+ *out = 0;
+ for (size_t i = 0; i < 2; ++i) {
+ *out <<= 8;
+ uint8_t tmp;
+ if (fread(&tmp, 1, sizeof(uint8_t), file) != sizeof(uint8_t))
+ return false;
+ *out |= tmp;
+ }
+ return true;
+}
+
+class RtpFileReaderImpl : public RtpFileReader {
+ public:
+ virtual bool Init(const std::string& filename,
+ const std::set<uint32_t>& ssrc_filter) = 0;
+};
+
+class InterleavedRtpFileReader : public RtpFileReaderImpl {
+ public:
+ virtual ~InterleavedRtpFileReader() {
+ if (file_ != NULL) {
+ fclose(file_);
+ file_ = NULL;
+ }
+ }
+
+ virtual bool Init(const std::string& filename,
+ const std::set<uint32_t>& ssrc_filter) {
+ file_ = fopen(filename.c_str(), "rb");
+ if (file_ == NULL) {
+ printf("ERROR: Can't open file: %s\n", filename.c_str());
+ return false;
+ }
+ return true;
+ }
+ virtual bool NextPacket(RtpPacket* packet) {
+ assert(file_ != NULL);
+ packet->length = RtpPacket::kMaxPacketBufferSize;
+ uint32_t len = 0;
+ TRY(ReadUint32(&len, file_));
+ if (packet->length < len) {
+ FATAL() << "Packet is too large to fit: " << len << " bytes vs "
+ << packet->length
+ << " bytes allocated. Consider increasing the buffer "
+ "size";
+ }
+ if (fread(packet->data, 1, len, file_) != len)
+ return false;
+
+ packet->length = len;
+ packet->original_length = len;
+ packet->time_ms = time_ms_;
+ time_ms_ += 5;
+ return true;
+ }
+
+ private:
+ FILE* file_ = NULL;
+ int64_t time_ms_ = 0;
+};
+
+// Read RTP packets from file in rtpdump format, as documented at:
+// http://www.cs.columbia.edu/irt/software/rtptools/
+class RtpDumpReader : public RtpFileReaderImpl {
+ public:
+ RtpDumpReader() : file_(NULL) {}
+ virtual ~RtpDumpReader() {
+ if (file_ != NULL) {
+ fclose(file_);
+ file_ = NULL;
+ }
+ }
+
+ bool Init(const std::string& filename,
+ const std::set<uint32_t>& ssrc_filter) {
+ file_ = fopen(filename.c_str(), "rb");
+ if (file_ == NULL) {
+ printf("ERROR: Can't open file: %s\n", filename.c_str());
+ return false;
+ }
+
+ char firstline[kFirstLineLength + 1] = {0};
+ if (fgets(firstline, kFirstLineLength, file_) == NULL) {
+ DEBUG_LOG("ERROR: Can't read from file\n");
+ return false;
+ }
+ if (strncmp(firstline, "#!rtpplay", 9) == 0) {
+ if (strncmp(firstline, "#!rtpplay1.0", 12) != 0) {
+ DEBUG_LOG("ERROR: wrong rtpplay version, must be 1.0\n");
+ return false;
+ }
+ } else if (strncmp(firstline, "#!RTPencode", 11) == 0) {
+ if (strncmp(firstline, "#!RTPencode1.0", 14) != 0) {
+ DEBUG_LOG("ERROR: wrong RTPencode version, must be 1.0\n");
+ return false;
+ }
+ } else {
+ DEBUG_LOG("ERROR: wrong file format of input file\n");
+ return false;
+ }
+
+ uint32_t start_sec;
+ uint32_t start_usec;
+ uint32_t source;
+ uint16_t port;
+ uint16_t padding;
+ TRY(ReadUint32(&start_sec, file_));
+ TRY(ReadUint32(&start_usec, file_));
+ TRY(ReadUint32(&source, file_));
+ TRY(ReadUint16(&port, file_));
+ TRY(ReadUint16(&padding, file_));
+
+ return true;
+ }
+
+ bool NextPacket(RtpPacket* packet) override {
+ uint8_t* rtp_data = packet->data;
+ packet->length = RtpPacket::kMaxPacketBufferSize;
+
+ uint16_t len;
+ uint16_t plen;
+ uint32_t offset;
+ TRY(ReadUint16(&len, file_));
+ TRY(ReadUint16(&plen, file_));
+ TRY(ReadUint32(&offset, file_));
+
+ // Use 'len' here because a 'plen' of 0 specifies rtcp.
+ len -= kPacketHeaderSize;
+ if (packet->length < len) {
+ FATAL() << "Packet is too large to fit: " << len << " bytes vs "
+ << packet->length
+ << " bytes allocated. Consider increasing the buffer "
+ "size";
+ }
+ if (fread(rtp_data, 1, len, file_) != len) {
+ return false;
+ }
+
+ packet->length = len;
+ packet->original_length = plen;
+ packet->time_ms = offset;
+ return true;
+ }
+
+ private:
+ FILE* file_;
+
+ RTC_DISALLOW_COPY_AND_ASSIGN(RtpDumpReader);
+};
+
+enum {
+ kResultFail = -1,
+ kResultSuccess = 0,
+ kResultSkip = 1,
+
+ kPcapVersionMajor = 2,
+ kPcapVersionMinor = 4,
+ kLinktypeNull = 0,
+ kLinktypeEthernet = 1,
+ kBsdNullLoopback1 = 0x00000002,
+ kBsdNullLoopback2 = 0x02000000,
+ kEthernetIIHeaderMacSkip = 12,
+ kEthertypeIp = 0x0800,
+ kIpVersion4 = 4,
+ kMinIpHeaderLength = 20,
+ kFragmentOffsetClear = 0x0000,
+ kFragmentOffsetDoNotFragment = 0x4000,
+ kProtocolTcp = 0x06,
+ kProtocolUdp = 0x11,
+ kUdpHeaderLength = 8,
+ kMaxReadBufferSize = 4096
+};
+
+const uint32_t kPcapBOMSwapOrder = 0xd4c3b2a1UL;
+const uint32_t kPcapBOMNoSwapOrder = 0xa1b2c3d4UL;
+
+#define TRY_PCAP(expr) \
+ do { \
+ int r = (expr); \
+ if (r == kResultFail) { \
+ DEBUG_LOG1("FAIL at " __FILE__ ":%d", __LINE__); \
+ return kResultFail; \
+ } else if (r == kResultSkip) { \
+ return kResultSkip; \
+ } \
+ } while (0)
+
+// Read RTP packets from file in tcpdump/libpcap format, as documented at:
+// http://wiki.wireshark.org/Development/LibpcapFileFormat
+class PcapReader : public RtpFileReaderImpl {
+ public:
+ PcapReader()
+ : file_(NULL),
+ swap_pcap_byte_order_(false),
+#ifdef WEBRTC_ARCH_BIG_ENDIAN
+ swap_network_byte_order_(false),
+#else
+ swap_network_byte_order_(true),
+#endif
+ read_buffer_(),
+ packets_by_ssrc_(),
+ packets_(),
+ next_packet_it_() {
+ }
+
+ virtual ~PcapReader() {
+ if (file_ != NULL) {
+ fclose(file_);
+ file_ = NULL;
+ }
+ }
+
+ bool Init(const std::string& filename,
+ const std::set<uint32_t>& ssrc_filter) override {
+ return Initialize(filename, ssrc_filter) == kResultSuccess;
+ }
+
+ int Initialize(const std::string& filename,
+ const std::set<uint32_t>& ssrc_filter) {
+ file_ = fopen(filename.c_str(), "rb");
+ if (file_ == NULL) {
+ printf("ERROR: Can't open file: %s\n", filename.c_str());
+ return kResultFail;
+ }
+
+ if (ReadGlobalHeader() < 0) {
+ return kResultFail;
+ }
+
+ int total_packet_count = 0;
+ uint32_t stream_start_ms = 0;
+ int32_t next_packet_pos = ftell(file_);
+ for (;;) {
+ TRY_PCAP(fseek(file_, next_packet_pos, SEEK_SET));
+ int result = ReadPacket(&next_packet_pos, stream_start_ms,
+ ++total_packet_count, ssrc_filter);
+ if (result == kResultFail) {
+ break;
+ } else if (result == kResultSuccess && packets_.size() == 1) {
+ assert(stream_start_ms == 0);
+ PacketIterator it = packets_.begin();
+ stream_start_ms = it->time_offset_ms;
+ it->time_offset_ms = 0;
+ }
+ }
+
+ if (feof(file_) == 0) {
+ printf("Failed reading file!\n");
+ return kResultFail;
+ }
+
+ printf("Total packets in file: %d\n", total_packet_count);
+ printf("Total RTP/RTCP packets: %" PRIuS "\n", packets_.size());
+
+ for (SsrcMapIterator mit = packets_by_ssrc_.begin();
+ mit != packets_by_ssrc_.end(); ++mit) {
+ uint32_t ssrc = mit->first;
+ const std::vector<uint32_t>& packet_indices = mit->second;
+ uint8_t pt = packets_[packet_indices[0]].rtp_header.payloadType;
+ printf("SSRC: %08x, %" PRIuS " packets, pt=%d\n", ssrc,
+ packet_indices.size(), pt);
+ }
+
+ // TODO(solenberg): Better validation of identified SSRC streams.
+ //
+ // Since we're dealing with raw network data here, we will wrongly identify
+ // some packets as RTP. When these packets are consumed by RtpPlayer, they
+ // are unlikely to cause issues as they will ultimately be filtered out by
+ // the RtpRtcp module. However, we should really do better filtering here,
+ // which we can accomplish in a number of ways, e.g.:
+ //
+ // - Verify that the time stamps and sequence numbers for RTP packets are
+ // both increasing/decreasing. If they move in different directions, the
+ // SSRC is likely bogus and can be dropped. (Normally they should be inc-
+ // reasing but we must allow packet reordering).
+ // - If RTP sequence number is not changing, drop the stream.
+ // - Can also use srcip:port->dstip:port pairs, assuming few SSRC collisions
+ // for up/down streams.
+
+ next_packet_it_ = packets_.begin();
+ return kResultSuccess;
+ }
+
+ bool NextPacket(RtpPacket* packet) override {
+ uint32_t length = RtpPacket::kMaxPacketBufferSize;
+ if (NextPcap(packet->data, &length, &packet->time_ms) != kResultSuccess)
+ return false;
+ packet->length = static_cast<size_t>(length);
+ packet->original_length = packet->length;
+ return true;
+ }
+
+ virtual int NextPcap(uint8_t* data, uint32_t* length, uint32_t* time_ms) {
+ assert(data);
+ assert(length);
+ assert(time_ms);
+
+ if (next_packet_it_ == packets_.end()) {
+ return -1;
+ }
+ if (*length < next_packet_it_->payload_length) {
+ return -1;
+ }
+ TRY_PCAP(fseek(file_, next_packet_it_->pos_in_file, SEEK_SET));
+ TRY_PCAP(Read(data, next_packet_it_->payload_length));
+ *length = next_packet_it_->payload_length;
+ *time_ms = next_packet_it_->time_offset_ms;
+ next_packet_it_++;
+
+ return 0;
+ }
+
+ private:
+ // A marker of an RTP packet within the file.
+ struct RtpPacketMarker {
+ uint32_t packet_number; // One-based index (like in WireShark)
+ uint32_t time_offset_ms;
+ uint32_t source_ip;
+ uint32_t dest_ip;
+ uint16_t source_port;
+ uint16_t dest_port;
+ RTPHeader rtp_header;
+ int32_t pos_in_file; // Byte offset of payload from start of file.
+ uint32_t payload_length;
+ };
+
+ typedef std::vector<RtpPacketMarker>::iterator PacketIterator;
+ typedef std::map<uint32_t, std::vector<uint32_t> > SsrcMap;
+ typedef std::map<uint32_t, std::vector<uint32_t> >::iterator SsrcMapIterator;
+
+ int ReadGlobalHeader() {
+ uint32_t magic;
+ TRY_PCAP(Read(&magic, false));
+ if (magic == kPcapBOMSwapOrder) {
+ swap_pcap_byte_order_ = true;
+ } else if (magic == kPcapBOMNoSwapOrder) {
+ swap_pcap_byte_order_ = false;
+ } else {
+ return kResultFail;
+ }
+
+ uint16_t version_major;
+ uint16_t version_minor;
+ TRY_PCAP(Read(&version_major, false));
+ TRY_PCAP(Read(&version_minor, false));
+ if (version_major != kPcapVersionMajor ||
+ version_minor != kPcapVersionMinor) {
+ return kResultFail;
+ }
+
+ int32_t this_zone; // GMT to local correction.
+ uint32_t sigfigs; // Accuracy of timestamps.
+ uint32_t snaplen; // Max length of captured packets, in octets.
+ uint32_t network; // Data link type.
+ TRY_PCAP(Read(&this_zone, false));
+ TRY_PCAP(Read(&sigfigs, false));
+ TRY_PCAP(Read(&snaplen, false));
+ TRY_PCAP(Read(&network, false));
+
+ // Accept only LINKTYPE_NULL and LINKTYPE_ETHERNET.
+ // See: http://www.tcpdump.org/linktypes.html
+ if (network != kLinktypeNull && network != kLinktypeEthernet) {
+ return kResultFail;
+ }
+
+ return kResultSuccess;
+ }
+
+ int ReadPacket(int32_t* next_packet_pos,
+ uint32_t stream_start_ms,
+ uint32_t number,
+ const std::set<uint32_t>& ssrc_filter) {
+ assert(next_packet_pos);
+
+ uint32_t ts_sec; // Timestamp seconds.
+ uint32_t ts_usec; // Timestamp microseconds.
+ uint32_t incl_len; // Number of octets of packet saved in file.
+ uint32_t orig_len; // Actual length of packet.
+ TRY_PCAP(Read(&ts_sec, false));
+ TRY_PCAP(Read(&ts_usec, false));
+ TRY_PCAP(Read(&incl_len, false));
+ TRY_PCAP(Read(&orig_len, false));
+
+ *next_packet_pos = ftell(file_) + incl_len;
+
+ RtpPacketMarker marker = {0};
+ marker.packet_number = number;
+ marker.time_offset_ms = CalcTimeDelta(ts_sec, ts_usec, stream_start_ms);
+ TRY_PCAP(ReadPacketHeader(&marker));
+ marker.pos_in_file = ftell(file_);
+
+ if (marker.payload_length > sizeof(read_buffer_)) {
+ printf("Packet too large!\n");
+ return kResultFail;
+ }
+ TRY_PCAP(Read(read_buffer_, marker.payload_length));
+
+ RtpUtility::RtpHeaderParser rtp_parser(read_buffer_, marker.payload_length);
+ if (rtp_parser.RTCP()) {
+ rtp_parser.ParseRtcp(&marker.rtp_header);
+ packets_.push_back(marker);
+ } else {
+ if (!rtp_parser.Parse(marker.rtp_header, NULL)) {
+ DEBUG_LOG("Not recognized as RTP/RTCP");
+ return kResultSkip;
+ }
+
+ uint32_t ssrc = marker.rtp_header.ssrc;
+ if (ssrc_filter.empty() || ssrc_filter.find(ssrc) != ssrc_filter.end()) {
+ packets_by_ssrc_[ssrc].push_back(
+ static_cast<uint32_t>(packets_.size()));
+ packets_.push_back(marker);
+ } else {
+ return kResultSkip;
+ }
+ }
+
+ return kResultSuccess;
+ }
+
+ int ReadPacketHeader(RtpPacketMarker* marker) {
+ int32_t file_pos = ftell(file_);
+
+ // Check for BSD null/loopback frame header. The header is just 4 bytes in
+ // native byte order, so we check for both versions as we don't care about
+ // the header as such and will likely fail reading the IP header if this is
+ // something else than null/loopback.
+ uint32_t protocol;
+ TRY_PCAP(Read(&protocol, true));
+ if (protocol == kBsdNullLoopback1 || protocol == kBsdNullLoopback2) {
+ int result = ReadXxpIpHeader(marker);
+ DEBUG_LOG("Recognized loopback frame");
+ if (result != kResultSkip) {
+ return result;
+ }
+ }
+
+ TRY_PCAP(fseek(file_, file_pos, SEEK_SET));
+
+ // Check for Ethernet II, IP frame header.
+ uint16_t type;
+ TRY_PCAP(Skip(kEthernetIIHeaderMacSkip)); // Source+destination MAC.
+ TRY_PCAP(Read(&type, true));
+ if (type == kEthertypeIp) {
+ int result = ReadXxpIpHeader(marker);
+ DEBUG_LOG("Recognized ethernet 2 frame");
+ if (result != kResultSkip) {
+ return result;
+ }
+ }
+
+ return kResultSkip;
+ }
+
+ uint32_t CalcTimeDelta(uint32_t ts_sec, uint32_t ts_usec, uint32_t start_ms) {
+ // Round to nearest ms.
+ uint64_t t2_ms = ((static_cast<uint64_t>(ts_sec) * 1000000) + ts_usec +
+ 500) / 1000;
+ uint64_t t1_ms = static_cast<uint64_t>(start_ms);
+ if (t2_ms < t1_ms) {
+ return 0;
+ } else {
+ return t2_ms - t1_ms;
+ }
+ }
+
+ int ReadXxpIpHeader(RtpPacketMarker* marker) {
+ assert(marker);
+
+ uint16_t version;
+ uint16_t length;
+ uint16_t id;
+ uint16_t fragment;
+ uint16_t protocol;
+ uint16_t checksum;
+ TRY_PCAP(Read(&version, true));
+ TRY_PCAP(Read(&length, true));
+ TRY_PCAP(Read(&id, true));
+ TRY_PCAP(Read(&fragment, true));
+ TRY_PCAP(Read(&protocol, true));
+ TRY_PCAP(Read(&checksum, true));
+ TRY_PCAP(Read(&marker->source_ip, true));
+ TRY_PCAP(Read(&marker->dest_ip, true));
+
+ if (((version >> 12) & 0x000f) != kIpVersion4) {
+ DEBUG_LOG("IP header is not IPv4");
+ return kResultSkip;
+ }
+
+ if (fragment != kFragmentOffsetClear &&
+ fragment != kFragmentOffsetDoNotFragment) {
+ DEBUG_LOG("IP fragments cannot be handled");
+ return kResultSkip;
+ }
+
+ // Skip remaining fields of IP header.
+ uint16_t header_length = (version & 0x0f00) >> (8 - 2);
+ assert(header_length >= kMinIpHeaderLength);
+ TRY_PCAP(Skip(header_length - kMinIpHeaderLength));
+
+ protocol = protocol & 0x00ff;
+ if (protocol == kProtocolTcp) {
+ DEBUG_LOG("TCP packets are not handled");
+ return kResultSkip;
+ } else if (protocol == kProtocolUdp) {
+ uint16_t length;
+ uint16_t checksum;
+ TRY_PCAP(Read(&marker->source_port, true));
+ TRY_PCAP(Read(&marker->dest_port, true));
+ TRY_PCAP(Read(&length, true));
+ TRY_PCAP(Read(&checksum, true));
+ marker->payload_length = length - kUdpHeaderLength;
+ } else {
+ DEBUG_LOG("Unknown transport (expected UDP or TCP)");
+ return kResultSkip;
+ }
+
+ return kResultSuccess;
+ }
+
+ int Read(uint32_t* out, bool expect_network_order) {
+ uint32_t tmp = 0;
+ if (fread(&tmp, 1, sizeof(uint32_t), file_) != sizeof(uint32_t)) {
+ return kResultFail;
+ }
+ if ((!expect_network_order && swap_pcap_byte_order_) ||
+ (expect_network_order && swap_network_byte_order_)) {
+ tmp = ((tmp >> 24) & 0x000000ff) | (tmp << 24) |
+ ((tmp >> 8) & 0x0000ff00) | ((tmp << 8) & 0x00ff0000);
+ }
+ *out = tmp;
+ return kResultSuccess;
+ }
+
+ int Read(uint16_t* out, bool expect_network_order) {
+ uint16_t tmp = 0;
+ if (fread(&tmp, 1, sizeof(uint16_t), file_) != sizeof(uint16_t)) {
+ return kResultFail;
+ }
+ if ((!expect_network_order && swap_pcap_byte_order_) ||
+ (expect_network_order && swap_network_byte_order_)) {
+ tmp = ((tmp >> 8) & 0x00ff) | (tmp << 8);
+ }
+ *out = tmp;
+ return kResultSuccess;
+ }
+
+ int Read(uint8_t* out, uint32_t count) {
+ if (fread(out, 1, count, file_) != count) {
+ return kResultFail;
+ }
+ return kResultSuccess;
+ }
+
+ int Read(int32_t* out, bool expect_network_order) {
+ int32_t tmp = 0;
+ if (fread(&tmp, 1, sizeof(uint32_t), file_) != sizeof(uint32_t)) {
+ return kResultFail;
+ }
+ if ((!expect_network_order && swap_pcap_byte_order_) ||
+ (expect_network_order && swap_network_byte_order_)) {
+ tmp = ((tmp >> 24) & 0x000000ff) | (tmp << 24) |
+ ((tmp >> 8) & 0x0000ff00) | ((tmp << 8) & 0x00ff0000);
+ }
+ *out = tmp;
+ return kResultSuccess;
+ }
+
+ int Skip(uint32_t length) {
+ if (fseek(file_, length, SEEK_CUR) != 0) {
+ return kResultFail;
+ }
+ return kResultSuccess;
+ }
+
+ FILE* file_;
+ bool swap_pcap_byte_order_;
+ const bool swap_network_byte_order_;
+ uint8_t read_buffer_[kMaxReadBufferSize];
+
+ SsrcMap packets_by_ssrc_;
+ std::vector<RtpPacketMarker> packets_;
+ PacketIterator next_packet_it_;
+
+ RTC_DISALLOW_COPY_AND_ASSIGN(PcapReader);
+};
+
+RtpFileReader* RtpFileReader::Create(FileFormat format,
+ const std::string& filename,
+ const std::set<uint32_t>& ssrc_filter) {
+ RtpFileReaderImpl* reader = NULL;
+ switch (format) {
+ case kPcap:
+ reader = new PcapReader();
+ break;
+ case kRtpDump:
+ reader = new RtpDumpReader();
+ break;
+ case kLengthPacketInterleaved:
+ reader = new InterleavedRtpFileReader();
+ break;
+ }
+ if (!reader->Init(filename, ssrc_filter)) {
+ delete reader;
+ return NULL;
+ }
+ return reader;
+}
+
+RtpFileReader* RtpFileReader::Create(FileFormat format,
+ const std::string& filename) {
+ return RtpFileReader::Create(format, filename, std::set<uint32_t>());
+}
+
+} // namespace test
+} // namespace webrtc
diff --git a/webrtc/test/rtp_file_reader.h b/webrtc/test/rtp_file_reader.h
new file mode 100644
index 0000000000..c132d318fd
--- /dev/null
+++ b/webrtc/test/rtp_file_reader.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2014 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+#ifndef WEBRTC_TEST_RTP_FILE_READER_H_
+#define WEBRTC_TEST_RTP_FILE_READER_H_
+
+#include <set>
+#include <string>
+
+#include "webrtc/common_types.h"
+
+namespace webrtc {
+namespace test {
+
+struct RtpPacket {
+ // Accommodate for 50 ms packets of 32 kHz PCM16 samples (3200 bytes) plus
+ // some overhead.
+ static const size_t kMaxPacketBufferSize = 3500;
+ uint8_t data[kMaxPacketBufferSize];
+ size_t length;
+ // The length the packet had on wire. Will be different from |length| when
+ // reading a header-only RTP dump.
+ size_t original_length;
+
+ uint32_t time_ms;
+};
+
+class RtpFileReader {
+ public:
+ enum FileFormat { kPcap, kRtpDump, kLengthPacketInterleaved };
+
+ virtual ~RtpFileReader() {}
+ static RtpFileReader* Create(FileFormat format,
+ const std::string& filename);
+ static RtpFileReader* Create(FileFormat format,
+ const std::string& filename,
+ const std::set<uint32_t>& ssrc_filter);
+
+ virtual bool NextPacket(RtpPacket* packet) = 0;
+};
+} // namespace test
+} // namespace webrtc
+#endif // WEBRTC_TEST_RTP_FILE_READER_H_
diff --git a/webrtc/test/rtp_file_reader_unittest.cc b/webrtc/test/rtp_file_reader_unittest.cc
new file mode 100644
index 0000000000..929813f999
--- /dev/null
+++ b/webrtc/test/rtp_file_reader_unittest.cc
@@ -0,0 +1,125 @@
+/*
+ * 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 <map>
+
+#include "testing/gtest/include/gtest/gtest.h"
+#include "webrtc/base/scoped_ptr.h"
+#include "webrtc/modules/rtp_rtcp/source/rtp_utility.h"
+#include "webrtc/test/rtp_file_reader.h"
+#include "webrtc/test/testsupport/fileutils.h"
+
+namespace webrtc {
+
+class TestRtpFileReader : public ::testing::Test {
+ public:
+ void Init(const std::string& filename, bool headers_only_file) {
+ std::string filepath =
+ test::ResourcePath("video_coding/" + filename, "rtp");
+ rtp_packet_source_.reset(
+ test::RtpFileReader::Create(test::RtpFileReader::kRtpDump, filepath));
+ ASSERT_TRUE(rtp_packet_source_.get() != NULL);
+ headers_only_file_ = headers_only_file;
+ }
+
+ int CountRtpPackets() {
+ test::RtpPacket packet;
+ int c = 0;
+ while (rtp_packet_source_->NextPacket(&packet)) {
+ if (headers_only_file_)
+ EXPECT_LT(packet.length, packet.original_length);
+ else
+ EXPECT_EQ(packet.length, packet.original_length);
+ c++;
+ }
+ return c;
+ }
+
+ private:
+ rtc::scoped_ptr<test::RtpFileReader> rtp_packet_source_;
+ bool headers_only_file_;
+};
+
+TEST_F(TestRtpFileReader, Test60Packets) {
+ Init("pltype103", false);
+ EXPECT_EQ(60, CountRtpPackets());
+}
+
+TEST_F(TestRtpFileReader, Test60PacketsHeaderOnly) {
+ Init("pltype103_header_only", true);
+ EXPECT_EQ(60, CountRtpPackets());
+}
+
+typedef std::map<uint32_t, int> PacketsPerSsrc;
+
+class TestPcapFileReader : public ::testing::Test {
+ public:
+ void Init(const std::string& filename) {
+ std::string filepath =
+ test::ResourcePath("video_coding/" + filename, "pcap");
+ rtp_packet_source_.reset(
+ test::RtpFileReader::Create(test::RtpFileReader::kPcap, filepath));
+ ASSERT_TRUE(rtp_packet_source_.get() != NULL);
+ }
+
+ int CountRtpPackets() {
+ int c = 0;
+ test::RtpPacket packet;
+ while (rtp_packet_source_->NextPacket(&packet)) {
+ EXPECT_EQ(packet.length, packet.original_length);
+ c++;
+ }
+ return c;
+ }
+
+ PacketsPerSsrc CountRtpPacketsPerSsrc() {
+ PacketsPerSsrc pps;
+ test::RtpPacket packet;
+ while (rtp_packet_source_->NextPacket(&packet)) {
+ RtpUtility::RtpHeaderParser rtp_header_parser(packet.data, packet.length);
+ webrtc::RTPHeader header;
+ if (!rtp_header_parser.RTCP() && rtp_header_parser.Parse(header, NULL)) {
+ pps[header.ssrc]++;
+ }
+ }
+ return pps;
+ }
+
+ private:
+ rtc::scoped_ptr<test::RtpFileReader> rtp_packet_source_;
+};
+
+TEST_F(TestPcapFileReader, TestEthernetIIFrame) {
+ Init("frame-ethernet-ii");
+ EXPECT_EQ(368, CountRtpPackets());
+}
+
+TEST_F(TestPcapFileReader, TestLoopbackFrame) {
+ Init("frame-loopback");
+ EXPECT_EQ(491, CountRtpPackets());
+}
+
+TEST_F(TestPcapFileReader, TestTwoSsrc) {
+ Init("ssrcs-2");
+ PacketsPerSsrc pps = CountRtpPacketsPerSsrc();
+ EXPECT_EQ(2UL, pps.size());
+ EXPECT_EQ(370, pps[0x78d48f61]);
+ EXPECT_EQ(60, pps[0xae94130b]);
+}
+
+TEST_F(TestPcapFileReader, TestThreeSsrc) {
+ Init("ssrcs-3");
+ PacketsPerSsrc pps = CountRtpPacketsPerSsrc();
+ EXPECT_EQ(3UL, pps.size());
+ EXPECT_EQ(162, pps[0x938c5eaa]);
+ EXPECT_EQ(113, pps[0x59fe6ef0]);
+ EXPECT_EQ(61, pps[0xed2bd2ac]);
+}
+} // namespace webrtc
diff --git a/webrtc/test/rtp_file_writer.cc b/webrtc/test/rtp_file_writer.cc
new file mode 100644
index 0000000000..d9e0586468
--- /dev/null
+++ b/webrtc/test/rtp_file_writer.cc
@@ -0,0 +1,108 @@
+/*
+ * Copyright (c) 2014 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "webrtc/test/rtp_file_writer.h"
+
+#include <stdio.h>
+
+#include <string>
+
+#include "webrtc/base/checks.h"
+#include "webrtc/base/constructormagic.h"
+
+namespace webrtc {
+namespace test {
+
+static const uint16_t kPacketHeaderSize = 8;
+static const char kFirstLine[] = "#!rtpplay1.0 0.0.0.0/0\n";
+
+// Write RTP packets to file in rtpdump format, as documented at:
+// http://www.cs.columbia.edu/irt/software/rtptools/
+class RtpDumpWriter : public RtpFileWriter {
+ public:
+ explicit RtpDumpWriter(FILE* file) : file_(file) {
+ RTC_CHECK(file_ != NULL);
+ Init();
+ }
+ virtual ~RtpDumpWriter() {
+ if (file_ != NULL) {
+ fclose(file_);
+ file_ = NULL;
+ }
+ }
+
+ bool WritePacket(const RtpPacket* packet) override {
+ uint16_t len = static_cast<uint16_t>(packet->length + kPacketHeaderSize);
+ uint16_t plen = static_cast<uint16_t>(packet->original_length);
+ uint32_t offset = packet->time_ms;
+ RTC_CHECK(WriteUint16(len));
+ RTC_CHECK(WriteUint16(plen));
+ RTC_CHECK(WriteUint32(offset));
+ return fwrite(packet->data, sizeof(uint8_t), packet->length, file_) ==
+ packet->length;
+ }
+
+ private:
+ bool Init() {
+ fprintf(file_, "%s", kFirstLine);
+
+ RTC_CHECK(WriteUint32(0));
+ RTC_CHECK(WriteUint32(0));
+ RTC_CHECK(WriteUint32(0));
+ RTC_CHECK(WriteUint16(0));
+ RTC_CHECK(WriteUint16(0));
+
+ return true;
+ }
+
+ bool WriteUint32(uint32_t in) {
+ // Loop through shifts = {24, 16, 8, 0}.
+ for (int shifts = 24; shifts >= 0; shifts -= 8) {
+ uint8_t tmp = static_cast<uint8_t>((in >> shifts) & 0xFF);
+ if (fwrite(&tmp, sizeof(uint8_t), 1, file_) != 1)
+ return false;
+ }
+ return true;
+ }
+
+ bool WriteUint16(uint16_t in) {
+ // Write 8 MSBs.
+ uint8_t tmp = static_cast<uint8_t>((in >> 8) & 0xFF);
+ if (fwrite(&tmp, sizeof(uint8_t), 1, file_) != 1)
+ return false;
+ // Write 8 LSBs.
+ tmp = static_cast<uint8_t>(in & 0xFF);
+ if (fwrite(&tmp, sizeof(uint8_t), 1, file_) != 1)
+ return false;
+ return true;
+ }
+
+ FILE* file_;
+
+ RTC_DISALLOW_COPY_AND_ASSIGN(RtpDumpWriter);
+};
+
+RtpFileWriter* RtpFileWriter::Create(FileFormat format,
+ const std::string& filename) {
+ FILE* file = fopen(filename.c_str(), "wb");
+ if (file == NULL) {
+ printf("ERROR: Can't open file: %s\n", filename.c_str());
+ return NULL;
+ }
+ switch (format) {
+ case kRtpDump:
+ return new RtpDumpWriter(file);
+ }
+ fclose(file);
+ return NULL;
+}
+
+} // namespace test
+} // namespace webrtc
diff --git a/webrtc/test/rtp_file_writer.h b/webrtc/test/rtp_file_writer.h
new file mode 100644
index 0000000000..453b27762e
--- /dev/null
+++ b/webrtc/test/rtp_file_writer.h
@@ -0,0 +1,33 @@
+/*
+ * Copyright (c) 2014 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+#ifndef WEBRTC_TEST_RTP_FILE_WRITER_H_
+#define WEBRTC_TEST_RTP_FILE_WRITER_H_
+
+#include <string>
+
+#include "webrtc/common_types.h"
+#include "webrtc/test/rtp_file_reader.h"
+
+namespace webrtc {
+namespace test {
+class RtpFileWriter {
+ public:
+ enum FileFormat {
+ kRtpDump,
+ };
+
+ virtual ~RtpFileWriter() {}
+ static RtpFileWriter* Create(FileFormat format, const std::string& filename);
+
+ virtual bool WritePacket(const RtpPacket* packet) = 0;
+};
+} // namespace test
+} // namespace webrtc
+#endif // WEBRTC_TEST_RTP_FILE_WRITER_H_
diff --git a/webrtc/test/rtp_file_writer_unittest.cc b/webrtc/test/rtp_file_writer_unittest.cc
new file mode 100644
index 0000000000..2c7c88cc3f
--- /dev/null
+++ b/webrtc/test/rtp_file_writer_unittest.cc
@@ -0,0 +1,75 @@
+/*
+ * Copyright (c) 2014 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include <string.h>
+
+#include "testing/gtest/include/gtest/gtest.h"
+#include "webrtc/base/scoped_ptr.h"
+#include "webrtc/test/rtp_file_reader.h"
+#include "webrtc/test/rtp_file_writer.h"
+#include "webrtc/test/testsupport/fileutils.h"
+
+namespace webrtc {
+
+class RtpFileWriterTest : public ::testing::Test {
+ public:
+ void Init(const std::string& filename) {
+ filename_ = test::OutputPath() + filename;
+ rtp_writer_.reset(
+ test::RtpFileWriter::Create(test::RtpFileWriter::kRtpDump, filename_));
+ }
+
+ void WriteRtpPackets(int num_packets) {
+ ASSERT_TRUE(rtp_writer_.get() != NULL);
+ test::RtpPacket packet;
+ for (int i = 1; i <= num_packets; ++i) {
+ packet.length = i;
+ packet.original_length = i;
+ packet.time_ms = i;
+ memset(packet.data, i, packet.length);
+ EXPECT_TRUE(rtp_writer_->WritePacket(&packet));
+ }
+ }
+
+ void CloseOutputFile() { rtp_writer_.reset(); }
+
+ void VerifyFileContents(int expected_packets) {
+ ASSERT_TRUE(rtp_writer_.get() == NULL)
+ << "Must call CloseOutputFile before VerifyFileContents";
+ rtc::scoped_ptr<test::RtpFileReader> rtp_reader(
+ test::RtpFileReader::Create(test::RtpFileReader::kRtpDump, filename_));
+ ASSERT_TRUE(rtp_reader.get() != NULL);
+ test::RtpPacket packet;
+ int i = 0;
+ while (rtp_reader->NextPacket(&packet)) {
+ ++i;
+ EXPECT_EQ(static_cast<size_t>(i), packet.length);
+ EXPECT_EQ(static_cast<size_t>(i), packet.original_length);
+ EXPECT_EQ(static_cast<uint32_t>(i), packet.time_ms);
+ for (int j = 0; j < i; ++j) {
+ EXPECT_EQ(i, packet.data[j]);
+ }
+ }
+ EXPECT_EQ(expected_packets, i);
+ }
+
+ private:
+ rtc::scoped_ptr<test::RtpFileWriter> rtp_writer_;
+ std::string filename_;
+};
+
+TEST_F(RtpFileWriterTest, WriteToRtpDump) {
+ Init("test_rtp_file_writer.rtp");
+ WriteRtpPackets(10);
+ CloseOutputFile();
+ VerifyFileContents(10);
+}
+
+} // namespace webrtc
diff --git a/webrtc/test/rtp_rtcp_observer.h b/webrtc/test/rtp_rtcp_observer.h
new file mode 100644
index 0000000000..89b6dd06bd
--- /dev/null
+++ b/webrtc/test/rtp_rtcp_observer.h
@@ -0,0 +1,141 @@
+/*
+ * 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_ENGINE_TEST_COMMON_RTP_RTCP_OBSERVER_H_
+#define WEBRTC_VIDEO_ENGINE_TEST_COMMON_RTP_RTCP_OBSERVER_H_
+
+#include <map>
+#include <vector>
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+#include "webrtc/base/criticalsection.h"
+#include "webrtc/modules/rtp_rtcp/interface/rtp_header_parser.h"
+#include "webrtc/test/constants.h"
+#include "webrtc/test/direct_transport.h"
+#include "webrtc/typedefs.h"
+#include "webrtc/video_send_stream.h"
+
+namespace webrtc {
+namespace test {
+
+class PacketTransport;
+
+class RtpRtcpObserver {
+ public:
+ enum Action {
+ SEND_PACKET,
+ DROP_PACKET,
+ };
+
+ virtual ~RtpRtcpObserver() {}
+
+ virtual EventTypeWrapper Wait() {
+ EventTypeWrapper result = observation_complete_->Wait(timeout_ms_);
+ return result;
+ }
+
+ virtual Action OnSendRtp(const uint8_t* packet, size_t length) {
+ return SEND_PACKET;
+ }
+
+ virtual Action OnSendRtcp(const uint8_t* packet, size_t length) {
+ return SEND_PACKET;
+ }
+
+ virtual Action OnReceiveRtp(const uint8_t* packet, size_t length) {
+ return SEND_PACKET;
+ }
+
+ virtual Action OnReceiveRtcp(const uint8_t* packet, size_t length) {
+ return SEND_PACKET;
+ }
+
+ protected:
+ explicit RtpRtcpObserver(unsigned int event_timeout_ms)
+ : observation_complete_(EventWrapper::Create()),
+ parser_(RtpHeaderParser::Create()),
+ timeout_ms_(event_timeout_ms) {
+ parser_->RegisterRtpHeaderExtension(kRtpExtensionTransmissionTimeOffset,
+ kTOffsetExtensionId);
+ parser_->RegisterRtpHeaderExtension(kRtpExtensionAbsoluteSendTime,
+ kAbsSendTimeExtensionId);
+ parser_->RegisterRtpHeaderExtension(kRtpExtensionTransportSequenceNumber,
+ kTransportSequenceNumberExtensionId);
+ }
+
+ const rtc::scoped_ptr<EventWrapper> observation_complete_;
+ const rtc::scoped_ptr<RtpHeaderParser> parser_;
+
+ private:
+ unsigned int timeout_ms_;
+};
+
+class PacketTransport : public test::DirectTransport {
+ public:
+ enum TransportType { kReceiver, kSender };
+
+ PacketTransport(Call* send_call,
+ RtpRtcpObserver* observer,
+ TransportType transport_type,
+ const FakeNetworkPipe::Config& configuration)
+ : test::DirectTransport(configuration, send_call),
+ observer_(observer),
+ transport_type_(transport_type) {}
+
+ private:
+ bool SendRtp(const uint8_t* packet,
+ size_t length,
+ const PacketOptions& options) override {
+ EXPECT_FALSE(RtpHeaderParser::IsRtcp(packet, length));
+ RtpRtcpObserver::Action action;
+ {
+ if (transport_type_ == kSender) {
+ action = observer_->OnSendRtp(packet, length);
+ } else {
+ action = observer_->OnReceiveRtp(packet, length);
+ }
+ }
+ switch (action) {
+ case RtpRtcpObserver::DROP_PACKET:
+ // Drop packet silently.
+ return true;
+ case RtpRtcpObserver::SEND_PACKET:
+ return test::DirectTransport::SendRtp(packet, length, options);
+ }
+ return true; // Will never happen, makes compiler happy.
+ }
+
+ bool SendRtcp(const uint8_t* packet, size_t length) override {
+ EXPECT_TRUE(RtpHeaderParser::IsRtcp(packet, length));
+ RtpRtcpObserver::Action action;
+ {
+ if (transport_type_ == kSender) {
+ action = observer_->OnSendRtcp(packet, length);
+ } else {
+ action = observer_->OnReceiveRtcp(packet, length);
+ }
+ }
+ switch (action) {
+ case RtpRtcpObserver::DROP_PACKET:
+ // Drop packet silently.
+ return true;
+ case RtpRtcpObserver::SEND_PACKET:
+ return test::DirectTransport::SendRtcp(packet, length);
+ }
+ return true; // Will never happen, makes compiler happy.
+ }
+
+ RtpRtcpObserver* const observer_;
+ TransportType transport_type_;
+};
+} // namespace test
+} // namespace webrtc
+
+#endif // WEBRTC_VIDEO_ENGINE_TEST_COMMON_RTP_RTCP_OBSERVER_H_
diff --git a/webrtc/test/run_all_unittests.cc b/webrtc/test/run_all_unittests.cc
new file mode 100644
index 0000000000..486df5ea8c
--- /dev/null
+++ b/webrtc/test/run_all_unittests.cc
@@ -0,0 +1,16 @@
+/*
+ * Copyright (c) 2011 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/test_suite.h"
+
+int main(int argc, char** argv) {
+ webrtc::test::TestSuite test_suite(argc, argv);
+ return test_suite.Run();
+}
diff --git a/webrtc/test/run_loop.cc b/webrtc/test/run_loop.cc
new file mode 100644
index 0000000000..92f85dd09d
--- /dev/null
+++ b/webrtc/test/run_loop.cc
@@ -0,0 +1,22 @@
+/*
+ * 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/run_loop.h"
+
+#include <stdio.h>
+
+namespace webrtc {
+namespace test {
+
+void PressEnterToContinue() {
+ puts(">> Press ENTER to continue...");
+ while (getc(stdin) != '\n' && !feof(stdin));
+}
+} // namespace test
+} // namespace webrtc
diff --git a/webrtc/test/run_loop.h b/webrtc/test/run_loop.h
new file mode 100644
index 0000000000..31012525e2
--- /dev/null
+++ b/webrtc/test/run_loop.h
@@ -0,0 +1,22 @@
+/*
+ * 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_ENGINE_TEST_COMMON_RUN_LOOP_H_
+#define WEBRTC_VIDEO_ENGINE_TEST_COMMON_RUN_LOOP_H_
+
+namespace webrtc {
+namespace test {
+
+// Blocks until the user presses enter.
+void PressEnterToContinue();
+
+} // namespace test
+} // namespace webrtc
+
+#endif // WEBRTC_VIDEO_ENGINE_TEST_COMMON_RUN_LOOP_H_
diff --git a/webrtc/test/run_test.cc b/webrtc/test/run_test.cc
new file mode 100644
index 0000000000..4daea42a1e
--- /dev/null
+++ b/webrtc/test/run_test.cc
@@ -0,0 +1,23 @@
+/*
+ * 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/run_test.h"
+
+#include <stdio.h>
+
+namespace webrtc {
+namespace test {
+
+void RunTest(void(*test)()) {
+ (*test)();
+}
+
+} // namespace test
+} // namespace webrtc
diff --git a/webrtc/test/run_test.h b/webrtc/test/run_test.h
new file mode 100644
index 0000000000..b515254685
--- /dev/null
+++ b/webrtc/test/run_test.h
@@ -0,0 +1,22 @@
+/*
+ * 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_RUN_TEST_H
+#define WEBRTC_TEST_RUN_TEST_H
+
+namespace webrtc {
+namespace test {
+
+// Running a test function on a separate thread, if required by the OS.
+void RunTest(void(*test)());
+
+} // namespace test
+} // namespace webrtc
+
+#endif // WEBRTC_TEST_RUN_TEST_H
diff --git a/webrtc/test/statistics.cc b/webrtc/test/statistics.cc
new file mode 100644
index 0000000000..0075d4c9a3
--- /dev/null
+++ b/webrtc/test/statistics.cc
@@ -0,0 +1,41 @@
+/*
+ * 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/statistics.h"
+
+#include <math.h>
+
+namespace webrtc {
+namespace test {
+
+Statistics::Statistics() : sum_(0.0), sum_squared_(0.0), count_(0) {}
+
+void Statistics::AddSample(double sample) {
+ sum_ += sample;
+ sum_squared_ += sample * sample;
+ ++count_;
+}
+
+double Statistics::Mean() const {
+ if (count_ == 0)
+ return 0.0;
+ return sum_ / count_;
+}
+
+double Statistics::Variance() const {
+ if (count_ == 0)
+ return 0.0;
+ return sum_squared_ / count_ - Mean() * Mean();
+}
+
+double Statistics::StandardDeviation() const {
+ return sqrt(Variance());
+}
+} // namespace test
+} // namespace webrtc
diff --git a/webrtc/test/statistics.h b/webrtc/test/statistics.h
new file mode 100644
index 0000000000..0fc3a04ea9
--- /dev/null
+++ b/webrtc/test/statistics.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_ENGINE_TEST_COMMON_STATISTICS_H_
+#define WEBRTC_VIDEO_ENGINE_TEST_COMMON_STATISTICS_H_
+
+#include "webrtc/typedefs.h"
+
+namespace webrtc {
+namespace test {
+
+class Statistics {
+ public:
+ Statistics();
+
+ void AddSample(double sample);
+
+ double Mean() const;
+ double Variance() const;
+ double StandardDeviation() const;
+
+ private:
+ double sum_;
+ double sum_squared_;
+ uint64_t count_;
+};
+} // namespace test
+} // namespace webrtc
+
+#endif // WEBRTC_VIDEO_ENGINE_TEST_COMMON_STATISTICS_H_
diff --git a/webrtc/test/test.gyp b/webrtc/test/test.gyp
new file mode 100644
index 0000000000..0d251dffdd
--- /dev/null
+++ b/webrtc/test/test.gyp
@@ -0,0 +1,251 @@
+# Copyright (c) 2011 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.
+
+# TODO(andrew): consider moving test_support to src/base/test.
+{
+ 'includes': [
+ '../build/common.gypi',
+ ],
+ 'targets': [
+ {
+ 'target_name': 'channel_transport',
+ 'type': 'static_library',
+ 'dependencies': [
+ '<(DEPTH)/testing/gtest.gyp:gtest',
+ '<(webrtc_root)/common.gyp:webrtc_common',
+ '<(webrtc_root)/system_wrappers/system_wrappers.gyp:system_wrappers',
+ ],
+ 'sources': [
+ 'channel_transport/channel_transport.cc',
+ 'channel_transport/include/channel_transport.h',
+ 'channel_transport/traffic_control_win.cc',
+ 'channel_transport/traffic_control_win.h',
+ 'channel_transport/udp_socket_manager_posix.cc',
+ 'channel_transport/udp_socket_manager_posix.h',
+ 'channel_transport/udp_socket_manager_wrapper.cc',
+ 'channel_transport/udp_socket_manager_wrapper.h',
+ 'channel_transport/udp_socket_posix.cc',
+ 'channel_transport/udp_socket_posix.h',
+ 'channel_transport/udp_socket_wrapper.cc',
+ 'channel_transport/udp_socket_wrapper.h',
+ 'channel_transport/udp_socket2_manager_win.cc',
+ 'channel_transport/udp_socket2_manager_win.h',
+ 'channel_transport/udp_socket2_win.cc',
+ 'channel_transport/udp_socket2_win.h',
+ 'channel_transport/udp_transport.h',
+ 'channel_transport/udp_transport_impl.cc',
+ 'channel_transport/udp_transport_impl.h',
+ ],
+ },
+ {
+ 'target_name': 'frame_generator',
+ 'type': 'static_library',
+ 'sources': [
+ 'frame_generator.cc',
+ 'frame_generator.h',
+ ],
+ 'dependencies': [
+ '<(webrtc_root)/common_video/common_video.gyp:common_video',
+ ],
+ },
+ {
+ 'target_name': 'rtp_test_utils',
+ 'type': 'static_library',
+ 'sources': [
+ 'rtcp_packet_parser.cc',
+ 'rtcp_packet_parser.h',
+ 'rtp_file_reader.cc',
+ 'rtp_file_reader.h',
+ 'rtp_file_writer.cc',
+ 'rtp_file_writer.h',
+ ],
+ 'dependencies': [
+ '<(DEPTH)/webrtc/common.gyp:webrtc_common',
+ '<(DEPTH)/testing/gtest.gyp:gtest',
+ '<(webrtc_root)/modules/modules.gyp:rtp_rtcp',
+ ],
+ },
+ {
+ 'target_name': 'field_trial',
+ 'type': 'static_library',
+ 'sources': [
+ 'field_trial.cc',
+ 'field_trial.h',
+ ],
+ 'dependencies': [
+ '<(webrtc_root)/common.gyp:webrtc_common',
+ '<(webrtc_root)/system_wrappers/system_wrappers.gyp:system_wrappers',
+ ],
+ },
+ {
+ 'target_name': 'histogram',
+ 'type': 'static_library',
+ 'sources': [
+ 'histogram.cc',
+ 'histogram.h',
+ ],
+ 'dependencies': [
+ '<(webrtc_root)/common.gyp:webrtc_common',
+ '<(webrtc_root)/system_wrappers/system_wrappers.gyp:system_wrappers',
+ ],
+ },
+ {
+ 'target_name': 'test_main',
+ 'type': 'static_library',
+ 'sources': [
+ 'test_main.cc',
+ ],
+ 'dependencies': [
+ 'field_trial',
+ 'histogram',
+ 'test_support',
+ '<(DEPTH)/testing/gtest.gyp:gtest',
+ '<(DEPTH)/third_party/gflags/gflags.gyp:gflags',
+ ],
+ },
+ {
+ 'target_name': 'test_support',
+ 'type': 'static_library',
+ 'dependencies': [
+ '<(DEPTH)/testing/gtest.gyp:gtest',
+ '<(DEPTH)/testing/gmock.gyp:gmock',
+ '<(webrtc_root)/common.gyp:gtest_prod',
+ '<(webrtc_root)/system_wrappers/system_wrappers.gyp:system_wrappers',
+ ],
+ 'sources': [
+ 'testsupport/fileutils.cc',
+ 'testsupport/fileutils.h',
+ 'testsupport/frame_reader.cc',
+ 'testsupport/frame_reader.h',
+ 'testsupport/frame_writer.cc',
+ 'testsupport/frame_writer.h',
+ 'testsupport/gtest_disable.h',
+ 'testsupport/iosfileutils.mm',
+ 'testsupport/mock/mock_frame_reader.h',
+ 'testsupport/mock/mock_frame_writer.h',
+ 'testsupport/packet_reader.cc',
+ 'testsupport/packet_reader.h',
+ 'testsupport/perf_test.cc',
+ 'testsupport/perf_test.h',
+ 'testsupport/trace_to_stderr.cc',
+ 'testsupport/trace_to_stderr.h',
+ ],
+ 'conditions': [
+ ['OS=="ios"', {
+ 'xcode_settings': {
+ 'CLANG_ENABLE_OBJC_ARC': 'YES',
+ },
+ }],
+ ['use_x11==1', {
+ 'dependencies': [
+ '<(DEPTH)/tools/xdisplaycheck/xdisplaycheck.gyp:xdisplaycheck',
+ ],
+ }],
+ ],
+ },
+ {
+ # Depend on this target when you want to have test_support but also the
+ # main method needed for gtest to execute!
+ 'target_name': 'test_support_main',
+ 'type': 'static_library',
+ 'dependencies': [
+ 'field_trial',
+ 'histogram',
+ 'test_support',
+ '<(DEPTH)/testing/gmock.gyp:gmock',
+ '<(DEPTH)/testing/gtest.gyp:gtest',
+ '<(DEPTH)/third_party/gflags/gflags.gyp:gflags',
+ ],
+ 'sources': [
+ 'run_all_unittests.cc',
+ 'test_suite.cc',
+ 'test_suite.h',
+ ],
+ },
+ {
+ # Depend on this target when you want to have test_support and a special
+ # main for mac which will run your test on a worker thread and consume
+ # events on the main thread. Useful if you want to access a webcam.
+ # This main will provide all the scaffolding and objective-c black magic
+ # for you. All you need to do is to implement a function in the
+ # run_threaded_main_mac.h file (ImplementThisToRunYourTest).
+ 'target_name': 'test_support_main_threaded_mac',
+ 'type': 'static_library',
+ 'dependencies': [
+ 'test_support',
+ ],
+ 'sources': [
+ 'testsupport/mac/run_threaded_main_mac.h',
+ 'testsupport/mac/run_threaded_main_mac.mm',
+ ],
+ },
+ {
+ 'target_name': 'test_support_unittests',
+ 'type': '<(gtest_target_type)',
+ 'dependencies': [
+ 'channel_transport',
+ 'test_support_main',
+ '<(DEPTH)/testing/gmock.gyp:gmock',
+ '<(DEPTH)/testing/gtest.gyp:gtest',
+ ],
+ 'sources': [
+ 'channel_transport/udp_transport_unittest.cc',
+ 'channel_transport/udp_socket_manager_unittest.cc',
+ 'channel_transport/udp_socket_wrapper_unittest.cc',
+ 'testsupport/always_passing_unittest.cc',
+ 'testsupport/unittest_utils.h',
+ 'testsupport/fileutils_unittest.cc',
+ 'testsupport/frame_reader_unittest.cc',
+ 'testsupport/frame_writer_unittest.cc',
+ 'testsupport/packet_reader_unittest.cc',
+ 'testsupport/perf_test_unittest.cc',
+ ],
+ # Disable warnings to enable Win64 build, issue 1323.
+ 'msvs_disabled_warnings': [
+ 4267, # size_t to int truncation.
+ ],
+ 'conditions': [
+ ['OS=="android"', {
+ 'dependencies': [
+ '<(DEPTH)/testing/android/native_test.gyp:native_test_native_code',
+ ],
+ }],
+ ],
+ },
+ ],
+ 'conditions': [
+ ['include_tests==1 and OS=="android"', {
+ 'targets': [
+ {
+ 'target_name': 'test_support_unittests_apk_target',
+ 'type': 'none',
+ 'dependencies': [
+ '<(apk_tests_path):test_support_unittests_apk',
+ ],
+ },
+ ],
+ }],
+ ['test_isolation_mode != "noop"', {
+ 'targets': [
+ {
+ 'target_name': 'test_support_unittests_run',
+ 'type': 'none',
+ 'dependencies': [
+ 'test_support_unittests',
+ ],
+ 'includes': [
+ '../build/isolate.gypi',
+ ],
+ 'sources': [
+ 'test_support_unittests.isolate',
+ ],
+ },
+ ],
+ }],
+ ],
+}
diff --git a/webrtc/test/test_main.cc b/webrtc/test/test_main.cc
new file mode 100644
index 0000000000..733831f5be
--- /dev/null
+++ b/webrtc/test/test_main.cc
@@ -0,0 +1,32 @@
+/*
+ * 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 "gflags/gflags.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "webrtc/test/field_trial.h"
+#include "webrtc/test/testsupport/fileutils.h"
+
+DEFINE_string(force_fieldtrials, "",
+ "Field trials control experimental feature code which can be forced. "
+ "E.g. running with --force_fieldtrials=WebRTC-FooFeature/Enable/"
+ " will assign the group Enable to field trial WebRTC-FooFeature.");
+
+int main(int argc, char* argv[]) {
+ ::testing::InitGoogleTest(&argc, argv);
+
+ // AllowCommandLineParsing allows us to ignore flags passed on to us by
+ // Chromium build bots without having to explicitly disable them.
+ google::AllowCommandLineReparsing();
+ google::ParseCommandLineFlags(&argc, &argv, false);
+
+ webrtc::test::SetExecutablePath(argv[0]);
+ webrtc::test::InitFieldTrialsFromString(FLAGS_force_fieldtrials);
+ return RUN_ALL_TESTS();
+}
diff --git a/webrtc/test/test_suite.cc b/webrtc/test/test_suite.cc
new file mode 100644
index 0000000000..2900f0eddb
--- /dev/null
+++ b/webrtc/test/test_suite.cc
@@ -0,0 +1,62 @@
+/*
+ * Copyright (c) 2011 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/test_suite.h"
+
+#include "gflags/gflags.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "webrtc/base/logging.h"
+#include "webrtc/test/testsupport/fileutils.h"
+#include "webrtc/test/testsupport/trace_to_stderr.h"
+#include "webrtc/test/field_trial.h"
+
+DEFINE_bool(logs, false, "print logs to stderr");
+
+DEFINE_string(force_fieldtrials, "",
+ "Field trials control experimental feature code which can be forced. "
+ "E.g. running with --force_fieldtrials=WebRTC-FooFeature/Enable/"
+ " will assign the group Enable to field trial WebRTC-FooFeature.");
+
+namespace webrtc {
+namespace test {
+
+TestSuite::TestSuite(int argc, char** argv) {
+ SetExecutablePath(argv[0]);
+ testing::InitGoogleMock(&argc, argv); // Runs InitGoogleTest() internally.
+ // AllowCommandLineParsing allows us to ignore flags passed on to us by
+ // Chromium build bots without having to explicitly disable them.
+ google::AllowCommandLineReparsing();
+ google::ParseCommandLineFlags(&argc, &argv, true);
+
+ webrtc::test::InitFieldTrialsFromString(FLAGS_force_fieldtrials);
+}
+
+TestSuite::~TestSuite() {
+}
+
+int TestSuite::Run() {
+ Initialize();
+ int result = RUN_ALL_TESTS();
+ Shutdown();
+ return result;
+}
+
+void TestSuite::Initialize() {
+ rtc::LogMessage::SetLogToStderr(FLAGS_logs);
+ if (FLAGS_logs)
+ trace_to_stderr_.reset(new TraceToStderr);
+}
+
+void TestSuite::Shutdown() {
+}
+
+} // namespace test
+} // namespace webrtc
diff --git a/webrtc/test/test_suite.h b/webrtc/test/test_suite.h
new file mode 100644
index 0000000000..dab2acd388
--- /dev/null
+++ b/webrtc/test/test_suite.h
@@ -0,0 +1,50 @@
+/*
+ * Copyright (c) 2011 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_TEST_SUITE_H_
+#define WEBRTC_TEST_TEST_SUITE_H_
+
+// Derived from Chromium's src/base/test/test_suite.h.
+
+// Defines a basic test suite framework for running gtest based tests. You can
+// instantiate this class in your main function and call its Run method to run
+// any gtest based tests that are linked into your executable.
+
+#include "webrtc/base/constructormagic.h"
+#include "webrtc/base/scoped_ptr.h"
+
+namespace webrtc {
+namespace test {
+
+class TraceToStderr;
+
+class TestSuite {
+ public:
+ TestSuite(int argc, char** argv);
+ virtual ~TestSuite();
+
+ int Run();
+
+ protected:
+ // Override these for custom initialization and shutdown handling. Use these
+ // instead of putting complex code in your constructor/destructor.
+ virtual void Initialize();
+ virtual void Shutdown();
+
+ RTC_DISALLOW_COPY_AND_ASSIGN(TestSuite);
+
+ private:
+ rtc::scoped_ptr<TraceToStderr> trace_to_stderr_;
+};
+
+} // namespace test
+} // namespace webrtc
+
+#endif // TEST_TEST_SUITE_H_
diff --git a/webrtc/test/test_support_unittests.isolate b/webrtc/test/test_support_unittests.isolate
new file mode 100644
index 0000000000..3fd89d602e
--- /dev/null
+++ b/webrtc/test/test_support_unittests.isolate
@@ -0,0 +1,24 @@
+# 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.
+{
+ 'conditions': [
+ ['OS=="linux" or OS=="mac" or OS=="win"', {
+ 'variables': {
+ 'command': [
+ '<(DEPTH)/testing/test_env.py',
+ '<(PRODUCT_DIR)/test_support_unittests<(EXECUTABLE_SUFFIX)',
+ ],
+ 'files': [
+ '<(DEPTH)/DEPS',
+ '<(DEPTH)/testing/test_env.py',
+ '<(PRODUCT_DIR)/test_support_unittests<(EXECUTABLE_SUFFIX)',
+ ],
+ },
+ }],
+ ],
+}
diff --git a/webrtc/test/testsupport/always_passing_unittest.cc b/webrtc/test/testsupport/always_passing_unittest.cc
new file mode 100644
index 0000000000..afb80e662c
--- /dev/null
+++ b/webrtc/test/testsupport/always_passing_unittest.cc
@@ -0,0 +1,19 @@
+/*
+ * Copyright (c) 2014 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace webrtc {
+
+// A test that always passes. Useful when all tests in a executable are
+// disabled, since a gtest returns exit code 1 if no tests have executed.
+TEST(AlwaysPassingTest, AlwaysPassingTest) {}
+
+} // namespace webrtc
diff --git a/webrtc/test/testsupport/fileutils.cc b/webrtc/test/testsupport/fileutils.cc
new file mode 100644
index 0000000000..15abf5c517
--- /dev/null
+++ b/webrtc/test/testsupport/fileutils.cc
@@ -0,0 +1,268 @@
+/*
+ * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "webrtc/test/testsupport/fileutils.h"
+
+#include <assert.h>
+
+#ifdef WIN32
+#include <direct.h>
+#include <tchar.h>
+#include <windows.h>
+#include <algorithm>
+
+#include "webrtc/system_wrappers/include/utf_util_win.h"
+#define GET_CURRENT_DIR _getcwd
+#else
+#include <unistd.h>
+
+#include "webrtc/base/scoped_ptr.h"
+#define GET_CURRENT_DIR getcwd
+#endif
+
+#include <sys/stat.h> // To check for directory existence.
+#ifndef S_ISDIR // Not defined in stat.h on Windows.
+#define S_ISDIR(mode) (((mode) & S_IFMT) == S_IFDIR)
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "webrtc/typedefs.h" // For architecture defines
+
+namespace webrtc {
+namespace test {
+
+#if defined(WEBRTC_IOS)
+// Defined in iosfileutils.mm. No header file to discourage use elsewhere.
+std::string IOSResourcePath(std::string name, std::string extension);
+#endif
+
+namespace {
+
+#ifdef WIN32
+const char* kPathDelimiter = "\\";
+#else
+const char* kPathDelimiter = "/";
+#endif
+
+#ifdef WEBRTC_ANDROID
+const char* kRootDirName = "/sdcard/";
+#else
+// The file we're looking for to identify the project root dir.
+const char* kProjectRootFileName = "DEPS";
+const char* kOutputDirName = "out";
+const char* kFallbackPath = "./";
+#endif
+#if !defined(WEBRTC_IOS)
+const char* kResourcesDirName = "resources";
+#endif
+
+char relative_dir_path[FILENAME_MAX];
+bool relative_dir_path_set = false;
+
+} // namespace
+
+const char* kCannotFindProjectRootDir = "ERROR_CANNOT_FIND_PROJECT_ROOT_DIR";
+
+void SetExecutablePath(const std::string& path) {
+ std::string working_dir = WorkingDir();
+ std::string temp_path = path;
+
+ // Handle absolute paths; convert them to relative paths to the working dir.
+ if (path.find(working_dir) != std::string::npos) {
+ temp_path = path.substr(working_dir.length() + 1);
+ }
+ // On Windows, when tests are run under memory tools like DrMemory and TSan,
+ // slashes occur in the path as directory separators. Make sure we replace
+ // such cases with backslashes in order for the paths to be correct.
+#ifdef WIN32
+ std::replace(temp_path.begin(), temp_path.end(), '/', '\\');
+#endif
+
+ // Trim away the executable name; only store the relative dir path.
+ temp_path = temp_path.substr(0, temp_path.find_last_of(kPathDelimiter));
+ strncpy(relative_dir_path, temp_path.c_str(), FILENAME_MAX);
+ relative_dir_path_set = true;
+}
+
+bool FileExists(std::string& file_name) {
+ struct stat file_info = {0};
+ return stat(file_name.c_str(), &file_info) == 0;
+}
+
+#ifdef WEBRTC_ANDROID
+
+std::string ProjectRootPath() {
+ return kRootDirName;
+}
+
+std::string OutputPath() {
+ return kRootDirName;
+}
+
+std::string WorkingDir() {
+ return kRootDirName;
+}
+
+#else // WEBRTC_ANDROID
+
+std::string ProjectRootPath() {
+ std::string path = WorkingDir();
+ if (path == kFallbackPath) {
+ return kCannotFindProjectRootDir;
+ }
+ if (relative_dir_path_set) {
+ path = path + kPathDelimiter + relative_dir_path;
+ }
+ // Check for our file that verifies the root dir.
+ size_t path_delimiter_index = path.find_last_of(kPathDelimiter);
+ while (path_delimiter_index != std::string::npos) {
+ std::string root_filename = path + kPathDelimiter + kProjectRootFileName;
+ if (FileExists(root_filename)) {
+ return path + kPathDelimiter;
+ }
+ // Move up one directory in the directory tree.
+ path = path.substr(0, path_delimiter_index);
+ path_delimiter_index = path.find_last_of(kPathDelimiter);
+ }
+ // Reached the root directory.
+ fprintf(stderr, "Cannot find project root directory!\n");
+ return kCannotFindProjectRootDir;
+}
+
+std::string OutputPath() {
+ std::string path = ProjectRootPath();
+ if (path == kCannotFindProjectRootDir) {
+ return kFallbackPath;
+ }
+ path += kOutputDirName;
+ if (!CreateDir(path)) {
+ return kFallbackPath;
+ }
+ return path + kPathDelimiter;
+}
+
+std::string WorkingDir() {
+ char path_buffer[FILENAME_MAX];
+ if (!GET_CURRENT_DIR(path_buffer, sizeof(path_buffer))) {
+ fprintf(stderr, "Cannot get current directory!\n");
+ return kFallbackPath;
+ } else {
+ return std::string(path_buffer);
+ }
+}
+
+#endif // !WEBRTC_ANDROID
+
+// Generate a temporary filename in a safe way.
+// Largely copied from talk/base/{unixfilesystem,win32filesystem}.cc.
+std::string TempFilename(const std::string &dir, const std::string &prefix) {
+#ifdef WIN32
+ wchar_t filename[MAX_PATH];
+ if (::GetTempFileName(ToUtf16(dir).c_str(),
+ ToUtf16(prefix).c_str(), 0, filename) != 0)
+ return ToUtf8(filename);
+ assert(false);
+ return "";
+#else
+ int len = dir.size() + prefix.size() + 2 + 6;
+ rtc::scoped_ptr<char[]> tempname(new char[len]);
+
+ snprintf(tempname.get(), len, "%s/%sXXXXXX", dir.c_str(),
+ prefix.c_str());
+ int fd = ::mkstemp(tempname.get());
+ if (fd == -1) {
+ assert(false);
+ return "";
+ } else {
+ ::close(fd);
+ }
+ std::string ret(tempname.get());
+ return ret;
+#endif
+}
+
+bool CreateDir(std::string directory_name) {
+ struct stat path_info = {0};
+ // Check if the path exists already:
+ if (stat(directory_name.c_str(), &path_info) == 0) {
+ if (!S_ISDIR(path_info.st_mode)) {
+ fprintf(stderr, "Path %s exists but is not a directory! Remove this "
+ "file and re-run to create the directory.\n",
+ directory_name.c_str());
+ return false;
+ }
+ } else {
+#ifdef WIN32
+ return _mkdir(directory_name.c_str()) == 0;
+#else
+ return mkdir(directory_name.c_str(), S_IRWXU | S_IRWXG | S_IRWXO) == 0;
+#endif
+ }
+ return true;
+}
+
+std::string ResourcePath(std::string name, std::string extension) {
+#if defined(WEBRTC_IOS)
+ return IOSResourcePath(name, extension);
+#else
+ std::string platform = "win";
+#ifdef WEBRTC_LINUX
+ platform = "linux";
+#endif // WEBRTC_LINUX
+#ifdef WEBRTC_MAC
+ platform = "mac";
+#endif // WEBRTC_MAC
+
+#ifdef WEBRTC_ARCH_64_BITS
+ std::string architecture = "64";
+#else
+ std::string architecture = "32";
+#endif // WEBRTC_ARCH_64_BITS
+
+ std::string resources_path = ProjectRootPath() + kResourcesDirName +
+ kPathDelimiter;
+ std::string resource_file = resources_path + name + "_" + platform + "_" +
+ architecture + "." + extension;
+ if (FileExists(resource_file)) {
+ return resource_file;
+ }
+ // Try without architecture.
+ resource_file = resources_path + name + "_" + platform + "." + extension;
+ if (FileExists(resource_file)) {
+ return resource_file;
+ }
+ // Try without platform.
+ resource_file = resources_path + name + "_" + architecture + "." + extension;
+ if (FileExists(resource_file)) {
+ return resource_file;
+ }
+
+ // Fall back on name without architecture or platform.
+ return resources_path + name + "." + extension;
+#endif // defined (WEBRTC_IOS)
+}
+
+size_t GetFileSize(std::string filename) {
+ FILE* f = fopen(filename.c_str(), "rb");
+ size_t size = 0;
+ if (f != NULL) {
+ if (fseek(f, 0, SEEK_END) == 0) {
+ size = ftell(f);
+ }
+ fclose(f);
+ }
+ return size;
+}
+
+} // namespace test
+} // namespace webrtc
diff --git a/webrtc/test/testsupport/fileutils.h b/webrtc/test/testsupport/fileutils.h
new file mode 100644
index 0000000000..78789fa870
--- /dev/null
+++ b/webrtc/test/testsupport/fileutils.h
@@ -0,0 +1,159 @@
+/*
+ * Copyright (c) 2011 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 <stdio.h>
+
+// File utilities for testing purposes.
+//
+// The ProjectRootPath() method is a convenient way of getting an absolute
+// path to the project source tree root directory. Using this, it is easy to
+// refer to test resource files in a portable way.
+//
+// Notice that even if Windows platforms use backslash as path delimiter, it is
+// also supported to use slash, so there's no need for #ifdef checks in test
+// code for setting up the paths to the resource files.
+//
+// Example use:
+// Assume we have the following code being used in a test source file:
+// const std::string kInputFile = webrtc::test::ProjectRootPath() +
+// "test/data/voice_engine/audio_long16.wav";
+// // Use the kInputFile for the tests...
+//
+// Then here's some example outputs for different platforms:
+// Linux:
+// * Source tree located in /home/user/webrtc/trunk
+// * Test project located in /home/user/webrtc/trunk/src/testproject
+// * Test binary compiled as:
+// /home/user/webrtc/trunk/out/Debug/testproject_unittests
+// Then ProjectRootPath() will return /home/user/webrtc/trunk/ no matter if
+// the test binary is executed from standing in either of:
+// /home/user/webrtc/trunk
+// or
+// /home/user/webrtc/trunk/out/Debug
+// (or any other directory below the trunk for that matter).
+//
+// Windows:
+// * Source tree located in C:\Users\user\webrtc\trunk
+// * Test project located in C:\Users\user\webrtc\trunk\src\testproject
+// * Test binary compiled as:
+// C:\Users\user\webrtc\trunk\src\testproject\Debug\testproject_unittests.exe
+// Then ProjectRootPath() will return C:\Users\user\webrtc\trunk\ when the
+// test binary is executed from inside Visual Studio.
+// It will also return the same path if the test is executed from a command
+// prompt standing in C:\Users\user\webrtc\trunk\src\testproject\Debug
+//
+// Mac:
+// * Source tree located in /Users/user/webrtc/trunk
+// * Test project located in /Users/user/webrtc/trunk/src/testproject
+// * Test binary compiled as:
+// /Users/user/webrtc/trunk/xcodebuild/Debug/testproject_unittests
+// Then ProjectRootPath() will return /Users/user/webrtc/trunk/ no matter if
+// the test binary is executed from standing in either of:
+// /Users/user/webrtc/trunk
+// or
+// /Users/user/webrtc/trunk/out/Debug
+// (or any other directory below the trunk for that matter).
+
+#ifndef WEBRTC_TEST_TESTSUPPORT_FILEUTILS_H_
+#define WEBRTC_TEST_TESTSUPPORT_FILEUTILS_H_
+
+#include <string>
+
+namespace webrtc {
+namespace test {
+
+// This is the "directory" returned if the ProjectPath() function fails
+// to find the project root.
+extern const char* kCannotFindProjectRootDir;
+
+// Finds the root dir of the project, to be able to set correct paths to
+// resource files used by tests.
+// The implementation is simple: it just looks for the file defined by
+// kProjectRootFileName, starting in the current directory (the working
+// directory) and then steps upward until it is found (or it is at the root of
+// the file system).
+// If the current working directory is above the project root dir, it will not
+// be found.
+//
+// If symbolic links occur in the path they will be resolved and the actual
+// directory will be returned.
+//
+// Returns the absolute path to the project root dir (usually the trunk dir)
+// WITH a trailing path delimiter.
+// If the project root is not found, the string specified by
+// kCannotFindProjectRootDir is returned.
+std::string ProjectRootPath();
+
+// Creates and returns the absolute path to the output directory where log files
+// and other test artifacts should be put. The output directory is generally a
+// directory named "out" at the top-level of the project, i.e. a subfolder to
+// the path returned by ProjectRootPath(). The exception is Android where we use
+// /sdcard/ instead.
+//
+// Details described for ProjectRootPath() apply here too.
+//
+// Returns the path WITH a trailing path delimiter. If the project root is not
+// found, the current working directory ("./") is returned as a fallback.
+std::string OutputPath();
+
+// Generates an empty file with a unique name in the specified directory and
+// returns the file name and path.
+std::string TempFilename(const std::string &dir, const std::string &prefix);
+
+// Returns a path to a resource file for the currently executing platform.
+// Adapts to what filenames are currently present in the
+// [project-root]/resources/ dir.
+// Returns an absolute path according to this priority list (the directory
+// part of the path is left out for readability):
+// 1. [name]_[platform]_[architecture].[extension]
+// 2. [name]_[platform].[extension]
+// 3. [name]_[architecture].[extension]
+// 4. [name].[extension]
+// Where
+// * platform is either of "win", "mac" or "linux".
+// * architecture is either of "32" or "64".
+//
+// Arguments:
+// name - Name of the resource file. If a plain filename (no directory path)
+// is supplied, the file is assumed to be located in resources/
+// If a directory path is prepended to the filename, a subdirectory
+// hierarchy reflecting that path is assumed to be present.
+// extension - File extension, without the dot, i.e. "bmp" or "yuv".
+std::string ResourcePath(std::string name, std::string extension);
+
+// Gets the current working directory for the executing program.
+// Returns "./" if for some reason it is not possible to find the working
+// directory.
+std::string WorkingDir();
+
+// Creates a directory if it not already exists.
+// Returns true if successful. Will print an error message to stderr and return
+// false if a file with the same name already exists.
+bool CreateDir(std::string directory_name);
+
+// Checks if a file exists.
+bool FileExists(std::string& file_name);
+
+// File size of the supplied file in bytes. Will return 0 if the file is
+// empty or if the file does not exist/is readable.
+size_t GetFileSize(std::string filename);
+
+// Sets the executable path, i.e. the path to the executable that is being used
+// when launching it. This is usually the path relative to the working directory
+// but can also be an absolute path. The intention with this function is to pass
+// the argv[0] being sent into the main function to make it possible for
+// fileutils.h to find the correct project paths even when the working directory
+// is outside the project tree (which happens in some cases).
+void SetExecutablePath(const std::string& path_to_executable);
+
+} // namespace test
+} // namespace webrtc
+
+#endif // WEBRTC_TEST_TESTSUPPORT_FILEUTILS_H_
diff --git a/webrtc/test/testsupport/fileutils_unittest.cc b/webrtc/test/testsupport/fileutils_unittest.cc
new file mode 100644
index 0000000000..dff7f2249b
--- /dev/null
+++ b/webrtc/test/testsupport/fileutils_unittest.cc
@@ -0,0 +1,142 @@
+/*
+ * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "webrtc/test/testsupport/fileutils.h"
+
+#include <stdio.h>
+
+#include <list>
+#include <string>
+
+#include "testing/gtest/include/gtest/gtest.h"
+#include "webrtc/test/testsupport/gtest_disable.h"
+
+#ifdef WIN32
+#define chdir _chdir
+static const char* kPathDelimiter = "\\";
+#else
+static const char* kPathDelimiter = "/";
+#endif
+
+static const std::string kResourcesDir = "resources";
+static const std::string kTestName = "fileutils_unittest";
+static const std::string kExtension = "tmp";
+
+namespace webrtc {
+
+// Test fixture to restore the working directory between each test, since some
+// of them change it with chdir during execution (not restored by the
+// gtest framework).
+class FileUtilsTest : public testing::Test {
+ protected:
+ FileUtilsTest() {
+ }
+ virtual ~FileUtilsTest() {}
+ // Runs before the first test
+ static void SetUpTestCase() {
+ original_working_dir_ = webrtc::test::WorkingDir();
+ }
+ void SetUp() {
+ ASSERT_EQ(chdir(original_working_dir_.c_str()), 0);
+ }
+ void TearDown() {
+ ASSERT_EQ(chdir(original_working_dir_.c_str()), 0);
+ }
+ private:
+ static std::string original_working_dir_;
+};
+
+std::string FileUtilsTest::original_working_dir_ = "";
+
+// Tests that the project root path is returned for the default working
+// directory that is automatically set when the test executable is launched.
+// The test is not fully testing the implementation, since we cannot be sure
+// of where the executable was launched from.
+TEST_F(FileUtilsTest, ProjectRootPath) {
+ std::string project_root = webrtc::test::ProjectRootPath();
+ // Not very smart, but at least tests something.
+ ASSERT_GT(project_root.length(), 0u);
+}
+
+// Similar to the above test, but for the output dir
+TEST_F(FileUtilsTest, DISABLED_ON_ANDROID(OutputPathFromUnchangedWorkingDir)) {
+ std::string path = webrtc::test::OutputPath();
+ std::string expected_end = "out";
+ expected_end = kPathDelimiter + expected_end + kPathDelimiter;
+ ASSERT_EQ(path.length() - expected_end.length(), path.find(expected_end));
+}
+
+// Tests with current working directory set to a directory higher up in the
+// directory tree than the project root dir.
+TEST_F(FileUtilsTest, DISABLED_ON_ANDROID(OutputPathFromRootWorkingDir)) {
+ ASSERT_EQ(0, chdir(kPathDelimiter));
+ ASSERT_EQ("./", webrtc::test::OutputPath());
+}
+
+TEST_F(FileUtilsTest, TempFilename) {
+ std::string temp_filename = webrtc::test::TempFilename(
+ webrtc::test::OutputPath(), "TempFilenameTest");
+ ASSERT_TRUE(webrtc::test::FileExists(temp_filename))
+ << "Couldn't find file: " << temp_filename;
+ remove(temp_filename.c_str());
+}
+
+// Only tests that the code executes
+TEST_F(FileUtilsTest, CreateDir) {
+ std::string directory = "fileutils-unittest-empty-dir";
+ // Make sure it's removed if a previous test has failed:
+ remove(directory.c_str());
+ ASSERT_TRUE(webrtc::test::CreateDir(directory));
+ remove(directory.c_str());
+}
+
+TEST_F(FileUtilsTest, WorkingDirReturnsValue) {
+ // Hard to cover all platforms. Just test that it returns something without
+ // crashing:
+ std::string working_dir = webrtc::test::WorkingDir();
+ ASSERT_GT(working_dir.length(), 0u);
+}
+
+// Due to multiple platforms, it is hard to make a complete test for
+// ResourcePath. Manual testing has been performed by removing files and
+// verified the result confirms with the specified documentation for the
+// function.
+TEST_F(FileUtilsTest, ResourcePathReturnsValue) {
+ std::string resource = webrtc::test::ResourcePath(kTestName, kExtension);
+ ASSERT_GT(resource.find(kTestName), 0u);
+ ASSERT_GT(resource.find(kExtension), 0u);
+}
+
+TEST_F(FileUtilsTest, ResourcePathFromRootWorkingDir) {
+ ASSERT_EQ(0, chdir(kPathDelimiter));
+ std::string resource = webrtc::test::ResourcePath(kTestName, kExtension);
+ ASSERT_NE(resource.find("resources"), std::string::npos);
+ ASSERT_GT(resource.find(kTestName), 0u);
+ ASSERT_GT(resource.find(kExtension), 0u);
+}
+
+TEST_F(FileUtilsTest, GetFileSizeExistingFile) {
+ // Create a file with some dummy data in.
+ std::string temp_filename = webrtc::test::TempFilename(
+ webrtc::test::OutputPath(), "fileutils_unittest");
+ FILE* file = fopen(temp_filename.c_str(), "wb");
+ ASSERT_TRUE(file != NULL) << "Failed to open file: " << temp_filename;
+ ASSERT_GT(fprintf(file, "%s", "Dummy data"), 0) <<
+ "Failed to write to file: " << temp_filename;
+ fclose(file);
+ ASSERT_GT(webrtc::test::GetFileSize(std::string(temp_filename.c_str())), 0u);
+ remove(temp_filename.c_str());
+}
+
+TEST_F(FileUtilsTest, GetFileSizeNonExistingFile) {
+ ASSERT_EQ(0u, webrtc::test::GetFileSize("non-existing-file.tmp"));
+}
+
+} // namespace webrtc
diff --git a/webrtc/test/testsupport/frame_reader.cc b/webrtc/test/testsupport/frame_reader.cc
new file mode 100644
index 0000000000..b03616aa44
--- /dev/null
+++ b/webrtc/test/testsupport/frame_reader.cc
@@ -0,0 +1,85 @@
+/*
+ * Copyright (c) 2011 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/testsupport/frame_reader.h"
+
+#include <assert.h>
+
+#include "webrtc/test/testsupport/fileutils.h"
+
+namespace webrtc {
+namespace test {
+
+FrameReaderImpl::FrameReaderImpl(std::string input_filename,
+ size_t frame_length_in_bytes)
+ : input_filename_(input_filename),
+ frame_length_in_bytes_(frame_length_in_bytes),
+ input_file_(NULL) {
+}
+
+FrameReaderImpl::~FrameReaderImpl() {
+ Close();
+}
+
+bool FrameReaderImpl::Init() {
+ if (frame_length_in_bytes_ <= 0) {
+ fprintf(stderr, "Frame length must be >0, was %zu\n",
+ frame_length_in_bytes_);
+ return false;
+ }
+ input_file_ = fopen(input_filename_.c_str(), "rb");
+ if (input_file_ == NULL) {
+ fprintf(stderr, "Couldn't open input file for reading: %s\n",
+ input_filename_.c_str());
+ return false;
+ }
+ // Calculate total number of frames.
+ size_t source_file_size = GetFileSize(input_filename_);
+ if (source_file_size <= 0u) {
+ fprintf(stderr, "Found empty file: %s\n", input_filename_.c_str());
+ return false;
+ }
+ number_of_frames_ = static_cast<int>(source_file_size /
+ frame_length_in_bytes_);
+ return true;
+}
+
+void FrameReaderImpl::Close() {
+ if (input_file_ != NULL) {
+ fclose(input_file_);
+ input_file_ = NULL;
+ }
+}
+
+bool FrameReaderImpl::ReadFrame(uint8_t* source_buffer) {
+ assert(source_buffer);
+ if (input_file_ == NULL) {
+ fprintf(stderr, "FrameReader is not initialized (input file is NULL)\n");
+ return false;
+ }
+ size_t nbr_read = fread(source_buffer, 1, frame_length_in_bytes_,
+ input_file_);
+ if (nbr_read != static_cast<unsigned int>(frame_length_in_bytes_) &&
+ ferror(input_file_)) {
+ fprintf(stderr, "Error reading from input file: %s\n",
+ input_filename_.c_str());
+ return false;
+ }
+ if (feof(input_file_) != 0) {
+ return false; // No more frames to process.
+ }
+ return true;
+}
+
+size_t FrameReaderImpl::FrameLength() { return frame_length_in_bytes_; }
+int FrameReaderImpl::NumberOfFrames() { return number_of_frames_; }
+
+} // namespace test
+} // namespace webrtc
diff --git a/webrtc/test/testsupport/frame_reader.h b/webrtc/test/testsupport/frame_reader.h
new file mode 100644
index 0000000000..9b50ec7bf4
--- /dev/null
+++ b/webrtc/test/testsupport/frame_reader.h
@@ -0,0 +1,74 @@
+/*
+ * Copyright (c) 2011 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_TESTSUPPORT_FRAME_READER_H_
+#define WEBRTC_TEST_TESTSUPPORT_FRAME_READER_H_
+
+#include <stdio.h>
+
+#include <string>
+
+#include "webrtc/typedefs.h"
+
+namespace webrtc {
+namespace test {
+
+// Handles reading of frames from video files.
+class FrameReader {
+ public:
+ virtual ~FrameReader() {}
+
+ // Initializes the frame reader, i.e. opens the input file.
+ // This must be called before reading of frames has started.
+ // Returns false if an error has occurred, in addition to printing to stderr.
+ virtual bool Init() = 0;
+
+ // Reads a frame into the supplied buffer, which must contain enough space
+ // for the frame size.
+ // Returns true if there are more frames to read, false if we've already
+ // read the last frame (in the previous call).
+ virtual bool ReadFrame(uint8_t* source_buffer) = 0;
+
+ // Closes the input file if open. Essentially makes this class impossible
+ // to use anymore. Will also be invoked by the destructor.
+ virtual void Close() = 0;
+
+ // Frame length in bytes of a single frame image.
+ virtual size_t FrameLength() = 0;
+ // Total number of frames in the input video source.
+ virtual int NumberOfFrames() = 0;
+};
+
+class FrameReaderImpl : public FrameReader {
+ public:
+ // Creates a file handler. The input file is assumed to exist and be readable.
+ // Parameters:
+ // input_filename The file to read from.
+ // frame_length_in_bytes The size of each frame.
+ // For YUV this is 3 * width * height / 2
+ FrameReaderImpl(std::string input_filename, size_t frame_length_in_bytes);
+ ~FrameReaderImpl() override;
+ bool Init() override;
+ bool ReadFrame(uint8_t* source_buffer) override;
+ void Close() override;
+ size_t FrameLength() override;
+ int NumberOfFrames() override;
+
+ private:
+ std::string input_filename_;
+ size_t frame_length_in_bytes_;
+ int number_of_frames_;
+ FILE* input_file_;
+};
+
+} // namespace test
+} // namespace webrtc
+
+#endif // WEBRTC_TEST_TESTSUPPORT_FRAME_READER_H_
diff --git a/webrtc/test/testsupport/frame_reader_unittest.cc b/webrtc/test/testsupport/frame_reader_unittest.cc
new file mode 100644
index 0000000000..25ea9132f2
--- /dev/null
+++ b/webrtc/test/testsupport/frame_reader_unittest.cc
@@ -0,0 +1,71 @@
+/*
+ * Copyright (c) 2011 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/testsupport/frame_reader.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+#include "webrtc/test/testsupport/fileutils.h"
+
+namespace webrtc {
+namespace test {
+
+const std::string kInputFileContents = "baz";
+// Setting the kFrameLength value to a value much larger than the
+// file to test causes the ReadFrame test to fail on Windows.
+const size_t kFrameLength = 1000;
+
+class FrameReaderTest: public testing::Test {
+ protected:
+ FrameReaderTest() {}
+ virtual ~FrameReaderTest() {}
+ void SetUp() {
+ // Create a dummy input file.
+ temp_filename_ = webrtc::test::TempFilename(webrtc::test::OutputPath(),
+ "frame_reader_unittest");
+ FILE* dummy = fopen(temp_filename_.c_str(), "wb");
+ fprintf(dummy, "%s", kInputFileContents.c_str());
+ fclose(dummy);
+
+ frame_reader_ = new FrameReaderImpl(temp_filename_, kFrameLength);
+ ASSERT_TRUE(frame_reader_->Init());
+ }
+ void TearDown() {
+ delete frame_reader_;
+ // Cleanup the dummy input file.
+ remove(temp_filename_.c_str());
+ }
+ FrameReader* frame_reader_;
+ std::string temp_filename_;
+};
+
+TEST_F(FrameReaderTest, InitSuccess) {
+ FrameReaderImpl frame_reader(temp_filename_, kFrameLength);
+ ASSERT_TRUE(frame_reader.Init());
+ ASSERT_EQ(kFrameLength, frame_reader.FrameLength());
+ ASSERT_EQ(0, frame_reader.NumberOfFrames());
+}
+
+TEST_F(FrameReaderTest, ReadFrame) {
+ uint8_t buffer[3];
+ bool result = frame_reader_->ReadFrame(buffer);
+ ASSERT_FALSE(result); // No more files to read.
+ ASSERT_EQ(kInputFileContents[0], buffer[0]);
+ ASSERT_EQ(kInputFileContents[1], buffer[1]);
+ ASSERT_EQ(kInputFileContents[2], buffer[2]);
+}
+
+TEST_F(FrameReaderTest, ReadFrameUninitialized) {
+ uint8_t buffer[3];
+ FrameReaderImpl file_reader(temp_filename_, kFrameLength);
+ ASSERT_FALSE(file_reader.ReadFrame(buffer));
+}
+
+} // namespace test
+} // namespace webrtc
diff --git a/webrtc/test/testsupport/frame_writer.cc b/webrtc/test/testsupport/frame_writer.cc
new file mode 100644
index 0000000000..1b9e8a82ef
--- /dev/null
+++ b/webrtc/test/testsupport/frame_writer.cc
@@ -0,0 +1,70 @@
+/*
+ * Copyright (c) 2011 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/testsupport/frame_writer.h"
+
+#include <assert.h>
+
+namespace webrtc {
+namespace test {
+
+FrameWriterImpl::FrameWriterImpl(std::string output_filename,
+ size_t frame_length_in_bytes)
+ : output_filename_(output_filename),
+ frame_length_in_bytes_(frame_length_in_bytes),
+ output_file_(NULL) {
+}
+
+FrameWriterImpl::~FrameWriterImpl() {
+ Close();
+}
+
+bool FrameWriterImpl::Init() {
+ if (frame_length_in_bytes_ <= 0) {
+ fprintf(stderr, "Frame length must be >0, was %zu\n",
+ frame_length_in_bytes_);
+ return false;
+ }
+ output_file_ = fopen(output_filename_.c_str(), "wb");
+ if (output_file_ == NULL) {
+ fprintf(stderr, "Couldn't open output file for writing: %s\n",
+ output_filename_.c_str());
+ return false;
+ }
+ return true;
+}
+
+void FrameWriterImpl::Close() {
+ if (output_file_ != NULL) {
+ fclose(output_file_);
+ output_file_ = NULL;
+ }
+}
+
+size_t FrameWriterImpl::FrameLength() { return frame_length_in_bytes_; }
+
+bool FrameWriterImpl::WriteFrame(uint8_t* frame_buffer) {
+ assert(frame_buffer);
+ if (output_file_ == NULL) {
+ fprintf(stderr, "FrameWriter is not initialized (output file is NULL)\n");
+ return false;
+ }
+ size_t bytes_written = fwrite(frame_buffer, 1, frame_length_in_bytes_,
+ output_file_);
+ if (bytes_written != frame_length_in_bytes_) {
+ fprintf(stderr, "Failed to write %zu bytes to file %s\n",
+ frame_length_in_bytes_, output_filename_.c_str());
+ return false;
+ }
+ return true;
+}
+
+} // namespace test
+} // namespace webrtc
diff --git a/webrtc/test/testsupport/frame_writer.h b/webrtc/test/testsupport/frame_writer.h
new file mode 100644
index 0000000000..8a6b1c2152
--- /dev/null
+++ b/webrtc/test/testsupport/frame_writer.h
@@ -0,0 +1,70 @@
+/*
+ * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#ifndef WEBRTC_TEST_TESTSUPPORT_FRAME_WRITER_H_
+#define WEBRTC_TEST_TESTSUPPORT_FRAME_WRITER_H_
+
+#include <stdio.h>
+
+#include <string>
+
+#include "webrtc/typedefs.h"
+
+namespace webrtc {
+namespace test {
+
+// Handles writing of video files.
+class FrameWriter {
+ public:
+ virtual ~FrameWriter() {}
+
+ // Initializes the file handler, i.e. opens the input and output files etc.
+ // This must be called before reading or writing frames has started.
+ // Returns false if an error has occurred, in addition to printing to stderr.
+ virtual bool Init() = 0;
+
+ // Writes a frame of the configured frame length to the output file.
+ // Returns true if the write was successful, false otherwise.
+ virtual bool WriteFrame(uint8_t* frame_buffer) = 0;
+
+ // Closes the output file if open. Essentially makes this class impossible
+ // to use anymore. Will also be invoked by the destructor.
+ virtual void Close() = 0;
+
+ // Frame length in bytes of a single frame image.
+ virtual size_t FrameLength() = 0;
+};
+
+class FrameWriterImpl : public FrameWriter {
+ public:
+ // Creates a file handler. The input file is assumed to exist and be readable
+ // and the output file must be writable.
+ // Parameters:
+ // output_filename The file to write. Will be overwritten if already
+ // existing.
+ // frame_length_in_bytes The size of each frame.
+ // For YUV: 3*width*height/2
+ FrameWriterImpl(std::string output_filename, size_t frame_length_in_bytes);
+ ~FrameWriterImpl() override;
+ bool Init() override;
+ bool WriteFrame(uint8_t* frame_buffer) override;
+ void Close() override;
+ size_t FrameLength() override;
+
+ private:
+ std::string output_filename_;
+ size_t frame_length_in_bytes_;
+ FILE* output_file_;
+};
+
+} // namespace test
+} // namespace webrtc
+
+#endif // WEBRTC_TEST_TESTSUPPORT_FRAME_WRITER_H_
diff --git a/webrtc/test/testsupport/frame_writer_unittest.cc b/webrtc/test/testsupport/frame_writer_unittest.cc
new file mode 100644
index 0000000000..da1e268662
--- /dev/null
+++ b/webrtc/test/testsupport/frame_writer_unittest.cc
@@ -0,0 +1,63 @@
+/*
+ * Copyright (c) 2011 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/testsupport/frame_writer.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+#include "webrtc/test/testsupport/fileutils.h"
+
+namespace webrtc {
+namespace test {
+
+const size_t kFrameLength = 1000;
+
+class FrameWriterTest: public testing::Test {
+ protected:
+ FrameWriterTest() {}
+ virtual ~FrameWriterTest() {}
+ void SetUp() {
+ temp_filename_ = webrtc::test::TempFilename(webrtc::test::OutputPath(),
+ "frame_writer_unittest");
+ frame_writer_ = new FrameWriterImpl(temp_filename_, kFrameLength);
+ ASSERT_TRUE(frame_writer_->Init());
+ }
+ void TearDown() {
+ delete frame_writer_;
+ // Cleanup the temporary file.
+ remove(temp_filename_.c_str());
+ }
+ FrameWriter* frame_writer_;
+ std::string temp_filename_;
+};
+
+TEST_F(FrameWriterTest, InitSuccess) {
+ FrameWriterImpl frame_writer(temp_filename_, kFrameLength);
+ ASSERT_TRUE(frame_writer.Init());
+ ASSERT_EQ(kFrameLength, frame_writer.FrameLength());
+}
+
+TEST_F(FrameWriterTest, WriteFrame) {
+ uint8_t buffer[kFrameLength];
+ memset(buffer, 9, kFrameLength); // Write lots of 9s to the buffer
+ bool result = frame_writer_->WriteFrame(buffer);
+ ASSERT_TRUE(result); // success
+ // Close the file and verify the size.
+ frame_writer_->Close();
+ ASSERT_EQ(kFrameLength, GetFileSize(temp_filename_));
+}
+
+TEST_F(FrameWriterTest, WriteFrameUninitialized) {
+ uint8_t buffer[3];
+ FrameWriterImpl frame_writer(temp_filename_, kFrameLength);
+ ASSERT_FALSE(frame_writer.WriteFrame(buffer));
+}
+
+} // namespace test
+} // namespace webrtc
diff --git a/webrtc/test/testsupport/gtest_disable.h b/webrtc/test/testsupport/gtest_disable.h
new file mode 100644
index 0000000000..fdc56acc05
--- /dev/null
+++ b/webrtc/test/testsupport/gtest_disable.h
@@ -0,0 +1,57 @@
+/*
+ * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+#ifndef TEST_TESTSUPPORT_INCLUDE_GTEST_DISABLE_H_
+#define TEST_TESTSUPPORT_INCLUDE_GTEST_DISABLE_H_
+
+// Helper macros for platform disables. These can be chained. Example use:
+// TEST_F(ViEStandardIntegrationTest,
+// DISABLED_ON_LINUX(RunsBaseTestWithoutErrors)) { // ...
+//
+// Or, you can disable a whole test class by wrapping all mentions of the test
+// class name inside one of these macros.
+//
+// The platform #defines we are looking at here are set by the build system.
+#ifdef WEBRTC_LINUX
+#define DISABLED_ON_LINUX(test) DISABLED_##test
+#else
+#define DISABLED_ON_LINUX(test) test
+#endif
+
+#ifdef WEBRTC_MAC
+#define DISABLED_ON_MAC(test) DISABLED_##test
+#else
+#define DISABLED_ON_MAC(test) test
+#endif
+
+#ifdef _WIN32
+#define DISABLED_ON_WIN(test) DISABLED_##test
+#else
+#define DISABLED_ON_WIN(test) test
+#endif
+
+// Using some extra magic here to be able to chain Android and iOS macros.
+// http://stackoverflow.com/questions/8231966/why-do-i-need-double-layer-of-indirection-for-macros
+#ifdef WEBRTC_ANDROID
+#define DISABLED_ON_ANDROID_HIDDEN(test) DISABLED_##test
+#define DISABLED_ON_ANDROID(test) DISABLED_ON_ANDROID_HIDDEN(test)
+#else
+#define DISABLED_ON_ANDROID_HIDDEN(test) test
+#define DISABLED_ON_ANDROID(test) DISABLED_ON_ANDROID_HIDDEN(test)
+#endif
+
+#ifdef WEBRTC_IOS
+#define DISABLED_ON_IOS_HIDDEN(test) DISABLED_##test
+#define DISABLED_ON_IOS(test) DISABLED_ON_IOS_HIDDEN(test)
+#else
+#define DISABLED_ON_IOS_HIDDEN(test) test
+#define DISABLED_ON_IOS(test) DISABLED_ON_IOS_HIDDEN(test)
+#endif
+
+#endif // TEST_TESTSUPPORT_INCLUDE_GTEST_DISABLE_H_
diff --git a/webrtc/test/testsupport/gtest_prod_util.h b/webrtc/test/testsupport/gtest_prod_util.h
new file mode 100644
index 0000000000..5370589899
--- /dev/null
+++ b/webrtc/test/testsupport/gtest_prod_util.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#ifndef WEBRTC_TEST_TESTSUPPORT_GTEST_PROD_UTIL_H_
+#define WEBRTC_TEST_TESTSUPPORT_GTEST_PROD_UTIL_H_
+
+// Define our own version of FRIEND_TEST here rather than including
+// gtest_prod.h to avoid depending on any part of GTest in production code.
+#define FRIEND_TEST_WEBRTC(test_case_name, test_name)\
+friend class test_case_name##_##test_name##_Test
+
+// This file is a plain copy of Chromium's base/gtest_prod_util.h.
+//
+// This is a wrapper for gtest's FRIEND_TEST macro that friends
+// test with all possible prefixes. This is very helpful when changing the test
+// prefix, because the friend declarations don't need to be updated.
+//
+// Example usage:
+//
+// class MyClass {
+// private:
+// void MyMethod();
+// FRIEND_TEST_ALL_PREFIXES(MyClassTest, MyMethod);
+// };
+#define FRIEND_TEST_ALL_PREFIXES(test_case_name, test_name) \
+ FRIEND_TEST_WEBRTC(test_case_name, test_name); \
+ FRIEND_TEST_WEBRTC(test_case_name, DISABLED_##test_name); \
+ FRIEND_TEST_WEBRTC(test_case_name, FLAKY_##test_name); \
+ FRIEND_TEST_WEBRTC(test_case_name, FAILS_##test_name)
+
+#endif // WEBRTC_TEST_TESTSUPPORT_GTEST_PROD_UTIL_H_
diff --git a/webrtc/test/testsupport/iosfileutils.mm b/webrtc/test/testsupport/iosfileutils.mm
new file mode 100644
index 0000000000..f3615ed681
--- /dev/null
+++ b/webrtc/test/testsupport/iosfileutils.mm
@@ -0,0 +1,60 @@
+/*
+ * Copyright 2015 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.
+ */
+
+#if defined(WEBRTC_IOS)
+
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
+#import <Foundation/Foundation.h>
+#include <string.h>
+
+#include "webrtc/base/checks.h"
+#include "webrtc/typedefs.h"
+
+namespace webrtc {
+namespace test {
+
+// TODO(henrika): move to shared location.
+// See https://code.google.com/p/webrtc/issues/detail?id=4773 for details.
+NSString* NSStringFromStdString(const std::string& stdString) {
+ // std::string may contain null termination character so we construct
+ // using length.
+ return [[NSString alloc] initWithBytes:stdString.data()
+ length:stdString.length()
+ encoding:NSUTF8StringEncoding];
+}
+
+std::string StdStringFromNSString(NSString* nsString) {
+ NSData* charData = [nsString dataUsingEncoding:NSUTF8StringEncoding];
+ return std::string(reinterpret_cast<const char*>([charData bytes]),
+ [charData length]);
+}
+
+// For iOS, resource files are added to the application bundle in the root
+// and not in separate folders as is the case for other platforms. This method
+// therefore removes any prepended folders and uses only the actual file name.
+std::string IOSResourcePath(std::string name, std::string extension) {
+ @autoreleasepool {
+ NSString* path = NSStringFromStdString(name);
+ NSString* fileName = path.lastPathComponent;
+ NSString* fileType = NSStringFromStdString(extension);
+ // Get full pathname for the resource identified by the name and extension.
+ NSString* pathString = [[NSBundle mainBundle] pathForResource:fileName
+ ofType:fileType];
+ return StdStringFromNSString(pathString);
+ }
+}
+
+} // namespace test
+} // namespace webrtc
+
+#endif // defined(WEBRTC_IOS)
diff --git a/webrtc/test/testsupport/mac/run_threaded_main_mac.h b/webrtc/test/testsupport/mac/run_threaded_main_mac.h
new file mode 100644
index 0000000000..c8cc4bbaeb
--- /dev/null
+++ b/webrtc/test/testsupport/mac/run_threaded_main_mac.h
@@ -0,0 +1,22 @@
+/*
+ * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+/**
+ * This file and its corresponding .mm file implement a main function on Mac.
+ * It's useful if you need to access a webcam in your Mac application. The code
+ * forks a worker thread which runs the below ImplementThisToRunYourTest
+ * function, and uses the main thread to pump messages. That way we can run our
+ * code in a regular sequential fashion and still pump events, which are
+ * necessary to access the webcam for instance.
+ */
+
+// Implement this method to do whatever you want to do in the worker thread.
+// The argc and argv variables are the unmodified command line from main.
+int ImplementThisToRunYourTest(int argc, char** argv);
diff --git a/webrtc/test/testsupport/mac/run_threaded_main_mac.mm b/webrtc/test/testsupport/mac/run_threaded_main_mac.mm
new file mode 100644
index 0000000000..4a908fbf2d
--- /dev/null
+++ b/webrtc/test/testsupport/mac/run_threaded_main_mac.mm
@@ -0,0 +1,90 @@
+/*
+ * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#import "webrtc/test/testsupport/mac/run_threaded_main_mac.h"
+
+#import <Cocoa/Cocoa.h>
+
+// This class passes parameter from main to the worked thread and back.
+@interface AutoTestInWorkerThread : NSObject {
+ int argc_;
+ char** argv_;
+ int result_;
+ bool done_;
+}
+
+- (void)setDone:(bool)done;
+- (bool)done;
+- (void)setArgc:(int)argc argv:(char**)argv;
+- (int) result;
+- (void)runTest:(NSObject*)ignored;
+
+@end
+
+@implementation AutoTestInWorkerThread
+
+- (void)setDone:(bool)done {
+ done_ = done;
+}
+
+- (bool)done {
+ return done_;
+}
+
+- (void)setArgc:(int)argc argv:(char**)argv {
+ argc_ = argc;
+ argv_ = argv;
+}
+
+- (void)runTest:(NSObject*)ignored {
+ NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
+
+ result_ = ImplementThisToRunYourTest(argc_, argv_);
+ done_ = true;
+
+ [pool release];
+ return;
+}
+
+- (int)result {
+ return result_;
+}
+
+@end
+
+int main(int argc, char * argv[]) {
+ NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
+
+ [NSApplication sharedApplication];
+
+ int result = 0;
+ AutoTestInWorkerThread* tests = [[AutoTestInWorkerThread alloc] init];
+
+ [tests setArgc:argc argv:argv];
+ [tests setDone:false];
+
+ [NSThread detachNewThreadSelector:@selector(runTest:)
+ toTarget:tests
+ withObject:nil];
+
+ NSRunLoop* main_run_loop = [NSRunLoop mainRunLoop];
+ NSDate *loop_until = [NSDate dateWithTimeIntervalSinceNow:0.1];
+ bool runloop_ok = true;
+ while (![tests done] && runloop_ok) {
+ runloop_ok = [main_run_loop runMode:NSDefaultRunLoopMode
+ beforeDate:loop_until];
+ loop_until = [NSDate dateWithTimeIntervalSinceNow:0.1];
+ }
+
+ result = [tests result];
+
+ [pool release];
+ return result;
+}
diff --git a/webrtc/test/testsupport/metrics/video_metrics.cc b/webrtc/test/testsupport/metrics/video_metrics.cc
new file mode 100644
index 0000000000..947b81d442
--- /dev/null
+++ b/webrtc/test/testsupport/metrics/video_metrics.cc
@@ -0,0 +1,198 @@
+/*
+ * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "webrtc/test/testsupport/metrics/video_metrics.h"
+
+#include <assert.h>
+#include <stdio.h>
+
+#include <algorithm> // min_element, max_element
+
+#include "webrtc/common_video/libyuv/include/webrtc_libyuv.h"
+#include "webrtc/video_frame.h"
+
+namespace webrtc {
+namespace test {
+
+// Copy here so our callers won't need to include libyuv for this constant.
+double kMetricsPerfectPSNR = kPerfectPSNR;
+
+// Used for calculating min and max values.
+static bool LessForFrameResultValue(const FrameResult& s1,
+ const FrameResult& s2) {
+ return s1.value < s2.value;
+}
+
+enum VideoMetricsType { kPSNR, kSSIM, kBoth };
+
+// Calculates metrics for a frame and adds statistics to the result for it.
+void CalculateFrame(VideoMetricsType video_metrics_type,
+ const VideoFrame* ref,
+ const VideoFrame* test,
+ int frame_number,
+ QualityMetricsResult* result) {
+ FrameResult frame_result = {0, 0};
+ frame_result.frame_number = frame_number;
+ switch (video_metrics_type) {
+ case kPSNR:
+ frame_result.value = I420PSNR(ref, test);
+ break;
+ case kSSIM:
+ frame_result.value = I420SSIM(ref, test);
+ break;
+ default:
+ assert(false);
+ }
+ result->frames.push_back(frame_result);
+}
+
+// Calculates average, min and max values for the supplied struct, if non-NULL.
+void CalculateStats(QualityMetricsResult* result) {
+ if (result == NULL || result->frames.size() == 0) {
+ return;
+ }
+ // Calculate average.
+ std::vector<FrameResult>::iterator iter;
+ double metrics_values_sum = 0.0;
+ for (iter = result->frames.begin(); iter != result->frames.end(); ++iter) {
+ metrics_values_sum += iter->value;
+ }
+ result->average = metrics_values_sum / result->frames.size();
+
+ // Calculate min/max statistics.
+ iter = std::min_element(result->frames.begin(), result->frames.end(),
+ LessForFrameResultValue);
+ result->min = iter->value;
+ result->min_frame_number = iter->frame_number;
+ iter = std::max_element(result->frames.begin(), result->frames.end(),
+ LessForFrameResultValue);
+ result->max = iter->value;
+ result->max_frame_number = iter->frame_number;
+}
+
+// Single method that handles all combinations of video metrics calculation, to
+// minimize code duplication. Either psnr_result or ssim_result may be NULL,
+// depending on which VideoMetricsType is targeted.
+int CalculateMetrics(VideoMetricsType video_metrics_type,
+ const char* ref_filename,
+ const char* test_filename,
+ int width,
+ int height,
+ QualityMetricsResult* psnr_result,
+ QualityMetricsResult* ssim_result) {
+ assert(ref_filename != NULL);
+ assert(test_filename != NULL);
+ assert(width > 0);
+ assert(height > 0);
+
+ FILE* ref_fp = fopen(ref_filename, "rb");
+ if (ref_fp == NULL) {
+ // Cannot open reference file.
+ fprintf(stderr, "Cannot open file %s\n", ref_filename);
+ return -1;
+ }
+ FILE* test_fp = fopen(test_filename, "rb");
+ if (test_fp == NULL) {
+ // Cannot open test file.
+ fprintf(stderr, "Cannot open file %s\n", test_filename);
+ fclose(ref_fp);
+ return -2;
+ }
+ int frame_number = 0;
+
+ // Read reference and test frames.
+ const size_t frame_length = 3 * width * height >> 1;
+ VideoFrame ref_frame;
+ VideoFrame test_frame;
+ rtc::scoped_ptr<uint8_t[]> ref_buffer(new uint8_t[frame_length]);
+ rtc::scoped_ptr<uint8_t[]> test_buffer(new uint8_t[frame_length]);
+
+ // Set decoded image parameters.
+ int half_width = (width + 1) / 2;
+ ref_frame.CreateEmptyFrame(width, height, width, half_width, half_width);
+ test_frame.CreateEmptyFrame(width, height, width, half_width, half_width);
+
+ size_t ref_bytes = fread(ref_buffer.get(), 1, frame_length, ref_fp);
+ size_t test_bytes = fread(test_buffer.get(), 1, frame_length, test_fp);
+ while (ref_bytes == frame_length && test_bytes == frame_length) {
+ // Converting from buffer to plane representation.
+ ConvertToI420(kI420, ref_buffer.get(), 0, 0, width, height, 0,
+ kVideoRotation_0, &ref_frame);
+ ConvertToI420(kI420, test_buffer.get(), 0, 0, width, height, 0,
+ kVideoRotation_0, &test_frame);
+ switch (video_metrics_type) {
+ case kPSNR:
+ CalculateFrame(kPSNR, &ref_frame, &test_frame, frame_number,
+ psnr_result);
+ break;
+ case kSSIM:
+ CalculateFrame(kSSIM, &ref_frame, &test_frame, frame_number,
+ ssim_result);
+ break;
+ case kBoth:
+ CalculateFrame(kPSNR, &ref_frame, &test_frame, frame_number,
+ psnr_result);
+ CalculateFrame(kSSIM, &ref_frame, &test_frame, frame_number,
+ ssim_result);
+ break;
+ }
+ frame_number++;
+ ref_bytes = fread(ref_buffer.get(), 1, frame_length, ref_fp);
+ test_bytes = fread(test_buffer.get(), 1, frame_length, test_fp);
+ }
+ int return_code = 0;
+ if (frame_number == 0) {
+ fprintf(stderr, "Tried to measure video metrics from empty files "
+ "(reference file: %s test file: %s)\n", ref_filename,
+ test_filename);
+ return_code = -3;
+ } else {
+ CalculateStats(psnr_result);
+ CalculateStats(ssim_result);
+ }
+ fclose(ref_fp);
+ fclose(test_fp);
+ return return_code;
+}
+
+int I420MetricsFromFiles(const char* ref_filename,
+ const char* test_filename,
+ int width,
+ int height,
+ QualityMetricsResult* psnr_result,
+ QualityMetricsResult* ssim_result) {
+ assert(psnr_result != NULL);
+ assert(ssim_result != NULL);
+ return CalculateMetrics(kBoth, ref_filename, test_filename, width, height,
+ psnr_result, ssim_result);
+}
+
+int I420PSNRFromFiles(const char* ref_filename,
+ const char* test_filename,
+ int width,
+ int height,
+ QualityMetricsResult* result) {
+ assert(result != NULL);
+ return CalculateMetrics(kPSNR, ref_filename, test_filename, width, height,
+ result, NULL);
+}
+
+int I420SSIMFromFiles(const char* ref_filename,
+ const char* test_filename,
+ int width,
+ int height,
+ QualityMetricsResult* result) {
+ assert(result != NULL);
+ return CalculateMetrics(kSSIM, ref_filename, test_filename, width, height,
+ NULL, result);
+}
+
+} // namespace test
+} // namespace webrtc
diff --git a/webrtc/test/testsupport/metrics/video_metrics.h b/webrtc/test/testsupport/metrics/video_metrics.h
new file mode 100644
index 0000000000..8448ccfd24
--- /dev/null
+++ b/webrtc/test/testsupport/metrics/video_metrics.h
@@ -0,0 +1,121 @@
+/*
+ * Copyright (c) 2011 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_TESTSUPPORT_METRICS_VIDEO_METRICS_H_
+#define WEBRTC_TESTSUPPORT_METRICS_VIDEO_METRICS_H_
+
+#include <limits>
+#include <vector>
+
+namespace webrtc {
+namespace test {
+
+// The highest PSNR value our algorithms will return.
+extern double kMetricsPerfectPSNR;
+
+// Contains video quality metrics result for a single frame.
+struct FrameResult {
+ int frame_number;
+ double value;
+};
+
+// Result from a PSNR/SSIM calculation operation.
+// The frames in this data structure are 0-indexed.
+struct QualityMetricsResult {
+ QualityMetricsResult() :
+ average(0.0),
+ min(std::numeric_limits<double>::max()),
+ max(std::numeric_limits<double>::min()),
+ min_frame_number(-1),
+ max_frame_number(-1)
+ {};
+ double average;
+ double min;
+ double max;
+ int min_frame_number;
+ int max_frame_number;
+ std::vector<FrameResult> frames;
+};
+
+// Calculates PSNR and SSIM values for the reference and test video files
+// (must be in I420 format). All calculated values are filled into the
+// QualityMetricsResult structs.
+//
+// PSNR values have the unit decibel (dB) where a high value means the test file
+// is similar to the reference file. The higher value, the more similar. The
+// maximum PSNR value is kMetricsInfinitePSNR. For more info about PSNR, see
+// http://en.wikipedia.org/wiki/PSNR.
+//
+// SSIM values range between -1.0 and 1.0, where 1.0 means the files are
+// identical. For more info about SSIM, see http://en.wikipedia.org/wiki/SSIM
+// This function only compares video frames up to the point when the shortest
+// video ends.
+// Return value:
+// 0 if successful, negative on errors:
+// -1 if the source file cannot be opened
+// -2 if the test file cannot be opened
+// -3 if any of the files are empty
+// -4 if any arguments are invalid.
+int I420MetricsFromFiles(const char* ref_filename,
+ const char* test_filename,
+ int width,
+ int height,
+ QualityMetricsResult* psnr_result,
+ QualityMetricsResult* ssim_result);
+
+// Calculates PSNR values for the reference and test video files (must be in
+// I420 format). All calculated values are filled into the QualityMetricsResult
+// struct.
+//
+// PSNR values have the unit decibel (dB) where a high value means the test file
+// is similar to the reference file. The higher value, the more similar. The
+// maximum PSNR value is kMetricsInfinitePSNR. For more info about PSNR, see
+// http://en.wikipedia.org/wiki/PSNR.
+//
+// This function only compares video frames up to the point when the shortest
+// video ends.
+//
+// Return value:
+// 0 if successful, negative on errors:
+// -1 if the source file cannot be opened
+// -2 if the test file cannot be opened
+// -3 if any of the files are empty
+// -4 if any arguments are invalid.
+int I420PSNRFromFiles(const char* ref_filename,
+ const char* test_filename,
+ int width,
+ int height,
+ QualityMetricsResult* result);
+
+// Calculates SSIM values for the reference and test video files (must be in
+// I420 format). All calculated values are filled into the QualityMetricsResult
+// struct.
+// SSIM values range between -1.0 and 1.0, where 1.0 means the files are
+// identical.
+// This function only compares video frames up to the point when the shortest
+// video ends.
+// For more info about SSIM, see http://en.wikipedia.org/wiki/SSIM
+//
+// Return value:
+// 0 if successful, negative on errors:
+// -1 if the source file cannot be opened
+// -2 if the test file cannot be opened
+// -3 if any of the files are empty
+// -4 if any arguments are invalid.
+int I420SSIMFromFiles(const char* ref_filename,
+ const char* test_filename,
+ int width,
+ int height,
+ QualityMetricsResult* result);
+
+} // namespace test
+} // namespace webrtc
+
+#endif // WEBRTC_TESTSUPPORT_METRICS_VIDEO_METRICS_H_
diff --git a/webrtc/test/testsupport/metrics/video_metrics_unittest.cc b/webrtc/test/testsupport/metrics/video_metrics_unittest.cc
new file mode 100644
index 0000000000..0958dcfbad
--- /dev/null
+++ b/webrtc/test/testsupport/metrics/video_metrics_unittest.cc
@@ -0,0 +1,147 @@
+/*
+ * Copyright (c) 2011 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/testsupport/metrics/video_metrics.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+#include "webrtc/test/testsupport/fileutils.h"
+
+namespace webrtc {
+
+static const int kWidth = 352;
+static const int kHeight = 288;
+
+static const int kMissingReferenceFileReturnCode = -1;
+static const int kMissingTestFileReturnCode = -2;
+static const int kEmptyFileReturnCode = -3;
+static const double kPsnrPerfectResult = 48.0;
+static const double kSsimPerfectResult = 1.0;
+
+class VideoMetricsTest: public testing::Test {
+ protected:
+ VideoMetricsTest() {
+ video_file_ = webrtc::test::ResourcePath("foreman_cif_short", "yuv");
+ }
+ virtual ~VideoMetricsTest() {}
+ void SetUp() {
+ non_existing_file_ = webrtc::test::OutputPath() +
+ "video_metrics_unittest_non_existing";
+ remove(non_existing_file_.c_str()); // To be sure it doesn't exist.
+
+ // Create an empty file:
+ empty_file_ = webrtc::test::TempFilename(
+ webrtc::test::OutputPath(), "video_metrics_unittest_empty_file");
+ FILE* dummy = fopen(empty_file_.c_str(), "wb");
+ fclose(dummy);
+ }
+ void TearDown() {
+ remove(empty_file_.c_str());
+ }
+ webrtc::test::QualityMetricsResult psnr_result_;
+ webrtc::test::QualityMetricsResult ssim_result_;
+ std::string non_existing_file_;
+ std::string empty_file_;
+ std::string video_file_;
+};
+
+// Tests that it is possible to run with the same reference as test file
+TEST_F(VideoMetricsTest, ReturnsPerfectResultForIdenticalFilesPSNR) {
+ EXPECT_EQ(0, I420PSNRFromFiles(video_file_.c_str(), video_file_.c_str(),
+ kWidth, kHeight, &psnr_result_));
+ EXPECT_EQ(kPsnrPerfectResult, psnr_result_.average);
+}
+
+TEST_F(VideoMetricsTest, ReturnsPerfectResultForIdenticalFilesSSIM) {
+ EXPECT_EQ(0, I420SSIMFromFiles(video_file_.c_str(), video_file_.c_str(),
+ kWidth, kHeight, &ssim_result_));
+ EXPECT_EQ(kSsimPerfectResult, ssim_result_.average);
+}
+
+TEST_F(VideoMetricsTest, ReturnsPerfectResultForIdenticalFilesBothMetrics) {
+ EXPECT_EQ(0, I420MetricsFromFiles(video_file_.c_str(), video_file_.c_str(),
+ kWidth, kHeight, &psnr_result_,
+ &ssim_result_));
+ EXPECT_EQ(kPsnrPerfectResult, psnr_result_.average);
+ EXPECT_EQ(kSsimPerfectResult, ssim_result_.average);
+}
+
+// Tests that the right return code is given when the reference file is missing.
+TEST_F(VideoMetricsTest, MissingReferenceFilePSNR) {
+ EXPECT_EQ(kMissingReferenceFileReturnCode,
+ I420PSNRFromFiles(non_existing_file_.c_str(),
+ video_file_.c_str(), kWidth, kHeight,
+ &ssim_result_));
+}
+
+TEST_F(VideoMetricsTest, MissingReferenceFileSSIM) {
+ EXPECT_EQ(kMissingReferenceFileReturnCode,
+ I420SSIMFromFiles(non_existing_file_.c_str(),
+ video_file_.c_str(), kWidth, kHeight,
+ &ssim_result_));
+}
+
+TEST_F(VideoMetricsTest, MissingReferenceFileBothMetrics) {
+ EXPECT_EQ(kMissingReferenceFileReturnCode,
+ I420MetricsFromFiles(non_existing_file_.c_str(),
+ video_file_.c_str(), kWidth, kHeight,
+ &psnr_result_, &ssim_result_));
+}
+
+// Tests that the right return code is given when the test file is missing.
+TEST_F(VideoMetricsTest, MissingTestFilePSNR) {
+ EXPECT_EQ(kMissingTestFileReturnCode,
+ I420PSNRFromFiles(video_file_.c_str(), non_existing_file_.c_str(),
+ kWidth, kHeight, &ssim_result_));
+}
+
+TEST_F(VideoMetricsTest, MissingTestFileSSIM) {
+ EXPECT_EQ(kMissingTestFileReturnCode,
+ I420SSIMFromFiles(video_file_.c_str(), non_existing_file_.c_str(),
+ kWidth, kHeight, &ssim_result_));
+}
+
+TEST_F(VideoMetricsTest, MissingTestFileBothMetrics) {
+ EXPECT_EQ(kMissingTestFileReturnCode,
+ I420MetricsFromFiles(video_file_.c_str(),
+ non_existing_file_.c_str(), kWidth, kHeight,
+ &psnr_result_, &ssim_result_));
+}
+
+// Tests that the method can be executed with empty files.
+TEST_F(VideoMetricsTest, EmptyFilesPSNR) {
+ EXPECT_EQ(kEmptyFileReturnCode,
+ I420PSNRFromFiles(empty_file_.c_str(), video_file_.c_str(),
+ kWidth, kHeight, &ssim_result_));
+ EXPECT_EQ(kEmptyFileReturnCode,
+ I420PSNRFromFiles(video_file_.c_str(), empty_file_.c_str(),
+ kWidth, kHeight, &ssim_result_));
+}
+
+TEST_F(VideoMetricsTest, EmptyFilesSSIM) {
+ EXPECT_EQ(kEmptyFileReturnCode,
+ I420SSIMFromFiles(empty_file_.c_str(), video_file_.c_str(),
+ kWidth, kHeight, &ssim_result_));
+ EXPECT_EQ(kEmptyFileReturnCode,
+ I420SSIMFromFiles(video_file_.c_str(), empty_file_.c_str(),
+ kWidth, kHeight, &ssim_result_));
+}
+
+TEST_F(VideoMetricsTest, EmptyFilesBothMetrics) {
+ EXPECT_EQ(kEmptyFileReturnCode,
+ I420MetricsFromFiles(empty_file_.c_str(), video_file_.c_str(),
+ kWidth, kHeight,
+ &psnr_result_, &ssim_result_));
+ EXPECT_EQ(kEmptyFileReturnCode,
+ I420MetricsFromFiles(video_file_.c_str(), empty_file_.c_str(),
+ kWidth, kHeight,
+ &psnr_result_, &ssim_result_));
+}
+
+} // namespace webrtc
diff --git a/webrtc/test/testsupport/mock/mock_frame_reader.h b/webrtc/test/testsupport/mock/mock_frame_reader.h
new file mode 100644
index 0000000000..f06750ef4a
--- /dev/null
+++ b/webrtc/test/testsupport/mock/mock_frame_reader.h
@@ -0,0 +1,33 @@
+/*
+ * Copyright (c) 2011 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_TESTSUPPORT_MOCK_MOCK_FRAME_READER_H_
+#define WEBRTC_TEST_TESTSUPPORT_MOCK_MOCK_FRAME_READER_H_
+
+#include "webrtc/test/testsupport/frame_reader.h"
+
+#include "testing/gmock/include/gmock/gmock.h"
+
+namespace webrtc {
+namespace test {
+
+class MockFrameReader : public FrameReader {
+ public:
+ MOCK_METHOD0(Init, bool());
+ MOCK_METHOD1(ReadFrame, bool(uint8_t* source_buffer));
+ MOCK_METHOD0(Close, void());
+ MOCK_METHOD0(FrameLength, size_t());
+ MOCK_METHOD0(NumberOfFrames, int());
+};
+
+} // namespace test
+} // namespace webrtc
+
+#endif // WEBRTC_TEST_TESTSUPPORT_MOCK_MOCK_FRAME_READER_H_
diff --git a/webrtc/test/testsupport/mock/mock_frame_writer.h b/webrtc/test/testsupport/mock/mock_frame_writer.h
new file mode 100644
index 0000000000..10f58fb47b
--- /dev/null
+++ b/webrtc/test/testsupport/mock/mock_frame_writer.h
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 2011 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_TESTSUPPORT_MOCK_MOCK_FRAME_WRITER_H_
+#define WEBRTC_TEST_TESTSUPPORT_MOCK_MOCK_FRAME_WRITER_H_
+
+#include "webrtc/test/testsupport/frame_writer.h"
+
+#include "testing/gmock/include/gmock/gmock.h"
+
+namespace webrtc {
+namespace test {
+
+class MockFrameWriter : public FrameWriter {
+ public:
+ MOCK_METHOD0(Init, bool());
+ MOCK_METHOD1(WriteFrame, bool(uint8_t* frame_buffer));
+ MOCK_METHOD0(Close, void());
+ MOCK_METHOD0(FrameLength, size_t());
+};
+
+} // namespace test
+} // namespace webrtc
+
+#endif // WEBRTC_TEST_TESTSUPPORT_MOCK_MOCK_FRAME_WRITER_H_
diff --git a/webrtc/test/testsupport/packet_reader.cc b/webrtc/test/testsupport/packet_reader.cc
new file mode 100644
index 0000000000..e27ec22f16
--- /dev/null
+++ b/webrtc/test/testsupport/packet_reader.cc
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2011 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/testsupport/packet_reader.h"
+
+#include <assert.h>
+#include <stdio.h>
+#include <algorithm>
+
+namespace webrtc {
+namespace test {
+
+PacketReader::PacketReader()
+ : initialized_(false) {}
+
+PacketReader::~PacketReader() {}
+
+void PacketReader::InitializeReading(uint8_t* data,
+ size_t data_length_in_bytes,
+ size_t packet_size_in_bytes) {
+ assert(data);
+ assert(packet_size_in_bytes > 0);
+ data_ = data;
+ data_length_ = data_length_in_bytes;
+ packet_size_ = packet_size_in_bytes;
+ currentIndex_ = 0;
+ initialized_ = true;
+}
+
+int PacketReader::NextPacket(uint8_t** packet_pointer) {
+ if (!initialized_) {
+ fprintf(stderr, "Attempting to use uninitialized PacketReader!\n");
+ return -1;
+ }
+ *packet_pointer = data_ + currentIndex_;
+ size_t old_index = currentIndex_;
+ currentIndex_ = std::min(currentIndex_ + packet_size_, data_length_);
+ return static_cast<int>(currentIndex_ - old_index);
+}
+
+} // namespace test
+} // namespace webrtc
diff --git a/webrtc/test/testsupport/packet_reader.h b/webrtc/test/testsupport/packet_reader.h
new file mode 100644
index 0000000000..b58db4d093
--- /dev/null
+++ b/webrtc/test/testsupport/packet_reader.h
@@ -0,0 +1,54 @@
+/*
+ * Copyright (c) 2011 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_TESTSUPPORT_PACKET_READER_H_
+#define WEBRTC_TEST_TESTSUPPORT_PACKET_READER_H_
+
+#include <cstddef>
+#include "webrtc/typedefs.h"
+
+namespace webrtc {
+namespace test {
+
+// Reads chunks of data to simulate network packets from a byte array.
+class PacketReader {
+ public:
+ PacketReader();
+ virtual ~PacketReader();
+
+ // Inizializes a new reading operation. Must be done before invoking the
+ // NextPacket method.
+ // * data_length_in_bytes is the length of the data byte array.
+ // 0 length will result in no packets are read.
+ // * packet_size_in_bytes is the number of bytes to read in each NextPacket
+ // method call. Must be > 0
+ virtual void InitializeReading(uint8_t* data, size_t data_length_in_bytes,
+ size_t packet_size_in_bytes);
+
+ // Moves the supplied pointer to the beginning of the next packet.
+ // Returns:
+ // * The size of the packet ready to read (lower than the packet size for
+ // the last packet)
+ // * 0 if there are no more packets to read
+ // * -1 if InitializeReading has not been called (also prints to stderr).
+ virtual int NextPacket(uint8_t** packet_pointer);
+
+ private:
+ uint8_t* data_;
+ size_t data_length_;
+ size_t packet_size_;
+ size_t currentIndex_;
+ bool initialized_;
+};
+
+} // namespace test
+} // namespace webrtc
+
+#endif // WEBRTC_TEST_TESTSUPPORT_PACKET_READER_H_
diff --git a/webrtc/test/testsupport/packet_reader_unittest.cc b/webrtc/test/testsupport/packet_reader_unittest.cc
new file mode 100644
index 0000000000..2679be473f
--- /dev/null
+++ b/webrtc/test/testsupport/packet_reader_unittest.cc
@@ -0,0 +1,125 @@
+/*
+ * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "webrtc/test/testsupport/packet_reader.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+#include "webrtc/test/testsupport/unittest_utils.h"
+
+namespace webrtc {
+namespace test {
+
+class PacketReaderTest: public PacketRelatedTest {
+ protected:
+ PacketReaderTest() {}
+ virtual ~PacketReaderTest() {}
+ void SetUp() {
+ reader_ = new PacketReader();
+ }
+ void TearDown() {
+ delete reader_;
+ }
+ void VerifyPacketData(size_t expected_length,
+ int actual_length,
+ uint8_t* original_data_pointer,
+ uint8_t* new_data_pointer) {
+ EXPECT_EQ(static_cast<int>(expected_length), actual_length);
+ EXPECT_EQ(*original_data_pointer, *new_data_pointer);
+ EXPECT_EQ(0, memcmp(original_data_pointer, new_data_pointer,
+ actual_length));
+ }
+ PacketReader* reader_;
+};
+
+// Test lack of initialization
+TEST_F(PacketReaderTest, Uninitialized) {
+ uint8_t* data_pointer = NULL;
+ EXPECT_EQ(-1, reader_->NextPacket(&data_pointer));
+ EXPECT_EQ(NULL, data_pointer);
+}
+
+TEST_F(PacketReaderTest, InitializeZeroLengthArgument) {
+ reader_->InitializeReading(packet_data_, 0, kPacketSizeInBytes);
+ ASSERT_EQ(0, reader_->NextPacket(&packet_data_pointer_));
+}
+
+// Test with something smaller than one packet
+TEST_F(PacketReaderTest, NormalSmallData) {
+ const int kDataLengthInBytes = 1499;
+ uint8_t data[kDataLengthInBytes];
+ uint8_t* data_pointer = data;
+ memset(data, 1, kDataLengthInBytes);
+
+ reader_->InitializeReading(data, kDataLengthInBytes, kPacketSizeInBytes);
+ int length_to_read = reader_->NextPacket(&data_pointer);
+ VerifyPacketData(kDataLengthInBytes, length_to_read, data, data_pointer);
+ EXPECT_EQ(0, data_pointer - data); // pointer hasn't moved
+
+ // Reading another one shall result in 0 bytes:
+ length_to_read = reader_->NextPacket(&data_pointer);
+ EXPECT_EQ(0, length_to_read);
+ EXPECT_EQ(kDataLengthInBytes, data_pointer - data);
+}
+
+// Test with data length that exactly matches one packet
+TEST_F(PacketReaderTest, NormalOnePacketData) {
+ uint8_t data[kPacketSizeInBytes];
+ uint8_t* data_pointer = data;
+ memset(data, 1, kPacketSizeInBytes);
+
+ reader_->InitializeReading(data, kPacketSizeInBytes, kPacketSizeInBytes);
+ int length_to_read = reader_->NextPacket(&data_pointer);
+ VerifyPacketData(kPacketSizeInBytes, length_to_read, data, data_pointer);
+ EXPECT_EQ(0, data_pointer - data); // pointer hasn't moved
+
+ // Reading another one shall result in 0 bytes:
+ length_to_read = reader_->NextPacket(&data_pointer);
+ EXPECT_EQ(0, length_to_read);
+ EXPECT_EQ(kPacketSizeInBytes, static_cast<size_t>(data_pointer - data));
+}
+
+// Test with data length that will result in 3 packets
+TEST_F(PacketReaderTest, NormalLargeData) {
+ reader_->InitializeReading(packet_data_, kPacketDataLength,
+ kPacketSizeInBytes);
+
+ int length_to_read = reader_->NextPacket(&packet_data_pointer_);
+ VerifyPacketData(kPacketSizeInBytes, length_to_read,
+ packet1_, packet_data_pointer_);
+
+ length_to_read = reader_->NextPacket(&packet_data_pointer_);
+ VerifyPacketData(kPacketSizeInBytes, length_to_read,
+ packet2_, packet_data_pointer_);
+
+ length_to_read = reader_->NextPacket(&packet_data_pointer_);
+ VerifyPacketData(1u, length_to_read,
+ packet3_, packet_data_pointer_);
+
+ // Reading another one shall result in 0 bytes:
+ length_to_read = reader_->NextPacket(&packet_data_pointer_);
+ EXPECT_EQ(0, length_to_read);
+ EXPECT_EQ(kPacketDataLength,
+ static_cast<size_t>(packet_data_pointer_ - packet_data_));
+}
+
+// Test with empty data.
+TEST_F(PacketReaderTest, EmptyData) {
+ const int kDataLengthInBytes = 0;
+ // But don't really try to allocate a zero-length array...
+ uint8_t data[kPacketSizeInBytes];
+ uint8_t* data_pointer = data;
+ reader_->InitializeReading(data, kDataLengthInBytes, kPacketSizeInBytes);
+ EXPECT_EQ(kDataLengthInBytes, reader_->NextPacket(&data_pointer));
+ // Do it again to make sure nothing changes
+ EXPECT_EQ(kDataLengthInBytes, reader_->NextPacket(&data_pointer));
+}
+
+} // namespace test
+} // namespace webrtc
diff --git a/webrtc/test/testsupport/perf_test.cc b/webrtc/test/testsupport/perf_test.cc
new file mode 100644
index 0000000000..3266bb7945
--- /dev/null
+++ b/webrtc/test/testsupport/perf_test.cc
@@ -0,0 +1,177 @@
+/*
+ * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+// A stripped-down version of Chromium's chrome/test/perf/perf_test.cc.
+// ResultsToString(), PrintResult(size_t value) and AppendResult(size_t value)
+// have been modified. The remainder are identical to the Chromium version.
+
+#include "webrtc/test/testsupport/perf_test.h"
+
+#include <sstream>
+#include <stdio.h>
+
+namespace {
+
+std::string ResultsToString(const std::string& measurement,
+ const std::string& modifier,
+ const std::string& trace,
+ const std::string& values,
+ const std::string& prefix,
+ const std::string& suffix,
+ const std::string& units,
+ bool important) {
+ // <*>RESULT <graph_name>: <trace_name>= <value> <units>
+ // <*>RESULT <graph_name>: <trace_name>= {<mean>, <std deviation>} <units>
+ // <*>RESULT <graph_name>: <trace_name>= [<value>,value,value,...,] <units>
+
+ // TODO(ajm): Use of a stream here may violate the style guide (depending on
+ // one's definition of "logging"). Consider adding StringPrintf-like
+ // functionality as in the original Chromium implementation.
+ std::ostringstream stream;
+ if (important) {
+ stream << "*";
+ }
+ stream << "RESULT " << measurement << modifier << ": " << trace << "= "
+ << prefix << values << suffix << " " << units << std::endl;
+ return stream.str();
+}
+
+void PrintResultsImpl(const std::string& measurement,
+ const std::string& modifier,
+ const std::string& trace,
+ const std::string& values,
+ const std::string& prefix,
+ const std::string& suffix,
+ const std::string& units,
+ bool important) {
+ printf("%s", ResultsToString(measurement, modifier, trace, values,
+ prefix, suffix, units, important).c_str());
+}
+
+} // namespace
+
+namespace webrtc {
+namespace test {
+
+void PrintResult(const std::string& measurement,
+ const std::string& modifier,
+ const std::string& trace,
+ size_t value,
+ const std::string& units,
+ bool important) {
+ std::ostringstream value_stream;
+ value_stream << value;
+ PrintResultsImpl(measurement, modifier, trace, value_stream.str(), "", "",
+ units, important);
+}
+
+void AppendResult(std::string& output,
+ const std::string& measurement,
+ const std::string& modifier,
+ const std::string& trace,
+ size_t value,
+ const std::string& units,
+ bool important) {
+ std::ostringstream value_stream;
+ value_stream << value;
+ output += ResultsToString(measurement, modifier, trace,
+ value_stream.str(),
+ "", "", units, important);
+}
+
+void PrintResult(const std::string& measurement,
+ const std::string& modifier,
+ const std::string& trace,
+ const std::string& value,
+ const std::string& units,
+ bool important) {
+ PrintResultsImpl(measurement, modifier, trace, value, "", "", units,
+ important);
+}
+
+void AppendResult(std::string& output,
+ const std::string& measurement,
+ const std::string& modifier,
+ const std::string& trace,
+ const std::string& value,
+ const std::string& units,
+ bool important) {
+ output += ResultsToString(measurement, modifier, trace, value, "", "", units,
+ important);
+}
+
+void PrintResultMeanAndError(const std::string& measurement,
+ const std::string& modifier,
+ const std::string& trace,
+ const std::string& mean_and_error,
+ const std::string& units,
+ bool important) {
+ PrintResultsImpl(measurement, modifier, trace, mean_and_error,
+ "{", "}", units, important);
+}
+
+void AppendResultMeanAndError(std::string& output,
+ const std::string& measurement,
+ const std::string& modifier,
+ const std::string& trace,
+ const std::string& mean_and_error,
+ const std::string& units,
+ bool important) {
+ output += ResultsToString(measurement, modifier, trace, mean_and_error,
+ "{", "}", units, important);
+}
+
+void PrintResultList(const std::string& measurement,
+ const std::string& modifier,
+ const std::string& trace,
+ const std::string& values,
+ const std::string& units,
+ bool important) {
+ PrintResultsImpl(measurement, modifier, trace, values,
+ "[", "]", units, important);
+}
+
+void AppendResultList(std::string& output,
+ const std::string& measurement,
+ const std::string& modifier,
+ const std::string& trace,
+ const std::string& values,
+ const std::string& units,
+ bool important) {
+ output += ResultsToString(measurement, modifier, trace, values,
+ "[", "]", units, important);
+}
+
+void PrintSystemCommitCharge(const std::string& test_name,
+ size_t charge,
+ bool important) {
+ PrintSystemCommitCharge(stdout, test_name, charge, important);
+}
+
+void PrintSystemCommitCharge(FILE* target,
+ const std::string& test_name,
+ size_t charge,
+ bool important) {
+ fprintf(target, "%s", SystemCommitChargeToString(test_name, charge,
+ important).c_str());
+}
+
+std::string SystemCommitChargeToString(const std::string& test_name,
+ size_t charge,
+ bool important) {
+ std::string trace_name(test_name);
+ std::string output;
+ AppendResult(output, "commit_charge", "", "cc" + trace_name, charge, "kb",
+ important);
+ return output;
+}
+
+} // namespace test
+} // namespace webrtc
diff --git a/webrtc/test/testsupport/perf_test.h b/webrtc/test/testsupport/perf_test.h
new file mode 100644
index 0000000000..76e09e1cbb
--- /dev/null
+++ b/webrtc/test/testsupport/perf_test.h
@@ -0,0 +1,122 @@
+/*
+ * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+// A stripped-down version of Chromium's chrome/test/perf/perf_test.h.
+// Several functions have been removed; the prototypes of the remainder have
+// not been changed.
+
+#ifndef WEBRTC_TEST_TESTSUPPORT_PERF_TEST_H_
+#define WEBRTC_TEST_TESTSUPPORT_PERF_TEST_H_
+
+#include <string>
+
+namespace webrtc {
+namespace test {
+
+// Prints numerical information to stdout in a controlled format, for
+// post-processing. |measurement| is a description of the quantity being
+// measured, e.g. "vm_peak"; |modifier| is provided as a convenience and
+// will be appended directly to the name of the |measurement|, e.g.
+// "_browser"; |trace| is a description of the particular data point, e.g.
+// "reference"; |value| is the measured value; and |units| is a description
+// of the units of measure, e.g. "bytes". If |important| is true, the output
+// line will be specially marked, to notify the post-processor. The strings
+// may be empty. They should not contain any colons (:) or equals signs (=).
+// A typical post-processing step would be to produce graphs of the data
+// produced for various builds, using the combined |measurement| + |modifier|
+// string to specify a particular graph and the |trace| to identify a trace
+// (i.e., data series) on that graph.
+void PrintResult(const std::string& measurement,
+ const std::string& modifier,
+ const std::string& trace,
+ size_t value,
+ const std::string& units,
+ bool important);
+
+void AppendResult(std::string& output,
+ const std::string& measurement,
+ const std::string& modifier,
+ const std::string& trace,
+ size_t value,
+ const std::string& units,
+ bool important);
+
+// Like the above version of PrintResult(), but takes a std::string value
+// instead of a size_t.
+void PrintResult(const std::string& measurement,
+ const std::string& modifier,
+ const std::string& trace,
+ const std::string& value,
+ const std::string& units,
+ bool important);
+
+void AppendResult(std::string& output,
+ const std::string& measurement,
+ const std::string& modifier,
+ const std::string& trace,
+ const std::string& value,
+ const std::string& units,
+ bool important);
+
+// Like PrintResult(), but prints a (mean, standard deviation) result pair.
+// The |<values>| should be two comma-separated numbers, the mean and
+// standard deviation (or other error metric) of the measurement.
+void PrintResultMeanAndError(const std::string& measurement,
+ const std::string& modifier,
+ const std::string& trace,
+ const std::string& mean_and_error,
+ const std::string& units,
+ bool important);
+
+void AppendResultMeanAndError(std::string& output,
+ const std::string& measurement,
+ const std::string& modifier,
+ const std::string& trace,
+ const std::string& mean_and_error,
+ const std::string& units,
+ bool important);
+
+// Like PrintResult(), but prints an entire list of results. The |values|
+// will generally be a list of comma-separated numbers. A typical
+// post-processing step might produce plots of their mean and standard
+// deviation.
+void PrintResultList(const std::string& measurement,
+ const std::string& modifier,
+ const std::string& trace,
+ const std::string& values,
+ const std::string& units,
+ bool important);
+
+void AppendResultList(std::string& output,
+ const std::string& measurement,
+ const std::string& modifier,
+ const std::string& trace,
+ const std::string& values,
+ const std::string& units,
+ bool important);
+
+// Prints memory commit charge stats for use by perf graphs.
+void PrintSystemCommitCharge(const std::string& test_name,
+ size_t charge,
+ bool important);
+
+void PrintSystemCommitCharge(FILE* target,
+ const std::string& test_name,
+ size_t charge,
+ bool important);
+
+std::string SystemCommitChargeToString(const std::string& test_name,
+ size_t charge,
+ bool important);
+
+} // namespace test
+} // namespace webrtc
+
+#endif // WEBRTC_TEST_TESTSUPPORT_PERF_TEST_H_
diff --git a/webrtc/test/testsupport/perf_test_unittest.cc b/webrtc/test/testsupport/perf_test_unittest.cc
new file mode 100644
index 0000000000..f7e50e1bc6
--- /dev/null
+++ b/webrtc/test/testsupport/perf_test_unittest.cc
@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "webrtc/test/testsupport/perf_test.h"
+
+#include <string>
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace webrtc {
+namespace test {
+
+TEST(PerfTest, AppendResult) {
+ std::string expected = "RESULT measurementmodifier: trace= 42 units\n";
+ std::string output;
+ AppendResult(output, "measurement", "modifier", "trace", 42, "units", false);
+ EXPECT_EQ(expected, output);
+ std::cout << output;
+
+ expected += "*RESULT foobar: baz= 7 widgets\n";
+ AppendResult(output, "foo", "bar", "baz", 7, "widgets", true);
+ EXPECT_EQ(expected, output);
+ std::cout << output;
+}
+
+} // namespace test
+} // namespace webrtc
diff --git a/webrtc/test/testsupport/trace_to_stderr.cc b/webrtc/test/testsupport/trace_to_stderr.cc
new file mode 100644
index 0000000000..7338441fa5
--- /dev/null
+++ b/webrtc/test/testsupport/trace_to_stderr.cc
@@ -0,0 +1,63 @@
+/*
+ * 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/testsupport/trace_to_stderr.h"
+
+#include <assert.h>
+#include <stdio.h>
+
+#include <string>
+
+namespace webrtc {
+namespace test {
+
+static const int kLevelFilter = kTraceError | kTraceWarning | kTraceTerseInfo;
+
+TraceToStderr::TraceToStderr()
+ : override_time_(false),
+ time_seconds_(0) {
+ Trace::set_level_filter(kLevelFilter);
+ Trace::CreateTrace();
+ Trace::SetTraceCallback(this);
+}
+
+TraceToStderr::TraceToStderr(bool override_time)
+ : override_time_(override_time),
+ time_seconds_(0) {
+ Trace::set_level_filter(kLevelFilter);
+ Trace::CreateTrace();
+ Trace::SetTraceCallback(this);
+}
+
+TraceToStderr::~TraceToStderr() {
+ Trace::SetTraceCallback(NULL);
+ Trace::ReturnTrace();
+}
+
+void TraceToStderr::SetTimeSeconds(float time) { time_seconds_ = time; }
+
+void TraceToStderr::Print(TraceLevel level, const char* msg_array, int length) {
+ if (level & kLevelFilter) {
+ assert(length > Trace::kBoilerplateLength);
+ std::string msg = msg_array;
+ std::string msg_log = msg.substr(Trace::kBoilerplateLength);
+ if (override_time_) {
+ fprintf(stderr, "%.2fs %s\n", time_seconds_, msg_log.c_str());
+ } else {
+ std::string msg_time = msg.substr(Trace::kTimestampPosition,
+ Trace::kTimestampLength);
+ fprintf(stderr, "%s %s\n", msg_time.c_str(), msg_log.c_str());
+ }
+ fflush(stderr);
+ }
+}
+
+} // namespace test
+} // namespace webrtc
diff --git a/webrtc/test/testsupport/trace_to_stderr.h b/webrtc/test/testsupport/trace_to_stderr.h
new file mode 100644
index 0000000000..a713b798c5
--- /dev/null
+++ b/webrtc/test/testsupport/trace_to_stderr.h
@@ -0,0 +1,52 @@
+/*
+ * 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_TEST_SUPPORT_TRACE_TO_STDERR_H_
+#define WEBRTC_TEST_TEST_SUPPORT_TRACE_TO_STDERR_H_
+
+#include "webrtc/system_wrappers/include/trace.h"
+
+namespace webrtc {
+namespace test {
+
+// Upon constructing an instance of this class, all traces will be redirected
+// to stderr. At destruction, redirection is halted.
+class TraceToStderr : public TraceCallback {
+ public:
+ TraceToStderr();
+ // Set |override_time| to true to control the time printed with each trace
+ // through SetTimeSeconds(). Otherwise, the trace's usual wallclock time is
+ // used.
+ //
+ // This is useful for offline test tools, where the file time is much more
+ // informative than the real time.
+ explicit TraceToStderr(bool override_time);
+ ~TraceToStderr() override;
+
+ // Every subsequent trace printout will use |time|. Has no effect if
+ // |override_time| in the constructor was set to false.
+ //
+ // No attempt is made to ensure thread-safety between the trace writing and
+ // time updating. In tests, since traces will normally be triggered by the
+ // main thread doing the time updating, this should be of no concern.
+ virtual void SetTimeSeconds(float time);
+
+ // Implements TraceCallback.
+ void Print(TraceLevel level, const char* msg_array, int length) override;
+
+ private:
+ bool override_time_;
+ float time_seconds_;
+};
+
+} // namespace test
+} // namespace webrtc
+
+#endif // WEBRTC_TEST_TEST_SUPPORT_TRACE_TO_STDERR_H_
diff --git a/webrtc/test/testsupport/unittest_utils.h b/webrtc/test/testsupport/unittest_utils.h
new file mode 100644
index 0000000000..ba6db9883b
--- /dev/null
+++ b/webrtc/test/testsupport/unittest_utils.h
@@ -0,0 +1,56 @@
+/*
+ * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#ifndef WEBRTC_TEST_TESTSUPPORT_UNITTEST_UTILS_H_
+#define WEBRTC_TEST_TESTSUPPORT_UNITTEST_UTILS_H_
+
+namespace webrtc {
+namespace test {
+
+const size_t kPacketSizeInBytes = 1500;
+const size_t kPacketDataLength = kPacketSizeInBytes * 2 + 1;
+const int kPacketDataNumberOfPackets = 3;
+
+// A base test fixture for packet related tests. Contains
+// two full prepared packets with 1s, 2s in their data and a third packet with
+// a single 3 in it (size=1).
+// A packet data structure is also available, that contains these three packets
+// in order.
+class PacketRelatedTest: public testing::Test {
+ protected:
+ // Tree packet byte arrays with data used for verification:
+ uint8_t packet1_[kPacketSizeInBytes];
+ uint8_t packet2_[kPacketSizeInBytes];
+ uint8_t packet3_[1];
+ // Construct a data structure containing these packets
+ uint8_t packet_data_[kPacketDataLength];
+ uint8_t* packet_data_pointer_;
+
+ PacketRelatedTest() {
+ packet_data_pointer_ = packet_data_;
+
+ memset(packet1_, 1, kPacketSizeInBytes);
+ memset(packet2_, 2, kPacketSizeInBytes);
+ memset(packet3_, 3, 1);
+ // Fill the packet_data:
+ memcpy(packet_data_pointer_, packet1_, kPacketSizeInBytes);
+ memcpy(packet_data_pointer_ + kPacketSizeInBytes, packet2_,
+ kPacketSizeInBytes);
+ memcpy(packet_data_pointer_ + kPacketSizeInBytes * 2, packet3_, 1);
+ }
+ virtual ~PacketRelatedTest() {}
+ void SetUp() {}
+ void TearDown() {}
+};
+
+} // namespace test
+} // namespace webrtc
+
+#endif // WEBRTC_TEST_TESTSUPPORT_UNITTEST_UTILS_H_
diff --git a/webrtc/test/vcm_capturer.cc b/webrtc/test/vcm_capturer.cc
new file mode 100644
index 0000000000..1c6b91915e
--- /dev/null
+++ b/webrtc/test/vcm_capturer.cc
@@ -0,0 +1,107 @@
+/*
+ * 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/vcm_capturer.h"
+
+#include "webrtc/modules/video_capture/include/video_capture_factory.h"
+#include "webrtc/video_send_stream.h"
+
+namespace webrtc {
+namespace test {
+
+VcmCapturer::VcmCapturer(webrtc::VideoCaptureInput* input)
+ : VideoCapturer(input), started_(false), vcm_(NULL) {
+}
+
+bool VcmCapturer::Init(size_t width, size_t height, size_t target_fps) {
+ VideoCaptureModule::DeviceInfo* device_info =
+ VideoCaptureFactory::CreateDeviceInfo(42); // Any ID (42) will do.
+
+ char device_name[256];
+ char unique_name[256];
+ if (device_info->GetDeviceName(0, device_name, sizeof(device_name),
+ unique_name, sizeof(unique_name)) !=
+ 0) {
+ Destroy();
+ return false;
+ }
+
+ vcm_ = webrtc::VideoCaptureFactory::Create(0, unique_name);
+ vcm_->RegisterCaptureDataCallback(*this);
+
+ device_info->GetCapability(vcm_->CurrentDeviceName(), 0, capability_);
+ delete device_info;
+
+ capability_.width = static_cast<int32_t>(width);
+ capability_.height = static_cast<int32_t>(height);
+ capability_.maxFPS = static_cast<int32_t>(target_fps);
+ capability_.rawType = kVideoI420;
+
+ if (vcm_->StartCapture(capability_) != 0) {
+ Destroy();
+ return false;
+ }
+
+ assert(vcm_->CaptureStarted());
+
+ return true;
+}
+
+VcmCapturer* VcmCapturer::Create(VideoCaptureInput* input,
+ size_t width,
+ size_t height,
+ size_t target_fps) {
+ VcmCapturer* vcm_capturer = new VcmCapturer(input);
+ if (!vcm_capturer->Init(width, height, target_fps)) {
+ // TODO(pbos): Log a warning that this failed.
+ delete vcm_capturer;
+ return NULL;
+ }
+ return vcm_capturer;
+}
+
+
+void VcmCapturer::Start() {
+ rtc::CritScope lock(&crit_);
+ started_ = true;
+}
+
+void VcmCapturer::Stop() {
+ rtc::CritScope lock(&crit_);
+ started_ = false;
+}
+
+void VcmCapturer::Destroy() {
+ if (vcm_ == NULL) {
+ return;
+ }
+
+ vcm_->StopCapture();
+ vcm_->DeRegisterCaptureDataCallback();
+ vcm_->Release();
+
+ // TODO(pbos): How do I destroy the VideoCaptureModule? This still leaves
+ // non-freed memory.
+ vcm_ = NULL;
+}
+
+VcmCapturer::~VcmCapturer() { Destroy(); }
+
+void VcmCapturer::OnIncomingCapturedFrame(const int32_t id,
+ const VideoFrame& frame) {
+ rtc::CritScope lock(&crit_);
+ if (started_)
+ input_->IncomingCapturedFrame(frame);
+}
+
+void VcmCapturer::OnCaptureDelayChanged(const int32_t id, const int32_t delay) {
+}
+} // test
+} // webrtc
diff --git a/webrtc/test/vcm_capturer.h b/webrtc/test/vcm_capturer.h
new file mode 100644
index 0000000000..53d61fc53a
--- /dev/null
+++ b/webrtc/test/vcm_capturer.h
@@ -0,0 +1,50 @@
+/*
+ * 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_ENGINE_TEST_COMMON_VCM_CAPTURER_H_
+#define WEBRTC_VIDEO_ENGINE_TEST_COMMON_VCM_CAPTURER_H_
+
+#include "webrtc/base/criticalsection.h"
+#include "webrtc/common_types.h"
+#include "webrtc/common_video/libyuv/include/webrtc_libyuv.h"
+#include "webrtc/modules/video_capture/include/video_capture.h"
+#include "webrtc/test/video_capturer.h"
+
+namespace webrtc {
+namespace test {
+
+class VcmCapturer : public VideoCapturer, public VideoCaptureDataCallback {
+ public:
+ static VcmCapturer* Create(VideoCaptureInput* input,
+ size_t width,
+ size_t height,
+ size_t target_fps);
+ virtual ~VcmCapturer();
+
+ void Start() override;
+ void Stop() override;
+
+ void OnIncomingCapturedFrame(const int32_t id,
+ const VideoFrame& frame) override; // NOLINT
+ void OnCaptureDelayChanged(const int32_t id, const int32_t delay) override;
+
+ private:
+ explicit VcmCapturer(VideoCaptureInput* input);
+ bool Init(size_t width, size_t height, size_t target_fps);
+ void Destroy();
+
+ rtc::CriticalSection crit_;
+ bool started_ GUARDED_BY(crit_);
+ VideoCaptureModule* vcm_;
+ VideoCaptureCapability capability_;
+};
+} // test
+} // webrtc
+
+#endif // WEBRTC_VIDEO_ENGINE_TEST_COMMON_VCM_CAPTURER_H_
diff --git a/webrtc/test/video_capturer.cc b/webrtc/test/video_capturer.cc
new file mode 100644
index 0000000000..840378f013
--- /dev/null
+++ b/webrtc/test/video_capturer.cc
@@ -0,0 +1,54 @@
+/*
+ * 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/video_capturer.h"
+
+#include "webrtc/test/testsupport/fileutils.h"
+#include "webrtc/test/frame_generator_capturer.h"
+#include "webrtc/test/vcm_capturer.h"
+
+namespace webrtc {
+namespace test {
+
+class NullCapturer : public VideoCapturer {
+ public:
+ NullCapturer() : VideoCapturer(NULL) {}
+ virtual ~NullCapturer() {}
+
+ virtual void Start() {}
+ virtual void Stop() {}
+};
+
+VideoCapturer::VideoCapturer(VideoCaptureInput* input) : input_(input) {
+}
+
+VideoCapturer* VideoCapturer::Create(VideoCaptureInput* input,
+ size_t width,
+ size_t height,
+ int fps,
+ Clock* clock) {
+ VcmCapturer* vcm_capturer = VcmCapturer::Create(input, width, height, fps);
+
+ if (vcm_capturer != NULL) {
+ return vcm_capturer;
+ }
+ // TODO(pbos): Log a warning that this failed.
+
+ FrameGeneratorCapturer* frame_generator_capturer =
+ FrameGeneratorCapturer::Create(input, width, height, fps, clock);
+ if (frame_generator_capturer != NULL) {
+ return frame_generator_capturer;
+ }
+ // TODO(pbos): Log a warning that this failed.
+
+ return new NullCapturer();
+}
+} // test
+} // webrtc
diff --git a/webrtc/test/video_capturer.h b/webrtc/test/video_capturer.h
new file mode 100644
index 0000000000..3fe86f1998
--- /dev/null
+++ b/webrtc/test/video_capturer.h
@@ -0,0 +1,42 @@
+/*
+ * 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_ENGINE_TEST_COMMON_VIDEO_CAPTURER_H_
+#define WEBRTC_VIDEO_ENGINE_TEST_COMMON_VIDEO_CAPTURER_H_
+
+#include <stddef.h>
+
+namespace webrtc {
+
+class Clock;
+
+class VideoCaptureInput;
+
+namespace test {
+
+class VideoCapturer {
+ public:
+ static VideoCapturer* Create(VideoCaptureInput* input,
+ size_t width,
+ size_t height,
+ int fps,
+ Clock* clock);
+ virtual ~VideoCapturer() {}
+
+ virtual void Start() = 0;
+ virtual void Stop() = 0;
+
+ protected:
+ explicit VideoCapturer(VideoCaptureInput* input);
+ VideoCaptureInput* input_;
+};
+} // test
+} // webrtc
+
+#endif // WEBRTC_VIDEO_ENGINE_TEST_COMMON_VIDEO_CAPTURER_H_
diff --git a/webrtc/test/video_renderer.cc b/webrtc/test/video_renderer.cc
new file mode 100644
index 0000000000..c7b60e5949
--- /dev/null
+++ b/webrtc/test/video_renderer.cc
@@ -0,0 +1,37 @@
+/*
+ * 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/video_renderer.h"
+
+// TODO(pbos): Android renderer
+
+#include "webrtc/typedefs.h"
+
+namespace webrtc {
+namespace test {
+
+class NullRenderer : public VideoRenderer {
+ void RenderFrame(const VideoFrame& video_frame,
+ int time_to_render_ms) override {}
+ bool IsTextureSupported() const override { return false; }
+};
+
+VideoRenderer* VideoRenderer::Create(const char* window_title,
+ size_t width,
+ size_t height) {
+ VideoRenderer* renderer = CreatePlatformRenderer(window_title, width, height);
+ if (renderer != NULL) {
+ // TODO(mflodman) Add a warning log.
+ return renderer;
+ }
+
+ return new NullRenderer();
+}
+} // namespace test
+} // namespace webrtc
diff --git a/webrtc/test/video_renderer.h b/webrtc/test/video_renderer.h
new file mode 100644
index 0000000000..c8623270a7
--- /dev/null
+++ b/webrtc/test/video_renderer.h
@@ -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.
+ */
+#ifndef WEBRTC_VIDEO_ENGINE_TEST_COMMON_VIDEO_RENDERER_H_
+#define WEBRTC_VIDEO_ENGINE_TEST_COMMON_VIDEO_RENDERER_H_
+
+#include <stddef.h>
+
+#include "webrtc/video_renderer.h"
+
+namespace webrtc {
+namespace test {
+class VideoRenderer : public webrtc::VideoRenderer {
+ public:
+ // Creates a platform-specific renderer if possible, or a null implementation
+ // if failing.
+ static VideoRenderer* Create(const char* window_title, size_t width,
+ size_t height);
+ // Returns a renderer rendering to a platform specific window if possible,
+ // NULL if none can be created.
+ // Creates a platform-specific renderer if possible, returns NULL if a
+ // platform renderer could not be created. This occurs, for instance, when
+ // running without an X environment on Linux.
+ static VideoRenderer* CreatePlatformRenderer(const char* window_title,
+ size_t width, size_t height);
+ virtual ~VideoRenderer() {}
+ protected:
+ VideoRenderer() {}
+};
+} // namespace test
+} // namespace webrtc
+
+#endif // WEBRTC_VIDEO_ENGINE_TEST_COMMON_VIDEO_RENDERER_H_
diff --git a/webrtc/test/webrtc_test_common.gyp b/webrtc/test/webrtc_test_common.gyp
new file mode 100644
index 0000000000..d075cb470b
--- /dev/null
+++ b/webrtc/test/webrtc_test_common.gyp
@@ -0,0 +1,176 @@
+# 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.
+{
+ 'includes': [
+ '../build/common.gypi',
+ ],
+ 'targets': [
+ {
+ 'target_name': 'webrtc_test_common',
+ 'type': 'static_library',
+ 'sources': [
+ 'call_test.cc',
+ 'call_test.h',
+ 'configurable_frame_size_encoder.cc',
+ 'configurable_frame_size_encoder.h',
+ 'constants.cc',
+ 'constants.h',
+ 'direct_transport.cc',
+ 'direct_transport.h',
+ 'encoder_settings.cc',
+ 'encoder_settings.h',
+ 'fake_audio_device.cc',
+ 'fake_audio_device.h',
+ 'fake_decoder.cc',
+ 'fake_decoder.h',
+ 'fake_encoder.cc',
+ 'fake_encoder.h',
+ 'fake_network_pipe.cc',
+ 'fake_network_pipe.h',
+ 'fake_voice_engine.cc',
+ 'fake_voice_engine.h',
+ 'frame_generator_capturer.cc',
+ 'frame_generator_capturer.h',
+ 'layer_filtering_transport.cc',
+ 'layer_filtering_transport.h',
+ 'mock_transport.h',
+ 'null_transport.cc',
+ 'null_transport.h',
+ 'random.cc',
+ 'random.h',
+ 'rtp_rtcp_observer.h',
+ 'run_loop.cc',
+ 'run_loop.h',
+ 'statistics.cc',
+ 'statistics.h',
+ 'vcm_capturer.cc',
+ 'vcm_capturer.h',
+ 'video_capturer.cc',
+ 'video_capturer.h',
+ 'win/run_loop_win.cc',
+ ],
+ 'conditions': [
+ ['OS=="win"', {
+ 'sources!': [
+ 'run_loop.cc',
+ ],
+ }],
+ ],
+ 'dependencies': [
+ '<(DEPTH)/testing/gtest.gyp:gtest',
+ '<(DEPTH)/third_party/gflags/gflags.gyp:gflags',
+ '<(webrtc_root)/base/base.gyp:rtc_base',
+ '<(webrtc_root)/common.gyp:webrtc_common',
+ '<(webrtc_root)/modules/modules.gyp:media_file',
+ '<(webrtc_root)/modules/modules.gyp:video_render',
+ '<(webrtc_root)/test/test.gyp:frame_generator',
+ '<(webrtc_root)/test/test.gyp:test_support',
+ '<(webrtc_root)/test/test.gyp:rtp_test_utils',
+ '<(webrtc_root)/webrtc.gyp:webrtc',
+ ],
+ },
+ {
+ 'target_name': 'webrtc_test_renderer',
+ 'type': 'static_library',
+ 'sources': [
+ 'gl/gl_renderer.cc',
+ 'gl/gl_renderer.h',
+ 'linux/glx_renderer.cc',
+ 'linux/glx_renderer.h',
+ 'linux/video_renderer_linux.cc',
+ 'mac/video_renderer_mac.h',
+ 'mac/video_renderer_mac.mm',
+ 'null_platform_renderer.cc',
+ 'video_renderer.cc',
+ 'video_renderer.h',
+ 'win/d3d_renderer.cc',
+ 'win/d3d_renderer.h',
+ ],
+ 'conditions': [
+ ['OS=="linux"', {
+ 'sources!': [
+ 'null_platform_renderer.cc',
+ ],
+ }],
+ ['OS=="mac"', {
+ 'sources!': [
+ 'null_platform_renderer.cc',
+ ],
+ }],
+ ['OS!="linux" and OS!="mac"', {
+ 'sources!' : [
+ 'gl/gl_renderer.cc',
+ 'gl/gl_renderer.h',
+ ],
+ }],
+ ['OS=="win"', {
+ 'sources!': [
+ 'null_platform_renderer.cc',
+ ],
+ 'include_dirs': [
+ '<(directx_sdk_path)/Include',
+ ],
+ }],
+ ],
+ 'dependencies': [
+ '<(DEPTH)/testing/gtest.gyp:gtest',
+ '<(webrtc_root)/modules/modules.gyp:media_file',
+ '<(webrtc_root)/test/test.gyp:frame_generator',
+ '<(webrtc_root)/test/test.gyp:test_support',
+ ],
+ 'direct_dependent_settings': {
+ 'conditions': [
+ ['OS=="linux"', {
+ 'libraries': [
+ '-lXext',
+ '-lX11',
+ '-lGL',
+ ],
+ }],
+ ['OS=="android"', {
+ 'libraries' : [
+ '-lGLESv2', '-llog',
+ ],
+ }],
+ ['OS=="mac"', {
+ 'xcode_settings' : {
+ 'OTHER_LDFLAGS' : [
+ '-framework Cocoa',
+ '-framework OpenGL',
+ '-framework CoreVideo',
+ ],
+ },
+ }],
+ ],
+ },
+ },
+ ],
+ 'conditions': [
+ ['include_tests==1', {
+ 'targets': [
+ {
+ 'target_name': 'webrtc_test_common_unittests',
+ 'type': '<(gtest_target_type)',
+ 'dependencies': [
+ 'webrtc_test_common',
+ '<(DEPTH)/testing/gtest.gyp:gtest',
+ '<(DEPTH)/testing/gmock.gyp:gmock',
+ '<(webrtc_root)/modules/modules.gyp:video_capture',
+ '<(webrtc_root)/test/test.gyp:test_support_main',
+ ],
+ 'sources': [
+ 'fake_network_pipe_unittest.cc',
+ 'frame_generator_unittest.cc',
+ 'rtp_file_reader_unittest.cc',
+ 'rtp_file_writer_unittest.cc',
+ ],
+ },
+ ], #targets
+ }], # include_tests
+ ], # conditions
+}
diff --git a/webrtc/test/win/d3d_renderer.cc b/webrtc/test/win/d3d_renderer.cc
new file mode 100644
index 0000000000..86900e93dd
--- /dev/null
+++ b/webrtc/test/win/d3d_renderer.cc
@@ -0,0 +1,219 @@
+/*
+ * 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/win/d3d_renderer.h"
+
+#include "webrtc/common_video/libyuv/include/webrtc_libyuv.h"
+
+namespace webrtc {
+namespace test {
+
+#define D3DFVF_CUSTOMVERTEX (D3DFVF_XYZ|D3DFVF_TEX1)
+
+struct D3dCustomVertex {
+ float x, y, z;
+ float u, v;
+};
+
+const char kD3DClassName[] = "d3d_renderer";
+
+VideoRenderer* VideoRenderer::CreatePlatformRenderer(const char* window_title,
+ size_t width,
+ size_t height) {
+ return D3dRenderer::Create(window_title, width, height);
+}
+
+D3dRenderer::D3dRenderer(size_t width, size_t height)
+ : width_(width),
+ height_(height),
+ hwnd_(NULL),
+ d3d_(NULL),
+ d3d_device_(NULL),
+ texture_(NULL),
+ vertex_buffer_(NULL) {
+ assert(width > 0);
+ assert(height > 0);
+}
+
+D3dRenderer::~D3dRenderer() { Destroy(); }
+
+LRESULT WINAPI D3dRenderer::WindowProc(HWND hwnd, UINT msg, WPARAM wparam,
+ LPARAM lparam) {
+ if (msg == WM_DESTROY || (msg == WM_CHAR && wparam == VK_RETURN)) {
+ PostQuitMessage(0);
+ return 0;
+ }
+
+ return DefWindowProcA(hwnd, msg, wparam, lparam);
+}
+
+void D3dRenderer::Destroy() {
+ texture_ = NULL;
+ vertex_buffer_ = NULL;
+ d3d_device_ = NULL;
+ d3d_ = NULL;
+
+ if (hwnd_ != NULL) {
+ DestroyWindow(hwnd_);
+ assert(!IsWindow(hwnd_));
+ hwnd_ = NULL;
+ }
+}
+
+bool D3dRenderer::Init(const char* window_title) {
+ hwnd_ = CreateWindowA(kD3DClassName,
+ window_title,
+ WS_OVERLAPPEDWINDOW,
+ 0,
+ 0,
+ static_cast<int>(width_),
+ static_cast<int>(height_),
+ NULL,
+ NULL,
+ NULL,
+ NULL);
+
+ if (hwnd_ == NULL) {
+ Destroy();
+ return false;
+ }
+
+ d3d_ = Direct3DCreate9(D3D_SDK_VERSION);
+ if (d3d_ == NULL) {
+ Destroy();
+ return false;
+ }
+
+ D3DPRESENT_PARAMETERS d3d_params = {};
+
+ d3d_params.Windowed = TRUE;
+ d3d_params.SwapEffect = D3DSWAPEFFECT_COPY;
+
+ IDirect3DDevice9* d3d_device;
+ if (d3d_->CreateDevice(D3DADAPTER_DEFAULT,
+ D3DDEVTYPE_HAL,
+ hwnd_,
+ D3DCREATE_SOFTWARE_VERTEXPROCESSING,
+ &d3d_params,
+ &d3d_device) != D3D_OK) {
+ Destroy();
+ return false;
+ }
+ d3d_device_ = d3d_device;
+ d3d_device->Release();
+
+ IDirect3DVertexBuffer9* vertex_buffer;
+ const int kRectVertices = 4;
+ if (d3d_device_->CreateVertexBuffer(kRectVertices * sizeof(D3dCustomVertex),
+ 0,
+ D3DFVF_CUSTOMVERTEX,
+ D3DPOOL_MANAGED,
+ &vertex_buffer,
+ NULL) != D3D_OK) {
+ Destroy();
+ return false;
+ }
+ vertex_buffer_ = vertex_buffer;
+ vertex_buffer->Release();
+
+ d3d_device_->SetRenderState(D3DRS_CULLMODE, D3DCULL_NONE);
+ d3d_device_->SetRenderState(D3DRS_LIGHTING, FALSE);
+ Resize(width_, height_);
+
+ ShowWindow(hwnd_, SW_SHOWNOACTIVATE);
+ d3d_device_->Present(NULL, NULL, NULL, NULL);
+
+ return true;
+}
+
+D3dRenderer* D3dRenderer::Create(const char* window_title,
+ size_t width,
+ size_t height) {
+ static ATOM wc_atom = 0;
+ if (wc_atom == 0) {
+ WNDCLASSA wc = {};
+
+ wc.style = CS_HREDRAW | CS_VREDRAW;
+ wc.lpfnWndProc = WindowProc;
+ wc.hCursor = LoadCursor(NULL, IDC_ARROW);
+ wc.hbrBackground = reinterpret_cast<HBRUSH>(COLOR_WINDOW);
+ wc.lpszClassName = kD3DClassName;
+
+ wc_atom = RegisterClassA(&wc);
+ if (wc_atom == 0)
+ return false;
+ }
+
+ D3dRenderer* d3d_renderer = new D3dRenderer(width, height);
+ if (!d3d_renderer->Init(window_title)) {
+ delete d3d_renderer;
+ return NULL;
+ }
+
+ return d3d_renderer;
+}
+
+void D3dRenderer::Resize(size_t width, size_t height) {
+ width_ = width;
+ height_ = height;
+ IDirect3DTexture9* texture;
+
+ d3d_device_->CreateTexture(static_cast<UINT>(width_),
+ static_cast<UINT>(height_),
+ 1,
+ 0,
+ D3DFMT_A8R8G8B8,
+ D3DPOOL_MANAGED,
+ &texture,
+ NULL);
+ texture_ = texture;
+ texture->Release();
+
+ // Vertices for the video frame to be rendered to.
+ static const D3dCustomVertex rect[] = {
+ {-1.0f, -1.0f, 0.0f, 0.0f, 1.0f},
+ {-1.0f, 1.0f, 0.0f, 0.0f, 0.0f},
+ {1.0f, -1.0f, 0.0f, 1.0f, 1.0f},
+ {1.0f, 1.0f, 0.0f, 1.0f, 0.0f},
+ };
+
+ void* buf_data;
+ if (vertex_buffer_->Lock(0, 0, &buf_data, 0) != D3D_OK)
+ return;
+
+ memcpy(buf_data, &rect, sizeof(rect));
+ vertex_buffer_->Unlock();
+}
+
+void D3dRenderer::RenderFrame(const webrtc::VideoFrame& frame,
+ int /*render_delay_ms*/) {
+ if (static_cast<size_t>(frame.width()) != width_ ||
+ static_cast<size_t>(frame.height()) != height_) {
+ Resize(static_cast<size_t>(frame.width()),
+ static_cast<size_t>(frame.height()));
+ }
+
+ D3DLOCKED_RECT lock_rect;
+ if (texture_->LockRect(0, &lock_rect, NULL, 0) != D3D_OK)
+ return;
+
+ ConvertFromI420(frame, kARGB, 0, static_cast<uint8_t*>(lock_rect.pBits));
+ texture_->UnlockRect(0);
+
+ d3d_device_->BeginScene();
+ d3d_device_->SetFVF(D3DFVF_CUSTOMVERTEX);
+ d3d_device_->SetStreamSource(0, vertex_buffer_, 0, sizeof(D3dCustomVertex));
+ d3d_device_->SetTexture(0, texture_);
+ d3d_device_->DrawPrimitive(D3DPT_TRIANGLESTRIP, 0, 2);
+ d3d_device_->EndScene();
+
+ d3d_device_->Present(NULL, NULL, NULL, NULL);
+}
+} // namespace test
+} // namespace webrtc
diff --git a/webrtc/test/win/d3d_renderer.h b/webrtc/test/win/d3d_renderer.h
new file mode 100644
index 0000000000..46ce266460
--- /dev/null
+++ b/webrtc/test/win/d3d_renderer.h
@@ -0,0 +1,54 @@
+/*
+ * 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_ENGINE_TEST_COMMON_WIN_D3D_RENDERER_H_
+#define WEBRTC_VIDEO_ENGINE_TEST_COMMON_WIN_D3D_RENDERER_H_
+
+#include <Windows.h>
+#include <d3d9.h>
+#pragma comment(lib, "d3d9.lib") // located in DirectX SDK
+
+#include "webrtc/base/scoped_ref_ptr.h"
+#include "webrtc/test/video_renderer.h"
+#include "webrtc/typedefs.h"
+
+namespace webrtc {
+namespace test {
+
+class D3dRenderer : public VideoRenderer {
+ public:
+ static D3dRenderer* Create(const char* window_title, size_t width,
+ size_t height);
+ virtual ~D3dRenderer();
+
+ void RenderFrame(const webrtc::VideoFrame& frame, int delta) override;
+ bool IsTextureSupported() const override { return false; }
+
+ private:
+ D3dRenderer(size_t width, size_t height);
+
+ static LRESULT WINAPI WindowProc(HWND hwnd, UINT msg, WPARAM wparam,
+ LPARAM lparam);
+ bool Init(const char* window_title);
+ void Resize(size_t width, size_t height);
+ void Destroy();
+
+ size_t width_, height_;
+
+ HWND hwnd_;
+ rtc::scoped_refptr<IDirect3D9> d3d_;
+ rtc::scoped_refptr<IDirect3DDevice9> d3d_device_;
+
+ rtc::scoped_refptr<IDirect3DTexture9> texture_;
+ rtc::scoped_refptr<IDirect3DVertexBuffer9> vertex_buffer_;
+};
+} // namespace test
+} // namespace webrtc
+
+#endif // WEBRTC_VIDEO_ENGINE_TEST_COMMON_WIN_D3D_RENDERER_H_
diff --git a/webrtc/test/win/run_loop_win.cc b/webrtc/test/win/run_loop_win.cc
new file mode 100644
index 0000000000..ec29cc5a67
--- /dev/null
+++ b/webrtc/test/win/run_loop_win.cc
@@ -0,0 +1,33 @@
+/*
+ * 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/run_loop.h"
+
+#include <assert.h>
+
+#include <conio.h>
+#include <stdio.h>
+#include <Windows.h>
+
+namespace webrtc {
+namespace test {
+
+void PressEnterToContinue() {
+ puts(">> Press ENTER to continue...");
+
+ MSG msg;
+ BOOL ret;
+ while ((ret = GetMessage(&msg, NULL, 0, 0)) != 0) {
+ assert(ret != -1);
+ TranslateMessage(&msg);
+ DispatchMessage(&msg);
+ }
+}
+} // namespace test
+} // namespace webrtc