Media engine and channel support for per-channel dscp values, specified by RtpParameter
 - Similar to network priority
 - Still requires MediaConfig.enable_dscp = true (i.e. googDscp == true to peerconnection)
 - Needs followups 1) Specify value in chrome renderer js idl 2) disable audio bwe when value differs from video  3)remove googDscp guard

Bug: webrtc:5008
Change-Id: Ibdcbb1183f0ca2ae85e3bced6d0aedbccae3ced4
Reviewed-on: https://webrtc-review.googlesource.com/c/93560
Commit-Queue: Tim Haloun <thaloun@chromium.org>
Reviewed-by: Stefan Holmer <stefan@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#25280}
diff --git a/api/rtpparameters.h b/api/rtpparameters.h
index 678ac02..377fb78 100644
--- a/api/rtpparameters.h
+++ b/api/rtpparameters.h
@@ -404,6 +404,14 @@
   // bitrate priority.
   double bitrate_priority = kDefaultBitratePriority;
 
+  // The relative DiffServ Code Point priority for this encoding, allowing
+  // packets to be marked relatively higher or lower without affecting
+  // bandwidth allocations. See https://w3c.github.io/webrtc-dscp-exp/ . NB
+  // we follow chromium's translation of the allowed string enum values for
+  // this field to 1.0, 0.5, et cetera, similar to bitrate_priority above.
+  // TODO(http://crbug.com/webrtc/8630): Implement this per encoding parameter.
+  double network_priority = kDefaultBitratePriority;
+
   // Indicates the preferred duration of media represented by a packet in
   // milliseconds for this encoding. If set, this will take precedence over the
   // ptime set in the RtpCodecParameters. This could happen if SDP negotiation
