Implement conference-mode temporal-layer screencast.

Renames VideoStream::temporal_layers to temporal_layer_thresholds_bps to
convey that it contains thresholds needed to ramp up between them (1
threshold -> 2 temporal layers, etc.).

R=mflodman@webrtc.org, stefan@webrtc.org
BUG=1788,1667

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

git-svn-id: http://webrtc.googlecode.com/svn/trunk@7578 4adac7df-926f-26a2-2b94-8c16560cd09d
diff --git a/talk/media/webrtc/webrtcvideoengine2.cc b/talk/media/webrtc/webrtcvideoengine2.cc
index d10870b..d902718 100644
--- a/talk/media/webrtc/webrtcvideoengine2.cc
+++ b/talk/media/webrtc/webrtcvideoengine2.cc
@@ -118,6 +118,8 @@
 
 static const int kDefaultRtcpReceiverReportSsrc = 1;
 
+static const int kConferenceModeTemporalLayerBitrateBps = 100000;
+
 // External video encoders are given payloads 120-127. This also means that we
 // only support up to 8 external payload types.
 static const int kExternalVideoPayloadTypeBase = 120;
@@ -1740,6 +1742,14 @@
   encoder_config.streams = encoder_factory_->CreateVideoStreams(
       codec, parameters_.options, parameters_.config.rtp.ssrcs.size());
 
+  // Conference mode screencast uses 2 temporal layers split at 100kbit.
+  if (parameters_.options.conference_mode.GetWithDefaultIfUnset(false) &&
+      is_screencast && encoder_config.streams.size() == 1) {
+    encoder_config.streams[0].temporal_layer_thresholds_bps.clear();
+    encoder_config.streams[0].temporal_layer_thresholds_bps.push_back(
+        kConferenceModeTemporalLayerBitrateBps);
+  }
+
   bool stream_reconfigured = stream_->ReconfigureVideoEncoder(encoder_config);
 
   encoder_factory_->DestroyVideoEncoderSettings(
diff --git a/talk/media/webrtc/webrtcvideoengine2_unittest.cc b/talk/media/webrtc/webrtcvideoengine2_unittest.cc
index c5ce503..7c8da13 100644
--- a/talk/media/webrtc/webrtcvideoengine2_unittest.cc
+++ b/talk/media/webrtc/webrtcvideoengine2_unittest.cc
@@ -1273,6 +1273,43 @@
 
   EXPECT_EQ(capture_format_hd.width, encoder_config.streams.front().width);
   EXPECT_EQ(capture_format_hd.height, encoder_config.streams.front().height);
+  EXPECT_TRUE(encoder_config.streams[0].temporal_layer_thresholds_bps.empty());
+
+  EXPECT_TRUE(channel_->SetCapturer(last_ssrc_, NULL));
+}
+
+TEST_F(WebRtcVideoChannel2Test,
+       ConferenceModeScreencastConfiguresTemporalLayer) {
+  static const int kConferenceScreencastTemporalBitrateBps = 100000;
+  VideoOptions options;
+  options.conference_mode.Set(true);
+  channel_->SetOptions(options);
+
+  AddSendStream();
+
+  cricket::FakeVideoCapturer capturer;
+  capturer.SetScreencast(true);
+  EXPECT_TRUE(channel_->SetCapturer(last_ssrc_, &capturer));
+  cricket::VideoFormat capture_format_hd =
+      capturer.GetSupportedFormats()->front();
+  EXPECT_EQ(cricket::CS_RUNNING, capturer.Start(capture_format_hd));
+
+  EXPECT_TRUE(channel_->SetSend(true));
+
+  EXPECT_TRUE(capturer.CaptureFrame());
+  ASSERT_EQ(1u, fake_call_->GetVideoSendStreams().size());
+  FakeVideoSendStream* send_stream = fake_call_->GetVideoSendStreams().front();
+
+  webrtc::VideoEncoderConfig encoder_config = send_stream->GetEncoderConfig();
+
+  // Verify screencast settings.
+  encoder_config = send_stream->GetEncoderConfig();
+  EXPECT_EQ(webrtc::VideoEncoderConfig::kScreenshare,
+            encoder_config.content_type);
+  ASSERT_EQ(1u, encoder_config.streams.size());
+  ASSERT_EQ(1u, encoder_config.streams[0].temporal_layer_thresholds_bps.size());
+  EXPECT_EQ(kConferenceScreencastTemporalBitrateBps,
+            encoder_config.streams[0].temporal_layer_thresholds_bps[0]);
 
   EXPECT_TRUE(channel_->SetCapturer(last_ssrc_, NULL));
 }
