Bug 880067 - Part 5: rtcp-fb unit tests r=ekr
authorAdam Roach [:abr] <adam@nostrum.com>
Thu, 05 Sep 2013 17:00:37 -0500
changeset 147552 623728a4a34bdbc8c72e5f310d300f99b308f506
parent 147551 063a0d518fa28433ee1cabf357b81304ca837d51
child 147553 1c9f1b7a9899206e34f323a642239b8d328973ca
push id33909
push useradam@nostrum.com
push dateTue, 17 Sep 2013 21:33:40 +0000
treeherdermozilla-inbound@623728a4a34b [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersekr
bugs880067
milestone27.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 880067 - Part 5: rtcp-fb unit tests r=ekr
media/webrtc/signaling/src/media-conduit/MediaConduitInterface.h
media/webrtc/signaling/src/media-conduit/VideoConduit.cpp
media/webrtc/signaling/test/signaling_unittests.cpp
--- a/media/webrtc/signaling/src/media-conduit/MediaConduitInterface.h
+++ b/media/webrtc/signaling/src/media-conduit/MediaConduitInterface.h
@@ -151,16 +151,27 @@ class VideoSessionConduit : public Media
 public:
   /**
    * Factory function to create and initialize a Video Conduit Session
    * return: Concrete VideoSessionConduitObject or NULL in the case
    *         of failure
    */
   static RefPtr<VideoSessionConduit> Create();
 
+  enum FrameRequestType
+  {
+    FrameRequestNone,
+    FrameRequestFir,
+    FrameRequestPli,
+    FrameRequestUnknown
+  };
+
+  VideoSessionConduit() : mFrameRequestMethod(FrameRequestNone),
+                          mUsingNackBasic(false) {}
+
   virtual ~VideoSessionConduit() {}
 
   virtual Type type() const { return VIDEO; }
 
   /**
    * Function to attach Renderer end-point of the Media-Video conduit.
    * @param aRenderer : Reference to the concrete Video renderer implementation
    * Note: Multiple invocations of this API shall remove an existing renderer
@@ -203,16 +214,33 @@ public:
    * @param sendSessionConfig: CodecConfiguration
    * NOTE: This API can be invoked multiple time. Invoking this API may involve restarting
    *        reception sub-system on the engine
    *
    */
   virtual MediaConduitErrorCode ConfigureRecvMediaCodecs(
                                 const std::vector<VideoCodecConfig* >& recvCodecConfigList) = 0;
 
+
+  /**
+    * These methods allow unit tests to double-check that the
+    * rtcp-fb settings are as expected.
+    */
+    FrameRequestType FrameRequestMethod() const {
+      return mFrameRequestMethod;
+    }
+
+    bool UsingNackBasic() const {
+      return mUsingNackBasic;
+    }
+
+   protected:
+     /* RTCP feedback settings, for unit testing purposes */
+     FrameRequestType mFrameRequestMethod;
+     bool mUsingNackBasic;
 };
 
 /**
  * MediaSessionConduit for audio
  * Refer to the comments on MediaSessionConduit above for overall
  * information
  */
 class AudioSessionConduit : public MediaSessionConduit
--- a/media/webrtc/signaling/src/media-conduit/VideoConduit.cpp
+++ b/media/webrtc/signaling/src/media-conduit/VideoConduit.cpp
@@ -569,26 +569,42 @@ WebrtcVideoConduit::ConfigureRecvMediaCo
     if(mPtrRTP->SetKeyFrameRequestMethod(mChannel, kf_request) != 0)
     {
       CSFLogError(logTag,  "%s KeyFrameRequest Failed %d ", __FUNCTION__,
                   mPtrViEBase->LastError());
       return kMediaConduitKeyFrameRequestError;
     }
   }
 
+  switch (kf_request) {
+    case webrtc::kViEKeyFrameRequestNone:
+      mFrameRequestMethod = FrameRequestNone;
+      break;
+    case webrtc::kViEKeyFrameRequestPliRtcp:
+      mFrameRequestMethod = FrameRequestPli;
+      break;
+    case webrtc::kViEKeyFrameRequestFirRtcp:
+      mFrameRequestMethod = FrameRequestFir;
+      break;
+    default:
+      MOZ_ASSERT(PR_FALSE);
+      mFrameRequestMethod = FrameRequestUnknown;
+  }
+
   if(use_nack_basic)
   {
     CSFLogDebug(logTag, "Enabling NACK (recv) for video stream\n");
     if (mPtrRTP->SetNACKStatus(mChannel, true) != 0)
     {
       CSFLogError(logTag,  "%s NACKStatus Failed %d ", __FUNCTION__,
                   mPtrViEBase->LastError());
       return kMediaConduitNACKStatusError;
     }
   }
