Adds two tests for verifying padding and ramp-up behavior.

BUG=1837
R=pbos@webrtc.org

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

git-svn-id: http://webrtc.googlecode.com/svn/trunk@4591 4adac7df-926f-26a2-2b94-8c16560cd09d
diff --git a/webrtc/video_engine/internal/video_send_stream.cc b/webrtc/video_engine/internal/video_send_stream.cc
index 7d9e680..da72877 100644
--- a/webrtc/video_engine/internal/video_send_stream.cc
+++ b/webrtc/video_engine/internal/video_send_stream.cc
@@ -16,6 +16,7 @@
 #include "webrtc/video_engine/include/vie_base.h"
 #include "webrtc/video_engine/include/vie_capture.h"
 #include "webrtc/video_engine/include/vie_codec.h"
+#include "webrtc/video_engine/include/vie_external_codec.h"
 #include "webrtc/video_engine/include/vie_network.h"
 #include "webrtc/video_engine/include/vie_rtp_rtcp.h"
 #include "webrtc/video_engine/new_include/video_send_stream.h"
@@ -77,7 +78,7 @@
                                  bool overuse_detection,
                                  webrtc::VideoEngine* video_engine,
                                  const newapi::VideoSendStream::Config& config)
-    : transport_(transport), config_(config) {
+    : transport_(transport), config_(config), external_codec_(NULL) {
 
   if (config_.codec.numberOfSimulcastStreams > 0) {
     assert(config_.rtp.ssrcs.size() == config_.codec.numberOfSimulcastStreams);
@@ -92,9 +93,17 @@
   rtp_rtcp_ = ViERTP_RTCP::GetInterface(video_engine);
   assert(rtp_rtcp_ != NULL);
 
-  assert(config_.rtp.ssrcs.size() == 1);
-  rtp_rtcp_->SetLocalSSRC(channel_, config_.rtp.ssrcs[0]);
+  if (config_.rtp.ssrcs.size() == 1) {
+    rtp_rtcp_->SetLocalSSRC(channel_, config_.rtp.ssrcs[0]);
+  } else {
+    for (size_t i = 0; i < config_.rtp.ssrcs.size(); ++i) {
+      rtp_rtcp_->SetLocalSSRC(channel_, config_.rtp.ssrcs[i],
+                              kViEStreamTypeNormal, i);
+    }
+  }
   rtp_rtcp_->SetNACKStatus(channel_, config_.rtp.nack.rtp_history_ms > 0);
+  rtp_rtcp_->SetTransmissionSmoothingStatus(channel_, config_.pacing);
+  rtp_rtcp_->SetSendTimestampOffsetStatus(channel_, true, 1);
 
   capture_ = ViECapture::GetInterface(video_engine);
   capture_->AllocateExternalCaptureDevice(capture_id_, external_capture_);
@@ -105,6 +114,15 @@
 
   network_->RegisterSendTransport(channel_, *this);
 
+  if (config.encoder) {
+    external_codec_ = ViEExternalCodec::GetInterface(video_engine);
+    if (external_codec_->RegisterExternalSendCodec(
+        channel_, config.codec.plType, config.encoder,
+        config.internal_source) != 0) {
+      abort();
+    }
+  }
+
   codec_ = ViECodec::GetInterface(video_engine);
   if (codec_->SetSendCodec(channel_, config_.codec) != 0) {
     abort();
@@ -126,9 +144,16 @@
   capture_->DisconnectCaptureDevice(channel_);
   capture_->ReleaseCaptureDevice(capture_id_);
 
+  if (external_codec_) {
+    external_codec_->DeRegisterExternalSendCodec(channel_,
+                                                 config_.codec.plType);
+  }
+
   video_engine_base_->Release();
   capture_->Release();
   codec_->Release();
+  if (external_codec_)
+    external_codec_->Release();
   network_->Release();
   rtp_rtcp_->Release();
 }
@@ -199,7 +224,7 @@
   assert(length >= 0);
   bool success = transport_->SendRTP(static_cast<const uint8_t*>(packet),
                                      static_cast<size_t>(length));
-  return success ? 0 : -1;
+  return success ? length : -1;
 }
 
 int VideoSendStream::SendRTCPPacket(int /*channel*/,
@@ -208,7 +233,7 @@
   assert(length >= 0);
   bool success = transport_->SendRTCP(static_cast<const uint8_t*>(packet),
                                       static_cast<size_t>(length));
-  return success ? 0 : -1;
+  return success ? length : -1;
 }
 
 bool VideoSendStream::DeliverRtcp(const uint8_t* packet, size_t length) {
diff --git a/webrtc/video_engine/internal/video_send_stream.h b/webrtc/video_engine/internal/video_send_stream.h
index 686e5d5..ac0f55b 100644
--- a/webrtc/video_engine/internal/video_send_stream.h
+++ b/webrtc/video_engine/internal/video_send_stream.h
@@ -24,6 +24,7 @@
 class ViECapture;
 class ViECodec;
 class ViEExternalCapture;
+class ViEExternalCodec;
 class ViENetwork;
 class ViERTP_RTCP;
 
@@ -74,6 +75,7 @@
   ViECapture* capture_;
   ViECodec* codec_;
   ViEExternalCapture* external_capture_;
+  ViEExternalCodec* external_codec_;
   ViENetwork* network_;
   ViERTP_RTCP* rtp_rtcp_;
 
diff --git a/webrtc/video_engine/new_include/video_send_stream.h b/webrtc/video_engine/new_include/video_send_stream.h
index 75edad3..86ecc78 100644
--- a/webrtc/video_engine/new_include/video_send_stream.h
+++ b/webrtc/video_engine/new_include/video_send_stream.h
@@ -81,6 +81,7 @@
           encoder(NULL),
           internal_source(false),
           target_delay_ms(0),
+          pacing(false),
           stats_callback(NULL),
           start_state(NULL) {}
     VideoCodec codec;
@@ -138,6 +139,10 @@
     // used for streaming instead of a real-time call.
     int target_delay_ms;
 
+    // True if network a send-side packet buffer should be used to pace out
+    // packets onto the network.
+    bool pacing;
+
     // Callback for periodically receiving send stats.
     StatsCallback* stats_callback;
 
diff --git a/webrtc/video_engine/test/common/fake_encoder.cc b/webrtc/video_engine/test/common/fake_encoder.cc
new file mode 100644
index 0000000..d9674b3
--- /dev/null
+++ b/webrtc/video_engine/test/common/fake_encoder.cc
@@ -0,0 +1,98 @@
+/*
+ *  Copyright (c) 2013 The WebRTC project authors. All Rights Reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "webrtc/video_engine/test/common/fake_encoder.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace webrtc {
+
+FakeEncoder::FakeEncoder(Clock* clock)
+    : clock_(clock),
+      callback_(NULL),
+      target_bitrate_kbps_(0),
+      last_encode_time_ms_(0) {
+  memset(encoded_buffer_, 0, sizeof(encoded_buffer_));
+}
+
+FakeEncoder::~FakeEncoder() {}
+
+int32_t FakeEncoder::InitEncode(const VideoCodec* config,
+                                int32_t number_of_cores,
+                                uint32_t max_payload_size) {
+  config_ = *config;
+  target_bitrate_kbps_ = config_.startBitrate;
+  return 0;
+}
+
+int32_t FakeEncoder::Encode(
+    const I420VideoFrame& input_image,
+    const CodecSpecificInfo* codec_specific_info,
+    const std::vector<VideoFrameType>* frame_types) {
+  assert(config_.maxFramerate > 0);
+  int delta_since_last_encode = 1000 / config_.maxFramerate;
+  int64_t time_now_ms = clock_->TimeInMilliseconds();
+  if (last_encode_time_ms_ > 0) {
+    // For all frames but the first we can estimate the display time by looking
+    // at the display time of the previous frame.
+    delta_since_last_encode = time_now_ms - last_encode_time_ms_;
+  }
+
+  int bits_available = target_bitrate_kbps_ * delta_since_last_encode;
+  last_encode_time_ms_ = time_now_ms;
+
+  for (int i = 0; i < config_.numberOfSimulcastStreams; ++i) {
+    CodecSpecificInfo specifics;
+    memset(&specifics, 0, sizeof(specifics));
+    specifics.codecType = kVideoCodecVP8;
+    specifics.codecSpecific.VP8.simulcastIdx = i;
+    int min_stream_bits = config_.simulcastStream[i].minBitrate *
+        delta_since_last_encode;
+    int max_stream_bits = config_.simulcastStream[i].maxBitrate *
+        delta_since_last_encode;
+    int stream_bits = (bits_available > max_stream_bits) ? max_stream_bits :
+        bits_available;
+    int stream_bytes = (stream_bits + 7) / 8;
+    EXPECT_LT(stream_bytes, kMaxFrameSizeBytes);
+    if (stream_bytes > kMaxFrameSizeBytes)
+      return -1;
+
+    EncodedImage encoded(encoded_buffer_, stream_bytes, kMaxFrameSizeBytes);
+    encoded._timeStamp = input_image.timestamp();
+    encoded.capture_time_ms_ = input_image.render_time_ms();
+    if (min_stream_bits > bits_available) {
+      encoded._length = 0;
+      encoded._frameType = kSkipFrame;
+    }
+    if (callback_->Encoded(encoded, &specifics, NULL) != 0)
+      return -1;
+
+    bits_available -= encoded._length * 8;
+  }
+  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, int rtt) {
+  return 0;
+}
+
+int32_t FakeEncoder::SetRates(uint32_t new_target_bitrate, uint32_t framerate) {
+  target_bitrate_kbps_ = new_target_bitrate;
+  return 0;
+}
+}  // namespace webrtc
diff --git a/webrtc/video_engine/test/common/fake_encoder.h b/webrtc/video_engine/test/common/fake_encoder.h
new file mode 100644
index 0000000..8a14f88
--- /dev/null
+++ b/webrtc/video_engine/test/common/fake_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_VIDEO_ENGINE_TEST_COMMON_FAKE_ENCODER_H_
+#define WEBRTC_VIDEO_ENGINE_TEST_COMMON_FAKE_ENCODER_H_
+
+#include <vector>
+
+#include "webrtc/modules/video_coding/codecs/interface/video_codec_interface.h"
+#include "webrtc/system_wrappers/interface/clock.h"
+
+namespace webrtc {
+
+class FakeEncoder : public VideoEncoder {
+ public:
+  explicit FakeEncoder(Clock* clock);
+
+  virtual ~FakeEncoder();
+
+  virtual int32_t InitEncode(const VideoCodec* config,
+                             int32_t number_of_cores,
+                             uint32_t max_payload_size) OVERRIDE;
+
+  virtual int32_t Encode(
+     const I420VideoFrame& input_image,
+     const CodecSpecificInfo* codec_specific_info,
+     const std::vector<VideoFrameType>* frame_types) OVERRIDE;
+
+  virtual int32_t RegisterEncodeCompleteCallback(
+      EncodedImageCallback* callback) OVERRIDE;
+
+  virtual int32_t Release() OVERRIDE;
+
+  virtual int32_t SetChannelParameters(uint32_t packet_loss, int rtt) OVERRIDE;
+
+  virtual int32_t SetRates(uint32_t new_target_bitrate,
+                           uint32_t framerate) OVERRIDE;
+
+ private:
+  enum { kMaxFrameSizeBytes = 100000 };
+
+  Clock* clock_;
+  VideoCodec config_;
+  EncodedImageCallback* callback_;
+  int target_bitrate_kbps_;
+  int64_t last_encode_time_ms_;
+  uint8_t encoded_buffer_[kMaxFrameSizeBytes];
+};
+}  // namespace webrtc
+
+#endif  // WEBRTC_VIDEO_ENGINE_TEST_COMMON_FAKE_ENCODER_H_
diff --git a/webrtc/video_engine/test/engine_tests.cc b/webrtc/video_engine/test/engine_tests.cc
index 71c3c81..e564912 100644
--- a/webrtc/video_engine/test/engine_tests.cc
+++ b/webrtc/video_engine/test/engine_tests.cc
@@ -13,13 +13,17 @@
 
 #include "testing/gtest/include/gtest/gtest.h"
 
+#include "webrtc/modules/remote_bitrate_estimator/include/remote_bitrate_estimator.h"
+#include "webrtc/modules/rtp_rtcp/interface/receive_statistics.h"
 #include "webrtc/modules/rtp_rtcp/interface/rtp_header_parser.h"
+#include "webrtc/modules/rtp_rtcp/interface/rtp_rtcp.h"
 #include "webrtc/modules/rtp_rtcp/source/rtcp_utility.h"
 #include "webrtc/system_wrappers/interface/critical_section_wrapper.h"
 #include "webrtc/system_wrappers/interface/scoped_ptr.h"
 #include "webrtc/system_wrappers/interface/event_wrapper.h"
 #include "webrtc/video_engine/new_include/video_call.h"
 #include "webrtc/video_engine/test/common/direct_transport.h"
+#include "webrtc/video_engine/test/common/fake_encoder.h"
 #include "webrtc/video_engine/test/common/frame_generator.h"
 #include "webrtc/video_engine/test/common/frame_generator_capturer.h"
 #include "webrtc/video_engine/test/common/generate_ssrcs.h"
@@ -27,6 +31,199 @@
 
 namespace webrtc {
 
+class StreamObserver : public newapi::Transport, public RemoteBitrateObserver {
+ public:
+  typedef std::map<uint32_t, int> BytesSentMap;
+  StreamObserver(int num_expected_ssrcs, newapi::Transport* feedback_transport,
+                 Clock* clock)
+      : critical_section_(CriticalSectionWrapper::CreateCriticalSection()),
+        all_ssrcs_sent_(EventWrapper::Create()),
+        rtp_parser_(RtpHeaderParser::Create()),
+        feedback_transport_(new TransportWrapper(feedback_transport)),
+        receive_stats_(ReceiveStatistics::Create(clock)),
+        clock_(clock),
+        num_expected_ssrcs_(num_expected_ssrcs) {
+    // Ideally we would only have to instantiate an RtcpSender, an
+    // RtpHeaderParser and a RemoteBitrateEstimator here, but due to the current
+    // state of the RTP module we need a full module and receive statistics to
+    // be able to produce an RTCP with REMB.
+    RtpRtcp::Configuration config;
+    config.receive_statistics = receive_stats_.get();
+    config.outgoing_transport = feedback_transport_.get();
+    rtp_rtcp_.reset(RtpRtcp::CreateRtpRtcp(config));
+    rtp_rtcp_->SetREMBStatus(true);
+    rtp_rtcp_->SetRTCPStatus(kRtcpNonCompound);
+    rtp_parser_->RegisterRtpHeaderExtension(kRtpExtensionTransmissionTimeOffset,
+                                            1);
+    AbsoluteSendTimeRemoteBitrateEstimatorFactory rbe_factory;
+    remote_bitrate_estimator_.reset(rbe_factory.Create(this, clock));
+  }
+
+  virtual void OnReceiveBitrateChanged(const std::vector<unsigned int>& ssrcs,
+                                       unsigned int bitrate) {
+    CriticalSectionScoped lock(critical_section_.get());
+    if (ssrcs.size() == num_expected_ssrcs_ && bitrate >= kExpectedBitrateBps)
+      all_ssrcs_sent_->Set();
+    rtp_rtcp_->SetREMBData(bitrate, static_cast<uint8_t>(ssrcs.size()),
+                           &ssrcs[0]);
+    rtp_rtcp_->Process();
+  }
+
+  virtual bool SendRTP(const uint8_t* packet, size_t length) OVERRIDE {
+    CriticalSectionScoped lock(critical_section_.get());
+    RTPHeader header;
+    EXPECT_TRUE(rtp_parser_->Parse(packet, static_cast<int>(length),
+                                   &header));
+    receive_stats_->IncomingPacket(header, length, false, true);
+    rtp_rtcp_->SetRemoteSSRC(header.ssrc);
+    remote_bitrate_estimator_->IncomingPacket(clock_->TimeInMilliseconds(),
+                                              static_cast<int>(length - 12),
+                                              header);
+    if (remote_bitrate_estimator_->TimeUntilNextProcess() <= 0) {
+      remote_bitrate_estimator_->Process();
+    }
+    return true;
+  }
+
+  virtual bool SendRTCP(const uint8_t* packet, size_t length) OVERRIDE {
+    return true;
+  }
+
+  EventTypeWrapper Wait() {
+    return all_ssrcs_sent_->Wait(120 * 1000);
+  }
+
+ private:
+  class TransportWrapper : public webrtc::Transport {
+   public:
+    explicit TransportWrapper(newapi::Transport* new_transport)
+        : new_transport_(new_transport) {}
+
+    virtual int SendPacket(int channel, const void *data, int len) OVERRIDE {
+      return new_transport_->SendRTP(static_cast<const uint8_t*>(data), len) ?
+          len : -1;
+    }
+
+    virtual int SendRTCPPacket(int channel, const void *data,
+                               int len) OVERRIDE {
+      return new_transport_->SendRTCP(static_cast<const uint8_t*>(data), len) ?
+          len : -1;
+    }
+
+   private:
+    newapi::Transport* new_transport_;
+  };
+
+  static const unsigned int kExpectedBitrateBps = 1200000;
+
+  scoped_ptr<CriticalSectionWrapper> critical_section_;
+  scoped_ptr<EventWrapper> all_ssrcs_sent_;
+  scoped_ptr<RtpHeaderParser> rtp_parser_;
+  scoped_ptr<RtpRtcp> rtp_rtcp_;
+  scoped_ptr<TransportWrapper> feedback_transport_;
+  scoped_ptr<ReceiveStatistics> receive_stats_;
+  scoped_ptr<RemoteBitrateEstimator> remote_bitrate_estimator_;
+  Clock* clock_;
+  const size_t num_expected_ssrcs_;
+};
+
+class RampUpTest : public ::testing::TestWithParam<bool> {
+ public:
+  virtual void SetUp() {
+    reserved_ssrcs_.clear();
+  }
+
+  static void SetCodecStreamSettings(VideoCodec* video_codec) {
+    video_codec->width = 1280;
+    video_codec->height = 720;
+    video_codec->startBitrate = 300;
+    video_codec->minBitrate = 50;
+    video_codec->maxBitrate = 1800;
+
+    video_codec->numberOfSimulcastStreams = 3;
+    video_codec->simulcastStream[0].width = 320;
+    video_codec->simulcastStream[0].height = 180;
+    video_codec->simulcastStream[0].numberOfTemporalLayers = 0;
+    video_codec->simulcastStream[0].maxBitrate = 150;
+    video_codec->simulcastStream[0].targetBitrate = 150;
+    video_codec->simulcastStream[0].minBitrate = 50;
+    video_codec->simulcastStream[0].qpMax = video_codec->qpMax;
+
+    video_codec->simulcastStream[1].width = 640;
+    video_codec->simulcastStream[1].height = 360;
+    video_codec->simulcastStream[1].numberOfTemporalLayers = 0;
+    video_codec->simulcastStream[1].maxBitrate = 500;
+    video_codec->simulcastStream[1].targetBitrate = 500;
+    video_codec->simulcastStream[1].minBitrate = 150;
+    video_codec->simulcastStream[1].qpMax = video_codec->qpMax;
+
+    video_codec->simulcastStream[2].width = 1280;
+    video_codec->simulcastStream[2].height = 720;
+    video_codec->simulcastStream[2].numberOfTemporalLayers = 0;
+    video_codec->simulcastStream[2].maxBitrate = 1200;
+    video_codec->simulcastStream[2].targetBitrate = 1200;
+    video_codec->simulcastStream[2].minBitrate = 600;
+    video_codec->simulcastStream[2].qpMax = video_codec->qpMax;
+  }
+
+ protected:
+  std::map<uint32_t, bool> reserved_ssrcs_;
+};
+
+TEST_P(RampUpTest, RampUpWithPadding) {
+  test::DirectTransport receiver_transport;
+  StreamObserver stream_observer(3, &receiver_transport,
+                                 Clock::GetRealTimeClock());
+  newapi::VideoCall::Config call_config(&stream_observer);
+  scoped_ptr<newapi::VideoCall> call(newapi::VideoCall::Create(call_config));
+  newapi::VideoSendStream::Config send_config =
+      call->GetDefaultSendConfig();
+
+  receiver_transport.SetReceiver(call->Receiver());
+
+  FakeEncoder encoder(Clock::GetRealTimeClock());
+  send_config.encoder = &encoder;
+  send_config.internal_source = false;
+  SetCodecStreamSettings(&send_config.codec);
+  send_config.codec.plType = 100;
+  send_config.pacing = GetParam();
+
+  test::GenerateRandomSsrcs(&send_config, &reserved_ssrcs_);
+
+  newapi::VideoSendStream* send_stream =
+      call->CreateSendStream(send_config);
+
+  newapi::VideoReceiveStream::Config receive_config;
+  receive_config.rtp.ssrc = send_config.rtp.ssrcs[0];
+  receive_config.rtp.nack.rtp_history_ms =
+      send_config.rtp.nack.rtp_history_ms;
+  newapi::VideoReceiveStream* receive_stream = call->CreateReceiveStream(
+      receive_config);
+
+  scoped_ptr<test::FrameGeneratorCapturer> frame_generator_capturer(
+      test::FrameGeneratorCapturer::Create(
+          send_stream->Input(),
+          test::FrameGenerator::Create(
+              send_config.codec.width, send_config.codec.height,
+              Clock::GetRealTimeClock()),
+          30));
+
+  receive_stream->StartReceive();
+  send_stream->StartSend();
+  frame_generator_capturer->Start();
+
+  EXPECT_EQ(kEventSignaled, stream_observer.Wait());
+
+  frame_generator_capturer->Stop();
+  send_stream->StopSend();
+  receive_stream->StopReceive();
+
+  call->DestroyReceiveStream(receive_stream);
+  call->DestroySendStream(send_stream);
+}
+
+INSTANTIATE_TEST_CASE_P(RampUpTest, RampUpTest, ::testing::Bool());
+
 struct EngineTestParams {
   size_t width, height;
   struct {
diff --git a/webrtc/video_engine/test/tests.gypi b/webrtc/video_engine/test/tests.gypi
index 4a76b22..f0e8e5f 100644
--- a/webrtc/video_engine/test/tests.gypi
+++ b/webrtc/video_engine/test/tests.gypi
@@ -14,6 +14,8 @@
       'sources': [
         'common/direct_transport.cc',
         'common/direct_transport.h',
+        'common/fake_encoder.cc',
+        'common/fake_encoder.h',
         'common/file_capturer.cc',
         'common/file_capturer.h',
         'common/flags.cc',