diff --git a/webrtc/config.cc b/webrtc/config.cc
index 3d205f1..70bd870 100644
--- a/webrtc/config.cc
+++ b/webrtc/config.cc
@@ -39,10 +39,10 @@
   ss << ", max_bitrate_bps:" << max_bitrate_bps;
   ss << ", max_qp: " << max_qp;
 
-  ss << ", temporal_layers: {";
-  for (size_t i = 0; i < temporal_layers.size(); ++i) {
-    ss << temporal_layers[i];
-    if (i != temporal_layers.size() - 1)
+  ss << ", temporal_layer_thresholds_bps: {";
+  for (size_t i = 0; i < temporal_layer_thresholds_bps.size(); ++i) {
+    ss << temporal_layer_thresholds_bps[i];
+    if (i != temporal_layer_thresholds_bps.size() - 1)
       ss << "}, {";
   }
   ss << '}';
diff --git a/webrtc/config.h b/webrtc/config.h
index af78d94..8ea2828 100644
--- a/webrtc/config.h
+++ b/webrtc/config.h
@@ -104,8 +104,17 @@
 
   int max_qp;
 
-  // Bitrate thresholds for enabling additional temporal layers.
-  std::vector<int> temporal_layers;
+  // Bitrate thresholds for enabling additional temporal layers. Since these are
+  // thresholds in between layers, we have one additional layer. One threshold
+  // gives two temporal layers, one below the threshold and one above, two give
+  // three, and so on.
+  // The VideoEncoder may redistribute bitrates over the temporal layers so a
+  // bitrate threshold of 100k and an estimate of 105k does not imply that we
+  // get 100k in one temporal layer and 5k in the other, just that the bitrate
+  // in the first temporal layer should not exceed 100k.
+  // TODO(pbos): Apart from a special case for two-layer screencast these
+  // thresholds are not propagated to the VideoEncoder. To be implemented.
+  std::vector<int> temporal_layer_thresholds_bps;
 };
 
 struct VideoEncoderConfig {
diff --git a/webrtc/video/video_send_stream.cc b/webrtc/video/video_send_stream.cc
index 7657250..d6ebc2b 100644
--- a/webrtc/video/video_send_stream.cc
+++ b/webrtc/video/video_send_stream.cc
@@ -306,12 +306,18 @@
   } else {
     video_codec.codecType = kVideoCodecGeneric;
   }
+
   switch (config.content_type) {
     case VideoEncoderConfig::kRealtimeVideo:
       video_codec.mode = kRealtimeVideo;
       break;
     case VideoEncoderConfig::kScreenshare:
       video_codec.mode = kScreensharing;
+      if (config.streams.size() == 1 &&
+          config.streams[0].temporal_layer_thresholds_bps.size() == 1) {
+        video_codec.targetBitrate =
+            config.streams[0].temporal_layer_thresholds_bps[0] / 1000;
+      }
       break;
   }
 
@@ -327,7 +333,8 @@
                                           config.encoder_specific_settings);
     }
     video_codec.codecSpecific.VP8.numberOfTemporalLayers =
