Add header extension filtering for WebRtcVoiceEngine/MediaChannel.

Rework filtering functionality to be reused for both Audio+Video.

BUG=webrtc:4690

Review URL: https://codereview.webrtc.org/1481963002

Cr-Commit-Position: refs/heads/master@{#10869}
diff --git a/talk/libjingle_tests.gyp b/talk/libjingle_tests.gyp
index 265c37b..be64c5e 100755
--- a/talk/libjingle_tests.gyp
+++ b/talk/libjingle_tests.gyp
@@ -91,15 +91,15 @@
         'media/base/videocapturer_unittest.cc',
         'media/base/videocommon_unittest.cc',
         'media/base/videoengine_unittest.h',
+        'media/base/videoframe_unittest.h',
         'media/devices/dummydevicemanager_unittest.cc',
         'media/devices/filevideocapturer_unittest.cc',
         'media/sctp/sctpdataengine_unittest.cc',
         'media/webrtc/simulcast_unittest.cc',
+        'media/webrtc/webrtcmediaengine_unittest.cc',
         'media/webrtc/webrtcvideocapturer_unittest.cc',
-        'media/base/videoframe_unittest.h',
         'media/webrtc/webrtcvideoframe_unittest.cc',
         'media/webrtc/webrtcvideoframefactory_unittest.cc',
-
         # Disabled because some tests fail.
         # TODO(ronghuawu): Reenable these tests.
         # 'media/devices/devicemanager_unittest.cc',
diff --git a/talk/media/base/mediachannel.h b/talk/media/base/mediachannel.h
index 3f777b3..f9ffffa 100644
--- a/talk/media/base/mediachannel.h
+++ b/talk/media/base/mediachannel.h
@@ -413,8 +413,8 @@
   std::string ToString() const {
     std::ostringstream ost;
     ost << "{";
-    ost << "id: , " << id;
     ost << "uri: " << uri;
+    ost << ", id: " << id;
     ost << "}";
     return ost.str();
   }
@@ -936,7 +936,7 @@
 
 template <class Codec>
 struct RtpParameters {
-  virtual std::string ToString() {
+  virtual std::string ToString() const {
     std::ostringstream ost;
     ost << "{";
     ost << "codecs: " << VectorToString(codecs) << ", ";
@@ -952,7 +952,7 @@
 
 template <class Codec, class Options>
 struct RtpSendParameters : RtpParameters<Codec> {
-  std::string ToString() override {
+  std::string ToString() const override {
     std::ostringstream ost;
     ost << "{";
     ost << "codecs: " << VectorToString(this->codecs) << ", ";
@@ -1160,13 +1160,13 @@
 enum SendDataResult { SDR_SUCCESS, SDR_ERROR, SDR_BLOCK };
 
 struct DataOptions {
-  std::string ToString() {
+  std::string ToString() const {
     return "{}";
   }
 };
 
 struct DataSendParameters : RtpSendParameters<DataCodec, DataOptions> {
-  std::string ToString() {
+  std::string ToString() const {
     std::ostringstream ost;
     // Options and extensions aren't used.
     ost << "{";
diff --git a/talk/media/webrtc/webrtcmediaengine.cc b/talk/media/webrtc/webrtcmediaengine.cc
index e1d4ac2..31e5025 100644
--- a/talk/media/webrtc/webrtcmediaengine.cc
+++ b/talk/media/webrtc/webrtcmediaengine.cc
@@ -26,9 +26,11 @@
  */
 
 #include "talk/media/webrtc/webrtcmediaengine.h"
+
+#include <algorithm>
+
 #include "talk/media/webrtc/webrtcvideoengine2.h"
 #include "talk/media/webrtc/webrtcvoiceengine.h"
-#include "webrtc/base/arraysize.h"
 
 namespace cricket {
 
@@ -69,43 +71,85 @@
   return CreateWebRtcMediaEngine(adm, encoder_factory, decoder_factory);
 }
 
-const char* kBweExtensionPriorities[] = {
-    kRtpTransportSequenceNumberHeaderExtension,
-    kRtpAbsoluteSenderTimeHeaderExtension, kRtpTimestampOffsetHeaderExtension};
-
-const size_t kBweExtensionPrioritiesLength = arraysize(kBweExtensionPriorities);
-
-int GetPriority(const RtpHeaderExtension& extension,
-                const char* extension_prios[],
-                size_t extension_prios_length) {
-  for (size_t i = 0; i < extension_prios_length; ++i) {
-    if (extension.uri == extension_prios[i])
-      return static_cast<int>(i);
-  }
-  return -1;
-}
-
-std::vector<RtpHeaderExtension> FilterRedundantRtpExtensions(
-    const std::vector<RtpHeaderExtension>& extensions,
-    const char* extension_prios[],
-    size_t extension_prios_length) {
-  if (extensions.empty())
-    return std::vector<RtpHeaderExtension>();
-  std::vector<RtpHeaderExtension> filtered;
-  std::map<int, const RtpHeaderExtension*> sorted;
-  for (auto& extension : extensions) {
-    int priority =
-        GetPriority(extension, extension_prios, extension_prios_length);
-    if (priority == -1) {
-      filtered.push_back(extension);
-      continue;
-    } else {
-      sorted[priority] = &extension;
+namespace {
+// Remove mutually exclusive extensions with lower priority.
+void DiscardRedundantExtensions(
+    std::vector<webrtc::RtpExtension>* extensions,
+    rtc::ArrayView<const char*> extensions_decreasing_prio) {
+  RTC_DCHECK(extensions);
+  bool found = false;
+  for (const char* name : extensions_decreasing_prio) {
+    auto it = std::find_if(extensions->begin(), extensions->end(),
+        [name](const webrtc::RtpExtension& rhs) {
+          return rhs.name == name;
+        });
+    if (it != extensions->end()) {
+      if (found) {
+        extensions->erase(it);
+      }
+      found = true;
     }
   }
-  if (!sorted.empty())
-    filtered.push_back(*sorted.begin()->second);
-  return filtered;
+}
+}  // namespace
+
+bool ValidateRtpExtensions(const std::vector<RtpHeaderExtension>& extensions) {
+  bool id_used[14] = {false};
+  for (const auto& extension : extensions) {
+    if (extension.id <= 0 || extension.id >= 15) {
+      LOG(LS_ERROR) << "Bad RTP extension ID: " << extension.ToString();
+      return false;
+    }
+    if (id_used[extension.id - 1]) {
+      LOG(LS_ERROR) << "Duplicate RTP extension ID: " << extension.ToString();
+      return false;
+    }
+    id_used[extension.id - 1] = true;
+  }
+  return true;
 }
 
+std::vector<webrtc::RtpExtension> FilterRtpExtensions(
+    const std::vector<RtpHeaderExtension>& extensions,
+    bool (*supported)(const std::string&),
+    bool filter_redundant_extensions) {
+  RTC_DCHECK(ValidateRtpExtensions(extensions));
+  RTC_DCHECK(supported);
+  std::vector<webrtc::RtpExtension> result;
+
+  // Ignore any extensions that we don't recognize.
+  for (const auto& extension : extensions) {
+    if (supported(extension.uri)) {
+      result.push_back({extension.uri, extension.id});
+    } else {
+      LOG(LS_WARNING) << "Unsupported RTP extension: " << extension.ToString();
+    }
+  }
+
+  // Sort by name, ascending, so that we don't reset extensions if they were
+  // specified in a different order (also allows us to use std::unique below).
+  std::sort(result.begin(), result.end(),
+      [](const webrtc::RtpExtension& rhs, const webrtc::RtpExtension& lhs) {
+        return rhs.name < lhs.name;
+      });
+
+  // Remove unnecessary extensions (used on send side).
+  if (filter_redundant_extensions) {
+    auto it = std::unique(result.begin(), result.end(),
+        [](const webrtc::RtpExtension& rhs, const webrtc::RtpExtension& lhs) {
+          return rhs.name == lhs.name;
+        });
+    result.erase(it, result.end());
+
+    // Keep just the highest priority extension of any in the following list.
+    static const char* kBweExtensionPriorities[] = {
+      kRtpTransportSequenceNumberHeaderExtension,
+      kRtpAbsoluteSenderTimeHeaderExtension,
+      kRtpTimestampOffsetHeaderExtension
+    };
+    DiscardRedundantExtensions(&result, kBweExtensionPriorities);
+  }
+
+  return result;
+}
 }  // namespace cricket
diff --git a/talk/media/webrtc/webrtcmediaengine.h b/talk/media/webrtc/webrtcmediaengine.h
index 8d754040..831d072 100644
--- a/talk/media/webrtc/webrtcmediaengine.h
+++ b/talk/media/webrtc/webrtcmediaengine.h
@@ -28,7 +28,11 @@
 #ifndef TALK_MEDIA_WEBRTCMEDIAENGINE_H_
 #define TALK_MEDIA_WEBRTCMEDIAENGINE_H_
 
+#include <string>
+#include <vector>
+
 #include "talk/media/base/mediaengine.h"
+#include "webrtc/config.h"
 
 namespace webrtc {
 class AudioDeviceModule;
@@ -48,13 +52,18 @@
       WebRtcVideoDecoderFactory* decoder_factory);
 };
 
-extern const char* kBweExtensionPriorities[];
-extern const size_t kBweExtensionPrioritiesLength;
+// Verify that extension IDs are within 1-byte extension range and are not
+// overlapping.
+bool ValidateRtpExtensions(const std::vector<RtpHeaderExtension>& extensions);
 
-std::vector<RtpHeaderExtension> FilterRedundantRtpExtensions(
+// Convert cricket::RtpHeaderExtension:s to webrtc::RtpExtension:s, discarding
+// any extensions not validated by the 'supported' predicate. Duplicate
+// extensions are removed if 'filter_redundant_extensions' is set, and also any
+// mutually exclusive extensions (see implementation for details).
+std::vector<webrtc::RtpExtension> FilterRtpExtensions(
     const std::vector<RtpHeaderExtension>& extensions,
-    const char* extension_prios[],
-    size_t extension_prios_length);
+    bool (*supported)(const std::string&),
+    bool filter_redundant_extensions);
 
 }  // namespace cricket
 
diff --git a/talk/media/webrtc/webrtcmediaengine_unittest.cc b/talk/media/webrtc/webrtcmediaengine_unittest.cc
new file mode 100644
index 0000000..7c80e77
--- /dev/null
+++ b/talk/media/webrtc/webrtcmediaengine_unittest.cc
@@ -0,0 +1,205 @@
+/*
+ * libjingle
+ * Copyright 2015 Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *  1. Redistributions of source code must retain the above copyright notice,
+ *     this list of conditions and the following disclaimer.
+ *  2. Redistributions in binary form must reproduce the above copyright notice,
+ *     this list of conditions and the following disclaimer in the documentation
+ *     and/or other materials provided with the distribution.
+ *  3. The name of the author may not be used to endorse or promote products
+ *     derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+#include "talk/media/webrtc/webrtcmediaengine.h"
+
+namespace cricket {
+namespace {
+
+std::vector<RtpHeaderExtension> MakeUniqueExtensions() {
+  std::vector<RtpHeaderExtension> result;
+  char name[] = "a";
+  for (int i = 0; i < 7; ++i) {
+    result.push_back(RtpHeaderExtension(name, 1 + i));
+    name[0]++;
+    result.push_back(RtpHeaderExtension(name, 14 - i));
+    name[0]++;
+  }
+  return result;
+}
+
+std::vector<RtpHeaderExtension> MakeRedundantExtensions() {
+  std::vector<RtpHeaderExtension> result;
+  char name[] = "a";
+  for (int i = 0; i < 7; ++i) {
+    result.push_back(RtpHeaderExtension(name, 1 + i));
+    result.push_back(RtpHeaderExtension(name, 14 - i));
+    name[0]++;
+  }
+  return result;
+}
+
+bool SupportedExtensions1(const std::string& name) {
+  return name == "c" || name == "i";
+}
+
+bool SupportedExtensions2(const std::string& name) {
+  return name != "a" && name != "n";
+}
+
+bool IsSorted(const std::vector<webrtc::RtpExtension>& extensions) {
+  const std::string* last = nullptr;
+  for (const auto& extension : extensions) {
+    if (last && *last > extension.name) {
+      return false;
+    }
+    last = &extension.name;
+  }
+  return true;
+}
+}  // namespace
+
+TEST(WebRtcMediaEngineTest, ValidateRtpExtensions_EmptyList) {
+  std::vector<RtpHeaderExtension> extensions;
+  EXPECT_TRUE(ValidateRtpExtensions(extensions));
+}
+
+TEST(WebRtcMediaEngineTest, ValidateRtpExtensions_AllGood) {
+  std::vector<RtpHeaderExtension> extensions = MakeUniqueExtensions();
+  EXPECT_TRUE(ValidateRtpExtensions(extensions));
+}
+
+TEST(WebRtcMediaEngineTest, ValidateRtpExtensions_OutOfRangeId_Low) {
+  std::vector<RtpHeaderExtension> extensions = MakeUniqueExtensions();
+  extensions.push_back(RtpHeaderExtension("foo", 0));
+  EXPECT_FALSE(ValidateRtpExtensions(extensions));
+}
+
+TEST(WebRtcMediaEngineTest, ValidateRtpExtensions_OutOfRangeId_High) {
+  std::vector<RtpHeaderExtension> extensions = MakeUniqueExtensions();
+  extensions.push_back(RtpHeaderExtension("foo", 15));
+  EXPECT_FALSE(ValidateRtpExtensions(extensions));
+}
+
+TEST(WebRtcMediaEngineTest, ValidateRtpExtensions_OverlappingIds_StartOfSet) {
+  std::vector<RtpHeaderExtension> extensions = MakeUniqueExtensions();
+  extensions.push_back(RtpHeaderExtension("foo", 1));
+  EXPECT_FALSE(ValidateRtpExtensions(extensions));
+}
+
+TEST(WebRtcMediaEngineTest, ValidateRtpExtensions_OverlappingIds_EndOfSet) {
+  std::vector<RtpHeaderExtension> extensions = MakeUniqueExtensions();
+  extensions.push_back(RtpHeaderExtension("foo", 14));
+  EXPECT_FALSE(ValidateRtpExtensions(extensions));
+}
+
+TEST(WebRtcMediaEngineTest, FilterRtpExtensions_EmptyList) {
+  std::vector<RtpHeaderExtension> extensions;
+  std::vector<webrtc::RtpExtension> filtered =
+      FilterRtpExtensions(extensions, SupportedExtensions1, true);
+  EXPECT_EQ(0, filtered.size());
+}
+
+TEST(WebRtcMediaEngineTest, FilterRtpExtensions_IncludeOnlySupported) {
+  std::vector<RtpHeaderExtension> extensions = MakeUniqueExtensions();
+  std::vector<webrtc::RtpExtension> filtered =
+      FilterRtpExtensions(extensions, SupportedExtensions1, false);
+  EXPECT_EQ(2, filtered.size());
+  EXPECT_EQ("c", filtered[0].name);
+  EXPECT_EQ("i", filtered[1].name);
+}
+
+TEST(WebRtcMediaEngineTest, FilterRtpExtensions_SortedByName_1) {
+  std::vector<RtpHeaderExtension> extensions = MakeUniqueExtensions();
+  std::vector<webrtc::RtpExtension> filtered =
+      FilterRtpExtensions(extensions, SupportedExtensions2, false);
+  EXPECT_EQ(12, filtered.size());
+  EXPECT_TRUE(IsSorted(filtered));
+}
+
+TEST(WebRtcMediaEngineTest, FilterRtpExtensions_SortedByName_2) {
+  std::vector<RtpHeaderExtension> extensions = MakeUniqueExtensions();
+  std::vector<webrtc::RtpExtension> filtered =
+      FilterRtpExtensions(extensions, SupportedExtensions2, true);
+  EXPECT_EQ(12, filtered.size());
+  EXPECT_TRUE(IsSorted(filtered));
+}
+
+TEST(WebRtcMediaEngineTest, FilterRtpExtensions_DontRemoveRedundant) {
+  std::vector<RtpHeaderExtension> extensions = MakeRedundantExtensions();
+  std::vector<webrtc::RtpExtension> filtered =
+      FilterRtpExtensions(extensions, SupportedExtensions2, false);
+  EXPECT_EQ(12, filtered.size());
+  EXPECT_TRUE(IsSorted(filtered));
+  EXPECT_EQ(filtered[0].name, filtered[1].name);
+}
+
+TEST(WebRtcMediaEngineTest, FilterRtpExtensions_RemoveRedundant) {
+  std::vector<RtpHeaderExtension> extensions = MakeRedundantExtensions();
+  std::vector<webrtc::RtpExtension> filtered =
+      FilterRtpExtensions(extensions, SupportedExtensions2, true);
+  EXPECT_EQ(6, filtered.size());
+  EXPECT_TRUE(IsSorted(filtered));
+  EXPECT_NE(filtered[0].name, filtered[1].name);
+}
+
+TEST(WebRtcMediaEngineTest, FilterRtpExtensions_RemoveRedundantBwe_1) {
+  std::vector<RtpHeaderExtension> extensions;
+  extensions.push_back(
+      RtpHeaderExtension(kRtpTransportSequenceNumberHeaderExtension, 3));
+  extensions.push_back(
+      RtpHeaderExtension(kRtpTimestampOffsetHeaderExtension, 9));
+  extensions.push_back(
+      RtpHeaderExtension(kRtpAbsoluteSenderTimeHeaderExtension, 6));
+  extensions.push_back(
+      RtpHeaderExtension(kRtpTransportSequenceNumberHeaderExtension, 1));
+  extensions.push_back(
+      RtpHeaderExtension(kRtpTimestampOffsetHeaderExtension, 14));
+  std::vector<webrtc::RtpExtension> filtered =
+      FilterRtpExtensions(extensions, SupportedExtensions2, true);
+  EXPECT_EQ(1, filtered.size());
+  EXPECT_EQ(kRtpTransportSequenceNumberHeaderExtension, filtered[0].name);
+}
+
+TEST(WebRtcMediaEngineTest, FilterRtpExtensions_RemoveRedundantBwe_2) {
+  std::vector<RtpHeaderExtension> extensions;
+  extensions.push_back(
+      RtpHeaderExtension(kRtpTimestampOffsetHeaderExtension, 1));
+  extensions.push_back(
+      RtpHeaderExtension(kRtpAbsoluteSenderTimeHeaderExtension, 14));
+  extensions.push_back(
+      RtpHeaderExtension(kRtpTimestampOffsetHeaderExtension, 7));
+  std::vector<webrtc::RtpExtension> filtered =
+      FilterRtpExtensions(extensions, SupportedExtensions2, true);
+  EXPECT_EQ(1, filtered.size());
+  EXPECT_EQ(kRtpAbsoluteSenderTimeHeaderExtension, filtered[0].name);
+}
+
+TEST(WebRtcMediaEngineTest, FilterRtpExtensions_RemoveRedundantBwe_3) {
+  std::vector<RtpHeaderExtension> extensions;
+  extensions.push_back(
+      RtpHeaderExtension(kRtpTimestampOffsetHeaderExtension, 2));
+  extensions.push_back(
+      RtpHeaderExtension(kRtpTimestampOffsetHeaderExtension, 14));
+  std::vector<webrtc::RtpExtension> filtered =
+      FilterRtpExtensions(extensions, SupportedExtensions2, true);
+  EXPECT_EQ(1, filtered.size());
+  EXPECT_EQ(kRtpTimestampOffsetHeaderExtension, filtered[0].name);
+}
+}  // namespace cricket
diff --git a/talk/media/webrtc/webrtcvideoengine2.cc b/talk/media/webrtc/webrtcvideoengine2.cc
index 2f07687..308b68b 100644
--- a/talk/media/webrtc/webrtcvideoengine2.cc
+++ b/talk/media/webrtc/webrtcvideoengine2.cc
@@ -243,20 +243,6 @@
   return true;
 }
 
-static std::string RtpExtensionsToString(
-    const std::vector<RtpHeaderExtension>& extensions) {
-  std::stringstream out;
-  out << '{';
-  for (size_t i = 0; i < extensions.size(); ++i) {
-    out << "{" << extensions[i].uri << ": " << extensions[i].id << "}";
-    if (i != extensions.size() - 1) {
-      out << ", ";
-    }
-  }
-  out << '}';
-  return out.str();
-}
-
 inline const webrtc::RtpExtension* FindHeaderExtension(
     const std::vector<webrtc::RtpExtension>& extensions,
     const std::string& name) {
@@ -370,60 +356,6 @@
   return false;
 }
 
-static bool ValidateRtpHeaderExtensionIds(
-    const std::vector<RtpHeaderExtension>& extensions) {
-  std::set<int> extensions_used;
-  for (size_t i = 0; i < extensions.size(); ++i) {
-    if (extensions[i].id <= 0 || extensions[i].id >= 15 ||
-        !extensions_used.insert(extensions[i].id).second) {
-      LOG(LS_ERROR) << "RTP extensions are with incorrect or duplicate ids.";
-      return false;
-    }
-  }
-  return true;
-}
-
-static bool CompareRtpHeaderExtensionIds(
-    const webrtc::RtpExtension& extension1,
-    const webrtc::RtpExtension& extension2) {
-  // Sorting on ID is sufficient, more than one extension per ID is unsupported.
-  return extension1.id > extension2.id;
-}
-
-static std::vector<webrtc::RtpExtension> FilterRtpExtensions(
-    const std::vector<RtpHeaderExtension>& extensions) {
-  std::vector<webrtc::RtpExtension> webrtc_extensions;
-  for (size_t i = 0; i < extensions.size(); ++i) {
-    // Unsupported extensions will be ignored.
-    if (webrtc::RtpExtension::IsSupportedForVideo(extensions[i].uri)) {
-      webrtc_extensions.push_back(webrtc::RtpExtension(
-          extensions[i].uri, extensions[i].id));
-    } else {
-      LOG(LS_WARNING) << "Unsupported RTP extension: " << extensions[i].uri;
-    }
-  }
-
-  // Sort filtered headers to make sure that they can later be compared
-  // regardless of in which order they were entered.
-  std::sort(webrtc_extensions.begin(), webrtc_extensions.end(),
-            CompareRtpHeaderExtensionIds);
-  return webrtc_extensions;
-}
-
-static bool RtpExtensionsHaveChanged(
-    const std::vector<webrtc::RtpExtension>& before,
-    const std::vector<webrtc::RtpExtension>& after) {
-  if (before.size() != after.size())
-    return true;
-  for (size_t i = 0; i < before.size(); ++i) {
-    if (before[i].id != after[i].id)
-      return true;
-    if (before[i].name != after[i].name)
-      return true;
-  }
-  return false;
-}
-
 std::vector<webrtc::VideoStream>
 WebRtcVideoChannel2::WebRtcVideoSendStream::CreateSimulcastVideoStreams(
     const VideoCodec& codec,
@@ -856,6 +788,7 @@
 }
 
 bool WebRtcVideoChannel2::SetSendParameters(const VideoSendParameters& params) {
+  LOG(LS_INFO) << "SetSendParameters: " << params.ToString();
   // TODO(pbos): Refactor this to only recreate the send streams once
   // instead of 4 times.
   return (SetSendCodecs(params.codecs) &&
@@ -865,6 +798,7 @@
 }
 
 bool WebRtcVideoChannel2::SetRecvParameters(const VideoRecvParameters& params) {
+  LOG(LS_INFO) << "SetRecvParameters: " << params.ToString();
   // TODO(pbos): Refactor this to only recreate the recv streams once
   // instead of twice.
   return (SetRecvCodecs(params.codecs) &&
@@ -1507,20 +1441,17 @@
 bool WebRtcVideoChannel2::SetRecvRtpHeaderExtensions(
     const std::vector<RtpHeaderExtension>& extensions) {
   TRACE_EVENT0("webrtc", "WebRtcVideoChannel2::SetRecvRtpHeaderExtensions");
-  LOG(LS_INFO) << "SetRecvRtpHeaderExtensions: "
-               << RtpExtensionsToString(extensions);
-  if (!ValidateRtpHeaderExtensionIds(extensions))
+  if (!ValidateRtpExtensions(extensions)) {
     return false;
-
-  std::vector<webrtc::RtpExtension> filtered_extensions =
-      FilterRtpExtensions(extensions);
-  if (!RtpExtensionsHaveChanged(recv_rtp_extensions_, filtered_extensions)) {
+  }
+  std::vector<webrtc::RtpExtension> filtered_extensions = FilterRtpExtensions(
+      extensions, webrtc::RtpExtension::IsSupportedForVideo, false);
+  if (recv_rtp_extensions_ == filtered_extensions) {
     LOG(LS_INFO) << "Ignoring call to SetRecvRtpHeaderExtensions because "
                     "header extensions haven't changed.";
     return true;
   }
-
-  recv_rtp_extensions_ = filtered_extensions;
+  recv_rtp_extensions_.swap(filtered_extensions);
 
   rtc::CritScope stream_lock(&stream_crit_);
   for (std::map<uint32_t, WebRtcVideoReceiveStream*>::iterator it =
@@ -1534,21 +1465,17 @@
 bool WebRtcVideoChannel2::SetSendRtpHeaderExtensions(
     const std::vector<RtpHeaderExtension>& extensions) {
   TRACE_EVENT0("webrtc", "WebRtcVideoChannel2::SetSendRtpHeaderExtensions");
-  LOG(LS_INFO) << "SetSendRtpHeaderExtensions: "
-               << RtpExtensionsToString(extensions);
-  if (!ValidateRtpHeaderExtensionIds(extensions))
+  if (!ValidateRtpExtensions(extensions)) {
     return false;
-
-  std::vector<webrtc::RtpExtension> filtered_extensions =
-      FilterRtpExtensions(FilterRedundantRtpExtensions(
-          extensions, kBweExtensionPriorities, kBweExtensionPrioritiesLength));
-  if (!RtpExtensionsHaveChanged(send_rtp_extensions_, filtered_extensions)) {
-    LOG(LS_INFO) << "Ignoring call to SetSendRtpHeaderExtensions because "
+  }
+  std::vector<webrtc::RtpExtension> filtered_extensions = FilterRtpExtensions(
+      extensions, webrtc::RtpExtension::IsSupportedForVideo, true);
+  if (send_rtp_extensions_ == filtered_extensions) {
+    LOG(LS_INFO) << "Ignoring call to SetRecvRtpHeaderExtensions because "
                     "header extensions haven't changed.";
     return true;
   }
-
-  send_rtp_extensions_ = filtered_extensions;
+  send_rtp_extensions_.swap(filtered_extensions);
 
   const webrtc::RtpExtension* cvo_extension = FindHeaderExtension(
       send_rtp_extensions_, kRtpVideoRotationHeaderExtension);
diff --git a/talk/media/webrtc/webrtcvoiceengine.cc b/talk/media/webrtc/webrtcvoiceengine.cc
index 8869a98..76c0c01 100644
--- a/talk/media/webrtc/webrtcvoiceengine.cc
+++ b/talk/media/webrtc/webrtcvoiceengine.cc
@@ -42,6 +42,7 @@
 #include "talk/media/base/audiorenderer.h"
 #include "talk/media/base/constants.h"
 #include "talk/media/base/streamparams.h"
+#include "talk/media/webrtc/webrtcmediaengine.h"
 #include "talk/media/webrtc/webrtcvoe.h"
 #include "webrtc/base/arraysize.h"
 #include "webrtc/base/base64.h"
@@ -296,20 +297,6 @@
   return config;
 }
 
-std::vector<webrtc::RtpExtension> FindAudioRtpHeaderExtensions(
-    const std::vector<RtpHeaderExtension>& extensions) {
-  std::vector<webrtc::RtpExtension> result;
-  for (const auto& extension : extensions) {
-    if (extension.uri == kRtpAbsoluteSenderTimeHeaderExtension ||
-        extension.uri == kRtpAudioLevelHeaderExtension) {
-      result.push_back({extension.uri, extension.id});
-    } else {
-      LOG(LS_WARNING) << "Unsupported RTP extension: " << extension.ToString();
-    }
-  }
-  return result;
-}
-
 class WebRtcVoiceCodecs final {
  public:
   // TODO(solenberg): Do this filtering once off-line, add a simple AudioCodec
@@ -1450,6 +1437,8 @@
 bool WebRtcVoiceMediaChannel::SetSendParameters(
     const AudioSendParameters& params) {
   RTC_DCHECK(worker_thread_checker_.CalledOnValidThread());
+  LOG(LS_INFO) << "WebRtcVoiceMediaChannel::SetSendParameters: "
+               << params.ToString();
   // TODO(pthatcher): Refactor this to be more clean now that we have
   // all the information at once.
 
@@ -1457,10 +1446,14 @@
     return false;
   }
 
-  std::vector<webrtc::RtpExtension> send_rtp_extensions =
-      FindAudioRtpHeaderExtensions(params.extensions);
-  if (send_rtp_extensions_ != send_rtp_extensions) {
-    send_rtp_extensions_.swap(send_rtp_extensions);
+  if (!ValidateRtpExtensions(params.extensions)) {
+    return false;
+  }
+  std::vector<webrtc::RtpExtension> filtered_extensions =
+      FilterRtpExtensions(params.extensions,
+                          webrtc::RtpExtension::IsSupportedForAudio, true);
+  if (send_rtp_extensions_ != filtered_extensions) {
+    send_rtp_extensions_.swap(filtered_extensions);
     for (auto& it : send_streams_) {
       it.second->RecreateAudioSendStream(send_rtp_extensions_);
     }
@@ -1475,6 +1468,8 @@
 bool WebRtcVoiceMediaChannel::SetRecvParameters(
     const AudioRecvParameters& params) {
   RTC_DCHECK(worker_thread_checker_.CalledOnValidThread());
+  LOG(LS_INFO) << "WebRtcVoiceMediaChannel::SetRecvParameters: "
+               << params.ToString();
   // TODO(pthatcher): Refactor this to be more clean now that we have
   // all the information at once.
 
@@ -1482,10 +1477,14 @@
     return false;
   }
 
-  std::vector<webrtc::RtpExtension> recv_rtp_extensions =
-      FindAudioRtpHeaderExtensions(params.extensions);
-  if (recv_rtp_extensions_ != recv_rtp_extensions) {
-    recv_rtp_extensions_.swap(recv_rtp_extensions);
+  if (!ValidateRtpExtensions(params.extensions)) {
+    return false;
+  }
+  std::vector<webrtc::RtpExtension> filtered_extensions =
+      FilterRtpExtensions(params.extensions,
+                          webrtc::RtpExtension::IsSupportedForAudio, false);
+  if (recv_rtp_extensions_ != filtered_extensions) {
+    recv_rtp_extensions_.swap(filtered_extensions);
     for (auto& it : recv_streams_) {
       it.second->RecreateAudioReceiveStream(recv_rtp_extensions_);
     }