+  mUsingNackBasic = use_nack_basic;
 
   //Start Receive on the video engine
   if(mPtrViEBase->StartReceive(mChannel) == -1)
   {
     error = mPtrViEBase->LastError();
     CSFLogError(logTag, "%s Start Receive Error %d ", __FUNCTION__, error);
 
     return kMediaConduitUnknownError;
--- a/media/webrtc/signaling/test/signaling_unittests.cpp
+++ b/media/webrtc/signaling/test/signaling_unittests.cpp
@@ -37,16 +37,21 @@ MtransportTestUtils *test_utils;
 nsCOMPtr<nsIThread> gThread;
 
 static int kDefaultTimeout = 5000;
 static bool fRtcpMux = true;
 
 static std::string callerName = "caller";
 static std::string calleeName = "callee";
 
+#define ARRAY_TO_STL(container, type, array) \
+        (container<type>((array), (array) + PR_ARRAY_SIZE(array)))
+
+#define ARRAY_TO_SET(type, array) ARRAY_TO_STL(std::set, type, array)
+
 namespace test {
 
 std::string indent(const std::string &s, int width = 4) {
   std::string prefix;
   std::string out;
   char previous = '\n';
   prefix.assign(width, ' ');
   for (std::string::const_iterator i = s.begin(); i != s.end(); i++) {
@@ -138,17 +143,18 @@ enum offerAnswerFlags
   ANSWER_AV = ANSWER_AUDIO | ANSWER_VIDEO
 };
 
 enum mediaPipelineFlags
 {
   PIPELINE_LOCAL = (1<<0),
   PIPELINE_RTCP_MUX = (1<<1),
   PIPELINE_SEND = (1<<2),
-  PIPELINE_VIDEO = (1<<3)
+  PIPELINE_VIDEO = (1<<3),
+  PIPELINE_RTCP_NACK = (1<<4)
 };
 
 
 static bool SetupGlobalThread() {
   if (!gThread) {
     nsIThread *thread;
 
     nsresult rv = NS_NewNamedThread("pseudo-main",&thread);
@@ -459,16 +465,27 @@ class ParsedSDP {
     ice_candidates_(),
     levels_(0),
     num_lines(0)
   {
     sdp_ = sdp;
     Parse();
   }
 
+  void DeleteAllLines(std::string objType)
+  {
+    int count = sdp_map_.count(objType);
+    std::cout << "Removing " << count << " lines from SDP (" << objType
+              << ")" << std::endl;
+
+    for (int i = 0; i < count; i++) {
+      DeleteLine(objType);
+    }
+  }
+
   void DeleteLine(std::string objType)
   {
     ReplaceLine(objType, "");
   }
 
   // Replaces the first instance of objType in the SDP with
   // a new string.
   // If content is an empty string then the line will be removed
@@ -478,17 +495,17 @@ class ParsedSDP {
     it = sdp_map_.find(objType);
     if(it != sdp_map_.end()) {
       SdpLine sdp_line_pair = (*it).second;
       int line_no = sdp_line_pair.first;
       sdp_map_.erase(it);
       if(content.empty()) {
         return;
       }
-      std::string value = content.substr(objType.length());
+      std::string value = content.substr(objType.length() + 1);
       sdp_map_.insert(std::pair<std::string, SdpLine>(objType,
         std::make_pair(line_no,value)));
     }
   }
 
   void AddLine(std::string content)
   {
     size_t whiteSpace = content.find(' ');
@@ -501,16 +518,37 @@ class ParsedSDP {
       key = content.substr(0, whiteSpace);
       value = content.substr(whiteSpace+1);
     }
     sdp_map_.insert(std::pair<std::string, SdpLine>(key,
       std::make_pair(num_lines,value)));
     num_lines++;
   }
 
+  // Returns the values for all lines of the indicated type
+  // Removes trailing "\r\n" from values.
+  std::vector<std::string> GetLines(std::string objType) const
+  {
+    std::multimap<std::string, SdpLine>::const_iterator it, end;
+    std::vector<std::string> values;
+    it = sdp_map_.lower_bound(objType);
+    end = sdp_map_.upper_bound(objType);
+    while (it != end) {
+      std::string value = it->second.second;
+      if (value.find("\r") != std::string::npos) {
+        value = value.substr(0, value.find("\r"));
+      } else {
+        ADD_FAILURE();
+      }
+      values.push_back(value);
+      ++it;
+    }
+    return values;
+  }
+
   //Parse SDP as std::string into map that looks like:
   // key: sdp content till first space
   // value : <line_number, sdp content after the first space>
   void Parse()
   {
     size_t prev = 0;
     size_t found = 0;
     num_lines = 0;
@@ -966,18 +1004,30 @@ void CreateAnswer(sipcc::MediaConstraint
 
     if (!streamInfo) {
       return nullptr;
     }
 
     return streamInfo->GetPipeline(track);
   }
 
-
-  void CheckMediaPipeline(int stream, int track, uint32_t flags) {
+  void CheckMediaPipeline(int stream, int track, uint32_t flags,
+    VideoSessionConduit::FrameRequestType frameRequestMethod =
+      VideoSessionConduit::FrameRequestNone) {
+
+    std::cout << name << ": Checking media pipeline settings for "
+              << ((flags & PIPELINE_LOCAL) ? "local " : "remote ")
+              << ((flags & PIPELINE_SEND) ? "sending " : "receiving ")
+              << ((flags & PIPELINE_VIDEO) ? "video" : "audio")
+              << " pipeline (stream " << stream
+              << ", track " << track << "); expect "
+              << ((flags & PIPELINE_RTCP_MUX) ? "MUX, " : "no MUX, ")
+              << ((flags & PIPELINE_RTCP_NACK) ? "NACK." : "no NACK.")
+              << std::endl;
+
     mozilla::RefPtr<mozilla::MediaPipeline> pipeline =
       GetMediaPipeline((flags & PIPELINE_LOCAL), stream, track);
     ASSERT_TRUE(pipeline);
     ASSERT_EQ(pipeline->IsDoingRtcpMux(), !!(flags & PIPELINE_RTCP_MUX));
     // We cannot yet test send/recv with video.
     if (!(flags & PIPELINE_VIDEO)) {
       if (flags & PIPELINE_SEND) {
         ASSERT_TRUE_WAIT(pipeline->rtp_packets_sent() >= 40 &&
@@ -988,19 +1038,31 @@ void CreateAnswer(sipcc::MediaConstraint
       } else {
         ASSERT_TRUE_WAIT(pipeline->rtp_packets_received() >= 40 &&
                          pipeline->rtcp_packets_sent() >= 1,
                          kDefaultTimeout);
         ASSERT_GE(pipeline->rtp_packets_received(), 40);
         ASSERT_GE(pipeline->rtcp_packets_sent(), 1);
       }
     }
+
+
+    // Check feedback method for video
+    if (flags & PIPELINE_VIDEO) {
+        mozilla::MediaSessionConduit *conduit = pipeline->Conduit();
+        ASSERT_TRUE(conduit);
+        ASSERT_EQ(conduit->type(), mozilla::MediaSessionConduit::VIDEO);
+        mozilla::VideoSessionConduit *video_conduit =
+          static_cast<mozilla::VideoSessionConduit*>(conduit);
+        ASSERT_EQ(!!(flags & PIPELINE_RTCP_NACK),
+                  video_conduit->UsingNackBasic());
+        ASSERT_EQ(frameRequestMethod, video_conduit->FrameRequestMethod());
+    }
   }
 
-
 public:
   mozilla::RefPtr<sipcc::PeerConnectionImpl> pc;
   nsRefPtr<TestObserver> pObserver;
   char* offer_;
   char* answer_;
   nsRefPtr<DOMMediaStream> domMediaStream_;
   sipcc::IceConfiguration cfg_;
   const std::string name;
@@ -1212,20 +1274,20 @@ public:
         ASSERT_TRUE_WAIT(a1_.IceCompleted() == true, kDefaultTimeout);
         ASSERT_TRUE_WAIT(a2_.IceCompleted() == true, kDefaultTimeout);
     }
   }
 
   void OfferModifiedAnswer(sipcc::MediaConstraints& aconstraints,
                            sipcc::MediaConstraints& bconstraints,
                            uint32_t offerSdpCheck, uint32_t answerSdpCheck) {
-    a1_.CreateOffer(aconstraints, OFFER_AV, offerSdpCheck);
+    a1_.CreateOffer(aconstraints, OFFER_AUDIO, offerSdpCheck);
     a1_.SetLocal(TestObserver::OFFER, a1_.offer());
     a2_.SetRemote(TestObserver::OFFER, a1_.offer());
-    a2_.CreateAnswer(bconstraints, a1_.offer(), OFFER_AV | ANSWER_AV,
+    a2_.CreateAnswer(bconstraints, a1_.offer(), OFFER_AUDIO | ANSWER_AUDIO,
                      answerSdpCheck);
     a2_.SetLocal(TestObserver::ANSWER, a2_.answer());
     ParsedSDP sdpWrapper(a2_.answer());
     sdpWrapper.ReplaceLine("m=audio", "m=audio 65375 RTP/SAVPF 109 8 101\r\n");
     sdpWrapper.AddLine("a=rtpmap:8 PCMA/8000\r\n");
     std::cout << "Modified SDP " << std::endl
               << indent(sdpWrapper.getSdp()) << std::endl;
     a1_.SetRemote(TestObserver::ANSWER, sdpWrapper.getSdp());
@@ -1293,16 +1355,93 @@ public:
     a1_.AddIceCandidate(candidate, mid, level, true);
   }
 
   void AddIceCandidateEarly(const char * candidate, const char * mid,
                             unsigned short level) {
     a1_.AddIceCandidate(candidate, mid, level, false);
   }
 
+  void CheckRtcpFbSdp(const std::string &sdp,
+                      const std::set<std::string>& expected) {
+
+    std::set<std::string>::const_iterator it;
+
+    // Iterate through the list of expected feedback types and ensure
+    // that none of them are missing.
+    for (it = expected.begin(); it != expected.end(); ++it) {
+      std::string attr = std::string("\r\na=rtcp-fb:120 ") + (*it) + "\r\n";
+      std::cout << " - Checking for a=rtcp-fb: '" << *it << "'" << std::endl;
+      ASSERT_NE(sdp.find(attr), std::string::npos);
+    }
+
+    // Iterate through all of the rtcp-fb lines in the SDP and ensure
+    // that all of them are expected.
+    ParsedSDP sdpWrapper(sdp);
+    std::vector<std::string> values = sdpWrapper.GetLines("a=rtcp-fb:120");
+    std::vector<std::string>::iterator it2;
+    for (it2 = values.begin(); it2 != values.end(); ++it2) {
+      std::cout << " - Verifying that rtcp-fb is okay: '" << *it2
+                << "'" << std::endl;
+      ASSERT_NE(0U, expected.count(*it2));
+    }
+  }
+
+  void TestRtcpFb(const std::set<std::string>& feedback,
+                  uint32_t rtcpFbFlags,
+                  VideoSessionConduit::FrameRequestType frameRequestMethod) {
+    sipcc::MediaConstraints constraints;
+
+    a1_.CreateOffer(constraints, OFFER_AV, SHOULD_SENDRECV_AV);
+    a1_.SetLocal(TestObserver::OFFER, a1_.offer());
+
+    ParsedSDP sdpWrapper(a1_.offer());
+
+    // Strip out any existing rtcp-fb lines
+    sdpWrapper.DeleteAllLines("a=rtcp-fb:120");
+
+    // Add rtcp-fb lines for the desired feedback types
+    // We know that the video section is generated second (last),
+    // so appending these to the end of the SDP has the desired effect.
+    std::set<std::string>::const_iterator it;
+    for (it = feedback.begin(); it != feedback.end(); ++it) {
+      sdpWrapper.AddLine(std::string("a=rtcp-fb:120 ") + (*it) + "\r\n");
+    }
+
+    std::cout << "Modified SDP " << std::endl
+              << indent(sdpWrapper.getSdp()) << std::endl;
+
+    // Double-check that the offered SDP matches what we expect
+    CheckRtcpFbSdp(sdpWrapper.getSdp(), feedback);
+
+    a2_.SetRemote(TestObserver::OFFER, sdpWrapper.getSdp());
+    a2_.CreateAnswer(constraints, sdpWrapper.getSdp(), OFFER_AV | ANSWER_AV);
+
+    CheckRtcpFbSdp(a2_.answer(), feedback);
+
+    a2_.SetLocal(TestObserver::ANSWER, a2_.answer());
+    a1_.SetRemote(TestObserver::ANSWER, a2_.answer());
+
+    ASSERT_TRUE_WAIT(a1_.IceCompleted() == true, kDefaultTimeout);
+    ASSERT_TRUE_WAIT(a2_.IceCompleted() == true, kDefaultTimeout);
+
+    a1_.CloseSendStreams();
+    a1_.CloseReceiveStreams();
+    a2_.CloseSendStreams();
+    a2_.CloseReceiveStreams();
+
+    // Check caller video settings for remote pipeline
+    a1_.CheckMediaPipeline(0, 2, (fRtcpMux ? PIPELINE_RTCP_MUX : 0) |
+      PIPELINE_SEND | PIPELINE_VIDEO | rtcpFbFlags, frameRequestMethod);
+
+    // Check callee video settings for remote pipeline
+    a2_.CheckMediaPipeline(0, 2, (fRtcpMux ? PIPELINE_RTCP_MUX : 0) |
+      PIPELINE_VIDEO | rtcpFbFlags, frameRequestMethod);
+  }
+
  protected:
   SignalingAgent a1_;  // Canonically "caller"
   SignalingAgent a2_;  // Canonically "callee"
 };
 
 TEST_F(SignalingTest, JustInit)
 {
 }
@@ -1691,19 +1830,17 @@ TEST_F(SignalingTest, FullCall)
   // Check the low-level media pipeline
   // for RTP and RTCP flows
   // The first Local pipeline gets stored at 0
   a1_.CheckMediaPipeline(0, 0, fRtcpMux ?
     PIPELINE_LOCAL | PIPELINE_RTCP_MUX | PIPELINE_SEND :
     PIPELINE_LOCAL | PIPELINE_SEND);
 
   // The first Remote pipeline gets stored at 1
-  a2_.CheckMediaPipeline(0, 1, fRtcpMux ?
-    PIPELINE_RTCP_MUX :
-    0);
+  a2_.CheckMediaPipeline(0, 1, (fRtcpMux ?  PIPELINE_RTCP_MUX : 0));
 }
 
 TEST_F(SignalingTest, FullCallAudioOnly)
 {
   sipcc::MediaConstraints constraints;
   OfferAnswer(constraints, constraints, OFFER_AUDIO | ANSWER_AUDIO,
               true, SHOULD_SENDRECV_AUDIO, SHOULD_SENDRECV_AUDIO);
 
@@ -1743,18 +1880,18 @@ TEST_F(SignalingTest, FullCallVideoOnly)
   //ASSERT_GE(a2_.GetPacketsSent(0), 40);
   //ASSERT_GE(a1_.GetPacketsReceived(0), 40);
   // ASSERT_GE(a2_.GetPacketsReceived(0), 40);
 }
 
 TEST_F(SignalingTest, OfferModifiedAnswer)
 {
   sipcc::MediaConstraints constraints;
-  OfferModifiedAnswer(constraints, constraints, SHOULD_SENDRECV_AV,
-                      SHOULD_SENDRECV_AV);
+  OfferModifiedAnswer(constraints, constraints, SHOULD_SENDRECV_AUDIO,
+                      SHOULD_SENDRECV_AUDIO);
   a1_.CloseSendStreams();
   a2_.CloseReceiveStreams();
 }
 
 TEST_F(SignalingTest, FullCallTrickle)
 {
   sipcc::MediaConstraints constraints;
   OfferAnswerTrickle(constraints, constraints,
@@ -2454,27 +2591,98 @@ TEST_F(SignalingTest, FullCallAudioNoMux
   ASSERT_GE(a2_.GetPacketsReceived(0), 40);
 
   // Check the low-level media pipeline
   // for RTP and RTCP flows
   // The first Local pipeline gets stored at 0
   a1_.CheckMediaPipeline(0, 0, PIPELINE_LOCAL | PIPELINE_SEND);
 
   // Now check video mux.
-  a1_.CheckMediaPipeline(0, 1, fRtcpMux ?
-    PIPELINE_LOCAL | PIPELINE_RTCP_MUX | PIPELINE_SEND | PIPELINE_VIDEO :
-    PIPELINE_LOCAL | PIPELINE_SEND | PIPELINE_VIDEO);
+  a1_.CheckMediaPipeline(0, 1,
+    PIPELINE_LOCAL | (fRtcpMux ? PIPELINE_RTCP_MUX : 0) | PIPELINE_SEND |
+    PIPELINE_VIDEO);
 
   // The first Remote pipeline gets stored at 1
   a2_.CheckMediaPipeline(0, 1, 0);
 
   // Now check video mux.
-  a2_.CheckMediaPipeline(0, 2, fRtcpMux ?
-    PIPELINE_RTCP_MUX | PIPELINE_VIDEO :
-    PIPELINE_VIDEO);
+  a2_.CheckMediaPipeline(0, 2, (fRtcpMux ?  PIPELINE_RTCP_MUX : 0) |
+    PIPELINE_VIDEO | PIPELINE_RTCP_NACK, VideoSessionConduit::FrameRequestPli);
+}
+
+TEST_F(SignalingTest, RtcpFbInOffer)
+{
+  sipcc::MediaConstraints constraints;
+  a1_.CreateOffer(constraints, OFFER_AV, SHOULD_SENDRECV_AV);
+  const char *expected[] = { "nack", "nack pli", "ccm fir" };
+  CheckRtcpFbSdp(a1_.offer(), ARRAY_TO_SET(std::string, expected));
+}
+
+TEST_F(SignalingTest, RtcpFbInAnswer)
+{
+  const char *feedbackTypes[] = { "nack", "nack pli", "ccm fir" };
+  TestRtcpFb(ARRAY_TO_SET(std::string, feedbackTypes),
+             PIPELINE_RTCP_NACK,
+             VideoSessionConduit::FrameRequestPli);
+}
+
+TEST_F(SignalingTest, RtcpFbNoNackBasic)
+{
+  const char *feedbackTypes[] = { "nack pli", "ccm fir" };
+  TestRtcpFb(ARRAY_TO_SET(std::string, feedbackTypes),
+             0,
+             VideoSessionConduit::FrameRequestPli);
+}
+
+TEST_F(SignalingTest, RtcpFbNoNackPli)
+{
+  const char *feedbackTypes[] = { "nack", "ccm fir" };
+  TestRtcpFb(ARRAY_TO_SET(std::string, feedbackTypes),
+             PIPELINE_RTCP_NACK,
+             VideoSessionConduit::FrameRequestFir);
+}
+
+TEST_F(SignalingTest, RtcpFbNoCcmFir)
+{
+  const char *feedbackTypes[] = { "nack", "nack pli" };
+  TestRtcpFb(ARRAY_TO_SET(std::string, feedbackTypes),
+             PIPELINE_RTCP_NACK,
+             VideoSessionConduit::FrameRequestPli);
+}
+
+TEST_F(SignalingTest, RtcpFbNoNack)
+{
+  const char *feedbackTypes[] = { "ccm fir" };
+  TestRtcpFb(ARRAY_TO_SET(std::string, feedbackTypes),
+             0,
+             VideoSessionConduit::FrameRequestFir);
+}
+
+TEST_F(SignalingTest, RtcpFbNoFrameRequest)
+{
+  const char *feedbackTypes[] = { "nack" };
+  TestRtcpFb(ARRAY_TO_SET(std::string, feedbackTypes),
+             PIPELINE_RTCP_NACK,
+             VideoSessionConduit::FrameRequestNone);
+}
+
+TEST_F(SignalingTest, RtcpFbPliOnly)
+{
+  const char *feedbackTypes[] = { "nack pli" };
+  TestRtcpFb(ARRAY_TO_SET(std::string, feedbackTypes),
+             0,
+             VideoSessionConduit::FrameRequestPli);
+}
+
+TEST_F(SignalingTest, RtcpFbNoFeedback)
+{
+  const char *feedbackTypes[] = { };
+  TestRtcpFb(ARRAY_TO_SET(std::string, feedbackTypes),
+             0,
+             VideoSessionConduit::FrameRequestNone);
 }
 
 // In this test we will change the offer SDP's a=setup value
 // from actpass to passive.  This will make the answer do active.
 TEST_F(SignalingTest, AudioCallForceDtlsRoles)
 {
   sipcc::MediaConstraints constraints;
   size_t match;