-        static_cast<unsigned char>(streams.back().temporal_layers.size());
+        static_cast<unsigned char>(
+            streams.back().temporal_layer_thresholds_bps.size() + 1);
   } else {
     // TODO(pbos): Support encoder_settings codec-agnostically.
     assert(config.encoder_specific_settings == NULL);
@@ -360,8 +367,8 @@
     sim_stream->targetBitrate = streams[i].target_bitrate_bps / 1000;
     sim_stream->maxBitrate = streams[i].max_bitrate_bps / 1000;
     sim_stream->qpMax = streams[i].max_qp;
-    sim_stream->numberOfTemporalLayers =
-        static_cast<unsigned char>(streams[i].temporal_layers.size());
+    sim_stream->numberOfTemporalLayers = static_cast<unsigned char>(
+        streams[i].temporal_layer_thresholds_bps.size() + 1);
 
     video_codec.width = std::max(video_codec.width,
                                  static_cast<unsigned short>(streams[i].width));
diff --git a/webrtc/video/video_send_stream_tests.cc b/webrtc/video/video_send_stream_tests.cc
index 79b1cd5..b863957 100644
--- a/webrtc/video/video_send_stream_tests.cc
+++ b/webrtc/video/video_send_stream_tests.cc
@@ -201,7 +201,7 @@
 
     virtual void PerformTest() OVERRIDE {
       EXPECT_EQ(kEventSignaled, Wait())
-          << "Timed out while waiting single RTP packet.";
+          << "Timed out while waiting for a single RTP packet.";
     }
 
     class DelayedEncoder : public test::FakeEncoder {
@@ -1440,8 +1440,8 @@
       send_config->encoder_settings.payload_name = "VP8";
 
       for (size_t i = 0; i < encoder_config->streams.size(); ++i) {
-        encoder_config->streams[i].temporal_layers.resize(
-            kNumberOfTemporalLayers);
+        encoder_config->streams[i].temporal_layer_thresholds_bps.resize(
+            kNumberOfTemporalLayers - 1);
       }
 
       encoder_config->encoder_specific_settings = &vp8_settings_;
@@ -1550,4 +1550,44 @@
   RunBaseTest(&test);
 }
 
+TEST_F(VideoSendStreamTest, TranslatesTwoLayerScreencastToTargetBitrate) {
+  static const int kScreencastTargetBitrateKbps = 200;
+  class ScreencastTargetBitrateTest : public test::SendTest,
+                                      public test::FakeEncoder {
+   public:
+    ScreencastTargetBitrateTest()
+        : SendTest(kDefaultTimeoutMs),
+          test::FakeEncoder(Clock::GetRealTimeClock()) {}
+
+   private:
+    virtual int32_t InitEncode(const VideoCodec* config,
+                               int32_t number_of_cores,
+                               uint32_t max_payload_size) {
+      EXPECT_EQ(static_cast<unsigned int>(kScreencastTargetBitrateKbps),
+                config->targetBitrate);
+      observation_complete_->Set();
+      return test::FakeEncoder::InitEncode(
+          config, number_of_cores, max_payload_size);
+    }
+    virtual void ModifyConfigs(
+        VideoSendStream::Config* send_config,
+        std::vector<VideoReceiveStream::Config>* receive_configs,
+        VideoEncoderConfig* encoder_config) OVERRIDE {
+      send_config->encoder_settings.encoder = this;
+      EXPECT_EQ(1u, encoder_config->streams.size());
+      EXPECT_TRUE(
+          encoder_config->streams[0].temporal_layer_thresholds_bps.empty());
+      encoder_config->streams[0].temporal_layer_thresholds_bps.push_back(
+          kScreencastTargetBitrateKbps * 1000);
+      encoder_config->content_type = VideoEncoderConfig::kScreenshare;
+    }
+
+    virtual void PerformTest() OVERRIDE {
+      EXPECT_EQ(kEventSignaled, Wait())
+          << "Timed out while waiting for the encoder to be initialized.";
+    }
+  } test;
+
+  RunBaseTest(&test);
+}
 }  // namespace webrtc