@@ -471,7 +479,8 @@
   bool operator==(const RtpEncodingParameters& o) const {
     return ssrc == o.ssrc && codec_payload_type == o.codec_payload_type &&
            fec == o.fec && rtx == o.rtx && dtx == o.dtx &&
-           bitrate_priority == o.bitrate_priority && ptime == o.ptime &&
+           bitrate_priority == o.bitrate_priority &&
+           network_priority == o.network_priority && ptime == o.ptime &&
            max_bitrate_bps == o.max_bitrate_bps &&
            min_bitrate_bps == o.min_bitrate_bps &&
            max_framerate == o.max_framerate &&
diff --git a/audio/audio_send_stream.cc b/audio/audio_send_stream.cc
index 39cb4e8..c2b0d1a 100644
--- a/audio/audio_send_stream.cc
+++ b/audio/audio_send_stream.cc
@@ -298,6 +298,7 @@
       FindExtensionIds(config_.rtp.extensions).transport_sequence_number != 0 &&
       !webrtc::field_trial::IsEnabled("WebRTC-Audio-ForceNoTWCC");
   if (config_.min_bitrate_bps != -1 && config_.max_bitrate_bps != -1 &&
+      !config_.has_dscp &&
       (has_transport_sequence_number ||
        !webrtc::field_trial::IsEnabled("WebRTC-Audio-SendSideBwe") ||
        webrtc::field_trial::IsEnabled("WebRTC-Audio-ABWENoTWCC"))) {
@@ -713,13 +714,16 @@
 
   bool has_transport_sequence_number = new_transport_seq_num_id != 0;
   if (new_config.min_bitrate_bps != -1 && new_config.max_bitrate_bps != -1 &&
+      !new_config.has_dscp &&
       (has_transport_sequence_number ||
        !webrtc::field_trial::IsEnabled("WebRTC-Audio-SendSideBwe"))) {
+    stream->transport_->packet_sender()->SetAccountForAudioPackets(true);
     stream->ConfigureBitrateObserver(
         new_config.min_bitrate_bps, new_config.max_bitrate_bps,
         new_config.bitrate_priority, has_transport_sequence_number);
     stream->rtp_rtcp_module_->SetAsPartOfAllocation(true);
   } else {
+    stream->transport_->packet_sender()->SetAccountForAudioPackets(false);
     stream->RemoveBitrateObserver();
     stream->rtp_rtcp_module_->SetAsPartOfAllocation(false);
   }
diff --git a/call/audio_send_stream.h b/call/audio_send_stream.h
index a659dd6..61d2f5c 100644
--- a/call/audio_send_stream.h
+++ b/call/audio_send_stream.h
@@ -100,6 +100,7 @@
     int max_bitrate_bps = -1;
 
     double bitrate_priority = 1.0;
+    bool has_dscp = false;
 
     // Defines whether to turn on audio network adaptor, and defines its config
     // string.
diff --git a/media/base/mediachannel.cc b/media/base/mediachannel.cc
index f1471b6..a5dadd2 100644
--- a/media/base/mediachannel.cc
+++ b/media/base/mediachannel.cc
@@ -28,7 +28,7 @@
   rtc::CritScope cs(&network_interface_crit_);
   network_interface_ = iface;
   media_transport_ = media_transport;
-  SetDscp(enable_dscp_ ? PreferredDscp() : rtc::DSCP_DEFAULT);
+  UpdateDscp();
 }
 
 int MediaChannel::GetRtpSendTimeExtnId() const {
diff --git a/media/base/mediachannel.h b/media/base/mediachannel.h
index 9948d96e..56edab7 100644
--- a/media/base/mediachannel.h
+++ b/media/base/mediachannel.h
@@ -267,9 +267,10 @@
 
   bool DscpEnabled() const { return enable_dscp_; }
 
- private:
   // This method sets DSCP |value| on both RTP and RTCP channels.
-  int SetDscp(rtc::DiffServCodePoint value) {
+  int UpdateDscp() {
+    rtc::DiffServCodePoint value =
+        enable_dscp_ ? PreferredDscp() : rtc::DSCP_DEFAULT;
     int ret;
     ret = SetOption(NetworkInterface::ST_RTP, rtc::Socket::OPT_DSCP, value);
     if (ret == 0) {
@@ -278,6 +279,7 @@
     return ret;
   }
 
+ private:
   bool DoSendPacket(rtc::CopyOnWriteBuffer* packet,
                     bool rtcp,
                     const rtc::PacketOptions& options) {
diff --git a/media/engine/webrtcvideoengine.cc b/media/engine/webrtcvideoengine.cc
index 37a7f34..e99886e 100644
--- a/media/engine/webrtcvideoengine.cc
+++ b/media/engine/webrtcvideoengine.cc
@@ -522,6 +522,7 @@
       video_config_(config.video),
       encoder_factory_(encoder_factory),
       decoder_factory_(decoder_factory),
+      preferred_dscp_(rtc::DSCP_DEFAULT),
       default_send_options_(options),
       last_stats_log_ms_(-1),
       discard_unknown_ssrc_packets_(webrtc::field_trial::IsEnabled(
@@ -659,7 +660,7 @@
 }
 
 rtc::DiffServCodePoint WebRtcVideoChannel::PreferredDscp() const {
-  return rtc::DSCP_AF41;
+  return preferred_dscp_;
 }
 
 bool WebRtcVideoChannel::SetSendParameters(const VideoSendParameters& params) {
@@ -779,6 +780,29 @@
     return webrtc::RTCError(webrtc::RTCErrorType::INTERNAL_ERROR);
   }
 
+  if (!parameters.encodings.empty()) {
+    const auto& priority = parameters.encodings[0].network_priority;
+    rtc::DiffServCodePoint new_dscp = rtc::DSCP_DEFAULT;
+    if (priority == 0.5 * webrtc::kDefaultBitratePriority) {
+      new_dscp = rtc::DSCP_CS1;
+    } else if (priority == webrtc::kDefaultBitratePriority) {
+      new_dscp = rtc::DSCP_DEFAULT;
+    } else if (priority == 2.0 * webrtc::kDefaultBitratePriority) {
+      new_dscp = rtc::DSCP_AF42;
+    } else if (priority == 4.0 * webrtc::kDefaultBitratePriority) {
+      new_dscp = rtc::DSCP_AF41;
+    } else {
+      RTC_LOG(LS_WARNING) << "Received invalid send network priority: "
+                          << priority;
+      return webrtc::RTCError(webrtc::RTCErrorType::INVALID_RANGE);
+    }
+
+    if (new_dscp != preferred_dscp_) {
+      preferred_dscp_ = new_dscp;
+      MediaChannel::UpdateDscp();
+    }
+  }
+
   return it->second->SetRtpParameters(parameters);
 }
 
@@ -1530,6 +1554,7 @@
   if (DscpEnabled()) {
     rtc_options.dscp = PreferredDscp();
   }
+
   return MediaChannel::SendRtcp(&packet, rtc_options);
 }
 
diff --git a/media/engine/webrtcvideoengine.h b/media/engine/webrtcvideoengine.h
index fe5f93b..f327bad 100644
--- a/media/engine/webrtcvideoengine.h
+++ b/media/engine/webrtcvideoengine.h
@@ -499,6 +499,7 @@
   // TODO(deadbeef): Don't duplicate information between
   // send_params/recv_params, rtp_extensions, options, etc.
   VideoSendParameters send_params_;
+  rtc::DiffServCodePoint preferred_dscp_;
   VideoOptions default_send_options_;
   VideoRecvParameters recv_params_;
   int64_t last_stats_log_ms_;
diff --git a/media/engine/webrtcvideoengine_unittest.cc b/media/engine/webrtcvideoengine_unittest.cc
index 1f946cb..ceac3f7 100644
--- a/media/engine/webrtcvideoengine_unittest.cc
+++ b/media/engine/webrtcvideoengine_unittest.cc
@@ -4596,6 +4596,7 @@
       new cricket::FakeNetworkInterface);
   MediaConfig config;
   std::unique_ptr<cricket::WebRtcVideoChannel> channel;
+  webrtc::RtpParameters parameters;
 
   channel.reset(static_cast<cricket::WebRtcVideoChannel*>(engine_.CreateChannel(
       call_.get(), config, VideoOptions(), webrtc::CryptoOptions())));
@@ -4603,17 +4604,37 @@
   // Default value when DSCP is disabled should be DSCP_DEFAULT.
   EXPECT_EQ(rtc::DSCP_DEFAULT, network_interface->dscp());
 
+  // Default value when DSCP is enabled is also DSCP_DEFAULT, until it is set
+  // through rtp parameters.
   config.enable_dscp = true;
   channel.reset(static_cast<cricket::WebRtcVideoChannel*>(engine_.CreateChannel(
       call_.get(), config, VideoOptions(), webrtc::CryptoOptions())));
   channel->SetInterface(network_interface.get(), /*media_transport=*/nullptr);
+  EXPECT_EQ(rtc::DSCP_DEFAULT, network_interface->dscp());
+
+  // Create a send stream to configure
+  EXPECT_TRUE(channel->AddSendStream(StreamParams::CreateLegacy(kSsrc)));
+  parameters = channel->GetRtpSendParameters(kSsrc);
+  ASSERT_FALSE(parameters.encodings.empty());
+
+  // Various priorities map to various dscp values.
+  parameters.encodings[0].network_priority = 4.0;
+  ASSERT_TRUE(channel->SetRtpSendParameters(kSsrc, parameters).ok());
   EXPECT_EQ(rtc::DSCP_AF41, network_interface->dscp());
+  parameters.encodings[0].network_priority = 0.5;
+  ASSERT_TRUE(channel->SetRtpSendParameters(kSsrc, parameters).ok());
+  EXPECT_EQ(rtc::DSCP_CS1, network_interface->dscp());
+
+  // A bad priority does not change the dscp value.
+  parameters.encodings[0].network_priority = 0.0;
+  ASSERT_FALSE(channel->SetRtpSendParameters(kSsrc, parameters).ok());
+  EXPECT_EQ(rtc::DSCP_CS1, network_interface->dscp());
 
   // Packets should also self-identify their dscp in PacketOptions.
   const uint8_t kData[10] = {0};
   EXPECT_TRUE(static_cast<webrtc::Transport*>(channel.get())
                   ->SendRtcp(kData, sizeof(kData)));
-  EXPECT_EQ(rtc::DSCP_AF41, network_interface->options().dscp);
+  EXPECT_EQ(rtc::DSCP_CS1, network_interface->options().dscp);
 
   // Verify that setting the option to false resets the
   // DiffServCodePoint.
@@ -5679,6 +5700,28 @@
   EXPECT_TRUE(channel_->SetVideoSend(primary_ssrc, nullptr, nullptr));
 }
 
+// RTCRtpEncodingParameters.network_priority must be one of a few values
+// derived from the default priority, corresponding to very-low, low, medium,
+// or high.
+TEST_F(WebRtcVideoChannelTest, SetRtpSendParametersInvalidNetworkPriority) {
+  AddSendStream();
+  webrtc::RtpParameters parameters = channel_->GetRtpSendParameters(last_ssrc_);
+  EXPECT_EQ(1UL, parameters.encodings.size());
+  EXPECT_EQ(webrtc::kDefaultBitratePriority,
+            parameters.encodings[0].network_priority);
+
+  double good_values[] = {0.5, 1.0, 2.0, 4.0};
+  double bad_values[] = {-1.0, 0.0, 0.49, 0.51, 1.1, 3.99, 4.1, 5.0};
+  for (auto it : good_values) {
+    parameters.encodings[0].network_priority = it;
+    EXPECT_TRUE(channel_->SetRtpSendParameters(last_ssrc_, parameters).ok());
+  }
+  for (auto it : bad_values) {
+    parameters.encodings[0].network_priority = it;
+    EXPECT_FALSE(channel_->SetRtpSendParameters(last_ssrc_, parameters).ok());
+  }
+}
+
 TEST_F(WebRtcVideoChannelTest, GetAndSetRtpSendParametersMaxFramerate) {
   const size_t kNumSimulcastStreams = 3;
   SetUpSimulcast(true, false);
diff --git a/media/engine/webrtcvoiceengine.cc b/media/engine/webrtcvoiceengine.cc
index aa0e140..baad232 100644
--- a/media/engine/webrtcvoiceengine.cc
+++ b/media/engine/webrtcvoiceengine.cc
@@ -57,11 +57,6 @@
 const int kOpusMinBitrateBps = 6000;
 const int kOpusBitrateFbBps = 32000;
 
-// Default audio dscp value.
-// See http://tools.ietf.org/html/rfc2474 for details.
-// See also http://tools.ietf.org/html/draft-jennings-rtcweb-qos-00
-const rtc::DiffServCodePoint kAudioDscpValue = rtc::DSCP_EF;
-
 const int kMinTelephoneEventCode = 0;  // RFC4733 (Section 2.3.1)
 const int kMaxTelephoneEventCode = 255;
 
@@ -730,6 +725,8 @@
     config_.rtp.mid = mid;
     config_.rtp.c_name = c_name;
     config_.rtp.extensions = extensions;
+    config_.has_dscp = rtp_parameters_.encodings[0].network_priority !=
+                       webrtc::kDefaultBitratePriority;
     config_.audio_network_adaptor_config = audio_network_adaptor_config;
     config_.encoder_factory = encoder_factory;
     config_.codec_pair_id = codec_pair_id;
@@ -926,12 +923,16 @@
     const absl::optional<int> old_rtp_max_bitrate =
         rtp_parameters_.encodings[0].max_bitrate_bps;
     double old_priority = rtp_parameters_.encodings[0].bitrate_priority;
+    double old_dscp = rtp_parameters_.encodings[0].network_priority;
     rtp_parameters_ = parameters;
     config_.bitrate_priority = rtp_parameters_.encodings[0].bitrate_priority;
+    config_.has_dscp = (rtp_parameters_.encodings[0].network_priority !=
+                        webrtc::kDefaultBitratePriority);
 
     bool reconfigure_send_stream =
         (rtp_parameters_.encodings[0].max_bitrate_bps != old_rtp_max_bitrate) ||
-        (rtp_parameters_.encodings[0].bitrate_priority != old_priority);
+        (rtp_parameters_.encodings[0].bitrate_priority != old_priority) ||
+        (rtp_parameters_.encodings[0].network_priority != old_dscp);
     if (rtp_parameters_.encodings[0].max_bitrate_bps != old_rtp_max_bitrate) {
       // Update the bitrate range.
       if (send_rate) {
@@ -1269,7 +1270,7 @@
 }
 
 rtc::DiffServCodePoint WebRtcVoiceMediaChannel::PreferredDscp() const {
-  return kAudioDscpValue;
+  return preferred_dscp_;
 }
 
 bool WebRtcVoiceMediaChannel::SetSendParameters(
@@ -1375,6 +1376,29 @@
     return webrtc::RTCError(webrtc::RTCErrorType::UNSUPPORTED_PARAMETER);
   }
 
+  if (!parameters.encodings.empty()) {
+    auto& priority = parameters.encodings[0].network_priority;
+    rtc::DiffServCodePoint new_dscp = rtc::DSCP_DEFAULT;
+    if (priority == 0.5 * webrtc::kDefaultBitratePriority) {
+      new_dscp = rtc::DSCP_CS1;
+    } else if (priority == 1.0 * webrtc::kDefaultBitratePriority) {
+      new_dscp = rtc::DSCP_DEFAULT;
+    } else if (priority == 2.0 * webrtc::kDefaultBitratePriority) {
+      new_dscp = rtc::DSCP_EF;
+    } else if (priority == 4.0 * webrtc::kDefaultBitratePriority) {
+      new_dscp = rtc::DSCP_EF;
+    } else {
+      RTC_LOG(LS_WARNING) << "Received invalid send network priority: "
+                          << priority;
+      return webrtc::RTCError(webrtc::RTCErrorType::INVALID_RANGE);
+    }
+
+    if (new_dscp != preferred_dscp_) {
+      preferred_dscp_ = new_dscp;
+      MediaChannel::UpdateDscp();
+    }
+  }
+
   // TODO(minyue): The following legacy actions go into
   // |WebRtcAudioSendStream::SetRtpParameters()| which is called at the end,
   // though there are two difference:
diff --git a/media/engine/webrtcvoiceengine.h b/media/engine/webrtcvoiceengine.h
index 9c013cd..be2fe0b 100644
--- a/media/engine/webrtcvoiceengine.h
+++ b/media/engine/webrtcvoiceengine.h
@@ -220,6 +220,7 @@
     if (DscpEnabled()) {
       rtc_options.dscp = PreferredDscp();
     }
+    rtc_options.dscp = PreferredDscp();
     rtc_options.info_signaled_after_sent.included_in_feedback =
         options.included_in_feedback;
     rtc_options.info_signaled_after_sent.included_in_allocation =
@@ -233,6 +234,7 @@
     if (DscpEnabled()) {
       rtc_options.dscp = PreferredDscp();
     }
+
     return VoiceMediaChannel::SendRtcp(&packet, rtc_options);
   }
 
@@ -264,6 +266,7 @@
   std::vector<AudioCodec> recv_codecs_;
 
   int max_send_bitrate_bps_ = 0;
+  rtc::DiffServCodePoint preferred_dscp_ = rtc::DSCP_DEFAULT;
   AudioOptions options_;
   absl::optional<int> dtmf_payload_type_;
   int dtmf_payload_freq_ = -1;
diff --git a/media/engine/webrtcvoiceengine_unittest.cc b/media/engine/webrtcvoiceengine_unittest.cc
index 36c0923..095d8b2 100644
--- a/media/engine/webrtcvoiceengine_unittest.cc
+++ b/media/engine/webrtcvoiceengine_unittest.cc
@@ -1138,6 +1138,28 @@
   EXPECT_EQ(32000, GetCodecBitrate(kSsrcs4[2]));
 }
 
+// RTCRtpEncodingParameters.network_priority must be one of a few values
+// derived from the default priority, corresponding to very-low, low, medium,
+// or high.
+TEST_F(WebRtcVoiceEngineTestFake, SetRtpSendParametersInvalidNetworkPriority) {
+  EXPECT_TRUE(SetupSendStream());
+  webrtc::RtpParameters parameters = channel_->GetRtpSendParameters(kSsrcX);
+  EXPECT_EQ(1UL, parameters.encodings.size());
+  EXPECT_EQ(webrtc::kDefaultBitratePriority,
+            parameters.encodings[0].network_priority);
+
+  double good_values[] = {0.5, 1.0, 2.0, 4.0};
+  double bad_values[] = {-1.0, 0.0, 0.49, 0.51, 1.1, 3.99, 4.1, 5.0};
+  for (auto it : good_values) {
+    parameters.encodings[0].network_priority = it;
+    EXPECT_TRUE(channel_->SetRtpSendParameters(kSsrcX, parameters).ok());
+  }
+  for (auto it : bad_values) {
+    parameters.encodings[0].network_priority = it;
+    EXPECT_FALSE(channel_->SetRtpSendParameters(kSsrcX, parameters).ok());
+  }
+}
+
 // Test that GetRtpSendParameters returns the currently configured codecs.
 TEST_F(WebRtcVoiceEngineTestFake, GetRtpSendParametersCodecs) {
   EXPECT_TRUE(SetupSendStream());
@@ -3020,6 +3042,7 @@
   cricket::FakeNetworkInterface network_interface;
   cricket::MediaConfig config;
   std::unique_ptr<cricket::WebRtcVoiceMediaChannel> channel;
+  webrtc::RtpParameters parameters;
 
   webrtc::AudioProcessing::Config apm_config;
   EXPECT_CALL(*apm_, GetConfig())
@@ -3040,12 +3063,31 @@
       static_cast<cricket::WebRtcVoiceMediaChannel*>(engine_->CreateChannel(
           &call_, config, cricket::AudioOptions(), webrtc::CryptoOptions())));
   channel->SetInterface(&network_interface, /*media_transport=*/nullptr);
+  EXPECT_EQ(rtc::DSCP_DEFAULT, network_interface.dscp());
+
+  // Create a send stream to configure
+  EXPECT_TRUE(
+      channel->AddSendStream(cricket::StreamParams::CreateLegacy(kSsrcZ)));
+  parameters = channel->GetRtpSendParameters(kSsrcZ);
+  ASSERT_FALSE(parameters.encodings.empty());
+
+  // Various priorities map to various dscp values.
+  parameters.encodings[0].network_priority = 4.0;
+  ASSERT_TRUE(channel->SetRtpSendParameters(kSsrcZ, parameters).ok());
   EXPECT_EQ(rtc::DSCP_EF, network_interface.dscp());
+  parameters.encodings[0].network_priority = 0.5;
+  ASSERT_TRUE(channel->SetRtpSendParameters(kSsrcZ, parameters).ok());
+  EXPECT_EQ(rtc::DSCP_CS1, network_interface.dscp());
+
+  // A bad priority does not change the dscp value.
+  parameters.encodings[0].network_priority = 0.0;
+  ASSERT_FALSE(channel->SetRtpSendParameters(kSsrcZ, parameters).ok());
+  EXPECT_EQ(rtc::DSCP_CS1, network_interface.dscp());
 
   // Packets should also self-identify their dscp in PacketOptions.
   const uint8_t kData[10] = {0};
   EXPECT_TRUE(channel->SendRtcp(kData, sizeof(kData)));
-  EXPECT_EQ(rtc::DSCP_EF, network_interface.options().dscp);
+  EXPECT_EQ(rtc::DSCP_CS1, network_interface.options().dscp);
 
   // Verify that setting the option to false resets the
   // DiffServCodePoint.
diff --git a/pc/rtpsender.cc b/pc/rtpsender.cc
index 7493542..dadd13d 100644
--- a/pc/rtpsender.cc
+++ b/pc/rtpsender.cc
@@ -58,7 +58,8 @@
 // layer.
 bool PerSenderRtpEncodingParameterHasValue(
     const RtpEncodingParameters& encoding_params) {
-  if (encoding_params.bitrate_priority != kDefaultBitratePriority) {
+  if (encoding_params.bitrate_priority != kDefaultBitratePriority ||
+      encoding_params.network_priority != kDefaultBitratePriority) {
     return true;
   }
   return false;